/* The game: the pearl, the flipping card, every screen, the reveal, the
   charts and the share dialog. */

/* --- layout ---
   The brand line (the small pearl and "Three Questions") sits above the card
   on every screen but the welcome (David, 26 October 2026: the brand line on
   the welcome "shouldn't be there at all"). On a phone the welcome is one
   centred column: the pearl, then "Three questions.", then the card. On a
   laptop or desktop the pearl sits to the left of the title on one row
   (David, 27 October 2026), the pair directly above the card. At
   Start the pearl and the title rise together and become the brand line
   (pearl.js dock(), travel.js riseInto()). The hero holds the big pearl on
   the welcome and the wait. */
.page { overflow-x: clip; }
.game {
  width: min(100% - 1.5rem, 1120px); margin-inline: auto; padding-block: .4rem 1rem;
  display: grid; justify-items: center; align-content: start; gap: 1.1rem;
}
/* a phone: a little more night either side of the glass, so its edges have something to bend */
@media (max-width: 480px) { .game { width: min(100% - 2.25rem, 1120px); } }
.hero { display: grid; justify-items: center; gap: 1.2rem; width: 100%; }
.hero-copy { text-align: center; max-width: 34rem; }
body:not([data-screen="welcome"]) .hero-copy { display: none; }
body:not([data-screen="welcome"]):not([data-screen="loading"]) .hero { display: none; }
.pearl-slot { display: grid; place-items: center; }

/* held by the window's width as well as by rem, so text set larger (a
   phone's 200% text) never runs "Three questions." off the edge */
.title {
  margin: 0; font: italic 600 min(clamp(2.4rem, 11.5vw, 3.2rem), 13vw)/.95 var(--display); letter-spacing: -.012em;
  padding-bottom: .08em; white-space: nowrap;
}
/* the title's stand-in as it rises into the brand line (travel.js) */
.title-ghost { white-space: nowrap; will-change: transform, opacity; }
/* Each part of the title carries its own iridescent fill, so the number
   word can change without the parent's text clip painting all three. */
.title-rest, .num-word {
  background: var(--iris); background-size: 200% 100%; -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: iris-drift 12s ease-in-out infinite alternate; animation-play-state: var(--motion, running);
}
@keyframes iris-drift { to { background-position: 100% 0; } }
/* The number word cycles One, Two, Three and rests on Three (main.js). The
   three words share one grid cell, so "questions." never moves; the cell is
   as wide as "Three". Under reduced motion "Three" simply stands. */
.num { display: inline-grid; justify-items: end; }
.num-word { grid-area: 1 / 1; opacity: 0; padding-inline-end: .04em; }
.num-word.is-on { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  /* Until the script takes over, "Three" waits a moment, so a fast load
     never flashes it before "One"; with no script at all it simply appears.
     The script counts only while this hold has not yet let "Three" show
     (main.js threeShowing reads its clock). 2.4 s, not 1.6 s (28 October
     2026): a phone's cold load at 150 ms and 1.6 Mbps started the script
     right at the old edge, so the count was a coin toss there. */
  .num:not(.is-cycling):not(.is-rested) .num-word.is-on {
    animation: iris-drift 12s ease-in-out infinite alternate, num-wait 2.4s steps(1, end) both;
    animation-play-state: var(--motion, running), running;
  }
}
@keyframes num-wait { from { opacity: 0; } to { opacity: 1; } }
.lede { margin: .9rem auto 0; max-width: 38ch; font-size: 1.12rem; color: var(--ink-soft); }
/* the lede rides inside the welcome card as its first block (main.js
   welcomeScreen copies it from index.html, where the first paint and search
   have it), so nothing stands between the title and the card */
.welcome .lede { margin: 0 0 1.1rem; max-width: none; font-size: 1.02rem; line-height: 1.5; text-align: left; }
.hero-copy .lede.is-moved { display: none; }

.stage { width: 100%; max-width: 640px; perspective: 1800px; position: relative; }
body[data-screen="reveal"] .stage, body[data-screen="finale"] .stage { max-width: 960px; }
@media (min-width: 1200px) { body[data-screen="finale"] .stage { max-width: 1160px; } }
body[data-screen="loading"] .stage { max-width: 560px; }

/* the welcome: no brand line, one centred column (research-look 3.1) */
body[data-screen="welcome"] .masthead { display: none; }
body[data-screen="welcome"] .game { justify-items: center; justify-content: center; grid-template-columns: minmax(0, 1fr); align-content: center; gap: .75rem; }
body[data-screen="welcome"] .hero { justify-items: center; gap: .75rem; }
body[data-screen="welcome"] .stage { max-width: 30rem; }
/* On a laptop or desktop the pearl sits to the left of the title, one
   lock-up like the brand line after Start (David, 27 October 2026: "On the
   first page, the pearl can be to the left of the three questions wording
   too please", "that is on web, not mobile"); the pearl's size and the gap
   follow the title's, so the pair keeps its shape at every window size.
   Phones keep the pearl above the title. */
@media (min-width: 900px) {
  body[data-screen="welcome"] .game { --lock-fs: clamp(3.4rem, 6vw, 5.4rem); gap: 1.1rem; padding-top: max(1.4rem, env(safe-area-inset-top)); }
  body[data-screen="welcome"] .stage { max-width: 33rem; }
  body[data-screen="welcome"] .hero { grid-auto-flow: column; justify-content: center; align-items: center; gap: calc(var(--lock-fs) * .3); }
  body[data-screen="welcome"] .title { font-size: var(--lock-fs); }
}

/* --- the pearl --- */
/* The pearl is one object that moves between places: the welcome's hero,
   the brand line, the wait. Its size comes from where it sits. It is the
   tactile take's pearl: push it and it gives, drag it and its surface rolls,
   flick it and it spins (js/pearl.js). Hovering leaves it as it is. */
.pearl {
  --pearl-size: 76px;
  --t1: #F3C6D9; --t2: #CBBEF5; --t3: #B9D8F5; --t4: #BFEBDD;
  position: relative; width: var(--pearl-size); aspect-ratio: 1; border-radius: 50%;
  touch-action: pan-y; cursor: grab; flex: none; -webkit-user-select: none; user-select: none;
  animation: breathe 7s ease-in-out infinite; animation-play-state: var(--motion, running);
}
.pearl.is-grabbing { cursor: grabbing; }
body[data-screen="welcome"] .pearl-slot .pearl { --pearl-size: min(40vw, 19dvh, 168px); }
body[data-screen="welcome"] .game { padding-top: max(1rem, env(safe-area-inset-top)); }
body[data-screen="loading"] .pearl-slot .pearl { --pearl-size: min(42vw, 20dvh, 190px); }
/* beside the title on a laptop or desktop: half as tall again as the
   title's size, as the brand line's pearl is beside its word */
@media (min-width: 900px) {
  body[data-screen="welcome"] .pearl-slot .pearl { --pearl-size: calc(var(--lock-fs) * 1.5); }
}
/* a laptop window under about 760 px tall (1280 x 720, and most laptop
   browsers once their toolbars are counted): the lock-up gives height first,
   so the welcome and its footer fit without a page scroll (design-final nit,
   26 October 2026); shorter still, the page scrolls as before (H1) */
@media (min-width: 900px) and (max-height: 760px) {
  body[data-screen="welcome"] .game { --lock-fs: clamp(3rem, min(6vw, 10.5dvh), 5.4rem); }
}
/* docked in the brand line: small, breathing gently, a 44 px target */
.brand-slot .pearl { --pearl-size: 34px; touch-action: none; animation-name: breathe-sm; }
.brand-slot .pearl::before { content: ""; position: absolute; inset: -5px; border-radius: 50%; }
/* the CSS pearl at brand size (reduced motion, low power): the brand mark's
   own iridescence and glow rather than a flat grey bead (design review L3) */
.brand-slot .pearl:not([data-tinted="yes"]):not(.is-red) .pearl-css {
  background: radial-gradient(circle at 34% 30%, #fff 0 8%, transparent 30%), conic-gradient(from 30deg, var(--pearl-rose), var(--pearl-lilac), var(--pearl-sky), var(--pearl-mint), var(--pearl-gold), var(--pearl-rose));
}
.brand-slot .pearl-css { box-shadow: 0 0 14px 1px var(--beam-glow); }
.brand-slot .pearl-css::before { filter: blur(3px) saturate(1.4); opacity: .5; }
.brand-slot .pearl-css::after { box-shadow: inset -4px -5px 9px rgba(20, 16, 40, .32), inset 2px 3px 6px rgba(255, 255, 255, .5); }
@keyframes breathe { 50% { transform: translateY(-6px) scale(1.025); } }
@keyframes breathe-sm { 50% { transform: scale(1.03); } }

.pearl-halo {
  position: absolute; inset: -32%; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, var(--beam-glow) 0, transparent 62%);
  opacity: .75; transition: opacity .5s ease, background .4s ease, transform .5s ease;
}
.pearl.is-happy .pearl-halo { opacity: 1; transform: scale(1.18); }
.pearl.is-red .pearl-halo { background: radial-gradient(circle, rgba(240, 60, 80, .55) 0, transparent 62%); opacity: 1; }

.pearl-css {
  position: absolute; inset: 0; border-radius: 50%; overflow: hidden; isolation: isolate;
  background: radial-gradient(circle at 36% 30%, #F7F4FB 0, #ECE7F5 26%, #CBC4DE 62%, #8C86A6 100%);
  box-shadow: 0 20px 60px -20px rgba(0, 0, 0, .5);
  transition: opacity .7s ease, background .6s ease;
}
.pearl[data-tinted="yes"] .pearl-css { background: radial-gradient(circle at 36% 30%, #FFFFFF 0, var(--t1) 24%, var(--t2) 58%, var(--t3) 100%); }
.pearl-css::before {
  content: ""; position: absolute; inset: -25%;
  background: conic-gradient(from 0deg, var(--t1), var(--t2), var(--t3), var(--t4), var(--pearl-gold), var(--t1));
  filter: blur(14px) saturate(1.5); opacity: .9; mix-blend-mode: multiply;
  animation: turn 26s linear infinite; animation-play-state: var(--motion, running);
  /* The turning layer clips itself to the pearl's circle (a third of its
     own box, which is half as wide again as the pearl, about the same
     centre, so the circle stays put however it turns). WebKit does not hold
     a turning, blurred, blended child inside its parent's rounded overflow,
     so an iPhone showed a pastel square behind the CSS pearl (seen by David,
     28 October 2026). The clip sits on this layer, not on .pearl-css, which
     would cut the pearl's own shadow and the brand pearl's glow with it. */
  clip-path: circle(calc(100% / 3));
}
.pearl-css::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background:
    radial-gradient(circle at 33% 27%, rgba(255, 255, 255, .62) 0, rgba(255, 255, 255, .26) 13%, transparent 36%),
    radial-gradient(circle at 72% 82%, rgba(20, 16, 40, .38), transparent 55%);
  box-shadow: inset -14px -20px 40px rgba(20, 16, 40, .3), inset 8px 10px 24px rgba(255, 255, 255, .5);
}
/* calm, even while it thinks or the reading is written: slow, steady, weighted */
.pearl[data-mood="thinking"] .pearl-css::before { animation-duration: 12s; }
.pearl[data-mood="building"] .pearl-css::before { animation-duration: 16s; }
/* the push and the spin, drawn on the CSS pearl too */
.pearl-css, .pearl-gl, .pearl-halo { translate: var(--push-x, 0px) var(--push-y, 0px); }
.pearl-css::before { rotate: var(--spin, 0deg); }
.pearl.is-red .pearl-css { background: radial-gradient(circle at 36% 30%, #FFE3E6 0, #F56C7E 40%, #B4233A 100%); }
@keyframes turn { to { transform: rotate(360deg); } }

.pearl-gl {
  position: absolute; inset: -3.2%; width: 106.4%; height: 106.4%; max-width: none;
  opacity: 0; transition: opacity .7s ease; pointer-events: none;
}
.pearl.is-gl .pearl-gl { opacity: 1; }
.pearl.is-gl .pearl-css { opacity: 0; }

.pearl.is-juddering .pearl-css, .pearl.is-juddering .pearl-gl, .pearl.is-juddering .pearl-halo { animation: judder .56s cubic-bezier(.36, .07, .19, .97) both; }
@keyframes judder {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(5px); }
  30%, 50%, 70% { transform: translateX(-9px) rotate(-2deg); }
  40%, 60% { transform: translateX(9px) rotate(2deg); }
}

/* --- the flipping card --- */
/* 3D only while it turns: at rest the card is flat, so its glass's backdrop
   is the night behind it, never an empty 3D context (direction 3's finding,
   27 October 2026) */
.flipper { display: grid; grid-template-columns: minmax(0, 1fr); position: relative; }
.flipper.is-flipping { transform-style: preserve-3d; }
.face {
  grid-area: 1 / 1; min-width: 0; justify-self: center; width: 100%;
  display: flex; flex-direction: column;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
}
.face > .screen { flex: 1 1 auto; }
/* the card's padding, shared by its screen and the rim's copy of the night */
.face { --pad-y: clamp(1.25rem, 5vw, 2.6rem); --pad-x: var(--pad-y); }
.face--back { transform: rotateY(180deg); }
/* The turn lands without a jolt (MERGED M3; David, 23 October 2026: "a
   weird slam where the whole screen shakes as it lands"): while it turns,
   the one grid row is exactly the card's eased height, and each face is its
   own height, never stretched to the taller one (so "Play again" no longer
   turns as a tall stretched slab), clipped to the turning card. The height
   flip.js eases to is the new face's own, so nothing moves when the old face
   goes. During the turn the glass drops its backdrop blur (Safari and 3D do
   not mix) and stands on a solid of the blurred night's own average, the
   same rim and bevel on it, so the turn never flashes to a flat slab. */
.flipper.is-flipping { will-change: transform; grid-template-rows: minmax(0, 1fr); }
.flipper.is-flipping > .face { align-self: start; max-height: 100%; min-height: 0; }
.flipper.is-flipping > .face > .screen { min-height: 0; overflow: hidden; }
/* the reduced-motion crossfade the same: each face its own height (H1) */
.flipper.is-fading { grid-template-rows: minmax(0, 1fr); }
.flipper.is-fading > .face { align-self: start; max-height: 100%; min-height: 0; }
.flipper.is-fading > .face > .screen { min-height: 0; overflow: hidden; }
/* Play again (design review H1): the outgoing face is only the part of it
   that was on screen, its height frozen by flip.js clip(), so it never grows
   to its whole length while the page turns back to the welcome. */
.face.is-clipped { overflow: hidden; }
.flipper.is-flipping > .face {
  -webkit-backdrop-filter: none; backdrop-filter: none; transition: none;
  background: linear-gradient(var(--glass-flip), var(--glass-flip));
}
.flipper.is-flipping .lens, .flipper.is-flipping > .face > .pane, .flipper.is-fading .lens { visibility: hidden; }
/* where solid glass was asked for, the turn stands on it too */
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
  .flipper.is-flipping > .face { background: var(--glass-solid); }
}

/* --- THE SHARP NIGHT and THE BEND (direction 1, Night window, round 3).
   Clear glass proves itself by what it does to sharp things behind it, so
   the night has a sharp layer: plain points of light and nothing else (no
   lines, no rings, no discs, never joined into a figure; David, 27 October
   2026: "I don't like those lines in the background"). It lives with the
   card (.night, the stage's first child): the width of the window, reaching
   up to the window's top and stopping just below the card, with the line
   above the card (the title on the welcome, the brand line elsewhere) and
   the footer kept clear of it, so no sharp light ever sits behind words
   outside the card. It scrolls with the card on a phone, so the pane's copy
   of it never has to move. While the card turns, it dims (is-turning).

   Inside the pane the night is drawn again by the pane's own copy (.lens,
   fx.js makeLens, aligned with the night by the same geometry, no script):
   in the middle a finer copy of the same points at 30% (--star-in), over
   the tint; in the glass's edge (--edge wide, base.css) the points at full
   strength (--star-rim). The night itself stops at the card's edge (a hole
   the card's size, cut by clip-path from the stage's own width), so
   nothing is drawn twice. THE BEND: once the player has touched or typed
   (and a phone's keyboard has settled), fx.js lens() gives the edge's copy
   a plain filter: url() (never a url() inside backdrop-filter, which WebKit
   drops; in WebKit too since David's iPhone look, fx.js canLens), a static
   bend with a small colour split, blue bent most, after a breath of
   softness so a split point is a soft spectrum, and the unbent edge fades
   into the bent one. Until then, or if the copy cannot line up, the edge
   shows its points unbent: the clear edge without the bend, never a
   broken picture. --- */
.stage {
  /* the card's width, for the night's hole (cqw); the stage's width never depends on what is in it */
  container-type: inline-size;
  /* the night's top above the card's top, and the line above the card it keeps clear of */
  --night-top: -100vh; --night-down: 34px;
  --ko-y: calc(.8rem + 22px); --ko-rx: 132px; --ko-ry: 36px;
}
body[data-screen="welcome"] .stage {
  --t-fs: min(clamp(2.4rem, 11.5vw, 3.2rem), 13vw); --t-gap: .6rem;
  --ko-y: calc(var(--t-gap) + var(--t-fs) * .52); --ko-rx: calc(var(--t-fs) * 4.6); --ko-ry: calc(var(--t-fs) * .52 + .9rem);
}
@media (min-width: 900px) {
  /* the title sits right of the pearl: the knockout moves with it, by half the pearl and the gap */
  body[data-screen="welcome"] .stage { --t-fs: var(--lock-fs); --t-gap: 1.1rem; --night-down: 14px; --ko-x: calc(50% + var(--lock-fs) * .9); }
}
.night {
  position: absolute; z-index: -1; pointer-events: none; overflow: clip;
  top: var(--night-top); bottom: calc(-1 * var(--night-down)); left: calc(50% - 50vw); width: 100vw;
  --ko: radial-gradient(var(--ko-rx) var(--ko-ry) at var(--ko-x, 50%) calc(-1 * var(--night-top) - var(--ko-y)), transparent 58%, #000 100%);
  --foot: linear-gradient(to bottom, #000 calc(100% - var(--night-down)), transparent);
  -webkit-mask-image: var(--ko), var(--foot); -webkit-mask-composite: source-in;
  mask-image: var(--ko), var(--foot); mask-composite: intersect;
  /* none of it behind the card: the card draws its own copy (.lens) */
  clip-path: polygon(evenodd, 0 0, 100% 0, 100% 100%, 0 100%, 0 0,
    calc(50vw - 50cqw) calc(-1 * var(--night-top)), calc(50vw + 50cqw) calc(-1 * var(--night-top)),
    calc(50vw + 50cqw) calc(100% - var(--night-down)), calc(50vw - 50cqw) calc(100% - var(--night-down)),
    calc(50vw - 50cqw) calc(-1 * var(--night-top)));
  transition: opacity .7s ease .1s;
}
.stage.is-turning > .night { opacity: 0; transition: opacity .18s ease; }
/* the points drift (base.css, THE SLOW SKY): the farthest layer, 4.5 px/s
   at their quickest at any width (95 s over --points-pace, 5 at David's
   "Lively", 28 October 2026, base.css), slower than every disc and, on a
   laptop, every pool (at 390 wide the pools, sized by the window, come down
   to about their pace), in a straight line and back
   (never a turn about a centre, which would read as a chart of the
   stars), starting halfway, at their quickest, never from rest. Each box
   is 56 px larger on every side than its layer, more than the 48 by 24 px
   it travels, so no empty strip ever shows; the night and every copy of
   it shift alike, so they stay lined up, and fx.js syncSky gives a face's
   copies the night's own start as it lands. The bend is static: the
   points slide through the glass's bent edge, gathering and splitting as
   they cross it. The middle's finer copy is faded out under the text
   column (.lens-col below), so nothing moves under the words. Transform
   alone. */
.night > i, .lens-sky > i { position: absolute; inset: -56px; animation: night-drift calc(95s / var(--points-pace)) cubic-bezier(.37, 0, .63, 1) calc(95s / var(--points-pace) / -2) infinite alternate; animation-play-state: var(--motion, running); }
@keyframes night-drift { to { transform: translate(-48px, -24px); } }
.night-pts { background: var(--star); opacity: var(--star-o); -webkit-mask: url(/img/night-points.svg) 0 0 / 480px 480px repeat; mask: url(/img/night-points.svg) 0 0 / 480px 480px repeat; }
/* the pane's copy: three layers over the same geometry as .night */
.lens {
  position: absolute; inset: 0; z-index: -2; border-radius: inherit; overflow: hidden; pointer-events: none;
  /* the catchlight (base.css --glint): the rim under the pearl, lit, the light carried a little way along the edge */
  background:
    radial-gradient(min(36%, 160px) 2.5px at var(--lx) 0, var(--glint), transparent),
    radial-gradient(76px 15px at var(--lx) 0, var(--glint-bloom), transparent);
}
.lens > div { position: absolute; inset: 0; }
/* the copy runs a little past the card's foot, so the edge can gather the night from just below it too */
.lens-sky { position: absolute; top: var(--night-top); bottom: -48px; left: calc(50% - 50vw); width: 100vw; }
/* the middle: the finer points at 30%, from the edge's inner face to the
   text column (--pad-x) and gone under it, so no point sits or drifts
   beside a glyph (a moving point is the one motion the eye picks up while
   reading, and a point at 30% under the explainer took it under 4.5:1,
   still or drifting). The fade in at the edge's inner face is as before,
   so the points still cross the seam from the edge unbroken. */
.lens-col {
  --h: linear-gradient(to right,
    transparent var(--edge), #000 calc(var(--edge) + 4px),
    #000 max(calc(var(--edge) + 4px), calc(var(--pad-x) - 4px)), transparent max(calc(var(--edge) + 4px), var(--pad-x)),
    transparent min(calc(100% - var(--edge) - 4px), calc(100% - var(--pad-x))), #000 min(calc(100% - var(--edge) - 4px), calc(100% - var(--pad-x) + 4px)),
    #000 calc(100% - var(--edge) - 4px), transparent calc(100% - var(--edge)));
  --v: linear-gradient(to bottom, transparent var(--edge), #000 calc(var(--edge) + 4px), #000 calc(100% - var(--edge) - 4px), transparent calc(100% - var(--edge)));
  -webkit-mask-image: var(--h), var(--v); -webkit-mask-composite: source-in;
  mask-image: var(--h), var(--v); mask-composite: intersect;
}
.lens-col .lens-pts { opacity: var(--star-in); -webkit-mask-image: url(/img/night-points-fine.svg); mask-image: url(/img/night-points-fine.svg); }
/* the edge: the points at full, gathered by the glass, gone at its inner face */
.lens-band, .lens-bent {
  --h: linear-gradient(to right, #000 calc(var(--edge) - 2px), transparent calc(var(--edge) + 2px), transparent calc(100% - var(--edge) - 2px), #000 calc(100% - var(--edge) + 2px));
  --v: linear-gradient(to bottom, #000 calc(var(--edge) - 2px), transparent calc(var(--edge) + 2px), transparent calc(100% - var(--edge) - 2px), #000 calc(100% - var(--edge) + 2px));
  -webkit-mask-image: var(--h), var(--v); -webkit-mask-composite: source-over;
  mask-image: var(--h), var(--v); mask-composite: add;
}
.lens-pts { background: var(--star); opacity: var(--star-rim); -webkit-mask: url(/img/night-points.svg) 0 0 / 480px 480px repeat; mask: url(/img/night-points.svg) 0 0 / 480px 480px repeat; }
/* the edge gathers more light than the open sky shows: the same field again, offset, so the glass's edge sparkles where it bends */
:is(.lens-band, .lens-bent) .lens-pts {
  -webkit-mask: url(/img/night-points.svg) 0 0 / 480px 480px repeat, url(/img/night-points.svg) 233px 157px / 480px 480px repeat;
  mask: url(/img/night-points.svg) 0 0 / 480px 480px repeat, url(/img/night-points.svg) 233px 157px / 480px 480px repeat;
}
/* the bent edge: its own layer, drawn once and only faded in after (a
   one-off crossfade in the edge, never behind the words; none under
   reduced motion) */
.lens-bent { opacity: 0; will-change: opacity; transition: opacity .5s ease; }
.lens-band { transition: opacity .5s ease; }
.face.is-lensed > .lens > .lens-bent { opacity: 1; }
.face.is-lensed > .lens > .lens-band { opacity: 0; }
.face.is-lensed > .lens > .lens-bent > .lens-sky { filter: url(#tq-lens-card); }
.stage.is-turning .lens { visibility: hidden; }
/* a browser without mask compositing or container units cannot cut the night's hole or the edge's fade: no sharp night at all, the clear glass over the soft sky */
@supports not (((mask-composite: intersect) or (-webkit-mask-composite: source-in)) and (width: 1cqw)) { .night, .lens { display: none; } }
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more), (forced-colors: active) { .night, .lens { display: none; } }

.screen {
  padding: var(--pad-y) var(--pad-x); min-width: 0; overflow-wrap: break-word; -webkit-hyphens: manual; hyphens: manual;
  border-radius: inherit;
}
/* a card with buttons at its foot is two parts (flip.js): a body that scrolls
   on a laptop and a foot that does not; on a phone the body is no box at all */
.screen-body { display: contents; }
.stack { display: grid; gap: 1.3rem; }

.actions { margin-top: 1.6rem; display: flex; gap: .7rem; justify-content: space-between; align-items: center; flex-wrap: wrap; }
.actions .btn--primary { margin-left: auto; }
.actions--centre { justify-content: center; }
.actions--centre .btn--primary { margin-left: 0; }
.actions--stack { flex-direction: column; align-items: stretch; }
/* the water card's two ways on at a phone's width (design review H4): "See
   the full picture" is too long to share a row, so both stand full width,
   one above the other, rather than stagger */
@media (max-width: 479px) {
  .reveal-card[data-section="water"] > .actions { flex-direction: column; align-items: stretch; }
  .reveal-card[data-section="water"] > .actions .btn { width: 100%; justify-content: center; }
  .reveal-card[data-section="water"] > .actions .btn--primary { margin-left: 0; }
}
.actions--stack .btn--primary { margin-left: 0; }
.actions--share { justify-content: center; }
.actions--share .btn--primary { margin-left: 0; }

/* Start: an invitation that breathes */
.btn--start { width: 100%; font-size: 1.1rem; min-height: 56px; }
.btn--start::after {
  content: ""; position: absolute; inset: -2px; z-index: -2; border-radius: inherit;
  box-shadow: 0 0 0 0 var(--beam-glow); animation: invite 3.2s ease-out infinite; animation-play-state: var(--motion, running);
}
@keyframes invite { 0% { box-shadow: 0 0 0 0 var(--beam-glow); } 70%, 100% { box-shadow: 0 0 0 14px transparent; } }

.btn.is-busy { pointer-events: none; }
.btn.is-busy .btn-arrow { display: none; }
.btn-spinner { display: none; width: 18px; height: 18px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; animation: turn .8s linear infinite; animation-play-state: var(--motion, running); }
.btn.is-busy .btn-spinner { display: inline-block; }

/* --- welcome --- */
.welcome { width: 100%; }
/* the lede, the first name, the date of birth, Start, then the quiet privacy link */
.welcome .stack { gap: 1rem; }
.welcome .quiet-link { margin-top: -.4rem; justify-self: center; }
/* the welcome's one AI line, small and plain, under the privacy link */
.welcome-ai { margin: -1rem 0 0; text-align: center; font-size: .86rem; line-height: 1.4; color: var(--ink-soft); }

/* the date of birth: one fieldset, three plain number fields side by side (day, month, year), 52 px tall */
.dob { margin: 0; padding: 0; border: 0; min-width: 0; }
.dob-legend { padding: 0; margin: 0 0 .4rem; font-weight: 600; }
.dob .hint { margin-bottom: .2rem; }
.dob-parts { display: flex; flex-wrap: wrap; gap: .7rem; }
.dob-part { display: grid; gap: .3rem; }
.dob-label { font-weight: 500; font-size: .95rem; color: var(--ink-soft); }
.dob-part--day .dob-input, .dob-part--month .dob-input { width: 4.6rem; }
.dob-part--year .dob-input { width: 6.4rem; }
.dob-input { text-align: center; font-variant-numeric: tabular-nums; }
/* On a laptop the welcome must still fit the window with the date added: the
   card widens a little and the first name and the date of birth sit side by
   side, their fields on one line (a judgement call of the date of birth
   round, for David's look at the test link). Phones keep one column. */
/* The two columns line up (design-dob M1): both start on one top line, both
   hints take two lines, and the name box drops by the height of the date's
   Day, Month and Year labels, so the label and the legend, the two hints,
   the boxes and the two error lines each share a row, before and after a
   date error. */
@media (min-width: 900px) {
  body[data-screen="welcome"] .stage { max-width: 44rem; }
  .welcome .stack { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 1.6rem; align-items: start; }
  .welcome .stack > :not(.field) { grid-column: 1 / -1; }
  .welcome .field > .hint { min-height: calc(2 * 1.45em); line-height: 1.45; text-wrap: pretty; }
  .welcome .dob .hint { margin-bottom: 0; }
  .welcome .field:not(.dob) > input { margin-top: calc(.95rem * 1.4 + .3rem); }
}
.dob-label { line-height: 1.4; }

/* --- the tick (the sign-up) --- */
.tick {
  margin-top: 1.4rem; display: flex; align-items: center; gap: .85rem; min-height: 52px; cursor: pointer;
  padding: .6rem .9rem; border-radius: 16px; border: 1px solid var(--line); background: var(--field);
  font-size: 1.08rem;
}
.tick input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.tick-box {
  flex: none; width: 28px; height: 28px; border-radius: 9px; border: 2px solid var(--grid-strong); position: relative;
  transition: background .2s ease, border-color .2s ease, transform .3s var(--ease-spring);
}
.tick-box::after {
  content: ""; position: absolute; left: 8px; top: 3px; width: 8px; height: 14px;
  border: solid var(--btn-ink); border-width: 0 3px 3px 0; transform: rotate(45deg) scale(0); transition: transform .3s var(--ease-spring);
}
.tick input:checked + .tick-box { background: var(--btn-bg); border-color: transparent; transform: scale(1.04); }
.is-springy .tick { transform: scale(var(--press, 1)); }
.tick input:checked + .tick-box::after { transform: rotate(45deg) scale(1); }
.tick input:focus-visible + .tick-box { outline: 3px solid var(--focus); outline-offset: 3px; }
.tick input[aria-invalid="true"] + .tick-box { border-color: var(--danger); }
.quiet-link { display: inline-flex; align-items: center; min-height: 44px; margin-top: .6rem; color: var(--ink-soft); font-size: .95rem; }

/* --- questions --- */
.pips { margin: 0 0 1rem; display: flex; align-items: center; gap: .45rem; }
.pip { width: 28px; height: 4px; border-radius: 4px; background: var(--line); transition: background .4s ease; }
.pip.is-on { background: var(--strike); }
.pips-text { margin-left: .5rem; font-size: .875rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft); }
.q-heading { margin: 0; }
.q-label { display: block; font: italic 600 clamp(1.9rem, 6.6vw, 2.7rem)/1.08 var(--display); text-wrap: balance; cursor: text; }
.explainer { margin: .75rem 0 1.1rem; color: var(--ink-soft); font-size: 1.04rem; max-width: 52ch; }
textarea.answer { min-height: 9.5rem; resize: vertical; }
.below-answer { display: flex; justify-content: space-between; gap: 1rem; margin-top: .55rem; }
.counter { margin: 0; font-size: .875rem; color: var(--ink-soft); white-space: nowrap; }
.note-slot:empty { display: none; }
.nudge {
  margin: .75rem 0 0; display: flex; gap: .65rem; align-items: flex-start;
  padding: .8rem 1rem; border-radius: 16px; font-weight: 600; line-height: 1.45;
  background: rgba(243, 198, 217, .1); border: 1px solid rgba(243, 198, 217, .35);
}
.note-slot--error .nudge { background: transparent; border-color: var(--danger); color: var(--danger); }
.note-slot--ok .nudge { background: transparent; border-color: var(--line); color: var(--ok); font-weight: 500; }
/* the same words sent again: one quiet line under the nudge (main.js renderNote),
   left-aligned with the bubble's text (its border, padding, mark and gap) */
.nudge-again { margin: .45rem 0 0 calc(1px + 1rem + 10px + .65rem); text-align: left; font-size: .95rem; color: var(--ink-soft); }
.nudge-mark { flex: none; width: 10px; height: 10px; margin-top: .45em; border-radius: 50%; background: var(--strike); box-shadow: 0 0 10px 2px var(--beam-glow); }
.note-slot--nudge .nudge { animation: nudge-in .6s var(--ease-spring) both; }
@keyframes nudge-in { from { opacity: 0; transform: translateY(8px) scale(.98); } }
.check-status { margin: .9rem 0 0; min-height: 1.4em; font-size: .95rem; color: var(--ink-soft); }
/* the status line keeps its room, empty or not, so a check never nudges the card (the shake David saw on Next) */
.check-status:empty { min-height: 1.4em; }

/* --- states --- */
.state { text-align: center; display: grid; justify-items: center; }
.state .lede-sm { margin-inline: auto; }
.state-mark { width: 54px; height: 54px; margin-bottom: 1rem; border-radius: 50%; border: 2px solid var(--line); position: relative; }
.state-mark::before, .state-mark::after { content: ""; position: absolute; border-radius: 3px; background: var(--strike); }
.state-mark--full_today::before, .state-mark--resting::before { left: 14px; right: 14px; top: 25px; height: 3px; }
.state-mark--slow_down::before { left: 25px; top: 13px; width: 3px; height: 15px; }
.state-mark--slow_down::after { left: 25px; top: 25px; width: 12px; height: 3px; }
.state-mark--error::before, .state-mark--offline::before, .state-mark--blocked::before { left: 25px; top: 12px; width: 3px; height: 18px; }
.state-mark--error::after, .state-mark--offline::after, .state-mark--blocked::after { left: 25px; top: 35px; width: 3px; height: 4px; }

/* --- loading --- */
.loading { text-align: center; }
/* the wait, from the playful take, made calmer: a slim bar of light through
   the real stages, dots that settle in as each part is done, the pearl above
   with a mote of light per finished part */
.progress { margin: 1.5rem auto 1.3rem; max-width: 26rem; height: 8px; border-radius: 999px; background: var(--grid); overflow: hidden; position: relative; }
.progress-fill {
  --p: .12; position: absolute; inset: 0; border-radius: inherit; background: var(--strike);
  transform-origin: left center; transform: scaleX(var(--p)); box-shadow: 0 0 16px 2px var(--beam-glow);
}
.progress-glint {
  position: absolute; inset: 0; background: linear-gradient(90deg, transparent 0, rgba(255, 255, 255, .8) 50%, transparent 100%);
  width: 30%; opacity: .7; animation: glint 3.2s ease-in-out infinite; animation-play-state: var(--motion, running);
}
@keyframes glint { from { transform: translateX(-110%); } to { transform: translateX(400%); } }
.steps { list-style: none; margin: 0 auto; padding: 0; display: grid; gap: .45rem; text-align: left; width: fit-content; max-width: 100%; }
.step { display: flex; align-items: center; gap: .7rem; color: var(--ink-soft); font-weight: 500; transition: color .3s ease; }
.step-dot { flex: none; width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--grid-strong); transition: background .3s ease, border-color .3s ease, transform .4s var(--ease-spring); }
.step.is-done { color: var(--ink); }
.step.is-done .step-dot { background: var(--strike); border-color: transparent; animation: step-in .55s cubic-bezier(.3, 1.25, .5, 1) both; }
@keyframes step-in { from { transform: scale(.45); } }
.step.is-active { color: var(--ink); }
.step.is-active .step-dot { border-color: var(--pearl-lilac); animation: pulse 2.6s ease-in-out infinite; animation-play-state: var(--motion, running); }
@keyframes pulse { 50% { box-shadow: 0 0 0 6px transparent, 0 0 12px 2px var(--beam-glow); transform: scale(1.08); } }
.beat { margin: 1.3rem 0 0; font: italic 600 1.3rem/1.25 var(--display); color: var(--ink); min-height: 1.3em; }
.slow-note { margin: .6rem 0 0; font-size: .95rem; color: var(--ink-soft); }
.slow-note:empty { display: none; }

/* --- the reveal --- */
.reveal-intro { text-align: center; padding-block: clamp(2.2rem, 8vw, 4rem); }
.intro-line { font-size: clamp(2.1rem, 7.4vw, 3.6rem); max-width: 18ch; margin-inline: auto; }

.reveal-card .pips { margin-bottom: 1.2rem; }
.rewrite { display: grid; gap: .3rem; }
.old { position: relative; margin: .1rem 0 .9rem; font: 600 clamp(1.55rem, 5.2vw, 2.3rem)/1.22 var(--display); color: var(--ink); max-width: 30ch; }
.strikes { position: absolute; inset: 0; pointer-events: none; }
.strike-line {
  position: absolute; left: var(--l); top: var(--t); width: var(--w); height: 3px; margin-top: -1.5px; border-radius: 3px;
  background: var(--strike); box-shadow: 0 0 10px 1px var(--beam-glow);
}
.strike-dot {
  position: absolute; left: var(--l); top: var(--t); width: 12px; height: 12px; border-radius: 50%;
  background: radial-gradient(circle, #FFFFFF 0 30%, var(--pearl-lilac) 70%);
  box-shadow: 0 0 10px 3px var(--beam), 0 0 28px 9px var(--beam-glow);
  transform: translate(-50%, -50%); opacity: 0;
}
.new-wrap { position: relative; max-width: 100%; width: fit-content; }
.new {
  margin: 0; font: italic 600 clamp(1.9rem, 6.6vw, 3rem)/1.08 var(--display); text-wrap: balance;
  background: var(--iris); background-size: 220% 100%; background-position: 0 0;
  -webkit-background-clip: text; background-clip: text; color: transparent; padding-bottom: .08em;
}
.new:focus { outline: none; }
.new:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; border-radius: 6px; }
.beam {
  position: absolute; top: -4%; bottom: 0; left: 0; width: 4px; border-radius: 4px; opacity: 0; pointer-events: none;
  background: var(--beam); box-shadow: 0 0 16px 5px var(--beam), 0 0 48px 18px var(--beam-glow);
}
.reveal-card.is-pre .cap--new, .reveal-card.is-pre .new, .reveal-card.is-pre [data-rise] { opacity: 0; }

.result { margin-top: 2rem; padding-top: 1.8rem; border-top: 1px solid var(--line); display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.8rem; }
.result-side, .result-main { min-width: 0; }
@media (min-width: 860px) {
  .result { grid-template-columns: minmax(250px, 300px) minmax(0, 1fr); gap: 2.6rem; align-items: start; }
}
.result-side { display: grid; gap: 1.4rem; align-content: start; }
.type-row { display: flex; gap: 1rem; align-items: center; }
.type-badge { flex: none; width: 72px; height: 72px; border-radius: 50%; display: grid; place-items: center; filter: drop-shadow(0 6px 18px var(--beam-glow)); }
.type-badge .emblem { width: 72px; height: 72px; }
.type-text { min-width: 0; }
.type-name { margin: .1rem 0 0; font: 600 1.75rem/1.1 var(--display); overflow-wrap: anywhere; }
.type-tagline { margin: .25rem 0 0; color: var(--ink-soft); font-size: .95rem; line-height: 1.45; }

.art { margin: 0; display: grid; gap: .55rem; }
.art-stage {
  position: relative; aspect-ratio: 4 / 3; border-radius: 22px; overflow: hidden;
  background: var(--glass-hi); border: 1px solid var(--line); display: grid; place-items: center;
  /* Every picture stays inside its rounded frame (David, 26 October 2026:
     "The colour picture also falls outside the squircle"): overflow alone
     lets a composited child (the swatch's turning, blended sheen) paint past
     the corners, so the frame clips its whole layer to its own shape too. */
  clip-path: inset(0 round 22px); isolation: isolate;
}
.art--animal .art-stage > *, .art--water .art-stage > * { max-width: 100%; max-height: 100%; }
.art figcaption { font-size: .875rem; color: var(--ink-soft); font-weight: 600; }
.art-fallback--animal { font-size: 5rem; line-height: 1; }
.art-fallback--water { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 40%, rgba(120, 190, 230, .35)); }

/* The colour: a lit orb in the player's own stops and finish (David, 27
   October 2026: "Lit orb"), centred in the square frame on its soft floor
   shadow, never an edge-to-edge gradient. reveal.js sets the stops as custom
   properties only (CSP). The body light comes from the upper left; a back
   rim and a hairline edge in the ink keep a very dark orb readable on the
   dark card and a very light one on the light card. The finish layer
   (::before) sits under the light (::after), so a highlight stays white. */
.art .swatch {
  --s1: #E9E4F2; --s2: #CBBEF5; --s3: #B9D8F5; --s4: #B9D8F5; --tx: 0; --ty: 0;
  --orb-edge: rgba(236, 234, 244, .3);
  --orb-edge: color-mix(in oklab, var(--ink) 30%, transparent);
  position: absolute; inset: 0; display: grid; place-items: center;
}
@media (prefers-color-scheme: light) {
  .art .swatch { --orb-edge: rgba(27, 25, 48, .18); --orb-edge: color-mix(in oklab, var(--ink) 18%, transparent); }
}
.swatch-floor {
  position: absolute; left: 26%; right: 26%; bottom: 8%; height: 9%; border-radius: 50%; pointer-events: none;
  background: radial-gradient(closest-side, rgba(8, 6, 22, .5), transparent);
  background: radial-gradient(closest-side, color-mix(in oklab, var(--s3) 40%, rgba(8, 6, 22, .72)), transparent);
  filter: blur(2px);
}
.swatch-orb {
  position: relative; width: 60%; aspect-ratio: 1; border-radius: 50%; margin-bottom: 7%;
  overflow: hidden; isolation: isolate; pointer-events: none;
  background:
    radial-gradient(circle at 68% 78%, transparent 36%, rgba(10, 8, 24, .34) 76%),
    radial-gradient(circle at calc(36% + var(--tx) * 5%) calc(32% + var(--ty) * 5%), var(--s1), var(--s2) 38%, var(--s3) 68%, var(--s4));
  box-shadow: 0 0 0 1px var(--orb-edge), inset -7px -10px 18px rgba(14, 10, 32, .38), inset 5px 7px 14px rgba(255, 255, 255, .2), 0 12px 24px -12px var(--s2);
}
.swatch-orb::before, .swatch-orb::after { content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none; }
/* the light: a soft highlight up and to the left, and a back rim along the lower right edge */
.swatch-orb::after {
  background:
    radial-gradient(circle at calc(33% + var(--tx) * 8%) calc(27% + var(--ty) * 8%), rgba(255, 255, 255, .9) 0 3%, rgba(255, 255, 255, .42) 9%, transparent 24%),
    radial-gradient(circle at 42% 38%, transparent 60%, rgba(255, 255, 255, .22) 72%);
}
/* gloss: one sharp highlight and a small window reflection low down */
.swatch[data-finish="gloss"] .swatch-orb::after {
  background:
    radial-gradient(ellipse 12% 9% at calc(33% + var(--tx) * 8%) calc(26% + var(--ty) * 8%), #fff 0 50%, rgba(255, 255, 255, 0) 100%),
    radial-gradient(circle at calc(33% + var(--tx) * 8%) calc(26% + var(--ty) * 8%), rgba(255, 255, 255, .3) 0 10%, transparent 22%),
    radial-gradient(ellipse 30% 9% at 56% 87%, rgba(255, 255, 255, .3), transparent 70%),
    radial-gradient(circle at 42% 38%, transparent 60%, rgba(255, 255, 255, .24) 72%);
}
/* glow: lit from within, a broad soft light in its body (never a second
   highlight beside the light's own), and a halo of its own colour made
   lighter, so a black or navy orb glows rather than smudges; it keeps the
   floor shadow and the hairline rim of every finish, so a white orb on the
   white morning frame still stands out */
.swatch[data-finish="glow"] .swatch-orb {
  --glow-to: #FFFFFF;
  background:
    radial-gradient(circle at 48% 50%, rgba(255, 255, 255, .22) 0, transparent 64%),
    radial-gradient(circle at 46% 44%, var(--s1), var(--s2) 44%, var(--s3) 76%, var(--s4));
  box-shadow: 0 0 0 1px var(--orb-edge), inset 0 0 14px 2px rgba(255, 255, 255, .2), 0 0 16px 2px rgba(255, 255, 255, .3);
  box-shadow: 0 0 0 1px var(--orb-edge), inset 0 0 14px 2px rgba(255, 255, 255, .2),
    0 0 16px 2px color-mix(in oklab, color-mix(in oklab, var(--s2) 45%, var(--glow-to)) 70%, transparent),
    0 0 32px 5px color-mix(in oklab, color-mix(in oklab, var(--s3) 40%, var(--glow-to)) 40%, transparent),
    0 12px 24px -12px var(--s2);
}
/* on the white morning frame the lightened halo leans to the pearl's lilac, so a white glow is still seen */
@media (prefers-color-scheme: light) { .swatch[data-finish="glow"] .swatch-orb { --glow-to: #D9CCFF; } }
/* metallic: a sharp highlight and a broad, soft band of sky light that
   bends round the sphere as a polished ball's reflection does, never a
   hard band straight across it */
.swatch[data-finish="metallic"] .swatch-orb::before {
  background:
    radial-gradient(130% 70% at 50% 112%, transparent 50%, rgba(255, 255, 255, .2) 60%, rgba(255, 255, 255, .06) 68%, transparent 78%),
    radial-gradient(120% 70% at 50% -14%, rgba(255, 255, 255, .28), transparent 62%),
    radial-gradient(110% 60% at 50% 104%, rgba(0, 0, 0, .14), transparent 70%);
  mix-blend-mode: overlay;
}
.swatch[data-finish="metallic"] .swatch-orb::after {
  background:
    radial-gradient(ellipse 14% 7% at 34% 24%, #fff 0 45%, rgba(255, 255, 255, 0) 100%),
    radial-gradient(circle at 42% 38%, transparent 60%, rgba(255, 255, 255, .26) 72%);
}
/* matte: no shine, only a broad soft light and a fine grain */
.swatch[data-finish="matte"] .swatch-orb { box-shadow: 0 0 0 1px var(--orb-edge), inset -7px -10px 18px rgba(14, 10, 32, .4), 0 12px 24px -12px var(--s2); }
.swatch[data-finish="matte"] .swatch-orb::before { background: url(/img/grain.svg); background-size: 120px; opacity: .3; mix-blend-mode: multiply; }
.swatch[data-finish="matte"] .swatch-orb::after {
  background:
    radial-gradient(circle at 32% 26%, rgba(255, 255, 255, .16), transparent 55%),
    radial-gradient(circle at 42% 38%, transparent 62%, rgba(255, 255, 255, .2) 74%);
}
/* pearlescent and iridescent: a sheen of colours over the body, which turns
   slowly and stands still under reduced motion */
.swatch[data-finish="pearlescent"] .swatch-orb::before {
  background: conic-gradient(from calc(200deg + var(--tx) * 40deg), var(--pearl-rose), var(--pearl-lilac), var(--pearl-sky), var(--pearl-mint), var(--pearl-gold), var(--pearl-rose));
  mix-blend-mode: soft-light; opacity: .8;
}
.swatch[data-finish="iridescent"] .swatch-orb::before {
  background: conic-gradient(from calc(160deg + var(--tx) * 60deg), #ff9ad5, #b59dff, #8fd3ff, #95f0c8, #ffe08f, #ff9ad5);
  mix-blend-mode: overlay; opacity: .62;
}
@media (prefers-reduced-motion: no-preference) {
  .swatch[data-finish="pearlescent"] .swatch-orb::before { animation: turn 18s linear infinite; animation-play-state: var(--motion, running); }
  .swatch[data-finish="iridescent"] .swatch-orb::before { animation: turn 14s linear infinite; animation-play-state: var(--motion, running); }
}
/* forced colours: a plain filled circle in the text colour */
@media (forced-colors: active) {
  .art .swatch .swatch-orb { forced-color-adjust: none; background: CanvasText; box-shadow: none; }
  .swatch-orb::before, .swatch-orb::after, .swatch-floor { display: none; }
}

.scales-box { display: grid; gap: .9rem; }
.scales-box h3 { margin: 0; }
.scales { display: grid; gap: 1rem; }

.headline { margin: 0; font: 600 clamp(1.6rem, 4.6vw, 2.25rem)/1.15 var(--display); text-wrap: balance; }
.points-title { margin: 1.5rem 0 .2rem; }
.points { list-style: none; margin: 0; padding: 0; counter-reset: pt; }
.point { position: relative; padding: .95rem 0 .95rem 2.6rem; border-top: 1px solid var(--line); counter-increment: pt; }
.point:first-child { border-top: 0; }
.point::before {
  content: counter(pt); position: absolute; left: 0; top: 1rem; width: 1.75rem; height: 1.75rem; border-radius: 50%;
  display: grid; place-items: center; font: 700 .85rem/1 var(--body); color: var(--btn-ink); background: var(--btn-bg);
}
.takeaway { margin: 0; font-weight: 700; font-size: 1.1rem; line-height: 1.3; text-wrap: pretty; }
.quote { margin: .3rem 0 0; font: italic 500 1.12rem/1.35 var(--display); color: var(--ink); overflow-wrap: anywhere; }
.meaning { margin: .35rem 0 0; color: var(--ink-soft); max-width: 60ch; text-wrap: pretty; }
.traits { list-style: none; margin: 1.3rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .55rem; }
/* nothing inside a card is glass (audit M10): the traits are words on a hairline */
.traits li { padding: .2rem .3rem .2rem 0; font-weight: 600; font-size: .95rem; }
.traits li::before { content: ""; display: inline-block; width: .55rem; height: .55rem; margin-right: .5rem; border-radius: 50%; background: var(--strike); vertical-align: .05em; }
.result-main .ai-line { margin-top: 1.4rem; }

/* the reveal at a glance (SPEC 2.6): their own words re-aimed as the big
   line, the headline, two takeaways as dots, each why behind a tap;
   no block of text past about three lines without a break */
.result--glance { display: block; margin-top: 1.4rem; padding-top: 1.4rem; }
.mirror { margin: 0; font: italic 600 clamp(1.45rem, 4.4vw, 2rem)/1.22 var(--display); color: var(--ink); overflow-wrap: anywhere; max-width: 34ch; }
.mirror--long { font-size: clamp(1.25rem, 3.6vw, 1.6rem); max-width: 42ch; }
/* a wider measure on a laptop, so a sentence of theirs runs to fewer lines (design review M1) */
@media (min-width: 900px) { .mirror { max-width: 46ch; } .mirror--long { max-width: 56ch; } }
/* on a phone: a touch smaller, and the takeaways' gutter narrower, so blocks stay near three lines */
@media (max-width: 600px) {
  .mirror, .mirror--long { max-width: none; }
  .mirror { font-size: 1.3rem; }
  .mirror--long { font-size: 1.18rem; }
  .points--dots .point { padding-left: 1.3rem; }
  .points--dots .point::before { left: .1rem; }
  .meaning { font-size: .97rem; }
  .together p { font-size: 1rem; }
  .summary { padding: 1.1rem 1.1rem; }
  .summary blockquote { font-size: 1.28rem; }
}
/* THE ANSWERS, treatment A: written in light, in their colour (David, 26
   October 2026: "The layout of the answwers is fairly boring, just white
   text, I feel like somethign can happen with that?"; research-look 2.2).
   Each line of their words is written in by the beam, then settles from
   beam-white into the section's own ink: on the colour card the player's own
   colour, on the others the section's hue mixed toward the ink, each stop
   clamped in reveal.js to 4.5:1 against the measured glass. Their own phrase
   catches one slow nacre sheen and rests iridescent. */
.mirror { position: relative; }
.mirror-line { display: block; position: relative; }
.mirror-line + .mirror-line { margin-top: .35em; }
.mirror.is-inked .mirror-line {
  background: linear-gradient(100deg, var(--ink-a, var(--ink)), var(--ink-b, var(--ink)));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.mirror-own {
  background: var(--iris); background-size: 240% 100%; background-position: 0 0;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.mirror .beam { top: 0; bottom: 0; width: 3px; }
/* the mirror's words are painted by their own ink and carry no halo, so on
   clear glass they get a local tint of their own, a soft pool of night (or
   morning) behind those lines alone, never a veil on the pane */
.face .mirror::before {
  content: ""; position: absolute; inset: -.45em -1.2em; z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side, var(--mirror-tint), color-mix(in srgb, var(--mirror-tint) 60%, transparent) 62%, transparent);
}
:root { --mirror-tint: rgba(6, 7, 16, .34); }
@media (prefers-color-scheme: light) { :root { --mirror-tint: rgba(255, 255, 255, .5); } }
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more), (forced-colors: active) { .face .mirror::before { display: none; } }
/* treatment C, the water card's mirror only: light on water, a caustic
   pattern drifting slowly under the words like sun on a pool floor, drawn
   once as a tile (an SVG image) and moved by transform alone, never a filter
   run every frame; it stands still under reduced motion. The pattern is a
   net of thin soft lines, each cell far larger than a letter (the design
   review's H3: the earlier, letter-sized squiggles read as ghost text). */
.caustics {
  position: absolute; inset: -.6rem -.8rem; z-index: -1; overflow: hidden; pointer-events: none; border-radius: 18px;
  -webkit-mask: radial-gradient(closest-side, #000 68%, transparent); mask: radial-gradient(closest-side, #000 68%, transparent);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
}
.caustics-tile {
  position: absolute; inset: -256px; opacity: .13; will-change: transform;
  background: rgb(196, 240, 234);
  -webkit-mask: var(--caustic) 0 0 / 512px 512px repeat; mask: var(--caustic) 0 0 / 512px 512px repeat;
  animation: caustic-drift 46s ease-in-out infinite alternate; animation-play-state: var(--motion, running);
}
@keyframes caustic-drift { to { transform: translate(180px, 96px) rotate(4deg); } }
@media (prefers-color-scheme: light) { .caustics-tile { opacity: .14; background: var(--hue-water); } }
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more), (forced-colors: active) { .caustics { display: none; } }
@media (forced-colors: active) { .mirror.is-inked .mirror-line, .mirror-own { color: CanvasText; background: none; } }
/* the headline a clear step above the takeaways (1.06rem), with room below
   it, so on the card it never reads as a third takeaway (design review L1) */
.result--glance .headline { margin-top: 1.1rem; font-size: clamp(1.3rem, 3.8vw, 1.55rem); font-family: var(--body); font-weight: 700; line-height: 1.25; }
.result--glance .headline + .points-thread { margin-top: 1.5rem; }
/* the takeaways hang as small pearls on a thread of light: the light runs
   down the thread as each one rises, and each bead lights (reveal.js) */
.points-thread { position: relative; margin-top: 1rem; }
.points--dots { margin-top: 0; }
.points-thread > .thread {
  position: absolute; left: .45rem; top: 1.4rem; bottom: 1.4rem; width: 1px; transform-origin: top center;
  background: linear-gradient(var(--ink-a, var(--hue)), var(--ink-b, var(--hue))); opacity: .35;
}
.points--dots .point { display: block; padding: .6rem 0 .6rem 1.85rem; border-top: 0; }
.points--dots .point::before { content: none; }
.bead {
  position: absolute; left: 0; top: .82rem; width: .9rem; height: .9rem; border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #fff 0 9%, transparent 34%), conic-gradient(from 30deg, var(--pearl-rose), var(--pearl-lilac), var(--pearl-sky), var(--pearl-mint), var(--pearl-gold), var(--pearl-rose));
  box-shadow: 0 0 10px 1px var(--beam-glow), inset -2px -3px 4px rgba(20, 16, 40, .3);
  transition: box-shadow .4s ease, transform .4s var(--ease-spring);
}
.point.is-open .bead { box-shadow: 0 0 16px 4px var(--beam-glow), inset -2px -3px 4px rgba(20, 16, 40, .3); transform: scale(1.12); }
.points--dots .takeaway { font-size: 1.06rem; }
/* "Why?" just to the right of the words, at every width (David, 26 October
   2026: "The why at the end should be just to the right of the wording. no
   point it being pushed under"; research-look 2.3): glued to the takeaway's
   last word, so the two wrap together and "Why?" never sits on a line alone.
   Its hit area grows by ::before, so the 44 px target never makes the line
   taller. */
.takeaway-end { white-space: nowrap; }
.why {
  position: relative; display: inline-flex; align-items: center; gap: .3rem; vertical-align: .06em;
  min-height: 0; min-width: 0; padding: .12rem .55rem; margin-left: .2rem; border: 0; border-radius: 999px;
  background: var(--why-bg, rgba(255, 255, 255, .1)); color: var(--ink); cursor: pointer;
  font: 600 .9rem/1.2 var(--body); touch-action: manipulation;
}
/* z-index 1: the next point (position: relative) would otherwise paint over
   the hit area's foot and leave the target 43 px (design review M2) */
.why { z-index: 1; }
.why::before { content: ""; position: absolute; inset: -12px -6px; }
.why[aria-expanded="true"] { background: var(--why-on, rgba(255, 255, 255, .2)); }
[data-section="colour"] .why { --why-bg: rgba(214, 111, 79, .24); --why-on: rgba(214, 111, 79, .4); }
[data-section="animal"] .why { --why-bg: rgba(140, 123, 234, .26); --why-on: rgba(140, 123, 234, .42); }
[data-section="water"] .why { --why-bg: rgba(43, 165, 155, .26); --why-on: rgba(43, 165, 155, .42); }
@media (prefers-color-scheme: light) {
  [data-section="colour"] .why { --why-bg: rgba(181, 86, 47, .13); --why-on: rgba(181, 86, 47, .24); }
  [data-section="animal"] .why { --why-bg: rgba(106, 79, 208, .13); --why-on: rgba(106, 79, 208, .24); }
  [data-section="water"] .why { --why-bg: rgba(0, 144, 122, .13); --why-on: rgba(0, 144, 122, .24); }
}
.why-label { text-decoration: none; }
/* "Tap to see" goes (research-look 2.3: it is what pushed "Why?" under the
   words). DRAFT FOR DAVID'S APPROVAL: the words stay in copy.js, unseen. */
.why-hint { display: none; }
.why:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
@media (forced-colors: active) { .why { border: 1px solid ButtonText; } }
.why-panel { padding: .45rem 0 .3rem; }
.why-panel .quote { margin-top: 0; color: var(--ink-a, var(--ink)); }
/* the water card after the tick goes straight to its reading: the struck and
   real questions it showed above the tick are not shown again (David, 26
   October 2026: "Not really too sure why it is asking these questions
   again"); the real question stays the card's heading for a screen reader */
.rewrite--asked { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.reveal-card[data-asked="yes"] .result--glance { margin-top: .2rem; padding-top: 0; border-top: 0; }
.result--glance .ai-line { margin-top: 1.2rem; }

/* the consent at the reveal (SPEC 2.8): the box, one plain line, two equal buttons */
.read-consent { margin-top: 1.6rem; padding-top: 1.4rem; border-top: 1px solid var(--line); display: grid; gap: .7rem; max-width: 40rem; }
.consent-sentence { margin: 0; font-size: 1.08rem; line-height: 1.5; }
/* how to change your mind, under the sentence (checker-dob F2) */
.consent-line { margin: 0; font-size: .95rem; line-height: 1.45; color: var(--ink-soft); }
.actions--equal { display: grid; grid-template-columns: 1fr 1fr; gap: .7rem; margin-top: .6rem; }
.actions--equal .btn { width: 100%; margin: 0; }
@media (max-width: 420px) { .actions--equal { grid-template-columns: 1fr; } }
.kept { margin-top: 1.6rem; padding-top: 1.4rem; border-top: 1px solid var(--line); }
.kept-line { margin: 0; font: italic 600 clamp(1.4rem, 4.2vw, 1.8rem)/1.25 var(--display); }
.kept-line:focus { outline: none; }
.kept-line:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; border-radius: 6px; }

.reveal-card[data-section="water"] .mirror { isolation: isolate; }
:root { --caustic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='512' height='512'%3E%3Cfilter id='c' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='turbulence' baseFrequency='.0039' numOctaves='2' seed='11' stitchTiles='stitch' result='n'/%3E%3CfeColorMatrix in='n' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 -5 0 0 0 1.15'/%3E%3CfeGaussianBlur stdDeviation='1.4'/%3E%3C/filter%3E%3Crect width='512' height='512' filter='url(%23c)'/%3E%3C/svg%3E"); }

/* --- charts --- */
[data-section="colour"] { --hue: var(--hue-colour); }
[data-section="animal"] { --hue: var(--hue-animal); }
[data-section="water"] { --hue: var(--hue-water); }
.spectrum { display: grid; gap: .2rem; }
.spectrum-poles { display: flex; justify-content: space-between; gap: 1rem; font-size: .875rem; color: var(--ink-soft); }
.pole.is-lean { color: var(--ink); font-weight: 700; }
.spectrum-track { position: relative; height: 24px; }
.spectrum-track::before { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 1px; background: var(--grid); }
.spectrum-mid { position: absolute; left: 50%; top: 50%; width: 1px; height: 12px; background: var(--grid-strong); transform: translate(-50%, -50%); }
.spectrum-lean { position: absolute; top: 50%; left: var(--from); width: var(--w); height: 6px; margin-top: -3px; border-radius: 0 4px 4px 0; background: var(--hue); }
.spectrum-lean.is-left { border-radius: 4px 0 0 4px; }
.spectrum-dot {
  position: absolute; left: var(--at); top: 50%; width: 14px; height: 14px; border-radius: 50%; background: var(--hue);
  box-shadow: 0 0 0 2px var(--surface); transform: translate(-50%, -50%); transition: transform .2s var(--ease-spring);
}
.spectrum-track.is-hot .spectrum-dot { transform: translate(-50%, -50%) scale(1.3); }
.spectrum-value { margin: 0; font-size: .875rem; font-weight: 600; }
.spectrum--compact .spectrum-poles { font-size: .875rem; }

.chart-tip {
  position: fixed; left: 0; top: 0; z-index: 50; transform: translate(var(--x), var(--y)); pointer-events: none;
  padding: .4rem .65rem; border-radius: 10px; background: var(--ink); color: var(--bg); font-size: .85rem; line-height: 1.3;
  display: grid; box-shadow: 0 8px 24px -8px rgba(0,0,0,.5);
}
.chart-tip strong { font-size: .95rem; }

/* The finale, centred and using the width (David, 26 October 2026: "they
   all seem to be shunted to the right, instead of using full width and
   centralised"): every block sits on the card's centre line; on a wide
   window the card takes the width and the long paragraph runs in two
   columns, so the whole is shorter. "In one line" and "In short" are one
   section now (reveal.js). */
.finale { display: grid; gap: 0; justify-items: center; text-align: center; align-content: start; }
.finale > * { width: 100%; }
.finale > .btn, .finale > .actions { width: auto; }
.finale-title { margin: .4rem 0 1.4rem; text-wrap: balance; }
.type-set { list-style: none; margin: 0; padding: 0; display: grid; gap: .8rem; }
@media (min-width: 760px) { .type-set { grid-template-columns: repeat(3, 1fr); } }
/* nothing inside a card is glass (audit M10): a type is space and one line of its hue */
.type-tile {
  padding: 1.1rem .4rem 1.2rem; border-top: 1px solid var(--line);
  display: grid; gap: .25rem; justify-items: center; text-align: center; position: relative;
}
.type-tile::after { content: ""; position: absolute; left: 50%; top: -1px; width: 3.5rem; height: 3px; border-radius: 3px; transform: translateX(-50%); background: var(--hue); }
.type-tile .emblem { width: 64px; height: 64px; margin-bottom: .4rem; }
.type-tile .type-name { font-size: 1.55rem; }
/* the breakdown, moved here from the section cards (SPEC 2.6): each type with its art and traits */
.type-tile { align-content: start; gap: .8rem; }
.type-head { display: flex; gap: .9rem; align-items: center; text-align: left; }
.type-head .emblem { flex: none; margin-bottom: 0; }
.type-tile .art { width: 100%; }
/* every picture whole inside its frame (art audit, 27 October 2026): the
   pictures are square (the water scene's circle, the animal's box), so the
   frame is square at the old 9.5rem height, and each picture fills the
   frame's inside exactly rather than sizing itself from its own content */
.type-tile .art-stage { aspect-ratio: 1; width: min(100%, 9.5rem); margin-inline: auto; }
.type-tile .art-stage > * { position: absolute; inset: 0; width: auto; height: auto; max-width: none; max-height: none; }
/* the water scene's own rimmed circle is its frame, round on purpose
   (DESIGN.md): no second, square frame round it */
.type-tile .art--water .art-stage { background: none; border-color: transparent; }
.type-tile .traits { margin-top: 0; justify-content: center; }
@media (min-width: 760px) { .type-set[data-count="2"] { grid-template-columns: repeat(2, 1fr); } }
.nine[data-groups="2"] .nine-groups { grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); }
.contrast { margin-left: 0; margin-right: 0; margin-bottom: 0; }
.contrast-line { margin: .5rem auto 0; max-width: 34ch; font: italic 600 clamp(1.5rem, 4.6vw, 2.1rem)/1.2 var(--display); text-wrap: balance; }
.together p + p { margin-top: .7rem; }
/* centred under its heading, as the "In one line" text above it: the
   paragraph is short now (25 words, 12 more for water), so one column */
.together { text-align: center; }

.finale-block { margin-top: 2.2rem; padding-top: 1.6rem; border-top: 1px solid var(--line); }
.block-title { margin: 0 0 1rem; font: 600 1.6rem/1.15 var(--display); }
.nine { margin: 0; }
.nine-groups { display: grid; gap: 1.6rem; }
@media (min-width: 760px) { .nine-groups { grid-template-columns: repeat(3, 1fr); gap: 1.8rem; } }
.nine-group { display: grid; gap: .8rem; align-content: start; text-align: left; }
.nine-head { margin: 0; display: flex; align-items: center; gap: .55rem; font-weight: 700; }
.swatch-key, .nine-head .swatch { position: static; width: 12px; height: 12px; border-radius: 3px; background: var(--hue); flex: none; }
.nine-head .swatch { box-shadow: inset 0 -30px 60px -20px rgba(20, 16, 40, .28), inset 0 1px 0 rgba(255, 255, 255, .6); }
.nine-note { margin-top: .9rem; font-size: .875rem; color: var(--ink-soft); }
.table-view, .all-types { margin-top: 1rem; }
.table-view summary, .all-types summary {
  display: inline-flex; align-items: center; gap: .4rem; min-height: 44px; cursor: pointer; font-weight: 600; color: var(--ink);
  text-decoration: underline; text-underline-offset: .22em;
}
.table-view table { width: 100%; border-collapse: collapse; font-size: .92rem; margin-top: .6rem; }
.table-view caption { text-align: left; color: var(--ink-soft); font-size: .875rem; padding-bottom: .4rem; }
.table-view th, .table-view td { text-align: left; padding: .45rem .5rem; border-bottom: 1px solid var(--line); }
.table-view .num { text-align: right; font-variant-numeric: tabular-nums; }

.standing-slot { min-height: 3rem; }
.standing-first { margin: 0; font: italic 600 1.5rem/1.2 var(--display); }
.standing-note { margin: .4rem 0 0; color: var(--ink-soft); }
.meters { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.1rem; }
@media (min-width: 760px) { .meters { grid-template-columns: repeat(3, 1fr); gap: 1.6rem; } }
.meter { display: grid; gap: .35rem; }
.meter-section { margin: 0; font-size: .875rem; color: var(--ink-soft); font-weight: 600; }
.meter-line { margin: 0; font-weight: 700; font-size: 1.05rem; line-height: 1.35; }
.meter-track { height: 8px; border-radius: 4px; background: var(--grid); overflow: hidden; }
.meter-fill { display: block; height: 100%; width: var(--w); border-radius: 0 4px 4px 0; background: var(--hue); }
.bars { margin: 1.2rem 0 0; }
.bars figcaption { font-weight: 700; margin-bottom: .5rem; }
.bar-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .15rem; }
.bar-row { display: grid; grid-template-columns: minmax(8rem, 13rem) 1fr 3rem; gap: .7rem; align-items: center; font-size: .92rem; min-height: 28px; }
.bar-row.is-you .bar-name { font-weight: 700; }
.bar-track { position: relative; height: 24px; display: flex; align-items: center; }
.bar-fill { display: block; height: 12px; width: var(--w); min-width: 2px; border-radius: 0 4px 4px 0; background: var(--muted-mark); }
.bar-row.is-you .bar-fill { background: var(--hue); }
.bar-value { text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 480px) { .bar-row { grid-template-columns: 1fr 2.6rem; } .bar-track { grid-column: 1 / -1; grid-row: 2; height: 16px; } }

.together p { margin: 0 auto; max-width: 52ch; font-size: 1.06rem; text-wrap: pretty; }
.summary { margin: 2.2rem 0 0; padding: 1.4rem 0 0; border-top: 1px solid var(--line); }
.summary blockquote { margin: .5rem 0 0; font: italic 500 clamp(1.35rem, 4vw, 1.75rem)/1.3 var(--display); }
.summary blockquote p { margin: 0; }
.summary blockquote p + p, .why-panel .meaning + .meaning { margin-top: .5rem; }
.finale > .ai-line { text-align: center; margin-top: 1.8rem; }
.standing-slot, .meters { text-align: left; }
.standing-first, .standing-note { text-align: center; }
.finale .btn--text { justify-self: center; margin-top: .6rem; }

/* --- the finale's close: the next player, the sign-up, more from David, support --- */
.another { text-align: center; }
.another-line { margin: 0 auto; max-width: 30rem; font: italic 600 clamp(1.35rem, 4.4vw, 1.7rem)/1.25 var(--display); text-wrap: balance; }
.another .actions { margin-top: 1rem; }
.another .btn--text { margin-top: 0; }
.another-note { margin: .5rem auto 0; max-width: 28rem; font-size: .875rem; color: var(--ink-soft); }
.signup-body { margin: .5rem 0 1rem; max-width: 36rem; color: var(--ink-soft); }
.signup-form { display: grid; gap: .8rem; max-width: 32rem; }
.signup-form .tick { margin-top: 0; font-size: 1rem; }
.signup-form .field-error { margin-top: -.3rem; }
.signup-form .btn--primary { justify-self: start; }
.signup-small { margin: 0; font-size: .875rem; color: var(--ink-soft); }
.signup-small a { color: inherit; text-underline-offset: 3px; }
.signup-status { margin: 0; font-weight: 600; color: var(--danger); }
.signup-status:empty { display: none; }
.signup-done { max-width: 32rem; padding: 1rem 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.signup-done p { margin: 0; }
.signup-done .signup-done-title { margin-bottom: .3rem; font: italic 600 1.45rem/1.2 var(--display); }
.more-links { list-style: none; margin: .7rem 0 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: .6rem; }
.more-link {
  display: inline-flex; align-items: center; min-height: 44px; padding: .5rem 1.1rem; border-radius: 999px;
  border: 1px solid var(--line); background: transparent; color: var(--ink); font-weight: 600; text-decoration: none;
  transition: border-color .2s ease;
}
.more-link:hover { border-color: var(--grid-strong); }
.support-line { margin: 2rem 0 0; text-align: center; font-size: .95rem; color: var(--ink-soft); }
/* the family game's finale: one quiet line that it is just for fun */
.just-for-fun { margin: 2rem auto 0; max-width: 30rem; text-align: center; font-size: .95rem; color: var(--ink-soft); }
/* the family finale's "Need help?": a plain line and two links, each a 44 px target */
.need-help { max-width: 34rem; margin-left: auto; margin-right: auto; text-align: center; }
.help-line, .help-danger { margin: .4rem 0; font-size: .95rem; color: var(--ink-soft); }
.help-links { list-style: none; margin: .4rem 0; padding: 0; display: grid; justify-items: center; gap: .6rem; }
/* each service: the link that calls or texts it, and its website beside it (design-dob M3) */
.help-item { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; column-gap: 1rem; }
.help-link { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; }
.help-site { display: inline-flex; align-items: center; min-height: 44px; font-size: .92rem; color: var(--ink-soft); text-underline-offset: 3px; }
/* on a phone each service's two links sit one above the other, the same for
   both; inside their 44 px targets the two words lean together, so each
   service reads as one pair (design-final nit, 26 October 2026) */
@media (max-width: 479px) {
  .help-item { flex-direction: column; }
  .help-item .help-link { align-items: flex-end; }
  .help-item .help-site { align-items: flex-start; }
}
.help-call { font-weight: 600; color: var(--ink); text-underline-offset: 3px; }
.support-line a { display: inline-flex; align-items: center; min-height: 44px; color: var(--ink); font-weight: 600; text-underline-offset: 3px; }

/* --- return --- */
.return { text-align: center; }
.return .lede-sm { margin-inline: auto; }
.return .actions--stack { max-width: 22rem; margin-inline: auto; }

/* --- turnstile --- */
/* Turnstile: out of sight while quiet (turnstile.js sets [hidden] and
   data-ts="quiet"); when a person is needed (data-ts="asking") it rises in
   under the card, centred, on a quiet hairline */
.ts-slot { display: flex; justify-content: center; margin-top: .8rem; }
.ts-slot:empty, .ts-slot[data-ts="quiet"] { display: none; }
.ts-slot[data-ts="asking"] {
  width: fit-content; max-width: 100%; margin-inline: auto; padding: .5rem; border-radius: 14px;
  border-top: 1px solid var(--line); animation: ts-in .5s var(--ease-light) both;
}
@keyframes ts-in { from { opacity: 0; transform: translateY(6px); } }

/* --- share dialog --- */
.share-dialog {
  width: min(100% - 1.5rem, 560px); max-height: min(92dvh, 900px); padding: 0; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--glass-solid); color: var(--ink); box-shadow: var(--shadow);
}
.share-dialog::backdrop { background: rgba(6, 7, 16, .62); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.dialog-inner { padding: clamp(1.2rem, 5vw, 2rem); display: grid; gap: 1rem; }
.dialog-title { margin: 0; font: italic 600 2rem/1.1 var(--display); }
.dialog-note { margin: 0; color: var(--ink-soft); }
.dialog-url { margin: 0; text-align: center; font-weight: 600; overflow-wrap: anywhere; }
.dialog-close { justify-self: center; }
.card-preview { margin: 0; display: grid; place-items: center; position: relative; min-height: 200px; }
/* the card's own dark ground is there from the first frame; the image
   stays hidden until its first card has loaded (share.js), so no broken
   image or alt text ever shows, and "Making your card" always sits on that
   dark ground, in a light ink of its own in both schemes */
.card-preview::before {
  content: ""; grid-area: 1 / 1; width: min(56vw, 240px); aspect-ratio: 9 / 16; border-radius: 16px;
  background: #0A0C16; box-shadow: 0 20px 50px -20px rgba(0,0,0,.6);
}
.card-preview img { grid-area: 1 / 1; width: min(56vw, 240px); height: auto; aspect-ratio: 9 / 16; border-radius: 16px; background: #0A0C16; }
.card-preview:not([data-loaded="yes"]) > img { visibility: hidden; }
.card-status { position: absolute; margin: 0; font-weight: 600; color: #ECEAF4; }
.card-preview[data-size="post"] img, .card-preview[data-size="post"]::before { aspect-ratio: 4 / 5; }
.sizes { margin: 0; padding: 0; border: 0; display: flex; gap: .5rem; justify-content: center; }
.seg { position: relative; display: inline-flex; }
.seg input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.seg-text {
  display: inline-flex; align-items: center; min-height: 44px; padding: .4rem 1.2rem; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: var(--glass); color: var(--ink); font-weight: 600;
}
.seg input:checked + .seg-text { background: var(--btn-bg); color: var(--btn-ink); border-color: transparent; }
.seg input:focus-visible + .seg-text { outline: 3px solid var(--focus); outline-offset: 3px; }
.share-hints { display: grid; gap: .3rem; text-align: center; }
.share-hint { margin: 0; font-size: .875rem; color: var(--ink-soft); }
.toggle { display: flex; align-items: center; gap: .8rem; min-height: 48px; cursor: pointer; font-weight: 600; }
.toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.toggle-ui { flex: none; width: 48px; height: 28px; border-radius: 999px; background: var(--grid); position: relative; transition: background .25s ease; }
.toggle-ui::after { content: ""; position: absolute; left: 3px; top: 3px; width: 22px; height: 22px; border-radius: 50%; background: var(--ink); transition: transform .3s var(--ease-spring); }
.toggle input:checked + .toggle-ui { background: var(--hue-animal); }
.toggle input:checked + .toggle-ui::after { transform: translateX(20px); background: #fff; }
.toggle input:focus-visible + .toggle-ui { outline: 3px solid var(--focus); outline-offset: 3px; }

/* --- delights --- */
.burst { position: fixed; left: var(--x); top: var(--y); width: 0; height: 0; z-index: 60; pointer-events: none; }
.burst-bit { position: absolute; left: 0; top: 0; width: 9px; height: 9px; border-radius: 50%; box-shadow: 0 0 10px 2px var(--beam-glow); }
.burst-bit--0 { background: var(--pearl-rose); }
.burst-bit--1 { background: var(--pearl-lilac); }
.burst-bit--2 { background: var(--pearl-sky); }
.burst-bit--3 { background: var(--pearl-mint); }

/* --- small screens --- */
@media (max-width: 420px) {
  .face { --pad-y: 1.15rem; --pad-x: 1.15rem; }
  .face { --radius: 22px; }
  .btn { padding-inline: 1.2rem; }
  .actions { gap: .5rem; }
  .point { padding-left: 2.3rem; }
}
@media (max-width: 899px) {
  body[data-screen="welcome"] .game { gap: .6rem; }
  body[data-screen="welcome"] .hero { gap: .6rem; }
}

/* --- from the playful take: the pearl's bloom and its motes (decoration only) --- */
.pearl-bloom { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.bloom-ring {
  position: absolute; inset: 0; border-radius: 50%; opacity: 0;
  border: 1.5px solid var(--ring, rgba(255, 255, 255, .6));
  box-shadow: 0 0 18px 2px var(--beam-glow);
}
@media (prefers-color-scheme: light) { .pearl { --ring: rgba(106, 63, 201, .45); } }
.pearl-motes { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.mote-orbit {
  --d: 9s; position: absolute; inset: -16%; border-radius: 50%; opacity: 0;
  animation: orbit var(--d) linear infinite; animation-play-state: var(--motion, running);
  transition: opacity .6s ease, inset .5s cubic-bezier(.5, 0, .75, 0);
}
.mote-orbit--0 { --d: 9s; --c: var(--pearl-rose); inset: -9%; }
.mote-orbit--1 { --d: 11s; --c: var(--pearl-lilac); inset: -15%; animation-delay: -3.4s; animation-direction: reverse; }
.mote-orbit--2 { --d: 10s; --c: var(--pearl-sky); inset: -12%; animation-delay: -6.1s; }
.mote-orbit--3 { --d: 12.5s; --c: var(--pearl-mint); inset: -19%; animation-delay: -1.3s; animation-direction: reverse; }
.mote {
  position: absolute; left: 50%; top: 0; width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%;
  background: radial-gradient(circle, #FFFFFF 0 32%, var(--c) 72%);
  box-shadow: 0 0 8px 2px var(--c), 0 0 18px 4px var(--beam-glow);
}
@keyframes orbit { to { transform: rotate(360deg); } }
.pearl-motes:is([data-count="1"], [data-count="2"], [data-count="3"], [data-count="4"]) .mote-orbit--0,
.pearl-motes:is([data-count="2"], [data-count="3"], [data-count="4"]) .mote-orbit--1,
.pearl-motes:is([data-count="3"], [data-count="4"]) .mote-orbit--2,
.pearl-motes[data-count="4"] .mote-orbit--3 { opacity: 1; }
.pearl-motes.is-gathering .mote-orbit { inset: 34% !important; opacity: 0 !important; transition: opacity .5s ease-in, inset .5s cubic-bezier(.5, 0, .75, 0); }
.brand-slot .pearl-motes { display: none; }
@media (prefers-reduced-motion: reduce) {
  .pearl-motes, .pearl-bloom { display: none !important; }
}

/* --- on a laptop or desktop, every screen fits inside the window (David,
   23 October 2026: "at least on web everything visible inside the
   viewport"): the page never scrolls; a long reveal section or the finale
   scrolls inside its own card. Phones may scroll as before. --- */
@media (min-width: 900px) and (min-height: 560px) {
  .page { height: 100vh; height: 100dvh; }
  .masthead { padding-top: max(.7rem, env(safe-area-inset-top)); padding-bottom: .2rem; }
  .game { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: .9rem; padding-block: clamp(.3rem, 2.5dvh, 1.6rem) .4rem; }
  .hero { flex: none; }
  .stage { flex: 0 1 auto; min-height: 0; display: flex; flex-direction: column; }
  .flipper { flex: 0 1 auto; min-height: 0; grid-template-rows: minmax(0, 1fr); }
  .face { min-height: 0; }
  .face > .screen { min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; }
  /* No visible scroll bar on a card that scrolls (David, 26 October 2026:
     "there is a scroll bar on the right, I think it should be hidden"): the
     wheel, touch and the keyboard still scroll it, and the "more below" fade
     and chevron stay the cue. scrollbar-width covers Firefox and Chrome 121
     on; the WebKit rule covers Safari and older Chrome. */
  .face > .screen::-webkit-scrollbar, .screen.has-foot > .screen-body::-webkit-scrollbar { display: none; width: 0; height: 0; }
  /* what the keyboard brings into view stops clear of the clip at the edge
     and of the fades, so its focus ring is never cut */
  .face > .screen, .screen.has-foot > .screen-body { scroll-padding-block: calc(var(--edge) + 1.75rem) 3.5rem; }
  .site-foot { flex: none; }
  /* the night from the window's top (its top is the page's) to well past its
     foot, which the page clips; the footer's band, measured from the top,
     kept clear */
  body:not([data-screen="welcome"]) .page { overflow: clip; }
  body:not([data-screen="welcome"]) .stage { --night-top: calc(-1 * (max(.7rem, env(safe-area-inset-top)) + 44px + .2rem + clamp(.3rem, 2.5dvh, 1.6rem))); --night-down: 100vh; --ko-y: calc(.2rem + clamp(.3rem, 2.5dvh, 1.6rem) + 22px); --foot-h: 72px; }
  body:not([data-screen="welcome"]) .stage { --foot-l: linear-gradient(to bottom, #000 calc(100vh - var(--foot-h) - 56px), transparent calc(100vh - var(--foot-h))); }
  body:not([data-screen="welcome"]) .night { --foot: var(--foot-l); }
  /* the pane's copy fades with the night toward the footer, so the points inside and outside the card end together */
  body:not([data-screen="welcome"]) .lens-sky { -webkit-mask-image: var(--foot-l); mask-image: var(--foot-l); }
  /* what scrolls is clipped at the glass's edge, its inner face, so a line on
     its way out of view never passes through the bent edge (direction 3's graft) */
  .face > .screen.has-foot > .screen-body { clip-path: inset(var(--edge) 0 0 0); }
  .face > .screen:not(.has-foot) { clip-path: inset(var(--edge) 0); }
  /* centred, but never pushed up past the window's top by a taller card (H1) */
  body[data-screen="welcome"] .game { display: grid; align-content: center; align-content: safe center; }
  body[data-screen="welcome"] .stage { display: block; }
  /* The welcome on a laptop window too short for it (design-dob H1: 1366 x
     657, 1280 x 600, 125 per cent zoom): the page grows with the card and
     scrolls, so the title, the card and the footer never overlap; where the
     window is tall enough it still fits and is centred. */
  body[data-screen="welcome"] .page { height: auto; min-height: 100vh; min-height: 100dvh; }
  body[data-screen="welcome"] .game { flex: 1 0 auto; }
  /* A card taller than the window (design review H3; David, 26 October
     2026: "a weird dark blue haze at the bottom when you scroll down"): no
     opaque band. A card with buttons at its foot is a body that scrolls and
     a foot that does not (flip.js); while there is more below, the body's
     last 3 rem fade out by a mask (it paints against the body's own box, so
     it does not scroll), with a small chevron, and the foot stands on the
     same glass with no fill of its own. The finale, whose buttons sit
     mid-way, fades at its own foot the same way, the fade its cue. */
  .face > .screen.has-foot { display: flex; flex-direction: column; overflow: hidden; padding-bottom: 0; }
  .screen.has-foot > .screen-body {
    display: block; flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none;
    margin: calc(-1 * var(--pad-y)) calc(-1 * var(--pad-x)) 0; padding: var(--pad-y) var(--pad-x) .4rem;
  }
  .screen.has-foot > :is(.actions, .read-consent, .kept) { flex: none; margin-top: 0; padding: .9rem 0 var(--pad-y); position: relative; }
  .screen-body[data-more="yes"], .finale[data-more="yes"] {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 3rem), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 3rem), transparent);
  }
  /* the finale, once it has scrolled, fades at its top too, over the few
     pixels between the edge's clip and its padding, to match its foot (a
     scroll-driven animation, where there is one; elsewhere the clip at the
     edge stands alone). At its top it fades nothing. */
  @supports (animation-timeline: scroll()) {
    .face > .finale {
      --top-mask: linear-gradient(to bottom, transparent var(--edge), #000 calc(var(--edge) + var(--top-fade)));
      -webkit-mask-image: var(--top-mask); mask-image: var(--top-mask);
      animation: top-fade linear both; animation-timeline: scroll(self); animation-range: 0px 32px;
    }
    .face > .finale[data-more="yes"] {
      -webkit-mask-image: var(--top-mask), linear-gradient(to bottom, #000 calc(100% - 3rem), transparent); -webkit-mask-composite: source-in;
      mask-image: var(--top-mask), linear-gradient(to bottom, #000 calc(100% - 3rem), transparent); mask-composite: intersect;
    }
  }
  .screen-body[data-more="yes"] + :is(.actions, .read-consent, .kept)::before {
    content: ""; position: absolute; left: 50%; top: -.55rem; width: 18px; height: 11px; transform: translateX(-50%);
    background: var(--chevron) center / 18px 11px no-repeat; pointer-events: none;
  }
}
/* a laptop's shorter window: the same card, a little tighter */
@media (min-width: 900px) and (max-height: 860px) {
  /* never less than the edge and 7 px, so a focus ring (3 px, 3 px out) at
     the top or foot of a card is never cut by the clip at the edge */
  .face { --pad-y: max(calc(var(--edge) + 7px), clamp(1.25rem, 3.4vh, 2.2rem)); --pad-x: clamp(1.25rem, 3vw, 2.4rem); }
  .q-label { font-size: clamp(1.8rem, 4.4vh, 2.5rem); }
  .explainer { margin: .5rem 0 .8rem; }
  textarea.answer { min-height: 7.5rem; }
  .actions { margin-top: 1.1rem; }
  .pips { margin-bottom: .7rem; }
  .lede { font-size: 1.06rem; }
}
@property --top-fade { syntax: '<length>'; inherits: false; initial-value: 0px; }
@keyframes top-fade { to { --top-fade: 20px; } }
