/* Design tokens.
   Colour, spacing, radius and the type ramp are inherited from the app so the
   site and the screenshots on it read as one product — see
   apps/flowbyflow/src/constants/theme.ts and constants/themes/medical.ts, and
   docs/superpowers/specs/2026-07-31-site-redesign-design.md.

   Colour is inherited by RAMP, not by a single hex. #007AFF measures 4.02:1 on
   white and fails WCAG AA for normal text, so it is never used for text in
   light mode: --color-accent-strong (#0056B3, 7.04:1) carries links and labels
   and --color-accent is reserved for non-text accents, where 3:1 is the bar.
   In dark mode #0A84FF reaches 5.11:1 and carries text directly.

   --color-accent-strong (text role) and --color-accent-fill (filled-control
   role) are two separate tokens, not one reused value, because the two roles
   have different contrast floors: text needs 4.5:1 against the page, a filled
   control's label needs 4.5:1 against the FILL and the fill itself only needs
   3:1 against the page. In light mode both roles happen to pass at #0056B3
   (7.04:1 either way), but in dark mode they diverge: #0A84FF gives a white
   label only 3.65:1, which fails. --color-accent-fill's dark value (#006ADB)
   is therefore derived rather than lifted from the app's ramp — the app has
   no dark-mode blue that clears AA under a white label at this size. Do not
   "simplify" this back to a single token or restore the fill to a ramp value. */
:root {
  /* spacing — app scale, extended with two larger steps the app has no use for
     but a full-width page does */
  --space-1: 0.25rem;  /* 4  */
  --space-2: 0.5rem;   /* 8  */
  --space-3: 1rem;     /* 16 */
  --space-4: 1.5rem;   /* 24 */
  --space-5: 2rem;     /* 32 */
  --space-6: 3rem;     /* 48 */
  --space-7: 4rem;     /* 64 — site extension */
  --space-8: 6rem;     /* 96 — site extension */

  /* radius — app scale. One rule: controls md, panels lg, media xl. */
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-xl: 1rem;
  --radius-full: 999px;

  /* type ramp — app values at the small end, fluid up to desktop.
     --text-display / --text-title / --leading-normal carry the editorial
     scheme's values (formerly scheme-b.css's overrides of these three
     tokens) — the app's own smaller values were the losing scheme A and are
     gone, not merely overridden. */
  --text-display: clamp(2.25rem, 1.5rem + 3.1vw, 4rem);
  --text-title: clamp(1.875rem, 1.45rem + 1.5vw, 2.75rem);
  --text-heading: clamp(1.375rem, 1.3rem + 0.3vw, 1.625rem);
  --text-body: clamp(1.0625rem, 1rem + 0.2vw, 1.1875rem);
  --text-footnote: 1rem;
  /* Bespoke lead-paragraph size (hero's first paragraph only) — tokenised so
     verify-build's "every font-size anchors to a --text-* token" sweep has
     something to anchor to instead of a raw clamp() in the rule below. */
  --text-lead: clamp(1.1875rem, 1.05rem + 0.6vw, 1.5rem);

  --leading-tight: 1.2;
  --leading-normal: 1.7;
  --leading-loose: 1.75;

  /* typography — editorial scheme (formerly scheme-b.css), now the only
     scheme. Serif headings over a sans body, folded in whole: scheme A
     ("app continuity", one family, tighter rhythm) lost the comparison and
     is deleted, not preserved as an alternative. */
  --font-heading: 'Noto Serif', Georgia, 'Times New Roman', serif;
  --font-body: 'Noto Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --weight-display: 700;
  --weight-title: 700;
  --weight-heading: 600;
  --tracking-display: -0.012em;
  --tracking-title: -0.006em;
  --leading-display: 1.18;
  --section-rhythm: var(--space-8); /* 96px */

  /* colour */
  --bg: #ffffff;
  --fg: #1a1c2c;
  --muted: #5a607a;
  --card: #f5f7fb;
  --border: #e3e8f0;
  --color-accent: #007aff;         /* non-text accents only (3:1 bar) */
  --color-accent-strong: #0056b3;  /* accent-coloured TEXT on the page bg */
  --color-accent-fill: #0056b3;    /* filled control background */
  --on-accent: #ffffff;            /* label on --color-accent-fill */

  --maxw: 72rem;

  /* Intrinsic inline size of every screenshot under assets/screens (all ten
     are captured at 744px wide). A raster asset rendered above 1:1 is soft,
     so this is the hard ceiling for any shot treatment that does not
     otherwise cap itself — see `.shot.loupe img`. verify-build.mjs asserts
     the PNGs really are this wide, so a future capture at a different width
     fails the build rather than silently re-introducing upscaling. */
  --shot-native-inline-size: 46.5rem; /* 744px */
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1220;
    --fg: #eef1f8;
    --muted: #a3abc4;
    --card: #171a2b;
    --border: #2a2f47;
    --color-accent: #0a84ff;
    --color-accent-strong: #0a84ff; /* 5.11:1 on --bg — safe for text */
    --color-accent-fill: #006adb;   /* white label 5.14:1; fill vs page 3.62:1 */
    --on-accent: #ffffff;
  }
}

/* Typography tokens (--font-heading, --font-body, weight/tracking/leading)
   are declared in :root above — see
   docs/superpowers/specs/2026-07-31-site-redesign-design.md for the scheme
   comparison that chose these values. */
body { font-family: var(--font-body); }
/* Headings do NOT hyphenate here — see the narrow-viewport block at the end of
   this file, which turns it on below 640px only. It was briefly set globally
   and shipped a hero reading "Medication reminders that actu-ally arrive" at
   1440px, where there is no shortage of space at all: `hyphens: auto` lets the
   engine break a word whenever the line happens to end awkwardly, not only
   when the word cannot fit. The sweep that was supposed to catch this only
   asserted that no line escaped its box — which a hyphen is precisely what
   prevents — so it passed. */
h1, h2, h3 { font-family: var(--font-heading); }
.hero h1 {
  font-size: var(--text-display);
  font-weight: var(--weight-display);
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-display);
  margin: .2em 0;
}
.section h2 {
  font-size: var(--text-title);
  font-weight: var(--weight-title);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-tight);
}
.card h3, .steps h3 { font-size: var(--text-heading); font-weight: var(--weight-heading); }

/* .section.banded breaks out to the viewport edge via `calc(50% - 50vw)`.
   `100vw` includes the vertical scrollbar's track on engines that size it
   that way (Chrome), while the centred parent's own `50%` does not, so the
   breakout overshoots the visible area by half the scrollbar width whenever
   a vertical scrollbar is present — confirmed at 768/1024/1440px. Nothing
   meaningful lives in that sliver (it is the decorative full-bleed tint,
   never text), so clipping it on the root is the standard mitigation for
   this well-known vw/scrollbar mismatch; there is no scrollbar-free way to
   make `vw` and `%` agree in CSS alone. Set on BOTH `html` and `body`: the
   root is the element `document.documentElement.scrollWidth` measures, and
   relying on overflow propagation from `body` alone left the root's own
   scrollWidth unclipped in testing, so `html` carries the fix for that.
   `body` repeats `overflow-x: clip` for a different reason: it is the
   ancestor that actually clips grossly oversized content lower in the tree
   (e.g. an unstyled screenshot rendering at its raw intrinsic width) — it
   mitigates overflow, it does not fix the breakout's own vw/% arithmetic.
   `clip`, not `hidden`: `hidden` still establishes a programmatically-
   scrollable container (`scrollWidth` keeps reporting the overflow,
   scrollLeft still reaches it), which measured as still-overflowing in
   testing; `clip` discards the scrollable overflow outright. */
html { overflow-x: clip; }
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--fg);
  font-size: var(--text-body); line-height: var(--leading-normal);
  overflow-x: clip;
}
a { color: var(--color-accent-strong); }
.skip-link { position:absolute; inset-inline-start:-999px; }
.skip-link:focus { inset-inline-start:1rem; inset-block-start:1rem; background:var(--color-accent-fill); color:var(--on-accent); padding:var(--space-2) var(--space-3); border-radius:var(--radius-md); z-index:10; }

/* F5 — authored focus indicator. `:focus-visible`, never bare `:focus`: the
   UA already shows a ring on `:focus` for a mouse click too in some browsers,
   which is the exact "everything has a fat blue box after every click"
   behaviour `:focus-visible` exists to avoid — keyboard/programmatic focus
   only.
   Colour: reuses `--fg` rather than any accent token. `--fg` already clears
   >=3:1 against both `--bg` and `--card` in *each* theme (it is the page's
   own body-text colour, chosen for reading contrast, which is a strictly
   higher bar than 3:1). That reuse is also what sidesteps the accent-fill
   case below, rather than trying to pick a colour that threads it: at
   light-mode `--color-accent-fill` (#0056b3, relative luminance 0.099), a
   ring colour would need luminance <=0.30 to clear 3:1 against `--bg`
   (#fff) but >=0.40 to clear 3:1 against the fill itself — mutually
   exclusive, so no single flat colour can ever satisfy both at once. That is
   why this is a structural fix, not a colour-matching one: `outline-offset`
   (3px, `.btn`'s own `margin: var(--space-1)` is 4px) pushes the whole ring
   stroke outside the control's own border box, onto `--bg`/`--card`, so it
   never actually overlaps `--color-accent-fill` at all — the ring drawn
   flush against a filled control (offset 0, or `box-shadow` in place of
   `outline`) is the classic "ring matches the fill it sits on" invisible-
   focus bug this sidesteps.
   `outline`, not `box-shadow`: outline paints in its own layer outside the
   border box regardless of what the element's own `box-shadow` already
   draws there (`.shot.device .frame`, `.shot.loupe img`), so it survives on
   elements that already carry one. */
:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: 3px;
}

main, .site-header, .site-footer { max-width: var(--maxw); margin: 0 auto; padding: var(--space-4); }
.site-header { display:flex; align-items:center; gap:var(--space-3); flex-wrap:wrap; }
.brand { display:flex; align-items:center; gap:.5rem; font-weight:700; text-decoration:none; color:var(--fg); }
.site-nav { display:flex; gap:var(--space-3); align-items:center; margin-inline-start:auto; flex-wrap:wrap; }
.site-nav a { color:var(--fg); text-decoration:none; }
.site-nav a:hover { color:var(--color-accent-strong); }
.site-nav .cta { background:var(--color-accent-fill); color:var(--on-accent); padding:var(--space-1) var(--space-2); border-radius:var(--radius-md); }
.lang-switcher a { color:var(--muted); margin:0 .15rem; font-size:var(--text-footnote); }
.lang-switcher a[aria-current] { color:var(--color-accent-strong); font-weight:700; }
/* 1.5rem (24px) sat inside --text-heading's own fluid range (1.375-1.625rem,
   22-26px) already — the nearest ramp step, caught by the same font-size
   sweep as the Job 3 list once it's written generically instead of against a
   hardcoded selector list. */
.nav-toggle { display:none; background:none; border:0; font-size:var(--text-heading); color:var(--fg); cursor:pointer; }
.hero { text-align: center; padding-block: var(--section-rhythm); padding-inline: var(--space-3); }
.hero p { color: var(--muted); font-size: var(--text-body); max-inline-size: 40rem; margin: var(--space-3) auto; }
/* The lead paragraph is the one place the editorial scheme earns its keep. */
.hero > p:first-of-type {
  font-size: var(--text-lead);
  line-height: var(--leading-loose);
  color: var(--fg);
}
.hero .shot.device { margin-block-start: var(--space-6); }
.btn {
  display: inline-block;
  background: var(--color-accent-fill);
  color: var(--on-accent);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  text-decoration: none;
  margin: var(--space-1);
  font-weight: 600;
}
.btn.secondary { background: var(--card); color: var(--fg); border: 1px solid var(--border); }
.hero p.hero-link { margin:.6rem 0 0; font-size:var(--text-footnote); }
.hero p.hero-link a { text-decoration:underline; }
.hero p.hero-proof { color:var(--muted); font-size:var(--text-footnote); letter-spacing:.01em; margin-top:1.4rem; }
/* One rhythm value per scheme drives every gap between sections, so the page
   has a beat instead of ten independently chosen paddings. */
.section { padding-block: var(--section-rhythm); }
.section > h2 { margin-block: 0 var(--space-4); }
.section > p { max-inline-size: 42rem; }

/* Alternating surface: the page is one long column, and a full-width tint is
   the cheapest way to mark where one idea ends and the next begins without
   adding chrome. */
.section.banded {
  background: var(--card);
  border-block: 1px solid var(--border);
  /* break out of the centred main column without a wrapper element */
  margin-inline: calc(50% - 50vw);
  padding-inline: calc(50vw - 50% + var(--space-3));
}

/* Lists in problem/audience read as a scanned column, not a bulleted paragraph. */
.section > ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-3);
  max-inline-size: 42rem;
}
.section > ul > li {
  padding-inline-start: var(--space-5);
  position: relative;
}
.section > ul > li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.62em;
  inline-size: var(--space-2);
  block-size: var(--space-2);
  border-radius: var(--radius-full);
  background: var(--color-accent);
}

.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: var(--space-4); }
/* A card had NO figure/ground separation anywhere on the site: `var(--bg)` on a
   `var(--bg)` page is 1.00:1, leaving a 1.23:1 hairline as the only thing
   defining it. The redesign notes claimed index.html was exempt because its
   cards sat on a banded section — measured, that is not true: all six cards on
   index live in the unbanded #benefits, and the three banded sections carry no
   cards at all. So this was uniform, not a two-page defect.
   Tint + elevation rather than a heavier border: the fill alone is only
   1.07:1 (light) / 1.08:1 (dark), which understates how a large flat area
   reads but is genuinely subtle, and the shadow supplies the edge cue that
   colour alone does not. The shadow values are `.shot.loupe`'s, reused rather
   than invented so the site has one elevation, not two.
   Deliberately no dark-mode shadow override. A black shadow does almost no
   work on a near-black page, and lifting the surface IS the dark-mode
   convention for elevation — which `--card` already does. Measured, dark is
   not the weaker theme here: fill-vs-page 1.08 (light 1.07), border-vs-page
   1.42 (light 1.23), border-vs-card 1.31 (light 1.15). The shadow is what
   brings LIGHT up to parity, not a light-only luxury that leaves dark behind,
   so no separator disappears in either theme.
   CONSEQUENCE — a card and `.section.banded` now share `var(--card)`, so a
   card nested inside a banded section would be invisible. verify-build asserts
   that never happens; if a banded section ever needs cards, the fix is a third
   surface token, not dropping the guard. */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 0.375rem 1.25rem rgb(0 0 0 / 10%);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.card h3 { margin: 0; }
.card p { margin: 0; color: var(--muted); }
/* The card's link is the last thing in reading order and is pinned to the
   bottom so a row of uneven cards still has its calls to action aligned.
   Excludes `.btn`: a filled call-to-action inside a card (e.g. the pricing
   CTA) must keep its own fill/label/size/margin from `.btn` untouched. A
   bare `.card > a` selector has specificity 0,2,0, which beats `.btn`'s
   0,1,0 and comes later in the file — without `:not(.btn)` it silently wins,
   recolouring the label to `--color-accent-strong` (accent-on-accent-fill,
   ~1:1 contrast) and overriding `.btn`'s font-size/margin. */
.card > a:not(.btn) {
  margin-block-start: auto;
  padding-block-start: var(--space-2);
  font-size: var(--text-footnote);
  color: var(--color-accent-strong);
}
.card:hover { border-color: var(--color-accent); }
@media (prefers-reduced-motion: no-preference) {
  .card { transition: border-color 200ms ease; }
}

.steps { list-style: none; counter-reset: step; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: var(--space-4); }
.steps li { counter-increment: step; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--space-4); }
.steps li::before {
  content: counter(step);
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--space-5);
  block-size: var(--space-5);
  border-radius: var(--radius-full);
  background: var(--color-accent-fill);
  color: var(--on-accent);
  font-weight: 700;
  font-size: var(--text-footnote);
  margin-block-end: var(--space-3);
}
.steps h3 { margin: 0 0 var(--space-2); }
.steps p { margin: 0; color: var(--muted); }
/* Job 3 tokenisation, nearest scale step per literal (scale: --space-1 .25rem,
   -2 .5rem, -3 1rem, -4 1.5rem, -5 2rem, -6 3rem):
   - details padding 1rem 1.2rem -> --space-3 for both (1.2rem is .2rem from
     --space-3 vs .3rem from --space-4; the asymmetry collapses, which is the
     honest result of "nearest step", not a mistake)
   - details margin .6rem -> --space-2 (.5rem, .1 away, vs .4 away from
     --space-3)
   - .price-table gap 1.2rem -> --space-3 (same reasoning as details padding)
   - .store-badges img height 48px -> --space-6 (3rem = 48px exactly)
   - .store-badges img margin .4rem -> --space-2 (.5rem, .1 away, vs .5 away
     from --space-1) */
details { background:var(--card); border:1px solid var(--border); border-radius:var(--radius-lg); padding:var(--space-3) var(--space-3); margin:var(--space-2) 0; }
details summary { font-weight:600; cursor:pointer; }
.price-table { display:grid; grid-template-columns:repeat(auto-fit,minmax(16rem,1fr)); gap:var(--space-3); }
/* 1.6rem sits inside --text-heading's fluid range (1.375rem-1.625rem, i.e.
   22-26px) near its top end — the nearest ramp step, and now fluid instead
   of a flat literal. */
.price { font-size:var(--text-heading); font-weight:700; margin:.2rem 0; }
.store-badges img { height:var(--space-6); margin:var(--space-2); }
/* Screenshots come in two shapes and must not be dressed the same way.
   `device` is a full screen capture and gets a phone body around it; `loupe`
   is a crop of part of the interface and gets a sunken surface that reads as a
   close-up. See docs/superpowers/specs/2026-07-31-site-redesign-design.md §D4.
   A figure written WITHOUT either modifier — or with `.detail`, the narrower
   variant used for the three close-ups on features.html — falls back to the
   plain `.shot img` rule below: a bordered white card at a sane max-width
   with the aspect ratio preserved (`block-size: auto` against the `<img>`'s
   own width/height attributes), so an unmigrated screenshot (the ten-page
   rollout is still pending — see docs/TODO.md) renders undistorted instead
   of matching no rule at all. (This rule used to omit `block-size: auto`
   while constraining `inline-size` — the image's HTML height attribute then
   won over its CSS-scaled width, stretching it vertically; both dressed
   variants below always set `block-size: auto` explicitly, so only the
   fallback was ever broken.)
   `.shot.device img`/`.shot.loupe img` explicitly reset every property the
   base rule sets (border, max-inline-size, background, border-radius) so the
   fallback never leaks into either dressed treatment. */
.shots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-6);
  align-items: start;
}
.shot { margin: 0; }
.shot img {
  display: block;
  inline-size: 100%;
  max-inline-size: 19rem;
  block-size: auto;
  margin-inline: auto;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.shot.detail img { max-inline-size: 26rem; }
.shot figcaption {
  max-inline-size: 22rem;
  margin: var(--space-3) auto 0;
  color: var(--muted);
  font-size: var(--text-footnote);
  text-align: center;
}

/* --- full screen: phone body ------------------------------------------- */
.shot.device {
  --bezel: 0.6rem;
  inline-size: fit-content;
  margin-inline: auto;
}
.shot.device .frame {
  /* Must establish its own box: `.frame` is a <span> (inline) wrapping an
     <img> that is `display: block` below. An inline element with a
     block-level child gets split into anonymous fragments around that
     child, and the leftover empty inline fragment paints this padded,
     rounded box as a stray sliver above the image — a phantom notch this
     design explicitly does not want. inline-block keeps the tag but stops
     the fragmentation. */
  display: inline-block;
  padding: var(--bezel);
  background: linear-gradient(160deg, #3a3f52, #14161f);
  border-radius: calc(var(--radius-xl) + var(--bezel) + 1rem);
  box-shadow:
    0 0 0 1px rgb(255 255 255 / 10%) inset,
    0 1.5rem 2.5rem rgb(0 0 0 / 22%);
}
.shot.device img {
  display: block;
  inline-size: 100%;
  max-inline-size: 19rem;
  block-size: auto;
  /* The captures are light-UI; keep a white backdrop in both colour schemes so
     the frame reads as a lit screen rather than a hole. */
  background: #fff;
  border: none;
  border-radius: calc(var(--radius-xl) + 0.5rem);
}

/* --- crop: close-up on a sunken surface -------------------------------- */
.shot.loupe {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: var(--space-4);
  /* Cap the FIGURE, not just the image inside it. `.shots` stretches its grid
     items, so capping only the `img` would leave the tinted card spanning the
     whole column with a small image floating in the middle of it.
     `inline-size: 100%` is load-bearing, not redundant: `margin-inline: auto`
     on a grid item suppresses the default stretch, which drops the figure to
     fit-content — and fit-content here resolved to the FIGCAPTION's own
     22rem cap (352px), silently making the caption dictate the image's size.
     Declaring 100% restores a deterministic width that the max below clamps.
     The cap is the image's 26rem plus this rule's padding and border, since
     `box-sizing: border-box` makes max-inline-size measure the border box. In
     a multi-figure grid the track (275px at 1440) is already narrower, so
     nothing changes there. */
  inline-size: 100%;
  max-inline-size: calc(26rem + var(--space-4) * 2 + 2px);
  /* Aligned to the inline start, NOT centred — this is an editorial choice,
     not a visual one. In #how the single loupe illustrates step 1 alone: the
     four options it shows (daily / specific days / interval / as needed) are
     literally the four phrases of step 1's body, in the same order, and its
     caption echoes that step's closing words. Centred under a three-card row
     it read as an illustration of the whole section, so the eye went looking
     for steps 2 and 3 inside it. Sitting under the first column, proximity
     does the association the frozen copy cannot state outright.
     `margin-inline-end`, not `margin-right`: this mirrors under dir="rtl".
     Only affects a figure narrower than its track, i.e. the single-figure
     case — in a multi-figure grid each figure fills its track and there is no
     free space to distribute. */
  margin-inline-end: auto;
}
.shot.loupe img {
  display: block;
  inline-size: 100%;
  /* NOT `none`. Lifting the base rule's 19rem cap is deliberate — a loupe
     reads larger than a plain shot — but `none` leaves no ceiling at all, and
     `.shots` is `repeat(auto-fit, minmax(15rem, 1fr))`: a section holding a
     SINGLE figure (#how) collapses the empty tracks and hands that one figure
     the whole content column. Measured with no cap: 1022px rendered from a
     744px capture at 1440px viewport — a 1.37x upscale, visibly soft, while
     the five-figure grid in #proof rendered the same class of asset at 275px
     (0.37x): the same asset class at 3.7x the size of itself, one of them
     past its own resolution.
     26rem matches `.shot.detail img`, so every close-up treatment on the site
     shares one ceiling and a screenshot reads at roughly the optical scale of
     the page's own text rather than dominating it. It also sits well below
     --shot-native-inline-size, which is the absolute never-upscale ceiling
     verify-build enforces across every shot rule. */
  max-inline-size: 26rem;
  block-size: auto;
  background: #fff;
  border: none;
  border-radius: var(--radius-lg);
  box-shadow: 0 0.375rem 1.25rem rgb(0 0 0 / 10%);
}
/* .blog-list's 18rem track minimum and article.post's 44rem reading measure
   are NOT tokenised: neither is a --space-*/--radius-*/--text-* scale value
   in disguise, they are content-fit dimensions (a card's min comfortable
   width; a verified 70-characters-per-line measure — see the Job 2 note
   below) and the nearest --space step for either (--space-8, 6rem) is so far
   off it would silently change the number, not just its spelling. Every
   other grid-track minmax() in this file (.grid's 17rem, .price-table's
   16rem, .shots' 15rem, .shot.detail's 26rem) is raw for the same reason —
   this is a consistent, deliberate exception to the token sweep, not an
   oversight. */
.blog-list { display:grid; grid-template-columns:repeat(auto-fit,minmax(18rem,1fr)); gap:var(--space-3); }
/* max-inline-size, not max-width: horizontal writing modes render them
   identically, so this is consistency with the codebase's logical-property
   rule, not an RTL fix — this element has no other physical-direction
   properties to correct. Value (44rem) is untouched: it is the measured,
   verified 70-characters-per-line reading width (704px at 1440, 18.87px/
   30.20px body text) and changing it needs a better reason than "it's a
   literal". */
article.post { max-inline-size:44rem; padding-block: var(--section-rhythm); }
/* Job 2 (F4) — was a flat `2.2rem`, independent of the ramp: 35.2px at every
   width, so it read BIGGER than a sibling page's h1 on mobile (34px) and
   SMALLER on desktop (37.7px) — the ramp inverted at the exact widths that
   should make it grow.
   `--text-title`, not `--text-display`: a post is a content page, so its h1
   is measured against its siblings (about/faq/pricing/terms), not against the
   landing hero. Those siblings currently render at the UA default 2em —
   34px at 320, 37.7px at 1440 — and --text-title (28→38px) tracks that,
   whereas --text-display would put the post at 56px on desktop, 48% larger
   than every other page's heading and exactly equal to the marketing hero.
   NOTE the underlying gap this exposes and does NOT close: content-page h1 is
   on no token at all, it is the UA default. Putting every h1 on --text-title
   is the full fix; it restyles all ten pages (mobile 34→28px) and was
   deliberately deferred as a separate design decision.
   `--leading-tight` (1.2), not the inherited `--leading-normal` (1.6) body
   line-height: a heading set at 1.6 line-height reads as loose, oversized
   paragraph text, not a title — matches `.section h2`'s own choice. */
article.post h1 { font-size:var(--text-title); line-height:var(--leading-tight); }
/* h2/h3 inside a post body: NOT verified against real content — welcome.md,
   the only post that exists, has neither. Each step sits one rung below the
   one above it (h1 title → h2 heading → h3 body, differentiated by weight
   rather than a further size drop, since --text-body is the last rung on the
   ramp) so a future post's structure doesn't fall back to unstyled UA
   defaults (2em/1.17em) that would fight the h1 for visual weight. Genuinely
   unverified until a post with real subheadings exists. */
article.post h2 { font-size:var(--text-heading); line-height:var(--leading-tight); margin-block: var(--space-6) var(--space-3); }
article.post h3 { font-size:var(--text-body); font-weight:700; line-height:var(--leading-tight); margin-block: var(--space-5) var(--space-2); }
.post-meta { color:var(--muted); font-size:var(--text-footnote); }
.breadcrumbs { font-size:var(--text-footnote); color:var(--muted); }
.breadcrumbs a { color:var(--muted); }
.site-footer { border-top:1px solid var(--border); margin-top:2rem; }
.site-footer nav { display:flex; gap:var(--space-3); flex-wrap:wrap; }
.site-footer a { color:var(--muted); text-decoration:none; }
.copyright { color:var(--muted); font-size:var(--text-footnote); }
@media (max-width:640px) {
  .nav-toggle { display:block; margin-inline-start:auto; }
  /* inline-size, not width: same equivalence-in-horizontal-writing-modes
     reasoning as article.post's max-inline-size above — consistency with the
     logical-property rule, not a fix for an RTL bug (this stacks the nav
     full-width regardless of writing direction). */
  /* Items align to the inline END so the stack lines up under the toggle that
     opened it, rather than jumping to the opposite side of the screen.
     `flex-end`, not `text-align: right`: flexbox alignment follows the writing
     direction, so this becomes left-aligned under dir="rtl" automatically,
     where a physical `right` would strand the menu away from the toggle. */
  /* Hyphenation is scoped to narrow viewports because that is the only place
     it is needed. Measured with hyphenation off, the only heading overflows are
     at 320px and all in RU, where the editorial scheme's larger floor and wider
     serif push long Cyrillic words past their box: "Напоминания" 263px in a
     240px box on the hero, plus "централизованы" and "чувствительных" on
     features and cycle-medications. At 375px and above every heading already
     fits. Reuses the existing 640px breakpoint rather than introducing a second
     one; 640 clears the measured failure at 320 by a wide margin.
     BODY COPY IS INCLUDED, and an earlier version of this rule was wrong to
     exclude it. It claimed body text "has no long-word problem at any width
     tested" — true of the two locales it had been tested on. German broke it
     immediately: "Nahrungsergänzungsmittel" renders 247px in a 225px box at
     320px, in ordinary paragraphs on the hero, the FAQ and the pricing list.
     A word longer than its column is a property of the language, not of the
     element it lands in, so the mechanism belongs on the text rather than on
     the headings alone — otherwise every future locale re-discovers this.
     (de.json also carries soft hyphens at that compound's seams, added before
     this rule existed. They are now redundant rather than load-bearing, and
     harmless: U+00AD and `hyphens: auto` cooperate, the explicit seams simply
     win where present.) */
  h1, h2, h3, p, li, figcaption, dd { hyphens: auto; }
  .site-nav { display:none; inline-size:100%; flex-direction:column; align-items:flex-end; }
  .site-nav.open { display:flex; }
}
