/* ================= eyeris — At-Home Eye Check ================= */

:root {
  --bg: #f7f5f0;          /* warm cream */
  --bg-soft: #eeeae1;
  --surface: #ffffff;
  --ink: #141416;
  --ink-soft: #46443f;    /* darkened for contrast */
  --ink-faint: #63605a;   /* 6.2:1 on white, 5.3:1 on --bg-soft — clears AA
                             everywhere it's used, including pill text */
  --accent: #141416;
  --accent-contrast: #ffffff;
  --accent-warm: #a34a17;
  --violet: #712eed;    /* brand purple */
  --green: #2e7d4f;
  --amber: #a37209;
  --line: rgba(20, 20, 22, 0.1);
  --radius-lg: 30px;
  --radius-md: 20px;
  --radius-sm: 13px;
  --tile: #ffffff;        /* featured tiles: plain white here, pure black in dark
                              mode. No tint, no inversion — the prism sheen is the
                              only colour on them, so it does all the talking. */
  --tile-ink: #141416;
  --tile-soft: #55534e;
  --tile-border: rgba(20, 20, 22, 0.10);
  --tile-border-hover: rgba(20, 20, 22, 0.20);
  --shadow-card: 0 1px 2px rgba(29,29,31,.04), 0 12px 40px rgba(29,29,31,.06);
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
          "Segoe UI", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
  /* the long, soft "ease-out-expo" curve behind Apple's product-page hovers —
     quick to start, very slow to settle, so nothing ever feels snappy or cheap */
  --ease: cubic-bezier(.16, 1, .3, 1);
  --logo-mask: url("data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'><path%20fill-rule='evenodd'%20d='M247.31%20124.76C246.96%20123.97%20238.49%20105.18%20219.66%2086.35C194.57%2061.26%20162.88%2048%20128%2048C93.1199%2048%2061.4299%2061.26%2036.3399%2086.35C17.5099%20105.18%208.9999%20124%208.6899%20124.76C8.23504%20125.783%208%20126.89%208%20128.01C8%20129.13%208.23504%20130.237%208.6899%20131.26C9.0399%20132.05%2017.5099%20150.83%2036.3399%20169.66C61.4299%20194.74%2093.1199%20208%20128%20208C162.88%20208%20194.57%20194.74%20219.66%20169.66C238.49%20150.83%20246.96%20132.05%20247.31%20131.26C247.765%20130.237%20248%20129.13%20248%20128.01C248%20126.89%20247.765%20125.783%20247.31%20124.76ZM128%20168C120.089%20168%20112.355%20165.654%20105.777%20161.259C99.1991%20156.864%2094.0722%20150.616%2091.0447%20143.307C88.0172%20135.998%2087.2251%20127.956%2088.7685%20120.196C90.3119%20112.437%2094.1215%20105.31%2099.7156%2099.7157C105.31%2094.1216%20112.437%2090.312%20120.196%2088.7686C127.956%2087.2252%20135.998%2088.0173%20143.307%2091.0448C150.616%2094.0723%20156.863%2099.1992%20161.259%20105.777C165.654%20112.355%20168%20120.089%20168%20128C168%20138.609%20163.786%20148.783%20156.284%20156.284C148.783%20163.786%20138.609%20168%20128%20168Z%20M240%20128C240.025%20131.281%20239.028%20134.489%20237.146%20137.177C235.264%20139.866%20232.591%20141.901%20229.5%20143L166.06%20166.07L143%20229.5C141.863%20232.562%20139.817%20235.203%20137.135%20237.068C134.454%20238.933%20131.266%20239.932%20128%20239.932C124.733%20239.932%20121.546%20238.933%20118.864%20237.068C116.183%20235.203%20114.136%20232.562%20113%20229.5L89.9396%20166.06L26.4996%20143C23.4376%20141.864%2020.7967%20139.817%2018.9319%20137.136C17.067%20134.454%2016.0674%20131.266%2016.0674%20128C16.0674%20124.734%2017.067%20121.546%2018.9319%20118.865C20.7967%20116.183%2023.4376%20114.137%2026.4996%20113L89.9396%2089.9401L113%2026.5001C114.136%2023.4381%20116.183%2020.7972%20118.864%2018.9323C121.546%2017.0675%20124.733%2016.0679%20128%2016.0679C131.266%2016.0679%20134.454%2017.0675%20137.135%2018.9323C139.817%2020.7972%20141.863%2023.4381%20143%2026.5001L166.07%2089.9401L229.5%20113C232.591%20114.099%20235.264%20116.135%20237.146%20118.823C239.028%20121.511%20240.025%20124.719%20240%20128Z'%20fill='%23000'/></svg>");
}

:root[data-theme="dark"] {
  --bg: #000000;          /* full black — go big on contrast */
  --bg-soft: #1c1c1f;
  --surface: #161618;
  --ink: #ffffff;
  --ink-soft: #d7d3ca;
  --ink-faint: #94908a;
  --accent: #ffffff;
  --accent-contrast: #000000;
  --accent-warm: #e79a63;
  --violet: #a67cf4;      /* lightened from the brand #712eed: that value is
                             only ~2.9:1 on the dark card surface — this
                             keeps the same hue and clears AA (~5.9:1) */
  --green: #7ecda0;
  --amber: #e6b64c;
  --line: rgba(255, 255, 255, 0.14);
  --shadow-card: 0 1px 2px rgba(0,0,0,.4), 0 12px 40px rgba(0,0,0,.5);
  --tile: #000000;        /* pure black — the tile reads as a hole in the page
                              that the prism lights up. Only the hairline border
                              gives it an edge. */
  --tile-ink: #ffffff;
  --tile-soft: #9b978f;
  --tile-border: rgba(255, 255, 255, 0.12);
  --tile-border-hover: rgba(255, 255, 255, 0.26);
}

/* Fraunces (Undercase Type, OFL licence) — self-hosted so the brand font
   never pings a third-party CDN, keeping the CSP locked to 'self' and the
   no-tracking promise intact. Wordmark only, so the latin 600 subset (35KB)
   is all we ship. */
@font-face {
  font-family: "Fraunces";
  src: url("fonts/fraunces-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  transition: background .35s ease, color .35s ease;
  /* the home carousel bleeds to 100vw, which can overshoot by a scrollbar's
     width on OSes with persistent scrollbars — never show a page-level
     horizontal scrollbar because of it */
  overflow-x: hidden;
}

/* ---------- Top bar ---------- */

/* Logo dead centre — the masthead's only occupant now that the score moved
   into the hero, so it can finally sit where it belongs. Controls live in
   the floating dock (bottom-right) and Exit is fixed top-right, so nothing
   competes with it at any width. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px 24px;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
}

/* ---------- Floating utility dock (bottom-right) ----------
   Language, appearance, voice and help in one quiet pill — out of the way
   of content, and consistent on every screen. */
/* One floating corner unit. The camera preview lives INSIDE the dock, above
   the controls row, sharing the same 8px padding on all four sides — so it
   reads as one object that grows, not two stacked cards. Camera off, the
   column collapses and it's a plain pill again. */
.dock {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-card);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  transition: border-radius .4s var(--ease);
}
/* a pill radius wrapped round a video looks wrong — square up when it shows */
.dock.has-cam { border-radius: 22px; }

.dock-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Fixed width is load-bearing: with none, the video's width:100% has no
   real size to resolve against and the browser falls back to the camera's
   native resolution (720p+) — the whole dock would balloon to fill the
   screen. This is what "the video is massive" actually was. */
.dock-cam {
  width: 168px;
  border-radius: 15px;
  overflow: hidden;
  background: #000;
  line-height: 0;              /* kill the phantom gap under the video */
}
.dock-cam video {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transform: scaleX(-1);       /* mirror — like a mirror, not CCTV */
}

@media (max-width: 640px) {
  .dock-cam { width: 128px; }
}

/* ---------- The eyeris brand lockup (nav) ----------
   Icon + wordmark as one drawn mark rather than an icon plus separate HTML
   text. currentColor, no blend-mode trickery here on purpose: the topbar
   sits on a blurred, semi-transparent backdrop (see .topbar above), and
   mix-blend-mode against a moving, partially-transparent background is
   unpredictable — var(--ink) already gives a clean, reliable light/dark
   swap exactly like every other icon in the dock. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--ink);
  padding: 6px 10px;
  border-radius: 999px;
  transition: transform .3s var(--ease), opacity .3s ease;
}
.brand-lockup {
  height: 22px;
  width: 22px;
  display: block;
  transition: opacity .4s var(--ease);
}
/* Live text, not baked into the icon SVG — a renamed BRAND has to reach
   this word, and only real text can be renamed. */
.brand-word {
  font-family: "Fraunces", ui-serif, "New York", Georgia, serif;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: .005em;
  line-height: 1;
  transition: opacity .4s var(--ease);
}
.brand:hover { opacity: .82; transform: scale(1.03); }
.brand:active { transform: scale(.96); }
@media (prefers-reduced-motion: reduce) {
  .brand:hover { transform: none; }
}

/* Home only: the nav lockup starts hidden (the big hero mark is already
   doing the job) and fades in once you've scrolled past it. Everywhere
   else .topbar never gets this class, so the lockup shows instantly —
   see the IntersectionObserver in app.js. */
.topbar.brand-hidden .brand-lockup,
.topbar.brand-hidden .brand-word { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .brand-lockup, .brand-word { transition: none; }
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  transition: transform .5s var(--ease), box-shadow .5s var(--ease), border-color .5s var(--ease);
}
.icon-btn:hover { transform: scale(1.08); box-shadow: var(--shadow-card); border-color: transparent; }
.icon-btn:active { transform: scale(0.96); transition-duration: .15s; }

/* Voice muted is worth seeing at a glance — it changes what the whole app
   does, and (in Guard) how it nudges you. */
#muteBtn.muted { color: #e2564a; }

/* ---------- Flow progress bar ---------- */

.flow-progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 60;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s ease;
}
.flow-progress.active { opacity: 1; }

.flow-progress-fill {
  height: 100%;
  width: 0%;
  background: var(--violet);
  border-radius: 0 2px 2px 0;
  transition: width .5s cubic-bezier(.2,.7,.2,1);
}

/* ---------- Free test finder ---------- */

.vq { margin-bottom: 22px; }


/* ---------- Screens ---------- */

#app {
  flex: 1;
  width: 100%;
  max-width: 1500px;
  margin: 0 auto;
  padding: 24px 24px 96px;
}

.screen {
  display: none;
  animation: rise .55s cubic-bezier(.2,.7,.2,1);
}
.screen.active { display: block; }

@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---------- Hero (welcome) ---------- */

.hero {
  text-align: center;
  padding: 64px 16px 46px;
  max-width: 720px;
  margin: 0 auto;
}

/* ---------- Hero mark ----------
   The same trick the Guard tile pulls: a solid surface INVERTED against the
   page (black on the cream one, white on the black one) with the prism sheen
   drifting across it — so the mark holds its shape while the colour moves
   through it.

   It works by making the logo a CSS mask on this box rather than a filled
   <svg>: the surface AND the rotating gradient are both children of the
   masked element, so both get clipped to the silhouette. The inline <svg>
   stays put as the fallback for anything without mask support (without it,
   a browser that ignores the mask would render a solid black square).

   Alphas run ~2.5x the tile's: the tile is ~590px of open surface, this is
   52px of thin silhouette, and the tile's .14-.26 stops read as grey mud at
   that size. Blur is scaled to match (44px on the tile -> 9px here). */
.hero-mark {
  position: relative;
  width: 52px;
  height: 52px;
  margin: 0 auto 26px;
  color: var(--ink);
}
.hero-mark-icon { display: block; width: 100%; height: 100%; }

@supports ((-webkit-mask-image: none) or (mask-image: none)) {
  .hero-mark {
    overflow: hidden;
    background: #000;
    -webkit-mask: var(--logo-mask) center / contain no-repeat;
    mask: var(--logo-mask) center / contain no-repeat;
  }
  :root[data-theme="dark"] .hero-mark { background: #fff; }
  .hero-mark-icon { display: none; }
  .hero-mark::before {
    content: "";
    position: absolute;
    inset: -70%;
    background: conic-gradient(from 210deg,
      rgba(126, 205, 160, 0)   0deg,
      rgba(90, 160, 255, .68)  60deg,
      rgba(163, 119, 243, .62) 130deg,
      rgba(255, 120, 120, .38) 190deg,
      rgba(255, 200, 80, .38)  240deg,
      rgba(126, 205, 160, .62) 300deg,
      rgba(126, 205, 160, 0)   360deg);
    filter: blur(9px);
    animation: sheen-cw 12s linear infinite;
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-mark::before { animation: none; }
}
@media (max-width: 640px) {
  .hero-mark { width: 42px; height: 42px; }
  .hero-mark::before { filter: blur(7px); }
}

/* Hero headline in the brand serif (Fraunces, same as the wordmark) — the
   card/section typography stays on the sans on purpose. Serif needs gentler
   tracking and a touch more line-height than the old sans setting. */
h1 {
  font-family: "Fraunces", ui-serif, "New York", Georgia, serif;
  font-size: clamp(44px, 6.6vw, 72px);
  line-height: 1.08;
  letter-spacing: -0.012em;
  font-weight: 600;
  margin-bottom: 24px;
}

.lede {
  font-size: 21px;
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 600px;
  margin: 0 auto 10px;
}

.hero-meta {
  font-size: 14.5px;
  color: var(--ink-faint);
  text-align: center;
  margin: 8px 0 46px;
}

/* ---------- Hero row: check, Guard, booking — three equal-height, fixed
   dark feature tiles, Apple-product-page style. Deliberately NOT a
   scrolling carousel: these three should all be visible at once, which is
   why #app is wide enough (1320px) to fit them comfortably. ---------- */

.hero-row {
  max-width: 1200px;
  margin: 0 auto 52px;          /* bottom gap matches the carousel's below (see .test-menu) */
  display: grid;
  /* 2 × 2 on wide screens — wider tiles, each with a graphic — collapsing to
     a single column on narrow ones */
  grid-template-columns: repeat(2, 1fr);
  gap: 18px;
  align-items: stretch;   /* every tile matches the tallest one in the row */
}

@media (max-width: 720px) {
  .hero-row { grid-template-columns: 1fr; }
}

/* ---------- Hero row: check, Guard, booking — three equal-height, fixed
   dark feature tiles, Apple-product-page style. Deliberately NOT a
   scrolling carousel: these three should all be visible at once, which is
   why #app is wide enough (1320px) to fit them comfortably. ---------- */

.hero-row {
  max-width: 1200px;
  margin: 0 auto 52px;          /* bottom gap matches the carousel's below (see .test-menu) */
  display: grid;
  /* 2 × 2 on wide screens — wider tiles, each with a graphic — collapsing to
     a single column on narrow ones */
  grid-template-columns: repeat(2, 1fr);
  gap: 18px;
  align-items: stretch;   /* every tile matches the tallest one in the row */
}

@media (max-width: 720px) {
  .hero-row { grid-template-columns: 1fr; }
}

/* Guard runs the OPPOSITE surface to its three siblings — pure black on the
   cream page, pure white on the black one — and carries the most vivid
   prism. Flip + loudest sweep together are what make it the tile your eye
   lands on first. */
.hero-tile-guard {
  --tile: #000000;
  --tile-ink: #ffffff;
  --tile-soft: #9b978f;
  --tile-border: rgba(255, 255, 255, 0.12);
  --tile-border-hover: rgba(255, 255, 255, 0.26);
}
:root[data-theme="dark"] .hero-tile-guard {
  --tile: #ffffff;
  --tile-ink: #141416;
  --tile-soft: #55534e;
  --tile-border: rgba(20, 20, 22, 0.10);
  --tile-border-hover: rgba(20, 20, 22, 0.20);
}

.hero-tile {
  position: relative;
  overflow: hidden;
  background: var(--tile);
  border: 1px solid var(--tile-border);
  border-radius: var(--radius-lg);
  padding: 32px 34px 30px;
  display: flex;
  flex-direction: row;         /* text on the left, graphic on the right */
  align-items: center;
  gap: 20px;
  animation: rise .6s cubic-bezier(.2,.7,.2,1) backwards;
  transition: transform .6s var(--ease), box-shadow .6s var(--ease), border-color .6s var(--ease);
}

.hero-tile-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  /* keep text above the tiles' decorative ::before/::after layers */
  position: relative;
  z-index: 1;
}


.hero-tile:nth-child(2) { animation-delay: .08s; }
.hero-tile:nth-child(3) { animation-delay: .16s; }
.hero-tile:nth-child(4) { animation-delay: .24s; }
.hero-tile:hover {
  transform: translateY(-6px);
  box-shadow: 0 20px 60px rgba(0,0,0,.35);
  border-color: var(--tile-border-hover);
}

/* ---------- Aurora prism sheen ----------
   The tile itself is plain (white, or pure black in dark mode) with only a
   hairline border — no coloured tint, no glass edge. ALL the colour is this
   slow, soft-focus conic sweep drifting behind the text like light through
   glass. Translucent stops over var(--tile) read correctly on both surfaces,
   so no per-theme override is needed; each tile's overflow:hidden contains
   the oversized rotating layer.

   Every tile mixes the prism DIFFERENTLY — its own colour order and
   emphasis, start angle, off-centre inset, blur, duration and rotation
   direction. Four identical sweeps side by side instantly read as a
   repeating loop; these never line up, and the 12/17/14/19s durations drift
   permanently out of phase. */
.hero-tile::before {
  content: "";
  position: absolute;
  pointer-events: none;
  animation: sheen-cw 16s linear infinite;
}
@keyframes sheen-cw  { to { transform: rotate(1turn); } }
@keyframes sheen-ccw { to { transform: rotate(-1turn); } }

/* Guard — the loudest: full spectrum, centred, slowest, clockwise. */
.hero-tile-guard::before {
  inset: -45%;
  background: conic-gradient(from 210deg,
    rgba(126, 205, 160, 0)   0deg,
    rgba(90, 160, 255, .26)  60deg,
    rgba(163, 119, 243, .24) 130deg,
    rgba(255, 120, 120, .14) 190deg,
    rgba(255, 200, 80, .14)  240deg,
    rgba(126, 205, 160, .24) 300deg,
    rgba(126, 205, 160, 0)   360deg);
  filter: blur(44px);
  animation-duration: 12s;
}

/* Free Online Test — cool: violet into blue, no warmth at all.
   Weighted top-left, anticlockwise. */
.hero-tile-check::before {
  inset: -55% -35% -35% -55%;
  background: conic-gradient(from 40deg,
    rgba(113, 46, 237, 0)     0deg,
    rgba(163, 119, 243, .22) 80deg,
    rgba(90, 160, 255, .24) 165deg,
    rgba(120, 220, 255, .14) 250deg,
    rgba(113, 46, 237, .18)   315deg,
    rgba(113, 46, 237, 0)     360deg);
  filter: blur(52px);
  animation-name: sheen-ccw;
  animation-duration: 17s;
}

/* Eye Workout — fresh: green into teal, one violet note.
   Weighted bottom-left, clockwise. */
.hero-tile-ex::before {
  inset: -35% -55% -55% -35%;
  background: conic-gradient(from 150deg,
    rgba(126, 205, 160, 0)   0deg,
    rgba(126, 205, 160, .24) 70deg,
    rgba(90, 210, 200, .18)  150deg,
    rgba(200, 230, 120, .12) 215deg,
    rgba(163, 119, 243, .14) 290deg,
    rgba(126, 205, 160, 0)   360deg);
  filter: blur(48px);
  animation-duration: 14s;
}

/* In-Person Test — warm: gold into rose, one cool note to stop it going
   sepia. Weighted right, anticlockwise, slowest of all. */
.hero-tile-book::before {
  inset: -50% -60% -40% -30%;
  background: conic-gradient(from 300deg,
    rgba(255, 170, 90, 0)    0deg,
    rgba(255, 200, 80, .18)  70deg,
    rgba(255, 130, 140, .18) 140deg,
    rgba(230, 120, 200, .14) 210deg,
    rgba(120, 140, 255, .12) 300deg,
    rgba(255, 170, 90, 0)    360deg);
  filter: blur(56px);
  animation-name: sheen-ccw;
  animation-duration: 19s;
}

@media (prefers-reduced-motion: reduce) {
  .hero-tile::before { animation: none; }
}

.hero-tile h3 {
  font-size: 27px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--tile-ink);
}

.hero-tile p {
  font-size: 16px;
  line-height: 1.55;
  color: var(--tile-soft);
  flex: 1;
}


/* Deliberately nowrap — the two buttons stay side by side rather than
   stacking to a second row, which is what was making this tile taller
   than the other two. */
.hero-tile-actions {
  display: flex;
  gap: 8px;
  flex-wrap: nowrap;
  margin-top: 8px;
  width: 100%;
}
.hero-tile-actions .btn {
  flex: 1 1 0;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Buttons swap the tile's two colours — bg becomes ink, ink becomes bg —
   so they're always the inverse of whatever the card is doing right now:
   a white pill on the dark tile in light mode, a dark pill on the white
   tile in dark mode. No separate dark-mode override needed. */
/* .btn.btn-hero (not just .btn-hero) deliberately — a same-specificity class
   selector can otherwise lose the cascade to the base .btn rule purely by
   source order, which is exactly what silently broke this button's colours
   once before: the base .btn transition list won and something else could
   just as easily blank these colours out again without this. */
.btn.btn-hero {
  background: var(--tile-ink);
  color: var(--tile);
  font-weight: 600;
  padding: 14px 30px;
  margin-top: 8px;
  transition: transform .45s var(--ease), box-shadow .45s var(--ease);
}
.btn-hero:hover { transform: translateY(-2px) scale(1.015); box-shadow: 0 10px 30px rgba(0,0,0,.3); }
.hero-tile-actions .btn-hero { margin-top: 0; padding: 13px 14px; font-size: 15px; }

.btn.btn-hero-outline {
  background: transparent;
  color: var(--tile-ink);
  font-weight: 600;
  padding: 12px 14px;
  font-size: 15px;
  border: 1.5px solid color-mix(in srgb, var(--tile-ink) 50%, transparent);
  /* background/border-color are deliberately NOT transitioned — both derive
     from --tile-ink, which flips on every theme toggle, and animating a
     property whose value depends on a theme-swapped variable is exactly
     what caused this button to render invisible earlier. Hover still snaps
     instantly instead, which is a fair trade for never breaking again. */
  transition: transform .45s var(--ease), box-shadow .45s var(--ease);
}
.btn-hero-outline:hover {
  background: color-mix(in srgb, var(--tile-ink) 12%, transparent);
  border-color: var(--tile-ink);
  transform: translateY(-2px) scale(1.015);
}

.carousel-nav {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 14px;
  padding-right: 4px;
}

.carousel-arrow {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink);
  cursor: pointer;
  transition: transform .4s var(--ease), box-shadow .4s var(--ease), opacity .3s ease;
}
.carousel-arrow:hover { transform: scale(1.08); box-shadow: var(--shadow-card); }
.carousel-arrow:active { transform: scale(.94); }
.carousel-arrow:disabled { opacity: .3; cursor: default; box-shadow: none; transform: none; }

/* ---------- Test menu ---------- */

/* The white "All tests" band — full-bleed to the viewport edges so it reads
   as a distinct block against the cream page. Only wraps the All-tests
   carousel; Eye exercises sits back on the plain cream afterwards, giving
   three bands (cream / white / cream). The background gradient flows in
   from the cream above at the TOP only — the bottom edge is a flat, hard
   cut back to cream, no gradient, on purpose. The big margin-top is the
   spacious Apple-brochure gap below the hero tiles. */
.tests-band {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-top: 104px;
  padding: 56px 0 72px;
  background: linear-gradient(to bottom, var(--bg) 0, var(--surface) 190px);
}
@media (max-width: 640px) {
  .tests-band { margin-top: 64px; padding-top: 40px; }
}

/* Scroll-reveal: cards ease up and fade in as they scroll into view.
   JS adds .pre-reveal (initial hidden state) only when it's going to drive
   the animation, so with no JS the cards stay visible. The animation has no
   fill, so once it finishes the card returns to its normal resting state and
   :hover works unhindered. */
[data-screen="welcome"] .menu-card.pre-reveal { opacity: 0; }
[data-screen="welcome"] .menu-card.reveal-in {
  animation: card-rise .7s var(--ease);
}
@keyframes card-rise {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  [data-screen="welcome"] .menu-card.pre-reveal { opacity: 1; }
  [data-screen="welcome"] .menu-card.reveal-in { animation: none; }
}

/* Big Apple-brochure section title. The 1248/24 max-width-and-padding pair
   lines its left edge up with the first carousel card at every width (the
   carousel insets its content by the same max(24px, 50vw-600px) amount).
   Colour matches the hero title; spacing is deliberately default (not the
   hero's tight tracking) per the brief. */
/* Section titles share the hero's brand serif so they read as siblings of
   the headline, but hold a flat 44px at every width — the hero's fluid
   ramp is for the one thing that should dominate the page; these sit a
   clear step below it and stay put. */
.menu-head {
  text-align: left;
  font-family: "Fraunces", ui-serif, "New York", Georgia, serif;
  font-size: 44px;
  font-weight: 600;
  letter-spacing: -0.012em;
  text-transform: none;
  line-height: 1.08;
  color: var(--ink);
  max-width: 1248px;
  margin: 0 auto 14px;
  padding: 0 24px;
}

/* Subtitle copies the hero lede's type styling (size, colour, line-height),
   so both read identically in light and dark mode. */
.menu-subhead {
  text-align: left;
  font-size: 21px;
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 1248px;
  margin: 0 auto 34px;
  padding: 0 24px;
}

/* "Eye exercises" sits back on the plain cream page, straight after the
   white band's hard bottom edge — three visual bands (cream / white /
   cream), no gradient on this seam, just a clean cut. Comfortable gap so
   it doesn't feel glued to the band above. */
.tests-band + .menu-head { margin-top: 76px; }

.menu-honesty {
  text-align: center;
  font-size: 13.5px;
  color: var(--ink-faint);
  max-width: 560px;
  margin: 28px auto 0;
  line-height: 1.55;
}

.privacy-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
  max-width: 720px;
  margin: 22px auto 0;
}

.privacy-text {
  font-size: 13.5px;
  color: var(--ink-faint);
  line-height: 1.55;
  max-width: 480px;
  text-align: center;
}

/* A long horizontal carousel — grows to any number of cards without
   changing layout, just add another .menu-card. Every card stretches to
   the same height automatically (flex default cross-axis stretch). */
.test-carousel-wrap {
  max-width: 1200px;
  margin: 0 auto;
}

/* Cards are shadowless now, so the scroll container only needs a little
   vertical headroom for the hover lift (translateY -7px) — no more
   padding/negative-margin gymnastics for shadow bleed. */
.test-menu {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 14px 4px 26px;
  margin: 0 -4px;
}
.test-menu::-webkit-scrollbar { display: none; }

/* Apple-product-page carousel: on the home screen the scroll container is
   FULL viewport width — cards run right off both screen edges — while the
   first card still lines up with the 1200px content column, via padding
   that matches the distance from the viewport edge to that column
   (max() falls back to #app's own 24px gutter on narrow screens). The
   negative margin is what breaks the container out of its centred wrap.
   Deliberately welcome-only: the results-screen copy of this carousel
   lives inside a white card and must stay inside it. */
[data-screen="welcome"] .test-menu {
  margin: 0 calc(50% - 50vw);
  padding-left: max(24px, calc(50vw - 600px));
  padding-right: max(24px, calc(50vw - 600px));
  scroll-padding-left: max(24px, calc(50vw - 600px));
}

/* Cards follow the site theme like everything else — white surface in light
   mode, dark surface in dark mode. Each test's preview lives in a small
   bright "screenshot" chip inside the card, since the tests themselves are
   always shown on a bright background regardless of theme — but the card
   chrome around it is never a fixed colour. */
/* Tall, poster-style cards, iOS-product-page fashion: a big graphic panel
   up top, bold question beneath, and a "Try now" affordance pinned to the
   bottom. The whole card is one click target. */
.menu-card {
  flex: 0 0 auto;
  width: min(316px, 80vw);
  min-height: 430px;
  scroll-snap-align: start;
  font-family: var(--font);
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 0 0 22px;
  overflow: hidden;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* deliberately shadowless, resting AND hover — shadows in a scroll
     container kept getting clipped at its edges; the lift + border shift
     carry the hover feedback on their own */
  transition: transform .55s var(--ease), border-color .4s ease;
}
.menu-card:hover {
  transform: translateY(-7px) scale(1.008);
  border-color: rgba(128, 124, 116, .45);
}
.menu-card:active {
  transform: translateY(-3px) scale(.995);
  transition: transform .2s var(--ease);
}

.menu-card-live { border-color: rgba(126, 205, 160, 0.55); }
.menu-card-live:hover { border-color: rgba(126, 205, 160, 0.75); }

.menu-top {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  flex: 1;
  padding: 0 24px;
}

.time-pill {
  background: var(--bg-soft);
  color: var(--ink-faint);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .02em;
  padding: 5px 13px;
  border-radius: 999px;
  margin-bottom: 4px;
}
/* Status badges are deliberately solid and theme-independent — not tied to
   var(--tile)/var(--tile-ink) — since a translucent same-hue tint on text
   reads fine on a dark tile but drops to ~1.7:1 contrast on a white one.
   time-pill-ex/-book deliberately have no override left — they read as
   plain, neutral time-pills like every other card's. */
.time-pill-live { background: #1f7a4d; color: #eafff2; }

.menu-q {
  font-size: 23px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink);
  line-height: 1.25;
}

/* Full-ink like the title (Apple does the same) — the faint grey version
   was hard to read at a glance and sat below AA on the tinted panel. */
.menu-sub {
  font-size: 16px;
  line-height: 1.45;
  color: var(--ink);
}

/* The preview is the poster panel at the top of the card — big, full
   strength always, on a softly tinted stage. order:-1 hoists it above the
   text without touching the markup of all sixteen cards. */
.menu-mock {
  order: -1;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 195px;
  flex-shrink: 0;
  width: 100%;
  background: var(--bg-soft);
}

.menu-mock-graphic {
  color: var(--ink);
  opacity: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 700;
  user-select: none;
  transform: scale(1.5);   /* SVGs stay vector-crisp under CSS scale */
  transition: transform .6s var(--ease);
}
.menu-card:hover .menu-mock-graphic { transform: scale(1.62); }

/* "Try now →" affordance pinned to the card's bottom edge */
.menu-cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
  margin-top: 2px;
}

.menu-cta-link {
  font-size: 16px;
  font-weight: 600;
  color: var(--violet);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.menu-cta-link svg { transition: transform .4s var(--ease); }
.menu-card:hover .menu-cta-link svg { transform: translateX(4px); }

.menu-cta .time-pill { margin-bottom: 0; }

.mock-snellen { text-align: center; line-height: 1.25; letter-spacing: .35em; }
.mock-snellen div:nth-child(1) { font-size: 24px; }
.mock-snellen div:nth-child(2) { font-size: 14px; }
.mock-snellen div:nth-child(3) { font-size: 8px; }

.mock-duo { display: flex; border-radius: 10px; overflow: hidden; }
.mock-duo span {
  padding: 12px 16px;
  font-size: 16px;
  letter-spacing: .25em;
  color: #000;
}
.mock-duo .r { background: #e8000d; }
.mock-duo .g { background: #00a33e; }

.mock-contrast { display: flex; gap: 16px; font-size: 24px; }

.mock-eyescan {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  font-family: var(--font);
}
.mock-eyescan-label { color: var(--violet); }

/* ---------- Per-test hover animations ----------
   Each card's icon gets one small, literal animation tied to what that test
   actually does (a moving dot for tests that track a target, a reveal for
   reading tests, a pulse for fixation points, etc). All run only on
   .menu-card:hover so the resting grid stays calm; shared keyframes are
   reused across cards where the underlying motion is genuinely the same. */

@keyframes menu-cascade { 0%, 100% { opacity: 0; transform: translateY(6px); } 30%, 80% { opacity: 1; transform: translateY(0); } }
@keyframes menu-tilt { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(8deg); } }
@keyframes menu-seesaw-up { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes menu-seesaw-down { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }
@keyframes menu-flash-in { 50% { opacity: 1; } }
@keyframes menu-pulse-scale { 50% { transform: scale(1.4); } }
@keyframes menu-fixate { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.6); opacity: .55; } }
@keyframes menu-measure { to { stroke-dashoffset: -12; } }
@keyframes menu-blindspot { 0% { cx: 60px; opacity: 1; } 60%, 100% { cx: 14px; opacity: 0; } }
@keyframes menu-approach { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.8); } }
@keyframes menu-saccade { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(3px); } 50% { transform: translateX(-3px); } 75% { transform: translateX(2px); } }
@keyframes menu-drip { 0% { transform: translateY(0); opacity: 1; } 70%, 100% { transform: translateY(10px); opacity: 0; } }
@keyframes menu-flash-corner { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }
@keyframes menu-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
@keyframes menu-focus-near { 0%, 100% { transform: scale(1); } 40% { transform: scale(1.4); } }
@keyframes menu-focus-far { 0%, 100% { transform: scale(1); } 90% { transform: scale(1.4); } }
@keyframes menu-pursue { 0% { cx: 8px; cy: 24px; } 25% { cx: 28px; cy: 6px; } 50% { cx: 42px; cy: 24px; } 75% { cx: 56px; cy: 42px; } 100% { cx: 76px; cy: 24px; } }
@keyframes menu-blink-close { 0%, 80%, 100% { transform: scaleY(1); } 90% { transform: scaleY(.15); } }

/* Reading tests — letters/words reveal top to bottom, like an eye chart
   being read line by line. snellen and nearread share .mock-snellen. */
.menu-card:hover .mock-snellen div { animation: menu-cascade 2.4s ease-in-out infinite; }
.menu-card:hover .mock-snellen div:nth-child(2) { animation-delay: .15s; }
.menu-card:hover .mock-snellen div:nth-child(3) { animation-delay: .3s; }

/* Astigmatism dial — the spoke pattern tilts, echoing "which lines look
   sharpest as it rotates." */
.menu-card[data-module="astig"]:hover .menu-mock-graphic svg { animation: menu-tilt 1.6s ease-in-out infinite; transform-origin: center; }

/* Duochrome — red and green letters see-saw, mirroring the "which side is
   clearer" comparison the test asks you to make. */
.menu-card:hover .mock-duo .r { animation: menu-seesaw-up 1.4s ease-in-out infinite; }
.menu-card:hover .mock-duo .g { animation: menu-seesaw-down 1.4s ease-in-out infinite; }

/* Contrast — the faintest letter flashes up to full strength and back,
   like contrast sensitivity being dialled up. */
.menu-card:hover .mock-contrast span:nth-child(1) { animation: menu-flash-in 1.8s ease-in-out infinite; }
.menu-card:hover .mock-contrast span:nth-child(2) { animation: menu-flash-in 1.8s ease-in-out infinite .12s; }
.menu-card:hover .mock-contrast span:nth-child(3) { animation: menu-flash-in 1.8s ease-in-out infinite .24s; }

/* Colour vision — the hidden shape's dots pulse so the embedded pattern
   stands out from the field, the way it's meant to for someone who can see it. */
.menu-card[data-module="colour"]:hover .menu-mock-graphic svg circle[fill="#8a9a3b"] {
  transform-box: fill-box; transform-origin: center;
  animation: menu-pulse-scale 1.6s ease-in-out infinite;
}

/* Amsler grid — the fixation dot pulses, exactly what you're told to stare
   at while watching the grid lines around it for distortion. */
.menu-card[data-module="amsler"]:hover .menu-mock-graphic svg circle {
  transform-box: fill-box; transform-origin: center;
  animation: menu-fixate 1.3s ease-in-out infinite;
}

/* Pupillary distance scan — the dashed measuring line marches, like an
   active measurement being taken between the two eyes. */
.menu-card[data-module="eyescan"]:hover .mock-eyescan line { animation: menu-measure .8s linear infinite; }

/* Blind spot — the target dot slides toward the fixation cross and
   vanishes, the literal blind-spot demonstration. */
.menu-card[data-module="bspot"]:hover .mock-bspot circle { animation: menu-blindspot 1.8s ease-in infinite; }

/* Eye dominance — the dominant eye's circle pulses forward while the other
   stays still, showing which eye is "leading." */
.menu-card[data-module="dominance"]:hover .menu-mock-graphic svg circle:nth-of-type(1),
.menu-card[data-module="dominance"]:hover .menu-mock-graphic svg circle:nth-of-type(2) {
  transform-box: fill-box; transform-origin: center;
  animation: menu-pulse-scale 1.4s ease-in-out infinite;
}

/* Convergence — the centre point grows as if approaching, the near-target
   your eyes are converging on. */
.menu-card[data-module="convergence"]:hover .menu-mock-graphic svg circle {
  transform-box: fill-box; transform-origin: center;
  animation: menu-approach 1.1s ease-in-out infinite;
}

/* Reading speed — the number rows jitter side to side like eyes saccading
   across a line, and the dimmer row sharpens up. */
.menu-card[data-module="speed"]:hover .mock-speed div { animation: menu-saccade .9s ease-in-out infinite; }
.menu-card[data-module="speed"]:hover .mock-speed div:nth-child(2) { animation: menu-saccade .9s ease-in-out infinite, menu-flash-in 1.8s ease-in-out infinite; }

/* Glare — the halo pulses brighter and dimmer, glare drifting over the
   letter. */
.menu-card[data-module="glare"]:hover .mock-glare svg circle:nth-of-type(1) {
  transform-box: fill-box; transform-origin: center;
  animation: menu-pulse-scale 1.5s ease-in-out infinite;
}
.menu-card[data-module="glare"]:hover .mock-glare svg circle:nth-of-type(2) {
  transform-box: fill-box; transform-origin: center;
  animation: menu-pulse-scale 1.5s ease-in-out infinite .3s;
}

/* Dry eye — a tear drips and fades. */
.menu-card[data-module="dryeye"]:hover .menu-mock-graphic svg path:nth-of-type(2) {
  transform-box: fill-box;
  animation: menu-drip 1.6s ease-in infinite;
}

/* Peripheral vision — the four corner targets flash in sequence, like
   stimuli appearing around the edge of your vision. */
.menu-card[data-module="peripheral"]:hover .menu-mock-graphic svg circle:nth-of-type(1) { animation: menu-flash-corner 1.2s ease-in-out infinite; }
.menu-card[data-module="peripheral"]:hover .menu-mock-graphic svg circle:nth-of-type(2) { animation: menu-flash-corner 1.2s ease-in-out infinite .15s; }
.menu-card[data-module="peripheral"]:hover .menu-mock-graphic svg circle:nth-of-type(3) { animation: menu-flash-corner 1.2s ease-in-out infinite .3s; }
.menu-card[data-module="peripheral"]:hover .menu-mock-graphic svg circle:nth-of-type(4) { animation: menu-flash-corner 1.2s ease-in-out infinite .45s; }

/* Photostress recovery — the glare flares then the letter sharpens back
   into focus, the glare-then-recover this test actually measures. */
.menu-card[data-module="photostress"]:hover .menu-mock-graphic svg circle:nth-of-type(1) {
  transform-box: fill-box; transform-origin: center;
  animation: menu-pulse-scale 1.6s ease-in-out infinite;
}
.menu-card[data-module="photostress"]:hover .menu-mock-graphic svg circle:nth-of-type(2) {
  transform-box: fill-box; transform-origin: center;
  animation: menu-pulse-scale 1.6s ease-in-out infinite .2s;
}
.menu-card[data-module="photostress"]:hover .menu-mock-graphic svg text {
  transform-box: fill-box; transform-origin: center;
  animation: menu-approach 1.6s ease-in-out infinite .5s;
}

/* Palming exercise — a slow, calm breathing pulse. */
.menu-card[data-module="ex-palming"]:hover .menu-mock-graphic svg { animation: menu-breathe 2.2s ease-in-out infinite; transform-origin: center; }

/* Near-far exercise — focus alternates between the near and far dot, the
   refocusing the exercise trains. */
.menu-card[data-module="ex-nearfar"]:hover .menu-mock-graphic svg circle:nth-of-type(1) {
  transform-box: fill-box; transform-origin: center;
  animation: menu-focus-near 1.8s ease-in-out infinite;
}
.menu-card[data-module="ex-nearfar"]:hover .menu-mock-graphic svg circle:nth-of-type(2) {
  transform-box: fill-box; transform-origin: center;
  animation: menu-focus-far 1.8s ease-in-out infinite;
}

/* Smooth pursuit exercise — the dot actually travels the wavy path, the
   moving target you're meant to track with your eyes. */
.menu-card[data-module="ex-pursuit"]:hover .menu-mock-graphic svg circle { animation: menu-pursue 2.4s ease-in-out infinite alternate; }

/* Blink reset exercise — the eye shape blinks closed and open. */
.menu-card[data-module="ex-blink"]:hover .menu-mock-graphic svg { animation: menu-blink-close 1.8s ease-in-out infinite; transform-origin: center; }

/* Lazy eight — the dot sweeps side to side along the eight's long axis. */
@keyframes menu-eight {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(24px, 16px); }
  50% { transform: translate(48px, 0); }
  75% { transform: translate(24px, 16px); }
}
.menu-card[data-module="ex-eight"]:hover .menu-mock-graphic svg circle {
  transform-box: fill-box;
  animation: menu-eight 2.6s ease-in-out infinite;
}

/* Eye circles — the whole dial slowly rotates, orbiting the dot. */
.menu-card[data-module="ex-roll"]:hover .menu-mock-graphic svg {
  animation: menu-tilt-full 3s linear infinite;
  transform-origin: center;
}
@keyframes menu-tilt-full { to { transform: rotate(1turn); } }

/* Corner to corner — the four corner targets flash in the saccade order. */
.menu-card[data-module="ex-saccade"]:hover .menu-mock-graphic svg circle:nth-of-type(1) { animation: menu-flash-corner 1.6s ease-in-out infinite; }
.menu-card[data-module="ex-saccade"]:hover .menu-mock-graphic svg circle:nth-of-type(2) { animation: menu-flash-corner 1.6s ease-in-out infinite .4s; }
.menu-card[data-module="ex-saccade"]:hover .menu-mock-graphic svg circle:nth-of-type(3) { animation: menu-flash-corner 1.6s ease-in-out infinite .8s; }
.menu-card[data-module="ex-saccade"]:hover .menu-mock-graphic svg circle:nth-of-type(4) { animation: menu-flash-corner 1.6s ease-in-out infinite 1.2s; }

/* Distance gazing — the far sun gently swells, like focus settling on it. */
.menu-card[data-module="ex-distance"]:hover .menu-mock-graphic svg circle {
  transform-box: fill-box; transform-origin: center;
  animation: menu-breathe 2.4s ease-in-out infinite;
}

/* ---------- Cards ---------- */

.card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 54px clamp(26px, 6vw, 68px);
  max-width: 780px;
  margin: 40px auto 0;
}

.card.narrow { max-width: 620px; }
.centered-card { text-align: center; }

.step-label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--violet);
  margin-bottom: 14px;
}

h2 {
  font-size: clamp(30px, 4.5vw, 44px);
  letter-spacing: -0.03em;
  line-height: 1.12;
  margin-bottom: 16px;
}

.subhead {
  font-size: 22px;
  letter-spacing: -0.015em;
  margin-bottom: 8px;
}

.instruction-text {
  font-size: 18px;
  line-height: 1.6;
  color: var(--ink-soft);
  margin-bottom: 24px;
}
.instruction-text.big { font-size: 20px; }

.hint {
  font-size: 15px;
  color: var(--ink-faint);
  margin-top: 16px;
  line-height: 1.5;
}
.hint a { color: var(--ink-soft); }
.instruction-text a { color: var(--ink-soft); }

.notice {
  background: var(--bg-soft);
  border-radius: var(--radius-md);
  padding: 16px 20px;
  font-size: 16px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 18px 0;
}

.divider {
  height: 1px;
  background: var(--line);
  margin: 34px 0;
}

.centered { text-align: center; margin-bottom: 10px; }

.big-emoji { font-size: 54px; margin-bottom: 18px; }

/* ---------- Buttons ---------- */

.btn {
  font-family: var(--font);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  border: none;
  border-radius: 999px;
  padding: 14px 30px;
  cursor: pointer;
  transition: transform .45s var(--ease), box-shadow .45s var(--ease), opacity .3s ease, filter .3s ease;
}
.btn:active { transform: scale(.97); transition-duration: .15s; }
.btn:disabled { opacity: .35; cursor: default; }

/* Inline icon inside a button (Phosphor, 256-unit viewBox) — sized to the
   label's cap height and optically centred against it. */
.btn-ic {
  width: 17px;
  height: 17px;
  vertical-align: -3px;
  margin-right: 7px;
}

a.btn {
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.chip-row-left { justify-content: flex-start; max-width: none; margin-left: 0; margin-right: 0; }

.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
}
.btn-primary:not(:disabled):hover { transform: translateY(-2px) scale(1.015); box-shadow: 0 10px 28px rgba(29,29,31,.2); }

.btn-secondary {
  background: var(--bg-soft);
  color: var(--ink);
}
.btn-secondary:hover { transform: translateY(-1px); filter: brightness(.96); }

.btn-ghost {
  background: transparent;
  color: var(--ink-soft);
  border: 1px solid var(--line);
}
.btn-ghost:hover { border-color: rgba(128,124,116,.45); color: var(--ink); transform: translateY(-1px); }

.btn-lg { font-size: 18px; padding: 17px 44px; }
.btn-sm { font-size: 14px; padding: 9px 18px; }

.choice-row {
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 28px;
}

/* ---------- Mic stage ---------- */

.mic-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 26px 0 8px;
}

.mic-orb {
  width: 84px; height: 84px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--bg-soft);
  color: var(--ink-faint);
  transition: background .3s ease, color .3s ease;
  position: relative;
}
.mic-orb.listening {
  background: var(--accent);
  color: var(--accent-contrast);
}
.mic-orb.listening::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 2px solid rgba(128,124,116,.35);
  animation: ripple 1.6s ease-out infinite;
}
.mic-orb.heard { background: var(--green); color: #fff; }

@keyframes ripple {
  from { transform: scale(1); opacity: 1; }
  to   { transform: scale(1.45); opacity: 0; }
}

.transcript {
  min-height: 26px;
  font-size: 18px;
  color: var(--ink-soft);
  font-style: italic;
  text-align: center;
}

/* ---------- Calibration ---------- */

.calib-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 30px;
  padding: 26px 0 6px;
}

.finger-wrap, .calib-card-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.finger-outline {
  width: 68px;
  height: 175px;
  border-radius: 34px 34px 18px 18px;
  background: linear-gradient(180deg, #efe9dc, #ddd4c2);
  border: 1.5px dashed rgba(29,29,31,.35);
  position: relative;
  overflow: hidden;
}
:root[data-theme="dark"] .finger-outline {
  background: linear-gradient(180deg, #3a372f, #2c2a24);
  border-color: rgba(240,237,230,.4);
}

.finger-nail {
  position: absolute;
  top: 10%;
  left: 22%;
  width: 56%;
  height: 20%;
  border-radius: 45% 45% 38% 38% / 55% 55% 42% 42%;
  background: rgba(255,255,255,.55);
  border: 1px solid rgba(29,29,31,.12);
}
:root[data-theme="dark"] .finger-nail {
  background: rgba(240,237,230,.18);
  border-color: rgba(240,237,230,.15);
}

.calib-caption {
  font-size: 12.5px;
  color: var(--ink-faint);
  letter-spacing: .02em;
  text-align: center;
}

.calib-card {
  width: 324px;
  aspect-ratio: 85.6 / 53.98;
  border-radius: 14px;
  background: linear-gradient(135deg, #e9e4d8, #d6cfbf);
  border: 1.5px dashed rgba(29,29,31,.35);
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 14px;
}

.calib-card-chip {
  position: absolute;
  top: 22%; left: 8%;
  width: 15%;
  aspect-ratio: 1.3;
  border-radius: 4px;
  background: linear-gradient(135deg, #cbb26a, #a98b3f);
  opacity: .85;
}

input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: min(420px, 90%);
  height: 6px;
  border-radius: 3px;
  background: var(--bg-soft);
  outline: none;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(29,29,31,.3);
  transition: transform .12s ease;
}
input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.12); }
input[type="range"]::-moz-range-thumb {
  width: 26px; height: 26px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  cursor: pointer;
}

.distance-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin: 14px 0 6px;
}

.chip {
  font-family: var(--font);
  font-size: 16px;
  font-weight: 550;
  padding: 12px 26px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-soft);
  cursor: pointer;
  transition: transform .4s var(--ease), box-shadow .4s var(--ease), border-color .3s ease, color .3s ease, background .3s ease;
}
.chip:hover { border-color: rgba(128,124,116,.5); color: var(--ink); transform: translateY(-1px); box-shadow: 0 6px 16px rgba(29,29,31,.08); }
.chip:active { transform: scale(.96); transition-duration: .15s; }
.chip.selected {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}

.chip-row {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 20px;
  max-width: 560px;
  margin-left: auto;
  margin-right: auto;
}

.chip.chip-sm { padding: 9px 16px; font-size: 14px; }

/* ---------- Say ready pill ---------- */

.say-ready {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-soft);
  border-radius: 999px;
  padding: 14px 26px;
  font-size: 16.5px;
  color: var(--ink-soft);
  margin-top: 6px;
}

/* Green means live — the same green as the dock's listening dot and the
   camera preview, so one colour carries one meaning across the whole app.
   (This was a red "recording" dot, which said the opposite of what the
   dock was saying at the very same moment.) */
.pulse-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--green);
  animation: pulse 1.4s ease-in-out infinite;
  flex-shrink: 0;
}

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .4; transform: scale(.8); }
}

/* ---------- Test chrome ---------- */

.test-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: 820px;
  margin: 8px auto 0;
  padding: 0 8px;
}

.test-eye-label {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--violet);
}

.test-line-label {
  font-size: 14px;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* Test stages stay bright even in dark mode — pupils dilate in the dark
   and the measurement changes. This is deliberate. */
.chart-stage {
  display: grid;
  place-items: center;
  min-height: 42vh;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  max-width: 820px;
  margin: 14px auto 0;
  padding: 40px 20px;
  overflow: hidden;
}
.chart-stage.stay-light {
  background: #ffffff;
  color: #111111;
}

.chart-letters {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 700;
  color: #111;
  white-space: nowrap;
  text-align: center;
  user-select: none;
  animation: rise .4s ease;
}

.test-footer {
  max-width: 820px;
  margin: 22px auto 0;
  text-align: center;
}

.question-text {
  font-size: 19px;
  line-height: 1.5;
  color: var(--ink);
  max-width: 560px;
  margin: 0 auto 16px;
}

.slots {
  display: flex;
  gap: 10px;
  justify-content: center;
  margin-bottom: 18px;
  min-height: 46px;
}

.slot {
  width: 48px; height: 50px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1.5px solid var(--line);
  display: grid;
  place-items: center;
  font-size: 23px;
  font-weight: 600;
  color: var(--ink);
  transition: all .18s ease;
}
.slot.filled {
  border-color: var(--ink);
  animation: pop .25s ease;
}
.slot.correct { background: rgba(46,125,79,.12); border-color: var(--green); color: var(--green); }
.slot.wrong   { background: rgba(192,80,60,.1); border-color: #c0503c; color: #c0503c; }

@keyframes pop {
  0% { transform: scale(.8); }
  60% { transform: scale(1.08); }
  100% { transform: scale(1); }
}

.listening-pill {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 13px 26px;
  font-size: 16px;
  color: var(--ink-soft);
  box-shadow: var(--shadow-card);
}

.test-commands {
  font-size: 13.5px;
  color: var(--ink-faint);
  margin-top: 14px;
}

.letter-pad {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 22px;
}

.pad-key {
  font-family: var(--font);
  width: 46px; height: 46px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--surface);
  font-size: 18px;
  font-weight: 600;
  color: var(--ink-soft);
  cursor: pointer;
  transition: all .12s ease;
}
.pad-key:hover { border-color: rgba(128,124,116,.5); color: var(--ink); transform: translateY(-1px); }
.pad-key:active { transform: scale(.94); }

.pad-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  margin-top: 14px;
}

/* ---------- Duochrome ---------- */

.duo-wrap {
  display: flex;
  width: 100%;
  max-width: 640px;
  border-radius: var(--radius-md);
  overflow: hidden;
}

.duo-panel {
  flex: 1;
  display: grid;
  place-items: center;
  padding: 48px 10px;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 700;
  color: #000;
  white-space: nowrap;
  user-select: none;
}

.duo-red   { background: #e8000d; }
.duo-green { background: #00a33e; }

/* ---------- Camera stages ---------- */

.cam-stage {
  position: relative;
  display: inline-block;
  margin: 10px auto 4px;
  max-width: 100%;
}

.cam-stage video {
  width: min(420px, 82vw);
  border-radius: var(--radius-md);
  transform: scaleX(-1);       /* mirror, like looking in a mirror */
  background: #111;
  display: block;
}

.cam-readout {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  background: rgba(20, 20, 20, 0.72);
  color: #fdfcf9;
  font-size: 26px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  padding: 8px 22px;
  border-radius: 999px;
  backdrop-filter: blur(8px);
}
.cam-readout.locked { background: rgba(46, 125, 79, 0.85); }

.scan-progress {
  position: absolute;
  left: 12%;
  right: 12%;
  bottom: 16px;
  height: 8px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.35);
  overflow: hidden;
}

.scan-progress-fill {
  height: 100%;
  width: 0%;
  border-radius: 4px;
  background: #fdfcf9;
  transition: width .25s ease;
}

.or-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 24px 0 14px;
  color: var(--ink-faint);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.or-row::before, .or-row::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

.dist-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

/* ---------- eyeris Guard ---------- */

.guard-grid {
  display: grid;
  grid-template-columns: minmax(200px, 300px) 1fr;
  gap: 20px;
  align-items: start;
  margin: 10px 0 6px;
}

.guard-cam video {
  width: 100%;
  border-radius: var(--radius-md);
  transform: scaleX(-1);
  background: #111;
  display: block;
}

.guard-tiles {
  display: grid;
  gap: 12px;
}

.guard-tile {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 16px 20px;
}

.guard-value {
  font-size: 42px;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.guard-value.good { color: var(--green); }
.guard-value.warn { color: var(--amber); }
.guard-value.bad  { color: #d3452e; }

/* One quick ring per detected blink — live proof the tracker saw it.
   Lands on the tile (not the value) so the every-second class rewrite on
   .guard-value can't clip it mid-animation. */
.guard-tile.blink-ping { animation: blink-ping .35s ease; }
@keyframes blink-ping {
  30% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--green) 45%, transparent); }
}

.guard-label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: 2px;
}

.guard-note {
  font-size: 14.5px;
  color: var(--ink-soft);
  margin-top: 6px;
  min-height: 1em;
}

@media (max-width: 640px) {
  .guard-grid { grid-template-columns: 1fr; }
}

/* ---------- Results report ---------- */

.report {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 26px 0 8px;
}

.report-row {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  background: var(--bg);
  border-radius: var(--radius-md);
  padding: 20px 22px;
}

.report-status {
  width: 12px; height: 12px;
  border-radius: 50%;
  margin-top: 5px;
  flex-shrink: 0;
}
.report-status.good  { background: var(--green); }
.report-status.note  { background: var(--amber); }

.report-body { flex: 1; }

.report-q {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 4px;
}

.report-headline {
  font-size: 20px;
  font-weight: 650;
  letter-spacing: -0.015em;
  margin-bottom: 4px;
}

.report-note {
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink-soft);
}

.soon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin-top: 18px;
}

.soon-card {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  background: var(--bg);
  border-radius: var(--radius-md);
  padding: 18px;
}

.soon-card span { font-size: 24px; line-height: 1.2; }
.soon-card strong { font-size: 15px; letter-spacing: -0.01em; }
.soon-card p { font-size: 13.5px; color: var(--ink-soft); margin-top: 3px; line-height: 1.45; }

/* ---------- Responsive ---------- */

@media (max-width: 640px) {
  .card { padding: 34px 22px; }
  .hero { padding-top: 36px; }
  .duo-wrap { flex-direction: column; }
}

/* ---------- Entrance stagger for the menu grid ---------- */
.screen.active .menu-card { animation: rise .6s cubic-bezier(.2,.7,.2,1) backwards; }
.menu-card:nth-child(1) { animation-delay: .03s; }
.menu-card:nth-child(2) { animation-delay: .06s; }
.menu-card:nth-child(3) { animation-delay: .09s; }
.menu-card:nth-child(4) { animation-delay: .12s; }
.menu-card:nth-child(5) { animation-delay: .15s; }
.menu-card:nth-child(6) { animation-delay: .18s; }
.menu-card:nth-child(7) { animation-delay: .21s; }
.menu-card:nth-child(8) { animation-delay: .24s; }
.menu-card:nth-child(9) { animation-delay: .27s; }
.menu-card:nth-child(10) { animation-delay: .30s; }
.menu-card:nth-child(11) { animation-delay: .33s; }
.menu-card:nth-child(12) { animation-delay: .36s; }
.menu-card:nth-child(13) { animation-delay: .39s; }
.menu-card:nth-child(14) { animation-delay: .42s; }

/* ---------- Whole-card clickable hero tiles ---------- */
/* Only applied (via JS) to tiles with exactly one call-to-action — the
   booking tile has two distinct actions, so it keeps its explicit buttons
   as the only click targets, since a whole-card click would be ambiguous
   about which of the two you meant. */
.hero-tile-clickable { cursor: pointer; }

/* ---------- eyeris Score ---------- */
/* Reuses the --tile/--tile-ink/--tile-soft variables so it automatically
   inherits the same light/dark-mode contrast fixes as the featured tiles
   above, and visually reads as part of the same "featured" family. */
.score-card {
  display: flex;
  align-items: center;
  gap: 24px;
  background: var(--tile);
  border-radius: var(--radius-lg);
  padding: 28px 32px;
  margin-bottom: 28px;
}

.score-value {
  font-size: 54px;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--tile-ink);
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

.score-body { flex: 1; }

.score-label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--tile-soft);
  margin-bottom: 4px;
}

.score-headline {
  font-size: 18px;
  font-weight: 600;
  color: var(--tile-ink);
  line-height: 1.4;
}

@media (max-width: 520px) {
  .score-card { flex-direction: column; align-items: flex-start; gap: 10px; }
}

/* ---------- Glare sensitivity overlay ---------- */
/* Sits inside the (always-bright) chart-stage, over the letters — a soft
   bright bloom that simulates oncoming headlights or window glare. */
.glare-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(circle at 68% 38%, rgba(255,255,255,.95), rgba(255,255,255,.55) 30%, transparent 62%);
}
.chart-stage { position: relative; }

/* ---------- Rapid number naming ---------- */
.speed-grid {
  display: flex;
  flex-direction: column;
  gap: 18px;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 700;
  color: #111;
}
.speed-row {
  display: flex;
  gap: 22px;
  justify-content: center;
  font-size: clamp(28px, 5vw, 40px);
  letter-spacing: .05em;
}

.speed-timer {
  font-size: 32px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  margin-top: 16px;
}

/* ---------- Peripheral awareness ---------- */
.peripheral-stage {
  position: relative;
  width: min(480px, 90vw);
  height: min(320px, 60vw);
}
.peripheral-fix {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  font-size: 34px;
  font-weight: 700;
  color: #111;
  user-select: none;
}
.peripheral-dot {
  position: absolute;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #d3452e;
  opacity: 0;
  transition: opacity .08s linear;
  pointer-events: none;
}
.peripheral-dot.flash { opacity: 1; }

/* ---------- A couple of the new mock previews need their own small layout ---------- */
.mock-speed { display: flex; flex-direction: column; gap: 5px; font-size: 15px; letter-spacing: .12em; }
.mock-dryeye, .mock-peripheral, .mock-bspot, .mock-dominance, .mock-convergence, .mock-glare {
  display: flex;
  align-items: center;
  justify-content: center;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------- Language + country selects ---------- */

.pill-select {
  font-family: var(--font);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 9px 14px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  text-align: center;
}
.pill-select:hover { border-color: rgba(128,124,116,.5); }

/* Lives inside the (light-surfaced) booking chooser now, not on the dark
   hero tile — so it uses the regular page palette, not --tile-*. */
.book-locale {
  font-size: 13px;
  color: var(--ink-faint);
  margin-bottom: 16px;
  display: flex;
  align-items: center;
  gap: 8px 14px;
  flex-wrap: wrap;
}
.book-locale select,
.book-locale a {
  font-family: var(--font);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-faint);
  background: transparent;
  border: none;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  text-decoration: underline;
  text-underline-offset: 3px;
  padding: 2px 0;
}
.book-locale select:hover,
.book-locale a:hover { color: var(--ink); }
.book-locale select:focus { outline: none; color: var(--ink); }

/* ---------- Last score (bottom left) ----------
   A small floating badge, not a card — the number is the whole point, so
   it's the only thing given weight. Mirrors the dock's own corner offset
   on the opposite side, clear of it and of everything else (the only other
   fixed bottom-left control, the fullscreen toggle, never shows on the
   welcome screen this badge lives on — so there's never a collision to
   resolve). Clicking it reopens that check's full report, not just the
   number. */
.timeline-chip {
  position: fixed;
  left: 18px;
  bottom: 18px;
  z-index: 39;
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-family: var(--font);
  color: var(--ink);          /* one colour for both halves; stale re-tints here */
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 11px 17px;
  cursor: pointer;
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  box-shadow: var(--shadow-card);
  transition: transform .35s var(--ease), border-color .3s ease;
}
.timeline-chip:hover:not(:disabled) { transform: translateY(-2px); border-color: var(--tile-border-hover); }
.timeline-chip:active:not(:disabled) { transform: scale(.96); }
/* disabled = score saved before report snapshots existed: still worth
   showing the number, but it mustn't look or feel like a button */
.timeline-chip:disabled { cursor: default; }

/* one line, one size, one colour — only the weight tells them apart */
.timeline-score {
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.timeline-note {
  font-size: 15px;
  font-weight: 400;
  line-height: 1;
}
/* a check this old is the one thing worth actually flagging */
.timeline-chip.stale { color: var(--amber); }

/* ---------- Results screen ----------
   The title carries the hero's brand serif — it's the other big moment in
   the app, so it gets the same voice as the homepage headline. The old
   "Your results" eyebrow above it was saying the identical words twice. */
.results-title {
  font-family: "Fraunces", ui-serif, "New York", Georgia, serif;
  font-size: 44px;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.08;
  margin-bottom: 22px;
}

/* The two gradient tiles that close the page. Same tiles as the homepage,
   so they inherit their own prism sheens; only the outer spacing differs —
   they sit inside a .card here, not on the open page. */
.hero-row-next {
  max-width: none;
  margin: 0;
}

@media (max-width: 640px) {
  .results-title { font-size: 34px; }
}

/* ---------- Full-screen toggle ---------- */

.fs-btn {
  position: fixed;
  left: 22px;
  bottom: 22px;
  z-index: 40;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--shadow-card);
  transition: transform .4s var(--ease), box-shadow .4s var(--ease);
}
.fs-btn:hover { transform: scale(1.1); }
.fs-btn:active { transform: scale(.94); }

/* ---------- Near reading ---------- */

.nearread-text {
  font-family: Georgia, "Times New Roman", serif;
  color: #111;
  max-width: 560px;
  text-align: center;
  line-height: 1.5;
}

/* ---------- Photostress ---------- */

.ps-flash {
  width: 100%;
  min-height: 38vh;
  background: #ffffff;
  display: grid;
  place-items: center;
}
#psCountdown {
  font-size: 84px;
  font-weight: 800;
  color: rgba(0,0,0,.14);   /* faint on purpose — the point is the bright white */
  font-variant-numeric: tabular-nums;
}

/* stagger for the two extra cards */
.menu-card:nth-child(15) { animation-delay: .45s; }
.menu-card:nth-child(16) { animation-delay: .48s; }

/* Listening indicator in the dock: grey = mic closed (Iris talking or not a
   question), pulsing green = ears open, speak now. */
.mic-dot {
  width: 9px;
  height: 9px;
  margin-left: 6px;
  border-radius: 50%;
  background: var(--line);
  flex-shrink: 0;
  transition: background .3s ease;
}
.mic-dot.listening {
  background: var(--green);
  animation: mic-dot-pulse 1.6s ease-in-out infinite;
}
@keyframes mic-dot-pulse {
  50% { opacity: .4; box-shadow: 0 0 0 4px color-mix(in srgb, var(--green) 25%, transparent); }
}

/* ---------- Help button (lives inside the dock) + panel ---------- */

/* Also carries .icon-btn, so it inherits the same circle, border and surface
   as its neighbours — this only styles the glyph. */
.help-btn {
  color: var(--ink-soft);
  font-size: 19px;
  font-weight: 700;
  line-height: 1;
}

.help-panel {
  position: fixed;
  right: 18px;
  bottom: 84px;
  z-index: 41;
  width: min(340px, calc(100vw - 44px));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 50px rgba(0,0,0,.28);
  padding: 22px 24px;
  animation: rise .4s var(--ease);
}
.help-panel h4 {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 6px;
}
.help-panel h4:not(:first-child) { margin-top: 18px; }
.help-panel p {
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0;
}
.help-panel .btn { margin-top: 16px; }

/* ---------- Results: next steps + actions + exercise promo ---------- */

.subhead-left { text-align: left; max-width: 1200px; margin-left: auto; margin-right: auto; padding: 0 4px; }

.results-actions { flex-wrap: wrap; }

.next-steps {
  background: var(--bg-soft);
  border-radius: var(--radius-md);
  padding: 20px 24px;
  margin: 6px 0 22px;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.next-steps strong { color: var(--ink); }
.next-steps ul { margin: 10px 0 0; padding-left: 20px; }
.next-steps li { margin-bottom: 6px; }


/* ---------- Exercise card + pill ---------- */

/* Workout cards keep their violet mock tint, but inherit the neutral border
   (resting + hover) from .menu-card, so both carousels sit on the same
   visual footing. */
.menu-card-ex .menu-mock { background: linear-gradient(135deg, rgba(113,46,237,.10), rgba(46,125,79,.08)); }

/* ---------- Eye exercises screen ---------- */

.ex-stage {
  position: relative;
  width: min(560px, 92vw);
  height: min(340px, 62vw);
  display: grid;
  place-items: center;
}
.ex-dot {
  position: absolute;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #7b74ff, #4a42d1);
  box-shadow: 0 4px 16px rgba(74,66,209,.4);
  will-change: left, top;
}
.ex-focus { font-size: 68px; user-select: none; }

.ex-timer {
  font-size: 42px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  margin-top: 10px;
}

/* How much of THIS exercise is left. The thin bar at the very top of the
   page carries the other half of the story — how far through the set. */
.ex-progress {
  width: min(280px, 60vw);
  height: 5px;
  margin: 18px auto 4px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}
.ex-progress-fill {
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background: var(--violet);
  transition: width .25s linear;
}

/* ---------- Exit button (top-left, during any test) ---------- */

.exit-btn {
  position: fixed;
  right: 22px;
  top: 14px;
  z-index: 45;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 40px;
  padding: 0 16px 0 13px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink-soft);
  font-family: var(--font);
  font-size: 14.5px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-card);
  transition: transform .4s var(--ease), color .3s ease, border-color .3s ease;
}
.exit-btn:hover { transform: scale(1.04); color: var(--ink); border-color: rgba(128,124,116,.5); }
.exit-btn:active { transform: scale(.96); }

/* ---------- Booking chooser ---------- */

.chooser-backdrop {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: rgba(0, 0, 0, .45);
  display: grid;
  place-items: center;
  padding: 20px;
}

.book-chooser {
  width: min(430px, 100%);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: 0 24px 80px rgba(0,0,0,.35);
  padding: 26px 28px 22px;
  animation: rise .4s var(--ease);
}

.book-chooser h4 {
  font-size: 20px;
  letter-spacing: -0.02em;
  margin: 0 0 4px;
}

.book-chooser-sub {
  font-size: 14px;
  color: var(--ink-faint);
  margin: 0 0 16px;
  line-height: 1.5;
}

.book-chooser-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}

.chooser-row {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  background: var(--bg);
  border: 1px solid var(--line);
  color: var(--ink);
  font-size: 15.5px;
  font-weight: 600;
  text-decoration: none;
  transition: transform .35s var(--ease), border-color .3s ease;
}
.chooser-row:hover { transform: translateX(3px); border-color: rgba(128,124,116,.45); }
.chooser-name { flex: 1; min-width: 0; }
.chooser-row .chooser-sub {
  display: block;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-faint);
  margin-top: 2px;
}
.chooser-arrow { flex-shrink: 0; color: var(--ink-faint); }

/* The white app-icon tile: fixed 40px rounded square, brand-colour monogram.
   Always white so every brand sits on the same canvas — like an app grid. */
.chooser-ic {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 11px;
  display: grid;
  place-items: center;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, .08);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .06);
  font-size: 16px;
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1;
}
.chooser-ic-generic { color: var(--ink-faint); font-weight: 600; }

/* ---------- Chooser footer (close + optician link) ---------- */

.chooser-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.chooser-footer a {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-faint);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.chooser-footer a:hover { color: var(--ink); }
