/* ==========================================================================
   GRANITE

   Two tones, and one accent. Black and white for everything — and a single
   green that belongs to the coach's correction alone.

   THE RULE: the corrected value — the coach changing something — is the ONE
   thing on the site permitted to carry colour. It lands in the accent green.
   Nothing else is green: not a button, not a hover, not a heading. If the
   accent is used for decoration it stops meaning anything, and the meaning is
   the point. (The Invert button still flips the two tones; the accent is fixed.)

   Every neutral is derived from --fg with alpha, so the whole site still flips
   with two swaps; the accent stays put.
   ========================================================================== */

/* Self-hosted so the site makes no third-party request — the privacy promise
   has to hold for the marketing page too, not just the app. Both are variable
   fonts: one file each carries the whole weight axis. OFL 1.1, see fonts/OFL.txt. */
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/geist.woff2") format("woff2");
}

@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/geist-mono.woff2") format("woff2");
}

:root {
  --bg: #000000;
  --fg: #FFFFFF;

  color-scheme: dark;

  /* Mix percentages differ per theme: sRGB is not perceptually linear, so the
     same ratio that clears 4.5:1 on black falls to 2:1 on white. These values
     are the lowest that hold AA for the 10px mono labels in each direction. */
  --dim:    color-mix(in srgb, var(--fg) 62%, var(--bg));
  --faint:  color-mix(in srgb, var(--fg) 48%, var(--bg));
  --line:   color-mix(in srgb, var(--fg) 15%, var(--bg));
  --line-2: color-mix(in srgb, var(--fg) 32%, var(--bg));

  /* ── The app's colour system — imported exactly from Granite/Views/Theme.swift.
     Two tones stay the base. Hue is permitted only in the two roles the app
     permits, and never as chrome:
       DATA — success / warning / danger, where the hue *is* the datum: the
              coach's correction, a fatigue caution, an active pain score.
              Rendered as a filled chip with fixed dark ink, always beside a
              word, so colour is never the only cue.
       TAGS — the five neon accents, as the small category / phase DOTS that
              colour-code a session at a glance. A dot beside a mono label,
              never a fill that would compete with the correction.
     All fixed across both inversions: a datum must read the same whichever way
     the page is flipped, so it sits outside the invertible palette. Light-theme
     visibility is handled per-token below (the neons are tuned to glow on
     black; on white they need deepening). Values are the exact sRGB the app
     ships. */
  --data-success: #7FF3CB;   /* Color(0.498,0.953,0.796) — the app's mint success green */
  --data-warning: #FAB354;   /* Color(0.98,0.70,0.33) — fatigue / back-off     */
  --data-danger:  #FF7075;   /* Color(1.00,0.44,0.46) — active pain            */
  --on-data:      #04140b;   /* fixed near-black ink for text on any data fill */

  /* The one green the product uses — a cool, fresh MINT, deliberately never
     anywhere near Spotify's saturated mid-green. The correction chip (the signature
     the eye lands on) and the positive data hue share it, dark ink on top. */
  --correction: #7FF3CB;

  --cat-main:  #38EDD1;  /* main lift    · accumulation   (neon teal)   */
  --cat-acc:   #B887FF;  /* accessory    · deload         (neon purple) */
  --cat-cond:  #FFDB59;  /* conditioning · intensification (neon yellow)*/
  --cat-rehab: #FF70B8;  /* rehab / injury                (neon pink)   */
  --cat-warm:  #5CB8FF;  /* warm-up      · maintenance    (neon blue)   */

  --sans: "Geist", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --measure: 36rem;
  --gutter:  clamp(1.25rem, 5vw, 4.5rem);
  --ease:    cubic-bezier(0.16, 1, 0.3, 1);
}

[data-theme="light"] {
  --bg: #FFFFFF;
  --fg: #000000;

  color-scheme: light;

  --dim:   color-mix(in srgb, var(--fg) 66%, var(--bg));
  --faint: color-mix(in srgb, var(--fg) 54%, var(--bg));

  /* The neons are tuned to glow on black; a solid dot of them washes out on
     white. Deepen each ~38% toward black for the light inversion — the hue
     survives, the dot stays legible. Data fills keep their ink contrast on
     their own colour, so they need no per-theme change. */
  --cat-main:  color-mix(in srgb, #38EDD1 62%, #000);
  --cat-acc:   color-mix(in srgb, #B887FF 62%, #000);
  --cat-cond:  color-mix(in srgb, #FFDB59 62%, #000);
  --cat-rehab: color-mix(in srgb, #FF70B8 62%, #000);
  --cat-warm:  color-mix(in srgb, #5CB8FF 62%, #000);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-size: clamp(1rem, 0.95rem + 0.28vw, 1.125rem);
  line-height: 1.6;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  accent-color: var(--fg);
  text-rendering: optimizeLegibility;
  /* Hang quotes and list marks into the margin so text blocks read optically
     flush — the same principle as the wordmark's hung sidebearing. */
  hanging-punctuation: first allow-end last;
}

::selection { background: var(--fg); color: var(--bg); }
a { color: inherit; }

/* A designed focus ring, not a browser default: a crisp 2px rule in the ink
   colour, offset so it reads as intentional on either theme. */
:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; }

/* Page navigations use view transitions too, so the four pages read as one
   surface: the wordmark, the toggle and the footer persist and morph between
   pages instead of hard-reloading. Enabled only where motion is welcome. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}

.wordmark  { view-transition-name: granite-wordmark; }
.invert    { view-transition-name: granite-invert; }
footer     { view-transition-name: granite-footer; }
.doc__back { view-transition-name: granite-docback; }

/* The invert toggle is the site's one interaction, so it gets treated as a
   gesture, not a fade: the new tone wipes across on a hard edge from the control
   in the top corner — a single decisive sweep, the way the coach flips the sheet.
   Scoped to its own transition type so it never bleeds onto a navigation (those
   get the quiet default crossfade plus the morphs above). During the wipe the
   shared names are dropped, so the whole page sweeps as one piece rather than
   leaving the wordmark and footer to crossfade on their own. */
:root:active-view-transition-type(invert) .wordmark,
:root:active-view-transition-type(invert) .invert,
:root:active-view-transition-type(invert) footer,
:root:active-view-transition-type(invert) .doc__back {
  view-transition-name: none;
}

:root:active-view-transition-type(invert)::view-transition-old(root) { animation: none; }

:root:active-view-transition-type(invert)::view-transition-new(root) {
  animation: granite-wipe 560ms var(--ease);
  z-index: 1;
}

@keyframes granite-wipe {
  from { clip-path: inset(0 0 0 100%); }
  to   { clip-path: inset(0 0 0 0); }
}

/* --- layout ------------------------------------------------------------- */

.wrap { max-width: 66rem; margin: 0 auto; padding-inline: var(--gutter); }
.band { padding-block: clamp(4.5rem, 9vw, 10rem); border-top: 1px solid var(--line); }
.band--flush { border-top: 0; }
.band--tight { padding-block: clamp(2rem, 4vw, 3.25rem); }
.prose { max-width: var(--measure); }

/* Direction A — the marginal document grid. Content bands are indexed and offset
   right, so the page reads like a training log with numbered sections hanging in
   the margin. Wide screens only; below 64rem it collapses to the plain column. */
main { counter-reset: chapter; }
.band--doc { counter-increment: chapter; }

@media (min-width: 64rem) {
  .band--doc > .wrap {
    display: grid;
    grid-template-columns: 8rem minmax(0, 1fr);
    column-gap: clamp(1.5rem, 3vw, 3rem);
  }

  /* All real content — including the sheets — sits in the right column, clear of
     the rail so the sticky marker never overlaps it. */
  .band--doc > .wrap > * { grid-column: 2; }

  /* The running-index marker: the section number over its label, and it STICKS
     in the margin as you scroll its section — a live table of contents, the way
     a book runs its chapter header down the page. */
  .band--doc > .wrap::before {
    content: counter(chapter, decimal-leading-zero) "\A" attr(data-label);
    white-space: pre-line;
    grid-column: 1;
    grid-row: 1;
    align-self: start;
    position: sticky;
    top: clamp(1.5rem, 5vw, 2.75rem);
    font-family: var(--mono);
    font-size: 0.625rem;
    letter-spacing: 0.12em;
    line-height: 1.7;
    text-transform: uppercase;
    color: var(--faint);
  }
}

/* --- the skim path: the whole mechanic in one glance -------------------- */

.skim {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.9rem;
  font-family: var(--mono);
  font-size: clamp(0.8125rem, 0.74rem + 0.4vw, 1rem);
  letter-spacing: -0.01em;
}

.skim__step { color: var(--fg); }
.skim__arrow { color: var(--faint); }

/* On a narrow screen the steps stack and the arrows turn to point down. */
@media (max-width: 34rem) {
  .skim { flex-direction: column; align-items: flex-start; gap: 0.65rem; }
  .skim__arrow { transform: rotate(90deg); transform-origin: left center; margin-left: 0.2rem; }
}

/* Visible to assistive tech and search, off-screen for sighted users. Lets the
   wordmark stay the visual h1 while the h1 still carries a descriptive line. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
}

.skip:focus {
  left: var(--gutter);
  top: 1rem;
  z-index: 20;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--fg);
  padding: 0.75rem 1rem;
}

/* --- theme toggle ------------------------------------------------------- */

.invert {
  position: fixed;
  top: clamp(1rem, 2.5vw, 1.75rem);
  right: clamp(1rem, 2.5vw, 1.75rem);
  z-index: 10;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  background: var(--bg);
  border: 1px solid var(--line-2);
  padding: 0.5rem 0.75rem;
  cursor: pointer;
  transition: color 200ms var(--ease), border-color 200ms var(--ease);
}

/* The visible chip stays tight; the hit area extends to the 44px Apple/WCAG
   minimum via an invisible overlay, so touch targets don't force the look. */
.invert::after {
  content: "";
  position: absolute;
  inset: -7px -3px;
}

.invert:hover { color: var(--fg); border-color: var(--fg); }

.invert i {
  width: 8px;
  height: 8px;
  background: var(--fg);
  font-style: normal;
  border: 1px solid var(--fg);
}

[data-theme="light"] .invert i { background: var(--bg); }

/* --- product masthead --------------------------------------------------- */
/* A slim top menu surfacing the three angles. The base stays black-and-white;
   each product just carries a different accent — mint (athletes), purple
   (coaches), blue (return to sport) — drawn from the app's own palette. The
   accent is the correction chip + the lit menu item on that product's pages. */
[data-product="coaches"] { --correction: #B887FF; }
[data-product="rts"]     { --correction: #5CB8FF; }

.masthead { padding-top: clamp(1rem, 2.5vw, 1.75rem); }
.masthead__nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem 1.6rem;
  padding-right: 6.5rem;              /* clear the fixed invert button */
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.mh {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  white-space: nowrap;
  color: var(--faint);
  text-decoration: none;
  transition: color 180ms var(--ease);
}
.mh i { width: 6px; height: 6px; flex: none; }
.mh[data-p="athlete"] i { background: #7FF3CB; }
.mh[data-p="coaches"] i { background: #B887FF; }
.mh[data-p="rts"]     i { background: #5CB8FF; }
.mh:hover { color: var(--fg); }
/* current product is lit; athletes is the default when no product is set */
.mh[data-p="athlete"] { color: var(--fg); }
[data-product="coaches"] .mh[data-p="athlete"],
[data-product="rts"]     .mh[data-p="athlete"] { color: var(--faint); }
[data-product="coaches"] .mh[data-p="coaches"],
[data-product="rts"]     .mh[data-p="rts"] { color: var(--fg); }

/* --- hero --------------------------------------------------------------- */

.hero {
  padding-block: clamp(6rem, 14vw, 11rem) clamp(4rem, 8vw, 7rem);
  border-bottom: 1px solid var(--line);
}

/* The negative inline start hangs the G's sidebearing into the gutter, so the
   stem aligns optically with the text below it rather than mathematically. */
.wordmark {
  font-size: clamp(4rem, 17.6vw, 15rem);
  font-weight: 700;
  line-height: 0.86;
  /* Open, not pinched: -0.02em lets the letters breathe — the T and E in
     particular need the air — while keeping the mark denser than default. */
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0 0 0 -0.035em;
}

/* Optical kern for the I–T pair: two verticals with no sidebearing relief read
   tight even at even tracking, so the "TE" gets a hair of extra space before it.
   Wraps only "te" in the wordmark, so it never adds a line-break opportunity. */
.wm-kern { margin-left: 0.03em; }

/* Variable-axis intro. Geist carries its whole weight axis in one file, so the
   mark can arrive light and set to its full weight once, like stone settling.
   A single beat, not a loop. No-JS and reduced motion get the final weight with
   no animation — font-weight:700 above is the resting state either way. */
@keyframes wordmark-set {
  from { opacity: 0; font-variation-settings: "wght" 240; }
  to   { opacity: 1; font-variation-settings: "wght" 700; }
}

.js .wordmark.reveal { opacity: 0; transform: none; }

.js .wordmark.reveal.in {
  transition: none;
  animation: wordmark-set 1100ms var(--ease) both;
  animation-delay: var(--d, 120ms);
}

.kicker {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 clamp(1.5rem, 3vw, 2.25rem);
}

.thesis {
  font-size: clamp(1.25rem, 1rem + 1.2vw, 2rem);
  font-weight: 400;
  line-height: 1.32;
  letter-spacing: -0.028em;
  color: var(--dim);
  max-width: 24ch;
  margin: clamp(1.75rem, 4vw, 3rem) 0 0;
  text-wrap: balance;
}

.thesis strong { color: var(--fg); font-weight: 600; }

.hero__sub {
  max-width: 34rem;
  color: var(--dim);
  margin: 1.5rem 0 0;
}

.hero__cta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  margin-top: clamp(2rem, 4vw, 2.75rem);
}

.hero__cta-note {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

.hero__sheet {
  margin-top: clamp(3rem, 7vw, 5.5rem);
  max-width: 42rem;
}

/* The interactive week: plain outline toggles. Turning one on is the athlete's
   input; the coach's answer is the inverted chip that lands in the sheet — so
   inversion still means "the coach changed something", now triggered by you. */
.week {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.55rem;
  margin-bottom: 1.25rem;
}

.week__label {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-right: 0.35rem;
}

.week__toggle {
  display: inline-flex;
  align-items: center;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--dim);
  background: var(--bg);
  border: 1px solid var(--line-2);
  padding: 0.5rem 0.8rem;
  cursor: pointer;
  transition: color 180ms var(--ease), border-color 180ms var(--ease);
}

/* The dot colour-codes the input the way the app tags a captured week: an
   activity is conditioning (yellow), a flagged joint is rehab (pink). The label
   stays mono — the dot carries the hue, never the text. */
.week__toggle::before {
  content: "";
  width: 6px;
  height: 6px;
  margin-right: 0.5rem;
  border: 1px solid var(--dot, currentColor);
  background: transparent;
  transition: background-color 180ms var(--ease), border-color 180ms var(--ease);
}

.week__toggle[data-cat="cond"]  { --dot: var(--cat-cond); }
.week__toggle[data-cat="rehab"] { --dot: var(--cat-rehab); }

.week__toggle:hover { color: var(--fg); border-color: var(--line); }
.week__toggle:hover::before { border-color: var(--dot, var(--fg)); }

.week__toggle[aria-pressed="true"] { color: var(--fg); border-color: var(--fg); }
.week__toggle[aria-pressed="true"]::before { background: var(--dot, var(--fg)); border-color: var(--dot, var(--fg)); }

/* Two-column hero on wide screens: the pitch on the left, the correction sheet
   beside it on the right, so the product's one demo lands in the first screen
   instead of below the fold. Below 64rem it stacks — lead, then sheet. The grid
   supplies the space after the wordmark, so the thesis drops its own top margin. */
.hero__grid { margin-top: clamp(1.75rem, 4vw, 3rem); }
.hero__grid .thesis { margin-top: 0; }

@media (min-width: 64rem) {
  .hero__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
    gap: clamp(2.5rem, 5vw, 5rem);
    align-items: start;
    margin-top: clamp(2.5rem, 5vw, 4rem);
  }
  .hero__sheet { margin-top: 0; max-width: none; }
}

/* --- type --------------------------------------------------------------- */

h1, h2, h3 { font-weight: 600; margin: 0; letter-spacing: -0.035em; text-wrap: balance; }
p { margin: 0 0 1.15em; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

.section-title {
  font-size: clamp(2rem, 1.3rem + 2.8vw, 4rem);
  line-height: 1.0;
  margin: 0 0 1.75rem;
  max-width: 18ch;
  text-transform: uppercase;
  letter-spacing: -0.05em;
}

.eyebrow {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 1.75rem;
}

.lede {
  font-size: clamp(1.125rem, 1rem + 0.7vw, 1.625rem);
  line-height: 1.42;
  letter-spacing: -0.024em;
}

.muted { color: var(--dim); }

/* --- the sheet: the signature ------------------------------------------- */

.sheet {
  border: 1px solid var(--line-2);
  font-family: var(--mono);
  font-size: clamp(0.75rem, 0.68rem + 0.32vw, 0.875rem);
  line-height: 1.5;
  letter-spacing: 0;
  /* Real coaching-sheet data typography: figures line up in columns and the
     zero is slashed so it can't be misread as an O in a load or a set. */
  font-variant-numeric: tabular-nums slashed-zero;
}

.sheet__head {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 1.1rem clamp(1.1rem, 3vw, 1.75rem);
  border-bottom: 1px solid var(--line);
}

.rows { padding-inline: clamp(1.1rem, 3vw, 1.75rem); }

.row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.35rem 1.5rem;
  align-items: center;
  padding-block: 0.95rem;
}

.row + .row { border-top: 1px solid var(--line); }
.row__name { color: var(--dim); }
.row__val { text-align: right; }

/* Per-row reasoning in the coach's review queue: full width beneath the row,
   in the same faint register as the coach's note. The override mark is a
   strong in --fg — emphasis, not inversion. */
.row__why {
  grid-column: 1 / -1;
  margin: 0.5rem 0 0.1rem;
  color: var(--faint);
  line-height: 1.5;
}

.row__why strong { color: var(--fg); font-weight: 500; }

/* The correction is marked up as <del>/<ins> — the semantics of "this was
   changed to that" — so assistive tech announces the old value as removed and
   the new as inserted. The visual is drawn by hand (the animated ::after strike
   and the chip), so the browser's default line-through / underline are cleared. */
.was, .now { text-decoration: none; }

/* the correction — struck, then inverted */
.was {
  position: relative;
  color: var(--faint);
  margin-right: 0.6rem;
  white-space: nowrap;
}

.was::after {
  content: "";
  position: absolute;
  left: -0.14em;
  right: -0.14em;
  top: 52%;
  height: 1px;
  background: var(--fg);
  transform-origin: left center;
}

.now {
  display: inline-block;
  background: var(--correction);
  color: var(--on-data);
  padding: 0.22em 0.5em;
  white-space: nowrap;
}

/* Without JS the correction is simply already made: struck and chipped. The
   animated start state only exists once we know we can finish it. */
.js .was::after { transform: scaleX(0); }
.js .now { clip-path: inset(0 100% 0 0); }
.js .note { opacity: 0; }

.sheet.play .was::after {
  animation: strike 360ms var(--ease) forwards;
  animation-delay: var(--t, 0ms);
}

/* The chip wipes in left to right, the way a coach writes it, rather than
   fading up like a notification. */
.sheet.play .now {
  animation: chip 440ms var(--ease) forwards;
  animation-delay: calc(var(--t, 0ms) + 300ms);
}

.note {
  margin: 0;
  padding: 1.6rem clamp(1.1rem, 3vw, 1.75rem) 1.85rem;
  border-top: 1px solid var(--line);
  font-family: var(--sans);
  font-size: clamp(0.9375rem, 0.9rem + 0.25vw, 1.0625rem);
  line-height: 1.55;
  letter-spacing: -0.011em;
}

.sheet.play .note {
  animation: settle 640ms var(--ease) forwards;
  animation-delay: calc(var(--t, 0ms) + 700ms);
}

.note__by {
  display: block;
  margin-top: 1rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

/* ── App colour system, on the site ──────────────────────────────────────────
   Everything below borrows the app's exact rule: a hue rides a small dot or a
   filled data chip, always next to a word, and the coach's correction stays the
   only thing that ever inverts. */

/* Category dot — prefixes an exercise name, the way each row is tagged in the
   app. `--dot` is set per category; the name text stays in its mono grey. */
.row__name[data-cat] {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}
.row__name[data-cat]::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  background: var(--dot, var(--fg));
}
.row__name[data-cat="main"]  { --dot: var(--cat-main); }
.row__name[data-cat="acc"]   { --dot: var(--cat-acc); }
.row__name[data-cat="cond"]  { --dot: var(--cat-cond); }
.row__name[data-cat="rehab"] { --dot: var(--cat-rehab); }
.row__name[data-cat="warm"]  { --dot: var(--cat-warm); }

/* Phase tag — a dotted marker inside a sheet head, tinted by periodisation
   phase exactly as the app tints its phase tag + timeline. */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
}
.tag::before {
  content: "";
  width: 6px;
  height: 6px;
  background: var(--dot, currentColor);
}
.tag[data-phase="accumulation"]    { --dot: var(--cat-main); }
.tag[data-phase="intensification"] { --dot: var(--cat-cond); }
.tag[data-phase="deload"]          { --dot: var(--cat-acc); }
.tag[data-phase="maintenance"]     { --dot: var(--cat-warm); }

/* Data flags — the inputs that drove the week's changes, shown the app's way:
   a filled chip with fixed dark ink where the hue *is* the reading. Pain is
   danger, accumulated fatigue is warning, an off-platform session is the
   conditioning tag. Each carries a word, so the colour is never the only cue. */
.flags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.9rem clamp(1.1rem, 3vw, 1.75rem);
  border-bottom: 1px solid var(--line);
}
.flag {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.28em 0.55em;
  background: var(--flag, var(--data-danger));
  color: var(--on-data);
}
.flag--pain { --flag: var(--data-danger); }
.flag--warn { --flag: var(--data-warning); }
/* An off-platform activity isn't a data reading but a category, so it reads as
   the conditioning tag: a dot, not a fill. */
.flag--cond {
  background: transparent;
  color: var(--dim);
  padding-inline: 0;
}
.flag--cond::before {
  content: "";
  width: 6px;
  height: 6px;
  background: var(--cat-cond);
}

/* Block timeline — a phase dot above each week, and a key beneath the strip,
   so the twelve weeks colour-code by phase the way the app's timeline does. */
.block__n {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
}
.block__wk[data-phase]::before { display: none; }
.block__n .dot {
  width: 6px;
  height: 6px;
  flex: none;
}
.block__wk[data-phase="accumulation"]    .dot { background: var(--cat-main); }
.block__wk[data-phase="intensification"] .dot { background: var(--cat-cond); }
.block__wk[data-phase="deload"]          .dot { background: var(--cat-acc); }
.block__wk[data-phase="maintenance"]     .dot { background: var(--cat-warm); }

.block__key {
  list-style: none;
  margin: 1.1rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.4rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}
.block__key li {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
}
.block__key .dot { width: 7px; height: 7px; flex: none; }
.block__key [data-phase="accumulation"]    { background: var(--cat-main); }
.block__key [data-phase="intensification"] { background: var(--cat-cond); }
.block__key [data-phase="deload"]          { background: var(--cat-acc); }
.block__key [data-phase="maintenance"]     { background: var(--cat-warm); }

@keyframes strike { to { transform: scaleX(1); } }
@keyframes chip   { to { clip-path: inset(0 0 0 0); } }
@keyframes settle { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* --- reveal ------------------------------------------------------------- */

.js .reveal { opacity: 0; transform: translateY(14px); }

.js .reveal.in {
  opacity: 1;
  transform: none;
  transition: opacity 750ms var(--ease), transform 750ms var(--ease);
  transition-delay: var(--d, 0ms);
}

/* --- claims ------------------------------------------------------------- */

.claims { display: grid; border: 1px solid var(--line-2); }

@media (min-width: 54rem) {
  .claims { grid-template-columns: repeat(3, 1fr); }

  /* Scoped with .claims so it outranks the `.claim + .claim` top border
     declared later in this file. Unscoped, that rule wins on specificity tie
     and every card past the first draws a stray line along the container edge. */
  .claims .claim + .claim { border-top: 0; border-left: 1px solid var(--line); }

  /* Four recognitions sit 2×2. Three-up would strand the fourth card in a
     row of its own with the wrong two borders on it. */
  .claims--four { grid-template-columns: repeat(2, 1fr); }
  .claims--four .claim:nth-child(odd) { border-left: 0; }
  .claims--four .claim:nth-child(even) { border-left: 1px solid var(--line); }
  .claims--four .claim:nth-child(n + 3) { border-top: 1px solid var(--line); }
}

.claim { padding: clamp(1.5rem, 3.5vw, 2.5rem); }
.claim + .claim { border-top: 1px solid var(--line); }

.claim h3 {
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 1.15rem;
}

.claim p { font-size: 0.9375rem; line-height: 1.6; color: var(--dim); }
.claim strong { color: var(--fg); font-weight: 500; }

/* --- spec: the feature list as a table, not an icon grid ---------------- */

.spec {
  border: 1px solid var(--line-2);
  margin-top: 2.75rem;
  counter-reset: spec;
}

.spec__row {
  display: grid;
  grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
  gap: 0.6rem 2.5rem;
  padding: clamp(1.35rem, 2.5vw, 1.85rem) clamp(1.1rem, 3vw, 1.75rem);
  counter-increment: spec;
}

/* 01–05, the coach's decisions indexed like a real sheet. */
.spec dt::before {
  content: counter(spec, decimal-leading-zero);
  display: block;
  margin-bottom: 0.55rem;
  color: var(--faint);
}

.spec__row + .spec__row { border-top: 1px solid var(--line); }

@media (max-width: 44rem) {
  .spec__row { grid-template-columns: 1fr; }
}

.spec dt {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  padding-top: 0.28em;
}

.spec dd {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--dim);
}

.spec dd strong { color: var(--fg); font-weight: 500; }

/* --- the block: twelve weeks as one training-log object ----------------- */

.block__strip {
  list-style: none;
  margin: 2.75rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  border-top: 1px solid var(--line-2);
  border-left: 1px solid var(--line-2);
  font-family: var(--mono);
}

.block__wk {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: clamp(0.9rem, 2vw, 1.35rem);
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.block__n {
  font-size: 0.625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}

.block__load {
  font-size: clamp(0.8125rem, 0.72rem + 0.4vw, 1rem);
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 44rem) { .block__strip { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 22rem) { .block__strip { grid-template-columns: repeat(2, 1fr); } }

/* The two deloads are the coach's corrections, so they land as chips when the
   block scrolls into view. */
.js .block__strip.in .was::after { animation: strike 360ms var(--ease) 220ms forwards; }
.js .block__strip.in .now { animation: chip 440ms var(--ease) 540ms forwards; }

/* --- price -------------------------------------------------------------- */

.price { display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: center; }

@media (min-width: 54rem) { .price { grid-template-columns: 1fr 1fr; } }

.tier { border-top: 1px solid var(--line); padding-top: 1.35rem; }
.tier + .tier { margin-top: 2.25rem; }

.tier__amount {
  font-size: clamp(2rem, 1.4rem + 2.6vw, 3.5rem);
  font-weight: 700;
  letter-spacing: -0.05em;
  line-height: 1;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

.tier__label {
  display: block;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: 0.8rem;
}

/* Two clean tiers. The market rate is an anchor in a faint sub-line, not a
   headline the reader has to decode. */
.tier__per {
  font-size: 0.34em;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--faint);
  text-transform: none;
}

.tier__sub {
  margin: 1rem 0 0;
  max-width: 24rem;
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--dim);
}

.price__close {
  font-size: clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
  letter-spacing: -0.02em;
  color: var(--fg);
}

/* A single carried line — bigger than prose, quieter than a section title, no
   uppercase. Used to land the sentence a campaign lane turns on. */
.statement {
  margin: 0;
  max-width: 22ch;
  font-size: clamp(1.5rem, 1.1rem + 2vw, 2.75rem);
  line-height: 1.12;
  letter-spacing: -0.03em;
  color: var(--fg);
  text-wrap: balance;
}

/* Founding-member banner — a promotion, kept in the site's register: a hairline
   band, mono, two tones. No colour, no urgency theatre; the offer and the count
   carry it. Reused above the homepage price and at the foot of a campaign page. */
.founding {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem 1.5rem;
  border: 1px solid var(--line-2);
  padding: 1.15rem clamp(1.1rem, 3vw, 1.6rem);
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}
.founding__text {
  margin: 0;
  max-width: 46rem;
  font-family: var(--mono);
  font-size: clamp(0.75rem, 0.7rem + 0.3vw, 0.8125rem);
  line-height: 1.55;
  color: var(--dim);
}
.founding__eyebrow {
  display: block;
  margin-bottom: 0.4rem;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}
.founding__text strong { color: var(--fg); font-weight: 500; }
.founding__count {
  margin: 0;
  flex: none;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}
.founding__count b { color: var(--fg); font-weight: 500; font-variant-numeric: tabular-nums; }

.cta {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--fg);
  padding: 1rem 1.9rem;
  transition: background-color 180ms var(--ease), color 180ms var(--ease);
}

/* the button fills, it does not invert — inversion belongs to the coach */
.cta:hover { background: var(--fg); color: var(--bg); }

/* --- form: one field, one button ---------------------------------------- */

.form { margin-top: clamp(2.25rem, 4vw, 3rem); }

.form__label {
  display: block;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 0.9rem;
}

.form__group { display: flex; flex-wrap: wrap; gap: 0.75rem; }

.form__input {
  flex: 1 1 15rem;
  min-width: 0;
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--line-2);
  border-radius: 0;
  padding: 1rem 1.1rem;
  transition: border-color 180ms var(--ease);
}

.form__input::placeholder { color: var(--faint); }
.form__input:hover { border-color: var(--fg); }
.form__input:focus-visible { outline: 1px solid var(--fg); outline-offset: 2px; }

/* the button fills, it does not invert — inversion belongs to the coach */
.form__submit { cursor: pointer; border-radius: 0; }

.form__ack {
  margin: 1.25rem 0 0;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg);
}

.form__ack[hidden] { display: none; }

/* --- footer ------------------------------------------------------------- */

footer {
  border-top: 1px solid var(--line);
  padding-block: 2.5rem 3.5rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* --dim, not --faint: at 10px the fainter grey lands at 4.29:1 on white,
     just under AA. The footer stays quiet either way, and now clears 4.5:1. */
  color: var(--dim);
}

footer .wrap { display: flex; justify-content: space-between; gap: 1rem 2rem; flex-wrap: wrap; }
footer a { color: var(--dim); text-decoration: none; transition: color 180ms var(--ease); }
footer a:hover { color: var(--fg); }
/* Wraps: five or six links + the stitch don't fit one row on a phone, and an
   un-wrapped nav was pushing the page ~16px wide. Row-gap clears the links'
   negative hit-area margins so wrapped rows never overlap. */
footer nav { display: flex; flex-wrap: wrap; gap: 1.25rem 1.75rem; }

/* The Frame stitch — a quiet full-width line above the credit row. Two live
   products, one sentence: not uppercase like the rest of the footer, because it
   reads as a note, not a label. */
.footer__stitch {
  flex-basis: 100%;
  margin: 0 0 1.6rem;
  max-width: 54rem;
  text-transform: none;
  letter-spacing: 0.01em;
  font-size: 0.6875rem;
  line-height: 1.6;
  color: var(--dim);
}
.footer__stitch a { color: var(--fg); }
.footer__stitch a:hover { color: var(--dim); }

/* Extend the hit area to clear the 24px minimum target without changing the
   footer's visual rhythm — the negative margin gives the padding back to layout. */
footer nav a { padding-block: 0.6rem; margin-block: -0.6rem; }

/* --- document pages ----------------------------------------------------- */

.doc { padding-block: clamp(3rem, 6vw, 5.5rem) 5rem; }

.doc__back {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--faint);
  display: inline-block;
  margin-bottom: 3.5rem;
  padding-block: 0.4rem;
  transition: color 180ms var(--ease);
}

.doc__back:hover { color: var(--fg); }

.doc h1 {
  font-size: clamp(2.25rem, 1.5rem + 3vw, 4rem);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.055em;
  text-transform: uppercase;
  margin-bottom: 0.75rem;
}

.doc__stamp {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 3rem;
}

.doc h2 {
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 3rem 0 0.9rem;
  padding-bottom: 0.8rem;
  border-bottom: 1px solid var(--line);
}

.doc p { color: var(--dim); }
.doc .lede { color: var(--fg); }

/* Prose lists — the browser's default disc was the one un-designed element on the site.
   An en-dash marker in the faint grey: the same voice as the doc__stamp, no new shapes. */
.doc ul, .prose ul {
  list-style: none;
  padding-inline-start: 0;
  margin-block: 1rem;
}
.doc ul li, .prose ul li {
  position: relative;
  padding-inline-start: 1.4em;
  color: var(--dim);
  margin-block-end: .45em;
}
.doc ul li::before, .prose ul li::before {
  content: "\2013";           /* en dash */
  position: absolute;
  inset-inline-start: 0;
  color: var(--faint);
}

/* Prose tables — hairline rules only, both themes, inverts for free. */
.doc table, .prose table {
  width: 100%;
  border-collapse: collapse;
  margin-block: 1.5rem;
  font-size: .95rem;
}
.doc th, .prose th {
  font-weight: 600;
  color: var(--fg);
  text-align: left;
  padding: .7rem .75rem .7rem 0;
  border-bottom: 1px solid var(--line);
}
.doc td, .prose td {
  color: var(--dim);
  text-align: left;
  padding: .7rem .75rem .7rem 0;
  border-bottom: 1px solid var(--line-2);
}

/* Inline links carry a resting hairline; on hover or focus a solid rule draws
   across it left to right — the same drawing gesture as the strike, in miniature. */
.doc a {
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--line-2);
  position: relative;
}

.doc a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--fg);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 260ms var(--ease);
}

.doc a:hover::after,
.doc a:focus-visible::after { transform: scaleX(1); }

/* --- higher-contrast preference ----------------------------------------- */

/* Readers who ask their OS for more contrast get the muted tones pushed toward
   the ink — still two tones, just louder. The chip and rules stay as they are. */
@media (prefers-contrast: more) {
  :root {
    --dim:    color-mix(in srgb, var(--fg) 82%, var(--bg));
    --faint:  color-mix(in srgb, var(--fg) 70%, var(--bg));
    --line:   color-mix(in srgb, var(--fg) 34%, var(--bg));
    --line-2: color-mix(in srgb, var(--fg) 55%, var(--bg));
  }
}

/* --- print -------------------------------------------------------------- */

/* Printed, the site is a coaching document on paper: black ink, the chips still
   inverted so a correction still reads, the toggle gone, and everything the
   scroll animations would have hidden forced visible. */
@media print {
  :root,
  [data-theme="light"],
  [data-theme="dark"] { --bg: #FFFFFF; --fg: #000000; }

  html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .invert, .skip { display: none !important; }

  .js .reveal { opacity: 1 !important; transform: none !important; }
  .js .wordmark.reveal { opacity: 1 !important; }
  .js .was::after { transform: scaleX(1) !important; }
  .js .now { clip-path: none !important; }
  .js .note { opacity: 1 !important; }
}

/* --- motion preferences ------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .was::after { transform: scaleX(1); }
  .js .now { clip-path: none; }
  .js .note { opacity: 1; }
  .js .reveal { opacity: 1; transform: none; }

  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none !important; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
