/* ============================================================
   Saltado Sessions: shared polish layer (every page links this
   before its inline <style>). Display typography, selection,
   focus, micro-interactions. Inline page styles win ties, so
   anything that must beat them uses a higher-specificity selector.
   ============================================================ */

/* Space Grotesk (variable, latin subset, self-hosted, OFL license).
   One file serves every weight 300–700. */
@font-face {
  font-family: 'Space Grotesk';
  src: url('/fonts/SpaceGrotesk-latin-var.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}


/* ============================================================
   One radius scale and three elevation steps. Pages had five
   radii (2/3/4/6/50%) and twelve distinct shadows, including two
   dropdown menus with the same geometry at different alphas.
   Declared here so a page's inline :root does not need to repeat
   them; --radius stays 2px (the dominant value) and --radius-lg
   3px for cards, menus and framed media.
   ============================================================ */
:root {
  --radius: 2px;
  --radius-lg: 3px;
  --sh-1: 0 10px 26px rgba(0, 0, 0, 0.24);   /* resting card */
  --sh-2: 0 16px 44px rgba(0, 0, 0, 0.44);   /* menu / popover */
  --sh-3: 0 2px 6px rgba(0, 0, 0, 0.5), 0 44px 90px rgba(0, 0, 0, 0.5); /* hero */
}

/* ============================================================
   INK TOKENS: the text-grade variants of the coloured hues, the
   same idea the app uses (client/src/styles.css: --warn-ink and
   friends). On every dark theme the hue IS the ink, so these are
   a no-op; classic-light overrides them further down.

   Why the site needs an --accent-ink the app does not: the app
   spends --accent on fills, dots and borders, but this site sets
   TEXT in it (.pname, the footer status lead, link hovers, the
   focus ring). On classic-light that read #b8701e on #a2a2a2 =
   1.53:1, i.e. the brand colour was effectively invisible as
   text and the focus ring failed 1.4.11 outright.

   The fallbacks matter: only index.html and support.html declare
   --error inline, and --ok/--warn are on fewer pages still, so a
   bare var(--ok) would resolve to nothing on most of the site.
   ============================================================ */
:root {
  /* Literal, not var(--accent), for the same reason --error-ink below is a
     literal: the Default theme has no [data-theme] block to hang an override
     on, and the raw #d99146 measures 3.87:1 on its own --surface-2 (the DAW
     table header, the Beta band). #dfa262 is that same hue and saturation
     (hsl 30.6, 65.9%) lifted from 56.3% to 62.8% lightness, which clears 4.5
     on --surface-2 (4.54) while staying the same amber on --bg (6.39). All 31
     pages declare --accent: #d99146 inline, so this tracks the one default
     palette; the five data-theme blocks restate their own. */
  --accent-ink: #dfa262;
  /* Meaningful graphics (the .p-ico feature and use-case icons) are held to
     3:1 by WCAG 1.4.11, not 4.5:1, so they get their own token rather than
     wearing --accent-ink and going muddy. On the dark five the raw hue already
     clears 3:1 comfortably, so this is a no-op there and they render exactly
     as before; only classic-light overrides it. */
  --icon-accent: var(--accent);
  /* Literals, not var(--ok)/var(--warn), for the same reason --accent-ink and
     --error-ink are literals. The old `var(--ok, #67b84b)` form looked like a
     safe fallback and was actually a no-op: all 31 pages declare --ok inline,
     so var() always found it and the ink was simply the raw hue. That is what
     left the DAW table's 38 checkmarks at 1.87:1 on classic-light and 2.62:1 on
     classic-dark. The five data-theme blocks now each restate their own, so a
     theme whose hue already reads as text says so explicitly with var(--ok).

     #74be5a is the Default theme's --ok (#67b84b, hsl 105.5 43.1% 50.8%) at the
     same hue and saturation, lifted to 53.5% lightness: the smallest step that
     clears 4.5 on all three beds green is set on (the card --surface 5.33, the
     DAW table's tinted Ableton row 4.55, the guarantee strip 4.98). The hue does
     not move, which is the point: this is a text-grade variant, not a new green.
     #e2b13a is the Default --warn unchanged; it already cleared every bed. */
  --ok-ink: #74be5a;
  --warn-ink: #e2b13a;
  /* The "informational" blue: the A&R tier's colour, previously the bare literal
     #6ea8ff hardcoded three times in index.html (the .price-tag chip, its border,
     and the .price-card.scout frame). As a token it can finally follow the theme,
     which matters because on classic-light that blue measured 1.11:1 as text.
     --info is the fill/border hue, --info-ink the text-grade one, the same split
     as --accent / --accent-ink. */
  --info: #6ea8ff;
  --info-ink: #6ea8ff;
  /* One step above --muted, for small text that --muted cannot carry: the DAW
     table's 10px column headers and .grp-fmt (they sit on --surface-2, the
     lightest bed on the page, where --muted measured 3.75 to 3.92 on all five
     dark themes), the .daw-ext monospace extensions, the 10px .why caveat notes
     (3.21 to 3.40 on the tinted Ableton row) and the .proofbar strip.
     Held to 4.5:1 on ALL FOUR of those beds. It is deliberately still a step
     below --text, which is what keeps the DAW table's hierarchy readable:
     name (--text) > headers and extensions (--muted-strong) > nothing weaker. */
  --muted-strong: #afafaf;
  /* The recessed-strip wash under the hero (.proofbar). Was a theme-blind
     rgba(0,0,0,0.12) inline in index.html, which is right on the dark five and
     wrong on classic-light: there --bg (#a2a2a2) is already the DARKEST surface
     in the palette, so darkening it further pushed the strip to #8f8f8f and left
     the muted spans on it at 3.55:1 with nowhere to go (the ink cannot get
     darker without colliding with --text). Flipped to a lightening wash on that
     one theme, the same way --band, --rule and --grid already flip. */
  --sunken: rgba(0, 0, 0, 0.12);
  /* Literal, not var(--error): the default theme's #e5544b measures 3.28:1
     against its own surfaces and never reads as text. #ec7f78 is that same
     red lifted to 4.51:1, and it is close to the #d98c8c the footer used to
     hardcode, so the original author's instinct was right, it just was not
     a token and so did not follow the themes. */
  --error-ink: #ec7f78;
  /* Hairline rules used as decoration (the footer's bar/beat ticks).
     Light-on-dark by default, flipped for classic-light, because a
     white 13% tick over #b2b2b2 is not visible at all. */
  --rule: rgba(255, 255, 255, 0.13);
  --rule-soft: rgba(255, 255, 255, 0.045);
}

:root { --font-display: 'Space Grotesk', 'Helvetica Neue', 'Arial Nova', Arial, 'Segoe UI', system-ui, sans-serif; }

/* One typeface throughout: the display grotesk also sets the body, so running
   copy stops falling back to generic system Arial next to the headings. Slightly
   looser tracking keeps Space Grotesk comfortable at paragraph size. html body
   beats each page's inline body{font-family}. */
html { scroll-behavior: smooth; }
html body { font-family: var(--font-display); letter-spacing: 0.002em; }

/* Product name in prose: mark "Sessions" as the app's name, not the generic
   word. The brand amber + a touch heavier. Applied to the FIRST mention per
   section so it reads as a proper noun without turning the page orange. */
.pname { color: var(--accent-ink); font-weight: 600; }

/* Headlines and marks wear the display face. Weight is capped at 700
   (the face's max) so browsers never synthesize a faux 800. */
body h1, body h2, body h3,
body .sec-head h2,
body .feat-row h3, body .use-card h3, body .step h3,
body .feat-group h2,
body .price {
  font-family: var(--font-display);
  font-weight: 700;
}
body h1 { letter-spacing: -0.02em; }
/* Stacked house-mark lockup: SALTADO is a small kicker sitting over SESSIONS
   (the product, the star), with the icon spanning both lines. A grid lays the
   three existing siblings out with NO markup change: a.brand beats each page's
   inline .brand{display:flex}, and .brand span.NAME beats the inline .wordmark
   rule. The full ownable "Saltado Sessions" stays in the DOM for trademark+SEO,
   but it reads the way people say it: Sessions. */
a.brand {
  display: grid;
  grid-template-columns: auto auto;
  /* 7px, not 11: the folders SVG carries a few px of its own right-edge padding,
     so a smaller column gap makes the mark and the wordmark read as one lockup. */
  column-gap: 7px; row-gap: 0;
  align-items: center;
  /* justify-content: start (not the default stretch) so the auto icon column
     stays icon-width. In the footer the brand link stretches to the wide column,
     and the default stretch would blow the icon column open and push the wordmark
     ~78px right; the topbar (a content-width flex item) never showed it. */
  justify-content: start;
}
a.brand .logo-mark { grid-row: 1 / span 2; align-self: center; }
.brand span.wordmark {
  grid-column: 2; grid-row: 1; align-self: end;
  font-family: var(--font-display);
  font-size: 9px; font-weight: 600; line-height: 1;
  color: var(--muted); letter-spacing: 0.3em; margin-bottom: 2px;
}
.brand span.product {
  grid-column: 2; grid-row: 2; align-self: start;
  font-family: var(--font-display);
  font-size: 18px; font-weight: 700; line-height: 0.85;
  color: var(--text); letter-spacing: 0.06em;
}
a.btn, button.btn { font-family: var(--font-display); font-weight: 500; }

/* Text selection in the brand amber. The literal fallback is load-bearing:
   10 pages (affiliates, audyn-alternative, best-ableton-project-manager,
   educators, eula, find-missing-samples-ableton, manual-methods,
   organize-ableton-projects, privacy, support) never declare --accent-soft
   in their inline :root. A var() that resolves to nothing makes the whole
   declaration invalid at computed-value time, so background fell back to
   its initial value, transparent, while color stayed var(--text): selecting
   text on those pages painted nothing at all in the default theme. The five
   data-theme blocks all declare it, so this only ever bit the default look. */
::selection { background: var(--accent-soft, rgba(217, 145, 70, 0.18)); color: var(--text); }

/* keyboard focus. --accent-ink, not --accent: a focus indicator is held to
   3:1 against what surrounds it (WCAG 1.4.11) and plain --accent measured
   1.53:1 on classic-light's --bg. */
:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

/* buttons: quiet lift on hover, settle on press */
a.btn, button.btn {
  transition: background 0.12s, border-color 0.12s, transform 0.1s ease, box-shadow 0.14s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
/* quiet neutral lift on hover, no colored glow (flat/Linear register) */
.btn.primary:hover { box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); }

/* Topbar stays flat and solid (its inline --sidebar color), no glass blur:
   the register is flat, precise and quiet, so no glassmorphism. */

/* cards respond to the cursor, restrained */
.use-card, .feat-group {
  transition: transform 0.15s ease, border-color 0.15s ease;
}
/* var(--muted), not the literal #6a6a6a it used to be: that value was tuned
   against the default theme's #363636 card and is theme-blind. On classic-dark
   it landed at 1.72:1 over --surface #474747, barely separable from the
   resting --border #5c5c5c at 1.39:1, so the hover simply did not read. The
   muted token is the system's own one-step-above-border tone and lifts to
   3.30 to 4.47:1 across the six themes, darkening rather than lightening on
   classic-light, which is the correct direction there. */
.use-card:hover, .feat-group:hover { transform: translateY(-2px); border-color: var(--muted); }

/* the DAW table's Beta badges: honest, but fine print, not a headline.
   Tokenised: the old literals #8c8c8c/#4d4d4d were the default theme's
   --muted/--border copied by hand, so the badge kept the dark palette on
   classic-light and sat at 1.32:1 there.
   var(--text), not var(--muted): the chip is 10px, so it is held to 4.5:1,
   and --muted over the header cell's --surface-2 measured 3.75 to 3.92 on the
   five dark themes. The border stays --border, which is what keeps it fine
   print: the pill outline carries the "this is an aside" reading, so the label
   inside it can afford to be legible. */
span.beta { color: var(--text); border-color: var(--border); }

/* ============================================================
   Appearance themes: the app's own six (client/src/styles.css),
   selectable from the topbar picker injected by site.js. Every
   page defines the Default palette inline in :root; these
   higher-specificity blocks override it when <html data-theme>
   is set, so the whole site re-skins exactly like the app.
   --accent-hover is site-only (the app derives hovers), tuned a
   step lighter than each accent.
   ============================================================ */
:root[data-theme='angst-robot'] {
  --bg: #2b2a44; --surface: #353357; --surface-2: #403e66; --border: #4d4a7d;
  --text: #dcdaf0; --muted: #9e9bc7;
  --accent: #9d8bff; --accent-hover: #b0a1ff; --accent-soft: rgba(157, 139, 255, 0.2);
  --ok: #6bd08f; --warn: #e2c14a; --error: #ff6b8a;
  /* ok and warn already clear 4.5:1 here, so they inherit the hue via the
     base :root; only the red needs a nudge (4.38 to 4.50). The violet as TEXT
     sat at 3.61 on --surface-2, so it is lifted from hsl lightness 77.3% to
     81.8% at the same hue and saturation (249.3, 100%): 4.52 on --surface-2,
     6.25 on --bg. */
  --accent-ink: #b1a2ff; --error-ink: #ff6f8e;
  /* The mint and the yellow both clear 4.5 as text on every bed here (worst case
     5.37 on the tinted Ableton row), so they restate the raw hue rather than
     inheriting the Default theme's literals from the base :root above. The blue
     inherits too: #6ea8ff measures 4.94 on this --surface. */
  --ok-ink: var(--ok); --warn-ink: var(--warn);
  /* --muted (#9e9bc7) reads 3.80 on --surface-2 and 3.31 on the tinted row. Same
     hue and saturation (hsl 244.6, 25.3%), lifted from 69.4% to 74.2%. */
  --muted-strong: #aeacd0;
  --sidebar: #232238;
}
:root[data-theme='classic-dark'] {
  --bg: #3b3b3b; --surface: #474747; --surface-2: #525252; --border: #5c5c5c;
  --text: #d6d6d6; --muted: #b5b5b5;
  --accent: #f0a52e; --accent-hover: #f4b552; --accent-soft: rgba(240, 165, 46, 0.18);
  --ok: #6ab04c; --warn: #e1b12c; --error: #eb5b4b;
  /* This theme's surfaces are the lightest of the dark five, so its green, red
     and amber all fall short as text (3.51, 2.71 and 3.77 on --surface-2).
     Lifted to 4.52, 4.55 and 4.55; the amber keeps its hue and saturation
     (hsl 36.8, 86.6%) and only rises from 56.5% to 67.1% lightness. */
  --accent-ink: #f4bc62; --ok-ink: #a6d193; --error-ink: #f3a097;
  /* #8ec377, the previous --ok-ink, was tuned against --surface alone (4.52) and
     fell short on the two beds green is actually set on elsewhere: the guarantee
     strip (4.29) and the DAW table's tinted Ableton row (2.62, the worst reading
     on the whole site). #a6d193 is the same green (hsl 105.5, 43.1%) at 69.4%
     lightness and clears all three (5.38 / 4.58 / 5.10).
     The amber follows for the same reason: --warn read 4.66 on --surface but
     3.85 on the tinted row, so it lifts from 52.7% to 62.5% lightness.
     The blue lifts from 71.6% to 76.4%: #6ea8ff was 3.85 on this --surface. */
  --warn-ink: #e7c156; --info-ink: #88b8ff;
  /* --muted (#b5b5b5) is 3.81 on --surface-2. This is the tightest gap of the
     six themes (--muted-strong 4.57 vs --text 5.38 on --surface-2) because the
     palette's whole range is narrow; it is still a visible 17-step separation. */
  --muted-strong: #c6c6c6;
  --sidebar: #303030;
}
:root[data-theme='classic-light'] {
  --bg: #a2a2a2; --surface: #b0b0b0; --surface-2: #bcbcbc; --border: #888888;
  /* --muted runs darker than the app's (#565656): the site sets long body copy
     in it, which needs more contrast on the light canvas than app chrome does.
     #393939, not the #454545 it was: the binding pair is muted body copy on
     --bg (#a2a2a2, darker than any card), and #454545 measured 3.76:1 there,
     so the feature paragraphs, the section sub-heads and the FAQ answers all
     failed on the one theme where the canvas is light. #393939 is the nearest
     value clearing 4.5 on --bg (4.52) and it clears --surface at 5.33. */
  --text: #1f1f1f; --muted: #393939;
  --accent: #b8701e; --accent-hover: #ca7f27; --accent-soft: rgba(184, 112, 30, 0.2);
  --ok: #3f8f2f; --warn: #a67c10; --error: #c0392b;
  /* Text-grade inks. The plain hues above stay for fills, dots and borders;
     as TEXT they sit between 1.5:1 and 2.1:1 on this theme's surfaces, which
     is unreadable. Each ink is its hue scaled toward black until it clears
     4.5:1 on --bg #a2a2a2, the darkest surface and therefore the binding one
     (measured: accent 4.57, ok 4.56, warn 4.51, error 4.52). They land a
     touch darker than the app's own inks (#1e4d17 / #4a3900 / #7a1f14),
     which clear 4.5 on app chrome but only reach 3.86 to 4.38 against this
     --bg, because the site sets running copy where the app sets chrome. */
  --accent-ink: #51310d; --ok-ink: #1c4015; --warn-ink: #493707; --error-ink: #6a1f18;
  /* The informational blue, the one hue this theme had no answer for: #6ea8ff
     measured 1.11:1 as text on --surface, the worst chip on the page after the
     verdict. #003d99 is the same hue and saturation (hsl 216, 100%) dropped from
     71.6% to 30% lightness: 4.55 on --surface. --info matches it rather than
     staying the pale sky blue, because .price-tag paints its border and its text
     the same colour, and a near-navy label inside a pale ring reads as a bug. */
  --info: #003d99; --info-ink: #003d99;
  /* No lift needed: --muted (#393939) already clears 4.5 on every bed the small
     text sits on here (--surface 5.33, --surface-2 6.08, the tinted Ableton row
     5.01, the proofbar 5.27 once --sunken flips below). Aliased rather than left
     to inherit, because the base :root literal is the Default theme's grey and
     would land at 1.4:1 on this canvas. This is the one theme where
     --muted-strong and --muted are the same value, and that is correct: the
     hierarchy still reads, since --text is #1f1f1f. */
  --muted-strong: var(--muted);
  /* Lightening, not darkening. See --sunken in the base :root: --bg is the
     darkest surface in this palette, so the shared black wash had nowhere to go. */
  --sunken: rgba(255, 255, 255, 0.14);
  /* Icons are graphics, not text: 3:1 is the bar, so they stop well short of
     --accent-ink's near-black and keep reading as the brand amber. The raw
     #b8701e measured 1.53:1 on --bg, which is the same invisibility that
     forced --accent-ink. #714512 is that hue and saturation (hsl 31.9, 72%)
     darkened from 42.4% to 25.8% lightness: 3.21 on --bg (the feature-row
     icons) and 3.78 on --surface (the use-card icons). */
  --icon-accent: #714512;
  --grid: rgba(0, 0, 0, 0.12); --grid-strong: rgba(0, 0, 0, 0.24); --band: rgba(0, 0, 0, 0.05);
  --rule: rgba(0, 0, 0, 0.16); --rule-soft: rgba(0, 0, 0, 0.06);
  --sidebar: #b2b2b2;
}
/* The folders mark is inlined by site.js (stroke: var(--accent), fill:
   var(--sidebar)), so it re-skins with every theme like the in-app lockup.
   No per-theme logo files needed. logo.svg remains only as the no-JS <img>
   fallback. */
:root[data-theme='immaterial'] {
  --bg: #242427; --surface: #2e2e32; --surface-2: #38383d; --border: #45454b;
  --text: #d2d2d6; --muted: #95959d;
  --accent: #ff6ec7; --accent-hover: #ff8ad2; --accent-soft: rgba(255, 110, 199, 0.18);
  --ok: #5fd0a0; --warn: #e2c04a; --error: #ff5c7a;
  /* The darkest surfaces of the six, so every hue already reads as text
     (4.55 at worst; the pink is 4.61 on --surface-2). Restated so the base
     :root literals do not leak in. */
  --accent-ink: var(--accent); --error-ink: var(--error);
  --ok-ink: var(--ok); --warn-ink: var(--warn);
  /* --muted (#95959d) is 3.92 on --surface-2 and 3.40 on the tinted Ableton row.
     Same hue and saturation (hsl 240, 4.4%), lifted from 60% to 65.3%. */
  --muted-strong: #a2a2a9;
  --sidebar: #1d1d20;
}
:root[data-theme='twenty-four-carat'] {
  --bg: #37352b; --surface: #423f33; --surface-2: #4e4a3c; --border: #5a5646;
  --text: #d8d3c2; --muted: #b0aa96;
  --accent: #cfa94f; --accent-hover: #dcb964; --accent-soft: rgba(207, 169, 79, 0.2);
  --ok: #7fae4a; --warn: #e0b83a; --error: #db5b47;
  /* Green 4.05, red 2.81 and gold 3.99 as text; lifted to 4.50, 4.54 and 4.54.
     The gold keeps its hue and saturation (hsl 42.2, 57.1%) and only rises
     from 56.1% to 62.5% lightness. */
  --accent-ink: #d6b669; --ok-ink: #a1c577; --error-ink: #e89487;
  /* #8cb65c, the previous --ok-ink, cleared --surface (4.50) and nothing else:
     2.87 on the tinted Ableton row, 3.81 on the guarantee strip. #a1c577 is the
     same green (hsl 87.6, 39.7%) at 62.2% lightness: 5.41 / 4.56 / 5.09.
     The gold --warn already clears every bed it is set on (4.69 at worst), so it
     restates the raw hue. The blue lifts one step, from 71.6% to 72.5%: #6ea8ff
     was 4.37 on this --surface. */
  --warn-ink: var(--warn); --info-ink: #74acff;
  /* --muted (#b0aa96) is 3.81 on --surface-2. Same hue and saturation
     (hsl 44.1, 13.2%), lifted from 63.9% to 70%. */
  --muted-strong: #bebaa9;
  --sidebar: #2c2a22;
}

/* ---- theme picker (markup injected by site.js into every topbar nav) ---- */
.theme-dd { position: relative; }
/* Quiet, icon-only trigger: a single accent dot (the current theme's color).
   No label, no caret. The menu explains itself on click. */
.theme-btn {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: var(--radius); cursor: pointer;
  padding: 9px 10px; font-family: inherit; color: var(--muted);
}
.theme-btn:hover, .theme-dd.open .theme-btn { background: rgba(128, 128, 128, 0.14); }
.theme-btn .dot {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--accent); border: 1px solid rgba(0, 0, 0, 0.35);
}
.theme-menu {
  position: absolute; top: calc(100% + 9px); right: 0; min-width: 218px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--sh-2); padding: 5px;
  display: none; z-index: 40;
}
.theme-dd.open .theme-menu { display: block; }
.theme-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border-radius: var(--radius); border: none; background: none;
  cursor: pointer; font: inherit; font-size: 13px; color: var(--text); text-align: left;
}
/* #1a1305, not the #161001 this rule used to carry: all 31 pages set exactly
   #1a1305 as their on-accent label colour, so the one odd value here was drift
   against a unanimous convention. Both clear 4.5:1 on every theme's accent. */
.theme-item:hover { background: var(--accent); color: #1a1305; }
.theme-item .sw { display: flex; gap: 3px; flex: 0 0 auto; }
.theme-item .sw i { width: 11px; height: 11px; border-radius: var(--radius); border: 1px solid rgba(0, 0, 0, 0.3); }
.theme-item .on { margin-left: auto; color: var(--accent-ink); font-weight: 700; }
.theme-item:hover .on { color: inherit; }

/* ============================================================
   ACCOUNT PILL, 2026-08-11 (Jules: "make it look and act
   expensive"). The LAST element in every topbar, to the right of
   the single accent CTA, because that is where an account lives on
   every product people already use.

   It is a ghost button, not a text link and not a second CTA: a
   hairline border in --border, --muted-strong ink, a person glyph
   at currentColor. The CTA keeps the only filled accent in the bar,
   so the two never compete; this one just has to look like a
   deliberate destination rather than a leftover link.

   Hover lifts the ink to --text and warms the border to --accent.
   That pairing is the point: the border moves toward the CTA's hue
   while the fill stays empty, which reads as "this is live" without
   borrowing the CTA's weight. No transform and no shadow, unlike
   a.btn, or it would bounce like a button.

   Focus needs nothing here: the global :focus-visible ring above
   already gives it --accent-ink at 2px with a 2px offset.

   Ink is --muted-strong, not --muted: this carries a glyph and a
   border, so it is a control, and controls do not get the site's
   quietest grey. Measured composited on the topbar's own --sidebar
   in all six themes: 5.19 (twenty-four-carat) to 8.15 (immaterial)
   at rest, 6.28 to 10.79 on hover. Lightest bed is classic-light.

   These selectors deliberately out-specify each page's inline
   `.topbar nav > a` rules, which load after this file; the panel
   copy is scoped separately (nav > a.acct never matches it) so the
   pill flattens into an ordinary full-width row on phones.
   ============================================================ */
.topbar .wrap > nav > a.acct {
  display: inline-flex; align-items: center; gap: 7px;
  margin-left: 4px;
  padding: 6px 12px 6px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: none;
  color: var(--muted-strong);
  font-size: 13px; font-weight: 600;
  transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
.topbar .wrap > nav > a.acct svg { width: 15px; height: 15px; flex: none; }
.topbar .wrap > nav > a.acct:hover {
  color: var(--text);
  border-color: var(--accent);
  background: rgba(128, 128, 128, 0.10);
}
/* In the panel it is a row like every other, just with its glyph. The
   base .site-menu-links rule owns the 48px height, the 15px type and
   the --accent-ink hover; only the gap and the glyph size are new. */
.topbar .site-menu-links a.acct { gap: 12px; }
.topbar .site-menu-links a.acct svg { width: 18px; height: 18px; flex: none; }

/* ============================================================
   PHONE MENU (markup injected by site.js into every topbar).

   The breakpoint is MEASURED, not guessed, and it moved three times
   on 2026-08-11: up when the nav gained an eighth link (Account),
   down when the two CTA buttons collapsed into one accent
   "Get Sessions", then up again when Account became the ghost pill
   at the right-hand end. Each measured in Chrome, at a 1280px
   window, with Space Grotesk actually loaded (the font matters, see
   below). The current row:

     brand lockup 127.5px + the .wrap's 20px flex gap + nav row
     780.5px (7 links, the theme dot, the 108.9px CTA, the 99.5px
     account pill, 6px gaps) + 2x24px .wrap padding = 976px.

   At 976 layout px the bar renders flush; below it the lockup
   compresses (measured: 2px lost at 974, 8px at 968). The pill
   costs 21.8px over the plain text link it replaced, and collapsing
   the CTA pair before it bought back 96.7px, so this still sits
   BELOW the 1000 it lived at for months while carrying one more
   item than it did then.

   ⚠️ Measure with the webfont, and measure the WHOLE row (the theme
   dot is injected by script, so a measurement taken before it lands
   is 38px short). Space Grotesk is the WIDE case here, not the
   narrow one: the same row in the fallback stack measures 963.1px,
   so sizing to 976 covers both the swapped and the unswapped paint.
   And the numbers this block carried before 2026-08-11 (985 flush,
   1000 collapse) were taken before Space Grotesk shipped and had
   quietly stopped describing the bar. A stale flush width is how a
   bar ends up jammed at a size nobody tested: re-measure, never
   extrapolate.

   The collapse fires at 993px: 976 plus a classic 17px scrollbar.
   So the narrowest window that keeps the full bar (994) still has
   977 layout px with a scrollbar, 1px clear of flush, and anything
   tighter gets the menu instead of a jammed row. Above 993px
   nothing in this block applies: the toggle is display:none, the
   panel is not rendered, and the theme picker sits in the nav where
   it always did.

   Every page defines its own .topbar rules in an inline <style> that
   loads AFTER this file, so the selectors here deliberately carry
   more specificity than the page's (.topbar.has-menu .wrap > nav > a
   beats .topbar nav > a). .has-menu is added by script, so a page
   with JS off keeps its full nav rather than losing it.
   ============================================================ */
.nav-toggle {
  display: none;
  flex: 0 0 auto;
  align-items: center; justify-content: center;
  width: 44px; height: 44px;              /* thumb target */
  margin: 0; padding: 0;
  background: none; border: none; border-radius: var(--radius);
  color: var(--muted); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.12s ease, background 0.12s ease;
}
.nav-toggle:hover, .topbar.menu-open .nav-toggle {
  color: var(--text); background: rgba(128, 128, 128, 0.14);
}
.nav-toggle .bars { position: relative; display: block; width: 18px; height: 12px; }
.nav-toggle .bars i {
  position: absolute; left: 0; right: 0; height: 1.5px; border-radius: 1px;
  background: currentColor;
  transition: transform 0.16s ease, opacity 0.1s ease;
}
.nav-toggle .bars i:nth-child(1) { top: 0; }
.nav-toggle .bars i:nth-child(2) { top: 5.25px; }
.nav-toggle .bars i:nth-child(3) { top: 10.5px; }
/* Bars to a close mark: the outer two travel to the middle and cross,
   the middle one leaves. One move, no bounce, no overshoot. */
.topbar.menu-open .nav-toggle .bars i:nth-child(1) { transform: translateY(5.25px) rotate(45deg); }
.topbar.menu-open .nav-toggle .bars i:nth-child(2) { opacity: 0; }
.topbar.menu-open .nav-toggle .bars i:nth-child(3) { transform: translateY(-5.25px) rotate(-45deg); }

/* The panel. Same surface as the bar it hangs off, the bar's own hairline
   above it, a matching one below, one soft shadow: it reads as the bar
   getting taller, not as a separate floating card. No blur, no glass. */
.topbar .site-menu {
  /* +1px because top:100% resolves against the padding box: without it the
     panel paints over the bar's own border-bottom, and the bar would lose
     its hairline the moment the menu opened. That border IS the panel's top
     hairline, which is why the panel declares none of its own. */
  position: absolute; top: calc(100% + 1px); left: 0; right: 0;
  background: var(--sidebar);
  border-bottom: 1px solid rgba(0, 0, 0, 0.4);
  box-shadow: 0 16px 30px rgba(0, 0, 0, 0.34);
  /* 24px matches the .wrap's gutter, so panel rows line up with the
     brand lockup above them (the wrap is full-width at every size this
     panel is visible at, since its 1080px cap is above the breakpoint). */
  padding: 6px 24px 18px;
  /* An absolutely positioned child resolves left/right against the topbar's
     PADDING box, so it ignores the scroll-lock compensation the bar carries.
     Adding it here keeps the panel's right edge on the same line as the bar's
     toggle. --menu-sb is 0 (and this is a plain 24px) on any touch device. */
  padding-right: calc(24px + var(--menu-sb, 0px));
  max-height: calc(100vh - 53px);
  max-height: calc(100dvh - 53px);        /* honours the mobile URL bar */
  overflow-y: auto; overscroll-behavior: contain;
  opacity: 0; transform: translateY(-8px); visibility: hidden;
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s linear 0.16s;
}
.topbar.menu-open .site-menu {
  opacity: 1; transform: none; visibility: visible;
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s;
}
/* An open panel pins the bar: the pages' hide-on-scroll script keeps
   adding .hidden, and a bar that slid away would take the panel with
   it. Specificity beats .topbar.hidden, so no !important is needed. */
.topbar.menu-open.hidden { transform: none; }

.site-menu-links { display: flex; flex-direction: column; }
.topbar .site-menu-links a {
  display: flex; align-items: center;
  min-height: 48px; padding: 2px 0;
  color: var(--text); text-decoration: none;
  font-size: 15px; font-weight: 500; letter-spacing: 0.01em;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.12s ease;
}
.topbar .site-menu-links a:hover, .topbar .site-menu-links a:active { color: var(--accent-ink); }

.site-menu-ctas {
  display: flex; flex-direction: column; gap: 10px;
  margin-top: 12px; padding-top: 16px;
  border-top: 1px solid var(--border);
}
.topbar .site-menu-ctas .btn { width: 100%; justify-content: center; }

.site-menu-theme {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0 12px;
  min-height: 48px; margin-top: 12px; padding-top: 6px;
  border-top: 1px solid var(--border);
}
.menu-theme-label {
  flex: 1 1 auto;
  font-size: 11px; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted);
}
/* display:contents dissolves the picker's wrapper so its button and its
   list become items of the row above: the dot sits on the label's line
   and the list opens full-width UNDER it, pushing the panel taller.
   The alternative (leaving it a popover) would be clipped the moment
   the panel has to scroll, since the picker is the panel's last row. */
.site-menu .theme-dd { display: contents; }
.site-menu .theme-btn { flex: 0 0 auto; width: 44px; height: 44px; padding: 0; }
.site-menu .theme-menu {
  position: static; flex: 1 1 100%; width: auto; min-width: 0;
  margin: 4px 0 6px; box-shadow: none;
}
.site-menu .theme-item { min-height: 44px; padding: 10px 12px; font-size: 14px; }

/* Scroll lock. --menu-sb is the width the vanished scrollbar took, set
   by site.js; it is handed back to the body AND to the fixed topbar,
   because a fixed element is laid out against the viewport and would
   otherwise stay put while the centred page shifted under it. */
body.menu-locked { overflow: hidden; padding-right: var(--menu-sb, 0px); }
body.menu-locked .topbar { padding-right: var(--menu-sb, 0px); }

/* Keep this width and MENU_BP in site.js identical: the CSS hides the
   links and the script decides where the theme picker lives, so a
   mismatch strands the picker in a panel nobody can open. */
@media (max-width: 993px) {
  /* Bar keeps: brand, the one accent CTA (.primary), toggle. The picker
     has been moved into the panel by then; the rule is belt and braces.
     The account pill is not .primary, so the same rule sends it to the
     panel, where it renders as an ordinary row with its glyph. */
  .topbar.has-menu .wrap > nav > a:not(.primary) { display: none; }
  .topbar.has-menu .wrap > nav > .theme-dd { display: none; }
  .topbar.has-menu .wrap > nav { gap: 4px; }
  .topbar.has-menu .nav-toggle { display: inline-flex; }
}

@media (prefers-reduced-motion: reduce) {
  .topbar .site-menu,
  .topbar.menu-open .site-menu { transition: none; }
  .nav-toggle .bars i { transition: none; }
}

/* scroll reveal: classes are added by script, so no-JS never hides content */
@media (prefers-reduced-motion: no-preference) {
  .rv { opacity: 0; transform: translateY(10px); transition: opacity 0.4s ease, transform 0.4s ease; }
  .rv.in { opacity: 1; transform: none; }
}

/* one mobile headline size for every page (several pages had none) */
@media (max-width: 720px) { body h1 { font-size: 28px; } }

/* ============================================================
   ANCHOR LANDINGS. The topbar is fixed and 53px tall on every
   page, so a hash jump that lands a section at scroll offset 0
   puts the bar on top of its heading. 69px = the bar plus a
   16px breath.

   Declared here rather than per page so a page that never
   thought about it gets the offset for free. A page whose own
   inline <style> sets section[id]{scroll-margin-top} still wins
   (inline blocks load after this file at equal specificity),
   which is deliberate: this is a floor, not a ceiling.
   ============================================================ */
section[id] { scroll-margin-top: 69px; }

/* Answer-first block at the top of every guide (2026-09-13, the site simplification pass).
   The h2 is literally "Short answer" so search and AI answer engines find the quotable
   summary first; the optional list under it links to the guide's own sections. */
.short-answer { background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 10px; padding: 18px 22px; margin: 24px 0 32px; }
.short-answer h2 { font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); margin: 0 0 8px; }
.short-answer p { margin: 0 0 10px; }
.short-answer p:last-child, .short-answer ol:last-child { margin-bottom: 0; }
.short-answer ol { margin: 8px 0 0 20px; padding: 0; }
.short-answer li { margin: 4px 0; }
.short-answer a { color: var(--accent-ink); }
