/* ============================================================
   Techtok PlayQuest — style.css
   Kid-first game UI in Techtok brand colors.
   ============================================================ */

/* ---------- Brand fonts (self-hosted, no external requests) ---------- */
@font-face {
  font-family: 'Neue Haas';
  src: url('../assets/fonts/NHaasGroteskTXPro-55Rg.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Haas';
  src: url('../assets/fonts/NHaasGroteskTXPro-65Md.ttf') format('truetype');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Haas';
  src: url('../assets/fonts/NHaasGroteskTXPro-75Bd.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */
:root {
  --ink: #0D065B;
  --ink-soft: #4A4680;
  --white: #FFFFFF;
  --yellow: #FAFF05;
  --yellow-dark: #C9CC00;
  --purple: #5F29FF;
  --purple-dark: #4318CC;
  --bg-yellow: #FFFFE6;
  --bg-purple: #EFEAFC;
  --bg-red: #FFDFDF;
  --bg-blue: #D6DCFF;
  --bg-green: #F1FFF9;
  --comp-yellow: #FCFF6C;
  --comp-purple: #AA8DFF;
  --comp-red: #FF8686;
  --comp-blue: #899CFF;
  --comp-green: #8CFFCF;
  --good: #14B86B;
  --good-dark: #0E8A50;
  --bad: #FF5C7A;

  --r-lg: 26px;
  --r-md: 18px;
  --r-sm: 12px;
  --r-pill: 999px;

  --shadow-sm: 0 3px 10px rgba(13, 6, 91, 0.10);
  --shadow-md: 0 10px 30px rgba(13, 6, 91, 0.18);

  --font: 'Neue Haas', 'Helvetica Neue', Helvetica, Arial, system-ui, sans-serif;

  /* ---------- Fluid type scale ----------
     Ten named steps instead of ad-hoc px, in two tiers that grow into
     different amounts of screen:

     Functional tier (xs-lg: labels, buttons, chips, body copy) caps at a
     620px-wide viewport, same as --appw's own ceiling — dense UI text
     doesn't need to keep growing just because a Chromebook has more room;
     past a point that only costs line length and makes rows wrap.

     Expressive tier (xl-hero: subheadings through hero/success titles)
     keeps growing all the way to a 1000px-wide viewport instead of
     stopping at 620 — these are short, punchy, emotional moments (page
     titles, "Helemaal juist!", the welcome screen), and a bigger screen
     genuinely earns bigger, bolder text there. It's a game; those moments
     are allowed to feel more fun with the extra room, not just sit frozen
     at phone size once the layout stops growing.

     Both tiers track viewport width continuously from a 360px small-phone
     floor, no breakpoint jumps, and every step keeps real separation from
     its neighbors at both ends so two adjacent steps are never the same
     size even at full scale. Not yet used anywhere — additive only until
     the existing font-size declarations are migrated onto it. */
  --fs-xs: clamp(10.5px, calc(9.81px + 0.19vw), 11px);
  --fs-sm: clamp(11.5px, calc(10.12px + 0.38vw), 12.5px);
  --fs-base: clamp(13px, calc(11.62px + 0.38vw), 14px);
  --fs-md: clamp(14.5px, calc(12.42px + 0.58vw), 16px);
  --fs-lg: clamp(16.5px, calc(14.42px + 0.58vw), 18px);
  --fs-xl: clamp(18.5px, calc(15.97px + 0.703vw), 23px);
  --fs-2xl: clamp(21px, calc(17.63px + 0.938vw), 27px);
  --fs-3xl: clamp(24px, calc(19.5px + 1.25vw), 32px);
  --fs-display: clamp(28px, calc(22.38px + 1.563vw), 38px);
  --fs-hero: clamp(33px, calc(24.56px + 2.344vw), 48px);

  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  --appw: 520px;
}

/* ---------- Reset ---------- */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  min-width: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  overflow-x: clip;
  /* touch-action:manipulation (the usual choice for "kill the 300ms tap
     delay") is actually shorthand for "pan-x pan-y pinch-zoom" — it still
     explicitly ALLOWS pinch-zoom, it only drops double-tap-to-zoom. iOS
     Safari also ignores the viewport meta's user-scalable=no/maximum-scale
     entirely as of iOS 10 (a deliberate Apple accessibility decision), so
     that's not a fallback either — touch-action is the only lever that
     actually works there. pan-x pan-y (no pinch-zoom) keeps normal
     scrolling and the same no-tap-delay benefit, minus native page zoom. */
  touch-action: pan-x pan-y;
  /* iOS Safari's elastic "rubber-band" bounce at the top/bottom of the page
     drags position:sticky/fixed bars along with it for a frame or two before
     they re-pin — that's what let map content show above the sticky top bar
     (.hud-wrap) in Safari. overscroll-behavior-y kills the bounce itself, so
     there's nothing for the bars to lag behind during. */
  overscroll-behavior-y: none;
  background:
    radial-gradient(circle at 20% 10%, #221591 0%, transparent 45%),
    radial-gradient(circle at 85% 85%, #3A1E9E 0%, transparent 40%),
    #0D065B;
}

body {
  font-family: var(--font);
  color: var(--ink);
  min-height: 100dvh;
  overflow-x: clip;
  overscroll-behavior-y: none;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

button {
  font-family: inherit;
  border: none;
  background: none;
  cursor: pointer;
  color: inherit;
}

img,
svg {
  max-width: 100%;
  display: block;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

#confetti-canvas {
  position: fixed;
  inset: 0;
  z-index: 900;
  pointer-events: none;
}

/* Cover for the safe-area-top/notch/status-bar region, ONLY while the map
   screen is active (body.screen-map-active, toggled in show() in app.js) —
   the map is the one screen whose top is navy (#0D065B, matching .hud);
   every other screen's top is a light pastel or a lighter blue-violet
   gradient, so showing this there would paint an unwanted dark strip over
   real content instead of blending in.
   While scrolling the map, Safari animates its own address bar from
   full-width down to a small pill, live-resizing how much of the page is
   visible — during that native animation, .hud/.hud::before (the map's own
   navy chrome) can lag a frame or two behind, briefly showing page content
   in the newly-exposed area (confirmed via screen recording: the map's
   pink CTA button flashing through where solid navy should be). This is a
   second, independent element for just that region, so it doesn't depend
   on .hud's own paint state catching up in time.
   Height matches .hud's own top padding (20px + safe-top, the iOS value)
   exactly — NOT taller — so this only ever covers the blank padding strip
   above where the HUD avatar actually starts. Taller than that and it
   paints over real content instead of just backstopping the empty margin
   above it. */
.status-bar-cover {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  max-width: var(--appw);
  margin: 0 auto;
  height: calc(20px + env(safe-area-inset-top, 0px));
  background: #0D065B;
  z-index: 9999;
  pointer-events: none;
}

body.screen-map-active .status-bar-cover {
  display: block;
}

/* ---------- App frame ---------- */
/* No max-width/shadow/card treatment here on purpose: the background
   (this element's own bg-purple, or whatever a screen overrides it with)
   fills the real browser viewport at every size, so an iPad/Chromebook/
   desktop never reads as "a phone floating in a dark void." --appw is
   still very much alive as a token, just applied one level down, to each
   screen's own content wrapper (.page-wrap, .mission-stage, etc. — see
   those rules) — so the CONTENT stays at a comfortable, centered reading
   width while the color behind it goes edge to edge. */
.app {
  min-height: 100dvh;
  position: relative;
  background: var(--bg-purple);
}

@media (min-width: 768px) {
  :root {
    --appw: 620px;
  }
}

body.full-viewport {
  --appw: 100vw;
  overflow: hidden !important;
}

body.full-viewport::-webkit-scrollbar,
body.full-viewport *::-webkit-scrollbar {
  display: none !important;
  width: 0 !important;
  height: 0 !important;
}

body.full-viewport .app {
  width: 100vw !important;
  max-width: 100vw !important;
  box-shadow: none !important;
  overflow: hidden !important;
}

body.full-viewport .bella-face {
  position: relative !important;
  background: #FFFFFF !important;
  border-radius: 50% !important;
  overflow: hidden !important;
}

body.full-viewport .bella-face::after {
  content: '' !important;
  position: absolute !important;
  inset: 0 !important;
  background: #FFFFFF url('../assets/bella-character.png') center / cover no-repeat !important;
  border-radius: 50% !important;
  z-index: 99 !important;
  pointer-events: none !important;
}

.screen {
  display: none;
}

.screen.active {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-weight: 700;
  font-size: var(--fs-lg);
  line-height: 1.2;
  padding: 16px 22px;
  border-radius: var(--r-md);
  transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
  user-select: none;
  -webkit-user-select: none;
}

.btn:active {
  transform: translateY(3px);
}

.btn-big {
  width: 100%;
  font-size: var(--fs-lg);
  padding: 18px 24px;
}

.btn-yellow {
  background: var(--yellow);
  color: var(--ink);
  box-shadow: 0 5px 0 var(--yellow-dark), var(--shadow-sm);
}

.btn-yellow:active {
  box-shadow: 0 2px 0 var(--yellow-dark);
}

.btn-purple {
  background: var(--purple);
  color: var(--white);
  box-shadow: 0 5px 0 var(--purple-dark), var(--shadow-sm);
}

.btn-purple:active {
  box-shadow: 0 2px 0 var(--purple-dark);
}

.btn-purple:disabled,
.btn-yellow:disabled {
  filter: grayscale(0.7) opacity(0.6);
  transform: none;
  cursor: default;
}

.btn-ghost {
  background: var(--bg-purple);
  color: var(--ink);
  box-shadow: 0 4px 0 #D5CCF2;
}

.btn-danger {
  background: var(--bad);
  color: #fff;
  box-shadow: 0 4px 0 #CC3A56;
}

.btn-dice {
  background: var(--white);
  color: var(--ink);
  width: 100%;
  box-shadow: 0 5px 0 #D5CCF2, var(--shadow-sm);
  margin-bottom: 14px;
  font-size: var(--fs-lg);
}

.btn-text {
  display: block;
  margin: 14px auto 0;
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--ink-soft);
  text-decoration: underline;
  text-underline-offset: 3px;
  padding: 8px;
}

.btn-row {
  display: flex;
  gap: 10px;
  align-items: stretch;
}

.btn-row .btn-big {
  width: auto;
  flex: 1;
}

.btn-back-row {
  flex: 0 0 auto;
  width: 52px;
  padding: 0;
  font-size: var(--fs-xl);
}

.btn-back-row-light {
  background: rgba(255, 255, 255, 0.16);
  color: var(--white);
  box-shadow: none;
  border: 1.5px solid rgba(255, 255, 255, 0.35);
}

.icon-btn {
  width: 42px;
  height: 42px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.92);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-sm);
  font-size: var(--fs-lg);
  font-weight: 700;
  flex-shrink: 0;
}

.icon-btn svg {
  width: 22px;
  height: 22px;
}

/* ---------- Brand ---------- */
.brand-mark {
  font-weight: 700;
  letter-spacing: 0.14em;
  font-size: var(--fs-base);
  color: var(--white);
}

.brand-mark span {
  color: var(--yellow);
}

/* ---------- Splash ---------- */
#screen-splash {
  background: var(--ink);
  align-items: center;
  justify-content: center;
  transition: opacity 0.45s ease;
}

#screen-splash.splash-out {
  opacity: 0;
}

.splash-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}

.splash-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 92px;
  height: 92px;
  background: transparent !important;
  border-radius: 50% !important;
  border: none !important;
  outline: none !important;
  box-shadow: none !important;
  overflow: visible !important;
}

.splash-logo svg {
  width: 92px;
  height: 92px;
  border-radius: 50% !important;
  border: none !important;
  outline: none !important;
  display: block;
  background: transparent !important;
  box-shadow: none !important;
  filter: drop-shadow(0 0 25px rgba(250, 255, 5, 0.45));
}

.splash-dots {
  display: flex;
  gap: 8px;
}

.splash-dots span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--yellow);
  animation: dotbounce 1s infinite ease-in-out;
}

.splash-dots span:nth-child(2) {
  animation-delay: 0.15s;
}

.splash-dots span:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes dotbounce {

  0%,
  60%,
  100% {
    transform: translateY(0);
    opacity: 0.5;
  }

  30% {
    transform: translateY(-8px);
    opacity: 1;
  }
}

/* ---------- Sky / clouds (welcome + map) ---------- */
.welcome-sky,
.map-sky {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 1;
}


.cloud,
.sky-bird,
.sky-butterfly,
.welcome-Buggie,
.ground-flower {
  pointer-events: auto;
  cursor: pointer;
  z-index: 1;
}

.anim-popped-out {
  transform: scale(0) !important;
  opacity: 0 !important;
  transition: transform 0.2s ease-in, opacity 0.2s ease-in !important;
}

.cloud {
  position: absolute;
  background: rgba(255, 255, 255, 0.85);
  border-radius: var(--r-pill);
  height: 26px;
  width: 90px;
  box-shadow: 22px -12px 0 6px rgba(255, 255, 255, 0.85), 48px -4px 0 2px rgba(255, 255, 255, 0.85);
  animation: drift 40s linear infinite;
  opacity: 0.9;
}

.cloud.c1 {
  top: 5%;
  left: -120px;
  animation-delay: -5s;
}

.cloud.c2 {
  top: 12%;
  left: -120px;
  animation-delay: -22s;
  scale: 0.75;
}

.cloud.c3 {
  top: 20%;
  left: -120px;
  animation-delay: -12s;
  scale: 1.15;
  opacity: 0.6;
}

.cloud.c4 {
  top: 28%;
  left: -120px;
  animation-delay: -32s;
  scale: 0.85;
  opacity: 0.75;
  animation-duration: 44s;
}

.cloud.c5 {
  top: 36%;
  left: -120px;
  animation-delay: -17s;
  scale: 1.25;
  opacity: 0.5;
  animation-duration: 36s;
}

.cloud.c6 {
  top: 44%;
  left: -120px;
  animation-delay: -28s;
  scale: 0.7;
  opacity: 0.8;
  animation-duration: 48s;
}

.sky-bird {
  position: absolute;
  width: 64px;
  left: -80px;
  filter: drop-shadow(0 2px 6px rgba(255, 255, 255, 0.35));
  animation: drift 32s linear infinite, flutter 1.8s ease-in-out infinite;
}

.sky-bird.b1 {
  top: 32%;
}

.sky-bird.b2 {
  top: 52%;
  width: 46px;
  animation-delay: -14s, -0.6s;
  animation-duration: 38s, 2.1s;
}

.sky-butterfly {
  position: absolute;
  width: 40px;
  filter: drop-shadow(0 2px 5px rgba(13, 6, 91, 0.28)) drop-shadow(0 0 3px rgba(255, 255, 255, 0.85));
}

.bfly-1 {
  left: -60px;
  top: 76%;
  /* Its own meandering path, not the straight-line drift clouds/birds use —
     a butterfly wanders and lingers before it eventually floats off. */
  animation: bfly-flight 24s ease-in-out infinite;
}

.bfly-2 {
  right: -60px;
  top: 83%;
  width: 30px;
  /* Opposite direction of bfly-1, its own timing and waypoints — not just a
     copy on a delay, an actually different butterfly. */
  animation: bfly-flight-rev 19s ease-in-out infinite;
  animation-delay: -7s;
}

.bfly-3 {
  left: -60px;
  top: 89%;
  width: 34px;
  animation: bfly-flight 31s ease-in-out infinite;
  animation-delay: -15s;
}

.bfly-grass {
  position: absolute;
  bottom: 180px;
  left: 25px;
  width: 32px;
  z-index: 2;
  animation: bfly-grass-hover 18s ease-in-out infinite alternate;
}

@keyframes bfly-grass-hover {
  0% {
    transform: translate(0, 0) rotate(-6deg);
  }

  25% {
    transform: translate(75px, -20px) rotate(8deg);
  }

  50% {
    transform: translate(165px, -8px) rotate(-4deg);
  }

  75% {
    transform: translate(235px, -26px) rotate(6deg);
  }

  100% {
    transform: translate(295px, -12px) rotate(-5deg);
  }
}

/* A wandering path, not a straight line: drifts on generally, but
   back-and-forth and up-and-down along the way, lingering a while before
   it finally floats off past the edge — closer to how a real butterfly
   actually moves than a steady glide. */
@keyframes bfly-flight {
  0% {
    translate: 0 0;
  }

  8% {
    translate: calc(var(--appw) * 0.10) -16px;
  }

  18% {
    translate: calc(var(--appw) * 0.16) 12px;
  }

  29% {
    translate: calc(var(--appw) * 0.30) -20px;
  }

  40% {
    translate: calc(var(--appw) * 0.26) 8px;
  }

  52% {
    translate: calc(var(--appw) * 0.46) -14px;
  }

  64% {
    translate: calc(var(--appw) * 0.58) 16px;
  }

  77% {
    translate: calc(var(--appw) * 0.74) -18px;
  }

  90% {
    translate: calc(var(--appw) * 0.90) 10px;
  }

  100% {
    translate: calc(var(--appw) + 260px) -6px;
  }
}

/* Mirror of bfly-flight — wanders right to left instead, with its own
   waypoints so it doesn't just look like the same flight in reverse. */
@keyframes bfly-flight-rev {
  0% {
    translate: 0 0;
  }

  10% {
    translate: calc(var(--appw) * -0.12) 10px;
  }

  22% {
    translate: calc(var(--appw) * -0.20) -14px;
  }

  35% {
    translate: calc(var(--appw) * -0.34) 18px;
  }

  47% {
    translate: calc(var(--appw) * -0.30) -10px;
  }

  60% {
    translate: calc(var(--appw) * -0.52) 14px;
  }

  73% {
    translate: calc(var(--appw) * -0.68) -16px;
  }

  86% {
    translate: calc(var(--appw) * -0.86) 8px;
  }

  100% {
    translate: calc((var(--appw) + 260px) * -1) -4px;
  }
}

/* Each wing is its own stroke pivoting from the point they meet, rotating
   in opposite directions — a real flap, not a rigid shape sliding by. */
.bird-wing-l,
.bird-wing-r {
  transform-origin: 16px 10px;
}

.bird-wing-l {
  animation: flap-wing-l 0.42s ease-in-out infinite;
}

.bird-wing-r {
  animation: flap-wing-r 0.42s ease-in-out infinite;
}

@keyframes flap-wing-l {

  0%,
  100% {
    rotate: 0deg;
  }

  50% {
    rotate: -22deg;
  }
}

@keyframes flap-wing-r {

  0%,
  100% {
    rotate: 0deg;
  }

  50% {
    rotate: 22deg;
  }
}

/* Wing pairs pivot from the body's centerline, scaling in toward it and
   back out — a flutter, not a static shape. */
.bfly-wing-l,
.bfly-wing-r {
  transform-origin: 14px 13px;
  animation: bfly-flap 0.6s ease-in-out infinite;
}

@keyframes bfly-flap {

  0%,
  100% {
    scale: 1 1;
  }

  50% {
    scale: 0.7 1;
  }
}

@keyframes flutter {

  0%,
  100% {
    translate: 0 0;
  }

  50% {
    translate: 0 -6px;
  }
}

/* transform (not the standalone translate property) so the large horizontal
   offset is reliably clipped by the sky container's overflow:hidden across
   browsers — the drift distance is wide enough that clipping quirks show. */
@keyframes drift {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(calc(var(--appw) + 260px));
  }
}

/* ---------- Welcome ---------- */
#screen-welcome {
  background: linear-gradient(180deg, #2A16A8 0%, #5F29FF 55%, #8F6BFF 100%);
  position: relative;
}

.welcome-Buggie {
  position: absolute;
  top: 9%;
  left: 50%;
  width: 250px;
  transform: translateX(-50%);
}

.welcome-Buggie svg {
  width: 100%;
}

.welcome-content {
  position: relative;
  z-index: 2;
  margin-top: auto;
  margin-left: auto;
  margin-right: auto;
  width: 100%;
  max-width: var(--appw);
  background: linear-gradient(180deg, transparent, rgba(13, 6, 91, 0.35) 30%);
  padding: 60px 24px calc(28px + var(--safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 16px;
  color: var(--white);
}

.welcome-content h1 {
  font-size: var(--fs-hero);
  line-height: 1.08;
  font-weight: 700;
  text-wrap: balance;
}

.glitch-word {
  color: var(--yellow);
  display: inline-block;
  animation: glitchflicker 2.4s infinite steps(1);
}

@keyframes glitchflicker {

  0%,
  88%,
  100% {
    transform: none;
    opacity: 1;
  }

  90% {
    transform: translate(-2px, 1px) skewX(-8deg);
    opacity: 0.7;
  }

  93% {
    transform: translate(2px, -1px);
    opacity: 1;
  }

  96% {
    transform: skewX(6deg);
    opacity: 0.85;
  }
}

.welcome-content p {
  font-size: 1.3em;
  line-height: 1.5;
  opacity: 0.94;
  max-width: 100%;
  text-wrap: pretty;
}

.privacy-pill {
  display: inline-flex;
  width: fit-content;
  align-items: center;
  gap: 6px;
  background: rgba(255, 255, 255, 0.16);
  border: 1.5px solid rgba(255, 255, 255, 0.35);
  color: var(--white);
  font-size: var(--fs-base);
  font-weight: 500;
  padding: 8px 14px;
  border-radius: var(--r-pill);
}

/* ---------- Onboarding (outcome screen) ---------- */
#screen-onboarding {
  background: linear-gradient(180deg, #2A16A8 0%, #5F29FF 55%, #8F6BFF 100%);
  position: relative;
}

.onboarding-Buggie {
  left: auto;
  right: 6%;
  top: 4%;
  width: 90px;
  transform: scaleX(-1);
}

.accent-word {
  color: var(--yellow);
}

.mission-steps {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 10px 0 4px;
}

.mission-step {
  display: flex;
  align-items: center;
  gap: 12px;
  background: rgba(255, 255, 255, 0.14);
  border-radius: var(--r-md);
  padding: 12px 16px;
  color: var(--white);
}

.mission-step-num {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--yellow);
  color: var(--ink);
  font-weight: 700;
  font-size: var(--fs-md);
}

.mission-step-num svg {
  width: 16px;
  height: 16px;
}

.mission-step:nth-child(2) .mission-step-num {
  background: var(--comp-green);
}

.mission-step:nth-child(3) .mission-step-num {
  background: var(--comp-blue);
}

.mission-step-label {
  font-size: 1em;
  font-weight: 700;
}

/* ---------- Hero builder ---------- */
#screen-builder {
  background: linear-gradient(180deg, #EFEAFC 0%, #D6DCFF 100%);
}

.builder-wrap {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  width: 100%;
  max-width: var(--appw);
  margin-left: auto;
  margin-right: auto;
  padding: calc(22px + var(--safe-top)) 22px 0;
}

.builder-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-bottom: 12px;
}

.builder-actions {
  position: sticky;
  bottom: 0;
  z-index: 5;
  flex-shrink: 0;
  margin: 0 -22px;
  padding: 18px 22px calc(22px + var(--safe-bottom));
  background: linear-gradient(180deg, rgba(214, 220, 255, 0) 0%, #D6DCFF 35%);
}

.builder-top {
  display: flex;
  justify-content: center;
  margin-bottom: 6px;
  flex-shrink: 0;
  position: sticky;
  top: 0;
  z-index: 5;
}

.builder-steps {
  display: flex;
  gap: 8px;
}

.bstep {
  width: 26px;
  height: 8px;
  border-radius: var(--r-pill);
  background: rgba(13, 6, 91, 0.15);
  transition: background 0.2s;
}

.bstep.now {
  background: var(--purple);
}

.bstep.done {
  background: var(--comp-purple);
}

.builder-preview {
  position: relative;
  display: flex;
  justify-content: center;
  padding: 18px 0 6px;
}

.builder-podium {
  position: absolute;
  bottom: 2px;
  width: 150px;
  height: 26px;
  border-radius: 50%;
  background: rgba(13, 6, 91, 0.14);
  filter: blur(1px);
}

.builder-hero {
  width: 150px;
  position: relative;
  z-index: 1;
}

.builder-hero svg {
  width: 100%;
}

#screen-builder h2 {
  font-size: var(--fs-3xl);
  font-weight: 700;
  text-align: center;
  margin-top: 10px;
}

.builder-sub {
  text-align: center;
  color: var(--ink-soft);
  font-size: 1.3em;
  margin: 6px 0 16px;
  text-wrap: pretty;
}

.species-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 18px;
}

.species-card {
  background: var(--white);
  border-radius: var(--r-md);
  padding: 10px 6px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  border: 3px solid transparent;
  box-shadow: var(--shadow-sm);
  transition: transform 0.12s ease, border-color 0.12s ease;
}

.species-card:active {
  transform: scale(0.94);
}

.species-card.sel {
  border-color: var(--purple);
  background: var(--bg-yellow);
}

.species-card .hero-svg {
  width: 64px;
  height: 64px;
}

.species-name {
  font-weight: 700;
  font-size: var(--fs-base);
}

.species-flavor {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
}

.palette-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 12px 14px;
  max-width: 270px;
  margin: 0 auto 16px;
  padding: 8px 0 14px;
}

.palette-row::-webkit-scrollbar {
  display: none;
}

.palette-dot {
  width: 52px;
  height: 52px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 4px solid var(--white);
  box-shadow: var(--shadow-sm);
  transition: transform 0.12s ease, border-color 0.12s;
}

.palette-dot:active {
  transform: scale(0.9);
}

.palette-dot.sel {
  border-color: var(--ink);
  transform: scale(1.12);
}

/* Onboarding voice picker — deliberately no name/gender on the card
   itself, just a sound to judge on its own terms. Two cards, so a plain
   side-by-side row reads clearer than trying to reuse the 3-up
   species-grid. */
.voice-grid {
  display: flex;
  gap: 12px;
  margin-bottom: 18px;
}

.voice-card {
  flex: 1;
  background: var(--white);
  border-radius: var(--r-md);
  padding: 18px 10px 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  border: 3px solid transparent;
  box-shadow: var(--shadow-sm);
  transition: transform 0.12s ease, border-color 0.12s ease;
}

.voice-card:active {
  transform: scale(0.96);
}

.voice-card.sel {
  border-color: var(--purple);
  background: var(--bg-yellow);
}

.voice-card-play {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-purple);
  color: var(--purple);
}

.voice-card.sel .voice-card-play {
  background: var(--purple);
  color: var(--white);
}

.voice-card-label {
  font-weight: 700;
  font-size: var(--fs-base);
}

.name-card {
  background: var(--white);
  border-radius: var(--r-lg);
  padding: 22px;
  text-align: center;
  box-shadow: var(--shadow-md);
  margin-bottom: 14px;
}

.name-big {
  font-size: var(--fs-display);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.name-code {
  margin-top: 8px;
  font-size: var(--fs-base);
  color: var(--ink-soft);
}

.name-code b {
  background: var(--bg-yellow);
  padding: 3px 10px;
  border-radius: var(--r-pill);
  color: var(--ink);
  letter-spacing: 0.08em;
}

/* The name itself is a tap target. A 9-year-old tested this screen, wanted a
   different hero name and tapped the name to change it, which did nothing.
   Tapping now points her at the word list below rather than rerolling,
   because she wanted a name she chose, not another random one. */
button.name-big {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  border: 0;
  background: none;
  font-family: inherit;
  color: inherit;
  padding: 4px 0;
  border-radius: var(--r-md);
  transition: transform 0.12s ease;
  /* The longest name is "Kosmische Panda" (15 chars). At --fs-display it
     overflows a phone-width card and wraps to two lines, so the name gets
     its own smaller ceiling and stays on one line. */
  font-size: clamp(24px, 6.5vw, 30px);
  white-space: nowrap;
}

button.name-big:active {
  transform: scale(0.97);
}

.name-edit-hint {
  width: 18px;
  height: 18px;
  margin-left: 8px;
  color: var(--ink-soft);
  vertical-align: middle;
}


/* ---------- HUD / Map ---------- */
#screen-map {
  background: linear-gradient(180deg, #A9CBFF 0%, var(--bg-blue) 50%, var(--bg-green) 100%);
  position: relative;
}

/* Sits at the bottom of the scrollable map canvas — the player starts in the
   sky and scrolls down into the ground. Outside .map-scroll as a direct child
   of #screen-map so it spans full viewport width; margin-top: auto docks it to
   the bottom of the screen when chapter content is short. */
.map-skyline {
  position: relative;
  z-index: 2;
  width: 100%;
  margin-top: auto;
  opacity: 0.94;
  pointer-events: none;
}

/* Real illustrated rolling hills, colors remapped to the brand's green
   palette. Shown at its natural size (not cropped to a fixed-height box) so
   the transparent area above the hill silhouette stays transparent and the
   curved edge overlays the page background, instead of being cut into a
   flat rectangle. */
.grass-hills-img {
  display: block;
  width: 100%;
  height: auto;
  min-height: 130px;
  object-fit: cover;
}

/* Simple flowers along the ground, each swaying from its own stem base —
   staggered timing/size per flower so they don't all sway in lockstep. */
.ground-flower {
  position: absolute;
  bottom: 4px;
  width: 16px;
  transform-origin: 50% 100%;
  animation: flower-sway 3.2s ease-in-out infinite alternate;
}

@keyframes flower-sway {
  0% {
    rotate: -7deg;
  }

  100% {
    rotate: 7deg;
  }
}

.ground-flower.gf-1 {
  left: 6%;
  animation-delay: -0.4s;
}

.ground-flower.gf-2 {
  left: 18%;
  width: 13px;
  bottom: 10px;
  animation-duration: 2.6s;
  animation-delay: -1.6s;
}

.ground-flower.gf-3 {
  left: 30%;
  animation-duration: 3.6s;
  animation-delay: -2.2s;
}

.ground-flower.gf-4 {
  left: 43%;
  width: 14px;
  bottom: 8px;
  animation-duration: 2.9s;
  animation-delay: -0.8s;
}

.ground-flower.gf-5 {
  left: 57%;
  animation-duration: 3.4s;
  animation-delay: -1.1s;
}

.ground-flower.gf-6 {
  left: 71%;
  width: 13px;
  bottom: 11px;
  animation-duration: 2.7s;
  animation-delay: -2.6s;
}

.ground-flower.gf-7 {
  left: 84%;
  animation-duration: 3.1s;
  animation-delay: -1.9s;
}

.ground-flower.gf-8 {
  left: 94%;
  width: 14px;
  bottom: 7px;
  animation-duration: 2.8s;
  animation-delay: -0.2s;
}

/* position:fixed, not sticky — same reasoning as .bottom-nav (see that
   rule): sticky has to continuously compare the element's normal-flow
   position against the current scroll offset, and that comparison can
   lag during an abrupt JS-driven scroll (showMap's scroll-to-current-node
   jump on a cold load straight to the map) before Safari's own
   address-bar/viewport animation has settled — which is what let map
   content show above this bar. Fixed is pinned to the viewport on every
   frame regardless of scroll position, so there's nothing to lag behind.
   Centered with left:0/right:0/max-width/margin:auto, not
   left:50%/transform, for the same Android-jank reasons as .bottom-nav.
   Deliberately NOT forcing a compositing layer here (no transform/
   will-change) — that was tried and it broke .hud::before's upward
   overflow (the pseudo-element that extends the navy background above
   this box to cover the area Safari's address bar vacates as it collapses
   to a pill while scrolling): forcing a layer clips a compositing-layer's
   painted region to its own box in WebKit, so content a child paints
   outside that box via negative offset can get cut off. */
.hud-wrap {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  max-width: var(--appw);
  margin: 0 auto;
  z-index: 50;
  /* This wrapper's own box can end up a hair taller than the visible .hud
     bar (safe-area insets, sub-pixel rounding vs the banner's sticky top
     offset below) — without this, that empty sliver silently eats clicks
     meant for the chapter banner's button once it's docked underneath. */
  pointer-events: none;
}

.hud-wrap>* {
  pointer-events: auto;
}

.hud {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: calc(14px + var(--safe-top)) 14px 12px;
  background: #0D065B;
  box-shadow: var(--shadow-md);
}

@supports (-webkit-touch-callout: none) {
  .hud {
    padding-top: calc(20px + var(--safe-top));
  }
}

.hud::before {
  content: '';
  position: absolute;
  bottom: 100%;
  left: -20vw;
  right: -20vw;
  height: 50vh;
  background: #0D065B;
  z-index: 0;
  pointer-events: none;
}

.hud-hero {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  background: rgba(255, 255, 255, 0.12);
  border-radius: var(--r-pill);
  padding: 5px 14px 5px 5px;
}

.hud-hero:active {
  transform: scale(0.96);
}

.hud-face {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
}

.hud-face svg {
  width: 34px;
  height: 34px;
}

.hud-names {
  display: flex;
  flex-direction: column;
  min-width: 0;
  text-align: left;
}

.hud-names b {
  color: var(--white);
  font-size: var(--fs-base);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.hud-names span {
  color: var(--comp-yellow);
  font-size: var(--fs-xs);
  font-weight: 500;
}

.hud-right {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* A total, not a bar. The bar filled toward the next level — a number the
   kid never chose and can't act on. Real progress is the map itself and the
   Missielogboek; this is just a score.
   NB: .hud-xp-bar / .hud-xp-fill below are still used by the mission log. */
.hud-xp {
  display: flex;
  align-items: center;
  gap: 5px;
  background: rgba(255, 255, 255, 0.14);
  border-radius: var(--r-pill);
  padding: 6px 12px;
  color: var(--yellow);
  font-size: var(--fs-base);
  font-weight: 700;
}

.hud-xp svg {
  width: 15px;
  height: 15px;
}

/* opens the Missielogboek drawer */
.hud-map {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.14);
  color: var(--white);
  transition: background 0.15s ease, transform 0.12s ease;
}

.hud-map svg {
  width: 19px;
  height: 19px;
}

.hud-map:active {
  transform: scale(0.92);
}

.hud-map.open {
  background: var(--yellow);
  color: var(--ink);
}

.hud-xp-bar {
  width: 84px;
  height: 9px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.25);
  overflow: hidden;
}

.hud-xp-fill {
  height: 100%;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--yellow), #FFD34D);
  transition: width 0.6s ease;
}

.map-scroll {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--appw);
  margin-left: auto;
  margin-right: auto;
  /* .hud-wrap floats over the content (position:fixed) rather than taking
     its own space in flow, so ~88px reserves room for the HUD bar itself
     (its padding + the 40px avatar, plus a hair of buffer) — then the
     existing 28px on top of that keeps chapter 1's banner from sitting
     flush against the HUD from the very first frame — same y-position as
     the sticky offset below — so the IntersectionObserver in
     watchStickyBanners() (app.js) can't call it "docked" before the user
     ever scrolls, skipping the hero state entirely. */
  padding-top: calc(88px + var(--safe-top) + 28px);
  /* .map-skyline sits immediately below .map-scroll in flow and spans full
     width to the bottom of the canvas, providing the ground buffer. */
  padding-bottom: 24px;
}

/* The "Jouw Missie" tab that hung under the header is gone — it made the
   sticky top bar feel heavy. The drawer it opened now hangs off the map
   button in the hud instead. */

.mission-drawer {
  position: relative;
  z-index: 9;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  background: var(--white);
  margin: 0 14px;
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  transition: max-height 0.4s ease, opacity 0.3s ease;
}

.mission-drawer.open {
  max-height: min(78vh, calc(100dvh - 200px));
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  opacity: 1;
  box-shadow: var(--shadow-md);
}

.mission-drawer-inner {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px 20px calc(110px + var(--safe-bottom));
}

.mission-drawer-title {
  display: block;
  text-align: center;
  font-size: var(--fs-lg);
}

.spine {
  display: flex;
  flex-direction: column;
}

/* ---------- Chapter sections (the six-chapter spine) ---------- */
.chapter-section {
  position: relative;
}

/* Marks exactly where the sticky wrapper starts, for watchStickyBanners()
   (app.js) to detect via IntersectionObserver when it's actually docked —
   CSS alone has no way to select a sticky element's "currently stuck" state. */
.chapter-banner-sentinel {
  height: 1px;
}

/* Sticks right under the HUD, swapping for the next chapter's as its
   section scrolls into place, Duolingo-style. Holds both the banner AND its
   missielogboek drawer as one pinned unit — otherwise, once the banner is
   docked, an opened drawer would expand at the chapter's normal scroll
   position instead of staying reachable under the pinned banner. */
.chapter-sticky {
  position: sticky;
  top: calc(68px + var(--safe-top));
  z-index: 10;
}

@supports (-webkit-touch-callout: none) {
  .chapter-sticky {
    top: calc(76px + var(--safe-top));
  }
}

/* Chapter identity lives on this banner, not the full scroll background —
   keeps the sky/clouds continuous behind the whole path. A roomy hero card
   at rest (big title, ghost numeral) that visibly compresses down into the
   compact docked bar as .is-stuck lands on .chapter-sticky (set by app.js)
   — the padding/font-size transitions below are what make that a morph
   instead of a swap. Fully rounded at rest; once docked, the top corners
   square off flush against the HUD above it. */
.chapter-banner {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 50px;
  margin: 0 14px;
  padding: 26px 22px 20px;
  background: var(--chapter-tint, transparent);
  border-radius: var(--r-lg);
  /* A real floating shadow, not the hairline --shadow-card — this banner
     needs to read as a lifted card over the sky, not a flat color block. */
  box-shadow: var(--shadow-sm);
  transition: padding 0.32s cubic-bezier(.4, 0, .2, 1), margin 0.32s cubic-bezier(.4, 0, .2, 1), border-radius 0.15s ease, transform 0.1s ease;
}

/* Only unlocked chapters carry data-chapter (see app.js) — a locked one
   keeps the plain, unclickable banner rather than opening an intro for
   content the player can't play yet. */
.chapter-banner[data-chapter] {
  cursor: pointer;
}

.chapter-banner[data-chapter]:active {
  transform: scale(0.99);
}

.chapter-sticky.is-stuck .chapter-banner {
  margin: 0;
  padding: 20px 20px 12px;
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}

/* A big pale numeral behind the title, purely decorative — it's what gives
   the resting banner presence, then shrinks away as the real icon button
   fades in once docked, so the two read as one continuous morph. */
.chapter-banner-ghost {
  position: absolute;
  right: 22px;
  top: 44%;
  transform: translateY(-44%);
  font-size: 88px;
  font-weight: 800;
  line-height: 1;
  color: var(--chapter-accent, var(--ink));
  opacity: 0.14;
  pointer-events: none;
  user-select: none;
  transition: opacity 0.26s ease, transform 0.32s cubic-bezier(.4, 0, .2, 1), font-size 0.32s cubic-bezier(.4, 0, .2, 1);
}

.chapter-sticky.is-stuck .chapter-banner-ghost {
  opacity: 0;
  font-size: 48px;
  transform: translateY(-44%) translateX(14px);
}

.chapter-banner-text {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.chapter-sticky.is-stuck .chapter-banner-text {
  gap: 1px;
}

/* Only makes sense to open once its chapter is the one actually pinned —
   opening it from a resting (not-yet-docked) banner felt disconnected, so
   it stays hidden (and scaled down, echoing the ghost numeral it
   visually replaces) until .chapter-sticky.is-stuck reveals it below. */
.chapter-banner-icon {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.9);
  color: var(--ink-soft);
  box-shadow: 0 3px 8px rgba(13, 6, 91, 0.14);
  opacity: 0;
  transform: scale(0.5);
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.3s cubic-bezier(.34, 1.56, .64, 1), background 0.15s ease, color 0.15s ease;
}

.chapter-sticky.is-stuck .chapter-banner-icon {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

.chapter-banner-icon:active {
  transform: scale(0.9);
}

.chapter-banner-icon.open {
  background: var(--yellow);
  color: var(--ink);
}

.chapter-banner-icon svg {
  width: 18px;
  height: 18px;
}

.chapter-num {
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--chapter-accent, var(--ink-soft));
  transition: font-size 0.28s ease;
}

.chapter-sticky.is-stuck .chapter-num {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
}

.chapter-title {
  font-size: var(--fs-2xl);
  font-weight: 800;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: font-size 0.28s cubic-bezier(.4, 0, .2, 1);
}

.chapter-sticky.is-stuck .chapter-title {
  font-size: var(--fs-lg);
}

.chapter-meta {
  text-align: left;
  padding: 8px 24px 30px;
}

/* Centers the subtitle pill regardless of .chapter-meta's own (left)
   text alignment, which the lock ribbon and lesson banner below still rely on. */
.chapter-subtitle-wrap {
  display: flex;
  justify-content: center;
}

/* A small pill, not a second heading fighting the banner above it — closer
   to a caption/kicker than a headline, since the banner already carries the
   bold chapter identity. Carries its own chapter accent color so it still
   reads as "alive" without falling back to a flat dark bar. */
.chapter-section-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-base);
  font-weight: 700;
  font-family: inherit;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  /*background: var(--chapter-tint, var(--purple));
    box-shadow: var(--shadow-sm);
*/
  border: none;
  padding: 9px 20px;
  border-radius: var(--r-pill);
  margin: 2px 0 22px;
  transition: transform 0.1s ease;
}

/* A <button> when its chapter is unlocked (see app.js), a plain <h3>
   otherwise — only the button variant gets pointer/press affordance. */
button.chapter-section-title {
  cursor: pointer;
}

button.chapter-section-title:active {
  transform: scale(0.96);
}

.chapter-subtitle-icon {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
}

.chapter-banner-lock {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 2px;
  color: var(--chapter-accent);
  font-size: var(--fs-sm);
  font-weight: 600;
  opacity: 0.7;
}

.chapter-banner-lock svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
}

.chapter-locked .chapter-title,
.chapter-locked .chapter-num {
  opacity: 0.55;
}



.chapter-beta-banner {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 14px;
  background: rgba(255, 255, 255, 0.7);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  line-height: 1.45;
  color: var(--ink-soft);
}

.chapter-beta-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  align-self: flex-start;
  background: var(--chapter-accent);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 3px 10px;
  border-radius: var(--r-pill);
}

.chapter-beta-pill svg {
  width: 13px;
  height: 13px;
}

.chapter-connector {
  width: 0;
  height: 34px;
  margin: 0 auto;
  border-left: 5px dashed rgba(255, 255, 255, 0.65);
}

.chapter-connector.done {
  border-left-style: solid;
  border-left-color: var(--good);
}

.map-path {
  position: relative;
  height: var(--path-h, 220px);
  margin-top: 6px;
}

.map-path-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.map-step {
  position: absolute;
  left: var(--nx);
  top: var(--ny);
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 150px;
}

.map-node {
  position: relative;
  width: 78px;
  height: 78px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6px 0 rgba(13, 6, 91, 0.25), var(--shadow-md);
  transition: transform 0.12s ease;
}

.map-node:active:not(:disabled) {
  transform: scale(0.92);
}

.map-node-icon svg {
  width: 36px;
  height: 36px;
}

.node-open,
.node-done {
  background: var(--white);
  color: var(--purple);
}

.ncolor-ocean.node-open,
.ncolor-ocean.node-done {
  color: #2F6FD6;
}

.ncolor-coral.node-open,
.ncolor-coral.node-done {
  color: #E0503F;
}

.ncolor-sunset.node-open,
.ncolor-sunset.node-done {
  color: #E06A1B;
}

.ncolor-berry.node-open,
.ncolor-berry.node-done {
  color: #D9427F;
}

.ncolor-galaxy.node-open,
.ncolor-galaxy.node-done {
  color: #6A3FE0;
}

.ncolor-forest.node-open,
.ncolor-forest.node-done {
  color: #2E9A62;
}

.node-done .map-node-icon {
  opacity: 0.5;
}

.map-node-badge {
  position: absolute;
  right: -4px;
  bottom: -4px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--good);
  color: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
  border: 3px solid var(--white);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
}

.map-node-badge svg {
  width: 15px;
  height: 15px;
}

.node-locked {
  background: rgba(255, 255, 255, 0.55);
  color: var(--ink-soft);
  cursor: default;
}

.node-auth-locked {
  cursor: pointer;
}

.map-node-lock {
  font-size: var(--fs-3xl);
  display: flex;
  align-items: center;
  justify-content: center;
}

.map-node-lock svg {
  width: 28px;
  height: 28px;
}

.node-trophy {
  background: var(--yellow);
  color: var(--ink);
  animation: nodepulse 1.6s infinite;
}

.map-node-ring {
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  border: 4px solid var(--yellow);
  animation: nodepulse 1.6s infinite;
}

@keyframes nodepulse {

  0%,
  100% {
    transform: scale(1);
    opacity: 1;
  }

  50% {
    transform: scale(1.08);
    opacity: 0.75;
  }
}

.map-node-label {
  margin-top: 8px;
  text-align: center;
  background: rgba(255, 255, 255, 0.92);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  display: flex;
  align-items: stretch;
  max-width: 170px;
}

.map-node-label-content {
  padding: 6px 12px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  align-items: center;
  justify-content: center;
}

.map-node-label-content b {
  font-size: var(--fs-base);
}

.map-sub {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* Only shown to a player who has finished nothing yet. See nothingDoneYet
   in app.js: the ring alone read as "you have arrived", not "begin here". */
.map-start-here {
  display: inline-block;
  background: var(--yellow);
  color: var(--ink);
  font-size: var(--fs-xs);
  font-weight: 800;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  margin-bottom: 4px;
}

.map-stars {
  font-size: var(--fs-base);
  letter-spacing: 2px;
}

.star {
  color: #D8D4F0;
}

.star svg {
  fill: currentColor;
}

.star.on {
  color: #FFB300;
}

.map-here {
  position: absolute;
  top: -46px;
  left: 50%;
  transform: translateX(-50%);
  width: 52px;
  animation: bob 2s ease-in-out infinite;
  filter: drop-shadow(0 4px 6px rgba(13, 6, 91, 0.3));
}

@keyframes bob {

  0%,
  100% {
    translate: 0 0;
  }

  50% {
    translate: 0 -7px;
  }
}

/* ---------- Bottom nav ----------
   position:fixed, pinned to the true viewport — the standard, most
   broadly-supported technique for an app-shell bottom bar (every major
   mobile PWA uses this). Centered with plain left:0/right:0 + max-width +
   margin:0 auto, NOT left:50%/transform:translateX(-50%) — that transform
   combo on a fixed element is a well-documented source of detachment/jank
   on Android Chrome during scroll and address-bar show/hide, which is what
   broke this bar. margin:auto centering has none of that failure mode and
   is just as widely supported. */
.bottom-nav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  max-width: var(--appw);
  margin: 0 auto;
  z-index: 50;
  display: flex;
  justify-content: space-around;
  background: var(--white);
  padding: 10px 8px calc(10px + var(--safe-bottom));
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: 0 -8px 30px rgba(13, 6, 91, 0.18);
}

.nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--ink-soft);
  padding: 4px 18px;
  border-radius: var(--r-md);
}

.nav-item .nav-icon svg {
  width: 24px;
  height: 24px;
}

.nav-item.active {
  color: var(--purple);
  background: var(--bg-purple);
}

/* Floating "back to where I am" button — the wrapper is a full-width fixed
   strip (same left:0/right:0/max-width/margin:auto centering as
   .bottom-nav — no transform, see that rule for why) with
   pointer-events:none, so only the button itself — right-aligned inside
   it — is actually clickable; the rest of the strip lets map taps through.
   Hidden by default: bindMapLocate() in app.js toggles .visible once
   #map-node-current scrolls out of view. */
.map-locate-wrap {
  position: fixed;
  left: 0;
  right: 0;
  max-width: var(--appw);
  margin: 0 auto;
  bottom: calc(86px + var(--safe-bottom));
  display: flex;
  justify-content: flex-end;
  padding: 0 16px;
  pointer-events: none;
  z-index: 55;
}

.map-locate-btn {
  pointer-events: none;
  width: 48px;
  height: 48px;
  border-radius: var(--r-pill);
  background: var(--yellow);
  color: var(--ink);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 0 var(--yellow-dark), var(--shadow-md);
  opacity: 0;
  transform: translateY(8px) scale(0.9);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.map-locate-btn svg {
  width: 22px;
  height: 22px;
}

.map-locate-btn.visible {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

.map-locate-btn:active {
  transform: translateY(3px) scale(1);
  box-shadow: 0 1px 0 var(--yellow-dark);
}

@media (prefers-reduced-motion: reduce) {
  .map-locate-btn {
    transition: opacity 0.2s ease;
  }
}

/* ---------- Mission stage ---------- */
/* #screen-mission/#screen-reward deliberately NOT listed here: both always
   get a real mcolor-* class set via JS (setScreenColor in engine.js,
   inline in app.js) before their content ever renders, and an ID selector
   here would out-specificity .mcolor-*'s class selector and silently win
   every time, stuck on this flat fallback instead of the real mission
   color — exactly the bug that made the header's scrim (which reads
   --top-fade, a different property, unaffected by this fight) match a
   color the visible background wasn't actually showing. .app's own
   background is the same var(--bg-purple), so there's no visible gap
   before a color class is applied. #screen-hero never gets an mcolor
   class at all, so it keeps this flat fallback for real. */
#screen-hero {
  background: var(--bg-purple);
}

.mission-stage {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  width: 100%;
  max-width: var(--appw);
  margin-left: auto;
  margin-right: auto;
  padding: calc(10px + var(--safe-top)) 16px 0;
}

/* --top-fade is declared first and the gradient is built FROM it (not the
   other way around) — one hex value per color, not two copies that could
   drift apart. .mission-top's scrim inherits --top-fade directly, so it's
   always reading the exact color the background itself uses, never a
   restated duplicate of it. */
.mcolor-ocean {
  --top-fade: #D8E9FF;
  background: linear-gradient(180deg, var(--top-fade) 0%, #EFEAFC 70%);
}

.mcolor-sunset {
  --top-fade: #FFE3C9;
  background: linear-gradient(180deg, var(--top-fade) 0%, #FFF6EC 70%);
}

.mcolor-berry {
  --top-fade: #FFDCEA;
  background: linear-gradient(180deg, var(--top-fade) 0%, #FFF3F8 70%);
}

.mcolor-galaxy {
  --top-fade: #E8DFFF;
  background: linear-gradient(180deg, var(--top-fade) 0%, #F6F2FF 70%);
}

.mcolor-forest {
  --top-fade: #D9F7E6;
  background: linear-gradient(180deg, var(--top-fade) 0%, #F1FFF9 70%);
}

/* position:sticky has to live here, not on the close button nested inside
   — a sticky element can only stay stuck within the height of its OWN
   parent, and a button sticky inside just .mission-top (42px tall) runs
   out of room to stick in almost immediately and scrolls away with it.
   .mission-top's real parent, .mission-stage, spans the whole scrollable
   round, so making the row itself sticky is what actually keeps the close
   button (and everything else in this header — dots, XP chip) reachable
   the whole way down. No background added: .mission-top was already
   transparent, and .icon-btn/.mission-xp-chip/.rdot already carry their
   own solid pills, so this still reads as a few small floating elements,
   not a sticky bar. */
.mission-top {
  position: sticky;
  top: calc(10px + var(--safe-top));
  z-index: 45;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 42px;
  margin-bottom: 12px;
  width: 100%;
}

.mission-top>button,
.mission-top>.icon-btn {
  flex-shrink: 0;
}

/* Soft scrim behind the sticky header: without it, scrolling content runs
   straight into the floating button/dots/chip instead of settling behind
   them. --top-fade (set per .mcolor-* above, inherited down through the
   DOM) is that screen's own gradient color, so this always matches
   whatever mission/lesson/creation color is active — falls back to the
   plain screens' flat bg-purple where no mcolor is set (hero, blueprint).
   Bleeds past .mission-stage's own 16px side padding and up through the
   safe-area so the fade reaches the true top/edges of the screen, not
   just the header's own box. */
.mission-top::before {
  content: '';
  position: absolute;
  top: 0;
  left: -20vw;
  right: -20vw;
  bottom: -20px;
  z-index: -1;
  background: linear-gradient(to bottom, var(--top-fade, var(--bg-purple)) 60%, transparent 100%);
  pointer-events: none;
}

/* Upward solid extension (identical to .hud::before on the map): extends
   the solid background color 50vh above the sticky top bar so scrolling
   content never bleeds into the iOS status bar, address bar, or safe area. */
.mission-top::after {
  content: '';
  position: absolute;
  bottom: 100%;
  left: -20vw;
  right: -20vw;
  height: 50vh;
  background: var(--top-fade, var(--bg-purple));
  z-index: -1;
  pointer-events: none;
}

.mission-top-mid {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-align: center;
  pointer-events: none;
  z-index: 1;
}

/* Title-only headers have just one side button, so flex:1 text-align:center
   centers within the leftover space, not the true header center. Bar headers
   (rdots flanked by two buttons) don't hit this, since a full-width bar has
   no "center" to be off. */
.mission-top-mid:has(.mission-top-title) {
  position: absolute;
  left: 0;
  right: 0;
}

.mission-top-mid * {
  pointer-events: auto;
}

.mission-top-title {
  font-weight: 800;
  font-size: var(--fs-lg);
  color: var(--ink);
  letter-spacing: -0.01em;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.mk-screen-step {
  font-weight: 800;
  font-size: var(--fs-sm);
  color: var(--ink);
  letter-spacing: 0.02em;
  text-align: center;
  margin-bottom: 6px;
}

.rdots {
  display: flex;
  gap: 4px;
  width: 100%;
  height: 8px;
}

.rdot {
  flex: 1;
  height: 100%;
  border-radius: var(--r-pill);
  background: rgba(13, 6, 91, 0.15);
  transition: background 0.2s;
}

.rdot.now {
  background: var(--purple);
}

.rdot.done {
  background: var(--good);
}

.mission-xp-chip {
  background: var(--ink);
  color: var(--yellow);
  font-weight: 700;
  font-size: var(--fs-base);
  padding: 8px 12px;
  border-radius: var(--r-pill);
  min-width: 58px;
  text-align: center;
  flex-shrink: 0;
}

/* ---------- Briefing ---------- */
.briefing {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 4px 2px 14px 2px;
}

.brief-header {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 24px;
  animation: slideup 0.35s ease both;
}

.brief-tag {
  display: inline-flex;
  align-self: flex-start;
  align-items: center;
  gap: 8px;
  background: rgba(255, 255, 255, 0.7);
  border-radius: var(--r-pill);
  padding: 4px 12px 4px 6px;
  margin-bottom: 6px;
  box-shadow: none;
}

.brief-tag-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  font-size: var(--fs-base);
  flex-shrink: 0;
}

.brief-tag-icon svg {
  width: 15px;
  height: 15px;
}

.brief-tag-text {
  font-size: var(--fs-sm);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--purple);
}

.brief-title {
  font-size: 1.8em;
  font-weight: 800;
  color: var(--ink);
  line-height: 1.2;
  margin: 0;
  text-wrap: pretty;
  letter-spacing: -0.01em;
}

.brief-sub {
  font-size: 1em;
  font-weight: 500;
  color: var(--ink-soft);
  margin: 0;
  line-height: 1.35;
  text-wrap: pretty;
}

/* Small static best-score row on the replay briefing — same amber-fill
   language as .star-big on the reward screen, just sized for a header
   line instead of a big celebratory pop-in. */
.brief-replay-stars {
  display: flex;
  gap: 4px;
  margin-top: 2px;
}

.brief-replay-stars span {
  color: #D8D4F0;
}

.brief-replay-stars span.on {
  color: #FFB300;
}

.brief-replay-stars svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
}

.brief-avatar {
  width: 84px;
  flex-shrink: 0;
}

.brief-avatar svg {
  width: 100%;
}

.brief-villain,
.brief-hero {
  display: flex;
  align-items: flex-end;
  gap: 10px;
}

.brief-villain {
  animation: slideup 0.4s 0.1s ease both;
}

.brief-hero {
  flex-direction: row;
  justify-content: flex-end;
  animation: slideup 0.4s 0.7s ease both;
}

.bubble {
  position: relative;
  padding: 24px 26px;
  border-radius: var(--r-lg);
  font-size: 1em;
  line-height: 1.45;
  font-weight: 500;
  box-shadow: var(--shadow-sm);
  animation: popin 0.35s ease both;
  text-wrap: pretty;
}

.bubble-villain {
  background: var(--ink);
  color: var(--white);
  border-bottom-left-radius: 6px;
  animation-delay: 0.25s;
}

.bubble-hero {
  background: var(--white);
  border-bottom-right-radius: 6px;
  animation-delay: 0.85s;
}

.bubble-coach {
  background: var(--white);
  border-bottom-left-radius: 6px;
  animation-delay: 0.4s;
  color: var(--ink);
}

.brief-task {
  margin-top: auto;
  display: flex;
  gap: 14px;
  align-items: center;
  border-radius: var(--r-lg);
  padding: 16px;
  background: var(--white);
  box-shadow: var(--shadow-md);
  animation: slideup 0.4s 1.6s ease both;
}

.brief-task-intro {
  margin-top: auto;
  display: flex;
  gap: 14px;
  text-wrap: pretty;
  align-items: center;
  border-radius: var(--r-lg);
  padding: 16px;
  background: var(--white);
  box-shadow: var(--shadow-md);
  animation: slideup 0.4s 1.6s ease both;
}

.briefing-actions {
  animation: slideup 0.4s 0.2s ease both;
}

.brief-task strong {
  font-size: var(--fs-base);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.brief-task p {
  font-size: var(--fs-md);
  font-weight: 500;
  margin-top: 3px;
  text-wrap: pretty;
}

.brief-task-icon {
  width: 54px;
  height: 54px;
  border-radius: var(--r-md);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Minimalist instruction label above round interactive areas */
.round-instruction-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-align: center;
  margin: 14px auto 14px auto;
  line-height: 1.3;
}

.brief-task-icon svg {
  width: 30px;
  height: 30px;
}

.icolor-ocean {
  background: var(--bg-blue);
  color: #2F6FD6;
}

.icolor-sunset {
  background: #FFE3C9;
  color: #E06A1B;
}

.icolor-berry {
  background: #FFDCEA;
  color: #D9427F;
}

.icolor-galaxy {
  background: #E8DFFF;
  color: #6A3FE0;
}

.icolor-forest {
  background: #D9F7E6;
  color: #2E9A62;
}

.mission-actions {
  position: sticky;
  bottom: 0;
  padding: 16px 20px calc(20px + var(--safe-bottom, 0px));
  background: var(--white, #ffffff);
  z-index: 100;
  display: flex;
  gap: 12px;
  align-items: center;
  box-shadow: 0 -10px 20px rgba(0, 0, 0, 0.05);
  border-top-left-radius: var(--r-lg, 24px);
  border-top-right-radius: var(--r-lg, 24px);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  margin-left: -16px;
  margin-right: -16px;
}

.mission-actions .btn-big {
  flex: 1;
}

/* ---------- Round ---------- */
/* padding-bottom here (not on .mission-stage, which .round-feedback's own
   negative-margin bleed math is tuned against — see that rule's comment)
   guarantees scroll slack below the last row of round content (e.g. a long
   order tray or answer grid). Without it, content taller than one viewport
   ends exactly at the document's bottom edge with nothing left to scroll
   into, so the last row sits behind Safari's floating bottom toolbar with
   no way to reach it. */
.round-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding-bottom: calc(24px + var(--safe-bottom, 0px));
}

/* ---- Bella asks (Duolingo-style: mentor + speech bubble on top) ----
   She holds the question for the whole round; reactions go to the
   feedback sheet so the question never gets overwritten. */
.round-ask {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  margin-bottom: 22px;
}

.bella-coach {
  flex-shrink: 0;
  position: relative;
  width: 92px;
  height: 92px;
}

.bella-face {
  position: relative;
  width: 92px;
  height: 92px;
  border-radius: 50%;
  background: linear-gradient(to bottom, rgba(214, 220, 255, 0.5), var(--white));
  border: 3px solid var(--white);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.bella-face .bella-zoom {
  --bella-shift: -23px;
}

.ask-bubble {
  position: relative;
  flex: 1;
  background: var(--white);
  border-radius: var(--r-lg);
  border-bottom-left-radius: 6px;
  padding: 14px 18px;
  box-shadow: var(--shadow-sm);
  min-height: 72px;
  display: flex;
  align-items: center;
}

/* tail, pointing back at Bella */
.ask-bubble::before {
  content: "";
  position: absolute;
  left: -7px;
  bottom: 12px;
  width: 14px;
  height: 14px;
  background: var(--white);
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
  border-bottom-left-radius: 3px;
}

.ask-text {
  width: 100%;
  font-size: 1.4em;
  line-height: 1.35;
  font-weight: 600;
  color: var(--ink);
  margin: 0;
  text-wrap: balance;
}

.ask-speak {
  position: absolute;
  right: -4px;
  bottom: -2px;
  z-index: 3;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-purple);
  color: var(--purple);
  border: 2.5px solid var(--white);
  box-shadow: 0 2px 8px rgba(13, 6, 91, 0.18);
  cursor: pointer;
  transition: transform 0.12s ease, background 0.15s ease, color 0.15s ease;
}

.ask-speak svg {
  width: 16px;
  height: 16px;
}

.ask-speak:hover {
  transform: scale(1.08);
}

.ask-speak:active {
  transform: scale(0.92);
}

.ask-speak.is-playing {
  background: var(--purple);
  color: var(--white);
}

.ask-speak.is-loading {
  opacity: 0.5;
}

.ask-speak.is-playing svg {
  animation: talkbob 0.5s ease-in-out infinite;
}

.round-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Lets the swipe card claim whatever vertical room .round-area has left
   below Bella's ask-bubble, instead of sizing to its own content. Scoped
   to this one layout — every other round sizes to its content and relies
   on .round-area's own flex:1 for the leftover space below it. */
.round-body-swipe {
  flex: 1;
}

/* ---- feedback sheet (pinned under the options) ---- */
.round-feedback {
  position: sticky;
  bottom: 0;
  margin: 16px -16px calc(-20px - var(--safe-bottom, 0px));
  padding: 28px 24px calc(28px + var(--safe-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: 24px;
  border-top-left-radius: var(--r-lg);
  border-top-right-radius: var(--r-lg);
  box-shadow: 0 -10px 24px rgba(13, 6, 91, 0.1);
  z-index: 90;
  /* .fb-good/.fb-hint paint their own tone; a toneless bar (the compose
     round's sticky "send" button, before any answer) needs a background of
     its own so it doesn't read as a transparent floating button. */
  background: var(--white);
}

.round-feedback[hidden] {
  display: none;
}

.fb-good {
  background: #E4F8ED;
}

.fb-hint {
  background: #FFF1F5;
}

.fb-head {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.fb-mark {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--white);
}

.fb-mark svg {
  width: 24px;
  height: 24px;
  stroke-width: 2.5;
}

/* Botto's own confused character, embedded in the compose round's wrong-try
   drawer (see .fb-mark above for the generic version every other layout
   uses) — full size, no colored circle badge, since he already has his own
   colors and reads oddly forced into a flat icon disc. */
.fb-botto {
  flex-shrink: 0;
  width: 64px;
}

/* The compose round's send bar and its wrong-try state share this row so
   Botto sits in the exact same spot in both — only his face and what's
   next to him change. */
.fb-compose-row {
  display: flex;
  align-items: center;
  gap: 14px;
}

.fb-compose-row .btn {
  flex: 1;
}

.fb-good .fb-mark {
  background: var(--good);
}

.fb-hint .fb-mark {
  background: var(--bad);
}

.fb-copy strong {
  display: block;
  font-size: var(--fs-2xl);
  font-weight: 800;
  letter-spacing: -0.01em;
  margin-bottom: 8px;
}

.fb-good .fb-copy strong {
  color: #0C7A45;
}

.fb-hint .fb-copy strong {
  color: #B3255C;
}

.fb-copy p {
  font-size: var(--fs-xl);
  line-height: 1.55;
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* Names the setup a first-timer can't see. Sits with the buttons it
   describes, not up in Bella's bubble. */
.task-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 4px;
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--ink-soft);
  text-align: center;
}

/* generic option feedback */
.opt {
  transition: transform 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}

.opt:active {
  transform: scale(0.95);
}

.opt-wrong {
  animation: shake 0.45s ease;
  border-color: var(--bad) !important;
}

.opt-right {
  border-color: var(--good) !important;
  box-shadow: 0 0 0 4px rgba(20, 184, 107, 0.25), var(--shadow-sm) !important;
}

/* icon grid */
.opt-grid-icons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

/* Reads as a physical key: outline + a solid bottom edge that the
   press "squashes". These options are deliberately label-free — the
   whole skill is reading the picture — so every hint that they're
   tappable has to come from the shape. */
.opt-icon {
  background: var(--white);
  border-radius: var(--r-md);
  border: 2px solid #DCD8F0;
  aspect-ratio: 1.35;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 0 #CBC5E6;
  color: var(--ink);
}

.opt-icon:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 #CBC5E6;
}

@media (hover: hover) {
  .opt-icon:hover {
    border-color: var(--comp-purple);
    background: #FBFAFF;
  }
}

.opt-icon-art>svg {
  width: 84px;
  height: 84px;
}

/* A real image (PNG or a standalone .svg file, e.g. "<img src=\"...\">"
   in "icon" instead of inline <svg> markup) is the picture itself, not
   a small symbol next to a caption — so unlike the hand-drawn line-art
   icons it fills nearly the whole card, scaling with it, with no label
   needed underneath (the image already reads on its own). */
.opt-icon-art>img {
  width: calc(100% - 16px);
  height: calc(100% - 16px);
  object-fit: contain;
}

/* Labelled variant (round opts in with "showLabels": true): stack the
   picture over a short caption instead of the picture standing alone. */
.opt-grid-icons-labelled .opt-icon {
  aspect-ratio: auto;
  flex-direction: column;
  gap: 6px;
  padding: 14px 10px 12px;
}

.opt-grid-icons-labelled .opt-icon-art>svg {
  width: 76px;
  height: 76px;
}

/* Real images stay bigger than the line-art icons even with a caption
   below them -- the caption is a quick label, not the main content, so
   the picture should fill nearly the whole card width (the card's
   height is auto, not fixed, so it just grows to fit). */
.opt-grid-icons-labelled .opt-icon-art>img {
  width: calc(100% - 8px);
  height: auto;
  object-fit: contain;
}

.opt-grid-icons-labelled .opt-icon-label {
  font-size: 13px;
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
  text-align: center;
}

/* posters / answer cards / prompt cards */
.opt-col {
  display: flex;
  flex-direction: column;
  gap: 12px;
  counter-reset: opt-count;
}

.poster {
  border-radius: var(--r-md);
  border: 3px solid rgba(13, 6, 91, 0.1);
  padding: 22px 16px;
  font-weight: 700;
  font-size: var(--fs-2xl);
  letter-spacing: 0.01em;
  box-shadow: var(--shadow-sm);
  text-align: center;
  line-height: 1.25;
}

.poster-tiny {
  font-size: var(--fs-xs);
  font-weight: 500;
}

.poster-big {
  font-size: var(--fs-3xl);
}

/* Scattered sticky-note board (opt-scatter, used when poster.sticky is
   set): square notes in a loose wrap, not a neat list -- each note gets
   its own tilt AND vertical nudge (nth-child, cycling every 6) so the
   whole board reads as haphazardly stuck up, not aligned. */
.opt-scatter {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 22px 16px;
  padding: 14px 6px 6px;
}

/* Sticky-note variant (poster.sticky: true) -- a plakbriefje on a
   corkboard, not a UI button: square, no border, big handwritten text,
   a folded-corner shadow, and a paper-like offset shadow instead of the
   flat button one. --stick-rot/--stick-y carry the scatter so :active
   can compose them with the press scale instead of transforms fighting. */
.poster-sticky {
  --stick-rot: -3deg;
  --stick-y: 0px;
  transform: rotate(var(--stick-rot)) translateY(var(--stick-y));
  position: relative;
  flex: 0 0 42%;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  border: none;
  border-radius: 2px;
  font-family: "Bradley Hand", "Segoe Print", "Comic Sans MS", cursive;
  font-size: 1.55em;
  font-weight: 700;
  line-height: 1.15;
  box-shadow: 2px 3px 0 rgba(13, 6, 91, 0.05), 3px 7px 12px rgba(13, 6, 91, 0.22);
}

.poster-sticky:nth-child(6n+1) { --stick-rot: -6deg; --stick-y: 8px; }
.poster-sticky:nth-child(6n+2) { --stick-rot: 4deg; --stick-y: -10px; }
.poster-sticky:nth-child(6n+3) { --stick-rot: -3deg; --stick-y: 12px; }
.poster-sticky:nth-child(6n+4) { --stick-rot: 7deg; --stick-y: -6px; }
.poster-sticky:nth-child(6n+5) { --stick-rot: -5deg; --stick-y: 2px; }
.poster-sticky:nth-child(6n) { --stick-rot: 3deg; --stick-y: -8px; }

.poster-sticky:active {
  transform: rotate(var(--stick-rot)) translateY(var(--stick-y)) scale(0.95);
}

@media (hover: hover) {
  .poster-sticky:hover {
    transform: rotate(0deg) translateY(var(--stick-y)) scale(1.06);
  }
}

.poster-sticky::after {
  content: '';
  position: absolute;
  right: 0;
  bottom: 0;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0 0 16px 16px;
  border-color: transparent transparent rgba(13, 6, 91, 0.18) transparent;
}
.answers-card {
  background: var(--white);
  border-radius: var(--r-md);
  border: 3px solid transparent;
  padding: 14px 16px;
  box-shadow: var(--shadow-sm);
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.answers-title {
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.answers-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--fs-md);
  font-weight: 500;
}

.ans-dot {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 700;
  font-size: var(--fs-md);
}

.opt-prompt {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--white);
  border-radius: var(--r-md);
  border: 2px solid #DCD8F0;
  box-shadow: 0 4px 0 #CBC5E6;
  color: var(--ink);
  padding: 14px 16px;
  font-size: 1em;
  line-height: 1.45;
  font-weight: 500;
  text-align: left;
  text-wrap: pretty;
  transition: transform 0.1s, box-shadow 0.1s, border-color 0.12s, background 0.12s;
  cursor: pointer;
}

.opt-prompt::before {
  counter-increment: opt-count;
  content: counter(opt-count, upper-alpha);
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--surface-alt);
  border: 1.5px solid #DCD8F0;
  color: var(--purple);
  font-size: var(--fs-base);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.opt-prompt:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 #CBC5E6;
}

@media (hover: hover) {
  .opt-prompt:hover {
    border-color: var(--comp-purple);
    background: #FBFAFF;
  }

  .opt-prompt:hover::before {
    background: var(--purple);
    border-color: var(--purple);
    color: var(--white);
  }
}

.opt-right.opt-prompt::before {
  background: var(--good) !important;
  border-color: var(--good) !important;
  color: var(--white) !important;
}

.opt-wrong.opt-prompt::before {
  background: var(--bad) !important;
  border-color: var(--bad) !important;
  color: var(--white) !important;
}

.opt-prompt-quote {
  color: var(--comp-purple);
  font-weight: 700;
  font-size: var(--fs-lg);
}

/* Chef Botto */
.botto-stage {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 2px;
}

.botto {
  width: 88px;
  flex-shrink: 0;
}

.botto-out {
  flex: 1;
  background: var(--white);
  border-radius: var(--r-md);
  border-bottom-left-radius: 4px;
  padding: 12px 14px;
  font-size: 1.1em;
  line-height: 1.45;
  font-weight: 500;
  box-shadow: var(--shadow-sm);
  min-height: 58px;
  display: flex;
  align-items: center;
  transition: background 0.25s;
}

.botto-out-good {
  background: var(--bg-green);
}

.botto-out-bad {
  background: var(--bg-red);
}

/* fix-it lab */
.fixit-preview {
  border-radius: var(--r-lg);
  border: 3px solid rgba(13, 6, 91, 0.1);
  min-height: 130px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-sm);
}

.fixit-btn {
  font-size: var(--fs-3xl);
  font-weight: 700;
  letter-spacing: 0.12em;
  padding: 16px 42px;
  border-radius: var(--r-md);
  border: 3px solid currentColor;
  transition: color 0.3s ease;
  pointer-events: none;
}

.fixit-hint {
  text-align: center;
  font-size: var(--fs-base);
  color: var(--ink-soft);
  font-weight: 500;
}

.swatch-row {
  display: flex;
  justify-content: center;
  gap: 16px;
}

.swatch {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  border: 4px solid var(--white);
  box-shadow: var(--shadow-md);
}

/* swipe — one short card, judge it JA/NEE, it flies off on a correct guess.
   The stage fills whatever vertical room .round-area has left below Bella's
   ask-bubble (flex:1, see .round-area), so the card reads as the main event
   of the screen rather than a small tile floating among other UI. */
.swipe-stage {
  position: relative;
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: stretch;
  min-height: 260px;
  padding: 6px 2px 4px;
}

/* Two static, content-less cards peeking out behind the live one — a
   cheap "this is a deck, there's more where that came from" cue that
   costs nothing to keep in sync since it never carries real content. */
.swipe-card-ghost {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 92%;
  height: 94%;
  max-width: 400px;
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  transition: transform 0.25s ease;
}

.swipe-card-ghost-1 {
  opacity: 0.7;
  transform: scale(0.96) translateY(10px) rotate(-1.5deg);
}

.swipe-card-ghost-2 {
  opacity: 0.4;
  transform: scale(0.9) translateY(20px) rotate(1.5deg);
}

.swipe-card {
  position: relative;
  z-index: 2;
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  padding: 32px 26px;
  width: 100%;
  max-width: 400px;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  text-align: center;
  transition: transform 0.35s ease, opacity 0.35s ease;
  touch-action: pan-y;
  cursor: grab;
  user-select: none;
}

/* One-time settle-in on mount: pops up from slightly small/low and rocks
   once, so a still card reads as "just placed here, and grabbable" —
   the same wordless cue a real card deck gives when you deal from it. */
@keyframes swipeCardIn {
  0% { transform: scale(0.9) translateY(18px); opacity: 0; }
  60% { transform: scale(1.02) rotate(0deg); opacity: 1; }
  75% { transform: rotate(-3deg); }
  90% { transform: rotate(2deg); }
  100% { transform: none; opacity: 1; }
}

.swipe-card.anim-swipe-in {
  animation: swipeCardIn 0.55s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}

.swipe-card.grabbing {
  cursor: grabbing;
  box-shadow: var(--shadow-sm);
  transition: none;
}

.swipe-card.fly-left {
  transform: translateX(-160%) rotate(-18deg);
  opacity: 0;
}

.swipe-card.fly-right {
  transform: translateX(160%) rotate(18deg);
  opacity: 0;
}

/* Tinder-style corner stamps: fade/scale in as a drag commits toward that
   side. They only ever echo the label its own button already shows, so
   this can't leak which side is actually correct — it's the same
   information, just felt during the drag instead of read after it. */
.swipe-stamp {
  position: absolute;
  top: 20px;
  z-index: 3;
  max-width: 34%;
  padding: 5px 10px;
  border: 3px solid var(--purple);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1.15;
  text-align: center;
  color: var(--purple);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.1s linear;
}

.swipe-stamp-no {
  left: 14px;
  transform: rotate(-12deg);
}

.swipe-stamp-yes {
  right: 14px;
  transform: rotate(12deg);
}

.swipe-card-icon {
  width: 96px;
  height: 96px;
  color: var(--purple);
}

/* A lucide <i> renders with explicit width="24" height="24" attributes,
   which beat the span's own size (that only bounds an unsized custom
   <svg>) -- force any icon, lucide or custom, to fill the span. */
.swipe-card-icon svg {
  width: 100%;
  height: 100%;
}

.swipe-card-text {
  font-size: var(--fs-xl);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.4;
}

/* Same sticky-bottom-drawer treatment as .mission-actions/.reward-actions —
   pinned, edge-to-edge off .mission-stage's 16px side padding, rounded top
   corners, safe-area aware — so this bar sits and behaves exactly like
   every other bottom bar in the app instead of inventing its own look.
   The negative bottom margin cancels out .round-area's own 24px
   padding-bottom (reserved for scroll slack on other layouts) — without
   it, this bar's containing block ends 24px short of the true viewport
   bottom and it can never stick past that, same reason .round-feedback
   needs the identical trick. */
.swipe-controls {
  position: sticky;
  bottom: 0;
  z-index: 90;
  display: flex;
  gap: 12px;
  margin: 16px -16px calc(-24px - var(--safe-bottom, 0px));
  padding: 16px 20px calc(20px + var(--safe-bottom, 0px));
  background: var(--white);
  box-shadow: 0 -10px 20px rgba(0, 0, 0, 0.05);
  border-top-left-radius: var(--r-lg);
  border-top-right-radius: var(--r-lg);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  transition: transform 0.25s ease, opacity 0.2s ease;
}

/* The round always resolves on the tap/drag that triggers this — hide
   the bar right away instead of letting it sit under #round-feedback,
   which slides up over the same strip of screen a beat later. */
.swipe-controls.is-resolved {
  transform: translateY(100%);
  opacity: 0;
  pointer-events: none;
}

/* Same neutral look for both directions — deliberately no red/green or
   check/x. Color-coding JA green and NEE red taught kids "tap green" by
   traffic-light instinct, independent of whether JA was actually correct
   that round. Correctness is only ever revealed after the tap (shake vs.
   fly-off), same as every other layout — the buttons themselves carry no
   hint, only the option's own text does. */
.swipe-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-weight: 700;
  font-size: var(--fs-md);
  padding: 16px 18px;
  border-radius: var(--r-md);
  background: var(--white);
  color: var(--ink);
  border: 2px solid #DCD8F0;
  box-shadow: 0 4px 0 #CBC5E6;
}

/* compare-choose — two whole reference designs, no correct/wrong; tap
   an attribute chip under either one to feed it into the live remix. */
.cc-variants {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 12px;
}

.cc-variant {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.cc-variant-label {
  align-self: flex-start;
  font-weight: 700;
  font-size: var(--fs-base);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* The artboard holds ONLY the design preview — the tappable chip legend
   lives outside it, so the bordered "this is the design" card never
   visually includes controls that aren't part of the design itself. */
.cc-artboard {
  width: 100%;
  background: var(--white);
  border-radius: var(--r-md);
  border: 2px solid #E7E2F5;
  padding: 14px;
  box-shadow: var(--shadow-sm);
  display: flex;
  justify-content: center;
}

.cc-btn-preview {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 100%;
  max-width: 220px;
  padding: 13px 18px;
  border-radius: var(--r-md);
  font-weight: 700;
  font-size: var(--fs-md);
  box-shadow: var(--shadow-sm);
}

.cc-btn-icon svg {
  width: 18px;
  height: 18px;
}

/* Only the two reference designs get this — never the remix preview at
   the bottom, which is a display, not an input. The translucent patch
   is what tells a kid the icon/text are each their own tap target and
   not just decoration inside one big button. */
.cc-hot {
  border-radius: 8px;
  transition: background 0.12s ease;
}

.cc-btn-icon.cc-hot {
  background: rgba(255, 255, 255, 0.22);
  padding: 4px 6px;
}

.cc-btn-text.cc-hot {
  padding: 4px 6px;
}

@media (hover: hover) {
  .cc-hot:hover {
    background: rgba(255, 255, 255, 0.35);
  }
}

.cc-remix {
  background: var(--white);
  border-radius: var(--r-lg);
  border: 2.5px solid var(--purple);
  padding: 16px;
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

/* width:100% for the same reason .cc-remix-preview-wrap needs it below —
   .cc-remix's align-items:center shrinks a plain block child to fit its
   content otherwise, which would center this row instead of spreading
   the label and reset button to opposite edges. */
.cc-remix-head {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.cc-remix-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 700;
  font-size: var(--fs-base);
  letter-spacing: 0.03em;
  color: var(--purple-dark);
}

.cc-reset-btn {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(13, 6, 91, 0.06);
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: opacity 0.12s ease;
}

.cc-reset-btn svg {
  width: 14px;
  height: 14px;
}

.cc-reset-btn:disabled {
  opacity: 0.35;
}

/* .cc-remix's align-items:center shrinks a plain block child to its
   content width — without this, #cc-remix-preview's button never gets
   the same full-width-to-expand-into that .cc-artboard gives the two
   reference designs, so it renders narrower even though both share the
   same max-width:220px cap. */
.cc-remix-preview-wrap {
  width: 100%;
  display: flex;
  justify-content: center;
}

.cc-remix-label svg {
  width: 15px;
  height: 15px;
}

/* Only shown before the very first pick — once anything is chosen the
   remix renders for real (picked pieces solid, untouched ones simply
   absent), so this dashed/placeholder look never mixes with real
   picked colors. */
.cc-btn-preview-empty {
  background: #F3EFFD;
  color: var(--ink-soft);
  border: 2px dashed var(--comp-purple);
  box-shadow: none;
}

/* creation-example — the sample drawing on a creationPhase's Bella-intro
   step (#353), before the real canvas opens. */
.creation-example {
  display: flex;
  justify-content: center;
  padding: 12px 0 4px;
}

.creation-example-img {
  max-width: 240px;
  width: 100%;
  border-radius: var(--r-md);
  border: 2px solid #E7E2F5;
  background: var(--white);
  box-shadow: var(--shadow-sm);
}

/* pattern — tap-to-match pairs, no dragging */
.pattern-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.pattern-card {
  display: flex;
  align-items: center;
  gap: 9px;
  background: var(--white);
  border-radius: var(--r-md);
  border: 2px solid #DCD8F0;
  box-shadow: 0 4px 0 #CBC5E6;
  padding: 12px;
  text-align: left;
}

.pattern-card-icon {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--bg-purple);
  color: var(--purple);
  display: flex;
  align-items: center;
  justify-content: center;
}

.pattern-card-icon svg {
  width: 15px;
  height: 15px;
}

.pattern-card-label {
  font-size: 1.2em;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.3;
  text-wrap: pretty;
}

/* Neutral "picked, not yet confirmed" state — purple, the app's
   generic selected-state color, never green/red (those stay reserved
   for the confirmed right/wrong result once the second card is tapped). */
.pattern-card-picked {
  border-color: var(--purple) !important;
  box-shadow: 0 0 0 3px rgba(95, 41, 255, 0.18), var(--shadow-sm) !important;
}

/* paired layout — icon buttons on the left, their meanings on the right */
.pattern-grid-paired {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 12px;
  align-items: start;
}

.pattern-col {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.pattern-card-left {
  justify-content: center;
  min-height: 96px;
  padding: 14px 20px;
}

.pattern-card-left .pattern-card-icon {
  width: auto;
  height: auto;
  background: none;
}

.pattern-card-left .pattern-card-icon svg {
  width: 60px;
  height: 60px;
}

.pattern-card-right {
  min-height: 96px;
}

/* compose */

/* The sentence she is building, as one readable line that fills in as she
   picks. The stem used to live only in .compose-group-name below, which is
   uppercase, small and muted: it read as a section label, so a 9-year-old
   never saw she was building a sentence at all. */
.compose-sentence {
  background: var(--white);
  border-radius: var(--r-md);
  padding: 14px 16px;
  box-shadow: var(--shadow-sm);
  margin-bottom: 12px;
  font-size: var(--fs-md);
  font-weight: 700;
  line-height: 1.7;
  color: var(--ink);
}

.cs-stem {
  margin-right: 6px;
}

.cs-slot {
  display: inline-block;
  min-width: 58px;
  margin-right: 6px;
  border-bottom: 3px dashed var(--purple);
  color: var(--purple);
}

.cs-slot.cs-filled {
  border-bottom-style: solid;
}

.compose-groups {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 4px;
}

.compose-group {
  background: var(--white);
  border-radius: var(--r-md);
  padding: 12px 14px;
  box-shadow: var(--shadow-sm);
}

.compose-group-name {
  font-weight: 700;
  font-size: var(--fs-base);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 8px;
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chip {
  background: var(--bg-purple);
  border: 2.5px solid transparent;
  border-radius: var(--r-pill);
  padding: 9px 14px;
  font-size: 1em;
  font-weight: 500;
  transition: all 0.12s ease;
}

.chip:active {
  transform: scale(0.93);
}

.chip-on {
  background: var(--comp-yellow);
  border-color: var(--ink);
  font-weight: 700;
}

/* ---------- Phone mock ----------
   A round screen has to read as a phone at a glance (Renate's note): real
   device chrome — status bar, dynamic island, home bar — inside a portrait
   frame, with the app's own UI (top bar, list rows, promo card, buttons)
   sitting below it so a kid knows what kind of screen they're looking at. */
.phone {
  --phone-frame: #0A0630;
  width: 100%;
  max-width: 288px;
  margin: 0 auto;
  background: var(--phone-frame);
  border-radius: 46px;
  padding: 7px;
  box-shadow: 0 0 0 2px rgba(13, 6, 91, 0.10), var(--shadow-md);
}

.phone-screen {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 460px;
  padding-bottom: 22px;
  background: var(--white);
  border-radius: 40px;
  overflow: hidden;
}

/* Dynamic island — the single strongest "this is a modern phone" cue. */
.phone-island {
  position: absolute;
  top: 9px;
  left: 50%;
  transform: translateX(-50%);
  width: 78px;
  height: 22px;
  background: var(--phone-frame);
  border-radius: var(--r-pill);
  z-index: 4;
}

.phone-statusbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 9px 20px 6px;
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
}

.phone-clock {
  letter-spacing: 0.02em;
}

.phone-signal {
  display: flex;
  align-items: center;
  gap: 4px;
}

.phone-signal svg {
  width: 14px;
  height: 14px;
}

/* The app's own header — carries the app identity that used to sit,
   confusingly, on the metal bezel. */
.app-topbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 16px 10px;
  border-bottom: 1px solid var(--bg-purple);
}

.app-topbar-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  background: var(--bg-purple);
  color: var(--purple);
}

.app-topbar-icon svg {
  width: 16px;
  height: 16px;
}

.app-topbar-name {
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--ink);
}

.phone-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 16px 18px;
  background: var(--white);
}

/* Home indicator. */
.phone-homebar {
  position: absolute;
  bottom: 7px;
  left: 50%;
  transform: translateX(-50%);
  width: 108px;
  height: 5px;
  border-radius: var(--r-pill);
  background: var(--ink);
  opacity: 0.28;
  z-index: 4;
}

.pp {
  border-radius: var(--r-sm);
  transition: all 0.25s ease;
  position: relative;
}

[data-part] {
  cursor: pointer;
}

/* iOS-style large title: the screen's heading, not a button. */
.pp-title {
  font-weight: 800;
  font-size: var(--fs-lg);
  letter-spacing: -0.01em;
  color: var(--ink);
  padding: 2px 0 2px;
}

/* A real list row: full-bleed, hairline divider, leading marker — not a
   floating pill that competes with the buttons. */
.pp-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 -16px;
  padding: 12px 16px;
  font-size: var(--fs-base);
  color: var(--ink-soft);
  border-bottom: 1px solid var(--bg-purple);
}

.pp-row::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--comp-blue);
}

.pp-img {
  font-size: 44px;
  text-align: center;
  padding: 12px;
  background: var(--bg-blue);
  border-radius: var(--r-sm);
}

/* An in-app announcement card — soft, branded, with an icon. (Was a
   yellow diagonal hazard stripe, which read as a warning sign.) */
.pp-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  background: var(--bg-purple);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font-weight: 600;
  font-size: var(--fs-sm);
  color: var(--ink);
  text-align: left;
}

.pp-banner-ico {
  flex: none;
  color: var(--purple);
}

.pp-banner-ico svg {
  display: block;
  width: 18px;
  height: 18px;
}

.pp-field {
  border: 2px solid var(--bg-blue);
  border-radius: var(--r-sm);
  padding: 11px 12px;
  font-size: var(--fs-base);
  color: var(--ink-soft);
  background: #FBFAFF;
}

.pp-ghost {
  color: #A7A3C8;
  font-size: var(--fs-xs);
  text-align: center;
  padding: 4px;
}

/* Buttons look tappable and 3D — clearly not list rows or cards. */
.pp-btn {
  width: 100%;
  padding: 13px 16px;
  border: 2px solid transparent;
  border-radius: var(--r-md);
  font-weight: 700;
  font-size: var(--fs-md);
  text-align: center;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.pp-btn:active {
  transform: translateY(2px);
}

.pp-btn-plain {
  background: var(--bg-purple);
  color: var(--ink);
  box-shadow: 0 3px 0 var(--comp-blue);
}

.pp-btn-primary,
.pp-btn-safe {
  background: var(--good);
  color: #fff;
  box-shadow: 0 4px 0 var(--good-dark);
}

.pp-btn-loud {
  background: var(--yellow);
  color: var(--ink);
  box-shadow: 0 4px 0 #D4D800;
}

.pp-btn-tiny {
  width: auto;
  align-self: center;
  padding: 7px 12px;
  background: var(--bg-purple);
  color: var(--ink-soft);
  font-size: var(--fs-xs);
  box-shadow: 0 2px 0 var(--comp-blue);
}

.pp-btn-danger {
  width: auto;
  align-self: center;
  padding: 9px 14px;
  background: var(--bad);
  color: #fff;
  font-size: var(--fs-sm);
  box-shadow: 0 3px 0 #D94666;
}

.pp-btn-calmed {
  background: var(--bg-purple) !important;
  color: var(--ink-soft) !important;
  font-size: var(--fs-sm) !important;
  padding: 9px !important;
  box-shadow: none !important;
}

.pp-btn-hero {
  background: var(--purple) !important;
  color: #fff !important;
  font-size: var(--fs-lg) !important;
  padding: 16px !important;
  width: 100% !important;
  box-shadow: 0 5px 0 var(--purple-dark) !important;
  animation: heropop 0.5s ease;
}

@keyframes heropop {
  0% {
    transform: scale(0.9);
  }

  55% {
    transform: scale(1.08);
  }

  100% {
    transform: scale(1);
  }
}

.pp-trapped {
  outline: 3px solid var(--bad);
  outline-offset: 2px;
  border-radius: var(--r-md);
}

.pp-row > span {
  flex: 1;
  min-width: 0;
}

/* ---------- Phone mock · app skins ----------
   screen.kind ("planner" | "tasks" | "settings" | "diary" | ...) dresses
   the mock as that kind of app: a matching top strip, a bottom tab bar,
   and part styling that reads as real agenda / task / settings rows.
   Decoration only — tappable parts and answer logic are untouched. */
.phone-screen {
  padding-bottom: 0;
}

.phone-footspace {
  flex: none;
  height: 26px;
  background: var(--white);
}

/* Bottom tab bar */
.phone-tabbar {
  display: flex;
  flex: none;
  padding: 7px 4px 16px;
  background: var(--white);
  border-top: 1px solid var(--bg-purple);
}

.phone-tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  font-size: 9.5px;
  font-weight: 600;
  color: #A7A3C8;
}

.phone-tab svg {
  width: 19px;
  height: 19px;
}

.phone-tab-on {
  color: var(--purple);
}

/* Planner · week strip */
.phone-datestrip {
  display: flex;
  flex: none;
  gap: 4px;
  padding: 10px 14px 12px;
  background: var(--white);
  border-bottom: 1px solid var(--bg-purple);
}

.phone-day {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.phone-day b {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #A7A3C8;
}

.phone-day i {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  font-style: normal;
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--ink);
}

.phone-day-on b {
  color: var(--purple);
}

.phone-day-on i {
  background: var(--purple);
  color: #fff;
}

/* Planner · agenda rows read as event blocks with a coloured spine */
.phone--planner .pp-row {
  margin: 0;
  padding: 11px 12px;
  background: #FAFAFF;
  border: 1px solid var(--bg-purple);
  border-left: 4px solid var(--purple);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-weight: 600;
  color: var(--ink);
}

.phone--planner .pp-row::before {
  display: none;
}

/* Tasks · rows read as to-do items with a checkbox */
.phone--tasks .pp-row::before {
  width: 16px;
  height: 16px;
  border: 2px solid var(--comp-blue);
  border-radius: 5px;
  background: none;
}

/* Settings · grouped list rows with a chevron */
.phone--settings .phone-body {
  gap: 0;
}

.phone--settings .pp-title {
  padding-bottom: 8px;
}

.phone--settings .pp-row,
.phone--settings .pp-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  margin: 0;
  padding: 13px 2px;
  border: 0;
  border-bottom: 1px solid var(--bg-purple);
  border-radius: 0;
  background: none;
  box-shadow: none;
  font-weight: 600;
  font-size: var(--fs-base);
  color: var(--ink);
  text-align: left;
}

.phone--settings .pp-row::before {
  display: none;
}

.phone--settings .pp-setrow-chev {
  flex: none;
  color: #A7A3C8;
}

.phone--settings .pp-setrow-chev svg {
  display: block;
  width: 16px;
  height: 16px;
}

/* ---------- Phone mock · design flaws ----------
   part.flaw renders one real, spottable UX defect on an otherwise-correct
   screen — for "tik op wat er niet klopt" rounds (mark that part correct,
   mission mode "spot"). Realistic, not cartoonish. */
.pp-flaw-tiny {
  align-self: flex-start;
  width: auto !important;
  padding: 3px 8px !important;
  font-size: var(--fs-xs) !important;
  box-shadow: none !important;
}

.pp-flaw-low-contrast,
.pp-flaw-low-contrast > span {
  color: rgba(13, 6, 91, 0.26) !important;
}

.pp-flaw-clashing {
  border-radius: 2px !important;
  background: #16E0D0 !important;
  color: #05323C !important;
  font-family: Georgia, "Times New Roman", serif !important;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  box-shadow: none !important;
}

.pp-flaw-buried {
  align-self: flex-end;
  width: auto !important;
  padding: 2px !important;
  border: 0 !important;
  background: none !important;
  box-shadow: none !important;
  color: var(--ink-soft) !important;
  font-size: var(--fs-xs) !important;
  font-weight: 500 !important;
  text-decoration: underline;
}

.pp-flaw-misaligned {
  align-self: flex-start;
  width: auto !important;
  margin-left: 24px;
  transform: rotate(-1.2deg);
}

.pp-flaw-disabled-look {
  opacity: 0.4 !important;
  filter: grayscale(0.65);
  box-shadow: none !important;
}

.pp-flaw-shouting {
  background: var(--bad) !important;
  color: #fff !important;
  font-size: var(--fs-lg) !important;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  box-shadow: 0 4px 0 #C7364F !important;
}

.trap-tag {
  position: absolute;
  top: -12px;
  right: -6px;
  z-index: 3;
  background: var(--bad);
  color: #fff;
  font-size: var(--fs-xs);
  font-weight: 700;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-sm);
}

.trap-villain {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: center;
  font-size: var(--fs-base);
  font-weight: 700;
  color: var(--ink-soft);
  font-style: italic;
}

.trap-villain svg {
  width: 54px;
  height: 54px;
}

.hint-toggle {
  margin: 4px auto 0;
  font-size: var(--fs-base);
  font-weight: 700;
  color: var(--purple);
  background: var(--white);
  padding: 10px 18px;
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-sm);
  max-width: 100%;
}

.hint-open {
  color: var(--ink);
  font-weight: 500;
  font-style: italic;
}

/* ---------- Hunt (Crasher-jacht) ----------
   A real web page screenshot in a phone browser frame, with invisible
   tap areas over each planted flaw. Wider than the app mock (.phone is
   capped at 288px) so body text on the page stays readable. */
/* "Groot scherm": sits on the corner of the screen it enlarges, like a
   video's fullscreen button (proximity: the control lives on its object),
   in the game's tactile purple button style. */
.hunt-expand {
  position: absolute;
  top: -10px;
  right: -10px;
  z-index: 5;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--purple);
  color: var(--white);
  box-shadow: 0 4px 0 var(--purple-dark), var(--shadow-md);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.hunt-expand:hover {
  transform: translateY(-1px);
}

.hunt-expand:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--purple-dark), var(--shadow-sm);
}

.hunt-expand:focus-visible {
  outline: 3px solid var(--yellow);
  outline-offset: 3px;
}

.hunt-expand svg {
  width: 22px;
  height: 22px;
  stroke-width: 2.5;
}

.hunt.is-expanded .hunt-expand {
  display: none;
}

/* Sticky bottom bar: the count next to the finish button. */
.round-feedback.fb-hunt-bar {
  flex-direction: row;
  align-items: center;
  gap: 16px;
  padding: 14px 20px calc(14px + var(--safe-bottom, 0px));
}

.fb-hunt-bar .hunt-count {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-base);
  line-height: 1.35;
  color: var(--ink);
}

.fb-hunt-bar .btn-big {
  width: auto;
  flex: none;
  padding: 14px 22px;
}

/* Small secondary controls (copy link, big screen): quiet pills, never
   competing with the page itself. */
.hunt-tool {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: var(--r-pill);
  background: var(--white);
  color: var(--purple);
  font-size: var(--fs-sm);
  font-weight: 700;
  white-space: nowrap;
  box-shadow: var(--shadow-sm);
}

.hunt-tool svg {
  width: 15px;
  height: 15px;
}


.phone--hunt {
  position: relative;
  max-width: 360px;
  margin-top: 14px;
}

.phone--hunt .phone-screen {
  min-height: 0;
  padding-bottom: 22px;
}

.hunt-urlbar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 0 12px 8px;
  padding: 7px 12px;
  border-radius: var(--r-pill);
  background: #F1F0F7;
  font-size: 12px;
  color: var(--ink-soft);
}

.hunt-urlbar svg {
  width: 12px;
  height: 12px;
}

a.hunt-urlbar {
  text-decoration: none;
}

.hunt-url-go {
  color: var(--purple);
}

.hunt-topbar {
  display: none;
}

/* Phones and touch screens: ONE scroll. The phone grows to the full height
   of the web page and the whole screen scrolls, instead of the page
   scrolling inside the phone inside a scrolling screen (thumbs got stuck
   inside the phone, or couldn't get it into view). The big screen keeps its
   own scroll. */
@media (max-width: 600px), (pointer: coarse) {
  .hunt:not(.is-expanded) .hunt-scroll {
    max-height: none;
    overflow: visible;
  }

  .hunt:not(.is-expanded) .hunt-scroll-cue {
    display: none;
  }
}

/* ---------- Hunt · Groot scherm ----------
   Everything but the page goes away; the page gets as big as the screen
   allows (capped at a comfortable reading width) and scrolls. Cards and the
   sticky bottom bar still show, fixed to the bottom above it. */
body.hunt-expanded {
  overflow: hidden;
}

.hunt.is-expanded .phone--hunt {
  position: fixed;
  inset: 0;
  z-index: 1000;
  max-width: none;
  margin: 0;
  padding: 0;
  border-radius: 0;
  background: var(--bg-purple);
  box-shadow: none;
}

.hunt.is-expanded .phone-screen {
  height: 100%;
  min-height: 0;
  padding-bottom: 0;
  border-radius: 0;
  background: transparent;
}

.hunt.is-expanded .phone-island,
.hunt.is-expanded .phone-statusbar,
.hunt.is-expanded .phone-homebar,
.hunt.is-expanded .hunt-scroll-cue {
  display: none;
}

.hunt.is-expanded .hunt-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 10px;
  background: var(--white);
  box-shadow: 0 4px 16px rgba(13, 6, 91, 0.08);
  font-size: var(--fs-base);
  color: var(--ink);
}

.hunt.is-expanded .hunt-topbar .hunt-tool {
  box-shadow: none;
  background: var(--bg-purple);
}

/* "Klein scherm" sits top-right, where the expand button was, and in the
   same filled purple so the pair reads as one on/off control. */
.hunt.is-expanded .hunt-topbar .hunt-collapse {
  background: var(--purple);
  color: var(--white);
  box-shadow: 0 3px 0 var(--purple-dark);
}

.hunt.is-expanded .hunt-urlbar {
  flex: none;
  width: min(100% - 32px, 760px);
  margin: 14px auto 10px;
  background: var(--white);
}

.hunt.is-expanded .hunt-scroll {
  flex: 1;
  min-height: 0;
  max-height: none;
  padding: 0 16px 180px;
  background: transparent;
}

.hunt.is-expanded .hunt-page {
  max-width: 760px;
  margin: 0 auto;
  border-radius: 12px;
  background: var(--white);
  box-shadow: var(--shadow-md);
}

.hunt.is-expanded .hunt-page img {
  border-radius: 12px;
}

/* The big screen is for hunting only: no "Ik ben klaar" bar there (the
   count sits in the top bar); right/wrong cards do still show. */
body.hunt-expanded .round-feedback.fb-hunt-bar {
  display: none;
}

body.hunt-expanded .round-feedback {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1001;
  max-width: 820px;
  margin: 0 auto;
}

/* On the non-expanded hunt page the whole screen scrolls (one scroll, no
   nested scroll inside the phone frame on touch). position:sticky on both
   the top bar and finish bar can lag a frame on iOS Safari when the address
   bar hides/shows, briefly exposing the html background. Same fix as the
   map's .hud-wrap: pin both to the viewport with position:fixed.

   Top bar: same left:0/right:0/max-width/margin:auto centering pattern;
   padding rebuilt here to replace what .mission-stage normally provides.
   The ::before scrim is made solid (no gradient fade) so the dense phone
   frame content can't bleed through it during the animation.

   Bottom bar: same centering; negative margin trick no longer needed since
/* In non-expanded hunt rounds, the finish bar is fixed to the bottom so
   the player can always finish without scrolling all the way to the end. */
body.hunt-round:not(.hunt-expanded) .round-feedback.fb-hunt-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  max-width: var(--appw);
  margin: 0 auto;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}

.hunt-scroll {
  max-height: min(60vh, 600px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--white);
}

.hunt-page {
  position: relative;
  line-height: 0;
}

.hunt-page img {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
}

.hunt-spot {
  position: absolute;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  cursor: pointer;
}

.hunt-spot:focus-visible {
  outline: 3px solid var(--purple);
  outline-offset: 2px;
}

.hunt-spot-found {
  outline: 3px solid var(--good);
  outline-offset: 1px;
  background: rgba(20, 184, 107, 0.10);
}

.hunt-spot-missed {
  outline: 2px dashed var(--bad);
  outline-offset: 1px;
  background: rgba(255, 92, 122, 0.08);
}

.hunt-pin {
  position: absolute;
  top: -10px;
  right: -8px;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--good);
  color: var(--white);
  box-shadow: var(--shadow-sm);
}

.hunt-pin svg {
  width: 14px;
  height: 14px;
  stroke-width: 3;
}

.hunt-toast {
  position: absolute;
  z-index: 3;
  transform: translate(-50%, -130%);
  padding: 5px 10px;
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--white);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  pointer-events: none;
  animation: hunt-toast 1.1s ease forwards;
}

@keyframes hunt-toast {
  0% { opacity: 0; margin-top: 6px; }
  15% { opacity: 1; margin-top: 0; }
  75% { opacity: 1; }
  100% { opacity: 0; }
}

.hunt-scroll-cue {
  position: absolute;
  bottom: 30px;
  left: 50%;
  transform: translateX(-50%);
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  color: var(--purple);
  box-shadow: var(--shadow-sm);
  pointer-events: none;
  transition: opacity 0.3s ease;
}

.hunt-scroll-cue.is-gone {
  opacity: 0;
}

.hunt-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
}

/* Rule card in the feedback sheet */
.round-feedback.fb-hunt {
  background: var(--white);
  border-top: 1px solid rgba(13, 6, 91, 0.08);
  box-shadow: 0 -10px 30px rgba(13, 6, 91, 0.12);
  padding-top: 22px;
  padding-bottom: calc(22px + var(--safe-bottom, 0px));
  gap: 16px;
}

/* Remove 40px mark offset and loud headers for hunt cards */
.fb-hunt .fb-mark {
  display: none;
}

.fb-hunt .fb-head {
  display: block;
  margin: 0;
  padding: 0;
}

.fb-hunt .fb-copy {
  display: block;
  width: 100%;
}

.fb-hunt .fb-copy strong {
  display: none;
}

.fb-hunt .fb-body {
  width: 100%;
}

/* Source links inside a teach text (the hunt's closing sheet points at
   real tools like a contrast checker) */
.fb-copy p a {
  color: var(--purple);
  font-weight: 700;
  text-underline-offset: 3px;
}

.hunt-card-content {
  width: 100%;
}

.hunt-rule-hero {
  margin-bottom: 6px;
}

.hunt-rule-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.hunt-rule-title {
  margin: 0;
  font-size: 22px;
  font-weight: 900;
  line-height: 1.25;
  color: var(--ink);
  letter-spacing: -0.015em;
  text-wrap: balance;
  flex: 1;
  min-width: 180px;
}

.hunt-badges {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.hunt-rule-sub {
  margin-top: 4px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-soft);
  line-height: 1.35;
}

.hunt-explanation {
  font-size: 15.5px;
  line-height: 1.5;
  color: var(--ink);
  margin: 10px 0 12px;
  text-wrap: pretty;
}

.hunt-lens {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.3;
}

.hunt-lens svg {
  width: 13px;
  height: 13px;
}

.hunt-lens-wcag { background: var(--bg-purple); color: var(--purple-dark); }
.hunt-lens-heuristiek { background: var(--bg-blue); color: var(--ink); }
.hunt-lens-principe { background: #DDF7EA; color: #0B6E40; }
.hunt-lens-wet { background: var(--bg-red); color: #B3255C; }

/* Sub-element diff pill when wrong rule was chosen */
.hunt-diff-note {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  background: rgba(179, 37, 92, 0.08);
  border: 1px solid rgba(179, 37, 92, 0.16);
  color: #8C1D48;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.3;
  margin: 6px 0 8px;
}

.hunt-diff-note svg {
  width: 13px;
  height: 13px;
  stroke-width: 2.5;
  flex-shrink: 0;
}

/* Ask rule question title */
.hunt-ask-header {
  font-size: 18px;
  font-weight: 800;
  color: var(--ink);
  margin-bottom: 12px;
  line-height: 1.3;
}

.hunt-source-wrap {
  margin-top: 8px;
}

.hunt-source-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px;
  border-radius: var(--r-pill);
  background: rgba(13, 6, 91, 0.06);
  border: 1px solid rgba(13, 6, 91, 0.12);
  color: var(--purple-dark);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  transition: background 0.15s ease, transform 0.1s ease;
}

.hunt-source-pill:hover,
.hunt-source-pill:focus-visible {
  background: rgba(13, 6, 91, 0.12);
  transform: translateY(-1px);
}

.hunt-source-pill svg {
  width: 14px;
  height: 14px;
}

.hunt-ask-opt {
  gap: 10px;
  font-size: 15px;
}

@media (max-width: 480px) {
  .hunt-rule-title {
    font-size: 19px;
  }
  .hunt-explanation {
    font-size: 14.5px;
  }
}

/* ---------- Order (flow) ---------- */
.order-slots {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.order-slot {
  border: 3px dashed var(--comp-blue);
  border-radius: var(--r-md);
  min-height: 60px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  background: rgba(255, 255, 255, 0.5);
  transition: border-color 0.2s;
}

.order-flow-done .order-slot {
  border-color: var(--good);
  border-style: solid;
}

.order-slot-num {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--ink);
  color: var(--yellow);
  font-weight: 700;
  font-size: var(--fs-md);
  display: flex;
  align-items: center;
  justify-content: center;
}

.order-tray {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 12px;
  min-height: 64px;
}

.order-card {
  background: var(--white);
  border: 3px solid transparent;
  border-radius: var(--r-md);
  padding: 10px 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  box-shadow: var(--shadow-sm);
  font-weight: 700;
  font-size: var(--fs-base);
  text-align: left;
  transition: transform 0.12s ease, opacity 0.15s ease, box-shadow 0.15s ease;
}

.order-card:active {
  transform: scale(0.94);
}

/* Placed into a slot — stays in its original tray spot (never reflows,
   so the remaining cards don't shift and a kid never has to re-scan for
   one that moved). Only its own content disappears; the box + shadow
   stay as a "this one's spoken for" placeholder. */
.order-card-used {
  box-shadow: none;
  border-style: dashed;
  border-color: #DCD8F0;
  background: rgba(255, 255, 255, 0.4);
  pointer-events: none;
}

.order-card-used .order-card-emoji,
.order-card-used .order-card-label {
  visibility: hidden;
}

.order-card-emoji {
  font-size: var(--fs-3xl);
}

/* A step can carry a small screen mockup instead of a single lucide icon
   (raw <svg>, same convention as .opt-icon-art) — constrain it to sit
   comfortably in the card row instead of filling it at native size. */
.order-card-emoji>svg {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
}

/* The slot's own independent rendering of whichever card is placed —
   not the tray button itself, so the tray copy can stay put untouched. */
.order-slot-content {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 24px;
}

.order-slot-content:not(:empty) {
  background: var(--white);
  border: 3px solid transparent;
  border-radius: var(--r-sm);
  padding: 8px 10px;
  font-weight: 700;
  font-size: var(--fs-base);
  text-align: left;
}

/* !important: .order-slot-content:not(:empty) sets background/border too,
   and its two-part selector otherwise outranks this plain class — same
   reason .opt-right needs it elsewhere. */
.order-card-right {
  border-color: var(--good) !important;
  background: var(--bg-green) !important;
}

/* Progressive hint (3rd+ miss on a round): glow points at the tray card
   that belongs in the earliest still-wrong slot, without labeling it as
   "correct" outright. */
.order-card-hint {
  animation: order-card-glow 1.1s ease-in-out infinite;
}

@keyframes order-card-glow {
  0%, 100% {
    box-shadow: var(--shadow-sm);
    border-color: transparent;
  }

  50% {
    box-shadow: 0 0 0 4px rgba(250, 255, 5, 0.6), var(--shadow-sm);
    border-color: var(--yellow);
  }
}

/* The teach card is now the .round-feedback sheet — see "feedback sheet". */

/* ---------- Reward ---------- */
/* No overflow:hidden here — that would make .reward-stage a scroll
   container of its own and break .reward-actions' position:sticky below
   (a sticky element only sticks against the nearest actual scrolling
   ancestor, which must stay the page). The burst graphic clips itself via
   .reward-burst-clip instead, so nothing needs the stage to do it. */
.reward-stage {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: var(--appw);
  margin-left: auto;
  margin-right: auto;
  /* Bottom padding is slack, not visual spacing: .reward-actions already
     pins itself to the true bottom via position:sticky, but when the
     badge/level-up rows above make this exactly (or barely under) one
     viewport tall, there's otherwise zero scroll distance available to
     correct for any mobile browser toolbar-height mismatch at first paint
     — same root cause as .round-area's own padding-bottom above. */
  padding: calc(30px + var(--safe-top)) 22px calc(20px + var(--safe-bottom, 0px));
  text-align: center;
  position: relative;
}

.reward-burst-clip {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.reward-burst {
  position: absolute;
  top: -140px;
  left: 50%;
  transform: translateX(-50%);
  width: 420px;
  height: 420px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(250, 255, 5, 0.5), transparent 65%);
  animation: nodepulse 2.4s infinite;
}

.reward-hero {
  width: 140px;
  position: relative;
  z-index: 2;
}

.reward-Buggie {
  display: flex;
  align-items: center;
  gap: 8px;
  z-index: 2;
  font-size: var(--fs-base);
  font-weight: 700;
  font-style: italic;
  color: var(--ink-soft);
  background: rgba(255, 255, 255, 0.75);
  border-radius: var(--r-pill);
  padding: 6px 14px 6px 6px;
  margin-top: 6px;
}

.reward-Buggie svg {
  width: 40px;
  height: 40px;
}

.reward-title {
  font-size: var(--fs-display);
  font-weight: 700;
  margin-top: 14px;
  z-index: 2;
}

.reward-stars {
  display: flex;
  gap: 10px;
  margin: 10px 0 16px;
  z-index: 2;
}

.star-big {
  font-size: 46px;
  color: #D8D4F0;
  transform: scale(0);
  animation: starpop 0.5s ease forwards;
}

.star-big svg {
  fill: currentColor;
}

.star-big.on {
  color: #FFB300;
  filter: drop-shadow(0 4px 0 rgba(13, 6, 91, 0.12));
}

@keyframes starpop {
  0% {
    transform: scale(0) rotate(-30deg);
  }

  70% {
    transform: scale(1.25) rotate(8deg);
  }

  100% {
    transform: scale(1) rotate(0);
  }
}

.reward-rows {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 2;
}

.reward-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--white);
  border-radius: var(--r-md);
  padding: 14px 18px;
  font-size: var(--fs-md);
  font-weight: 500;
  box-shadow: var(--shadow-sm);
}

.reward-row b {
  font-size: var(--fs-lg);
}

.reward-badge {
  display: flex;
  align-items: center;
  gap: 14px;
  text-align: left;
  background: var(--ink);
  color: var(--white);
  border-radius: var(--r-lg);
  padding: 16px;
  box-shadow: var(--shadow-md);
}

.reward-badge small {
  display: block;
  letter-spacing: 0.14em;
  font-size: var(--fs-xs);
  color: var(--comp-yellow);
  font-weight: 700;
}

.reward-badge b {
  font-size: var(--fs-xl);
  display: block;
}

.reward-badge i {
  font-size: var(--fs-sm);
  font-style: normal;
  opacity: 0.8;
}

.reward-badge-icon {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.reward-badge-icon svg {
  width: 30px;
  height: 30px;
}

.reward-level {
  background: var(--bg-yellow);
  color: var(--ink);
  font-weight: 700;
  font-size: var(--fs-md);
  border-radius: var(--r-md);
  padding: 14px;
  border: 2px dashed var(--yellow-dark);
  box-shadow: none;
}

/* Sticky like .mission-actions/.round-feedback, so it stays reachable when
   the badge/level-up/tip rows above push this taller than the viewport —
   margin-top:auto still pins it to the bottom on short reward screens,
   position:sticky keeps it pinned once the page actually scrolls.
   align-self:stretch (not width:100%) is what actually bleeds it to the
   stage's edges: .reward-stage centers its children (align-items:center),
   and centering an item's margin box is what the negative side margins
   below are relative to — with width:100% that box already matches the
   stage's content width, so centering a same-size box leaves it exactly
   where it started and the negative margins have nothing left to do.
   align-self:stretch instead lets those margins expand the box itself. */
.reward-actions {
  align-self: stretch;
  margin-top: auto;
  margin-left: -22px;
  margin-right: -22px;
  margin-bottom: calc(-20px - var(--safe-bottom, 0px));
  padding: 20px 22px calc(20px + var(--safe-bottom, 0px));
  position: sticky;
  bottom: 0;
  background: var(--white);
  box-shadow: 0 -10px 20px rgba(0, 0, 0, 0.05);
  border-top-left-radius: var(--r-lg);
  border-top-right-radius: var(--r-lg);
  z-index: 2;
}

/* Equal 50/50 split (the existing flex:1 from .btn-row .btn-big) is the
   default, same as "Stoppen" vs a short "Volgende →". white-space:nowrap
   is what lets a much longer CTA label ("Test je Prototype →") push past
   its half instead of wrapping — flex-shrink still applies, so "Stoppen"
   yields the extra room rather than the label breaking onto two lines. */
.reward-actions .btn-big {
  white-space: nowrap;
}

.reward-close {
  position: absolute;
  top: 16px;
  left: 16px;
  z-index: 10;
}

/* badge colors */
.bcolor-ocean {
  background: #2F6FD6;
  color: #fff;
}

.bcolor-sunset {
  background: #E06A1B;
  color: #fff;
}

.bcolor-berry {
  background: #D9427F;
  color: #fff;
}

.bcolor-galaxy {
  background: #6A3FE0;
  color: #fff;
}

.bcolor-forest {
  background: #2E9A62;
  color: #fff;
}

/* ---------- Pages (badges / me / summary) ---------- */
#screen-badges,
#screen-me {
  background: linear-gradient(180deg, #EFEAFC, #D6DCFF);
}

.page-wrap {
  position: relative;
  flex: 1;
  width: 100%;
  max-width: var(--appw);
  margin-left: auto;
  margin-right: auto;
  padding: calc(22px + var(--safe-top)) 22px calc(110px + var(--safe-bottom));
  display: flex;
  flex-direction: column;
}

/* ---- creator tips ----
   A tip is standalone creator content matched to the quest just finished.
   The card is enclosed and filled because it IS tappable — it opens the
   video overlay. */
.reward-tip {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  background: var(--white);
  border-radius: var(--r-md);
  padding: 10px 12px;
  box-shadow: var(--shadow-sm);
  transition: transform 0.12s ease;
}

.reward-tip:active {
  transform: scale(0.98);
}

.reward-tip-thumb {
  flex: 0 0 54px;
  height: 54px;
  border-radius: var(--r-sm);
  background-size: cover;
  background-position: center;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--white);
  background-color: var(--purple);
}

.reward-tip-thumb svg {
  width: 22px;
  height: 22px;
  filter: drop-shadow(0 1px 3px rgba(13, 6, 91, 0.6));
}

.reward-tip-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.reward-tip-body small {
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--purple);
}

.reward-tip-body b {
  font-size: var(--fs-md);
  color: var(--ink);
  line-height: 1.25;
}

.reward-tip>svg {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  color: var(--ink-soft);
}

.tip-overlay {
  z-index: 300;
}

.tip-player {
  width: min(92vw, 420px);
  background: var(--white);
  border-radius: var(--r-lg);
  overflow: hidden;
  position: relative;
}

/* openTip (app.js) plays this immediately in real fullscreen off the same
   tap that opened it — this inline sizing is only what's briefly visible
   during that hand-off, and the fallback for wherever fullscreen isn't
   available at all. Explicit aspect-ratio + object-fit:contain (every
   creator tip is a vertical short) rather than leaving the browser to
   guess a replaced element's size from a bare max-height, which is the
   ambiguous sizing that made this look letterboxed oddly before. */
.tip-player video {
  display: block;
  width: 100%;
  aspect-ratio: 9 / 16;
  max-height: 70vh;
  object-fit: contain;
  background: #000;
}

.tip-close {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 5;
  background: rgba(255, 255, 255, 0.92);
}

.tip-meta {
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.tip-meta b {
  font-size: var(--fs-lg);
}

.tip-meta span {
  font-size: var(--fs-base);
  color: var(--ink-soft);
  font-weight: 600;
}

.tip-meta p {
  margin-top: 6px;
  font-size: var(--fs-md);
  line-height: 1.45;
  color: var(--ink-soft);
}

#screen-more {
  background: linear-gradient(180deg, #EFEAFC, #D6DCFF);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* The "Meer" page — grouped lists, no separately-shadowed card styles: a
   settings page a kid actually wants to look at reads as a couple of quiet
   columns, not a stack of competing boxes. The account row is a plain row
   like every other one here (icon, text), just with a real button on the
   right instead of a chevron — logging in is an action, not a detail page
   to drill into. */
.more-list {
  display: flex;
  flex-direction: column;
  background: var(--white);
  border-radius: var(--r-md);
  box-shadow: 0 3px 0 rgba(13, 6, 91, 0.05), 0 6px 16px rgba(13, 6, 91, 0.04);
  overflow: hidden;
}

.more-list+.more-list,
.more-list+.more-label {
  margin-top: 20px;
}

.more-label {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--ink-soft);
  opacity: 0.75;
  margin: 0 0 8px 8px;
}

.more-row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 16px 18px;
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  text-align: left;
  border-bottom: 1px solid rgba(13, 6, 91, 0.07);
  transition: background-color 0.12s ease;
}

.more-list .more-row:last-child {
  border-bottom: none;
}

.more-row:active {
  background-color: rgba(13, 6, 91, 0.04);
}

.more-row>i:first-child,
.more-row>svg:first-child {
  width: 19px;
  height: 19px;
  color: var(--purple);
  flex-shrink: 0;
}

.more-row span {
  flex: 1;
  min-width: 0;
}

.more-row b {
  font-weight: 700;
  color: var(--ink-soft);
  font-size: var(--fs-base);
}

.more-row-btn {
  flex-shrink: 0;
  background: var(--purple);
  color: var(--white);
  font-size: var(--fs-base);
  font-weight: 700;
  padding: 8px 16px;
  border-radius: var(--r-pill);
  text-decoration: none;
  transition: transform 0.12s ease;
}

.more-row-btn:active {
  transform: scale(0.95);
}

.more-row-chevron {
  width: 17px;
  height: 17px;
  color: var(--ink-soft);
  opacity: 0.5;
  flex-shrink: 0;
}

.more-row-danger span,
.more-row-danger b {
  color: var(--bad);
}

.more-row-danger>i:first-child,
.more-row-danger>svg:first-child {
  color: var(--bad);
}

.more-footer {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  line-height: 1.5;
  padding: 0 6px;
  margin: 18px 0 0;
}

.more-footer i,
.more-footer svg {
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--good);
  width: 15px;
  height: 15px;
}

/* the hero screen's own spacing */
#screen-hero .me-card {
  margin-bottom: 18px;
}

#screen-hero .btn-text {
  margin-top: 10px;
}

.page-title {
  font-size: var(--fs-3xl);
  font-weight: 700;
  text-align: center;
  margin-bottom: 18px;
}

.empty-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 8px;
}

.empty-art {
  width: 110px;
  opacity: 0.9;
}

.empty-state p b {
  font-size: var(--fs-xl);
}

.empty-sub {
  color: var(--ink-soft);
  font-size: var(--fs-md);
  line-height: 1.5;
  max-width: 30ch;
}

.empty-state .btn {
  margin-top: 14px;
  width: auto;
}

.badge-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.badge-tile {
  border-radius: var(--r-lg);
  padding: 18px 12px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  box-shadow: var(--shadow-sm);
}

.badge-tile b {
  font-size: var(--fs-md);
}

.badge-tile span:last-child {
  font-size: var(--fs-sm);
  opacity: 0.85;
}

.badge-tile-icon {
  font-size: var(--fs-display);
}

.badge-tile-icon svg {
  width: 40px;
  height: 40px;
}

.badge-locked {
  background: rgba(255, 255, 255, 0.6);
  color: var(--ink-soft);
}

.badge-count {
  text-align: center;
  margin-top: 16px;
  font-weight: 700;
  color: var(--ink-soft);
}

/* me card */
.me-card {
  border-radius: var(--r-lg);
  padding: 26px 20px;
  text-align: center;
  color: var(--white);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.pcolor-sunset {
  background: linear-gradient(160deg, #FF9950, #FF5C7A);
}

.pcolor-berry {
  background: linear-gradient(160deg, #FF7BAC, #8C4DFF);
}

.pcolor-ocean {
  background: linear-gradient(160deg, #5FA8FF, #22D3B6);
}

.pcolor-forest {
  background: linear-gradient(160deg, #5ECC8F, #2E9A62);
}

.pcolor-galaxy {
  background: linear-gradient(160deg, #9D7BFF, #FF8FE1);
}

.pcolor-sun {
  background: linear-gradient(160deg, #D4A800, #FF8686);
}

.pcolor-coral {
  background: linear-gradient(160deg, #FF7A6B, #22D3B6);
}

.me-face {
  width: 110px;
  background: rgba(255, 255, 255, 0.25);
  border-radius: 50%;
  padding: 8px;
}

.me-card h1 {
  font-size: var(--fs-3xl);
}

.me-code {
  font-size: var(--fs-sm);
  letter-spacing: 0.1em;
  font-weight: 700;
  opacity: 0.9;
}

.me-code b {
  background: rgba(13, 6, 91, 0.35);
  padding: 4px 12px;
  border-radius: var(--r-pill);
  margin-left: 4px;
}

.me-lvl {
  font-size: var(--fs-base);
  font-weight: 700;
  background: rgba(255, 255, 255, 0.2);
  padding: 6px 16px;
  border-radius: var(--r-pill);
}

.me-stats {
  display: flex;
  gap: 10px;
  margin-top: 12px;
  width: 100%;
}

.me-stats>div {
  flex: 1;
  background: rgba(255, 255, 255, 0.92);
  color: var(--ink);
  border-radius: var(--r-md);
  padding: 12px 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.me-stats b {
  font-size: var(--fs-xl);
}

.me-stats span {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  font-weight: 700;
}

.me-note {
  font-size: var(--fs-base);
  color: var(--ink-soft);
  line-height: 1.5;
  text-align: center;
  margin: 16px 4px 18px;
}

/* ---------- Grade level section in hero drawer ---------- */
.me-grade {
  width: 100%;
  margin: 4px 0 18px;
}

.me-grade-label {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 8px;
}

.me-grade-btns {
  display: flex;
  gap: 8px;
}

.me-grade-btn {
  flex: 1;
  background: var(--surface);
  border: 2px solid transparent;
  border-radius: var(--r-md);
  padding: 10px 6px;
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  transition: border-color .15s, background .15s, transform .1s;
  line-height: 1.3;
}

.me-grade-btn:hover {
  border-color: var(--accent);
  transform: translateY(-1px);
}

.me-grade-btn.is-active {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, white);
}

/* ---------- Level picker screen ---------- */
#screen-level-picker {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100dvh;
  background: linear-gradient(160deg, #1a0a4a 0%, #3a1a8a 60%, #6c2fc0 100%);
  padding: 32px 20px;
  overflow-y: auto;
}

.level-picker-wrap {
  width: 100%;
  max-width: 420px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.level-picker-title {
  font-size: var(--fs-hero);
  font-weight: 900;
  color: #fff;
  text-align: center;
  margin: 0 0 10px;
}

.level-picker-sub {
  font-size: var(--fs-md);
  color: rgba(255, 255, 255, .7);
  text-align: center;
  margin: 0 0 28px;
  line-height: 1.5;
}

.level-picker-cards {
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
}

.level-card {
  display: flex;
  align-items: center;
  gap: 16px;
  background: rgba(255, 255, 255, .08);
  border: 2px solid rgba(255, 255, 255, .15);
  border-radius: 18px;
  padding: 18px 20px;
  cursor: pointer;
  transition: border-color .15s, background .15s, transform .12s;
  text-align: left;
  color: #fff;
}

.level-card:hover {
  background: rgba(255, 255, 255, .15);
  transform: translateY(-2px);
}

.level-card.is-selected {
  border-color: #ffd700;
  background: rgba(255, 215, 0, .15);
  box-shadow: 0 0 0 4px rgba(255, 215, 0, .15);
}

.level-card-emoji {
  font-size: var(--fs-display);
  flex-shrink: 0;
}

.level-card-name {
  font-size: var(--fs-lg);
  font-weight: 800;
  display: block;
}

.level-card-sub {
  font-size: var(--fs-base);
  color: rgba(255, 255, 255, .7);
  display: block;
  margin-top: 2px;
}


/* ---------- Summary ---------- */
#screen-summary {
  background: linear-gradient(180deg, #2A16A8, #5F29FF);
}

.summary-wrap {
  flex: 1;
  width: 100%;
  max-width: var(--appw);
  margin-left: auto;
  margin-right: auto;
  padding: calc(26px + var(--safe-top)) 20px calc(26px + var(--safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 16px;
  color: var(--white);
}

.summary-fireworks {
  font-size: 44px;
  text-align: center;
  animation: bob 1.6s infinite;
}

.summary-big {
  font-size: var(--fs-hero);
  text-align: center;
  font-weight: 700;
  color: var(--yellow);
  text-wrap: balance;
}

.summary-Buggie {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: center;
  font-size: var(--fs-base);
  font-weight: 500;
  font-style: italic;
  opacity: 0.95;
}

.summary-Buggie svg {
  width: 52px;
  height: 52px;
  flex-shrink: 0;
}

.summary-card {
  background: var(--white);
  color: var(--ink);
  border-radius: var(--r-lg);
  padding: 20px 18px;
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.summary-head {
  display: flex;
  align-items: center;
  gap: 12px;
}

.summary-face {
  width: 62px;
  flex-shrink: 0;
  background: var(--bg-purple);
  border-radius: 50%;
  padding: 4px;
}

.summary-head b {
  font-size: var(--fs-xl);
}

.summary-head small {
  color: var(--ink-soft);
  font-weight: 500;
}

.summary-line {
  font-size: var(--fs-md);
  line-height: 1.5;
  background: var(--bg-yellow);
  border-radius: var(--r-md);
  padding: 12px 14px;
}

.summary-skills {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.summary-skill {
  display: flex;
  align-items: center;
  gap: 12px;
  border: 2px solid var(--bg-purple);
  border-radius: var(--r-md);
  padding: 10px 12px;
}

.summary-skill>span:nth-child(2) {
  flex: 1;
}

.summary-skill b {
  font-size: var(--fs-md);
}

.summary-skill small {
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  line-height: 1.35;
  display: block;
  margin-top: 2px;
}

.summary-skill-icon {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.summary-skill-icon svg {
  width: 22px;
  height: 22px;
}

.summary-skill-stars {
  font-size: var(--fs-sm);
  letter-spacing: 1px;
  flex-shrink: 0;
}

.summary-parent-note {
  font-size: var(--fs-base);
  line-height: 1.55;
  background: var(--bg-green);
  border-radius: var(--r-md);
  padding: 14px;
}

/* ---------- Pro Summary Screen (Mobile First & Scannable) ---------- */
.summary-wrap-pro {
  padding: calc(16px + var(--safe-top)) 18px calc(24px + var(--safe-bottom));
  gap: 16px;
  max-width: 520px;
}

.pro-summary-hero {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.pro-summary-badge {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: rgba(255, 222, 89, 0.18);
  border: 2.5px solid var(--yellow);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--yellow);
  margin-bottom: 2px;
}

.pro-summary-badge svg {
  width: 30px;
  height: 30px;
}

.pro-summary-title {
  font-size: clamp(1.7rem, 6.5vw, 2.25rem);
  font-weight: 800;
  color: var(--yellow);
  margin: 0;
  line-height: 1.15;
  letter-spacing: -0.02em;
}

.pro-summary-sub {
  font-size: 1.05rem;
  color: rgba(255, 255, 255, 0.95);
  max-width: 380px;
  margin: 0;
  line-height: 1.4;
}

.pro-crasher-tag {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: var(--r-full);
  padding: 5px 14px;
  font-size: 0.85rem;
  font-weight: 600;
  color: #f1f5f9;
  margin-top: 4px;
}

.pro-crasher-tag svg {
  width: 16px;
  height: 16px;
  color: var(--good);
}

.pro-summary-card {
  background: var(--white);
  color: var(--ink);
  border-radius: var(--r-lg);
  padding: 18px 16px;
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.pro-stats-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  background: #f8f9fe;
  border: 1.5px solid #e9ecf8;
  border-radius: var(--r-md);
  padding: 12px 10px;
  text-align: center;
}

.pro-stat-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.pro-stat-num {
  font-size: 1.45rem;
  font-weight: 800;
  color: var(--ink);
  line-height: 1.1;
}

.pro-stat-lbl {
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

.pro-learnings {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.pro-learnings-header {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--ink);
}

.pro-learnings-header svg {
  width: 18px;
  height: 18px;
  color: var(--good);
}

.pro-learning-cards {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.pro-learning-card {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--white);
  border: 1.5px solid #edf0f8;
  border-radius: var(--r-md);
  padding: 10px 12px;
}

.pro-learning-icon {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pro-learning-icon svg {
  width: 22px;
  height: 22px;
}

.pro-learning-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.pro-learning-body b {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.25;
}

.pro-learning-body span {
  font-size: 0.88rem;
  color: #4b5563;
  line-height: 1.35;
}

.pro-learning-status {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(34, 197, 94, 0.12);
  color: var(--good);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.pro-learning-status svg {
  width: 16px;
  height: 16px;
  stroke-width: 2.5;
}

.pro-summary-next {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--bg-green);
  border: 1px solid rgba(46, 125, 50, 0.2);
  border-radius: var(--r-md);
  padding: 12px 14px;
  font-size: 0.92rem;
  line-height: 1.4;
  color: #1b4332;
}

.pro-summary-next svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: #2d6a4f;
  margin-top: 1px;
}

.pro-summary-next div {
  flex: 1;
}

.pro-summary-next b {
  font-weight: 700;
  margin-right: 4px;
}

/* ---------- Sheets & modals ---------- */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 800;
  background: rgba(13, 6, 91, 0.55);
  backdrop-filter: blur(3px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.sheet {
  width: min(100%, var(--appw));
  background: var(--white);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  padding: 14px 20px calc(22px + var(--safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.sheet-handle {
  width: 44px;
  height: 5px;
  border-radius: var(--r-pill);
  background: #D8D4F0;
  margin: 0 auto 4px;
}

.sheet h2 {
  font-size: var(--fs-xl);
  text-align: center;
}

.sheet-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--bg-purple);
  border-radius: var(--r-md);
  padding: 15px 16px;
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  border: none;
  cursor: pointer;
  box-sizing: border-box;
  width: 100%;
}

.sheet-row span {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.sheet-row b {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--purple);
  font-weight: 600;
}

.sheet-privacy {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  line-height: 1.5;
  text-align: left;
  padding: 2px 6px;
}

/* ---------- Mission Log ---------- */
.missionlog-story {
  font-size: var(--fs-base);
  color: var(--ink-soft);
  line-height: 1.5;
}

.missionlog-progress-label {
  font-size: var(--fs-base);
  margin-bottom: 6px;
}

.missionlog-bar {
  width: 100%;
  height: 10px;
  background: rgba(13, 6, 91, 0.1);
}

.missionlog-bar .hud-xp-fill {
  background: linear-gradient(90deg, var(--good), #3FD98A);
}

.missionlog-chapters {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* These always looked enclosed, and now they act it: each one jumps to its
   chapter on the map. Same rule as everywhere else — an enclosed, filled
   shape is something you can tap. */
.missionlog-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  background: var(--bg-purple);
  border-radius: var(--r-md);
  padding: 9px 14px;
  font-size: var(--fs-base);
  font-weight: 600;
  text-align: left;
  color: var(--ink);
  transition: transform 0.12s ease, background 0.15s ease;
}

.missionlog-row:active {
  transform: scale(0.98);
}

@media (hover: hover) {
  .missionlog-row:hover {
    background: var(--comp-purple);
  }

  .missionlog-row.ml-done:hover {
    background: rgba(20, 184, 107, 0.28);
  }
}

.missionlog-row-num {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--row-color, var(--ink));
  color: var(--white);
  font-size: var(--fs-xs);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.missionlog-row-title {
  flex: 1;
}

.missionlog-row-icon {
  display: flex;
  color: var(--ink-soft);
}

.missionlog-row-icon svg {
  width: 16px;
  height: 16px;
}

.missionlog-row.ml-done {
  background: rgba(20, 184, 107, 0.14);
}

.missionlog-row.ml-done .missionlog-row-icon {
  color: var(--good);
}

/* The chapter you actually opened this log from — its own tint plus a ring
   in its own color, so "you are here" reads at a glance. Comes after
   .ml-done so it wins if a chapter is both current and already completed. */
.missionlog-row.ml-current {
  background: var(--row-tint, var(--bg-purple));
  box-shadow: 0 0 0 2px var(--row-color, var(--purple)) inset;
}

.missionlog-row-current {
  flex: 0 0 auto;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--row-color, var(--purple));
}

.missionlog-row.ml-locked {
  opacity: 0.55;
}

.modal {
  background: var(--white);
  border-radius: var(--r-lg);
  padding: 24px 20px;
  width: min(92%, 360px);
  margin: auto;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 10px;
  box-shadow: var(--shadow-md);
}

.modal h2 {
  font-size: var(--fs-xl);
}

.modal p {
  font-size: var(--fs-md);
  color: var(--ink-soft);
  line-height: 1.5;
}

.modal-actions {
  display: flex;
  gap: 10px;
  margin-top: 8px;
}

.modal-actions .btn {
  flex: 1;
  font-size: var(--fs-md);
  padding: 13px 10px;
}

/* ---------- Paywall ---------- */
.paywall-backdrop {
  position: fixed;
  inset: 0;
  z-index: 950;
  background: rgba(13, 6, 91, 0.7);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.paywall-card {
  position: relative;
  background: var(--white);
  border-radius: var(--r-lg);
  padding: 36px 26px 28px;
  width: min(92%, 400px);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  box-shadow: var(--shadow-md);
}

.paywall-close {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 38px;
  height: 38px;
  border-radius: var(--r-pill);
  background: var(--white);
  box-shadow: var(--shadow-md);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
}

.paywall-close svg {
  width: 18px;
  height: 18px;
}

.paywall-icon {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: var(--yellow);
  box-shadow: 0 5px 0 var(--yellow-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  margin-bottom: 4px;
}

.paywall-icon svg {
  width: 28px;
  height: 28px;
}

.paywall-card h2 {
  font-size: var(--fs-2xl);
  line-height: 1.25;
}

.paywall-price {
  display: inline-block;
  background: var(--bg-purple);
  color: var(--purple);
  font-size: var(--fs-xl);
  font-weight: 800;
  border-radius: var(--r-pill);
  padding: 8px 18px;
}

.paywall-card p {
  font-size: var(--fs-base);
  color: var(--ink-soft);
  line-height: 1.55;
}

.paywall-card .btn {
  width: 100%;
  margin-top: 6px;
}

.paywall-login-row {
  font-size: var(--fs-base);
  color: var(--ink-soft);
}

.paywall-login-link {
  color: var(--purple);
  font-weight: 700;
  text-decoration: underline;
  padding: 2px;
}

/* ---------- Degraded storage banner ---------- */
.degraded-banner {
  position: fixed;
  top: calc(8px + var(--safe-top));
  left: 50%;
  transform: translateX(-50%);
  width: min(94%, calc(var(--appw) - 20px));
  z-index: 950;
  background: var(--ink);
  color: var(--white);
  border: 2px solid var(--yellow);
  border-radius: var(--r-md);
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--fs-base);
  line-height: 1.45;
  font-weight: 500;
  box-shadow: var(--shadow-md);
  animation: slideup 0.4s ease both;
}

.degraded-banner button {
  color: var(--yellow);
  font-size: var(--fs-md);
  font-weight: 700;
  padding: 4px 6px;
  flex-shrink: 0;
}

/* ---------- XP float ---------- */
.xp-float {
  position: fixed;
  z-index: 920;
  pointer-events: none;
  background: var(--ink);
  color: var(--yellow);
  font-weight: 700;
  font-size: var(--fs-md);
  padding: 6px 12px;
  border-radius: var(--r-pill);
  transform: translate(-50%, -50%);
  animation: xpfloat 1.25s ease-out forwards;
  box-shadow: var(--shadow-md);
}

@keyframes xpfloat {
  0% {
    opacity: 0;
    translate: 0 10px;
    scale: 0.6;
  }

  20% {
    opacity: 1;
    scale: 1.1;
  }

  100% {
    opacity: 0;
    translate: 0 -70px;
    scale: 1;
  }
}

/* ---------- Animations ---------- */
.anim-float {
  animation: bob 3s ease-in-out infinite;
}

.anim-bounce-in {
  animation: bouncein 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes bouncein {
  0% {
    transform: scale(0.3);
    opacity: 0;
  }

  100% {
    transform: scale(1);
    opacity: 1;
  }
}

.anim-pop {
  animation: popk 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes popk {
  0% {
    transform: scale(0.6);
    opacity: 0;
  }

  100% {
    transform: scale(1);
    opacity: 1;
  }
}

.anim-popin {
  animation: popin 0.3s ease both;
}

@keyframes popin {
  0% {
    transform: translateY(6px);
    opacity: 0;
  }

  100% {
    transform: none;
    opacity: 1;
  }
}

.anim-slideup {
  animation: slideup 0.4s ease both;
}

@keyframes slideup {
  0% {
    transform: translateY(24px);
    opacity: 0;
  }

  100% {
    transform: none;
    opacity: 1;
  }
}

.anim-shake {
  animation: shake 0.45s ease;
}

@keyframes shake {

  0%,
  100% {
    transform: translateX(0);
  }

  20% {
    transform: translateX(-8px);
  }

  40% {
    transform: translateX(8px);
  }

  60% {
    transform: translateX(-5px);
  }

  80% {
    transform: translateX(5px);
  }
}

.anim-pulse {
  animation: pulsek 0.4s ease;
}

@keyframes pulsek {
  0% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.18);
  }

  100% {
    transform: scale(1);
  }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------- Wider screens ---------- */
@media (min-width: 600px) {
  :root {
    --appw: 560px;
  }

  /* opt-grid-icons stays 2x2 on desktop as requested */

  .species-grid {
    grid-template-columns: repeat(6, 1fr);
  }

  .species-card .hero-svg {
    width: 52px;
    height: 52px;
  }
}

/* Inline lucide icon sizing to match text */
.lucide {
  width: 1.2em;
  height: 1.2em;
  vertical-align: middle;
  display: inline-block;
}

/* ---------- Creation Mode (Icon Studio) ---------- */
/* Locked to the viewport (height, not min-height) and never scrolls —
   unlike quiz/blueprint screens where content length varies and page
   scroll is expected, an artboard tool should never need a scroll to
   reach its own toolbar. The artboard keeps ONE size: the tray, the
   selected-shape bar and the instruction card are all drawers laid over
   it (never in the layout flow), so nothing opening or closing moves it. */
.creation-stage {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
  width: 100%;
  max-width: var(--appw);
  margin-left: auto;
  margin-right: auto;
  padding: calc(10px + var(--safe-top)) 16px 0;
  box-sizing: border-box;
}

.creation-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 12px;
  min-height: 0;
  position: relative;
  /* The artboard can be lifted when the drawer would cover the selected
     piece; it must not slide over the top bar. */
  overflow: hidden;
}

/* Instruction card: the toggle for it lives in the topbar (see
   .creation-hint-toggle, replacing where Wissen used to sit) — so
   collapsing it removes this card from the layout entirely, no leftover
   row just to hold a tap target. Opens on entry to a screen, collapses
   itself the moment a kid places their first element on it. */
.creation-prompt {
  position: absolute;
  top: 8px;
  left: 0;
  right: 0;
  z-index: 60;
  /* Floats over the canvas as a speech bubble from the avatar above:
     touches go through to the canvas so the first stroke or tap is never
     swallowed. Only the X takes taps. */
  pointer-events: none;
  background: var(--white);
  border-radius: var(--r-lg);
  padding: 14px 44px 14px 16px;
  box-shadow: var(--shadow-md);
  animation: mk-drawer-up 0.18s ease-out;
}

/* The tail, pointing up at the avatar in the top corner. */
.creation-prompt::before {
  content: '';
  position: absolute;
  top: -9px;
  right: 15px;
  width: 18px;
  height: 18px;
  background: var(--white);
  border-radius: 3px;
  transform: rotate(45deg);
}

.creation-prompt-x {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-soft);
  pointer-events: auto;
}

.creation-prompt-x svg {
  width: 18px;
  height: 18px;
}

.creation-goal {
  font-weight: 700;
  font-size: var(--fs-md);
  color: var(--ink);
  line-height: 1.3;
}

.creation-hint {
  margin-top: 4px;
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--ink-soft);
  line-height: 1.35;
}

.creation-hint b {
  color: var(--purple);
  font-weight: 700;
}

.creation-hint .hint-ic {
  width: 15px;
  height: 15px;
  vertical-align: -3px;
}

/* Topbar avatar — doubles as the show/hide toggle for .creation-prompt
   above, and as Buggie's expression (studioHint's mood) at a glance. */
/* .icon-btn already centers its child and sizes any svg to 22px
   (.icon-btn svg) — .coach-avatar-mini just needs to not fight that. */
.coach-avatar-mini {
  display: flex;
}

.creation-canvas-wrap {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.4);
  border-radius: var(--r-lg);
  padding: 44px 16px 44px;
  position: relative;
  box-sizing: border-box;
  margin-bottom: 16px;
  /* A 2nd finger landing here opens the full-screen preview (makerij.js) —
     without this, the browser can start its own scroll/gesture recognition
     on the 2nd touch before our capture-phase listener gets to it. */
  touch-action: none;
}

/* Sized by BOTH constraints at once (height:100% of whatever vertical
   space is left, max-width:100% of the wrap) — the browser picks
   whichever is tighter, so the 3:4 artboard always fits fully in view,
   shrinking automatically when the tray/hint above take more room and
   growing back the moment they don't, instead of ever scrolling.
   .creation-canvas-frame carries that sizing so Wissen (.creation-canvas-clear)
   can hang off its corner, outside the canvas's own overflow:hidden — the
   same "hanging badge" trick as .placed-del, just anchored to the whole
   artboard instead of one shape, so it reads as acting on the canvas
   instead of floating in the topbar with nothing near it. */
.creation-canvas-frame {
  position: relative;
  height: 100%;
  max-width: 100%;
  aspect-ratio: 3/4;
}

/* Exactly 3:4 whatever the room is (it used to stretch taller on narrow
   phones), so a design has the same proportions at every size and on every
   device. The wrap is the container this measures against. */
@supports (width: 1cqw) {
  .creation-canvas-wrap {
    container-type: size;
  }

  .creation-canvas-frame {
    width: min(100cqw, 75cqh);
    height: auto;
    max-width: none;
  }
}

/* isSimpleDraw (#353): the fixed 3:4 phone-screen shape exists so an app
   screen looks the same everywhere it's reused - a free doodle has no such
   reuse and no reason to leave visible empty space around it, so it just
   fills the wrap it's given instead. */
.creation-stage.is-simple-draw .creation-canvas-frame {
  aspect-ratio: auto;
  width: 100%;
  height: 100%;
  max-width: 100%;
}

/* The wrap's normal 44px top/bottom padding leaves room to hang the corner
   Wissen button off the frame - isSimpleDraw drops that button, so the
   padding can shrink too and give the canvas back the space. */
.creation-stage.is-simple-draw .creation-canvas-wrap {
  padding: 16px;
}

/* Every drawn design sits in one of these. It is the container that text
   and component sizes (cqw) measure against, so a design looks the same at
   any size: editor, full screen, thumbnail, share picture. Touches pass
   through it to the artboard; only the pieces themselves take them. */
.mk-art {
  position: absolute;
  inset: 0;
  container-type: inline-size;
  pointer-events: none;
}

.mk-art .placed {
  pointer-events: auto;
}

.creation-canvas {
  position: absolute;
  inset: 0;
  background: var(--white);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  /* Own the whole touch stream (drag/resize are custom pointer-event
     handling in makerij.js) — without this, a single-finger drag can
     start a native scroll attempt underneath our gesture handling. */
  touch-action: none;
}

/* Wipe-all sits above the artboard's top-right corner, in the strip the
   wrap reserves for it - never on the drawing (mirror of the full-screen
   button below the lower-right corner). */
.creation-canvas-clear {
  position: absolute;
  top: 6px;
  right: 14px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--white);
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-sm);
  border: 2px solid rgba(13, 6, 91, 0.06);
  z-index: 6;
}

.creation-canvas-clear svg {
  width: 16px;
  height: 16px;
}

.creation-canvas-clear:active {
  transform: scale(0.9);
}

/* Sits below the artboard's lower-right corner, in the strip the wrap
   reserves for it - never on the drawing itself. */
.creation-canvas-focus {
  position: absolute;
  right: 14px;
  bottom: 6px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--white);
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-sm);
  border: 2px solid rgba(13, 6, 91, 0.06);
  z-index: 6;
}

.creation-canvas-focus svg {
  width: 18px;
  height: 18px;
}

.creation-canvas-focus:active {
  transform: scale(0.9);
}

/* Full-screen edit: top bar, instruction bubble and Klaar/Volgende row go;
   the toolbar stays. */
.creation-stage.is-focus .mission-top,
.creation-stage.is-focus .creation-prompt,
.creation-stage.is-focus .makerij-actions-row {
  display: none;
}

.creation-stage.is-focus .makerij-toolbar-row {
  padding-bottom: 2px;
}

.creation-canvas.is-editing {
  box-shadow: var(--shadow-md), 0 0 0 3px var(--comp-purple);
}

.creation-canvas.shake {
  animation: shake 0.4s;
}

.creation-empty {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: var(--ink-soft);
  opacity: 0.55;
  pointer-events: none;
}

.creation-empty svg {
  width: 32px;
  height: 32px;
  animation: nudge 1.4s ease-in-out infinite;
}

.creation-empty span {
  font-size: var(--fs-base);
  font-weight: 700;
}

@keyframes nudge {

  0%,
  100% {
    transform: translateY(0);
  }

  50% {
    transform: translateY(5px);
  }
}

/* Placed shapes: independent draggable layers */
.placed {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: 10px;
  transition: box-shadow 0.15s;
  /* width and height are now set inline by app.js */
}

.placed .creation-placed-shape {
  position: static;
  width: 100%;
  height: 100%;
  display: block;
}

.placed .creation-placed-shape * {
  vector-effect: non-scaling-stroke;
}

.placed .creation-placed-text {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 2rem;
  line-height: 1;
  white-space: nowrap;
  pointer-events: none;
}

/* Placed "components" (ticket #140) — concrete UI elements next to plain
   shapes: knop, kopbalk, afbeelding, invoerveld, navigatiebalk, menu-icoon,
   sluitknop. Same .placed positioning/drag/resize/select shell as a shape;
   only what's inside differs. */
.mk-comp {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.mk-comp-label {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.mk-comp-button {
  border-radius: 999px;
  color: var(--white);
  box-shadow: 0 4px 0 rgba(0, 0, 0, 0.18);
}

.mk-comp-button .mk-comp-label {
  font-size: 0.85rem;
  font-size: 4.07cqw;
  padding: 0 10%;
}

.mk-comp-header {
  width: 100%;
  color: var(--white);
  border-radius: 8px;
  justify-content: flex-start;
  padding: 0 8%;
  box-sizing: border-box;
}

.mk-comp-header .mk-comp-label {
  font-size: 1rem;
  font-size: 4.79cqw;
}

.mk-comp-image {
  width: 100%;
  height: 100%;
  background: #EDEBF3;
  border: 2px dashed #C9C4D8;
  border-radius: 10px;
  box-sizing: border-box;
}

.mk-comp-image svg {
  width: 40%;
  height: 40%;
}

.mk-comp-image-filled {
  border-style: solid;
  overflow: hidden;
  padding: 0;
}

.mk-comp-image-filled img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}

/* Icon + label, not an icon-only circle like del/done next to it - kids
   need to read what a new, unfamiliar action does. */
.btn-context-upload {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  background: var(--bg-purple);
  color: var(--purple);
  border: none;
  border-radius: var(--r-pill);
  padding: 9px 14px;
  font-family: inherit;
  font-weight: 700;
  font-size: var(--fs-sm);
  white-space: nowrap;
}

.btn-context-upload svg {
  width: 18px;
  height: 18px;
}

/* Upload-failure message (#179) - over the canvas itself rather than
   squeezed into the bottom drawer, since there's real room there and it's
   what a kid is actually looking at when this happens. Large and short:
   a kid reads this, not a parent. */
.mk-upload-error-overlay {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  text-align: center;
  padding: 24px;
  background: rgba(255, 255, 255, 0.97);
  border-radius: inherit;
}

.mk-upload-error-overlay .mk-upload-error-icon {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--bg-purple);
  color: var(--purple);
  display: flex;
  align-items: center;
  justify-content: center;
}

.mk-upload-error-overlay .mk-upload-error-icon svg {
  width: 28px;
  height: 28px;
}

.mk-upload-error-overlay p {
  margin: 0;
  font-family: var(--font);
  font-weight: 800;
  font-size: var(--fs-xl);
  line-height: 1.3;
  color: var(--ink);
  max-width: 26ch;
}

.mk-upload-error-overlay .mk-upload-error-sub {
  font-weight: 500;
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  max-width: 32ch;
  margin-top: -8px;
}

.mk-upload-error-overlay .btn {
  text-decoration: none;
}

.mk-upload-error-overlay .btn svg {
  width: 16px;
  height: 16px;
  vertical-align: -3px;
}

.mk-upload-error-overlay .icon-btn {
  background: var(--bg-purple);
  color: var(--purple);
  position: absolute;
  top: 12px;
  right: 12px;
}

.mk-comp-input {
  width: 100%;
  height: 100%;
  background: var(--white);
  border: 1.5px solid #D8D4E4;
  border-radius: 8px;
  box-sizing: border-box;
  justify-content: flex-start;
  padding: 0 8%;
}

.mk-comp-input .mk-comp-label {
  font-family: inherit;
  font-weight: 500;
  font-size: 0.8rem;
  font-size: 3.83cqw;
  color: #9C97AC;
}

.mk-comp-navbar {
  width: 100%;
  height: 100%;
  background: var(--bg-purple);
  border-radius: 8px;
  justify-content: space-evenly;
  box-sizing: border-box;
}

.mk-navbar-dot {
  width: 22%;
  height: 40%;
  border-radius: 4px;
  flex-shrink: 0;
}

.mk-comp-card {
  width: 100%;
  height: 100%;
  background: var(--white);
  border-radius: 10px;
  box-shadow: 0 2px 8px rgba(13, 6, 91, 0.16);
  overflow: hidden;
  justify-content: flex-start;
  box-sizing: border-box;
}

.mk-card-bar {
  align-self: stretch;
  width: 5%;
  flex-shrink: 0;
}

.mk-card-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2.7cqw;
  padding: 0 7%;
}

.mk-card-body .mk-comp-label {
  font-size: 1rem;
  font-size: 4.79cqw;
  color: var(--ink);
  margin-bottom: 1.2cqw;
}

.mk-card-line {
  display: block;
  height: 2.1cqw;
  border-radius: 4px;
  background: #E3E0EC;
}

.mk-card-line-short {
  width: 60%;
}

.mk-comp-row {
  width: 100%;
  height: 100%;
  background: var(--white);
  border-bottom: 1.5px solid #E3E0EC;
  justify-content: flex-start;
  gap: 4%;
  padding: 0 4%;
  box-sizing: border-box;
}

.mk-row-dot {
  height: 62%;
  aspect-ratio: 1;
  border-radius: 50%;
  flex-shrink: 0;
}

.mk-comp-row .mk-comp-label {
  flex: 1;
  min-width: 0;
  font-size: 0.9rem;
  font-size: 4.31cqw;
  font-weight: 600;
  color: var(--ink);
}

.mk-row-chev {
  width: 2.7cqw;
  height: 2.7cqw;
  border-top: 0.75cqw solid #B7B2C6;
  border-right: 0.75cqw solid #B7B2C6;
  transform: rotate(45deg);
  flex-shrink: 0;
  margin-right: 2%;
}

.mk-comp-toggle {
  width: 100%;
  height: 100%;
  border-radius: 999px;
  justify-content: flex-end;
  padding: 0 7%;
  box-sizing: border-box;
}

.mk-comp-toggle span {
  height: 72%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--white);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}

.mk-comp-burger {
  width: 100%;
  height: 100%;
  border-radius: 22%;
  flex-direction: column;
  gap: 16%;
  box-sizing: border-box;
}

.mk-comp-burger span {
  display: block;
  width: 60%;
  height: 12%;
  border-radius: 2px;
  background: var(--white);
}

.mk-comp-close {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  position: relative;
  box-sizing: border-box;
}

.mk-comp-close::before,
.mk-comp-close::after {
  content: '';
  position: absolute;
  top: 46%;
  left: 20%;
  right: 20%;
  height: 12%;
  border-radius: 2px;
  background: var(--white);
}

.mk-comp-close::before {
  transform: rotate(45deg);
}

.mk-comp-close::after {
  transform: rotate(-45deg);
}

/* Inline label field in the context bar — only for a component that
   carries text (COMPONENT_DEFS.hasText), next to the swatches. */
.mk-context-text {
  flex: 1;
  min-width: 90px;
  border: 1.5px solid #DCD8F0;
  border-radius: var(--r-sm);
  padding: 8px 12px;
  font-size: var(--fs-sm);
  font-family: inherit;
  color: var(--ink);
  outline: none;
  background: var(--white);
  box-sizing: border-box;
}

.mk-context-text:focus {
  border-color: var(--purple);
}

/* No z-index boost here on purpose — selecting a shape used to force it
   to z-index:10, which meant selecting anything that sat BEHIND another
   shape (e.g. a large background shape under a smaller accent) made that
   other shape vanish behind it while selected: nothing was deleted, it
   was just hidden by the now-elevated selection, but it read as data
   loss. The selection ring below is visible enough on its own without
   needing to reorder the stack. */
.placed.selected {
  box-shadow: 0 0 0 3px var(--white), 0 0 0 6px var(--purple);
  animation: pop 0.25s ease;
}

.placed-del {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--bad);
  color: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-sm);
  border: 2.5px solid var(--white);
  z-index: 5;
}

.placed-del svg {
  width: 15px;
  height: 15px;
}

.placed-del:active {
  transform: scale(0.88);
}

.placed-resize-handle {
  position: absolute;
  bottom: -12px;
  right: -12px;
  width: 28px;
  height: 28px;
  background: var(--white);
  border: 3px solid var(--purple);
  border-radius: 50%;
  cursor: nwse-resize;
  z-index: 20;
  box-shadow: var(--shadow-sm);
  touch-action: none;
}

/* Tappable screen thumbnail in the app-plan — opens De Makerij on that
   exact screen. Font reset because it's a <button>. */
.bp-screen-thumb {
  font: inherit;
  color: inherit;
  transition: transform 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}

.bp-screen-thumb:hover {
  transform: translateY(-2px);
  border-color: var(--purple) !important;
  box-shadow: 0 6px 16px rgba(95, 41, 255, 0.18);
}

.bp-screen-thumb:active {
  transform: translateY(0);
}

.creation-canvas-mini {
  width: 60px;
  height: 60px;
  background: var(--white);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-sm);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  margin-top: 6px;
}

.creation-placed-shape {
  position: absolute;
  width: 60%;
  height: 60%;
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.creation-placed-shape:nth-child(1) {
  width: 90%;
  height: 90%;
}

.creation-placed-shape:nth-child(2) {
  width: 70%;
  height: 70%;
}

.creation-placed-shape:nth-child(3) {
  width: 50%;
  height: 50%;
}

.creation-placed-shape:nth-child(4) {
  width: 30%;
  height: 30%;
}

.creation-placed-shape:nth-child(5) {
  width: 20%;
  height: 20%;
}

/* Bottom dock — one sticky container (not several stacked ones) holding,
   top to bottom: the expandable tray (or the selected-shape quick-actions
   bar in its place), the slim icon toolbar, and the Terug/Volgende/Klaar
   row. Closed by default, so the "always on screen" chrome stays as thin
   as possible and the artboard above gets the rest of the viewport. */
.studio-bottom-dock {
  position: sticky;
  bottom: 0;
  z-index: 100;
  margin: 0 -16px;
  padding: 10px 16px calc(10px + var(--safe-bottom, 0px));
}

/* The dock's white sheet is its own layer (z-index:-1) so the drawer
   (z-index:-2) can slide out from underneath its rounded top corners. */
.studio-bottom-dock::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--white);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: 0 -4px 12px rgba(13, 6, 91, 0.05), 0 8px 24px rgba(13, 6, 91, 0.15);
}

/* A drawer over the canvas, anchored to the top of the dock. Absolute, so
   opening it never changes the artboard's size. */
.makerij-tray {
  display: none;
}

.makerij-tray.open {
  display: block;
  position: absolute;
  left: 0;
  right: 0;
  /* Tucked BEHIND the dock (z-index:-1 keeps it under the dock's own
     content, above only its background): it runs down past the dock's
     rounded top corners, so the white dock sits on top of it like a
     stacked sheet instead of the two edges colliding. */
  z-index: -2;
  bottom: calc(100% - var(--r-lg));
  max-height: 45dvh;
  overflow-y: auto;
  padding: 16px 16px calc(10px + var(--r-lg));
  background: var(--bg-purple);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: 0 -6px 18px rgba(13, 6, 91, 0.16);
  animation: mk-drawer-up 0.18s ease-out;
}

@keyframes mk-drawer-up {
  from { transform: translateY(14px); opacity: 0; }
  to { transform: none; opacity: 1; }
}

/* Invisible tap-catcher behind the drawer, over the canvas area: tap
   outside the drawer to close it. */
.mk-scrim {
  position: absolute;
  inset: 0;
  z-index: 50;
  background: rgba(13, 6, 91, 0.12);
  border: 0;
  padding: 0;
  cursor: default;
}

.creation-canvas.is-drawing {
  cursor: crosshair;
}

.creation-stroke {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.mk-clear-confirm {
  position: absolute;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 16px;
  text-align: center;
  background: rgba(255, 255, 255, 0.98);
  border-radius: var(--r-md);
}

.mk-draw-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding-top: 4px;
}

/* isSimpleDraw (#353): colors + gum + undo on one row instead of a
   two-row drawer. The swatch strip shrinks/scrolls to fit; the two tool
   buttons keep their natural size at the end. */
.mk-draw-row-simple {
  display: flex;
  align-items: center;
  gap: 4px;
}

.mk-draw-row-simple .swatch-strip {
  flex: 1 1 auto;
  min-width: 0;
  justify-content: flex-start;
  /* 14px, not a smaller guess: .mk-context-swatches's own padding is
     already tuned to this value for exactly this clipping problem (see
     its comment below) - reusing it instead of re-deriving a number. */
  padding: 14px;
}

.mk-draw-row-simple .mk-draw-tool {
  flex-shrink: 0;
}

.mk-drawer-handle {
  display: flex;
  justify-content: center;
  width: 100%;
  margin: -12px 0 2px;
  padding: 10px 0;
  background: transparent;
}

.mk-drawer-handle span {
  width: 44px;
  height: 5px;
  border-radius: 3px;
  background: var(--ink-soft);
  opacity: 0.4;
}

.mk-draw-size {
  width: 40px;
  height: 48px;
  border-radius: var(--r-md);
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
}

.mk-draw-size span {
  display: block;
  border-radius: 50%;
  box-shadow: 0 0 0 1.5px rgba(13, 6, 91, 0.2);
}

.mk-draw-size.on,
.mk-draw-tool.on {
  /* white chip + purple ring: the drawer itself is the same tint as
     --bg-purple, so a tinted selected state was invisible there */
  background: var(--white);
  color: var(--purple);
  box-shadow: 0 0 0 3px var(--purple);
}

.mk-draw-tool {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 6px 8px;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink-soft);
  font-weight: 700;
  font-size: var(--fs-xs);
}

.mk-draw-tool svg {
  width: 20px;
  height: 20px;
}

.mk-draw-tool:disabled {
  opacity: 0.35;
}

.makerij-toolbar-row {
  display: flex;
  justify-content: center;
  gap: 6px;
}

.mk-tool-btn {
  flex: 1;
  max-width: 100px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 8px 6px;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink-soft);
  font-weight: 700;
  font-size: var(--fs-xs);
  transition: background 0.15s, color 0.15s;
}

.mk-tool-btn svg {
  width: 20px;
  height: 20px;
}

.mk-tool-btn.active {
  background: var(--bg-purple);
  color: var(--purple);
}

/* Quick actions for the currently-selected shape — recolour or delete
   right there, replacing the tray/toolbar row while a shape is selected,
   so recolouring never needs a detour through the Kleuren tab. */
/* Two rows (#184): row 1 = what this item looks like (color/text/photo -
   varies a lot by item type), row 2 = what to do with it (always the same
   few actions). Keeping them apart is what stops a button/header/input
   component - swatches AND a text field on the same item - from squeezing
   the actions row into an unpredictable wrap on a phone. */
.mk-context-bar {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.mk-context-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
}

.mk-context-row-actions {
  gap: 4px;
  padding-top: 4px;
  border-top: 1px solid rgba(13, 6, 91, 0.06);
}

/* .mk-tool-btn's flex:1 makes sense in its home row (3-4 tabs filling the
   full toolbar width evenly) - here it's mixed with the fixed-size
   del/done circles, so it should just size to its own content instead of
   stretching to fill whatever's left. */
.mk-context-row-actions .mk-tool-btn {
  flex: 0 0 auto;
}

.swatch-strip.mk-context-swatches {
  /* was flex:1 (stretch to fill the row), which is exactly what pinned
     everything else to the right and read as "left-aligned" overall -
     shrink-if-needed instead, so the whole bar centers as one compact
     group. */
  flex: 0 1 auto;
  flex-wrap: nowrap;
  overflow-x: auto;
  justify-content: center;
  gap: 8px;
  /* room for the selected swatch's ring and shadow, which overflow-x
     would otherwise clip into a visible box */
  padding: 14px;
}

.mk-context-swatches .swatch.sel {
  /* ring only: a blurred shadow gets clipped by the strip's scroll box */
  box-shadow: 0 0 0 4px var(--comp-yellow);
}

.mk-context-swatches .swatch {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
}

.mk-context-del {
  background: var(--bg-red);
  color: var(--bad);
  flex-shrink: 0;
}

/* The tap-to-arm confirm view (#185) - its own focused mk-context-bar-
   confirm layout entirely replaces the normal bar (see contextBarHTML),
   so there is nothing else nearby - notably Klaar's own checkmark icon -
   to mistake for the way to confirm. */
.mk-context-bar-confirm {
  align-items: center;
}

.mk-context-confirm-text {
  margin: 0;
  font-family: var(--font);
  font-weight: 800;
  font-size: var(--fs-md);
  color: var(--ink);
}

.mk-context-del-confirm {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  background: var(--bad);
  color: var(--white);
  border: none;
  border-radius: var(--r-pill);
  padding: 9px 14px;
  font-family: inherit;
  font-weight: 700;
  font-size: var(--fs-sm);
  white-space: nowrap;
  animation: popk 0.2s ease;
}

.mk-context-del-confirm svg {
  width: 18px;
  height: 18px;
}

.mk-context-done {
  background: var(--good);
  color: var(--white);
  flex-shrink: 0;
}

/* Full-screen read-only preview (makerij.js openFullscreenPreview) — a 2nd
   finger anywhere near the artboard pops the whole artwork in here, large,
   over a dimmed backdrop. Pinch/wheel zoom further and drag to pan once
   past 1x; a plain tap (no pan) or the X closes it. */
.mk-fullscreen-preview {
  position: fixed;
  inset: 0;
  z-index: 400;
  background: rgba(13, 6, 91, 0.92);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  box-sizing: border-box;
  touch-action: none;
  animation: mkFullscreenIn 0.18s ease;
}

@keyframes mkFullscreenIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .mk-fullscreen-preview {
    animation: none;
  }
}

.mk-fullscreen-canvas {
  position: relative;
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  aspect-ratio: 3/4;
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}

.mk-fullscreen-content {
  position: absolute;
  inset: 0;
}

.mk-fullscreen-close {
  position: absolute;
  top: calc(16px + var(--safe-top, 0px));
  right: 16px;
  z-index: 2;
}

/* A real gap + hairline, not just spacing — kids have less precise
   taps than adults, and this button sits right below tools that change
   constantly (tray/context-bar), so it needs to read as a clearly
   separate zone, not "one more button in the row above." */
.makerij-actions-row {
  padding-top: 18px;
  margin-top: 6px;
  border-top: 2px solid rgba(13, 6, 91, 0.08);
}

/* isSimpleDraw (#353): the divider above Klaar was meant to separate it
   from a taller drawer + toolbar-row stack. With just the one compact
   colors/gum/undo row, it reads as a stray leftover line rather than an
   intentional section break, so it's dropped along with some of the gap. */
.studio-bottom-dock-simple .makerij-actions-row {
  border-top: none;
  padding-top: 10px;
  margin-top: 0;
}

/* "Nog een scherm erbij" (#229) — sits above Terug/Klaar on the last
   screen. Deliberately quieter than Klaar: adding is the side road, and a
   kid should never lose track of how to finish. */
.mk-add-screen {
  width: 100%;
  margin-bottom: 10px;
  padding: 12px 18px;
  font-size: var(--fs-md);
}

.mk-add-screen svg {
  width: 18px;
  height: 18px;
}

/* Header chip for a screen the kid added: shows its name, tap to rename.
   Lesson screens keep the plain .mk-screen-step counter instead. */
.mk-screen-name {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  margin-bottom: 6px;
  padding: 4px 11px;
  /* White on the header's own soft lavender — a bg-purple pill here is
     invisible, and this has to read as "you can tap me". */
  border: 1.5px solid #DCD8F0;
  border-radius: var(--r-pill);
  background: var(--white);
  color: var(--ink);
  font-family: inherit;
  font-weight: 800;
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  cursor: pointer;
}

.mk-screen-name span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mk-screen-name svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  color: var(--purple);
}

.mk-screen-name:active {
  transform: translateY(1px);
}

/* The chip opened up: name field, confirm, and delete. Rename and delete
   only exist here, so the header costs no extra room when it's closed. */
.mk-screen-namebar {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  margin-bottom: 6px;
}

.mk-screen-name-input {
  flex: 1;
  min-width: 0;
  border: 1.5px solid var(--purple);
  border-radius: var(--r-sm);
  padding: 5px 9px;
  background: var(--white);
  color: var(--ink);
  font-family: inherit;
  font-weight: 700;
  font-size: var(--fs-sm);
  outline: none;
}

.mk-screen-ok,
.mk-screen-del {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--r-sm);
  padding: 6px 9px;
  font-family: inherit;
  font-weight: 700;
  font-size: var(--fs-xs);
  cursor: pointer;
}

.mk-screen-ok {
  background: var(--purple);
  color: var(--white);
}

/* White with a border, not a tinted pill: the header sits on the same soft
   lavender, so a bg-purple button there reads as a floating outline. */
.mk-screen-del {
  background: var(--white);
  border: 1.5px solid #DCD8F0;
  color: var(--ink-soft);
}

/* Armed state — one tap arms, the next removes (same as .placed-del, #185). */
.mk-screen-del.armed {
  background: var(--bad);
  color: var(--white);
  white-space: nowrap;
}

.mk-screen-ok svg,
.mk-screen-del svg {
  width: 15px;
  height: 15px;
}

.mk-text-row {
  display: flex;
  gap: 8px;
}

.mk-text-input {
  flex: 1;
  min-width: 0;
  border: 2px solid #DCD8F0;
  border-radius: var(--r-sm);
  padding: 10px 12px;
  font-size: var(--fs-md);
  font-family: inherit;
  color: var(--ink);
  outline: none;
  background: var(--white);
  box-sizing: border-box;
}

.mk-text-input:focus {
  border-color: var(--purple);
}

.mk-text-add {
  width: auto;
  flex-shrink: 0;
  padding: 0 18px;
}

.shape-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}

.swatch-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  padding: 8px 0;
}

.swatch {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 3px solid rgba(13, 6, 91, 0.12);
  box-shadow: var(--shadow-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.12s;
  position: relative;
}

.swatch:active {
  transform: scale(0.9);
}

.swatch svg {
  width: 20px;
  height: 20px;
  opacity: 0;
  color: var(--white);
  stroke-width: 4;
  transition: opacity 0.1s;
}

.swatch.sel {
  transform: scale(1.14);
  border-color: var(--ink);
  box-shadow: 0 0 0 4px var(--comp-yellow), var(--shadow-md);
}

.swatch.sel svg {
  opacity: 1;
}

/* Yellow / white swatches need a dark check for contrast */
.swatch[data-color="#FAFF05"] svg,
.swatch[data-color="#FFFFFF"] svg {
  color: var(--ink);
}

.btn-shape {
  width: 48px;
  height: 48px;
  border-radius: var(--r-sm);
  background: var(--bg-purple);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.1s;
}

.btn-shape:active {
  transform: scale(0.9);
}

.btn-shape svg {
  width: 28px;
  height: 28px;
}

@keyframes shake {

  0%,
  100% {
    transform: translateX(0);
  }

  20%,
  60% {
    transform: translateX(-7px);
  }

  40%,
  80% {
    transform: translateX(7px);
  }
}

.creation-celebration {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(13, 6, 91, 0.85);
  backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px;
  overflow-y: auto;
}

.creation-celeb-burst {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 300px;
  height: 300px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(250, 255, 5, 0.4), transparent 70%);
  animation: nodepulse 2s infinite;
  pointer-events: none;
}

/* Finished-design celebration (ticket #199): the kid's screen(s) shown in
   a real phone frame at the true 3/4 aspect ratio (the old preview forced
   them into a 200x200 square, squashing every mobile layout), plus a
   "Deel je ontwerp" action that shares a PNG to a parent / teacher. */
.creation-celeb-inner {
  position: relative;
  z-index: 1;
  margin: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  width: 100%;
  max-width: 460px;
}

.creation-celeb-inner .coach-avatar {
  width: 64px;
  height: 64px;
  margin-bottom: 10px;
}

.creation-celeb-title {
  color: var(--white);
  font-size: var(--fs-2xl);
  margin: 0 0 4px;
}

.creation-celeb-sub {
  color: rgba(255, 255, 255, 0.75);
  font-size: var(--fs-md);
  font-weight: 500;
  margin: 0 0 22px;
}

.creation-celeb-phones {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  justify-content: center;
  max-width: 100%;
  padding: 4px;
  margin-bottom: 26px;
}

.creation-celeb-phones.is-multi {
  justify-content: flex-start;
  overflow-x: auto;
  scrollbar-width: none;
}

.creation-celeb-phones.is-multi::-webkit-scrollbar {
  display: none;
}

.creation-celeb-phones .celeb-phone {
  flex: 0 0 auto;
  background: var(--ink);
  border-radius: 30px;
  padding: 8px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);
}

.creation-celeb-phones .celeb-phone-screen {
  position: relative;
  width: min(60vw, 208px);
  aspect-ratio: 3 / 4;
  background: var(--white);
  border-radius: 22px;
  overflow: hidden;
}

.creation-celeb-phones.is-multi .celeb-phone-screen {
  width: min(40vw, 150px);
}

/* Three+ screens don't fit a phone side by side — shrink them a touch and
   let the row scroll, with the last one peeking so it's clearly there. */
.creation-celeb-phones.is-many .celeb-phone-screen {
  width: min(33vw, 132px);
}

.creation-celeb-actions {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  max-width: 320px;
}

.creation-celeb-actions .btn {
  width: 100%;
  white-space: nowrap;
}

.creation-celeb-actions .btn svg {
  width: 20px;
  height: 20px;
}

/* "Bewaar je ontwerp" sheet — the fallback when the native share sheet
   isn't available (see showSaveImage in makerij.js). The picture itself is
   the point: press-and-hold saves it on a phone, the button saves it on a
   desktop. */
.mk-save-overlay {
  position: fixed;
  inset: 0;
  z-index: 600;
  background: rgba(13, 6, 91, 0.85);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  overflow-y: auto;
}

.mk-save-box {
  position: relative;
  margin: auto;
  width: 100%;
  max-width: 460px;
  background: var(--white);
  border-radius: var(--r-lg);
  padding: 22px 20px 20px;
  text-align: center;
}

.mk-save-close {
  position: absolute;
  top: 12px;
  right: 12px;
  background: var(--bg-purple);
  color: var(--ink);
}

.mk-save-title {
  margin: 0 0 14px;
  padding-right: 40px;
  font-size: var(--fs-xl);
  text-align: left;
}

.mk-save-img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-sm);
  /* the long-press "Bewaar afbeelding" gesture needs the image itself to
     stay touchable, so no pointer-events/user-select suppression here */
}

.mk-save-hint {
  margin: 12px 0 16px;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-soft);
}

.mk-save-dl {
  width: 100%;
  text-decoration: none;
}

.mk-save-dl svg {
  width: 20px;
  height: 20px;
}

/* First-run coachmarks — scoped to the makerij (Makerij.showCoachmarks in
   makerij.js). A dim layer with one cut-out "spotlight" box positioned
   over a real on-screen element, plus a small callout card; always
   skippable, never blocks the tool underneath. */
.mk-coach-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  pointer-events: none;
}

.mk-coach-spot {
  position: fixed;
  border-radius: var(--r-md);
  box-shadow: 0 0 0 2000px rgba(13, 6, 91, 0.6), 0 0 0 3px var(--yellow);
  transition: top 0.25s ease, left 0.25s ease, width 0.25s ease, height 0.25s ease;
  pointer-events: none;
}

.mk-coach-card {
  position: fixed;
  left: 16px;
  right: 16px;
  max-width: 360px;
  margin: 0 auto;
  background: var(--white);
  border-radius: var(--r-lg);
  padding: 16px 18px;
  box-shadow: var(--shadow-md);
  pointer-events: auto;
}

.mk-coach-text {
  margin: 0 0 12px;
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
}

.mk-coach-text b {
  color: var(--purple);
}

.mk-coach-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.mk-coach-actions .btn {
  padding: 10px 20px;
  width: auto;
}

.summary-creation {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed rgba(13, 6, 91, 0.1);
}

.summary-creation-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* The "App-plan" / "De Makerij" button for a finished node — a loose,
   tactile pill UNDER the label card (not fused to it), so it clearly
   reads as tappable. Exactly one shows per node: "App-plan" when the
   step feeds the app-plan, else "De Makerij" for a standalone Maker
   exercise. celebrateMakerTools() adds .tool-reveal the first time. */
.map-node-tools {
  margin-top: 7px;
  display: flex;
  justify-content: center;
}

.map-node-creation,
.map-node-blueprint {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 11px 5px 9px;
  border: 0;
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: transform 0.1s, box-shadow 0.1s, filter 0.15s;
}

.map-node-creation {
  background: var(--ink);
  color: var(--yellow);
  box-shadow: 0 3px 0 rgba(13, 6, 91, 0.4);
}

.map-node-blueprint {
  background: var(--purple);
  color: #fff;
  box-shadow: 0 3px 0 rgba(95, 41, 255, 0.4);
}

.map-node-creation:active,
.map-node-blueprint:active {
  transform: translateY(2px);
  box-shadow: 0 1px 0 rgba(13, 6, 91, 0.4);
}

.map-node-creation svg,
.map-node-blueprint svg {
  width: 15px;
  height: 15px;
}

@media (hover: hover) {
  .map-node-creation:hover,
  .map-node-blueprint:hover { filter: brightness(1.09); }
}

/* First-earned reveal: pop the new button in, then a soft looping pulse
   that runs until the coach bubble is dismissed. */
@keyframes toolReveal {
  0%   { transform: scale(0.2) translateY(8px); opacity: 0; }
  60%  { transform: scale(1.15) translateY(0); opacity: 1; }
  80%  { transform: scale(0.95); }
  100% { transform: scale(1); }
}
@keyframes toolNudge {
  0%       { opacity: 0.6; transform: scale(1); }
  70%,100% { opacity: 0; transform: scale(1.55); }
}
.map-node-creation.tool-reveal,
.map-node-blueprint.tool-reveal {
  animation: toolReveal 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.map-node-creation.tool-nudge::after,
.map-node-blueprint.tool-nudge::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: inherit;
  border: 2.5px solid currentColor;
  opacity: 0;
  pointer-events: none;
  animation: toolNudge 1.5s ease-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .map-node-creation.tool-reveal,
  .map-node-blueprint.tool-reveal { animation: none; }
  .map-node-creation.tool-nudge::after,
  .map-node-blueprint.tool-nudge::after { animation: none; opacity: 0.5; }
}

/* Coach bubble under the freshly-earned button: hero + an arrow pointing
   up at it. Lives inside .map-step so it scrolls with the map. */
.map-step--coaching { z-index: 20; }

.tool-coach {
  position: absolute;
  top: calc(100% + 16px);
  left: 50%;
  width: 216px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--yellow);
  color: var(--ink);
  border: 2px solid var(--ink);
  border-radius: var(--r-md);
  /* Soft float, NOT the tactile 0 Xpx 0 press-shadow — this is a spoken
     note pointing at the button, it must not read as a second button. */
  box-shadow: 0 12px 26px -8px rgba(13, 6, 91, 0.45);
  padding: 8px 10px;
  font-size: var(--fs-sm);
  line-height: 1.3;
  text-align: left;
  transform: translateX(-50%);
  animation: coachIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes coachIn {
  0%   { opacity: 0; transform: translateX(-50%) scale(0.55) translateY(-6px); }
  100% { opacity: 1; transform: translateX(-50%) scale(1) translateY(0); }
}
.tool-coach.gone { animation: coachOut 0.3s ease both; }
@keyframes coachOut { to { opacity: 0; transform: translateX(-50%) translateY(10px); } }
@media (prefers-reduced-motion: reduce) { .tool-coach { animation: none; } }

/* Rotated square, half buried in the bubble — the two outward edges carry
   the ink border (the "^"), the buried half is plain yellow so it merges
   with the bubble with no seam line where they meet. */
.tool-coach .tc-arrow {
  position: absolute;
  top: -8px;
  left: 50%;
  width: 15px;
  height: 15px;
  margin-left: -7.5px;
  background: var(--yellow);
  border-top: 2px solid var(--ink);
  border-left: 2px solid var(--ink);
  border-top-left-radius: 3px;
  transform: rotate(45deg);
}
.tool-coach .tc-hero {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  overflow: hidden;
  background: #fff;
  border: 2px solid var(--ink);
}
.tool-coach .tc-hero svg { width: 100%; height: 100%; }
.tool-coach .tc-text b { font-weight: 900; }

/* The chapter header's action icons (app-plan + mission log) sit together
   on the right; both use .chapter-banner-icon. The app-plan one carries a
   purple tint so it reads as "your plan", not another nav control. */
.chapter-banner-actions {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
}
.chapter-banner-plan {
  color: var(--purple);
}
.chapter-sticky.is-stuck .chapter-banner-plan {
  background: var(--purple);
  color: #fff;
}

/* ---------- Chapter-intro Lesson banner (on the map) ---------- */
.chapter-lesson {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  margin-top: 14px;
  margin-bottom: 40px;
  padding: 12px 14px;
  border-radius: var(--r-md);
  background: var(--white);
  box-shadow: var(--shadow-sm);
  border: 2.5px solid var(--chapter-accent, var(--purple));
  position: relative;
  z-index: 3;
  transition: transform 0.12s ease, box-shadow 0.15s ease;
}

.chapter-lesson:active {
  transform: scale(0.98);
}

.chapter-lesson.is-done {
  border-style: dashed;
  opacity: 0.92;
}

.chapter-lesson-ico {
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--chapter-accent, var(--purple));
  color: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
}

.chapter-lesson-ico svg {
  width: 22px;
  height: 22px;
}

.chapter-lesson-text {
  flex: 1;
  min-width: 0;
  color: var(--ink);
}

.chapter-lesson-text b {
  display: block;
  font-size: var(--fs-md);
  line-height: 1.15;
}

.chapter-lesson-text span {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  font-weight: 500;
}

.chapter-lesson-cta {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--bg-purple);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
}

.chapter-lesson:not(.is-done) .chapter-lesson-cta {
  background: var(--yellow);
}

.chapter-lesson-cta svg {
  width: 17px;
  height: 17px;
}

/* ---------- Lesson screen (chapter-intro module player) ---------- */
.lesson-stage {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  width: 100%;
  max-width: var(--appw);
  margin-left: auto;
  margin-right: auto;
  padding: calc(10px + var(--safe-top)) 16px 0;
}

.lesson-progress {
  height: 8px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.35);
  overflow: hidden;
  margin: 8px 0 14px;
}

.lesson-progress-fill {
  height: 100%;
  background: var(--yellow);
  border-radius: var(--r-pill);
  transition: width 0.35s ease;
}

/* Big centered avatar + floating control pill (matches /games/trail) */
.lesson-avatar-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin: 6px 0 16px;
}

.bella-big {
  position: relative;
  width: 156px;
  height: 156px;
  border-radius: 50%;
  background: linear-gradient(to bottom, rgba(214, 220, 255, 0.5), var(--white));
  border: 4px solid var(--white);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

/* Crops the full-body rig down to a head-shot. The shift is per-size:
   it scales with the frame, so a smaller frame needs a smaller nudge. */
.bella-zoom {
  position: absolute;
  inset: 0;
  transform: scale(2) translateY(var(--bella-shift, -40px));
  transform-origin: center;
}

.bella-zoom canvas {
  width: 100%;
  height: 100%;
  display: block;
}

/* Shown only if the Rive avatar can't load — Lucide, like every other icon. */
.bella-fallback {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--purple);
}

.bella-fallback svg {
  width: 46%;
  height: 46%;
  stroke-width: 1.8;
}

.bella-face .bella-fallback svg {
  width: 52%;
  height: 52%;
}

.bella-big.speaking .bella-fallback {
  animation: talkbob 0.4s ease-in-out infinite;
}

@keyframes talkbob {

  0%,
  100% {
    transform: translateY(0) scale(1);
  }

  50% {
    transform: translateY(-3px) scale(1.06);
  }
}

.voice-pill {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px 5px 5px;
  background: var(--white);
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-md);
}

.voice-play {
  position: relative;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.voice-play.is-disabled {
  opacity: 0.4;
  pointer-events: none;
}

.voice-ring-svg {
  position: absolute;
  inset: 0;
  width: 44px;
  height: 44px;
  transform: rotate(-90deg);
  pointer-events: none;
}

.voice-ring-bg {
  fill: none;
  stroke: rgba(13, 6, 91, 0.08);
  stroke-width: 3;
}

.voice-ring {
  fill: none;
  stroke: var(--purple);
  stroke-width: 3;
  stroke-linecap: round;
  transition: stroke-dashoffset 0.15s linear;
}

.voice-play-ico {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--purple);
  color: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}

.voice-play-ico svg {
  width: 17px;
  height: 17px;
}

.voice-play:active .voice-play-ico {
  transform: scale(0.92);
}

.voice-sep {
  width: 1px;
  height: 18px;
  background: rgba(13, 6, 91, 0.1);
}

.voice-mini {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* display:flex above would otherwise beat the UA's [hidden] rule */
.voice-mini[hidden],
.voice-sep[hidden] {
  display: none;
}

.voice-mini svg {
  width: 16px;
  height: 16px;
}

.voice-mini:active {
  transform: scale(0.9);
  color: var(--purple);
}

.voice-cc.on {
  background: var(--purple);
  color: var(--white);
}

/* muted: the one control that says "she is off right now" */
.voice-mute.is-off {
  background: var(--bg-red);
  color: var(--bad);
}

.spin {
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Karaoke captions — one cue at a time, words light up as spoken */
.lesson-caption {
  max-width: 100%;
  margin: 0 auto 14px;
  padding: 11px 16px;
  background: var(--ink);
  border-radius: var(--r-md);
  text-align: center;
  font-weight: 700;
  font-size: var(--fs-md);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.5);
  animation: popin 0.25s ease both;
}

.lesson-caption[hidden] {
  display: none;
}

.lesson-caption span {
  transition: color 0.1s ease;
}

.lesson-caption span.spoken {
  color: var(--white);
}

.lesson-caption span.now {
  color: var(--ink);
  background: var(--yellow);
  border-radius: 6px;
  padding: 1px 5px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* padding-bottom for the same reason as .round-area: without it, a long
   lesson can end exactly at the document's bottom edge with no slack to
   scroll clear of the mobile browser's floating bottom toolbar. */
.lesson-body {
  flex: 1;
  padding-bottom: calc(24px + var(--safe-bottom, 0px));
}

.lesson-loading {
  color: var(--white);
  text-align: center;
  padding: 40px 0;
  font-weight: 500;
  opacity: 0.85;
}

.lesson-card {
  background: var(--white);
  border-radius: var(--r-lg);
  padding: 20px 18px;
  box-shadow: var(--shadow-md);
}

.lesson-h {
  font-size: var(--fs-2xl);
  font-weight: 700;
  line-height: 1.2;
}

.lesson-p {
  margin-top: 8px;
  color: var(--ink-soft);
  font-weight: 500;
  line-height: 1.4;
}

/* tools/achievements/tips are now key points — see "EXPLANATION STYLE" below.
   They were tinted boxes, which read as buttons (and the yellow tip box read
   as the primary button exactly). */

/* ============================================================
   EXPLANATION STYLE — "key points"
   ------------------------------------------------------------
   The rule this encodes: in PlayQuest an enclosed, filled shape
   means "tap me" — .btn, .opt-icon, .quiz-opt and .pp-btn are
   all a filled box with a hard bottom edge. So teaching content
   must never be a filled box, or a kid reads a list of facts as
   a row of dead buttons and waits for something to happen.

   Explanation is OPEN instead: no fill, no border, no elevation.
   Points are grouped by a shared marker column and steady rhythm
   rather than by boxes — one visible set, obviously un-tappable.
   Only the marker changes with the content: a dot for plain
   points or contrast pairs, a number (plus a connecting rail)
   for ordered steps. That's the extension point — any new kind
   of grouped content gets a marker, not a new box.
   ============================================================ */
.lesson-points {
  margin: 14px 0 2px;
}

.lesson-reveal[hidden] {
  display: none;
}

/* the one line that frames the whole slide */
.lp-lead {
  border-left: 3px solid var(--comp-purple);
  padding-left: 12px;
  margin-bottom: 18px;
  font-size: var(--fs-lg);
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
  white-space: pre-line;
}

.lp-list {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.lp-item {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

/* The rail is a claim about ORDER, so only an ordered run gets one. Drawn
   per item from just under one marker to the next, which keeps it exact
   whatever height the copy runs to — a single line down the whole list
   leaves stubs above the first marker and below the last. */
.lp-list.is-ordered .lp-item:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 12px;
  top: 30px;
  bottom: -22px;
  width: 2px;
  border-radius: 1px;
  background: var(--bg-purple);
}

/* ---- markers: the one thing that varies by content type ----
   dot  = a plain point or a contrast pair
   num  = an ordered step (gets the rail)
   icon = a named thing (a tool, an achievement, a tip)
   Themes tint the marker. That's where an author's colour goes now —
   tinting a box would put the enclosure straight back. */
.lp-marker {
  --mk: var(--purple);
  /* the point's accent; themes reassign it */
  position: relative;
  flex: 0 0 26px;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: var(--mk);
}

.lp-dot::after {
  content: "";
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--mk);
}

.lp-num {
  margin-top: -1px;
  background: var(--mk);
  color: var(--white);
  /* the digit, not the disc */
  font-size: var(--fs-base);
  font-weight: 700;
}

.lp-icon {
  background: color-mix(in srgb, var(--mk) 14%, transparent);
}

.lp-icon svg {
  width: 15px;
  height: 15px;
  stroke-width: 2.4;
}

/* theme tints — authored per tool/tip, applied to the marker only */
.lp-t-purple {
  --mk: var(--purple);
}

.lp-t-blue {
  --mk: #3D5AFE;
}

.lp-t-green {
  --mk: var(--good-dark);
}

.lp-t-red {
  --mk: #D9427F;
}

.lp-t-yellow {
  --mk: #B08900;
}

.lp-t-tip {
  --mk: #B08900;
}

.lp-body {
  flex: 1;
  padding-top: 1px;
  min-width: 0;
}

.lp-title {
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: 1.3;
}

.lp-desc {
  margin-top: 2px;
  font-size: var(--fs-md);
  font-weight: 500;
  line-height: 1.45;
  color: var(--ink-soft);
  white-space: pre-line;
}

/* a point with no title carries its copy at full strength */
.lp-body>.lp-desc:first-child {
  color: var(--ink);
  margin-top: 3px;
}

/* nested detail under a point (a tool's observations) */
.lp-sub {
  margin: 7px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.lp-sub li {
  position: relative;
  padding-left: 14px;
  font-size: var(--fs-md);
  font-weight: 500;
  line-height: 1.4;
  color: var(--ink-soft);
}

.lp-sub li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 9px;
  width: 6px;
  height: 2px;
  border-radius: 1px;
  background: rgba(13, 6, 91, 0.25);
}

/* ---- "the one Bella is on" (highlightAtSrtIndex) ----
   The authored design filled the live item with a solid purple block. That
   shape is this app's "tap me" (.btn, .opt-icon, .quiz-opt), so a kid would
   sit waiting for three facts to do something. Same meaning, carried the way
   the rest of the system carries it: the live item comes forward — full-ink
   text, marker up — and its neighbours step back. The marker doing the work
   also matches .rdot.now on the mission top bar, which is already how this
   game says "this is the one, now".

   showAtSrtIndex hides an item until its cue. Only ever set by the player
   once it knows it can reveal it again. */
.lp-item[hidden],
.lp-sub li[hidden] {
  display: none;
}

.lp-item,
.lp-sub li {
  transition: opacity 0.25s ease, color 0.25s ease;
}

.lp-marker {
  transition: transform 0.25s ease;
}

.lp-list.has-now .lp-item:not(.is-now),
.lp-sub.has-now li:not(.is-now) {
  opacity: 0.4;
}

.lp-item.is-now .lp-title,
.lp-item.is-now .lp-desc {
  color: var(--ink);
}

.lp-item.is-now .lp-marker {
  transform: scale(1.18);
}

.lp-sub li.is-now {
  color: var(--ink);
  font-weight: 600;
}

.lp-sub li.is-now::before {
  background: var(--purple);
  width: 10px;
}

/* a heading over a run of points (text columns) */
.lp-heading {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 12px;
}

/* Bringing it to life: stages are cumulative, so the only genuinely new
   thing on screen is the last point — animate that one alone. Animating
   them all would re-run the whole list on every cue and read as a flicker.
   The global prefers-reduced-motion rule neutralises both. */
.lesson-reveal:not([hidden]) .lp-item:last-child {
  animation: point-in 0.4s cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
}

.lesson-reveal:not([hidden]) .lp-item:last-child .lp-marker {
  animation: marker-pop 0.45s cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
}

@keyframes point-in {
  from {
    opacity: 0;
    transform: translateY(9px);
  }
}

@keyframes marker-pop {
  0% {
    transform: scale(0.4);
  }

  60% {
    transform: scale(1.15);
  }

  100% {
    transform: scale(1);
  }
}

/* Link tile → opens another tool (optionally a new tab).
   align-self: stretch makes tiles on the same wrapped row share the tallest
   height instead of each being its own content height. */
.lesson-tool-link {
  display: flex;
  align-items: center;
  gap: 12px;
  align-self: stretch;
  text-decoration: none;
  color: var(--ink);
  background: var(--bg-blue);
  border-radius: var(--r-md);
  padding: 12px 14px;
  border: 2.5px solid transparent;
  transition: transform 0.12s ease, border-color 0.15s ease;
}

.lesson-tool-link:active {
  transform: scale(0.98);
}

.lesson-tool-link:hover {
  border-color: var(--purple);
}

.lesson-tool-link img {
  width: 52px;
  height: 52px;
  border-radius: var(--r-sm);
  object-fit: cover;
  flex: 0 0 auto;
  box-shadow: none;
}

.tool-link-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.tool-link-title {
  font-weight: 700;
  font-size: var(--fs-md);
  line-height: 1.15;
}

.tool-link-sub {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  font-weight: 500;
}

.tool-link-go {
  flex: 0 0 auto;
  color: var(--purple);
  display: flex;
}

.tool-link-go svg {
  width: 18px;
  height: 18px;
}

/* Downloadable file (PDF / template) */
.lesson-download {
  text-align: center;
}

.dl-preview {
  max-width: 100%;
  max-height: 300px;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  margin-bottom: 12px;
}

.dl-desc {
  margin-bottom: 12px;
  margin-top: 0;
}

.dl-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: auto;
  text-decoration: none;
}

.dl-btn svg {
  width: 18px;
  height: 18px;
}

/* Rich content: image + text columns and multi-part steps */
.lesson-cols {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 14px;
  align-items: stretch;
}

@media (min-width: 600px) {
  .lesson-cols {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
  }
}

.lesson-col {
  flex: 1;
  min-width: 0;
}

.lesson-cols.cols-img {
  justify-content: center;
}

/* Link tiles: a list on phones, two per row when there's room. Tiles need
   ~250px for title, subtitle and the external-link arrow to sit side by side. */
.lesson-cols.cols-links {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}

@media (min-width: 600px) {
  .lesson-cols.cols-links {
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  }
}

.lesson-col-img {
  margin: 0;
  text-align: center;
}

.lesson-col-img img {
  max-width: 100%;
  max-height: 380px;
  border-radius: var(--r-md);
  object-fit: contain;
  box-shadow: var(--shadow-sm);
}

.lesson-col-img figcaption {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  margin-top: 8px;
  font-weight: 500;
}

/* A step driven by synced beats (content.reveals) shows exactly ONE thing at
   a time — a title plus an image plus explanatory text all at once is too
   much for a phone screen above the fold. Each beat is either a short text
   line or an image, swapping to the next as Bella's narration reaches its
   anchor phrase. See lesson.js's showReveal/applyReveals. */
.lesson-reveal-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 220px;
  margin-top: 14px;
  padding: 24px 16px;
  text-align: center;
}

.lesson-reveal-stage[hidden] {
  display: none;
}

.lesson-reveal-icon {
  display: inline-flex;
  color: var(--purple);
}

.lesson-reveal-icon[hidden] {
  display: none;
}

.lesson-reveal-icon svg {
  width: 48px;
  height: 48px;
}

.lesson-reveal-text {
  margin: 0;
  font-size: clamp(24px, 6vw, 36px);
  font-weight: 800;
  color: var(--ink);
}

.lesson-reveal-imgwrap {
  position: relative;
  width: 100%;
  display: flex;
  justify-content: center;
}

/* All of one step's callouts share one side (content.reveals[].callouts —
   see showReveal in lesson.js, and paper-sketch's alternating steps), so the
   image shifts toward the OTHER side instead of staying centered — the
   whole unused half becomes room for that side's labels, wide enough for
   them to run at a readable size instead of squeezed into a narrow margin. */
.lesson-reveal-imgwrap.callouts-left {
  justify-content: flex-end;
}

.lesson-reveal-imgwrap.callouts-right {
  justify-content: flex-start;
}

.lesson-reveal-img {
  max-width: 100%;
  max-height: 420px;
  border-radius: var(--r-md);
  object-fit: contain;
  box-shadow: var(--shadow-sm);
}

.lesson-reveal-imgwrap.callouts-left .lesson-reveal-img,
.lesson-reveal-imgwrap.callouts-right .lesson-reveal-img {
  max-width: 52%;
  max-height: 380px;
}

/* At phone width there isn't 48% of ~380px left over for a word like
   "Terugknop" to fit on one line — it was breaking mid-word. Give the
   label side more of the card at narrow viewports; --img-w (set from the
   image's real rendered pixel width, see layoutCallouts in lesson.js)
   follows automatically since callouts position off that, not a fixed %. */
@media (max-width: 380px) {
  .lesson-reveal-imgwrap.callouts-left .lesson-reveal-img,
  .lesson-reveal-imgwrap.callouts-right .lesson-reveal-img {
    max-width: 42%;
  }
}

.reveal-callouts {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Cumulative labels beside the image (content.reveals[].callouts), each
   pointing at the image with a plain arrow.
   Anchored beside the image so all arrows on a side align in a crisp vertical line,
   with text aligned flush to the arrow and the arrow aligned with the first line. */
.reveal-callout {
  position: absolute;
  top: 0;
  transform: translateY(-50%);
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: clamp(13px, 3.4vw, 15px);
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
  box-sizing: border-box;
}

.reveal-callout.callout-left {
  left: 6px;
  right: calc(var(--img-w, 52%) + 12px);
  max-width: none;
  text-align: right;
  justify-content: flex-end;
}

.reveal-callout.callout-right {
  left: calc(var(--img-w, 52%) + 12px);
  right: 6px;
  max-width: none;
  text-align: left;
  justify-content: flex-start;
}

.reveal-callout-arrow {
  flex: 0 0 auto;
  color: var(--purple);
  font-size: 1.15em;
  line-height: 1.25;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.reveal-callout b {
  flex: 0 1 auto;
  font-weight: 700;
  word-break: normal;
  overflow-wrap: break-word;
}

/* the global `img, svg { display: block }` reset otherwise beats the
   browser's native [hidden] rule (author-origin always wins over
   user-agent, regardless of specificity) and the image never actually
   disappears between beats. */
.lesson-reveal-img[hidden] {
  display: none;
}

.lesson-reveal-svgwrap {
  width: 100%;
  max-width: 420px;
}

.lesson-reveal-svgwrap svg {
  width: 100%;
  height: auto;
}

/* Same [hidden]-defeated-by-the-global-reset risk as .lesson-reveal-img
   above, but here it's the wrapper AND every toggled <g> inside the
   inlined drawing — belt and suspenders for both. */
.lesson-reveal-svgwrap[hidden],
.lesson-reveal-svgwrap [hidden] {
  display: none;
}

.lesson-reveal-tiles {
  display: flex;
  flex-wrap: wrap;
  gap: 20px 16px;
  justify-content: center;
  padding: 4px 8px;
}

/* Icon-badge-with-caption, the same shape this app already uses for an
   achievement (.lesson-done-ico) — never a filled rounded-rect with a
   drop-shadow and a :active press state, which is what a button looks
   like here (.btn, .bpk-tile). A circle + a word underneath reads as a
   sticker, not a control; nothing to teach a kid to poke for nothing. */
.reveal-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 96px;
}

.reveal-tile-icon {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: var(--bg-purple);
  color: var(--purple);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}

.reveal-tile-icon svg {
  width: 30px;
  height: 30px;
}

.reveal-tile b {
  font-size: var(--fs-sm);
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
  text-align: center;
}

.lesson-reveal-stage.reveal-pop > * {
  animation: lessonRevealPop .4s cubic-bezier(.2, .9, .3, 1.3);
}

/* Same pop, but scoped to ONE just-revealed piece (an svg <g>/<text>, or one
   new tile) instead of the whole stage — an svg/tiles beat only adds to
   what's already there, so re-popping everything already on screen would
   read as the whole drawing blinking every time one more part of it shows
   up. transform-box/-origin keep the scale centered on the element itself;
   an SVG element's default origin is the drawing's (0,0), not its own
   middle, which without this reads as a little jump toward the corner. */
.reveal-pop-in {
  animation: lessonRevealPop .35s cubic-bezier(.2, .9, .3, 1.3);
  transform-box: fill-box;
  transform-origin: center;
}

@keyframes lessonRevealPop {
  from { opacity: 0; transform: translateY(8px) scale(.9); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* text columns render as key points now (.lp-heading + .lp-list). */
.lesson-part {
  margin-top: 16px;
}

.lesson-part+.lesson-part {
  border-top: 1px dashed rgba(13, 6, 91, 0.12);
  padding-top: 14px;
}

.lesson-part-title {
  font-weight: 700;
  font-size: var(--fs-lg);
  margin-bottom: 4px;
  color: var(--ink);
}

/* tips render as key points now (amber lightbulb marker). */

/* ---------- Video content (local mp4/webm/ogg, Vimeo, YouTube, Visla) ----------
   One definition, not three — this used to be three separate, later-wins
   .lesson-video rules scattered across the file (a 16:9 relative box, a
   9:16 block box, and a flex/min-height box), each overriding a few of the
   previous one's properties and leaving the rest, so the real computed box
   was whichever Frankenstein merge of all three the cascade produced. That
   inconsistent shape is what made the play button (centered via
   position:absolute on this box) look "oddly positioned" — it was
   centering correctly on a box that wasn't the one actually visible.

   16/9 is just the default before a clip's real shape is known. --video-ar
   is set from the video's own videoWidth/videoHeight once metadata loads
   (bindLessonVideo, lesson.js) — always exact, unlike a guess. is-portrait-
   hint (from the fullHeightVideo content flag) only picks a better-looking
   *default* for the brief window before that, so a portrait clip doesn't
   flash in a landscape-shaped box first. max-height keeps a portrait clip
   from ever dominating the screen; object-fit:contain plus the black
   background turns any mismatch into ordinary letterboxing instead of a
   stretched or cropped picture. */
.lesson-video {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 70vh;
  margin: 14px auto 0;
  border-radius: var(--r-md);
  overflow: hidden;
  background: #000;
}

.lesson-video.is-portrait-hint {
  aspect-ratio: 9 / 16;
}

.lesson-video.has-real-ar {
  aspect-ratio: var(--video-ar);
}

.lesson-video iframe,
.lesson-video video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  object-fit: contain;
  background: #000;
}

/* Full-cover, not just a small centered circle — the whole frame is the
   tap target (easier to hit than a hand-rolled play icon ever was), and
   the gradient scrim is what makes a plain white button read clearly
   against any poster frame, light or dark. Hides once, on first play, and
   stays hidden — see bindLessonVideo's comment in lesson.js for why this
   is no longer wired to every play/pause/ended event. */
.video-play-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: linear-gradient(180deg, rgba(13, 6, 91, 0.1), rgba(13, 6, 91, 0.4));
  cursor: pointer;
  transition: opacity 0.2s ease;
}

.video-play-overlay-icon {
  width: 68px;
  height: 68px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--white);
  box-shadow: var(--shadow-md);
  transition: transform 0.15s ease;
}

.video-play-overlay:active .video-play-overlay-icon {
  transform: scale(0.92);
}

.video-play-overlay-icon svg {
  width: 30px;
  height: 30px;
  color: var(--ink);
  margin-left: 3px;
  /* optical centering for a play triangle */
  fill: var(--ink);
}

.lesson-video.is-playing .video-play-overlay {
  opacity: 0;
  pointer-events: none;
}

/* Lesson stops on the map path */
.map-node.is-lesson.node-open,
.map-node.is-lesson.node-done {
  background: var(--ink);
}

.map-node.is-lesson .map-node-icon {
  color: var(--yellow);
}

/* Tip stops: same lesson node underneath, distinct color/icon on the map.
   Reuses #B08900, the same amber already used for tip callouts in
   Lessons (.lp-t-tip's --mk, line 5519). */
.map-node.is-tip.node-open,
.map-node.is-tip.node-done {
  background: #4318CC;
}

.map-node.is-tip .map-node-icon {
  color: var(--white);
}

.quiz-q {
  font-size: var(--fs-lg);
  font-weight: 700;
  margin: 14px 0;
  line-height: 1.25;
}

.quiz-opt {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  background: var(--white);
  border: 2px solid #DCD8F0;
  border-radius: var(--r-md);
  padding: 13px 14px;
  margin-bottom: 14px;
  font-weight: 500;
  transition: all 0.12s ease;
  box-shadow: 0 4px 0 #CBC5E6;
  color: var(--ink);
}

.quiz-opt .qo-ico {
  flex: 0 0 auto;
  display: flex;
  color: var(--purple);
}

.quiz-opt .qo-ico svg {
  width: 20px;
  height: 20px;
}

.quiz-opt:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 #CBC5E6;
}

.quiz-opt.correct {
  background: var(--comp-green);
  border-color: var(--good);
  box-shadow: 0 4px 0 var(--good-dark);
}

.quiz-opt.correct:active {
  box-shadow: 0 1px 0 var(--good-dark);
}

.quiz-opt.wrong {
  background: var(--bg-red);
  border-color: var(--bad);
  box-shadow: 0 4px 0 #D96060;
}

.quiz-opt.wrong:active {
  box-shadow: 0 1px 0 #D96060;
}

/* ---- lesson footer ----
   Quiz feedback used to appear inline under the options, in its own colours,
   while a mission put it in a sheet on the bottom bar. Same moment, two
   different answers. Now the lesson reuses the mission's .round-feedback
   sheet, and it shares one sticky unit with the Terug/Verder bar so the
   result lands attached to the buttons rather than floating mid-card. */
.lesson-foot {
  position: sticky;
  bottom: 0;
  z-index: 100;
}

.lesson-foot .round-feedback {
  position: static;
  margin: 0 -16px;
  padding: 14px 20px 12px;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: 0 -10px 24px rgba(13, 6, 91, 0.10);
}

.lesson-foot .mission-actions {
  position: static;
}

/* the sheet already separates the footer from the content behind it */
.lesson-foot:has(.round-feedback:not([hidden])) .mission-actions {
  box-shadow: none;
}

/* Quiz feedback is the sheet's only copy (no heading above it), so it reads
   at full strength rather than as a sub-line under a title. */
.fb-lead {
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
}

/* ---------- Chapter intro ("trailer") ----------
   Full-screen hype-up for one chapter — tapped from the map's banner/
   subtitle chip, or shown automatically right after finishing the chapter
   before it (see returnToMapOrChapterIntro in app.js). Deliberately built
   like a small movie-trailer beat (mascot face-off, one-line taunt/comeback,
   a peek at what's inside) rather than a form: this is the screen doing
   marketing for the chapter, not just routing to it. --ci-dark/--ci-main
   come from the chapter's own PALETTES color, set inline per screen. */
/* No overflow:hidden here — same reason as .reward-stage: it would make this
   the scrolling ancestor and break .chintro-actions' position:sticky below. */
/* The gradient lives on the screen element (full viewport width, never
   capped) so it bleeds edge to edge; .chintro-stage below is just the
   centered, width-capped content column sitting on top of it — same
   split as .app/.page-wrap elsewhere. */
#screen-chapter-intro,
#screen-chapter-outro {
  background: linear-gradient(180deg, var(--ci-dark) 0%, var(--ci-main) 60%, var(--ci-dark) 100%);
}

.chintro-stage {
  position: relative;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  width: 100%;
  max-width: var(--appw);
  margin-left: auto;
  margin-right: auto;
  padding: calc(16px + var(--safe-top)) 22px 0;
  color: var(--white);
}

/* A big pale numeral behind the header, echoing .chapter-banner-ghost on the
   map — ties this screen back to the banner it was opened from, and gives
   the upper half presence instead of leaving it looking sparse. */
.chintro-ghost-num {
  position: absolute;
  top: calc(30px + var(--safe-top));
  left: 50%;
  transform: translateX(-50%);
  font-size: 220px;
  font-weight: 800;
  line-height: 1;
  color: var(--white);
  opacity: 0.08;
  pointer-events: none;
  user-select: none;
  z-index: 0;
}

/* Close button + status pill live in normal flow now, not absolutely
   positioned off env(safe-area-inset-top) directly — same pattern as
   .mission-top elsewhere in this app. iOS Safari has been seen reporting a
   stale (too-small) safe-area-inset-top on the very first screen shown in a
   session, before anything has forced it to settle (see the map's own fight
   with this class of bug, commits 4318655/a91e1cb/3b0b6e2) — an absolutely
   positioned element computed off that value can end up rendered too high,
   under the notch. In flow, this bar just gets pushed down by whatever
   .chintro-stage's own padding-top resolves to, in lockstep with everything
   else on the screen, so it can't end up wrong on its own. */
.chintro-topbar {
  position: relative;
  z-index: 5;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.chintro-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: rgba(255, 255, 255, 0.16);
  border: 1.5px solid rgba(255, 255, 255, 0.32);
  color: var(--white);
  font-size: var(--fs-sm);
  font-weight: 700;
  padding: 7px 12px;
  border-radius: var(--r-pill);
}

.chintro-status-pill svg {
  width: 13px;
  height: 13px;
}

.chintro-status-done {
  background: var(--good);
  border-color: transparent;
  color: var(--white);
}

.chintro-scene {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 320px;
  height: 190px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.chintro-burst {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 320px;
  height: 320px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.35), transparent 65%);
  animation: nodepulse 2.4s infinite;
}

/* Entrance (bouncein, transform: scale+opacity) + a "talking" nod timed to
   its speech bubble popping in (standalone scale:) + a settled idle bob
   (standalone translate:) once the exchange is done — three different CSS
   properties so they layer instead of one animation clobbering another.
   Defined as one rule (not the shared .anim-bounce-in utility class) so the
   `animation` shorthand isn't split across two competing selectors. */
.chintro-hero {
  position: relative;
  z-index: 2;
  width: 156px;
  animation:
    bouncein 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both,
    chintro-hero-talk 0.5s ease 1.15s both,
    chintro-hero-idle 2.8s ease-in-out 1.75s infinite;
}

.chintro-hero svg {
  width: 100%;
  display: block;
  filter: drop-shadow(0 10px 16px rgba(13, 6, 91, 0.25));
}

@keyframes chintro-hero-talk {

  0%,
  100% {
    scale: 1;
  }

  35% {
    scale: 1.1;
  }

  65% {
    scale: 0.96;
  }
}

@keyframes chintro-hero-idle {

  0%,
  100% {
    translate: 0 0;
  }

  50% {
    translate: 0 -8px;
  }
}

/* Same layering trick, mirrored: entrance (popk, transform) + a shake timed
   to Buggie's own bubble (standalone rotate:) + an idle shifty sway (also
   rotate:, but never active at the same time as the shake, so no conflict).
   The static horizontal flip moves to the standalone scale: property (was
   transform: scaleX(-1)) so it can't get clobbered by popk's transform. */
.chintro-villain {
  position: relative;
  z-index: 3;
  width: 92px;
  margin-left: -24px;
  scale: -1 1;
  animation:
    popk 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s both,
    chintro-villain-talk 0.7s ease 0.6s both,
    chintro-villain-idle 3.2s ease-in-out 1.75s infinite;
}

.chintro-villain svg {
  width: 100%;
  display: block;
  filter: drop-shadow(0 8px 12px rgba(13, 6, 91, 0.25));
}

@keyframes chintro-villain-talk {

  0%,
  100% {
    rotate: 0deg;
  }

  20% {
    rotate: -9deg;
  }

  40% {
    rotate: 7deg;
  }

  60% {
    rotate: -5deg;
  }

  80% {
    rotate: 3deg;
  }
}

@keyframes chintro-villain-idle {

  0%,
  100% {
    rotate: -4deg;
  }

  50% {
    rotate: 4deg;
  }
}

/* Buggie's eyes (see the .buggie-eyes <g> in data.js's BuggieSVG) blink on
   their own loop — scoped here only, so every other place Buggie's face
   renders across the app stays exactly as it was. */
.chintro-villain .buggie-eyes {
  transform-box: fill-box;
  transform-origin: center;
  animation: chintro-blink 4.2s ease-in-out 2.4s infinite;
}

@keyframes chintro-blink {

  0%,
  92%,
  100% {
    scale: 1 1;
  }

  96% {
    scale: 1 0.12;
  }
}

.chintro-captions {
  position: relative;
  z-index: 1;
  margin-top: -6px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  max-width: 300px;
}

/* Real chat bubbles, not italic caption lines — .bubble-villain/.bubble-hero
   (mission briefings) already established "dark bubble = villain, white
   bubble = hero"; reused here so a returning player reads it instantly. A
   small tail on each one, pointing back up at the character it belongs to
   (Buggie's on the right, the hero's on the left, matching the scene above),
   is what makes these read as speech instead of captions underneath a
   photo — full side-by-side "bubble next to character" comic panels don't
   fit here without shrinking the big dual-character hero shot, which is the
   screen's best screenshot moment. Buggie's line lands first, the hero's a
   beat later (see animation-delay below), each popping in right as its
   character does its talk animation. */
.chintro-bubble {
  position: relative;
  max-width: 84%;
  padding: 10px 14px;
  border-radius: var(--r-md);
  font-size: var(--fs-base);
  line-height: 1.42;
  font-weight: 500;
  text-align: left;
  text-wrap: pretty;
  box-shadow: var(--shadow-md);
  animation: popin 0.35s ease both;
}

.chintro-bubble::after {
  content: '';
  position: absolute;
  top: -7px;
  width: 0;
  height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
}

.chintro-bubble b {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  margin-bottom: 3px;
  opacity: 0.75;
}

.chintro-bubble-villain {
  align-self: flex-end;
  background: rgba(13, 6, 91, 0.85);
  color: var(--white);
  animation-delay: 0.6s;
}

.chintro-bubble-villain::after {
  right: 22px;
  border-bottom: 8px solid rgba(13, 6, 91, 0.85);
}

.chintro-bubble-villain b {
  color: var(--yellow);
  opacity: 0.95;
}

.chintro-bubble-hero {
  align-self: flex-start;
  background: var(--white);
  color: var(--ink);
  animation-delay: 1.15s;
}

.chintro-bubble-hero::after {
  left: 22px;
  border-bottom: 8px solid var(--white);
}

.chintro-bubble-hero b {
  color: var(--purple);
}

/* The tap-to-hear badge (.ask-speak, absolutely placed) sits on the bubble's
   own bottom-right corner; keep room so the last line of text never runs
   underneath it (#288). */
.chintro-bubble[data-voice] {
  padding-right: 42px;
}

/* Leads the screen — chapter number, title and tagline answer "what is
   this" before the mascots get their moment below. A player (or a
   screenshot) glancing at just the top of this screen should already know
   exactly which chapter this is. */
.chintro-header {
  position: relative;
  z-index: 1;
  margin-top: 18px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
}

.chintro-kicker {
  font-size: var(--fs-sm);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.8;
}

.chintro-title {
  font-size: var(--fs-hero);
  font-weight: 800;
  line-height: 1.12;
  text-wrap: balance;
}

.chintro-tagline {
  font-size: var(--fs-md);
  line-height: 1.5;
  opacity: 0.92;
  max-width: 320px;
  text-wrap: pretty;
}

/* Maker pack preview notice (#387): shown over everything on every ?pack=
   load, so a shared link can never pass as an official Techtok lesson. */
.pack-notice {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: grid;
  place-items: center;
  padding: calc(20px + env(safe-area-inset-top, 0px)) 20px calc(20px + env(safe-area-inset-bottom, 0px));
  background: rgba(13, 6, 91, 0.6);
}

.pack-notice-card {
  width: 100%;
  max-width: 420px;
  background: var(--white);
  color: var(--ink);
  border-radius: var(--r-lg);
  padding: 28px 24px 24px;
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.pack-notice-card h2 {
  font-size: var(--fs-2xl);
  line-height: 1.15;
}

.pack-notice-card p {
  font-size: var(--fs-md);
  line-height: 1.5;
  color: var(--ink-soft);
}

.pack-notice-card b {
  color: var(--ink);
}

.pack-notice-pill {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--purple);
  color: var(--white);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 4px 12px;
  border-radius: var(--r-pill);
}

.pack-notice-pill svg {
  width: 14px;
  height: 14px;
}

.pack-notice-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 8px;
}

.pack-notice-actions .btn {
  flex: 1 1 160px;
  font-size: var(--fs-md);
  text-decoration: none;
}

/* Credit line for an outside lesson maker (#387): small and quiet, under
   the tagline, so it reads as a byline and never competes with the title. */
.chintro-maker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-sm);
  font-weight: 700;
  opacity: 0.8;
}

.chintro-maker svg,
.chintro-maker i {
  width: 14px;
  height: 14px;
}

.chintro-preview {
  width: 100%;
  margin-top: 34px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.chintro-preview-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.65;
}

/* Wraps and centers rather than scrolling sideways — a chapter's whole
   curriculum should read at a glance (this screen doubles as a shareable
   summary), not hide behind a swipe with a cut-off chip as the only hint
   there's more. */
.chintro-preview-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  width: 100%;
  justify-content: center;
  padding: 2px 4px;
}

.chintro-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(255, 255, 255, 0.14);
  border: 1.5px solid rgba(255, 255, 255, 0.24);
  border-radius: var(--r-pill);
  padding: 8px 14px 8px 10px;
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
}

.chintro-chip svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

.chintro-chip-more {
  opacity: 0.7;
  padding: 8px 12px;
}

/* Same sticky-bottom-drawer treatment as .reward-actions/.mission-actions —
   pinned, edge-to-edge, safe-area aware — so the CTA sits in the same place
   and behaves the same way a player already knows from the rest of the app,
   on every device regardless of how tall its content ends up. align-self:
   stretch (not width:100%) is what bleeds it to the stage's edges: see
   .reward-actions' own note above for why. */
.chintro-actions {
  align-self: stretch;
  margin-top: auto;
  margin-left: -22px;
  margin-right: -22px;
  padding: 18px 22px calc(18px + var(--safe-bottom, 0px));
  position: sticky;
  bottom: 0;
  background: var(--ci-dark);
  box-shadow: 0 -10px 20px rgba(0, 0, 0, 0.14);
  border-top-left-radius: var(--r-lg);
  border-top-right-radius: var(--r-lg);
  z-index: 4;
}

.chintro-actions.btn-row .btn-big {
  white-space: nowrap;
}

@media (max-width: 400px) {
  .chintro-actions.btn-row .btn-big {
    font-size: var(--fs-md);
    padding: 16px 10px;
  }
}

#screen-chapter-outro .chintro-actions {
  margin-top: 24px;
}

/* Outro celebration overrides: defeated villain + next-chapter teaser card */
.choutro-villain-defeated {
  rotate: 14deg;
  filter: drop-shadow(0 6px 10px rgba(13, 6, 91, 0.22));
  animation:
    popk 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s both,
    choutro-villain-sad 3.2s ease-in-out 0.6s infinite;
}

@keyframes choutro-villain-sad {
  0%, 100% {
    translate: 0 0;
    rotate: 14deg;
  }
  50% {
    translate: 0 5px;
    rotate: 10deg;
  }
}

.choutro-next-card {
  width: 100%;
  margin-top: 24px;
  background: rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1.5px solid rgba(255, 255, 255, 0.26);
  border-radius: var(--r-lg);
  padding: 16px 20px;
  text-align: left;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
  display: flex;
  flex-direction: column;
  gap: 6px;
  position: relative;
  z-index: 2;
}

.choutro-next-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--yellow);
}

.choutro-next-badge svg {
  width: 14px;
  height: 14px;
}

.choutro-next-kicker {
  font-size: var(--fs-xs);
  font-weight: 700;
  opacity: 0.75;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.choutro-next-title {
  margin: 0;
  font-size: var(--fs-lg);
  font-weight: 800;
  line-height: 1.25;
  color: var(--white);
}

.choutro-next-desc {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: 1.4;
  opacity: 0.92;
  color: rgba(255, 255, 255, 0.92);
}

/* ============================================================
   Prototype Werkplaats (prototype-prompt-builder.js) — a fun,
   scaffolded flow: Bella teaches, Buggie taunts, big friendly type.
   ============================================================ */
/* One speech beat — reuses the mission-briefing look (.brief-villain /
   .bubble-villain / .bubble-coach) so the animation and style match the
   game intro exactly. Bella's face is the static .png in a round avatar. */
.pp-beat { margin-bottom: 10px; }
.pp-beat .bubble {
  flex: 1;
  min-width: 0;
  padding: 18px 20px 18px 22px;
}
.pp-beat .bubble b { font-weight: 800; }
/* merged beat: Buggie's taunt slides in first, Bella answers a beat later */
.pp-in-1 { animation: slideup 0.4s 0.05s ease both; }
.pp-in-1 .bubble { animation: popin 0.35s 0.2s ease both; }
.pp-in-2 { animation: slideup 0.45s 0.8s ease both; }
.pp-in-2 .bubble { animation: popin 0.4s 1s ease both; }
.pp-av-bella {
  width: 84px;
  height: 84px;
  flex-shrink: 0;
  border-radius: 50%;
  background: #fff url('../assets/bella-character.png') center 68% / 105% no-repeat;
  border: 3px solid var(--white);
  box-shadow: var(--shadow-sm);
  align-self: flex-end;
}
/* Badges the tap-to-hear button onto the avatar itself -- same spot as the
   mission briefing's own ask-speak button (.bella-coach) -- so .pp-speak
   just inherits .ask-speak's own default badge position/size instead of
   needing its own override. Sized up from the shared .brief-avatar's 84px
   to match that same mission-briefing avatar's own 92px -- scoped to this
   wrapper alone, so the actual mission-briefing screens (which reuse
   .brief-avatar too) stay exactly as they were. */
.pp-av-wrap {
  position: relative;
  flex-shrink: 0;
}
.pp-av-wrap .brief-avatar {
  width: 92px;
}
.pp-av-wrap .pp-av-bella {
  width: 92px;
  height: 92px;
}
/* Nudged further outside the circle than .ask-speak's own default corner
   offset -- Buggie's SVG fills his circle edge to edge (unlike Bella's
   portrait, which has real padding around her face), so the tighter
   default offset sat on top of his art instead of beside it. */
.pp-av-wrap .pp-speak {
  right: -6px;
  bottom: -10px;
}
.pp-bella-big {
  width: 108px;
  height: 108px;
  border-radius: 50%;
  background: #fff url('../assets/bella-character.png') center 62% / 80% no-repeat;
  border: 3px solid var(--white);
  box-shadow: var(--shadow-md);
}

/* chapter-recap.js's brief loading state, before its beats JSON lands */
.pp-recap-loading {
  display: flex;
  justify-content: center;
  padding: 80px 0;
  color: var(--ink-soft);
}
.pp-recap-loading svg { width: 32px; height: 32px; }

.pp-note {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: var(--yellow);
  color: var(--ink);
  border-radius: var(--r-md);
  padding: 13px 15px;
  font-size: var(--fs-base);
  font-weight: 600;
  line-height: 1.4;
}
.pp-note svg { width: 20px; height: 20px; flex-shrink: 0; }
.pp-note-bad { background: #FFE1E7; }

/* drawn screens — 3:4 like De Makerij's canvas, scaled as a whole */
.pp-phones {
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
}
.pp-phone { margin: 0; width: 108px; }
.pp-phone-screen {
  position: relative;
  width: 108px;
  aspect-ratio: 3 / 4;
  background: #fff;
  border: 3px solid var(--ink);
  border-radius: 16px;
  overflow: hidden;
}
.pp-phone-inner {
  position: absolute;
  top: 0;
  left: 0;
  width: 280px;
  aspect-ratio: 3 / 4;
  transform-origin: top left;
  transform: scale(0.3857);
}
.pp-phone figcaption {
  text-align: center;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink-soft);
  margin-top: 6px;
}

/* the two big choice cards */
.pp-choice { display: flex; flex-direction: column; gap: 12px; }
.pp-choice-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  text-align: left;
  width: 100%;
  padding: 16px 18px;
  border-radius: var(--r-lg);
  border: 3px solid var(--ink);
  background: var(--white);
  box-shadow: 0 5px 0 var(--ink);
  cursor: pointer;
  transition: transform 0.1s, box-shadow 0.1s;
}
.pp-choice-card:active { transform: translateY(3px); box-shadow: 0 2px 0 var(--ink); }
.pp-choice-ai { background: #F3EFFF; }
.pp-choice-self { background: #FFFDF0; }
.pp-choice-ic {
  width: 42px; height: 42px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--purple);
  margin-bottom: 4px;
}
.pp-choice-ic svg { width: 22px; height: 22px; stroke: #fff; color: #fff; }
.pp-choice-card b { font-size: var(--fs-lg); font-weight: 800; }
.pp-choice-card > span { font-size: var(--fs-sm); color: var(--ink-soft); line-height: 1.4; }
.pp-choice-tag {
  margin-top: 6px;
  font-size: var(--fs-xs) !important;
  font-weight: 800;
  color: var(--purple) !important;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* building animation */
.pp-building {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin: 20px 0 10px;
}
.pp-building .pp-bella-big { width: 120px; height: 120px; }
.pp-building-dots { display: flex; gap: 8px; }
.pp-building-dots span {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--purple);
  animation: ppDot 1s ease-in-out infinite;
}
.pp-building-dots span:nth-child(2) { animation-delay: 0.15s; }
.pp-building-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes ppDot { 0%, 100% { opacity: 0.3; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-6px); } }

/* Bella's bubble while the AI works — she stays, the bubble just thinks */
.pp-thinking { display: inline-flex; align-items: center; min-height: 1.4em; }
.pp-think { display: inline-flex; gap: 7px; align-items: center; padding: 2px 0; }
.pp-think span {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--purple);
  animation: ppDot 1s ease-in-out infinite;
}
.pp-think span:nth-child(2) { animation-delay: 0.15s; }
.pp-think span:nth-child(3) { animation-delay: 0.3s; }
.pp-build-line {
  text-align: center;
  font-size: var(--fs-lg);
  font-weight: 800;
  color: var(--ink);
  margin: 0;
  min-height: 1.4em;
}

/* self-serve / export screen — numbered steps */
.pp-flow {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-top: 16px;
}
.pp-fstep {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}
.pp-fnum {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--ink);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: var(--fs-md);
  font-weight: 800;
  margin-top: 1px;
}
.pp-fbody {
  flex: 1;
  min-width: 0;
}
.pp-fbody > b {
  display: block;
  font-size: var(--fs-md);
  font-weight: 800;
  color: var(--ink);
  margin-bottom: 6px;
}
.pp-fbody p {
  font-size: var(--fs-base);
  line-height: 1.45;
  color: var(--ink-soft);
  margin: 0;
}
.pp-dlcard {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 13px;
  text-align: left;
  background: var(--white);
  border: 2.5px solid var(--ink);
  border-radius: var(--r-lg);
  box-shadow: 0 4px 0 rgba(13, 6, 91, 0.16);
  padding: 13px 15px;
  margin-top: 4px;
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}
.pp-dlcard:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 rgba(13, 6, 91, 0.16);
}
.pp-file-ic {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: var(--yellow);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
}
.pp-file-ic svg { width: 22px; height: 22px; }
.pp-dlcard-t {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.pp-dlcard-t b { font-size: var(--fs-md); font-weight: 800; color: var(--ink); }
.pp-dlcard-t span { font-size: var(--fs-sm); color: var(--ink-soft); }
.pp-dlcard.is-done {
  background: #EAF7EE;
  border-color: var(--good);
  box-shadow: 0 4px 0 rgba(23, 158, 80, 0.22);
}
.pp-dlcard.is-done .pp-file-ic {
  background: var(--good);
  color: #fff;
}
.pp-dlcard.is-done .pp-file-ic svg { stroke: #fff; }
.pp-toolbtn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
  text-decoration: none;
  background: var(--white);
  color: var(--purple);
  border: 2.5px solid var(--purple);
  border-radius: var(--r-md);
  padding: 10px 16px;
  font-size: var(--fs-base);
  font-weight: 800;
}
.pp-toolbtn svg { width: 15px; height: 15px; }
.pp-copyline {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  background: var(--bg-purple);
  border-radius: var(--r-md);
  padding: 12px 14px;
  margin-top: 8px;
}
.pp-copyline > span {
  flex: 1;
  min-width: 150px;
  font-size: var(--fs-base);
  font-weight: 700;
  color: var(--ink);
}
.pp-copybtn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--purple);
  color: #fff;
  border: 0;
  border-radius: var(--r-pill);
  padding: 9px 15px;
  font-size: var(--fs-sm);
  font-weight: 800;
  cursor: pointer;
}
.pp-copybtn svg { width: 15px; height: 15px; stroke: #fff; }
.pp-inline-btn {
  display: inline-block !important;
  margin: 4px 0 0 !important;
  font-size: var(--fs-sm) !important;
  font-weight: 700;
  text-decoration: underline;
}

/* ===== result screen: a chat tool — a sticky full-width peek at the top of
   the app, the kid's asks scrolling below, Bella pinned above the nav ===== */
.bpk-content:has(.pp-result) { min-height: 0; gap: 0; padding-bottom: 0; }
.pp-result { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 10px; }

/* mini app preview: full container width, ~20vh tall, all corners rounded so
   it reads as the app's own style — a big tappable "peek", pinned at the top */
.pp-proto-sticky {
  flex-shrink: 0;
  position: sticky;
  top: calc(var(--safe-top) + 44px);
  z-index: 6;
  align-self: stretch;
  margin: 2px 0 4px;
}
.pp-mini-app {
  position: relative;
  display: block;
  width: 100%;
  height: 20vh;
  min-height: 116px;
  max-height: 184px;
  border: 2px solid var(--ink);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--white);
  box-shadow: 0 5px 0 rgba(13, 6, 91, 0.14);
  padding: 0;
  cursor: pointer;
  font-family: inherit;
}
.pp-mini-app:active { transform: translateY(2px); box-shadow: 0 3px 0 rgba(13, 6, 91, 0.14); }
.pp-mini-app-screen { display: block; width: 100%; height: 100%; overflow: hidden; }
.pp-mini-app-frame { width: 100%; height: 100%; border: 0; display: block; pointer-events: none; }
.pp-mini-app-open {
  position: absolute;
  right: 10px;
  bottom: 9px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--purple);
  color: #fff;
  border-radius: var(--r-pill);
  padding: 6px 13px;
  font-size: var(--fs-sm);
  font-weight: 800;
  box-shadow: 0 2px 8px rgba(13, 6, 91, 0.3);
}
.pp-mini-app-open svg { width: 13px; height: 13px; stroke: #fff; }

/* "Bekijk groot" overlay: the app fills the page (they user-test it here),
   but its width is capped to the app container so it isn't 100% on a tablet */
.bpk-proto-overlay:has(.bpk-proto-iframe-full) { align-items: center; }
.bpk-proto-overlay:has(.bpk-proto-iframe-full) .bpk-proto-overlay-bar,
.bpk-proto-overlay:has(.bpk-proto-iframe-full) .bpk-proto-iframe-full {
  width: 100%;
  max-width: var(--appw);
}

/* "opnieuw" — a small icon button at the head of the prompt bar */
.pp-restart-mini {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: none;
  border: 0;
  padding: 6px;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 800;
  color: var(--ink-soft);
  cursor: pointer;
}
.pp-restart-mini svg { width: 18px; height: 18px; }
.pp-restart-mini.is-armed { color: var(--bad); }

/* the kid's asks — a scroll area; scroll up to see earlier ones. Newest sits
   at the bottom, just above Bella; a handful hug the bottom too. */
.pp-chat {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 6px 2px 12px;
}
/* few messages: let them settle toward the bottom, near the input, like a
   chat that hasn't filled up yet */
.pp-chat > :first-child { margin-top: auto; }

/* undo helper — a small drawer at the end of the chat, never per-message */
.pp-undo { align-self: center; margin-top: 6px; max-width: 100%; }
.pp-undo summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-sm);
  font-weight: 800;
  color: var(--ink-soft);
  padding: 5px 10px;
  border-radius: var(--r-pill);
}
.pp-undo summary::-webkit-details-marker { display: none; }
.pp-undo summary svg { width: 14px; height: 14px; }
.pp-undo[open] summary { background: var(--bg-purple); color: var(--purple); }
.pp-undo-list { display: flex; flex-direction: column; gap: 6px; margin: 8px 0 2px; }
.pp-undo-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: var(--white);
  border: 1.5px solid #E6E2F6;
  border-radius: var(--r-md);
  padding: 9px 12px;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.pp-undo-row svg { width: 15px; height: 15px; flex-shrink: 0; color: var(--purple); }
.pp-undo-row:active { background: var(--bg-purple); }
.pp-undo-confirm {
  background: var(--bg-purple);
  border-radius: var(--r-md);
  padding: 10px 12px;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink);
  line-height: 1.4;
}
.pp-undo-yes, .pp-undo-no {
  border: 0;
  background: none;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 800;
  padding: 2px 8px;
  cursor: pointer;
}
.pp-undo-yes { color: var(--bad); }
.pp-undo-no { color: var(--ink-soft); }

/* Bella (full size) hangs above the nav on the screen's own ground — the
   chat above is its own scroll box, so nothing shows through behind her. */
.bpk-footer:has(.pp-navwrap) {
  background: transparent;
  box-shadow: none;
  padding: 0 0 var(--safe-bottom, 0px);
  margin-bottom: calc(-20px - var(--safe-bottom, 0px));
  gap: 0;
  border-radius: 0;
  animation: none;
}
.pp-navwrap { display: flex; flex-direction: column; }
.pp-pinned {
  margin: 0 4px 12px;
  animation: bpkIn 0.3s ease both;
}
.pp-navbar {
  background: var(--white);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: 0 -8px 22px rgba(13, 6, 91, 0.12);
  padding: 16px 20px calc(14px + var(--safe-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* "Voorbeeld nodig?" drawer — opens upward from the nav */
.pp-tips summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--fs-sm);
  font-weight: 800;
  color: var(--purple);
  padding: 4px 2px;
}
.pp-tips summary::-webkit-details-marker { display: none; }
.pp-tips summary svg { width: 15px; height: 15px; }
.pp-tips-caret { margin-left: auto; transition: transform 0.15s ease; }
.pp-tips[open] summary .pp-tips-caret { transform: rotate(180deg); }
.pp-chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 6px 2px 2px;
  scrollbar-width: none;
}
.pp-chips::-webkit-scrollbar { display: none; }
.pp-chip {
  flex-shrink: 0;
  border: 2px solid var(--purple);
  background: var(--white);
  color: var(--purple);
  border-radius: var(--r-pill);
  padding: 8px 13px;
  font-size: var(--fs-sm);
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}
.pp-chip:active { background: var(--bg-purple); transform: scale(0.97); }


/* ---------- Makerij playground (#336): the kid's own projects ---------- */
.pg-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: 14px;
}

.pg-card {
  display: flex;
  gap: 14px;
  align-items: stretch;
  background: var(--white);
  border-radius: var(--r-lg);
  padding: 12px;
  box-shadow: var(--shadow-sm);
}

.pg-open {
  flex-shrink: 0;
  width: 96px;
  padding: 0;
  background: transparent;
}

.pg-thumb {
  position: relative;
  display: block;
  width: 96px;
  aspect-ratio: 3 / 4;
  background: var(--white);
  border: 2px solid #DCD8F0;
  border-radius: 12px;
  overflow: hidden;
  pointer-events: none;
}

.pg-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pg-name-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.pg-name {
  font-size: var(--fs-md);
  color: var(--ink);
  line-height: 1.2;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pg-name-input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-weight: 700;
  padding: 6px 10px;
  border: 2px solid #DCD8F0;
  border-radius: var(--r-md);
}

.pg-meta {
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
}

.pg-actions {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

.pg-go {
  padding: 10px 16px;
  font-size: var(--fs-base);
  white-space: nowrap;
}

.pg-mini-btn.is-loading {
  opacity: 0.5;
  animation: pg-pulse 0.9s ease-in-out infinite;
}

@keyframes pg-pulse {
  50% { opacity: 0.2; }
}

.pg-mini-btn {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-soft);
}

.pg-mini-btn svg {
  width: 18px;
  height: 18px;
}

.pg-confirm {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.pg-confirm-text {
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--ink);
}

.pg-confirm-row {
  display: flex;
  gap: 8px;
}

.pg-confirm-yes,
.pg-confirm-no {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border-radius: 999px;
  padding: 10px 8px;
  font-weight: 700;
  font-size: var(--fs-sm);
  white-space: nowrap;
}

.pg-confirm-yes {
  background: var(--bad);
  color: var(--white);
}

.pg-confirm-yes svg {
  width: 16px;
  height: 16px;
}

.pg-confirm-no {
  background: var(--bg-purple);
  color: var(--ink);
}

.pg-new {
  width: 100%;
  margin-top: 16px;
}

.pg-full {
  margin: 16px 0 0;
  text-align: center;
  color: var(--ink-soft);
  font-weight: 600;
}

.pg-starters {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 14px 0 18px;
}

.pg-starter {
  background: var(--white);
  border: 2px solid #DCD8F0;
  border-radius: var(--r-md);
  padding: 12px 16px;
  font-weight: 700;
  color: var(--ink);
}

.pg-device-note {
  margin-top: 22px;
  text-align: center;
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
}

.pg-device-note svg {
  width: 14px;
  height: 14px;
  vertical-align: -2px;
}

/* Shown by the Makerij when a save is refused because the device is full. */
.mk-save-warning {
  position: fixed;
  left: 16px;
  right: 16px;
  top: calc(12px + var(--safe-top, 0px));
  z-index: 300;
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: var(--bad);
  color: var(--white);
  border-radius: var(--r-md);
  padding: 12px 14px;
  font-weight: 700;
  font-size: var(--fs-sm);
  box-shadow: var(--shadow-md);
}

.mk-save-warning svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

/* The app from the App Ontwerper lessons, shown first in Mijn Makerij. */
.pg-card-lesson {
  box-shadow: 0 0 0 3px var(--comp-purple), var(--shadow-sm);
}


/* ---------- Links + "Probeer je app" (#337) ---------- */

/* Tik-vak: a tap area laid over a drawing. Only visible while editing. */
.mk-comp-hotspot span {
  display: none;
}

.mk-art.is-edit .mk-comp-hotspot {
  border: 2px dashed var(--purple);
  background: rgba(95, 41, 255, 0.12);
  border-radius: 10px;
  box-sizing: border-box;
}

.mk-art.is-edit .mk-comp-hotspot span {
  display: block;
  font-size: 3.6cqw;
  font-weight: 800;
  color: var(--purple);
}

/* "This goes to screen N", on a linked piece while editing. */
.mk-link-badge {
  position: absolute;
  top: -10px;
  left: -10px;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 1px;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--purple);
  color: var(--white);
  font-size: 12px;
  font-weight: 800;
  line-height: 1;
  box-shadow: var(--shadow-sm);
  pointer-events: none;
}

.mk-link-badge svg {
  width: 13px;
  height: 13px;
}

.mk-tool-btn.is-linked {
  color: var(--purple);
}

/* Link picker: where should this go? */
.mk-link-picker {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.mk-link-choices {
  display: flex;
  gap: 10px;
  justify-content: center;
  overflow-x: auto;
  padding: 6px 4px;
}

.mk-link-choice {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 6px;
  border-radius: 12px;
  background: var(--white);
  border: 2px solid transparent;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink);
  max-width: 96px;
}

.mk-link-choice.on {
  border-color: var(--purple);
}

.mk-link-thumb {
  position: relative;
  display: block;
  width: 56px;
  aspect-ratio: 3 / 4;
  background: var(--white);
  border: 2px solid #DCD8F0;
  border-radius: 8px;
  overflow: hidden;
  pointer-events: none;
}

.mk-link-name {
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 84px;
}

/* Probeer: bottom-left of the artboard, in the strip, mirror of full screen. */
.creation-canvas-play {
  position: absolute;
  left: 14px;
  bottom: 6px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--purple);
  color: var(--white);
  box-shadow: var(--shadow-sm);
  z-index: 6;
}

.creation-canvas-play svg {
  width: 16px;
  height: 16px;
  fill: currentColor;
}

.creation-canvas-play:active {
  transform: scale(0.95);
}

/* The play overlay */
.mk-play-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: linear-gradient(180deg, #E9E3FF 0%, #FFF3E6 100%);
  padding: calc(10px + var(--safe-top, 0px)) 16px calc(14px + var(--safe-bottom, 0px));
  box-sizing: border-box;
}

.mk-play-top {
  width: 100%;
  max-width: var(--appw);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.mk-play-top-spacer {
  width: 44px;
}

.mk-play-title {
  font-weight: 800;
  font-size: var(--fs-md);
  color: var(--ink);
  text-align: center;
}

.mk-play-stage {
  flex: 1;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  container-type: size;
}

.mk-play-screen {
  position: relative;
  width: min(100cqw, 75cqh);
  aspect-ratio: 3 / 4;
  background: var(--white);
  border-radius: 24px;
  overflow: hidden;
  box-shadow: var(--shadow-md), 0 0 0 8px rgba(255, 255, 255, 0.6);
  touch-action: manipulation;
}

.mk-play-in {
  animation: mk-play-in 0.22s ease-out;
}

@keyframes mk-play-in {
  from { opacity: 0.2; transform: scale(0.97); }
  to { opacity: 1; transform: none; }
}

.mk-play-screen .mk-linked {
  cursor: pointer;
}

.mk-play-screen .mk-linked.pulse {
  animation: mk-link-pulse 0.9s ease-out;
}

@keyframes mk-link-pulse {
  0%, 60% { outline: 4px solid var(--purple); outline-offset: 3px; }
  100% { outline: 4px solid transparent; outline-offset: 8px; }
}

.mk-play-note {
  /* Inside the phone frame, along its bottom edge: it must never sit on the
     Terug / Opnieuw buttons below it. Touches pass through. */
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 10px;
  z-index: 20;
  pointer-events: none;
  margin: 0;
  padding: 10px 14px;
  border-radius: var(--r-md);
  background: var(--ink);
  color: var(--white);
  font-weight: 700;
  font-size: var(--fs-sm);
  text-align: center;
}

.mk-play-bottom {
  width: 100%;
  max-width: var(--appw);
  display: flex;
  gap: 10px;
  justify-content: center;
}

.mk-play-bottom .btn {
  flex: 1;
}

.mk-play-bottom .btn:disabled {
  opacity: 0.4;
}

/* The playground card's play button. */
.pg-play {
  color: var(--purple);
}

.mk-link-empty {
  margin: 0;
  text-align: center;
  color: var(--ink-soft);
  font-weight: 600;
  font-size: var(--fs-sm);
}

/* ---------- Makerij chooser (#376) ---------- */
.makerij-chooser {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 8px;
}
.makerij-choice {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 16px;
  background: var(--white);
  border: 2px solid var(--border);
  border-radius: var(--r-lg);
  cursor: pointer;
  text-align: left;
  transition: transform 0.1s, box-shadow 0.1s, border-color 0.15s;
  box-shadow: 0 2px 0 rgba(13, 6, 91, 0.08);
}
.makerij-choice:active { transform: scale(0.98); }
.makerij-choice:hover { border-color: var(--purple); }
.makerij-choice-icon {
  flex-shrink: 0;
  width: 52px;
  height: 52px;
  border-radius: var(--r-md);
  background: var(--bg-purple);
  color: var(--purple);
  display: flex;
  align-items: center;
  justify-content: center;
}
.makerij-choice-icon svg,
.makerij-choice-icon i { width: 26px; height: 26px; }
.choice-icon-review { background: var(--bg-green); color: var(--good); }
.makerij-choice-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.makerij-choice-text b { font-size: var(--fs-lg); }
.makerij-choice-text span { font-size: var(--fs-sm); color: var(--ink-soft); font-weight: 500; }
.makerij-choice > i { color: var(--ink-soft); flex-shrink: 0; width: 20px; height: 20px; }

/* ---------- Makerij Subpages & Review Tool Header ---------- */
.subpage-top,
.review-top {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  max-width: var(--appw);
  margin: 0 auto;
  min-height: 56px;
  padding: calc(14px + var(--safe-top, 0px)) 16px 10px;
  box-sizing: border-box;
  z-index: 40;
}

.subpage-top > .icon-btn,
.review-top > .icon-btn {
  position: relative;
  z-index: 2;
  flex-shrink: 0;
}

.subpage-top-mid,
.review-top-mid {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 1;
  padding: 0 60px;
  box-sizing: border-box;
}

.subpage-top-mid *,
.review-top-mid * {
  pointer-events: auto;
}

.subpage-top-title,
.review-top-title {
  font-family: var(--font);
  font-size: var(--fs-lg);
  font-weight: 800;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  letter-spacing: -0.01em;
  text-align: center;
  white-space: nowrap;
}

.subpage-top-title svg,
.subpage-top-title i {
  width: 20px;
  height: 20px;
  color: var(--purple);
}

.subpage-top-spacer,
.review-top-spacer {
  width: 42px;
  height: 42px;
  flex-shrink: 0;
  visibility: hidden;
  pointer-events: none;
}

.review-stage {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  width: 100%;
  max-width: var(--appw);
  margin: 0 auto;
  position: relative;
  box-sizing: border-box;
}

.page-wrap-sub {
  padding-top: 6px;
  padding-bottom: calc(40px + var(--safe-bottom, 0px));
}

.review-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 24px 16px 32px;
  gap: 16px;
}

/* consent & modal card */
.review-consent-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px 16px 36px;
  width: 100%;
}

.review-consent-card {
  background: var(--white);
  border-radius: var(--r-xl);
  padding: 32px 22px 26px;
  box-shadow: 0 10px 32px rgba(13, 6, 91, 0.08);
  border: 2px solid rgba(13, 6, 91, 0.06);
  max-width: 410px;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  animation: popin 0.35s ease both;
}

.review-consent-badge {
  width: 62px;
  height: 62px;
  border-radius: 20px;
  background: linear-gradient(135deg, var(--bg-purple) 0%, #E5DCF8 100%);
  color: var(--purple);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
  box-shadow: 0 4px 14px rgba(107, 70, 193, 0.16);
}

.review-consent-badge svg,
.review-consent-badge i {
  width: 32px;
  height: 32px;
  stroke-width: 2.2;
}

.review-consent-card h2 {
  font-family: var(--font);
  font-size: 22px;
  font-weight: 800;
  color: var(--ink);
  letter-spacing: -0.02em;
  margin: 0 0 8px;
  line-height: 1.25;
}

.review-consent-intro {
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-soft);
  line-height: 1.5;
  margin: 0 0 20px;
  text-wrap: pretty;
}

.review-consent-points {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  margin-bottom: 24px;
}

.review-consent-point {
  background: #FAF8FE;
  border: 1.5px solid rgba(13, 6, 91, 0.06);
  border-radius: var(--r-md);
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  transition: transform 0.15s, border-color 0.15s;
}

.review-consent-point:hover {
  border-color: rgba(107, 70, 193, 0.3);
  transform: translateY(-1px);
}

.review-point-icon {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--white);
  color: var(--purple);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 2px 5px rgba(13, 6, 91, 0.06);
}

.review-point-icon svg,
.review-point-icon i {
  width: 18px;
  height: 18px;
  stroke-width: 2.2;
}

.review-point-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.review-point-text strong {
  font-size: 13.5px;
  font-weight: 800;
  color: var(--ink);
  line-height: 1.25;
}

.review-point-text span {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-soft);
  line-height: 1.4;
}

.review-consent-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
}

.review-consent-actions .btn-big {
  padding: 14px 20px;
  font-size: var(--fs-md);
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}


/* upload */
.review-upload-zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 40px 24px;
  border: 2px dashed var(--border);
  border-radius: var(--r-lg);
  background: var(--white);
  width: 100%;
  max-width: 380px;
  text-align: center;
  transition: border-color 0.15s, background 0.15s;
}
.review-upload-zone.drag-over {
  border-color: var(--purple);
  background: var(--bg-purple);
}
.review-upload-zone > i,
.review-upload-zone > svg { width: 40px; height: 40px; color: var(--ink-soft); }
.review-upload-zone p { margin: 0; font-weight: 600; }
.review-upload-hint { font-size: var(--fs-sm); color: var(--ink-soft); font-weight: 500; }
.review-preview {
  width: 100%;
  max-width: 380px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}
.review-preview img {
  width: 100%;
  max-height: 320px;
  object-fit: contain;
  border-radius: var(--r-md);
  border: 2px solid var(--border);
  background: var(--white);
}
.review-preview-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
}

/* loading */
.review-loading { justify-content: center; text-align: center; }
.review-loading h2 { margin: 0; }
.review-loading-sub { color: var(--ink-soft); font-size: var(--fs-sm); max-width: 300px; }
.review-loading-spinner {
  width: 48px;
  height: 48px;
  border: 4px solid var(--border);
  border-top-color: var(--purple);
  border-radius: 50%;
  animation: reviewSpin 0.8s linear infinite;
}
@keyframes reviewSpin { to { transform: rotate(360deg); } }

/* error */
.review-error { justify-content: center; text-align: center; }
.review-error-icon { color: var(--bad); }
.review-error-icon svg { width: 48px; height: 48px; }

/* results */
.review-results { gap: 20px; }
.review-results-summary { text-align: center; }
.review-results-summary h2 { margin: 0 0 4px; }
.review-results-summary p { color: var(--ink-soft); font-size: var(--fs-base); margin: 0; }

.review-canvas-wrap {
  position: relative;
  width: 100%;
  max-width: 440px;
  margin: 0 auto;
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md), 0 0 0 1px rgba(0,0,0,0.06);
  background: var(--white);
  overflow: hidden;
  isolation: isolate;
  z-index: 5;
}

.review-canvas {
  position: relative;
  width: 100%;
  line-height: 0;
}

.review-canvas img {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
}

.review-spot {
  position: absolute;
  padding: 0;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  transition: transform 0.12s ease, outline 0.12s ease, background 0.12s ease, box-shadow 0.12s ease;
  z-index: 2;
  box-sizing: border-box;
}

.review-spot.sev-high {
  outline: 3px solid var(--bad);
  outline-offset: 1px;
  background: rgba(255, 92, 122, 0.18);
}

.review-spot.sev-medium {
  outline: 3px solid #E5A100;
  outline-offset: 1px;
  background: rgba(229, 161, 0, 0.18);
}

.review-spot.sev-low {
  outline: 2px dashed var(--purple);
  outline-offset: 1px;
  background: rgba(139, 92, 246, 0.14);
}

.review-spot.sev-good {
  outline: 2px solid var(--good);
  outline-offset: 1px;
  background: rgba(20, 184, 107, 0.14);
}

.review-spot:hover,
.review-spot.is-selected {
  z-index: 10;
  transform: scale(1.02);
}

.review-spot.is-selected {
  outline-width: 4px;
  box-shadow: 0 0 0 4px var(--yellow), var(--shadow-md);
}

.review-spot-pin {
  position: absolute;
  top: -11px;
  right: -9px;
  min-width: 22px;
  height: 22px;
  padding: 0 5px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 800;
  color: var(--white);
  box-shadow: var(--shadow-sm);
  pointer-events: none;
}

.sev-high .review-spot-pin { background: var(--bad); }
.sev-medium .review-spot-pin { background: #E5A100; color: var(--ink); }
.sev-low .review-spot-pin { background: var(--purple); }
.sev-good .review-spot-pin { background: var(--good); }

.review-spot-pin svg {
  width: 11px;
  height: 11px;
  stroke-width: 3;
}

.review-spot-pin span {
  line-height: 1;
}

.review-content.review-results {
  gap: 16px;
  padding-bottom: calc(100px + var(--safe-bottom, 0px));
}

/* Floating Groot scherm button on canvas wrap (#379) */
.review-canvas-expand {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 200;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--purple);
  color: var(--white);
  box-shadow: 0 3px 0 var(--purple-dark), var(--shadow-md);
  border: none;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.review-canvas-expand:hover {
  transform: translateY(-1px);
}

.review-canvas-expand:active {
  transform: translateY(2px);
  box-shadow: 0 1px 0 var(--purple-dark);
}

.review-canvas-expand svg,
.review-canvas-expand i {
  width: 20px;
  height: 20px;
  stroke-width: 2.5;
}

/* Expanded full-screen mode conform Crasher-jacht */
.review-expanded-topbar {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 56px;
  z-index: 1050;
  background: var(--white);
  padding: calc(8px + env(safe-area-inset-top, 0px)) 16px 8px;
  box-shadow: 0 4px 16px rgba(13, 6, 91, 0.08);
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
}

.review-expanded-topbar .hunt-count {
  font-size: var(--fs-base);
  font-weight: 700;
  color: var(--ink);
}

.review-expanded-topbar .hunt-collapse {
  background: var(--purple);
  color: var(--white);
  box-shadow: 0 3px 0 var(--purple-dark);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-radius: var(--r-pill);
  font-weight: 800;
  font-size: var(--fs-sm);
  border: none;
  cursor: pointer;
}

.review-expanded-topbar .hunt-collapse svg,
.review-expanded-topbar .hunt-collapse i {
  width: 16px;
  height: 16px;
}

body.review-expanded {
  overflow: hidden;
}

.review-stage.is-expanded .review-top,
.review-stage.is-expanded .review-results-intro,
.review-stage.is-expanded .review-canvas-expand,
.review-stage.is-expanded .review-dock-bar {
  display: none !important;
}

.review-stage.is-expanded .review-expanded-topbar {
  display: flex;
}

.review-stage.is-expanded .review-content.review-results {
  padding: 0;
  gap: 0;
}

.review-stage.is-expanded .review-canvas-wrap {
  position: fixed;
  inset: 0;
  top: calc(56px + env(safe-area-inset-top, 0px));
  z-index: 500;
  max-width: none;
  border-radius: 0;
  background: var(--bg-purple);
  box-shadow: none;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 20px 16px calc(140px + var(--safe-bottom, 0px));
  box-sizing: border-box;
}

.review-stage.is-expanded .review-canvas {
  max-width: 760px;
  margin: 0 auto;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: var(--shadow-md);
  background: var(--white);
}

.review-stage.is-expanded .review-canvas img {
  border-radius: 14px;
}

.review-stage.is-expanded .round-feedback {
  z-index: 1100;
}

/* Tip Drawer in Review Stage: canonical rounded top corners & elevation */
.review-stage .round-feedback {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  max-width: min(100%, 520px);
  margin: 0 auto;
  border-top-left-radius: 28px !important;
  border-top-right-radius: 28px !important;
  border-bottom-left-radius: 0 !important;
  border-bottom-right-radius: 0 !important;
  background: var(--white);
  border-top: 1px solid rgba(13, 6, 91, 0.08);
  box-shadow: 0 -12px 36px rgba(13, 6, 91, 0.16);
  padding: 24px 20px calc(22px + var(--safe-bottom, 0px));
  z-index: 600;
  box-sizing: border-box;
}

.review-spot-pin {
  pointer-events: auto;
  cursor: pointer;
}

/* Sticky bottom action bar conform .mission-actions */
.review-dock-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  max-width: var(--appw, 480px);
  margin: 0 auto;
  padding: 14px 20px calc(18px + var(--safe-bottom, 0px));
  background: var(--white, #ffffff);
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: stretch;
  box-shadow: 0 -10px 20px rgba(0, 0, 0, 0.05);
  border-top-left-radius: var(--r-lg, 24px);
  border-top-right-radius: var(--r-lg, 24px);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  box-sizing: border-box;
}

.review-dock-actions {
  display: flex;
  gap: 12px;
  width: 100%;
}

.review-dock-actions .btn-big,
.review-dock-bar > .btn-big {
  flex: 1;
  width: 100%;
  margin: 0;
}

.review-dock-quota {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-size: var(--fs-xs, 12px);
  font-weight: 700;
  color: var(--purple-dark, #5F29FF);
  text-align: center;
  padding-bottom: 2px;
}

.review-dock-quota svg,
.review-dock-quota i {
  width: 14px;
  height: 14px;
  color: var(--purple);
}

.review-dock-quota.is-limit {
  color: var(--bad);
}

.review-dock-quota.is-limit svg,
.review-dock-quota.is-limit i {
  color: var(--bad);
}

/* Drawer navigation & header inside feedback sheet */
.hunt-rule-topline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  margin-bottom: 6px;
}

.review-tip-counter {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--purple-dark);
  background: var(--bg-purple);
  padding: 3px 10px;
  border-radius: var(--r-pill);
}

.review-drawer-x {
  background: none;
  border: none;
  color: var(--ink-soft);
  cursor: pointer;
  padding: 6px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s, background 0.15s;
}

.review-drawer-x:hover {
  color: var(--ink);
  background: rgba(13, 6, 91, 0.08);
}

.review-drawer-x svg,
.review-drawer-x i {
  width: 18px;
  height: 18px;
}

.review-drawer-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  margin-top: 6px;
}

.review-nav-arrow {
  width: 52px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-pill);
  flex-shrink: 0;
  padding: 0;
}

.review-nav-arrow:disabled {
  opacity: 0.35;
  pointer-events: none;
}

.review-nav-arrow svg,
.review-nav-arrow i {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.review-nav-close {
  flex: 1;
  min-width: 0;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  font-weight: 800;
  border-radius: var(--r-pill);
}

.badge-beta,
.makerij-choice-text .badge-beta,
.makerij-choice-text b .badge-beta,
.review-top-title .badge-beta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1px 7px;
  border-radius: var(--r-pill);
  background: var(--purple);
  color: #ffffff !important;
  font-size: 10px !important;
  font-weight: 800 !important;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  vertical-align: middle;
  margin-left: 6px;
  line-height: 1.35;
  box-shadow: 0 1px 2px rgba(107, 70, 193, 0.2);
}


.review-results-intro {
  text-align: center;
  max-width: 440px;
  margin: 0 auto;
}

.review-results-intro h3 {
  margin: 0 0 4px;
  font-size: 20px;
  font-weight: 900;
  color: var(--ink);
  letter-spacing: -0.01em;
}

.review-results-intro h3 b {
  color: var(--purple-dark);
}

.review-results-intro p {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  font-weight: 600;
}

.review-auth-gate {
  justify-content: center;
  text-align: center;
  max-width: 380px;
  margin: 0 auto;
}

.review-auth-icon {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--bg-purple);
  color: var(--purple);
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 8px;
}

.review-auth-icon svg {
  width: 32px;
  height: 32px;
}

.review-auth-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  margin-top: 16px;
}

.review-scan-counter {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--purple-dark);
  background: var(--bg-purple);
  padding: 6px 14px;
  border-radius: var(--r-pill);
  margin-top: 10px;
}

.review-scan-counter.is-limit {
  color: var(--bad);
  background: var(--bg-red);
}

.review-scan-counter svg {
  width: 14px;
  height: 14px;
}

/* ---------- Saved Reviews Overview (#379) ---------- */
.review-overview {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  padding: 8px 16px calc(115px + var(--safe-bottom, 0px));
  gap: 16px;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}

.review-overview-header {
  text-align: center;
  margin: 4px 0 14px;
  width: 100%;
}

.review-overview-header .page-title {
  font-size: var(--fs-2xl);
  font-weight: 800;
  color: var(--ink);
  margin: 0 0 6px;
  letter-spacing: -0.01em;
  text-align: center;
}

.review-overview-sub {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  margin: 0 auto;
  line-height: 1.45;
  font-weight: 500;
  text-align: center;
  max-width: 32ch;
}

.review-saved-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
}

.review-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px;
  background: var(--white);
  border: 2px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 2px 8px rgba(13, 6, 91, 0.05);
  cursor: pointer;
  position: relative;
  transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.15s ease;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  width: 100%;
  box-sizing: border-box;
}

.review-card:hover {
  border-color: rgba(107, 70, 193, 0.45);
  box-shadow: 0 6px 20px rgba(13, 6, 91, 0.09);
  transform: translateY(-2px);
}

.review-card:active {
  transform: scale(0.99);
}

.review-card-thumb-wrap {
  width: 90px;
  height: 62px;
  border-radius: var(--r-md);
  overflow: hidden;
  flex-shrink: 0;
  background: #0D065B;
  border: 1px solid rgba(13, 6, 91, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 4px rgba(13, 6, 91, 0.08);
}

.review-card-thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.review-card-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.review-card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.review-card-title {
  font-size: 15px;
  font-weight: 800;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin: 0;
}

.review-card-date {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink-soft);
  flex-shrink: 0;
}

.review-card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.review-card-meta {
  display: flex;
  align-items: center;
  gap: 6px;
}

.review-card-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11.5px;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: var(--r-pill);
}

.review-card-tag svg,
.review-card-tag i {
  width: 13px;
  height: 13px;
}

.review-card-tag.is-good {
  background: #EAFBF0;
  color: #1A7F37;
}

.review-card-tag.is-high {
  background: #FFF1F0;
  color: #D9427F;
}

.review-card-tag.is-tips {
  background: #F1EDFC;
  color: var(--purple);
}

.review-card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.review-card-cta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 800;
  color: var(--purple);
  background: var(--bg-purple);
  padding: 3px 8px;
  border-radius: var(--r-pill);
  transition: background 0.15s, color 0.15s;
}

.review-card:hover .review-card-cta {
  background: var(--purple);
  color: #ffffff;
}

.review-card-cta svg,
.review-card-cta i {
  width: 12px;
  height: 12px;
}

.review-card-del-btn {
  background: none;
  border: none;
  color: var(--ink-soft);
  cursor: pointer;
  padding: 6px;
  border-radius: var(--r-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s, background 0.15s;
  opacity: 0.6;
}

.review-card-del-btn:hover {
  color: var(--bad);
  background: rgba(255, 92, 122, 0.12);
  opacity: 1;
}

.review-card-del-btn svg,
.review-card-del-btn i {
  width: 16px;
  height: 16px;
}

.review-card-confirm {
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.97);
  backdrop-filter: blur(4px);
  border-radius: var(--r-lg);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 16px;
  gap: 12px;
  z-index: 5;
}

.review-card-confirm span {
  font-size: 13px;
  font-weight: 700;
  color: var(--bad);
}

.review-confirm-row {
  display: flex;
  gap: 8px;
}

.review-confirm-yes {
  color: var(--bad) !important;
  font-weight: 700;
  font-size: 11.5px;
  padding: 5px 10px;
  background: rgba(255, 92, 122, 0.1) !important;
  border-radius: var(--r-pill);
}

.review-confirm-no {
  font-size: 11.5px;
  padding: 5px 10px;
  border-radius: var(--r-pill);
}

.review-privacy-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-soft);
  text-align: center;
  margin-top: 8px;
  padding: 8px 14px;
  background: rgba(13, 6, 91, 0.04);
  border-radius: var(--r-pill);
  align-self: center;
}

.review-privacy-badge svg,
.review-privacy-badge i {
  width: 14px;
  height: 14px;
  color: var(--purple);
  flex-shrink: 0;
}

.review-consent-footnote {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--ink-soft);
  text-align: center;
  margin-top: 8px;
}

.review-consent-footnote svg,
.review-consent-footnote i {
  width: 15px;
  height: 15px;
  color: var(--purple);
  flex-shrink: 0;
}

.review-upload-saved-bar {
  margin-bottom: 6px;
  width: 100%;
  display: flex;
  justify-content: center;
}

.review-link-saved {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--purple);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px 12px;
  border-radius: var(--r-pill);
}

.review-link-saved:hover {
  background: var(--bg-purple);
}

.review-link-saved svg,
.review-link-saved i {
  width: 16px;
  height: 16px;
}



