/* ============================================================
   The Official Couples Daily — PWA stylesheet
   Brand tokens from PRD §12. No CDN, no build step: the app must
   render identically with the network switched off.
   ============================================================ */

/* ---------- Fonts (self-hosted; Google Fonts CDN is offline-hostile) ---------- */
@font-face {
  font-family: 'Fraunces';
  src: url('fonts/fraunces-600.woff2') format('woff2-variations');
  font-weight: 500 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('fonts/inter-400.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */
:root {
  /* ----------------------------------------------------------------
     WARM SAND — the light theme. Chosen by Dee, 28 Aug 2026.

     Every colour in this stylesheet resolves through a token below.
     There are no bare rgba() literals in the rules any more, which is
     what makes a theme a token swap rather than a 1,100-line edit.

     The --navy-* names are kept ON PURPOSE. They are referenced ~90
     times across the rules and renaming them would turn a readable
     colour change into an unreviewable whole-file diff. They are
     surface roles now, not literal navy. Rename them in their own PR
     if it is ever worth the churn.
     ---------------------------------------------------------------- */

  /* Surfaces, lightest page ground to raised card */
  --navy-deep: #f3ece2; /* page ground, behind the app shell */
  --navy: #faf6f0;      /* app background — the warm sand itself */
  --navy-card: #ffffff; /* raised cards */
  --navy-line: #e6dccd; /* hairlines and dividers */

  /* Brand accents.
     DARKENED DELIBERATELY. The originals (--aqua #4fd1b5, --gold #d4af37)
     were picked against a near-black navy and fail contrast on a light
     ground — #4fd1b5 on white is roughly 1.7:1, well under the 4.5:1
     minimum for body text. These are the same hues carried down in
     luminance until they pass. Do not "restore" the brighter values. */
  /* These two are set to the exact value that clears 4.5:1 against the CREAM
     ground (the harder of the two backgrounds), not against white. Both are
     used for small text - the episode eyebrow and the "Tip of the day" label -
     so large-text 3:1 is not the applicable bar. Verified: gold 4.53:1,
     muted 4.56:1 on cream. Lighten either and small text stops being legible. */
  --gold: #8e6c16;
  --aqua: #146e76;
  --teal: #146e76;
  --teal-deep: #0a4147; /* for dark chips that sit ON the light ground */
  --mauve: #8d6fa6;

  /* Text */
  --ink: #1a2b3d;
  --muted: #647283;
  --on-accent: #ffffff; /* text/glyphs sitting on an accent fill */
  --on-gold: #ffffff;   /* text on a --gold fill. White wins here: against the
                           darkened gold it measures 4.88:1, where the original
                           near-black brown only reaches 3.32:1. */
  --danger: #b3261e;

  /* Derived overlay roles. Every former rgba() literal maps to one of
     these, so a future theme only edits this block. */
  --ink-rgb: 26, 43, 61;
  --accent-rgb: 20, 110, 118;
  --gold-rgb: 142, 108, 22;
  --mauve-rgb: 141, 111, 166;

  --surface-1: rgba(var(--ink-rgb), 0.031);
  --surface-2: rgba(var(--ink-rgb), 0.037);
  --surface-3: rgba(var(--ink-rgb), 0.074);
  --surface-4: rgba(var(--ink-rgb), 0.099);
  --surface-5: rgba(var(--ink-rgb), 0.124);
  --line-1: rgba(var(--ink-rgb), 0.05);
  --line-2: rgba(var(--ink-rgb), 0.062);
  --line-3: rgba(var(--ink-rgb), 0.087);
  --line-4: rgba(var(--ink-rgb), 0.112);
  --line-5: rgba(var(--ink-rgb), 0.155);
  --ink-90: rgba(var(--ink-rgb), 0.9);
  --ink-85: rgba(var(--ink-rgb), 0.85);
  --ink-80: rgba(var(--ink-rgb), 0.8);
  --scrim: var(--line-3);
  --scrim-strong: var(--line-3);
  --shadow-sm: 0 2px 6px rgba(var(--ink-rgb), 0.09);
  --shadow-md: 0 10px 24px rgba(var(--ink-rgb), 0.09);
  --shadow-lg: 0 18px 40px rgba(var(--ink-rgb), 0.11);

  --tab-h: 68px;
  --max-w: 480px;

  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 24px;
  --r-xl: 30px;
  --r-pill: 999px;

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

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  color-scheme: light;
}

/* ---------- Reset ---------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
}
html {
  -webkit-text-size-adjust: 100%; /* respect dynamic type, block orientation zoom */
}
body {
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  background: var(--navy-deep);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
  touch-action: manipulation;
  /* The app shell owns scrolling: the page itself never scrolls, so the tab bar
     stays pinned and iOS doesn't rubber-band the whole document. */
  overflow: hidden;
}
img {
  display: block;
  max-width: 100%;
}
button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
a {
  color: var(--aqua);
  text-decoration: none;
}
:focus-visible {
  outline: 2px solid var(--aqua);
  outline-offset: 2px;
  border-radius: 6px;
}
.display {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- App frame ---------- */
#app {
  position: relative;
  width: 100%;
  max-width: var(--max-w);
  /* --app-h is measured from window.innerHeight (see setViewportHeight in
     app.js): correct in a top-level tab, in standalone, and inside an iframe,
     where dvh can resolve against the wrong viewport. dvh is the fallback. */
  height: 100dvh;
  height: var(--app-h, 100dvh);
  margin: 0 auto;
  background: linear-gradient(180deg, var(--navy) 0%, var(--navy-deep) 60%);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
@media (min-width: 520px) {
  body {
    background:
      radial-gradient(1200px 600px at 50% -10%, var(--navy-line) 0%, transparent 60%), var(--navy-deep);
  }
  #app {
    box-shadow: 0 0 0 1px var(--surface-2), var(--shadow-lg);
  }
}

/* ---------- Screens ---------- */
main#view {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  padding: calc(var(--safe-top) + 12px) 20px calc(var(--tab-h) + var(--safe-bottom) + 24px);
  scrollbar-width: none;
}
main#view::-webkit-scrollbar {
  width: 0;
}
.screen {
  animation: fade 0.22s var(--ease);
}
@keyframes fade {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------- Type helpers ---------- */
.kicker {
  color: var(--gold);
  letter-spacing: 0.28em;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
}
.eyebrow {
  color: var(--muted);
  letter-spacing: 0.2em;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
}
h1.title {
  font-size: 1.5rem;
  margin: 2px 0 0;
}
h2.title {
  font-size: 1.4rem;
  margin: 2px 0 0;
}
.sub {
  color: var(--muted);
  font-size: 0.78rem;
  margin: 6px 0 0;
  line-height: 1.5;
}

/* A .sub that has turned into a failure message. --gold is what .badge.warn
   already uses for "something needs your attention", so the app has one
   warning colour rather than two. Reused here because the checkout sheet's
   reassurance line ("Payment is taken securely by Stripe") is replaced in
   place when checkout cannot be opened, and muted grey would read as more
   reassurance rather than less. */
.sub.warn {
  color: var(--gold);
}

/* ---------- Header row ---------- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* ---------- Centred brand mark (Today screen) ----------
   Dee chose the centred placement on 28 Aug 2026. The mark is an arched
   COUPLES / C / HQ lockup: it is designed to be looked at, and it loses its
   arch definition below roughly 80px, which is why it leads the screen rather
   than sitting small in a corner.

   6 Oct 2026: Dee asked for the mark larger (104px -> 150px) and the round
   profile button removed from this row (Profile is now a tab), so the mark is
   the only thing in it. The source PNG is 233px wide, so 150px stays sharp up
   to ~1.5x screens and softens slightly beyond; ask for a larger export from
   the designer if that ever shows. */
.topbar--brand {
  position: relative;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 4px;
}
.topbar--brand .brandblock {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}
.topbar--brand .brandmark {
  width: 150px;
  height: auto;
  margin: 6px 0 4px;
  /* The mark is a fixed-size decorative asset; stop it flexing on small screens. */
  flex: 0 0 auto;
}
/* Very narrow phones: keep the mark from dominating the first screen. */
@media (max-width: 340px) {
  .topbar--brand .brandmark {
    width: 120px;
  }
}
/* Users who ask for less motion still get the mark; it is static either way. */
@media (prefers-reduced-motion: reduce) {
  .screen {
    animation: none;
  }
}

/* ---------- Cards ---------- */
.card {
  background: var(--surface-1);
  border: 1px solid var(--line-3);
  border-radius: var(--r-md);
}
/* hero episode */
.hero {
  display: block;
  width: 100%;
  text-align: left;
  margin-top: 8px;
  border-radius: var(--r-xl);
  overflow: hidden;
  border: 1px solid var(--line-3);
  background: linear-gradient(135deg, var(--navy-card), var(--navy-deep));
  position: relative;
  padding: 20px;
  min-height: 178px;
  transition: transform 0.15s var(--ease);
}
.hero:active {
  transform: scale(0.99);
}
.hero .ep-title {
  font-size: 1.55rem;
  line-height: 1.1;
  margin-top: 6px;
  padding-right: 92px;
}
.hero .coach {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  overflow: hidden;
  box-shadow: 0 0 0 2px var(--aqua), 0 0 18px rgba(var(--accent-rgb), 0.5);
}
.hero .coach img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero .playrow {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.play-dot {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--aqua);
  color: var(--navy-deep);
  display: grid;
  place-items: center;
  flex: 0 0 auto;
}
.hero .playmeta {
  font-size: 0.78rem;
  color: var(--ink-90);
  font-weight: 500;
}
.hero .resume-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: var(--surface-5);
}
.hero .resume-bar > i {
  display: block;
  height: 100%;
  background: var(--aqua);
}

/* ---------- Home screen (Aura-style, Dee 6 Oct 2026) ----------
   One calm card and two actions, centred. The photo is the largest thing on
   the screen on purpose: Dee asked to keep it, and it is what makes the card
   read as "a message from Dee" rather than a media tile. */
.hero--home {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-top: 28px;
  padding: 28px 22px 26px;
  min-height: 0;
  background: var(--navy-card);
  box-shadow: var(--shadow-sm);
}
.hero--home .coach {
  position: static;
  width: 112px;
  height: 112px;
  margin-bottom: 18px;
  box-shadow: 0 0 0 3px var(--navy-card), 0 0 0 5px rgba(var(--accent-rgb), 0.35);
}
.hero--home .kicker {
  color: var(--gold);
}
.hero--home .ep-title {
  padding-right: 0;
  margin-top: 8px;
  font-size: 1.7rem;
}
.hero--home .playrow {
  position: static;
  margin-top: 22px;
}
.hero--home .play-dot {
  width: 48px;
  height: 48px;
  color: var(--on-accent);
}
.home-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin-top: 26px;
  text-align: center;
}
.home-actions .btn {
  max-width: 280px;
}
.home-actions .home-list-link {
  font-size: 0.95rem;
  padding: 6px 4px;
}
.home-actions .sub {
  margin: 0;
}
.free-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: var(--r-pill);
  background: rgba(var(--accent-rgb), 0.12);
  color: var(--aqua);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.link-btn {
  color: var(--aqua);
  font-size: 0.78rem;
  font-weight: 500;
}

/* ---------- Course cards ---------- */
.course {
  display: block;
  width: 100%;
  text-align: left;
  border-radius: var(--r-md);
  overflow: hidden;
  border: 1px solid var(--line-3);
  margin-top: 16px;
  transition: transform 0.15s var(--ease);
}
.course:active {
  transform: scale(0.99);
}

/* ---------- Episode list: search, Saved filter, hearts (6 Oct 2026) ---------- */
.ep-tools {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 16px;
}
.ep-search {
  width: 100%;
  padding: 12px 14px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-4);
  background: var(--navy-card);
  color: var(--ink);
  font: inherit;
  font-size: 1rem; /* 16px or more stops iOS zooming the page on focus */
}
.ep-search:focus {
  outline: 2px solid var(--aqua);
  outline-offset: 1px;
}
.ep-filter {
  display: flex;
  gap: 8px;
}
.ep-filter button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: var(--r-pill);
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--line-4);
}
.ep-filter button svg {
  width: 15px;
  height: 15px;
}
.ep-filter button[aria-pressed='true'] {
  color: var(--on-accent);
  background: var(--aqua);
  box-shadow: none;
}
.ep-empty {
  margin-top: 18px;
}
.ep-row {
  position: relative;
}
.ep-row[hidden] {
  display: none;
}
.heart {
  position: absolute;
  right: 10px;
  bottom: 8px;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--muted);
}
.heart[aria-pressed='true'] {
  color: var(--danger);
}
.heart[aria-pressed='true'] svg path {
  fill: currentColor;
}
.course-head {
  height: 78px;
  display: flex;
  align-items: center;
  padding: 0 16px;
}
.course-head.aqua {
  background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.3), var(--navy-card));
}
.course-head.teal {
  background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.6), var(--navy-card));
}
.course-head.mauve {
  background: linear-gradient(90deg, rgba(var(--mauve-rgb), 0.4), var(--navy-card));
}
.course-head span {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: 1.1rem;
  line-height: 1.15;
}
.course-body {
  background: var(--navy-card);
  padding: 12px 16px 14px;
}
.course-body p {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--ink-80);
}
.meta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.74rem;
  color: var(--muted);
  margin-top: 8px;
  gap: 10px;
}
.progress {
  height: 6px;
  margin-top: 8px;
  border-radius: var(--r-pill);
  background: var(--line-3);
  overflow: hidden;
}
.progress > i {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--aqua);
  transition: width 0.3s var(--ease);
}
.cta-inline {
  color: var(--aqua);
  font-size: 0.76rem;
  font-weight: 500;
  margin-top: 8px;
  display: inline-block;
}

/* ---------- Lesson list ---------- */
.list {
  border-radius: var(--r-md);
  background: var(--surface-1);
  border: 1px solid var(--line-3);
  overflow: hidden;
  margin-top: 12px;
}
.list > * + * {
  border-top: 1px solid var(--line-2);
}
.row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  padding: 13px 16px;
  font-size: 0.88rem;
}
.row .grow {
  flex: 1 1 auto;
  min-width: 0;
}
.row .muted {
  color: var(--muted);
  font-size: 0.78rem;
}
.row .tick {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1.5px solid var(--line-5);
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  color: transparent;
  font-size: 12px;
}
.row .tick.on {
  background: var(--aqua);
  border-color: var(--aqua);
  color: var(--navy-deep);
}
.row.locked {
  opacity: 0.55;
}

/* ---------- Books ---------- */
.shelf {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
  margin-top: 18px;
}
.book {
  text-align: left;
}
.book .cover {
  aspect-ratio: 10 / 16;
  border-radius: var(--r-sm);
  overflow: hidden;
  border: 1px solid var(--line-3);
  box-shadow: var(--shadow-md);
  background: var(--navy-card);
  position: relative;
}
.book .cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.book .name {
  font-size: 0.74rem;
  color: var(--muted);
  margin-top: 8px;
  line-height: 1.35;
}
.book.wide {
  grid-column: span 2;
  justify-self: center;
  width: 50%;
}
/* Locked book covers: padlock and a faint wash, deliberately NOT blurred.
   This carried `backdrop-filter: blur(1px)` until Dee reported the covers on
   the live site as "all blurred" - she read her own books as broken artwork
   rather than as locked content. If the owner misreads the signal, a first-time
   visitor certainly will: 1px over an 8.7% scrim is too slight to say "locked"
   and lands as "poor quality" instead.
   It is also backwards commercially. Five real books with her name on them are
   among the strongest trust signals on the page, so obscuring them hides the
   very thing that makes someone want to join. The padlock does the work; the
   covers stay sharp. Do not reinstate the blur. */
.lock-badge {
  position: absolute;
  inset: 0;
  background: var(--scrim);
  display: grid;
  place-items: center;
}

/* ---------- Paywall ---------- */
.price-card {
  margin-top: 20px;
  border-radius: var(--r-lg);
  border: 1px solid rgba(var(--accent-rgb), 0.4);
  background: linear-gradient(180deg, var(--navy-card), var(--navy-deep));
  padding: 20px;
  text-align: center;
  position: relative;
  overflow: hidden;
}
.price-card::after {
  content: '';
  position: absolute;
  top: -32px;
  right: -32px;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: rgba(var(--accent-rgb), 0.12);
  filter: blur(28px);
}
.price {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 4px;
}
.price .amount {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 700;
  font-size: 3rem;
  line-height: 1;
}
.price .per {
  color: var(--muted);
  font-size: 0.85rem;
  margin-bottom: 8px;
}
.plan-toggle {
  display: flex;
  gap: 6px;
  padding: 4px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  margin: 0 auto 16px;
  max-width: 280px;
}
.plan-toggle button {
  flex: 1;
  padding: 8px 10px;
  border-radius: var(--r-pill);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  line-height: 1.25;
}
/* The saving badge on the Annual tab. It has to read while the OTHER tab is
   selected, so it carries its own colour rather than inheriting the tab's
   muted state. */
.plan-toggle .plan-save {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--gold);
  text-transform: uppercase;
}
.plan-toggle button[aria-pressed='true'] .plan-save {
  color: var(--aqua);
}
.plan-toggle button[aria-pressed='true'] {
  background: rgba(var(--accent-rgb), 0.16);
  color: var(--aqua);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), 0.4);
}
.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 14px 18px;
  border-radius: var(--r-pill);
  background: var(--aqua);
  color: var(--navy-deep);
  font-weight: 600;
  transition: transform 0.15s var(--ease), opacity 0.15s var(--ease);
}
.btn:active {
  transform: scale(0.99);
}
.btn[disabled] {
  opacity: 0.6;
  pointer-events: none;
}
.btn.ghost {
  background: transparent;
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--line-5);
}
.btn.gold {
  background: var(--gold);
  /* NOT --gold. The original was a literal #22190a (near-black brown) and a
     blanket hex-to-token pass mapped it onto --gold, which made the label the
     same colour as the button under it. Gold is too light a fill for white
     text, so this stays a dark ink rather than --on-accent. */
  color: var(--on-gold);
}
.checklist {
  margin-top: 20px;
  display: grid;
  gap: 12px;
}
.checklist div {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  font-size: 0.88rem;
  line-height: 1.4;
}
.checklist .mark {
  color: var(--aqua);
}

/* ---------- Overlay host (player + sheets) ----------
   Fixed and centred so overlays line up with the app column on desktop, and
   click-through when empty so it never swallows taps on the page beneath. */
#overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  justify-content: center;
  pointer-events: none;
}
#overlay > * {
  pointer-events: auto;
}

/* ---------- Player ---------- */
.player {
  position: relative;
  width: 100%;
  max-width: var(--max-w);
  height: 100%;
  background: var(--navy-deep);
  display: flex;
  flex-direction: column;
  padding: calc(var(--safe-top) + 8px) 20px calc(var(--safe-bottom) + 20px);
  overflow-y: auto;
  animation: rise 0.24s var(--ease);
}
@keyframes rise {
  from {
    transform: translateY(16px);
    opacity: 0;
  }
  to {
    transform: none;
    opacity: 1;
  }
}
.back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-size: 0.86rem;
  padding: 6px 0;
}
.stage {
  margin-top: 10px;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--line-3);
  background: linear-gradient(135deg, var(--navy-card), var(--navy-deep));
  position: relative;
  aspect-ratio: 16 / 9;
  display: grid;
}
.stage video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Literal black on purpose: this is video letterboxing, not a themed surface.
     It should stay black in any future theme. */
  background: #000;
}
.stage .card-art {
  padding: 20px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.stage .card-art .ep-title {
  font-size: 1.6rem;
  line-height: 1.12;
  margin-top: 6px;
}
.stage .byline {
  display: flex;
  align-items: center;
  gap: 12px;
}
.stage .byline .coach {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  overflow: hidden;
  box-shadow: 0 0 0 2px var(--aqua), 0 0 18px rgba(var(--accent-rgb), 0.45);
  flex: 0 0 auto;
}
.stage .byline .coach img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.stage .byline .who {
  font-size: 0.74rem;
  color: var(--muted);
  line-height: 1.35;
}
.stage .byline .who b {
  color: var(--ink-90);
  font-weight: 500;
}
.scrub {
  margin-top: 20px;
}
input[type='range'].seek {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 22px;
  background: transparent;
}
input[type='range'].seek::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--r-pill);
  background: linear-gradient(
    to right,
    var(--aqua) var(--pct, 0%),
    var(--surface-5) var(--pct, 0%)
  );
}
input[type='range'].seek::-moz-range-track {
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--surface-5);
}
input[type='range'].seek::-moz-range-progress {
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--aqua);
}
input[type='range'].seek::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -5px;
  border-radius: 50%;
  background: var(--aqua);
  box-shadow: var(--shadow-sm);
}
input[type='range'].seek::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: 0;
  border-radius: 50%;
  background: var(--aqua);
}
.times {
  display: flex;
  justify-content: space-between;
  font-size: 0.72rem;
  color: var(--muted);
  margin-top: -2px;
}
.controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 28px;
  margin-top: 18px;
}
.controls .big {
  width: 66px;
  height: 66px;
  border-radius: 50%;
  background: var(--aqua);
  color: var(--navy-deep);
  display: grid;
  place-items: center;
  box-shadow: 0 8px 24px rgba(var(--accent-rgb), 0.25);
}
.controls .side {
  color: var(--ink-85);
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
}
.player-actions {
  display: flex;
  gap: 10px;
  margin-top: 18px;
}
.chip {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 12px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  border: 1px solid var(--line-3);
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--ink-90);
}
.chip[aria-pressed='true'] {
  color: var(--aqua);
  border-color: rgba(var(--accent-rgb), 0.5);
  background: rgba(var(--accent-rgb), 0.12);
}
.desc {
  margin-top: 20px;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--ink-85);
}

/* ---------- Profile ---------- */
.profile-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding-top: 12px;
}
.profile-head .big-avatar {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 35%, var(--teal), var(--teal-deep));
  box-shadow: 0 0 0 2px var(--aqua);
  display: grid;
  place-items: center;
  font-size: 1.6rem;
  font-weight: 600;
  /* MUST be --on-accent, never --aqua: light text on a dark chip. On this
     theme --teal and --aqua resolve to the same accent, so an aqua initial
     disappears into its own background. */
  color: var(--on-accent);
}
.badge {
  margin-top: 8px;
  font-size: 0.74rem;
  color: var(--aqua);
  background: rgba(var(--accent-rgb), 0.1);
  border: 1px solid rgba(var(--accent-rgb), 0.3);
  border-radius: var(--r-pill);
  padding: 5px 12px;
}
.badge.warn {
  color: var(--gold);
  background: rgba(var(--gold-rgb), 0.1);
  border-color: rgba(var(--gold-rgb), 0.35);
}
.switch {
  position: relative;
  width: 44px;
  height: 26px;
  border-radius: var(--r-pill);
  /* On the dark theme an unchecked track was white-at-12% and the white knob
     read clearly against it. On a light ground that inverts into white-on-
     near-white, so the off state became almost invisible. The track is darkened
     and the knob given a lift shadow to restore the same read. */
  background: var(--surface-5);
  box-shadow: inset 0 0 0 1px var(--line-3);
  flex: 0 0 auto;
  transition: background 0.2s var(--ease);
}
.switch::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--on-accent);
  box-shadow: 0 1px 3px rgba(var(--ink-rgb), 0.28);
  transition: transform 0.2s var(--ease);
}
.switch[aria-checked='true'] {
  background: var(--aqua);
}
.switch[aria-checked='true']::after {
  transform: translateX(18px);
}

/* ---------- Tab bar ---------- */
nav.tabs {
  position: relative;
  flex: 0 0 auto;
  z-index: 50;
  height: calc(var(--tab-h) + var(--safe-bottom));
  padding-bottom: var(--safe-bottom);
  display: flex;
  background: var(--scrim-strong);
  backdrop-filter: blur(14px);
  border-top: 1px solid var(--line-3);
}
nav.tabs button {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  color: var(--muted);
  font-size: 10px;
  font-weight: 500;
  position: relative;
}
nav.tabs button[aria-current='page'] {
  color: var(--aqua);
}
nav.tabs .dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--aqua);
  opacity: 0;
}
nav.tabs button[aria-current='page'] .dot {
  opacity: 1;
}
nav.tabs .pip {
  position: absolute;
  top: 10px;
  right: calc(50% - 18px);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 0 2px var(--scrim-strong);
}

/* ---------- Toast / banners ---------- */
.banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  font-size: 0.78rem;
  background: rgba(var(--gold-rgb), 0.12);
  color: var(--gold);
  border-bottom: 1px solid rgba(var(--gold-rgb), 0.25);
}
.banner.info {
  background: rgba(var(--accent-rgb), 0.12);
  color: var(--aqua);
  border-color: rgba(var(--accent-rgb), 0.25);
}
.banner button {
  margin-left: auto;
  font-size: 0.76rem;
  font-weight: 600;
  text-decoration: underline;
}
#toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--tab-h) + var(--safe-bottom) + 16px);
  transform: translate(-50%, 12px);
  z-index: 90;
  background: var(--scrim-strong);
  border: 1px solid var(--line-4);
  color: var(--ink);
  padding: 10px 16px;
  border-radius: var(--r-pill);
  font-size: 0.8rem;
  box-shadow: var(--shadow-md);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
  max-width: calc(var(--max-w) - 40px);
  text-align: center;
}
#toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* ---------- Sheet (install help, checkout, sign-in) ---------- */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  pointer-events: auto;
  background: var(--scrim);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  animation: fade 0.2s var(--ease);
}
.sheet {
  width: 100%;
  max-width: var(--max-w);
  background: linear-gradient(180deg, var(--navy-card), var(--navy-deep));
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  border-top: 1px solid var(--surface-5);
  padding: 8px 20px calc(var(--safe-bottom) + 24px);
  animation: rise 0.24s var(--ease);
  max-height: 88dvh;
  overflow-y: auto;
}
.grabber {
  width: 40px;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--line-5);
  margin: 6px auto 14px;
}
.sheet h3 {
  margin: 0 0 6px;
  font-family: 'Fraunces', Georgia, serif;
  font-size: 1.2rem;
}
.sheet p {
  margin: 0;
  color: var(--muted);
  font-size: 0.84rem;
  line-height: 1.5;
}
.field {
  margin-top: 14px;
}
.field label {
  display: block;
  font-size: 0.74rem;
  color: var(--muted);
  margin-bottom: 6px;
}
.field input {
  width: 100%;
  padding: 13px 14px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  border: 1px solid var(--line-4);
  color: var(--ink);
  font-size: 16px; /* 16px stops iOS zoom-on-focus */
}
.field input:focus {
  outline: none;
  border-color: var(--aqua);
}
.steps {
  margin: 14px 0 0;
  padding-left: 18px;
  color: var(--ink-85);
  font-size: 0.85rem;
  line-height: 1.7;
}

/* "or" divider between the magic link and the Apple/Google buttons. Drawn with
   a border on the pseudo-elements rather than an <hr> so the label sits in the
   gap, and marked aria-hidden in the markup - it is decoration, and a screen
   reader announcing "separator or" between two buttons helps nobody. */
.or-rule {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 16px 0 12px;
  color: var(--muted);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.or-rule::before,
.or-rule::after {
  content: '';
  flex: 1;
  border-top: 1px solid var(--line-4);
}
.sheet .btn[disabled] {
  opacity: 0.6;
  pointer-events: none;
}

/* ---------- Empty / stub states ---------- */
.stub {
  margin-top: 14px;
  padding: 14px 16px;
  border-radius: var(--r-md);
  border: 1px dashed var(--line-5);
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.5;
}
footer.attrib {
  text-align: center;
  color: var(--muted);
  font-size: 0.7rem;
  margin-top: 26px;
  line-height: 1.6;
}

/* ---------------------------------------------------------------------
   The book reader.

   Sized for reading a chapter rather than scanning a screen. Everything
   here is in `em` or `ch` rather than pixels ON PURPOSE: a member who has
   scaled her phone's text up gets larger type and the same line length,
   which is the whole reason the books are reflowed text instead of page
   images. Fixing any of this in px silently undoes that.
   --------------------------------------------------------------------- */
.reader .book-p,
.reader .book-list {
  font-size: 1.06rem;
  line-height: 1.72;
  /* Around 65 characters, which is the comfortable range for continuous
     prose. Without a cap the text runs the full width of a tablet and the
     eye loses its place returning to the next line. */
  max-width: 34em;
  margin: 0 auto 1.1em;
}

.reader .book-h1,
.reader .book-h2,
.reader .book-subtitle {
  max-width: 34em;
  margin-left: auto;
  margin-right: auto;
}

.reader .book-h1 {
  font-family: 'Fraunces', Georgia, serif;
  font-size: 1.3rem;
  line-height: 1.3;
  margin: 2em auto 0.6em;
}

.reader .book-h2 {
  font-size: 1.02rem;
  letter-spacing: 0.04em;
  margin: 1.8em auto 0.5em;
}

.reader .book-subtitle {
  font-size: 1.06rem;
  font-style: italic;
  opacity: 0.85;
  margin: 0 auto 1.4em;
}

.reader .book-list {
  padding-left: 1.4em;
  max-width: 33em;
}

.reader .book-list li {
  margin-bottom: 0.5em;
}

/* The first block sits under the title rather than against it. */
.reader #reader-body > :first-child {
  margin-top: 0.5em;
}

/* Profile shows a person icon, not an initial: there is no name (Dee, 28 Oct 2026). */
.profile-head .big-avatar svg {
  width: 44%;
  height: 44%;
}
