/*
 * styles.css: Redwave Wish customer page. Mobile first (people scan the QR with a phone in a
 * shop aisle); wider screens just centre a 480 px column.
 *
 * Colours: the intro's own palette, ink on a soft off-white (inverted in dark mode). Redwave red
 * #E31B23 (Redwave_Business_Detials_and Office_details/Color_palatte) is kept for the pet alone, plus
 * error text. Until 2026-10-29 the buttons, links and shop name were red too: "TOOO red" (Shaafi).
 *
 * Scenes (Shaafi's storyboard, 2026-10-27: "think of it like a video", one focus per scene, never
 * another web form): intro film (.intro, intro.js) -> typing -> each dialog step full screen -> thanks.
 * Type does the work: one very large size for the scene's line, plain text for the rest; flat colour,
 * no cards or rules. Motion is transform + opacity only (plus a short blur on the intro words).
 *
 * Gotchas:
 *  - Minimal on purpose (Shaafi, 2026-10-27: v1 was "too cluttered"): the question, one box, Send.
 *  - Inputs are 16 px: anything smaller makes iPhone Safari zoom the page on focus.
 *  - cursor: default on body (Shaafi's rule): plain text shows the arrow, only inputs the I-beam.
 *  - Dhivehi: [lang=dv] switches to Noto Sans Thaana (loaded by app.js on first use) with a
 *    bigger line height, because Thaana vowel marks sit above and below the letters.
 */
:root {
  --red: #E31B23;
  --ink: #16171A;          /* buttons, links, focus: the intro's text colour */
  --ink-press: #000000;
  --on-ink: #FFFFFF;
  --err: #D0222A;           /* error text only */
  --bg: #F6F6F7;
  --card: #FFFFFF;
  --text: #16171A;
  --muted: #5E626B;
  --line: #E2E3E7;
  --chip: #F0F1F3;
  --ok: #1E9E6A;
  --warn-bg: #FFF4D6;
  --warn-text: #6B4E00;
  --shadow: 0 1px 2px rgb(0 0 0 / 0.05), 0 8px 24px rgb(0 0 0 / 0.06);
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-dv: "Noto Sans Thaana", "MV Faseyha", "MV Waheed", var(--font);
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);   /* strong ease-out: quick start, soft landing */
  --feed: cubic-bezier(0.32, 0.72, 0, 1);       /* the feed swipe after Send (an iOS-like sheet curve) */
  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root {
    --ink: #F5F5F5;
    --ink-press: #FFFFFF;
    --on-ink: #0B0C0E;
    --err: #FF5A61;          /* the brand red is too dark to read on black */
    --bg: #090A0C;
    --card: #111318;
    --text: #F5F5F5;
    --muted: #9B9EA6;
    --line: #272A31;
    --chip: #181A20;
    --ok: #31C48D;
    --warn-bg: #3A2C08;
    --warn-text: #F5B942;
    --shadow: none;
    color-scheme: dark;
  }
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 var(--font);
  cursor: default;
  display: flex;
  flex-direction: column;
  -webkit-tap-highlight-color: transparent;
}
[lang="dv"] body, [lang="dv"] input, [lang="dv"] textarea, [lang="dv"] button { font-family: var(--font-dv); }
[lang="dv"] body { line-height: 1.8; }
[hidden] { display: none !important; }
button, label[for] { cursor: pointer; }
button:disabled { cursor: default; }

.test-banner {
  background: var(--warn-bg);
  color: var(--warn-text);
  font-size: 13px;
  text-align: center;
  padding: 6px 16px;
}

.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px 2px;
  padding-top: max(10px, env(safe-area-inset-top));
  max-width: 512px;
  width: 100%;
  margin: 0 auto;
}
/* The shop's phone, quiet. Always left-to-right: the number is Latin even on the Dhivehi page. */
.shop-head { direction: ltr; text-align: start; padding-top: 4px; }
[dir="rtl"] .shop-head { text-align: right; }
.shop-phone { margin: 0; font-size: 14px; color: var(--muted); font-variant-numeric: tabular-nums; }
.shop-phone a { color: inherit; text-decoration: none; }
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.lang {
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--text);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 15px;
  min-height: 36px;
}

/* Everything fits on one screen (Shaafi: customers should never have to scroll). The typing box
   takes whatever height is left (flex: 1), between 100 px and min(440 px, 52vh). */
main {
  flex: 1;
  width: 100%;
  max-width: 512px;
  margin: 0 auto;
  padding: 0 16px 12px;
  display: flex;
  flex-direction: column;
}
#formScreen, #wishForm { flex: 1; display: flex; flex-direction: column; min-height: 0; }

h1 {
  font-size: clamp(30px, 8.6vw, 40px);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.03em;
  margin: 14px 0 6px;
  text-wrap: balance;
}
[lang="dv"] h1 { letter-spacing: 0; line-height: 1.5; }
.sub { color: var(--muted); margin: 0 0 20px; text-wrap: pretty; }
/* The question on top of the box, set like the intro's (same weight and tight tracking): the intro's
   question words fly onto it (intro.js). .landing hides it while they're on the way. */
h1.ask { margin: 10px 0 0; line-height: 1; letter-spacing: -0.04em; }
[lang="dv"] h1.ask { letter-spacing: 0; line-height: 1.5; }
h1.ask.landing { visibility: hidden; }
h1.ask.landing .wi { animation: none !important; } /* measured at rest by intro.js: no rise offset */
/* Coming out of the eye, the typing scene settles from a touch too big: the camera pulling back (intro.js
   adds .pull after measuring where the question's words land, and removes it when done). */
#formScreen.pull { animation: pull-back 1s var(--ease-out); }
@keyframes pull-back { from { transform: scale(1.08); } }
.perch { height: clamp(92px, 14vh, 128px); flex: none; } /* the pet's room above the box: it stands here, under the question */

.label {
  display: block;
  font-weight: 600;
  font-size: 15px;
  margin: 0 0 8px;
}

input:not([type="checkbox"]):not([type="file"]) {
  width: 100%;
  font: inherit;
  font-size: 16px;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 12px 14px;
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}
input:focus, .box:focus-within {
  border-color: color-mix(in srgb, var(--ink) 45%, transparent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink) 8%, transparent);
}

/* The one box: text on top, photo icon (or the chosen photo) in its bottom row. */
.box {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 22px;
  box-shadow: var(--shadow);
  /* 82 px below: room for the pet (74 px tall) to stand on the Send button without touching the box. */
  margin-bottom: 82px;
  /* The pet's layer is z-index 5. Normally the box is below it (the pet plays on top of the box);
     pet.js adds .pet-behind when the pet ducks behind the box to peek over its edge. */
  position: relative;
  z-index: 4;
  flex: 1 1 auto;
  min-height: 100px;
  max-height: min(440px, 52vh);   /* the scene's one big thing: tall enough that the screen isn't half empty */
  display: flex;
  flex-direction: column;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.box textarea {
  display: block;
  width: 100%;
  flex: 1;
  min-height: 0;
  resize: none;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 17px;
  padding: 16px 16px 28px;
}
.box.pet-behind { z-index: 6; }
.count { position: absolute; bottom: 8px; inset-inline-end: 14px; font-size: 12px; color: var(--muted); }

.as { font-size: 14px; color: var(--muted); margin: 10px 0 0; text-align: center; }
/* Text-style buttons ("Not you?", "Wrong number?", "No thanks", "Wish for something else"). */
.link {
  border: 0;
  background: none;
  padding: 0;
  color: var(--ink);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.link:disabled { color: var(--muted); text-decoration: none; }

.error { color: var(--err); font-size: 14px; margin: 0 0 10px; min-height: 0; }
.error:empty { display: none; }
.error.under { margin: 10px 0 0; text-align: center; }
/* Turnstile bot check on the phone step (app.js). Takes no room until Cloudflare actually shows its
   checkbox: interaction-only keeps it hidden for nearly everyone, and the one-screen layout must hold. */
.ts { display: flex; justify-content: center; }
.ts:empty { display: none; }
#phoneStep.ts-shown .privacy { display: none; }

.primary, .secondary {
  width: 100%;
  min-height: 54px;
  border-radius: 999px;   /* pills: round like the pet */
  font: inherit;
  font-size: 17px;
  font-weight: 600;
  transition: background 0.15s, transform 0.1s, opacity 0.15s;
}
.primary { border: 0; background: var(--ink); color: var(--on-ink); }
.primary:active:not(:disabled) { background: var(--ink-press); transform: scale(0.99); }
.primary:disabled { opacity: 0.6; }
.secondary { border: 1px solid var(--line); background: var(--card); color: var(--text); }
/* Send waits quietly in grey until there is something to send (the box is the scene's one focus), then
   turns ink-dark with a small pop (app.js adds .ready). Still tappable when grey: it says what's missing. */
#sendBtn:not(.ready) { background: var(--chip); color: var(--muted); }
#sendBtn.ready { animation: ready-pop 0.42s var(--ease-out); }
@keyframes ready-pop { 0% { transform: scale(0.96); } 55% { transform: scale(1.02); } 100% { transform: scale(1); } }
@media (hover: hover) {
  .primary:hover:not(:disabled) { background: var(--ink-press); }
}

.privacy { font-size: 13px; color: var(--muted); margin: 14px 0 0; text-align: center; }

/* ---------- thank you ---------- */
.thanks { text-align: center; padding-top: 24px; }
.thanks .sub { margin-bottom: 28px; font-size: 17px; }
.thanks h1 { margin-top: 12px; font-size: clamp(34px, 10vw, 48px); }
.notify { display: grid; gap: 12px; justify-items: center; margin-bottom: 8px; }
.notify-q { margin: 0; font-weight: 600; font-size: 17px; }
/* The "yes" begs to be tapped: full width, a soft glow ring that keeps breathing (this screen is seen
   once per wish, so a little delight is fine). "No thanks" stays small and quiet on purpose. */
.notify-yes { position: relative; isolation: isolate; }
.notify-yes::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 2px solid var(--ink);
  animation: beg 1.8s cubic-bezier(0.23, 1, 0.32, 1) infinite; /* transform + opacity only: cheap */
  z-index: -1;
}
@keyframes beg {
  0% { transform: scale(1); opacity: 0.7; }
  70%, 100% { transform: scale(1.06, 1.35); opacity: 0; }
}
.notify-no { font-size: 14px; color: var(--muted); text-decoration: none; }
.notify-done { color: var(--ok); font-weight: 600; margin: 0; min-height: 52px; display: flex; align-items: center; justify-content: center; }
.again { display: inline-block; margin-top: 18px; font-size: 15px; color: var(--muted); }

/* ---------- popup photo step ---------- */
.photo-choice { display: grid; gap: 10px; }
.photo-pick { display: flex; align-items: center; justify-content: center; gap: 10px; }
.photo-pick svg { width: 22px; height: 22px; fill: currentColor; }
.photo-chosen { display: grid; gap: 12px; justify-items: center; }
.photo-chosen img {
  width: 100%;
  max-height: 38vh;
  object-fit: cover;
  border-radius: 14px;
  border: 1px solid var(--line);
}
.photo-change { font-size: 15px; }
#photoStep .error { margin: 12px 0 0; }

/* ---------- Wishy, the pet (pet.js moves it; these are its looks) ----------
 * Shaafi's reference: a plain glossy ball whose eyes are two upright white pills (slant removed 2026-10-27). Redwave red, 84 x 74 px.
 * pet.js moves .pet and places each .eye; this file only gives them their looks. */
.pet-layer {
  /* Fixed and clipped: whatever the pet does (tilt, hop), it can't create a scrollbar. */
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none; /* never steals a tap from the form */
  z-index: 5;
}
.pet-wait .pet-layer { visibility: hidden; } /* until the intro film is over (app.js) */
.pet {
  position: absolute;
  top: 0; left: 0;
  width: 84px; height: 74px; /* a bit wider than tall (Shaafi); pet.js uses the same 84 x 74 */
  will-change: transform;
}
.pet-body {
  position: absolute;
  inset: 0;
  pointer-events: auto; /* tap the ball: it giggles (or hides, while still shy). The layer itself takes no taps. */
  cursor: pointer;
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
  transform-origin: 50% 100%; /* squash on landing from the ground up */
  border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #FF5A61 0%, var(--red) 46%, #9E0D13 100%);
  /* Only inner shading here: a drop shadow fixed to the ball travelled with it and made it look stuck to
     the page. Its real shadow is .pet-shadow, which stays on the ground. */
  box-shadow: inset -5px -7px 12px rgb(0 0 0 / 0.16);
  will-change: transform;
}
/* Ground shadow: pet.js keeps its centre on the surface under the ball and shrinks/fades it as the ball rises. */
.pet-shadow {
  position: absolute;
  top: 0; left: 0;
  width: 64px; height: 10px;
  margin: -5px 0 0 -32px;   /* centred on the point pet.js gives */
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(0 0 0 / 0.28), rgb(0 0 0 / 0));
  opacity: 0;
  will-change: transform, opacity;
}
.pet-eyes { position: absolute; inset: 0; }
/* Each eye is placed by pet.js on the ball's surface (translate + rotate + scale every frame), so
   no CSS transition or flex layout here: those made the two eyes drift out of line. */
.eye {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 13px;
  height: 22px; /* was 29: "eyes are too elongated" (Shaafi, 2026-10-28) */
  margin: -11px 0 0 -6.5px;
  border-radius: 999px;
  background: #fff;
  will-change: transform;
}
/* Happy: the pills become ^ ^ arcs */
.pet.happy .eye {
  width: 20px;
  height: 13px;
  margin: -6.5px 0 0 -10px;
  background: transparent;
  border: 4px solid #fff;
  border-bottom: 0;
  border-radius: 14px 14px 0 0;
}
.pet-spot { height: 150px; }
/* Asleep (pet.js sets .asleep after 90 s alone): the eyes become soft closed curves, like a gentle
   smile line under each eye; a flatter bend than a full U (Shaafi, 2026-10-28). */
.pet.asleep .eye {
  width: 18px;
  height: 7px;
  margin: 2px 0 0 -9px;
  background: transparent;
  border: 3.5px solid #fff;
  border-top: 0;
  border-radius: 0 0 12px 12px / 0 0 7px 7px;
}

/* ---------- after Send: each step is a full-screen scene (was a centred popup card) ----------
   Still a <dialog> (focus trap, Esc, back gesture), dressed as a page: the whole screen, the page's
   ground, no card, no shadow. Each step's parts rise in (.scene-in, set by showStep in app.js). */
dialog {
  border: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  width: 100vw;
  max-width: none;
  height: 100dvh;
  max-height: none;
  margin: 0;
  overflow-y: auto;
}
dialog[open] { animation: scene-fade 0.3s var(--ease-out); }
dialog::backdrop { background: var(--bg); }
@keyframes scene-fade { from { opacity: 0; } }
/* The photo step is a centred card (Shaafi, 2026-10-29) over the page, which stays loosely visible
   (dimmed, softly blurred) so the pet can be seen swallowing the box behind it. Same blur-rise entrance
   as everything else, quicker. The dialog stays full-screen but see-through (.card, app.js showStep). */
dialog.card { background: transparent; display: flex; align-items: center; justify-content: center; }
dialog.card::backdrop {
  background: color-mix(in srgb, var(--bg) 50%, transparent);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
dialog[open]::backdrop { animation: scene-fade 0.3s var(--ease-out); }
/* Off to the thank-you screen (app.js leaveForThanks): the step fades and lifts away. */
dialog.leaving, dialog.leaving::backdrop { animation: scene-out 0.3s ease-in forwards; }
@keyframes scene-out { to { opacity: 0; transform: translateY(-10px); } }
dialog.card #photoStep {
  margin: 16px;
  width: min(380px, calc(100% - 32px));
  padding: 30px 22px 22px;
  background: var(--card);
  border-radius: 26px;
  box-shadow: 0 24px 64px rgb(0 0 0 / 0.16), 0 2px 8px rgb(0 0 0 / 0.06);
  animation: card-in 0.42s var(--ease-out);
}
@keyframes card-in { from { opacity: 0; transform: translateY(14px) scale(0.97); filter: blur(6px); } }
dialog.card #photoStep h2 { font-size: 30px; padding-inline-end: 34px; } /* room for the × */
dialog.card #photoStep .close { top: 12px; inset-inline-end: 12px; }
dialog.card form.scene-in > :not(.close) { animation-duration: 0.5s; }
dialog.card form.scene-in > :nth-child(3) { animation-delay: 90ms; }
dialog.card form.scene-in > :nth-child(n+4) { animation-delay: 150ms; }
/* The full-screen steps span the width (content still centred) so that, while swiping in over the card,
   their own ground (.swipe-in below) covers the page. Only then: afterwards they're see-through again,
   or the cosy pet behind them (z-index 0) would be covered. */
dialog #phoneStep, dialog #codeStep { max-width: none; padding-inline: max(20px, calc((100% - 440px) / 2)); }

/* Photo -> name/phone -> code: like swiping to the next post in a feed (Shaafi, 2026-10-29; app.js
   showStep). The step slides up and off, laid over the next one, which slides up in from below. The
   photo popup itself just fades in, as before. Fast start, long soft stop, like a feed. */
dialog.swiping { overflow: hidden; }          /* no scrollbar flash from the step below the screen */
dialog form.swipe-in { animation: feed-in 0.6s var(--feed); min-height: 100dvh; background: var(--bg); }
/* #photoStep too: its card entrance rule (card-in) is more specific and would replay instead. */
dialog form.swipe-out, dialog.card #photoStep.swipe-out { position: absolute; top: 0; left: 0; right: 0; pointer-events: none;
  animation: feed-out 0.6s var(--feed) forwards; }
@keyframes feed-in { from { transform: translateY(100dvh); } }
@keyframes feed-out { to { transform: translateY(-100dvh); } }
/* The pet on the steps (app.js moves its layer in here): behind the step's content, cosy in a corner. */
dialog .pet-layer { z-index: 0; }
dialog form { z-index: 1; }
dialog form {
  position: relative;
  max-width: 480px;
  margin: 0 auto;
  padding: max(72px, 11vh) 20px 28px;
  padding-bottom: max(28px, env(safe-area-inset-bottom));
}
dialog h2 {
  margin: 0 0 10px;
  font-size: clamp(32px, 9.4vw, 44px);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
[lang="dv"] dialog h2 { letter-spacing: 0; line-height: 1.5; }
dialog .sub { margin-bottom: 28px; font-size: 17px; }
/* The step's parts rise in: heading first, then the rest. */
dialog form.scene-in > :not(.close) { animation: rise 0.8s var(--ease-out) both; }
dialog form.scene-in > :nth-child(3) { animation-delay: 220ms; }
dialog form.scene-in > :nth-child(n+4) { animation-delay: 340ms; }
dialog input { margin-bottom: 14px; }
.close {
  position: absolute;
  top: max(16px, env(safe-area-inset-top));
  inset-inline-end: 12px;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 50%;
  background: var(--chip);
  color: var(--text);
  font-size: 22px;
  line-height: 1;
}
.phone-field { display: flex; gap: 8px; align-items: stretch; margin-bottom: 14px; }
.phone-field span {
  display: flex;
  align-items: center;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--chip);
  font-size: 16px;
  color: var(--muted);
}
.phone-field input { margin-bottom: 0 !important; flex: 1; min-width: 0; }
/* Not pushed off-screen with left: -9999px: in Dhivehi (right to left) that made the steps scroll 9999 px
   sideways. A 1 px clipped, see-through box at its place instead. */
.hp { position: absolute !important; width: 1px !important; height: 1px; padding: 0; border: 0; opacity: 0;
  clip-path: inset(50%); pointer-events: none; }
input.code { font-size: 28px !important; letter-spacing: 0.4em; text-align: center; font-variant-numeric: tabular-nums; }
.code-links { display: flex; justify-content: space-between; margin-top: 14px; font-size: 14px; }

/* ---------- scene entrances: the parts of a scene rise in one after another ----------
   .rise marks the parts, .r1-.r6 their order; app.js adds .scene-in to the scene to play it. Nothing is
   hidden before that, so if the script fails the page is simply there. */
/* One entrance for everything, the intro's own: a soft blur that rises and clears (Shaafi, 2026-10-28: the
   screens after the intro should share its text style and flow, not feel "corporate"). Big headings go
   further: app.js (wordsIn) splits them into words that come in one by one, like the intro's motto. */
@keyframes rise { from { opacity: 0; transform: translateY(16px); filter: blur(8px); } }
.scene-in .rise { animation: rise 0.8s var(--ease-out) both; }
.scene-in .r2 { animation-delay: 110ms; }
.scene-in .r3 { animation-delay: 220ms; }
.scene-in .r4 { animation-delay: 330ms; }
.scene-in .split { animation: none !important; } /* its words animate instead (.wi) */
.scene-in .wi { display: inline-block; animation: rise 0.8s var(--ease-out) both; } /* delays set by app.js */
.scene-in .r5 { animation-delay: 900ms; }    /* thank-you: the "text me?" question comes after a beat */
.scene-in .r6 { animation-delay: 1050ms; }

/* ---------- the intro film (intro.js; Shaafi's frame-by-frame storyboard, 2026-10-28) ----------
   The motto fills the screen (.intro-hero) and leaves, the ball falls in, the camera dives through its eye
   (.intro-iris) into the question (.intro-panel), then the typing scene takes over. intro.js moves everything
   every frame (a small loop writing el.style, which the CSP allows); this only gives the looks, so no
   transitions on anything it moves. .intro-stage is the camera; the iris and the panel sit outside it so the
   zoom doesn't scale them. Stacking: hero < shadow < ball (in the stage), then iris, then panel. */
.intro {
  position: fixed;
  inset: 0;
  width: 100vw;               /* not 100%: it must not shift sideways if a scrollbar appears under it */
  z-index: 30;                /* over the page and the pet layer (5) */
  overflow: hidden;           /* the ball starts above the screen */
  background: var(--bg);
  color: var(--text);
  transition: opacity 0.4s var(--ease-out);
}
/* The pull-back (intro.js): the screen goes see-through so the typing scene shows under the question's
   words as they fly onto its heading; taps reach the page from then on. */
.intro.reveal { background: transparent; pointer-events: none; }
.intro.reveal .intro-panel { background: transparent; }
/* The camera (intro.js). No will-change here or on the ball: with it the browser draws the layer once at
   normal size and stretches that picture as the camera zooms in, which looked pixelated. */
.intro-stage, .intro-far { position: absolute; inset: 0; transform-origin: 0 0; }
/* The pet's twin: same size, red glossy oval and white pill eyes as .pet, so the hand-over is seamless. */
.intro-ball {
  position: absolute;
  top: 0; left: 0;
  z-index: 2;
  width: 84px; height: 74px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #FF5A61 0%, var(--red) 46%, #9E0D13 100%);
  box-shadow: inset -5px -7px 12px rgb(0 0 0 / 0.16);
  transform-origin: 50% 100%; /* squash and stretch from the ground up */
  transform: translate(-200px, -200px); /* off screen until intro.js places it */
}
.intro-eyes { position: absolute; inset: 0; } /* moved as one by intro.js (gaze, eased there) */
.intro-eyes span {
  position: absolute;
  top: 19px;
  width: 13px; height: 22px; /* same pills as .eye */
  border-radius: 999px;
  background: #fff;
  transition: transform 0.09s ease-out, height 0.12s, border-radius 0.12s;
}
.intro-eyes span:first-child { left: 24px; }
.intro-eyes span:last-child { right: 24px; }
.intro-eyes.shut span { transform: scaleY(0.12); }    /* blink, and "mmm" while it gulps */
.intro-eyes.squeeze span { transform: scaleY(0.38); } /* squashed: eyes squeezed */
.intro-eyes.sly span { transform: scaleY(0.6); }      /* "hmm, tasty": a sly half-squint before it eats */
.intro-eyes.wide span { transform: scale(1.12); }     /* "uh-oh" */
/* Happy ^ ^ arcs, as .pet.happy .eye */
.intro-eyes.happy span {
  top: 26px;
  width: 18px; height: 12px;
  background: transparent;
  border: 4px solid #fff;
  border-bottom: 0;
  border-radius: 14px 14px 0 0;
  transform: none;
}
.intro-eyes.happy span:first-child { left: 21px; }
.intro-eyes.happy span:last-child { right: 21px; }
.intro-shadow {
  position: absolute;
  top: 0; left: 0;
  z-index: 1;
  width: 64px; height: 10px;
  margin: -5px 0 0 -32px;     /* centred on the point intro.js gives */
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(0 0 0 / 0.28), rgb(0 0 0 / 0));
  opacity: 0;
}
/* The glass floor (intro.js places it at the ground line): the ball's mirror image, faint and fading
   out downwards, plus a hairline sheen where the glass is. Fades in with the motto. */
.intro-floor {
  position: absolute;
  left: 0; right: 0;
  height: 150px;
  z-index: 0;                 /* under the shadow and the ball */
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s ease-out;
  -webkit-mask-image: linear-gradient(to bottom, #000, transparent 130px);
  mask-image: linear-gradient(to bottom, #000, transparent 130px);
}
.intro-floor.on { opacity: 1; }
.intro-floor::before {        /* the sheen: a hairline that fades out to both sides */
  content: "";
  position: absolute;
  left: 8%; right: 8%; top: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text) 10%, transparent), transparent);
}
.intro-mirror { position: absolute; inset: 0; transform-origin: 0 0; opacity: 0.13; } /* 0.2 was "bit more dim" */
@media (prefers-color-scheme: dark) { .intro-mirror { opacity: 0.22; } } /* glass reads stronger on black */

/* Scene one: the motto big, filling the screen, its words fading in one by one (as the old intro did);
   it stays while the ball falls in past the words (behind the ball). */
.intro-hero {
  position: absolute;
  left: 24px; right: 24px; top: 20%;
  z-index: 1;
  margin: 0;
  font-size: clamp(54px, 15vw, 96px); /* big: it fills the screen (Shaafi) */
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -0.04em;
  text-wrap: balance;
  transform-origin: 0 0;
}
[lang="dv"] .intro-hero { letter-spacing: 0; line-height: 1.5; font-size: clamp(42px, 12vw, 76px); }
.intro-hero .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.3em);
  filter: blur(8px);
  /* soft, but not slow (Shaafi: first "fade in bit slowly", then "bit too slow, ease in, stay just a bit") */
  transition: opacity 0.7s ease-out, transform 0.9s var(--ease-out), filter 0.7s ease-out;
}
.intro-hero .w.in { opacity: 1; transform: none; filter: blur(0); }
/* The eye the camera dives into, drawn at its real on-screen size (intro.js sets left/top/width/height every
   frame) so it stays sharp however big it gets; a scaled copy would blur. Same white pill as the eye. */
.intro-iris { position: absolute; z-index: 3; left: 0; top: 0; border-radius: 9999px; background: #fff; display: none; }
/* The question screen: fades in over the eye's white once the camera is through. */
.intro-panel {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 24px 8vh;
  background: var(--bg);
  opacity: 0;
  transition: opacity 0.45s ease-out, background-color 0.5s ease-out;
}
.intro-panel.open { opacity: 1; }
.intro-q {
  width: 100%;
  max-width: 560px;
  font-size: clamp(40px, 12vw, 72px);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  margin: 0;
  text-wrap: balance;
}
/* Its words ease in one by one exactly like the motto's (intro.js adds .in to each), then fly onto the
   typing scene's heading (intro.js, Web Animations; that overrides the transform below). */
.intro-q .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.3em);
  filter: blur(8px);
  transition: opacity 0.7s ease-out, transform 0.9s var(--ease-out), filter 0.7s ease-out;
}
.intro-q .w.in { opacity: 1; transform: none; filter: blur(0); }
[lang="dv"] .intro-q { letter-spacing: 0; line-height: 1.5; font-size: clamp(34px, 10vw, 62px); }

/* Short screens (small phones, or Safari with its bars showing): squeeze the top a little so
   nothing needs a scroll. The pet's perch (46 px) and lane (76 px) can't shrink: the ball is 74 px tall. */
@media (max-height: 600px) {
  .test-banner { padding: 3px 16px; font-size: 12px; }
  .top { padding-top: 6px; }
  h1 { font-size: 24px; margin-top: 8px; }
  .box { min-height: 84px; }
}

@media (prefers-reduced-motion: reduce) {
  /* pet.js also keeps the pet still; the blink and happy face (no movement) stay. */
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
