/* Path of Living · quiz screen. Adapted from prototype.html v2, the reference
   implementation (DECISIONS §8). Two rules below are MEASUREMENT GUARDS, not
   styling choices, recorded in DECISIONS §8 — do not polish them away:
   · the two option boxes are visually IDENTICAL (visual asymmetry between
     poles is a bias);
   · the four circles differ in visual size but their hit areas are equal and
     ≥44pt, or slight answers cost more effort than strong ones and
     extremeness inflates. */

* { box-sizing: border-box; margin: 0; touch-action: manipulation; }
html { -webkit-text-size-adjust: 100%; }
body { font-family: system-ui, sans-serif; background: #06090f; color: #cfd6e4;
       display: flex; justify-content: center;
       -webkit-tap-highlight-color: transparent; }

#frame { position: relative; width: 100%; max-width: 430px;
         min-height: 100vh; min-height: 100dvh; /* vh fallback for pre-dvh browsers */
         overflow: hidden; display: flex; flex-direction: column; gap: 12px;
         padding: calc(16px + env(safe-area-inset-top)) 18px
                  calc(60px + env(safe-area-inset-bottom));
         background:
           radial-gradient(ellipse 120% 45% at 50% 108%, rgba(24,48,34,.85), rgba(24,48,34,0) 70%),
           linear-gradient(#070c16 0%, #0a1322 55%, #0d1a27 100%); }
@media (min-width: 640px) {
  body { padding: 24px; align-items: flex-start; }
  #frame { min-height: 760px; border: 1px solid #2a3242; border-radius: 12px; }
}

/* fireflies — spawned in top/bottom margins only (quiz.js), so they never
   cross behind text being read (DECISIONS §8) */
.fly { position: absolute; width: 4px; height: 4px; border-radius: 50%; background: #d8f2a8;
       box-shadow: 0 0 8px 2px rgba(205,240,150,.55); opacity: 0; pointer-events: none;
       animation: drift 9s linear infinite; }
@keyframes drift {
  0% { opacity: 0; transform: translate(0,0); }
  12% { opacity: .85; } 45% { opacity: .25; } 60% { opacity: .7; }
  100% { opacity: 0; transform: translate(26px,-58px); } }

/* positioned + raised so fireflies (unpositioned band dwellers) can never
   drift across this text (DECISIONS §8) */
#banner { font-size: 12px; color: #9fb0c8; border-bottom: 1px solid rgba(255,255,255,.14);
          padding-bottom: 6px; position: relative; z-index: 2; }
#progressWrap { font-size: 11px; color: #7787a0; position: relative; z-index: 2; }
#bar { height: 3px; background: rgba(255,255,255,.12); margin-top: 3px; }
#bar div { height: 3px; background: #cde39a; width: 0; transition: width .25s; }

#screen { display: flex; flex-direction: column; flex: 1; z-index: 2; }

/* --qboxH / --optH are measured at runtime from the longest rendered item
   (measure.js), so box and circle positions never shift between items */
.qbox { background: rgba(13,20,34,.62); border: 1px solid rgba(255,255,255,.18);
        -webkit-backdrop-filter: blur(6px);
        backdrop-filter: blur(6px); border-radius: 10px; padding: 16px 15px; min-height: 118px;
        height: var(--qboxH, auto);
        font-size: 19px; line-height: 1.5; color: #e9edf5; margin-top: 6px; }
.blank { border-bottom: 1.5px solid #e9edf5; padding: 0 2px; min-width: 70px; display: inline-block; }
.blank.empty { border-bottom-color: rgba(233,237,245,.5); }

/* MEASUREMENT GUARD: one .opt rule serves both boxes — identical on purpose */
.opts { display: flex; gap: 10px; margin-top: 12px; align-items: stretch; }
.opt { flex: 1 1 0; background: rgba(13,20,34,.55); border: 1px solid rgba(255,255,255,.22);
       -webkit-backdrop-filter: blur(6px);
       backdrop-filter: blur(6px); border-radius: 10px; padding: 12px 11px;
       height: var(--optH, auto);
       font-size: 15px; line-height: 1.35; color: #dfe5f0; display: flex; align-items: center; }
.opt.armedSide { border-color: rgba(214,235,170,.75); }

/* MEASUREMENT GUARD: every .circle button is 64×64 (>44pt) regardless of the
   visual size of the inner disc */
.circles { display: flex; margin-top: 14px; }
.cwrap { flex: 1 1 0; display: flex; align-items: center; justify-content: center; }
.circle { width: 64px; height: 64px; background: none; border: none; cursor: pointer;
          display: flex; align-items: center; justify-content: center; padding: 0; }
.circle i { display: block; border-radius: 50%; border: 1.5px solid #b9c6dd;
            background: rgba(20,30,48,.5); }
.circle.big i { width: 52px; height: 52px; }
.circle.small i { width: 34px; height: 34px; }
.circle.armed i { border-color: #e6f3c0; background: rgba(160,200,110,.25);
                  box-shadow: 0 0 14px 3px rgba(205,240,150,.65);
                  animation: pulse 1.1s ease-in-out infinite; }
@keyframes pulse { 50% { box-shadow: 0 0 22px 6px rgba(205,240,150,.85); } }
#hint { min-height: 16px; font-size: 12px; color: #93a55f; text-align: center; margin-top: 4px; }

/* The orb is a small sun: breathing glow, slow-turning flare rays that fade in
   as it grows (--flare, set per answer), and a hue that warms green→gold via
   an inline hue-rotate. Continuous animation is decorative — off under
   reduced motion and .noanim. */
#orb { position: absolute; left: 50%; bottom: calc(12px + env(safe-area-inset-bottom));
       transform: translateX(-50%); border-radius: 50%;
       background: radial-gradient(circle at 38% 32%, #fdffe9, #e9f7b4 30%, #b9d97e 55%, #5c7a3a 82%);
       box-shadow: 0 0 14px 4px rgba(200,235,140,.5);
       transition: width .3s, height .3s, filter .6s;
       animation: orbBreathe 3.6s ease-in-out infinite; }
#orb::after { content: ''; position: absolute; inset: -60%; border-radius: 50%;
       background: repeating-conic-gradient(rgba(245,255,210,0) 0deg 11deg,
                   rgba(245,255,210,.4) 15deg, rgba(245,255,210,0) 19deg 30deg);
       -webkit-mask-image: radial-gradient(circle, transparent 32%, #000 48%, transparent 74%);
       mask-image: radial-gradient(circle, transparent 32%, #000 48%, transparent 74%);
       opacity: var(--flare, 0); animation: orbSpin 18s linear infinite; }
@keyframes orbSpin { to { transform: rotate(360deg); } }
@keyframes orbBreathe {
  0%, 100% { box-shadow: 0 0 14px 4px rgba(200,235,140,.45), 0 0 42px 12px rgba(200,235,140,.10); }
  50%      { box-shadow: 0 0 24px 8px rgba(225,250,175,.80), 0 0 64px 20px rgba(225,250,175,.22); } }
/* pop replaces breathe for one flash (an animation always beats a class's
   static box-shadow, so the flash must itself be an animation) */
#orb.pop { animation: orbPop .32s ease-out; }
@keyframes orbPop {
  0%   { transform: translateX(-50%) scale(1.45);
         box-shadow: 0 0 40px 14px rgba(235,255,190,.95), 0 0 110px 34px rgba(235,255,190,.40); }
  100% { transform: translateX(-50%) scale(1);
         box-shadow: 0 0 14px 4px rgba(200,235,140,.45), 0 0 42px 12px rgba(200,235,140,.10); } }

/* alive sun candidate (?orb=alive, under review): canvas particle swarm —
   the container keeps all transform choreography, the canvas only paints */
#orb.alive { background: none; box-shadow: none; animation: none; }
#orb.alive::before, #orb.alive::after { display: none; }
/* 280px matches CANVAS_PX in orb-alive.js — the atmospheric bloom needs the
   headroom or it clips at the canvas edge into a visible square */
.orbCanvas { position: absolute; left: 50%; top: 50%; width: 280px; height: 280px;
  transform: translate(-50%, -50%); pointer-events: none; }
/* In alive mode the spirit keeps only the glide: spiritBreath's box-shadow
   ring wraps the old 51px body and reads as a hard-edged halo against the
   canvas swarm (user, 2026-08-18) — the swarm's own breathing carries the
   centered moment instead. More specific than #orb.spiritCenter, so it wins;
   the later .spiritDissolve rule still takes over on tap. */
#orb.alive.spiritCenter {
  animation: spiritGlide 1.1s cubic-bezier(0.3, 0, 0.15, 1) forwards; }

/* rich sun candidate (?orb=rich, under review): limb-darkened layered core,
   living granulation grain, soft two-lobe corona instead of hard conic rays.
   Authored in the same green→gold family — the §9 hue-rotate arc and growth
   mechanics are untouched. If ruled in, §9 gets the amendment. */
.orbRich #orb {
  background:
    radial-gradient(circle at 36% 30%, rgba(255,255,248,.95), rgba(255,255,248,0) 30%),
    radial-gradient(circle at 50% 46%,
      #fdffee 0%, #f2fbc8 20%, #dcee9e 40%, #b8d472 62%,
      #8fae4e 80%, #647d33 93%, rgba(58,74,30,.92) 100%);
  animation: orbBreatheRich 3.6s ease-in-out infinite;
}
@keyframes orbBreatheRich {
  0%, 100% { box-shadow: 0 0 8px 2px rgba(250,255,225,.6),
             0 0 22px 6px rgba(216,238,150,.35), 0 0 54px 18px rgba(190,220,120,.12); }
  50%      { box-shadow: 0 0 12px 3px rgba(252,255,235,.85),
             0 0 34px 10px rgba(224,244,165,.55), 0 0 80px 26px rgba(200,228,135,.22); } }
.orbRich #orb::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%; overflow: hidden;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120"><filter id="n"><feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2"/></filter><rect width="120" height="120" filter="url(%23n)" opacity="0.85"/></svg>');
  background-size: 220% 220%;
  mix-blend-mode: overlay; opacity: .55;
  animation: grainDrift 16s ease-in-out infinite alternate;
}
@keyframes grainDrift {
  from { transform: rotate(0deg) scale(1); }
  to   { transform: rotate(150deg) scale(1.22); } }
.orbRich #orb::after {
  inset: -80%;
  background:
    radial-gradient(ellipse 58% 100% at 50% 50%, rgba(238,250,195,.32), rgba(238,250,195,0) 62%),
    radial-gradient(ellipse 100% 54% at 50% 50%, rgba(238,250,195,.28), rgba(238,250,195,0) 62%);
  -webkit-mask-image: none; mask-image: none;
  opacity: var(--flare, 0);
  animation: coronaTurn 26s linear infinite;
}
@keyframes coronaTurn { to { transform: rotate(360deg); } }
.orbRich #orb.pop { animation: orbPopRich .32s ease-out; }
@keyframes orbPopRich {
  0%   { transform: translateX(-50%) scale(1.45);
         box-shadow: 0 0 26px 8px rgba(252,255,235,.95), 0 0 70px 24px rgba(224,244,165,.5); }
  100% { transform: translateX(-50%) scale(1);
         box-shadow: 0 0 8px 2px rgba(250,255,225,.6), 0 0 54px 18px rgba(190,220,120,.12); } }

@media (prefers-reduced-motion: reduce) {
  #orb, #orb::before, #orb::after, .fly, .circle.armed i, .cell.hot.settle { animation: none; }
  .axisSheet { transition: none; }
  .navrow button, .wide { transition: none; }
  .navrow button:active, .wide:active { transform: none; }
}
.noanim #orb, .noanim #orb::before, .noanim #orb::after, .noanim .fly,
.noanim .circle.armed i, .noanim .cell.hot.settle { animation: none; }

.navrow { display: flex; justify-content: space-between; align-items: center;
  margin-top: auto; padding-top: 12px; gap: 10px; }
/* Buttons, meadow register (user direction 2026-08-19): quiet glass for the
   sideways steps (Back, Start over, Nothing right now), a firefly-lit
   .primary for the forward action (Start, Continue, Finish). Single-action
   buttons, not measurement surfaces — but hit height still ≥44px. */
.navrow button, .wide { font-size: 15px; padding: 11px 18px; min-height: 44px;
      background: rgba(13,20,34,.55); color: #c2ccdb; cursor: pointer;
      border: 1px solid rgba(255,255,255,.16); border-radius: 11px;
      -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
      transition: border-color .25s ease, color .25s ease, box-shadow .25s ease,
                  transform .12s ease; }
.navrow button:active, .wide:active { transform: scale(.97); }
.navrow button:disabled { opacity: .4; cursor: default; }
.navrow button:focus-visible, .wide:focus-visible {
      outline: 1.5px solid rgba(214,235,170,.8); outline-offset: 2px; }
button.primary {
      background: linear-gradient(180deg, rgba(158,196,108,.20), rgba(158,196,108,.06)),
                  rgba(13,20,34,.55);
      border-color: rgba(214,235,170,.5); color: #edf5da;
      box-shadow: 0 0 14px rgba(205,240,150,.14), inset 0 0 8px rgba(205,240,150,.06); }
button.primary:active {
      box-shadow: 0 0 22px rgba(205,240,150,.3), inset 0 0 10px rgba(205,240,150,.1); }
@media (hover: hover) {
  .navrow button:hover:not(:disabled), .wide:hover { border-color: rgba(255,255,255,.3); color: #e2e9f3; }
  button.primary:hover:not(:disabled) { border-color: rgba(214,235,170,.7); color: #f4f9e8;
      box-shadow: 0 0 20px rgba(205,240,150,.24), inset 0 0 9px rgba(205,240,150,.08); }
}
.noanim .navrow button, .noanim .wide { transition: none; }
.noanim .navrow button:active, .noanim .wide:active { transform: none; }
.wide { width: 100%; margin-top: 12px; text-align: left; font-size: 16px; padding: 13px 16px; }
input[type=text] { font-size: 17px; padding: 11px; border: 1px solid rgba(255,255,255,.3);
      background: rgba(13,20,34,.6); color: #e9edf5; width: 100%; border-radius: 8px; }
.err { color: #e8a0a0; font-size: 13px; min-height: 16px; }
.help { font-size: 13px; color: #93a0b5; line-height: 1.4; }
h2 { font-size: 17px; font-weight: 600; color: #e9edf5; }
.soft { color: #8a97ad; font-size: 13px; line-height: 1.45; }

/* ------------------------------------------------------------------ result
   Seven paged frames (docs/design/frame-placement.html), scroll-snap paging.
   Pages are transparent so the meadow and the full-grown sun stay visible. */
/* The pager is a standing stage (DECISIONS §17, the user's sketch intent):
   the viewport never scrolls — frames stack in place, one visible; a scroll
   gesture EVENTS the swap: exiting content leaves, entering content arrives,
   in the same beat. `safe center` + per-frame overflow still guard font
   scaling and zoom: an overflowing frame scrolls internally and pages only
   from its boundary. */
#screen.paged { position: absolute; inset: 0; z-index: 2; display: block;
  overflow: hidden; overscroll-behavior: contain; }
.rframe { position: absolute; inset: 0;
  display: flex; flex-direction: column; gap: 14px;
  justify-content: center; justify-content: safe center; overflow-y: auto;
  opacity: 0; visibility: hidden; pointer-events: none;
  padding: calc(22px + env(safe-area-inset-top)) 20px
           calc(64px + env(safe-area-inset-bottom)); }
.rframe.active { opacity: 1; visibility: visible; pointer-events: auto; }
/* exit and enter run CONCURRENTLY (both frames hold .active for the turn's
   duration) — the crossfade is the whole point, a gap of bare background
   between frames is a defect (user, 2026-08-18). Enter paints above exit. */
.rframe.active.exitUp { animation: frameExitUp .38s cubic-bezier(0.4, 0, 0.7, 1) forwards; }
.rframe.active.exitDown { animation: frameExitDown .38s cubic-bezier(0.4, 0, 0.7, 1) forwards; }
@keyframes frameExitUp { to { opacity: 0; transform: translateY(-18px); } }
@keyframes frameExitDown { to { opacity: 0; transform: translateY(18px); } }
.rframe.active.enterFromBelow { animation: frameEnterBelow .5s cubic-bezier(0.2, 0, 0.2, 1) both; }
.rframe.active.enterFromAbove { animation: frameEnterAbove .5s cubic-bezier(0.2, 0, 0.2, 1) both; }
.rframe.enterFromBelow, .rframe.enterFromAbove { z-index: 1; }
@keyframes frameEnterBelow { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
@keyframes frameEnterAbove { from { opacity: 0; transform: translateY(-22px); } to { opacity: 1; transform: none; } }
.noanim .rframe.exitUp, .noanim .rframe.exitDown,
.noanim .rframe.enterFromBelow, .noanim .rframe.enterFromAbove { animation: none; }
@media (prefers-reduced-motion: reduce) {
  .rframe.exitUp, .rframe.exitDown,
  .rframe.enterFromBelow, .rframe.enterFromAbove { animation: none; }
}
#rf1 { justify-content: flex-start; }
#rf1 .reveal { margin: auto 0; }
.wordmark { font-size: 11px; letter-spacing: .18em; color: #6d7a92; }

/* reveal per sketch p12: bubbled THE, coordinate boxed on a contrasting
   color base ("box is part of design"), preposition line aligned under */
.revYou { font-size: 16px; color: #9aa8bf; }
.revThe { font-size: 30px; font-weight: 800; letter-spacing: .1em; margin-top: 10px;
  color: transparent; -webkit-text-stroke: 1.5px #e9edf5; }
@supports not (-webkit-text-stroke: 1px #fff) { .revThe { color: #e9edf5; } }
.coordBox { display: inline-block; margin-top: 8px; padding: 10px 16px;
  background: #cde39a; color: #0a1322; border-radius: 12px;
  font-size: 27px; font-weight: 700; letter-spacing: .03em;
  box-shadow: 0 0 24px rgba(205,227,154,.25); }
.revState { font-size: 18px; color: #cfd6e4; margin-top: 12px; }
.revState b { color: #cde39a; font-weight: 600; }
.reveal.small .revThe { font-size: 20px; -webkit-text-stroke-width: 1px; }
.reveal.small .coordBox { font-size: 19px; padding: 7px 12px; border-radius: 9px; }
.reveal.small .revState { font-size: 14px; margin-top: 8px; }
.swipeCue { text-align: center; color: #4a566c; font-size: 17px; }

/* recap header on the frames after the breakdowns (sketch p13) */
.recap { position: absolute; top: calc(18px + env(safe-area-inset-top)); right: 20px;
  text-align: right; font-size: 12px; line-height: 1.5; color: #9aa8bf; }
.rlabel { font-size: 11px; letter-spacing: .14em; color: #6d7a92; margin-bottom: 4px; }

.axisRow { display: block; width: 100%; background: none; border: none; padding: 8px 0;
  cursor: pointer; text-align: left; color: inherit; font: inherit; }
.lbl { display: flex; justify-content: space-between; align-items: center;
  font-size: 13px; color: #9aa8bf; }
/* sketch p12: the user's pole boxed, the unselected one faded */
.winChip { border: 1.5px solid rgba(205,227,154,.8); background: rgba(205,227,154,.12);
  border-radius: 6px; padding: 3px 8px; color: #e6f3c0; }
.loseLbl { opacity: .45; }
.bar { display: block; height: 10px; background: rgba(255,255,255,.1); border-radius: 5px;
  margin-top: 7px; overflow: hidden; }
.bar span { display: block; height: 10px; border-radius: 5px;
  background: linear-gradient(90deg, #9fbf6a, #cde39a); }

.diamond { width: 150px; height: 150px; margin: 34px auto; transform: rotate(45deg);
  display: grid; grid-template: 1fr 1fr / 1fr 1fr; gap: 5px; }
.cell { border: 1px solid rgba(255,255,255,.22); border-radius: 5px; }
.cell.hot { border: 3px solid #cde39a; box-shadow: 0 0 12px rgba(205,240,150,.4); }
/* slot-machine-like landing when the diamond scrolls into view (sketch p13) */
.cell.hot.settle { animation: cellSettle .9s cubic-bezier(.2, 1.4, .4, 1); }
@keyframes cellSettle {
  0%   { transform: scale(.55); box-shadow: 0 0 34px rgba(205,240,150,.95); }
  55%  { transform: scale(1.14); }
  100% { transform: scale(1); box-shadow: 0 0 12px rgba(205,240,150,.4); } }
.mini { font-size: 12px; color: #9aa8bf; text-align: center; }

.panel { background: rgba(13,20,34,.62); border: 1px solid rgba(255,255,255,.16);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px); border-radius: 10px; padding: 16px 15px; }
/* speech bubble (sketch p13); the silhouette + background scene arrive with
   the 22-asset set */
.voicePanel { position: relative; border-radius: 14px; }
.voicePanel::after { content: ''; position: absolute; left: 34px; bottom: -11px;
  width: 0; height: 0; border-left: 11px solid transparent;
  border-right: 5px solid transparent;
  border-top: 12px solid rgba(255,255,255,.16); }
.voicePanel::before { content: ''; position: absolute; left: 35px; bottom: -9px;
  width: 0; height: 0; border-left: 10px solid transparent;
  border-right: 4px solid transparent;
  border-top: 11px solid rgba(13,20,34,.95); z-index: 1; }
.voice { font-size: 15px; line-height: 1.6; color: #dbe2ee; font-style: italic; }
.voice p + p { margin-top: 10px; }
.rbody { font-size: 15.5px; line-height: 1.6; color: #e6ebf4; }
.contrast { border-style: dashed; border-color: rgba(255,255,255,.28); }
.x1 { font-size: 15px; color: #d8e6a9; line-height: 1.5; }
.contrastLine { font-size: 13.5px; color: #9aa8bf; line-height: 1.5; margin-top: 10px; }

.shareFrame { text-align: center; }
.shareRow { display: flex; justify-content: center; margin-top: 8px; }
.shareRow button { display: inline-flex; align-items: center; justify-content: center;
  padding: 12px 24px; background: rgba(13,20,34,.6); border: 1px solid rgba(255,255,255,.3);
  color: #dfe5f0; border-radius: 8px; cursor: pointer; }
.shareNote { min-height: 16px; font-size: 12px; color: #93a55f; }
.methodLink { margin-top: auto; margin-bottom: 16px; align-self: center; font-size: 12px;
  color: #7787a0; text-decoration: none; border-bottom: 1px solid rgba(119,135,160,.4);
  padding-bottom: 1px; }

/* axis detail: a separate full page sliding in from the right (sketch p12's
   right-swipe transition; user 2026-08-10: "not pop-up, separate pages") */
.axisSheet { position: absolute; inset: 0; z-index: 6; overflow-y: auto;
  padding: calc(22px + env(safe-area-inset-top)) 20px
           calc(40px + env(safe-area-inset-bottom));
  background:
    radial-gradient(ellipse 120% 45% at 50% 108%, rgba(24,48,34,.9), rgba(24,48,34,0) 70%),
    linear-gradient(#070c16 0%, #0a1322 55%, #0d1a27 100%);
  transform: translateX(102%); transition: transform .3s ease-out; }
.axisSheet.open { transform: none; }
.noanim .axisSheet { transition: none; }
.sheetBack { background: none; border: none; color: #9aa8bf; font: inherit;
  font-size: 14px; cursor: pointer; padding: 0 0 14px; text-align: left; display: block; }
.axisIntro { font-size: 14px; color: #c3ccdb; margin: 6px 0 12px; line-height: 1.5; }
.bandLine { font-size: 13.5px; color: #d8e6a9; line-height: 1.5; margin: 12px 0; }
.polePara { font-size: 14.5px; color: #c3ccdb; line-height: 1.55; margin-top: 12px; }
.polePara b { color: #e6ebf4; }
.polePara.faded { opacity: .5; }

/* ------------------------------------------------- question→result transition
   Review candidates behind ?transition=arrival|quiet (task #2). Compositor
   properties only. All of it dead when .noanim or reduced motion. */
.trOut { opacity: 0; transition: opacity .4s ease-in; }
.trIn { animation: trFadeUp .7s cubic-bezier(0.2, 0, 0.2, 1) both; }
@keyframes trFadeUp {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; } }

/* arrival: the grown sun rises ~2.6x and the meadow briefly holds light */
#orb.trRise { animation: orbUp 1.5s cubic-bezier(0.3, 0, 0.2, 1) forwards; }
@keyframes orbUp {
  to { transform: translateX(-50%) translateY(-46vh) scale(2.6);
       filter: hue-rotate(-50deg) saturate(1.5) brightness(1.25); } }
#orb.trSet { animation: orbDown .9s ease-in-out both; }
@keyframes orbDown {
  from { transform: translateX(-50%) translateY(-46vh) scale(2.6); }
  to   { transform: translateX(-50%); } }
.lightWash { position: absolute; inset: 0; z-index: 3; pointer-events: none;
  opacity: 0; transition: opacity 1.2s ease-out;
  background: radial-gradient(ellipse 150% 90% at 50% 88%,
    rgba(240,250,200,.38), rgba(233,247,180,.14) 45%, rgba(233,247,180,0) 75%); }
.lightWash.on { opacity: 1; }

/* spirit: the sun glides to center and breathes until TAPPED; the tap
   gathers it, blooms it away, one mote rises, a mild flash, then the name.
   The breath doubles as the tap affordance — no copy without a ruling. */
/* while armed as a button (transition.js sets role/tabindex), keyboard
   focus must be visible — a soft ring offset outside the glow */
#orb.spiritCenter:focus-visible { outline: 1.5px solid rgba(214,235,170,.7);
  outline-offset: 14px; border-radius: 50%; }
#orb.spiritCenter { z-index: 6; cursor: pointer; pointer-events: auto;
  animation: spiritGlide 1.1s cubic-bezier(0.3, 0, 0.15, 1) forwards,
             spiritBreath 1.9s ease-in-out 1.15s infinite; }
@keyframes spiritGlide {
  to { transform: translateX(-50%) translateY(-46dvh) scale(1.9); } }
@keyframes spiritBreath {
  0%, 100% { box-shadow: 0 0 18px 5px rgba(240,252,205,.55),
             0 0 48px 16px rgba(215,240,160,.22); }
  50%      { box-shadow: 0 0 30px 9px rgba(248,255,225,.9),
             0 0 84px 30px rgba(225,246,175,.4); } }
#orb.spiritCenter.spiritDissolve { pointer-events: none;
  animation: spiritBloom .8s cubic-bezier(0.45, 0, 0.85, 0.6) forwards; }
@keyframes spiritBloom {
  0%   { transform: translateX(-50%) translateY(-46dvh) scale(1.9);
         opacity: 1; filter: brightness(1); }
  32%  { transform: translateX(-50%) translateY(-46dvh) scale(1.5);
         opacity: 1; filter: brightness(1.7); }   /* the gather: an inhale */
  100% { transform: translateX(-50%) translateY(-46dvh) scale(3.6);
         opacity: 0; filter: brightness(2.4); } }
.spiritMote { position: absolute; left: 50%; top: calc(100% - 46dvh - 40px);
  width: 8px; height: 8px; border-radius: 50%; background: #fdffee;
  box-shadow: 0 0 14px 5px rgba(240,255,210,.9); opacity: 0; z-index: 6;
  pointer-events: none; transform: translateX(-50%);
  animation: moteRise 1.5s cubic-bezier(0.3, 0, 0.4, 1) .3s forwards; }
@keyframes moteRise {
  0%   { opacity: 0; transform: translateX(-50%) translateY(0) scale(.6); }
  18%  { opacity: 1; }
  100% { opacity: 0; transform: translateX(calc(-50% + 12px)) translateY(-24dvh) scale(1.1); } }
.spiritFlash { position: absolute; inset: 0; z-index: 5; pointer-events: none;
  opacity: 0; transition: opacity .5s ease-out;
  background: radial-gradient(ellipse 120% 90% at 50% 52%,
    rgba(250,255,235,.5), rgba(240,250,200,.18) 55%, transparent 80%); }
.spiritFlash.on { opacity: .8; transition: opacity .25s ease-in; }

/* quiet: everything settles, one dark beat, the name arrives in stages */
.trDim .fly { opacity: 0 !important; transition: opacity .8s ease-in; }
.trDim #orb { transition: filter .8s ease-in, width .3s, height .3s;
  filter: brightness(.45) saturate(.6) !important; }
.trDim { transition: none; }
.quietIn { opacity: 1 !important; }
.quietIn #rf1 > * { opacity: 0; animation: trFadeUp .5s cubic-bezier(0.2, 0, 0.2, 1) forwards; }
.quietIn #rf1 .wordmark { animation-delay: 1.5s; }
.quietIn #rf1 .reveal .revYou { opacity: 0; animation: trFadeUp .45s cubic-bezier(0.2, 0, 0.2, 1) .15s forwards; }
.quietIn #rf1 .reveal .revThe { opacity: 0; animation: trFadeUp .45s cubic-bezier(0.2, 0, 0.2, 1) .45s forwards; }
.quietIn #rf1 .reveal > div:nth-child(3) { opacity: 0; animation: trFadeUp .5s cubic-bezier(0.2, 0, 0.2, 1) .8s forwards; }
.quietIn #rf1 .reveal .revState { opacity: 0; animation: trFadeUp .45s cubic-bezier(0.2, 0, 0.2, 1) 1.2s forwards; }
.quietIn #rf1 .reveal { opacity: 1; animation: none; }
.quietIn #rf1 .swipeCue { animation-delay: 1.7s; }
.noanim .trOut, .noanim .trIn, .noanim .lightWash { transition: none; animation: none; opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .trOut, .trIn, .lightWash, #orb.trRise, #orb.trSet,
  #orb.spiritCenter, #orb.spiritDissolve, .spiritMote, .spiritFlash,
  .quietIn #rf1 > *, .quietIn #rf1 .reveal > * { transition: none; animation: none; opacity: 1; }
}

/* answer-absorption mote (mote-flight.js, DECISIONS §18): rises from the
   confirmed circle into the orb. Fixed overlay, zero layout, zero hit area —
   decorative by measurement-guard rule. Driven entirely by WAAPI; no CSS
   animation to guard for reduced motion (the JS gates on config.anim). */
.flyMote { position: fixed; left: 0; top: 0; width: 16px; height: 16px;
  margin: -8px 0 0 -8px; border-radius: 50%; pointer-events: none;
  z-index: 7; will-change: transform, opacity; }

/* letter-scatter (scatter.js). Timing vars --outT/--inT are set from the
   runtime speed option; the speed itself is undecided (DECISIONS §8). */
.ch { display: inline-block; }
.word { display: inline-block; }
.anim-out .ch { animation: chOut var(--outT, .18s) forwards; }
.anim-in  .ch { opacity: 0; animation: chIn var(--inT, .22s) forwards; }
@keyframes chOut { to { opacity: 0; transform: translateY(-12px); } }
@keyframes chIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.noanim .ch, .noanim .anim-out .ch, .noanim .anim-in .ch { animation: none; opacity: 1; transform: none; }

/* Confirm exit / entry for the screen CHROME (user, 2026-08-18): the option
   boxes, circles, nav, hint, and the blank's underline fade in the same
   window the letters scatter, so the armed glow decays away instead of
   popping at the re-render. Purely opacity — geometry, hit areas, and timing
   of the advance are untouched (measurement guard, DECISIONS §8).
   The blank: EXIT fades any .blank (it is filled by confirm time); ENTRY
   fades only .blank.empty — arming swaps in a filled .blank without .empty,
   which must keep appearing INSTANTLY (arm feedback is interaction, not
   decoration). */
#screen.exiting .opt, #screen.exiting .circles, #screen.exiting .navrow,
#screen.exiting #hint, #screen.exiting .blank {
  animation: chromeOut calc(var(--outT, .18s) * 1.5) ease forwards; }
#screen.entering .opt, #screen.entering .circles, #screen.entering .navrow,
#screen.entering #hint, #screen.entering .blank.empty {
  animation: chromeIn calc(var(--inT, .22s) * 1.3) ease both; }
@keyframes chromeOut { to { opacity: 0; } }
@keyframes chromeIn { from { opacity: 0; } }
.noanim #screen.exiting .opt, .noanim #screen.exiting .circles,
.noanim #screen.exiting .navrow, .noanim #screen.exiting #hint,
.noanim #screen.exiting .blank,
.noanim #screen.entering .opt, .noanim #screen.entering .circles,
.noanim #screen.entering .navrow, .noanim #screen.entering #hint,
.noanim #screen.entering .blank.empty { animation: none; }
@media (prefers-reduced-motion: reduce) {
  #screen.exiting .opt, #screen.exiting .circles, #screen.exiting .navrow,
  #screen.exiting #hint, #screen.exiting .blank,
  #screen.entering .opt, #screen.entering .circles,
  #screen.entering .navrow, #screen.entering #hint,
  #screen.entering .blank.empty { animation: none; }
}
