/* aplyx.app marketing site, plain CSS, no framework.
 *
 * Theme tokens mirror src/tauri/src/styles/tokens.css's Moss palette (the
 * desktop app's actual default as of the 2026-08-22 retheme: a muted
 * forest green with warm honey/clay notes on a parchment/ink ground,
 * replacing the earlier "Calm Cobalt" blue this site ran for a while),
 * so the site feels like the same product rather than a separately-
 * designed one. Mode works exactly like the desktop app: no data-theme
 * attribute = prefers-color-scheme decides; an explicit
 * [data-theme="light"|"dark"] (set by the toggle in site.js, persisted to
 * localStorage) always wins over the media query. */

@view-transition {
  navigation: auto;
}

/* Self-hosted variable Inter + Manrope, same files as the desktop app
   (src/tauri/src/assets/fonts/): no CDN fetch, consistent with the
   project's offline-first ethos. Manrope carries headlines (--font-display
   below), Inter carries body copy: the app's own default pairing, not
   Inter alone for everything like this site used to run. Inter's variable
   file covers the whole 100-900 weight range. */
@font-face {
  font-family: "Inter";
  src: url("assets/fonts/Inter-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Manrope";
  src: url("assets/fonts/Manrope-Variable.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: light;

  --ground: #f5f2ea;
  --surface: #ffffff;
  --surface-raised: #ede8db;
  --border: #ddd5c2;
  --border-strong: #c3b89e;
  --text: #2b2820;
  --text-muted: #655d4c;
  --text-faint: #948b75;

  --accent: #3f6b4a;
  --accent-strong: #2e5539;
  --accent-soft: #e3e9de;
  --accent-ink: #ffffff;

  /* Status-tracking demo colors (features.html): same 5-role palette
     proposed in docs/application-status-tracking-plan.md for the real
     app (good/info/special/danger, plus muted via --text-faint), same
     good/danger hex values as src/tauri/src/styles/tokens.css's Moss
     light palette, so this demo is an accurate preview of the real
     design, not a one-off invented for the marketing site. */
  --good: #3d7a52;
  --good-soft: #e3efe6;
  --info: #3d6e96;
  --info-soft: #e2ebf1;
  --special: #7a5c8a;
  --special-soft: #eee7f0;
  --danger: #a83f33;
  --danger-soft: #f5e4e1;

  /* Manrope (display) + Inter (body): the app's own default pairing,
     not a generic single sans-serif stack. */
  --font-display: "Manrope", ui-sans-serif, -apple-system, "SF Pro Text", "Segoe UI", Roboto, "Helvetica Neue", Arial,
    sans-serif;
  --font-body: "Inter", ui-sans-serif, -apple-system, "SF Pro Text", "Segoe UI", Roboto, "Helvetica Neue", Arial,
    sans-serif;

  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.375rem;
  --text-2xl: 1.75rem;
  --text-3xl: 2.5rem;
  --text-4xl: 3.5rem;

  --leading-tight: 1.1;
  --leading-normal: 1.6;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4.5rem;
  --space-9: 7rem;

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-pill: 999px;

  --shadow-lift: 0 16px 36px -14px rgb(40 32 20 / 0.18);
  --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);

  /* Entrance/scroll-reveal motion: orientation only, tuned down in dark
     mode below per the research note that fast motion reads as more
     jarring in low light. */
  --duration-quick: 120ms;
  --duration-normal: 220ms;
  --duration-slow: 420ms;
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
  /* Snappier "ease-out-expo" shape for hover micro-interactions only,
     distinct from --ease-out's entrance role, same separation of concerns
     tokens.css already keeps for the desktop app. */
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  /* Press feedback must land on pointer-down, not ease in over a hover
     duration (Designing Fluid Interfaces, WWDC18): the press-down is
     near-instant, the release settles with a gentle spring-like
     overshoot. Used only for the momentary :active scale on buttons. */
  --duration-press: 90ms;
  --ease-press: cubic-bezier(0.3, 0, 0.2, 1);
  --ease-press-release: cubic-bezier(0.34, 1.4, 0.64, 1);
  --reveal-rise: 16px;
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --ground: #17140f;
    --surface: #1e1b14;
    --surface-raised: #26221a;
    --border: rgba(127, 174, 134, 0.18);
    --border-strong: rgba(127, 174, 134, 0.32);
    --text: #ede6d6;
    --text-muted: #b0a68e;
    --text-faint: #7a7260;
    --accent: #7fae86;
    --accent-strong: #9cc4a1;
    --accent-soft: rgba(127, 174, 134, 0.14);
    --accent-ink: #17140f;
    --good: #6fbe8a;
    --good-soft: rgba(111, 190, 138, 0.13);
    --info: #7bafd4;
    --info-soft: rgba(123, 175, 212, 0.13);
    --special: #b79ac7;
    --special-soft: rgba(183, 154, 199, 0.13);
    --danger: #e08770;
    --danger-soft: rgba(224, 135, 112, 0.13);
    --shadow-lift: 0 16px 40px -14px rgb(0 0 0 / 0.55);
    --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent);
    --duration-normal: 180ms;
    --duration-slow: 340ms;
    --reveal-rise: 10px;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --ground: #17140f;
  --surface: #1e1b14;
  --surface-raised: #26221a;
  --border: rgba(127, 174, 134, 0.18);
  --border-strong: rgba(127, 174, 134, 0.32);
  --text: #ede6d6;
  --text-muted: #b0a68e;
  --text-faint: #7a7260;
  --accent: #7fae86;
  --accent-strong: #9cc4a1;
  --accent-soft: rgba(127, 174, 134, 0.14);
  --accent-ink: #17140f;
  --good: #6fbe8a;
  --good-soft: rgba(111, 190, 138, 0.13);
  --info: #7bafd4;
  --info-soft: rgba(123, 175, 212, 0.13);
  --special: #b79ac7;
  --special-soft: rgba(183, 154, 199, 0.13);
  --danger: #e08770;
  --danger-soft: rgba(224, 135, 112, 0.13);
  --shadow-lift: 0 16px 40px -14px rgb(0 0 0 / 0.55);
  --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent);
  --duration-normal: 180ms;
  --duration-slow: 340ms;
  --reveal-rise: 10px;
}

:root[data-theme="light"] {
  color-scheme: light;
  --ground: #f5f2ea;
  --surface: #ffffff;
  --surface-raised: #ede8db;
  --border: #ddd5c2;
  --border-strong: #c3b89e;
  --text: #2b2820;
  --text-muted: #655d4c;
  --text-faint: #948b75;
  --accent: #3f6b4a;
  --accent-strong: #2e5539;
  --accent-soft: #e3e9de;
  --accent-ink: #ffffff;
  --good: #3d7a52;
  --good-soft: #e3efe6;
  --info: #3d6e96;
  --info-soft: #e2ebf1;
  --special: #7a5c8a;
  --special-soft: #eee7f0;
  --danger: #a83f33;
  --danger-soft: #f5e4e1;
  --shadow-lift: 0 16px 36px -14px rgb(40 32 20 / 0.18);
  --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
  --duration-normal: 220ms;
  --duration-slow: 420ms;
  --reveal-rise: 16px;
}

/* --- Dashboard workspace palette (account.html, signed-in only) ----------
   The marketing site is warm (Moss / cream). The signed-in dashboard is a
   product surface — an admin console — so it gets its own calm, low-chroma
   workspace palette while keeping aplyx's green --accent for actions.
   Every token here is consumed only under
   `body:has(#dashboard-panel:not([hidden]))` further down; the rest of the
   site never sees them. Defined in the same four places as the palette
   above so the manual theme toggle wins in both directions. */
:root {
  --dash-canvas: #f6f6f7;
  --dash-surface: #ffffff;
  --dash-sunk: #fbfbfc;
  --dash-border: #e4e4e7;
  --dash-border-strong: #d3d3d8;
  --dash-text: #1f1f23;
  --dash-muted: #5f5f67;
  --dash-faint: #8a8a92;
  --dash-shadow: 0 1px 2px rgb(20 20 26 / 0.06), 0 1px 3px rgb(20 20 26 / 0.04);
}
@media (prefers-color-scheme: dark) {
  :root {
    --dash-canvas: #121215;
    --dash-surface: #1c1c20;
    --dash-sunk: #17171a;
    --dash-border: #2c2c32;
    --dash-border-strong: #3b3b43;
    --dash-text: #ececee;
    --dash-muted: #a1a1a9;
    --dash-faint: #73737b;
    --dash-shadow: 0 1px 2px rgb(0 0 0 / 0.44), 0 1px 3px rgb(0 0 0 / 0.32);
  }
}
:root[data-theme="dark"] {
  --dash-canvas: #121215;
  --dash-surface: #1c1c20;
  --dash-sunk: #17171a;
  --dash-border: #2c2c32;
  --dash-border-strong: #3b3b43;
  --dash-text: #ececee;
  --dash-muted: #a1a1a9;
  --dash-faint: #73737b;
  --dash-shadow: 0 1px 2px rgb(0 0 0 / 0.44), 0 1px 3px rgb(0 0 0 / 0.32);
}
:root[data-theme="light"] {
  --dash-canvas: #f6f6f7;
  --dash-surface: #ffffff;
  --dash-sunk: #fbfbfc;
  --dash-border: #e4e4e7;
  --dash-border-strong: #d3d3d8;
  --dash-text: #1f1f23;
  --dash-muted: #5f5f67;
  --dash-faint: #8a8a92;
  --dash-shadow: 0 1px 2px rgb(20 20 26 / 0.06), 0 1px 3px rgb(20 20 26 / 0.04);
}

* {
  box-sizing: border-box;
}

/* Author stylesheet rules always beat the UA stylesheet's own
   `[hidden] { display: none }`, regardless of specificity, so any
   element that both carries the `hidden` attribute AND matches an
   author `display:` rule (e.g. account.html's #dashboard-panel, which
   also matches .dashboard-shell { display: grid }) renders visible
   despite being hidden, a real bug found live: the signed-out dashboard
   was showing empty stats under the sign-in form, worst on mobile's
   single-column stacking. `!important` here is the correct, narrow use
   of it: `[hidden]` must always mean hidden, full stop, and this is
   the one rule in the whole file allowed to force that. */
[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
  padding: 0;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  background: var(--ground);
  color: var(--text);
  font-family: var(--font-body);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
}

/* Letter-spacing is size-specific, never one flat value (The Details of
   UI Typography, WWDC20): tight tracking that reads right on a 3.5rem
   hero reads cramped on a 1.125rem card heading. No default here; every
   concrete heading selector below sets its own tracking, tiered to its
   own font-size (-0.02em at --text-4xl down to -0.002em at --text-lg;
   normal/0 below that). */
h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 750;
  line-height: var(--leading-tight);
  margin: 0;
}

p {
  margin: 0;
}

a {
  color: inherit;
}

/* Genuine inline content links: inside a paragraph, not the top nav or
   footer (those are positionally obvious as navigation already, and
   keep their own deliberate muted styling; recoloring them here too
   would just add visual noise to chrome that isn't the point). This is
   for the handful of links that sit inside actual prose, where a reader
   has no other cue that a word is clickable. Visited state is tracked
   in sessionStorage (site.js), not a real :visited, deliberately session
   -scoped, not permanent browsing history, and also sidesteps the
   privacy-driven restrictions modern browsers place on :visited styling
   (color-only, and some browsers partition it per top-level site anyway,
   which would make it unreliable for this even if it fit the "just this
   session" requirement it doesn't). */
a.link {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
  text-decoration-thickness: 1px;
  transition: color var(--duration-quick) var(--ease-out-expo);
}

a.link:hover {
  color: var(--accent-strong);
}

a.link.is-visited {
  color: var(--text-faint);
  text-decoration-color: color-mix(in srgb, var(--text-faint) 50%, transparent);
}

a.link.is-visited:hover {
  color: var(--text-muted);
}

img {
  max-width: 100%;
  display: block;
}

button {
  font-family: inherit;
  cursor: pointer;
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

/* Keyboard-only "skip to content": off-screen until focused, then a real
 * visible chip at the top-left. First tab stop on every page. */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -3rem;
  z-index: 100;
  padding: var(--space-2) var(--space-4);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  transition: top var(--duration-quick) var(--ease-out);
}

.skip-link:focus-visible {
  top: var(--space-4);
}

main:focus-visible {
  outline: none;
  box-shadow: none;
}

.wrap {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 var(--space-6);
}

/* --- Liquid glass -----------------------------------------------------------
   One material, tuned once. Translucent tint + blur + a touch of saturation
   for the "wet" refraction, a bright inset top edge where light catches the
   pane, a soft inset floor shadow for thickness, and a real drop shadow so
   the surface reads as floating. Bigger surfaces (nav) get more blur and a
   deeper shadow than small ones (buttons, chips), per Apple's "bigger =
   thicker" rule. Falls back to a solid surface under
   prefers-reduced-transparency. */
:root {
  --glass-tint: rgba(255, 255, 255, 0.55);
  --glass-tint-strong: rgba(255, 255, 255, 0.72);
  --glass-blur: 18px;
  --glass-edge: rgba(255, 255, 255, 0.9);
  --glass-inner: inset 0 1px 0 0 var(--glass-edge), inset 0 -1px 1px 0 rgba(120, 130, 115, 0.12);
  --glass-shadow: 0 10px 30px -12px rgba(40, 50, 35, 0.22), 0 2px 8px -4px rgba(40, 50, 35, 0.14);
}
@media (prefers-color-scheme: dark) {
  :root {
    --glass-tint: rgba(30, 34, 27, 0.55);
    --glass-tint-strong: rgba(30, 34, 27, 0.74);
    --glass-edge: rgba(180, 220, 185, 0.16);
    --glass-inner: inset 0 1px 0 0 var(--glass-edge), inset 0 -1px 1px 0 rgba(0, 0, 0, 0.3);
    --glass-shadow: 0 12px 34px -12px rgba(0, 0, 0, 0.6), 0 2px 10px -4px rgba(0, 0, 0, 0.5);
  }
}
:root[data-theme="dark"] {
  --glass-tint: rgba(30, 34, 27, 0.55);
  --glass-tint-strong: rgba(30, 34, 27, 0.74);
  --glass-edge: rgba(180, 220, 185, 0.16);
  --glass-inner: inset 0 1px 0 0 var(--glass-edge), inset 0 -1px 1px 0 rgba(0, 0, 0, 0.3);
  --glass-shadow: 0 12px 34px -12px rgba(0, 0, 0, 0.6), 0 2px 10px -4px rgba(0, 0, 0, 0.5);
}
:root[data-theme="light"] {
  --glass-tint: rgba(255, 255, 255, 0.55);
  --glass-tint-strong: rgba(255, 255, 255, 0.72);
  --glass-edge: rgba(255, 255, 255, 0.9);
  --glass-inner: inset 0 1px 0 0 var(--glass-edge), inset 0 -1px 1px 0 rgba(120, 130, 115, 0.12);
  --glass-shadow: 0 10px 30px -12px rgba(40, 50, 35, 0.22), 0 2px 8px -4px rgba(40, 50, 35, 0.14);
}

.glass {
  background: var(--glass-tint);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  box-shadow: var(--glass-inner), var(--glass-shadow);
  border: 1px solid color-mix(in srgb, var(--glass-edge) 60%, transparent);
}

@media (prefers-reduced-transparency: reduce) {
  :root {
    --glass-tint: var(--surface);
    --glass-tint-strong: var(--surface);
  }
  .glass,
  .site-nav {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* --- Nav --- */

.site-nav {
  position: sticky;
  top: 0;
  z-index: 10;
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  background: var(--glass-tint);
  border-bottom: 1px solid transparent;
  box-shadow: inset 0 -1px 0 0 color-mix(in srgb, var(--glass-edge) 40%, transparent);
  transition:
    background var(--duration-normal) var(--ease-standard),
    border-color var(--duration-normal) var(--ease-standard),
    box-shadow var(--duration-normal) var(--ease-standard);
}

.site-nav.is-scrolled {
  background: var(--glass-tint-strong);
  border-bottom-color: color-mix(in srgb, var(--border) 60%, transparent);
  /* Scroll-edge effect: a soft shadow where floating chrome meets the
     content scrolling under it, not a hard 1px rule. */
  box-shadow:
    inset 0 1px 0 0 var(--glass-edge),
    0 10px 30px -18px rgb(0 0 0 / 0.45);
}

/* Nav links sit close to the logo rather than stretched to the far edge
   of the bar: space-between put a growing dead gap between them as
   the viewport widened, worse once the Extension link made the group
   itself wider too. Reverts to space-between on mobile (below), where
   the toggle/theme buttons need to stay pinned to the right edge. */
.site-nav .wrap {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-7);
  padding-top: var(--space-4);
  padding-bottom: var(--space-4);
}

.logo-lockup {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: var(--text);
}

.logo-lockup img {
  width: 30px;
  height: 30px;
  border-radius: var(--radius-sm);
}

.logo-wordmark {
  font-family: var(--font-body);
  font-size: var(--text-xl);
  font-weight: 650;
  letter-spacing: -0.02em;
}

/* Grows to fill the rest of .wrap (which no longer stretches nav-actions
   there itself via space-between, see .site-nav .wrap) so the
   margin-left: auto on .nav-link-account below has room to push the
   account link and everything after it to the far right edge, while
   .nav-links stays put, close to the logo. */
.nav-actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex: 1;
  min-width: 0;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.nav-link {
  position: relative;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-muted);
  text-decoration: none;
  transition: color var(--duration-quick) var(--ease-out-expo);
}

.nav-link:hover {
  color: var(--text);
}

.nav-link.is-active {
  color: var(--text);
}

/* Smooth underline wipe on hover: slides in from the left, out to the
   right, rather than a plain instant show/hide. */
.nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform var(--duration-normal) var(--ease-out-expo);
}

.nav-link:hover::after,
.nav-link.is-active::after {
  transform: scaleX(1);
  transform-origin: left;
}

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  transition:
    transform var(--duration-quick) var(--ease-out-expo),
    border-color var(--duration-quick) var(--ease-out-expo),
    color var(--duration-quick) var(--ease-out-expo);
}

.theme-toggle:hover {
  color: var(--text);
  border-color: var(--border-strong);
}

@media (hover: hover) and (pointer: fine) {
  .theme-toggle:hover {
    transform: translateY(-1px);
  }
}

.theme-toggle:active {
  transform: scale(0.92);
}

.theme-toggle svg {
  width: 1.1rem;
  height: 1.1rem;
}

/* Icon swap: default (light) shows the moon (click to go dark); dark
   mode shows the sun (click to go light). */
.theme-toggle .icon-sun {
  display: none;
}
.theme-toggle .icon-moon {
  display: inline-flex;
}
:root[data-theme="dark"] .theme-toggle .icon-sun {
  display: inline-flex;
}
:root[data-theme="dark"] .theme-toggle .icon-moon {
  display: none;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun {
    display: inline-flex;
  }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon {
    display: none;
  }
}

/* Mobile nav: hidden above the breakpoint (plain inline links), collapses
   to a hamburger below it so the four nav links + GitHub + theme toggle
   never fight the logo wordmark for space (was overlapping around ~400px
   with no collapse at all). */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  flex-direction: column;
  gap: 4px;
  transition: transform var(--duration-quick) var(--ease-out-expo), border-color var(--duration-quick) var(--ease-out-expo);
}

.nav-toggle:active {
  transform: scale(0.92);
}

@media (hover: hover) and (pointer: fine) {
  .nav-toggle:hover {
    border-color: var(--border-strong);
  }
}

.nav-toggle-bar {
  display: block;
  width: 1rem;
  height: 1.5px;
  background: var(--text-muted);
  border-radius: 1px;
  transition: transform var(--duration-quick) var(--ease-out-expo), opacity var(--duration-quick) var(--ease-out-expo);
}

/* Hamburger -> X, same "snappy" easing as the rest of the nav's
   micro-interactions. */
.nav-toggle.is-open .nav-toggle-bar:nth-child(1) {
  transform: translateY(5.5px) rotate(45deg);
}
.nav-toggle.is-open .nav-toggle-bar:nth-child(2) {
  opacity: 0;
}
.nav-toggle.is-open .nav-toggle-bar:nth-child(3) {
  transform: translateY(-5.5px) rotate(-45deg);
}

@media (max-width: 46rem) {
  .site-nav .wrap {
    justify-content: space-between;
  }

  .nav-toggle {
    display: inline-flex;
  }

  .nav-links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--space-2) var(--space-5) var(--space-4);
    background: var(--ground);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 16px 32px -24px rgb(0 0 0 / 0.5);

    /* Closed by default: clipped to zero height rather than
       display:none, so the open/close has something to transition. */
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    transition: max-height var(--duration-normal) var(--ease-standard), opacity var(--duration-quick) var(--ease-standard);
  }

  .nav-links.is-open {
    max-height: 20rem;
    opacity: 1;
  }

  .nav-links .nav-link {
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--border);
  }

  .nav-links .nav-link:last-child {
    border-bottom: none;
  }

  .nav-links .nav-link::after {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav-links {
    transition: none;
  }
  .nav-toggle-bar {
    transition: none;
  }
}

/* --- Sticky mobile CTA (index.html, pricing.html only) --- */
/* Hidden by default (desktop/tablet already show a CTA above the fold);
   fixed to the viewport bottom below the nav's own 46rem breakpoint,
   where the hero CTA has scrolled well out of reach. */
.sticky-mobile-cta {
  display: none;
}

@media (max-width: 46rem) {
  body.has-sticky-cta {
    padding-bottom: 4.5rem;
  }

  .sticky-mobile-cta {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 20;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-5) calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    background: color-mix(in srgb, var(--ground) 92%, transparent);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-top: 1px solid var(--border);
    box-shadow: 0 -8px 24px -20px rgb(0 0 0 / 0.4);
  }

  .sticky-mobile-cta .btn {
    flex: 1;
    justify-content: center;
  }
}

/* --- Hero --- */

.hero {
  position: relative;
  padding: var(--space-9) 0 var(--space-8);
  text-align: center;
  overflow: hidden;
}

.hero::before {
  content: "";
  position: absolute;
  top: -12rem;
  left: 50%;
  width: 60rem;
  height: 40rem;
  transform: translateX(-50%);
  background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 20%, transparent), transparent 70%);
  pointer-events: none;
  z-index: -1;
}

/* App screenshots peeking out from behind the headline: sit above the
   glow (::before, z-index -1) but below the actual hero text/CTAs
   (.hero-content, z-index 1), so the copy always stays crisp and
   readable on top of them.
   Constrained to the SAME centered, max-72rem column as .wrap (not the
   raw viewport), anchoring the peek offsets to the readable content
   column, rather than to 100vw, is what keeps them from overflowing
   unpredictably: on a very wide window they stay near the text instead
   of stretching out toward the browser edges, and on a narrower one
   there's less width for them to spill past in the first place. */
.hero-visuals {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 var(--space-6);
  z-index: 0;
  pointer-events: none;
}

.hero-content {
  position: relative;
  z-index: 1;
}

/* A soft, feathered scrim directly behind the headline/subhead/CTAs:
   z-index -1 relative to .hero-content's OWN stacking context (it still
   established one via position:relative + z-index:1 above), so this sits
   behind the text but stays above .hero-visuals overall. Without this,
   pushing the mockups down to clear the H1 (see the note further down)
   just moved the legibility problem onto the subhead instead: its
   --text-muted color has real contrast against var(--ground), but not
   against a bright screenshot glimpsed through 45%-opacity glass behind
   it. A hard-edged panel would solve that too, but would also flatly
   hide the mockups anywhere it covered them; this fades out via the
   radial gradient's transparent outer stop, so it only ever affects the
   region directly behind the text, and the mockups stay fully visible
   everywhere else, including the inner edges peeking in beside the
   headline. */
.hero-content::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: -2.5rem -6rem;
  background: radial-gradient(ellipse 60% 65% at center, var(--ground) 0%, var(--ground) 40%, transparent 72%);
  pointer-events: none;
}

/* The "glass" card around each mockup: translucent + blurred, so the
   image/terminal reads as sitting behind a pane of frosted glass rather
   than as a plain flat screenshot pasted on the page. */
.hero-mockup {
  position: absolute;
  width: 20rem;
  max-width: 28vw;
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in srgb, var(--border-strong) 70%, transparent);
  background: color-mix(in srgb, var(--surface) 45%, transparent);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
  opacity: 0;
  transition:
    opacity var(--duration-slow) var(--ease-out),
    transform var(--duration-slow) var(--ease-out),
    filter var(--duration-slow) var(--ease-out);
}

/* Higher specificity than the generic [data-reveal] rule (class + attribute
   beats attribute alone) so these combined rotate+scale transforms win
   outright, regardless of which rule comes first in the file; the
   staggered reveal script (site.js) still just toggles .is-visible,
   unaware anything here differs from a plain fade-and-rise. Filter is
   repeated explicitly here (matching the generic rule's blur-to-focus)
   since this whole `transition` shorthand wins over the generic rule's
   for any element matching both: omitting it here would silently drop
   the blur transition, not just fall back to the generic one. */
/* top offsets are deliberately large enough to clear the H1's own
   height (2 lines at --text-4xl): confirmed live that a smaller offset
   let the card's chrome bar/tab row sit UNDER the headline's z-index,
   visually bisected by the opaque text on top of it, which read as a
   rendering bug rather than an intentional "peeking from behind" look.
   Clearing the headline means only the subhead/whitespace below it ever
   overlaps a card, which is a much smaller, lighter-weight collision. */
.hero-mockup-desktop[data-reveal] {
  top: 12.5rem;
  left: -0.5rem;
  transform: rotate(-6deg) translateY(24px) scale(0.96);
  filter: blur(6px);
}
.hero-mockup-desktop[data-reveal].is-visible {
  opacity: 1;
  transform: rotate(-6deg) translateY(0) scale(1);
  filter: blur(0);
}

.hero-mockup-tui[data-reveal] {
  top: 15rem;
  right: -0.5rem;
  width: 22rem;
  max-width: 32vw;
  transform: rotate(5deg) translateY(24px) scale(0.96);
  filter: blur(6px);
}
.hero-mockup-tui[data-reveal].is-visible {
  opacity: 1;
  transform: rotate(5deg) translateY(0) scale(1);
  filter: blur(0);
}

.hero-mockup img {
  display: block;
  width: 100%;
}

.hero-mockup-chrome {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid color-mix(in srgb, var(--border-strong) 60%, transparent);
  background: color-mix(in srgb, var(--surface-raised) 60%, transparent);
}

.hero-mockup-dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
}
.hero-mockup-dot-red {
  background: #ef5350;
}
.hero-mockup-dot-yellow {
  background: #f6c344;
}
.hero-mockup-dot-green {
  background: #4caf6d;
}

.hero-mockup-chrome-title {
  margin-left: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

/* --- TUI mockup: plain HTML/CSS, not a screenshot; a terminal render
   can't be captured the same way the desktop app's browser-based UI can.
   Colors and labels are the TUI's real ones (src/tui/src/theme.ts's "Aplyx
   Default" moss accent #7FAE86; tab names and [x]/[ ] row styling from
   src/tui/src/ui/App.tsx and SearchScreen.tsx) so this reads as an accurate
   likeness rather than an invented mockup. --- */
.term {
  padding: var(--space-4);
  background: #0d0d12;
  color: #d8dae0;
  font-family: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
  font-size: 0.75rem;
  line-height: 1.9;
}

.term-tabs {
  display: flex;
  gap: 0.3rem;
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid #2a2a35;
  color: #8b8fa3;
  font-size: 0.65rem;
  white-space: nowrap;
  /* Hard clip boundary of its own: a rounded, backdrop-filtered
     ancestor's own `overflow: hidden` isn't always reliable at clipping
     descendants in every browser (confirmed live: without this, an
     overflowing tab row rendered outside the card's frame entirely
     rather than being cut off by it). This plain box has no
     backdrop-filter of its own, so its clip is unconditional. */
  overflow: hidden;
}

.term-tab-active {
  color: #7fae86;
  font-weight: 700;
}

.term-row {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.term-check {
  color: #565a6e;
}

.term-check-on {
  color: #4caf6d;
}

/* The mockup reads as a live session, not a static screenshot: a light
   sweep continuously passes over the job rows (ambient "processing"),
   and two of the four rows loop their checkbox between pending and
   applied on a staggered cycle. The "[x]" layer sits absolutely over the
   static "[ ]" (same 3-character cell, same monospace font, so they
   align exactly) and fades in and back out repeatedly. */
.term-rows {
  position: relative;
}

.term-rows::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 40%,
    color-mix(in srgb, #7fae86 22%, transparent) 50%,
    transparent 60%
  );
  pointer-events: none;
  will-change: transform;
}

.hero-mockup-tui[data-reveal].is-visible .term-rows::after {
  animation: term-scan-sweep 4s linear infinite;
}

/* transform, not background-position: a background-position animation
   repaints on every frame (main-thread bound), which can stall or skip
   under any contention and visually reads as the sweep stopping mid-way
   and snapping back. translateX runs on the compositor thread instead,
   immune to that class of stutter. */
@keyframes term-scan-sweep {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(100%);
  }
}

.term-check-wrap {
  position: relative;
  display: inline-block;
  width: 3ch;
}

.term-check-anim {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
}

.hero-mockup-tui[data-reveal].is-visible .term-check-anim {
  animation: term-check-loop 4.5s ease-in-out infinite;
}

/* Hard cut, not a fade: the layer underneath stays fully opaque the
   whole time (it's a static base state, not itself animated), so ANY
   nonzero transition window meant both layers were visibly blended
   together: two different words overlapping reads as garbled at any
   duration, not just a long one. 0.1% of a 4.5s animation is ~4.5ms,
   under a single frame, so the swap reads as instant. */
@keyframes term-check-loop {
  0%,
  0.1% {
    opacity: 0;
  }
  0.11%,
  54.9% {
    opacity: 1;
  }
  55%,
  100% {
    opacity: 0;
  }
}

.term-cursor {
  display: inline-block;
  width: 0.5em;
  height: 1em;
  margin-left: 0.3em;
  vertical-align: -0.15em;
  background: #7fae86;
  animation: term-cursor-blink 1s step-end infinite;
}

@keyframes term-cursor-blink {
  0%,
  50% {
    opacity: 1;
  }
  50.01%,
  100% {
    opacity: 0;
  }
}

.term-hint {
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  border-top: 1px solid #2a2a35;
  color: #8b8fa3;
  font-size: 0.72rem;
}

@media (max-width: 72rem) {
  /* Below this width there isn't comfortable room beside the headline
     for both mockups without them crowding the text or each other:
     hidden rather than repositioned, so the hero stays a clean,
     text-first read on tablet/mobile widths instead. */
  .hero-visuals {
    display: none;
  }
}

/* Small "peek" accent for the /features TUI+desktop row: the same
   glass-card treatment as the hero mockups, just scaled down. A normal
   flex sibling (margin-left: auto) rather than absolutely positioned,
   since .feature-row's icon+text fills the row edge-to-edge and this
   sidesteps any text-collision risk entirely rather than trying to
   carve out safe empty space for it. Reveals off the parent's own
   .is-visible (added by the same staggered-reveal observer already
   watching the parent's [data-reveal]) rather than needing a separate
   reveal target, with a short delay so it settles in just after the
   parent's own fade/rise finishes. */
.feature-row-peek {
  flex-shrink: 0;
  margin-left: auto;
  align-self: center;
  width: 6.5rem;
  height: 4.4rem;
  border-radius: var(--radius-md);
  border: 1px solid color-mix(in srgb, var(--border-strong) 70%, transparent);
  background: var(--surface);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
  opacity: 0;
  transform: translateY(10px) scale(0.92) rotate(-3deg);
  filter: blur(4px);
  transition:
    opacity var(--duration-slow) var(--ease-out) 80ms,
    transform var(--duration-slow) var(--ease-out) 80ms,
    filter var(--duration-slow) var(--ease-out) 80ms;
}

.feature-row-peek img {
  display: block;
  width: 230%;
  max-width: none;
  height: auto;
  object-fit: cover;
  object-position: top left;
}

.feature-row.is-visible .feature-row-peek {
  opacity: 1;
  transform: translateY(0) scale(1) rotate(-3deg);
  filter: blur(0);
}

@media (max-width: 30rem) {
  /* Nowhere comfortable for it to sit once the row lays out at full
     mobile width without crowding the row's own text: simplest to
     hide it there rather than fight for the space. */
  .feature-row-peek {
    display: none;
  }
}

.hero-lockup {
  display: inline-flex;
  margin-bottom: var(--space-6);
}

.hero-lockup img {
  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-md);
}

.hero h1 {
  font-size: var(--text-4xl);
  letter-spacing: -0.02em;
  max-width: 40rem;
  margin: 0 auto;
}

.hero-subhead {
  margin: var(--space-5) auto 0;
  max-width: 34rem;
  color: var(--text-muted);
  font-size: var(--text-lg);
}

.hero-ctas {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-7);
  flex-wrap: wrap;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: 650;
  letter-spacing: -0.005em;
  text-decoration: none;
  border: 1px solid transparent;
  /* Release: the spring-ish curve so the button "settles" back up.
     :active below overrides transform with the fast press curve so the
     press itself is instant. */
  transition:
    transform var(--duration-normal) var(--ease-press-release),
    box-shadow var(--duration-quick) var(--ease-out-expo),
    background var(--duration-quick) var(--ease-out-expo),
    border-color var(--duration-quick) var(--ease-out-expo);
}

.btn-primary {
  background-color: var(--accent);
  color: var(--accent-ink);
  /* A specular sheen across the top third so the solid pill catches light
     like the glass around it. Strong in light mode, barely-there in dark
     (the dark accent is already light — white on it just looks faded). */
  background-image: linear-gradient(180deg, var(--btn-sheen, rgba(255, 255, 255, 0.18)), rgba(255, 255, 255, 0) 45%);
  box-shadow: inset 0 1px 0 0 var(--btn-sheen, rgba(255, 255, 255, 0.22)), 0 6px 16px -8px color-mix(in srgb, var(--accent) 55%, transparent);
}

@media (prefers-color-scheme: dark) {
  :root { --btn-sheen: rgba(255, 255, 255, 0.06); }
}
:root[data-theme="dark"] { --btn-sheen: rgba(255, 255, 255, 0.06); }
:root[data-theme="light"] { --btn-sheen: rgba(255, 255, 255, 0.2); }

.btn-primary:hover {
  background-color: var(--accent-strong);
}

@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover {
    transform: translateY(-1px);
    box-shadow: inset 0 1px 0 0 var(--btn-sheen, rgba(255, 255, 255, 0.28)), 0 12px 26px -10px color-mix(in srgb, var(--accent) 60%, transparent);
  }
}

.btn-secondary {
  color: var(--text);
  background: var(--glass-tint);
  backdrop-filter: blur(12px) saturate(180%);
  -webkit-backdrop-filter: blur(12px) saturate(180%);
  border-color: color-mix(in srgb, var(--glass-edge) 55%, transparent);
  box-shadow: var(--glass-inner);
}

.btn-secondary:hover {
  background: var(--glass-tint-strong);
  border-color: color-mix(in srgb, var(--glass-edge) 80%, transparent);
}

@media (hover: hover) and (pointer: fine) {
  .btn-secondary:hover {
    transform: translateY(-1px);
    box-shadow: var(--glass-inner), 0 10px 24px -14px rgb(0 0 0 / 0.3);
  }
}

/* Direct-download stage: same frosted-glass material as .hero-mockup,
   giving the download buttons a pane of their own to sit on instead of
   floating loose against the page background. */
.download-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-6);
  padding: var(--space-7) var(--space-6);
  border-radius: var(--radius-xl);
  border: 1px solid color-mix(in srgb, var(--border-strong) 60%, transparent);
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  box-shadow: var(--shadow-lift);
}

.direct-download-primary {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
}

.direct-download-status {
  color: var(--text-muted);
  font-size: var(--text-sm);
}

/* The hero download button: platform glyph + two-line label. A primary
   CTA, so it reads as a solid, confident surface (the way Apple's own
   prominent buttons do), not a shiny tinted-glass chip. One quiet
   top-edge highlight where light would catch the rounded lip, a soft
   accent-tinted shadow to seat it on the stage, and nothing that
   animates on its own. */
.download-card {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-6);
  min-width: 15rem;
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: var(--accent-ink);
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent-strong) 55%, transparent);
  box-shadow:
    0 1px 0 0 color-mix(in srgb, white 14%, transparent) inset,
    0 8px 20px -14px color-mix(in srgb, var(--accent) 34%, transparent),
    0 1px 4px -1px color-mix(in srgb, black 12%, transparent);
  transition:
    transform var(--duration-normal) var(--ease-press-release),
    background var(--duration-quick) var(--ease-out-expo),
    box-shadow var(--duration-normal) var(--ease-out-expo);
}

.download-card-icon,
.download-card-text {
  position: relative;
  z-index: 1;
}

.download-card-icon {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
}

.download-card-icon svg {
  width: 100%;
  height: 100%;
}

.download-card-text {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  line-height: 1.25;
}

.download-card-eyebrow {
  font-size: var(--text-xs);
  font-weight: 500;
  opacity: 0.8;
  letter-spacing: 0.02em;
}

.download-card-label {
  font-size: var(--text-lg);
  font-weight: 650;
  /* Negative tracking as the label grows past body size (Details of UI
     Typography, WWDC20): letters read too loose at display size. */
  letter-spacing: -0.012em;
}

.download-card-detail {
  font-weight: 500;
  opacity: 0.85;
}

@media (hover: hover) and (pointer: fine) {
  .download-card:hover {
    transform: translateY(-2px);
    background: var(--accent-strong);
    box-shadow:
      0 1px 0 0 color-mix(in srgb, white 18%, transparent) inset,
      0 16px 32px -14px color-mix(in srgb, var(--accent) 50%, transparent),
      0 3px 8px -2px color-mix(in srgb, black 16%, transparent);
  }
}

/* Press: the card sinks toward the stage it sits on. Instant on
   pointer-down (fast press curve), and the ambient shadow tightens as it
   gets closer to the surface, so it reads as a real object being pushed
   in, not just a shrink. */
.download-card:active {
  transform: translateY(1px) scale(0.975);
  transition:
    transform var(--duration-press) var(--ease-press),
    box-shadow var(--duration-press) var(--ease-press);
  box-shadow:
    0 1px 0 0 color-mix(in srgb, white 12%, transparent) inset,
    0 4px 12px -8px color-mix(in srgb, var(--accent) 40%, transparent),
    0 1px 3px -1px color-mix(in srgb, black 16%, transparent);
}

/* Compact variant: the alt-arch Mac link and the secondary formats
   behind the disclosure. A quiet neutral surface, one hairline border,
   no gloss: it should sit back from the solid primary card. */
.download-card-compact {
  min-width: 0;
  padding: var(--space-3) var(--space-4);
  color: var(--text);
  text-shadow: none;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 1px 2px color-mix(in srgb, black 6%, transparent);
}

.download-card-compact .download-card-icon {
  width: 1.5rem;
  height: 1.5rem;
}

.download-card-compact .download-card-eyebrow {
  display: none;
}

.download-card-compact .download-card-label {
  font-size: var(--text-sm);
  font-weight: 600;
}

@media (hover: hover) and (pointer: fine) {
  .download-card-compact:hover {
    transform: translateY(-1px);
    border-color: var(--border-strong);
    background: var(--surface-raised, var(--surface));
  }
}

.download-card-compact:active {
  transform: translateY(1px) scale(0.98);
  transition: transform var(--duration-press) var(--ease-press);
}

.direct-download-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  border: 1px solid color-mix(in srgb, var(--border-strong) 50%, transparent);
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  backdrop-filter: blur(10px) saturate(150%);
  -webkit-backdrop-filter: blur(10px) saturate(150%);
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 550;
  cursor: pointer;
  transition:
    transform var(--duration-normal) var(--ease-press-release),
    color var(--duration-quick) var(--ease-out-expo),
    border-color var(--duration-quick) var(--ease-out-expo);
}

.direct-download-toggle svg {
  width: 1rem;
  height: 1rem;
  transition: transform var(--duration-normal) var(--ease-out-expo);
}

.direct-download-toggle:hover {
  color: var(--text);
}

.direct-download-toggle:active {
  transform: scale(0.97);
  transition: transform var(--duration-press) var(--ease-press);
}

.direct-download-toggle.is-open svg {
  transform: rotate(180deg);
}

.direct-download-all {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  padding-top: var(--space-2);
}

/* Materialize on reveal rather than a plain fade: blur clears and the
   group scales up from slightly small as it arrives, so the extra
   options read as a surface coming into focus (Apple materials note).
   Runs when the inline script clears the [hidden] attribute. */
.direct-download-all:not([hidden]) {
  animation: materialize-in var(--duration-slow) var(--ease-out-expo);
}

@keyframes materialize-in {
  from {
    opacity: 0;
    transform: scale(0.965) translateY(-4px);
    filter: blur(6px);
  }
  to {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .direct-download-all:not([hidden]) {
    animation: none;
  }
}

@media (max-width: 40rem) {
  .download-stage {
    padding: var(--space-6) var(--space-4);
  }

  .direct-download-primary,
  .direct-download-all {
    width: 100%;
  }

  .download-card,
  .download-card-compact {
    min-width: 0;
    width: 100%;
  }

  .direct-download-toggle {
    width: 100%;
    justify-content: center;
  }
}

/* Comes after both hover blocks so equal-specificity cascade order lets
   press feedback win over the hover-lift while a button is actively held
   down, instead of the two transforms fighting silently. The fast press
   curve here means the scale-down tracks the pointer-down; on release the
   .btn rule's spring-release curve carries it back. */
.btn:active {
  transform: scale(0.96);
  transition: transform var(--duration-press) var(--ease-press);
}

/* --- Bento feature grid --- */

.features {
  padding: var(--space-8) 0 var(--space-9);
}

.features h2 {
  font-size: var(--text-2xl);
  text-align: center;
  letter-spacing: -0.01em;
}

/* Alternating showcase rows: real product screenshots at actual
   legible size (reusing .feature-shot from /features, which already
   proves these same four screenshots read fine at this size) instead
   of the card-grid-with-corner-accent this replaced. Deliberately not
   a card: no per-item border/background box, just a media/copy pair
   whose side alternates so the section doesn't read as a repeated
   tile. */
.showcase-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
  margin-top: var(--space-7);
}

.showcase-row {
  display: flex;
  align-items: center;
  gap: var(--space-6);
}

.showcase-rows .showcase-row:nth-child(even) {
  flex-direction: row-reverse;
}

.showcase-media {
  flex: 0 0 44%;
  min-width: 0;
  margin-top: 0;
}

.showcase-copy {
  flex: 1;
  min-width: 0;
}

.showcase-copy .feature-row-icon {
  margin-bottom: var(--space-3);
}

.showcase-copy h3 {
  font-size: var(--text-xl);
  font-family: var(--font-body);
  letter-spacing: -0.006em;
}

.showcase-copy p {
  margin-top: var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

@media (max-width: 48rem) {
  .showcase-row,
  .showcase-rows .showcase-row:nth-child(even) {
    flex-direction: column;
    align-items: stretch;
  }
  .showcase-media {
    flex-basis: auto;
  }
}

.features-more {
  text-align: center;
  margin-top: var(--space-6);
}

/* Shared centered subhead under a section h2: same role .hero-subhead
   plays for the hero, just without the hero's own layout context. */
.section-subhead {
  max-width: 34rem;
  margin: var(--space-4) auto 0;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--text-lg);
}

/* --- Stats row --- */

.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-6);
  margin: var(--space-7) 0;
  padding: var(--space-6) 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  text-align: center;
}

.stat-number {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 750;
  letter-spacing: -0.02em;
  color: var(--accent);
}

.stat-label {
  max-width: 12rem;
  font-size: var(--text-sm);
  color: var(--text-muted);
}

@media (max-width: 56rem) {
  .stats {
    grid-template-columns: repeat(2, 1fr);
  }
}

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

/* --- How it works --- */

.how {
  padding: var(--space-8) 0;
}

.how h2,
.faq h2,
.cta-banner h2 {
  text-align: center;
  font-size: var(--text-2xl);
  letter-spacing: -0.01em;
}

.steps {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-5);
  margin: var(--space-7) 0 0;
  padding: 0;
  list-style: none;
}

.step {
  position: relative;
  padding: var(--space-6) var(--space-5);
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  background: var(--surface);
  transition:
    transform var(--duration-quick) var(--ease-out-expo),
    border-color var(--duration-quick) var(--ease-out-expo),
    box-shadow var(--duration-quick) var(--ease-out-expo);
}

.step-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--accent);
  margin-bottom: var(--space-4);
}

.step-icon svg {
  width: 1.4rem;
  height: 1.4rem;
}

.step:hover {
  border-color: var(--accent);
}

@media (hover: hover) and (pointer: fine) {
  /* Scoped through .steps (not just .step:hover) so this outranks
     [data-reveal].is-visible's own transform on specificity;
     equal-specificity selectors otherwise let source order decide, and the
     reveal rule sits later in this file, silently winning the `transform`
     property and killing the hover-lift on every card once it's scrolled
     into view. */
  .steps .step:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-lift);
  }
}

.step-number {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
  font-size: var(--text-xs);
}

.step h3 {
  font-size: var(--text-lg);
  letter-spacing: -0.002em;
}

.step p {
  margin-top: var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.step-fact {
  display: inline-block;
  margin-top: var(--space-4);
  padding: 0.2em var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.01em;
}

@media (max-width: 56rem) {
  .steps {
    grid-template-columns: repeat(2, 1fr);
  }
}

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

/* --- Review queue demo: a second live mockup, same techniques as the
   hero TUI panel (term-scan-sweep, term-check-loop) reapplied to the
   desktop app's actual Review queue screen instead of the terminal;
   real row content lifted from the multi-board search screenshot
   elsewhere on this page, for continuity. --- */
.review-demo {
  padding: var(--space-8) 0;
}

.review-demo .wrap {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-8);
  align-items: center;
}

.review-demo-copy h2 {
  font-size: var(--text-2xl);
  letter-spacing: -0.01em;
}

.review-demo-copy p {
  margin-top: var(--space-3);
  color: var(--text-muted);
}

.review-card {
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  background: #0d0d12;
  overflow: hidden;
  box-shadow: var(--shadow-lift);
}

/* .hero-mockup-chrome's default background/text colors are tuned to sit
   on top of a light-in-light-theme card, fine for the hero mockups,
   which have exactly that underneath. This card is unconditionally dark
   (it's a recreation of the app's own dark UI, not theme-following), so
   the chrome bar needs its own fixed-dark colors or its title goes
   low-contrast in light theme. */
.review-card .hero-mockup-chrome {
  background: #17171f;
  border-bottom-color: #2a2a35;
}

.review-card .hero-mockup-chrome-title {
  color: #8b8fa3;
}

/* Log/terminal register, not a card register: deliberately unlike the
   showcase rows above. Monospace throughout, lowercase section labels
   prefixed like a shell prompt, rows read as log lines rather than
   list-item cards. */
.review-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-4);
  color: #d8dae0;
  font-family: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
}

.review-panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-weight: 700;
  font-size: var(--text-sm);
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-3);
  border-bottom: 1px solid #2a2a35;
  letter-spacing: 0.02em;
}

.review-panel-head::before {
  content: "$ ";
  color: #565a6e;
  font-weight: 400;
}

/* .feature-badge's default accent tokens are theme-following, tuned for
   a light-in-light-theme surface (same issue as .hero-mockup-chrome
   above), this panel is unconditionally dark, so it needs its own
   fixed-dark override. Reuses the moss green already used for this
   mockup's scan-sweep accent rather than introducing a new hue. */
.review-panel .feature-badge {
  background: color-mix(in srgb, #7fae86 22%, #16161d);
  color: #9cc4a1;
}

.review-panel-count {
  font-weight: 400;
  color: #8b8fa3;
  font-size: var(--text-xs);
}

.review-rows {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  overflow: hidden;
  border-radius: var(--radius-md);
}

.review-rows::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 40%,
    color-mix(in srgb, #7fae86 20%, transparent) 50%,
    transparent 60%
  );
  pointer-events: none;
  will-change: transform;
}

.review-demo-mockup[data-reveal].is-visible .review-rows::after {
  animation: term-scan-sweep 4.5s linear infinite;
}

.review-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: #16161d;
  font-size: 0.8rem;
}

.review-row-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.review-row-right {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.review-score {
  color: #8b8fa3;
  font-size: 0.75rem;
}

.review-badge-wrap {
  position: relative;
  display: inline-block;
  width: 3.4rem;
  height: 1.05rem;
}

.review-badge {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  font-size: 0.58rem;
  font-weight: 700;
  /* Mixed against the row's actual solid background, not transparent:
     the badges stack (Applied absolutely over Pending), and a
     translucent background let the layer underneath show through for
     the entire time it was "on" (not just during a transition), which
     is what was actually causing the two words to overlap. */
  background: color-mix(in srgb, #f6c344 20%, #16161d);
  color: #f6c344;
}

.review-badge-on {
  background: color-mix(in srgb, #4caf6d 20%, #16161d);
  color: #4caf6d;
  opacity: 0;
}

.review-demo-mockup[data-reveal].is-visible .review-badge-anim {
  animation: term-check-loop 4.5s ease-in-out infinite;
}

/* A job that never reaches the queue at all (fit-check filtered it
   before a human would ever see it), dimmed rather than styled like
   an active row, so it visually reads as "backgrounded," not pending. */
.review-row-skipped {
  background: #131318;
}

.review-row-skipped .review-row-text {
  color: #6b6f80;
}

/* Static single-span pills for statuses that don't animate in this
   demo (skipped, and every status-tracking outcome), same pill shape
   as .review-badge for a consistent visual language, just without the
   two-layer stack .review-badge needs for its Pending<->Applied loop.
   Colors are the dark-theme values of this site's own --info/--special/
   --good status tokens (docs/application-status-tracking-plan.md),
   fixed rather than theme-following since this panel always renders
   dark regardless of site theme. */
.review-status-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.22em 0.65em;
  border-radius: var(--radius-pill);
  font-size: 0.58rem;
  font-weight: 700;
  white-space: nowrap;
  flex-shrink: 0;
}

.review-status-pill-skipped {
  background: color-mix(in srgb, #8b8fa3 18%, #131318);
  color: #8b8fa3;
}

.review-status-pill-oa {
  background: color-mix(in srgb, #7bafd4 20%, #16161d);
  color: #7bafd4;
}

.review-status-pill-interview {
  background: color-mix(in srgb, #b79ac7 20%, #16161d);
  color: #b79ac7;
}

.review-status-pill-offer {
  background: color-mix(in srgb, #6fbe8a 20%, #16161d);
  color: #6fbe8a;
}

@media (max-width: 56rem) {
  .review-demo .wrap {
    grid-template-columns: 1fr;
  }
}

/* --- FAQ --- */

.faq {
  padding: var(--space-8) 0;
}

.faq-list {
  max-width: 42rem;
  margin: var(--space-7) auto 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.faq-item {
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--border);
}

.faq-item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.faq-question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  cursor: pointer;
  list-style: none;
}

.faq-question::-webkit-details-marker {
  display: none;
}

.faq-item h3 {
  font-size: var(--text-lg);
  letter-spacing: -0.002em;
}

.faq-chevron {
  flex-shrink: 0;
  width: 1.1rem;
  height: 1.1rem;
  color: var(--text-muted);
  transition: transform var(--duration-quick) var(--ease-out-expo);
}

.faq-item[open] .faq-chevron {
  transform: rotate(180deg);
}

.faq-item p {
  /* Overrides the UA stylesheet's details:not([open]) > *:not(summary) {
     display: none } so the answer stays in normal flow and can be
     collapsed with an animatable max-height instead of snapping. */
  display: block;
  overflow: hidden;
  max-height: 0;
  margin-top: 0;
  color: var(--text-muted);
  opacity: 0;
  transition:
    max-height var(--duration-normal) var(--ease-standard),
    opacity var(--duration-quick) var(--ease-standard),
    margin-top var(--duration-normal) var(--ease-standard);
}

.faq-item[open] p {
  max-height: 16rem;
  margin-top: var(--space-3);
  opacity: 1;
}

/* --- Final CTA banner --- */

.cta-banner {
  padding: var(--space-8) 0 var(--space-9);
}

.cta-banner .wrap {
  max-width: 34rem;
  text-align: center;
  padding: var(--space-8) var(--space-6);
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 8%, var(--surface)), var(--surface));
}

.cta-banner p {
  margin: var(--space-3) 0 var(--space-6);
  color: var(--text-muted);
}

/* --- Generic content page (features / install / privacy / changelog) --- */

.page-hero {
  padding: var(--space-8) 0 var(--space-4);
  text-align: center;
}

.page-hero h1 {
  font-size: var(--text-3xl);
  letter-spacing: -0.014em;
}

.page-hero p {
  margin: var(--space-4) auto 0;
  max-width: 34rem;
  color: var(--text-muted);
  font-size: var(--text-lg);
}

.content {
  padding: var(--space-6) 0 var(--space-9);
}

.prose {
  max-width: 42rem;
  margin: 0 auto;
}

.prose h2 {
  font-size: var(--text-xl);
  letter-spacing: -0.006em;
  margin-top: var(--space-8);
}

.prose h2:first-child {
  margin-top: 0;
}

.prose p {
  margin-top: var(--space-4);
  color: var(--text-muted);
}

.prose ul {
  margin: var(--space-4) 0 0;
  padding-left: 1.2em;
  color: var(--text-muted);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.prose code {
  font-family: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
  font-size: 0.9em;
  background: var(--surface-raised);
  border-radius: var(--radius-sm);
  padding: 0.15em 0.4em;
}

/* Numbered subsections (1.1, 1.2, ...) inside the privacy page's legal
   version: one step down from .prose h2's numbered top-level sections. */
.prose h3 {
  font-size: var(--text-base);
  margin-top: var(--space-6);
  color: var(--text);
}

/* TL;DR / Fine Print switcher (privacy page): an iOS-style sliding
   toggle rather than tabs: one switch, two flanking labels, the knob
   slides and the track recolors on state change. Panel visibility itself
   still reuses .os-panel's is-active/display mechanism (site.js). */
.privacy-view-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.privacy-view-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-faint);
  cursor: pointer;
  transition: color var(--duration-quick) var(--ease-out);
}

.privacy-view-label:hover {
  color: var(--text-muted);
}

.privacy-view-label.is-active {
  color: var(--text);
}

.apple-toggle {
  position: relative;
  flex-shrink: 0;
  width: 2.75rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12);
  cursor: pointer;
  transition: background var(--duration-normal) var(--ease-standard), transform var(--duration-quick) var(--ease-out-expo);
}

.apple-toggle[aria-checked="true"] {
  background: var(--accent);
}

.apple-toggle:active {
  transform: scale(0.94);
}

.apple-toggle-knob {
  position: absolute;
  top: 0.125rem;
  left: 0.125rem;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  transition: transform var(--duration-normal) var(--ease-standard);
}

.apple-toggle[aria-checked="true"] .apple-toggle-knob {
  transform: translateX(1.25rem);
}

/* Not-yet-attorney-reviewed disclaimer at the top of the legal version:
   same honesty convention as .feature-badge: visible, not buried. */
.legal-disclaimer {
  margin-top: var(--space-5);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* --- Feature rows (/features) --- */

.feature-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin-top: var(--space-7);
}

.feature-row {
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
  padding: var(--space-6);
  border-radius: var(--radius-xl);
  border: 1px solid color-mix(in srgb, var(--glass-edge) 45%, var(--border));
  background: var(--glass-tint);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  box-shadow: var(--glass-inner), 0 8px 24px -18px rgb(0 0 0 / 0.22);
  transition:
    transform var(--duration-quick) var(--ease-out-expo),
    border-color var(--duration-quick) var(--ease-out-expo),
    box-shadow var(--duration-quick) var(--ease-out-expo);
}

.feature-row:hover {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

@media (hover: hover) and (pointer: fine) {
  /* Scoped through .feature-rows: see the .steps .step:hover comment
     above for why: without it, [data-reveal].is-visible's own transform
     wins the cascade tie by source order and this lift never shows. */
  .feature-rows .feature-row:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lift);
  }
}

.feature-row-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--accent);
}

.feature-row-icon svg {
  width: 1.4rem;
  height: 1.4rem;
}

.feature-row h3 {
  font-size: var(--text-lg);
  letter-spacing: -0.002em;
}

.feature-row p {
  margin-top: var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

/* Real product screenshot beneath a row's text: same window-chrome
   treatment as the home page's hero mockups (dots + title bar), but
   static/in-flow rather than absolutely positioned and floating. */
.feature-shot {
  margin-top: var(--space-5);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--surface-raised);
  overflow: hidden;
  box-shadow: var(--shadow-lift);
  opacity: 0;
  transform: translateY(16px) scale(0.98);
  filter: blur(4px);
  transition:
    opacity var(--duration-slow) var(--ease-out),
    transform var(--duration-slow) var(--ease-out),
    filter var(--duration-slow) var(--ease-out);
}

.feature-shot.is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
  filter: blur(0);
}

.feature-shot img {
  display: block;
  width: 100%;
  max-height: 24rem;
  object-fit: cover;
  object-position: top;
}

.feature-shot-chrome {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

/* Product screenshots are genuine renders of the app in each mode, not
   one dark image left to "read fine" on a light card: a dark and light
   capture are stacked in the same grid cell (so they share size/position)
   and crossfade by opacity, gated by the same data-theme/prefers-color-
   scheme dual guard as the theme-toggle icon swap above, so the shot
   always matches whatever mode the toggle (or the OS) currently has
   the site in. */
.shot-stack {
  display: grid;
}

.shot-stack > img {
  grid-area: 1 / 1;
  transition: opacity var(--duration-slow) var(--ease-standard);
}

.shot-stack > .shot-dark {
  opacity: 0;
}
.shot-stack > .shot-light {
  opacity: 1;
}
:root[data-theme="dark"] .shot-stack > .shot-dark {
  opacity: 1;
}
:root[data-theme="dark"] .shot-stack > .shot-light {
  opacity: 0;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .shot-stack > .shot-dark {
    opacity: 1;
  }
  :root:not([data-theme="light"]) .shot-stack > .shot-light {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .shot-stack > img {
    transition: none;
  }
}

/* --- features.html: three numbered spotlight features (a real sequence —
   find, tailor, decide — hence the numbering) alternating side-by-side
   with a real screenshot, followed by a tighter compact grid for the
   supporting features. Two different shapes on purpose: the spotlight
   rhythm signals "this is the product," the grid signals "and this comes
   with it," instead of every feature getting identical visual weight. --- */

.feature-spotlight-section {
  padding-bottom: var(--space-7);
}

.feature-spotlights {
  display: flex;
  flex-direction: column;
  gap: var(--space-9);
}

.feature-spotlight {
  display: flex;
  align-items: center;
  gap: var(--space-8);
}

.feature-spotlight-reverse {
  flex-direction: row-reverse;
}

.feature-spotlight-text {
  flex: 1 1 42%;
  min-width: 0;
}

.feature-spotlight-kicker {
  display: inline-block;
  margin-bottom: var(--space-3);
  color: var(--accent);
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.feature-spotlight-text h2 {
  font-size: var(--text-2xl);
  letter-spacing: -0.012em;
  text-wrap: balance;
}

.feature-spotlight-text p {
  margin-top: var(--space-4);
  color: var(--text-muted);
  font-size: var(--text-base);
  line-height: 1.65;
}

.feature-spotlight-shot {
  flex: 1 1 58%;
  min-width: 0;
  margin-top: 0;
}

.feature-spotlight-shot img {
  max-height: 22rem;
}

@media (max-width: 56rem) {
  .feature-spotlight,
  .feature-spotlight-reverse {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-5);
  }
}

.feature-compact-section {
  padding-top: var(--space-7);
  border-top: 1px solid var(--border);
}

.feature-compact-kicker {
  margin-bottom: var(--space-5);
  color: var(--text-faint);
  font-size: var(--text-xs);
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.feature-compact-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-5);
}

@media (max-width: 46rem) {
  .feature-compact-grid {
    grid-template-columns: 1fr;
  }
}

.feature-compact-card {
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in srgb, var(--glass-edge) 45%, var(--border));
  background: var(--glass-tint);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  box-shadow: var(--glass-inner);
  transition:
    transform var(--duration-quick) var(--ease-out-expo),
    border-color var(--duration-quick) var(--ease-out-expo);
}

@media (hover: hover) and (pointer: fine) {
  .feature-compact-card:hover {
    transform: translateY(-2px);
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  }
}

.feature-compact-card .feature-row-icon {
  margin-bottom: var(--space-4);
}

.feature-compact-card h3 {
  font-size: var(--text-base);
  letter-spacing: -0.002em;
}

.feature-compact-card p {
  margin-top: var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

/* Not-yet-shipped feature marker: same honesty convention as the
   pricing page's "illustrative, not final" note and the privacy page's
   "no server exists today" line: a feature described here that isn't
   built yet gets a visible, unambiguous flag, not silence. */
.feature-badge {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 0.15em var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--text-xs);
  font-weight: 700;
  vertical-align: middle;
  letter-spacing: 0.02em;
}

/* --- Status tracking demo (/features): a live preview of the color +
   glyph system proposed in docs/application-status-tracking-plan.md.
   A row of status pills with a soft "glow" bar that trails the mouse
   across them, smoothed by a CSS transition rather than redrawn every
   frame in JS (site.js just updates the target transform on
   mousemove; the smoothing itself is free, handled by the browser's
   own transition engine, the same technique the theme toggle's
   cross-fade already leans on). --- */
.status-demo {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--surface-raised);
  overflow: hidden;
}

.status-demo-glow {
  position: absolute;
  top: 0;
  left: 0;
  width: 12rem;
  height: 100%;
  border-radius: 50%;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 45%, transparent), transparent 75%);
  transform: translate(-999px, 0);
  transition: transform 550ms var(--ease-standard);
  pointer-events: none;
  z-index: 0;
}

.status-pill {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: 600;
  transition: transform var(--duration-quick) var(--ease-out-expo);
}

.status-demo:hover .status-pill {
  transform: translateY(-1px);
}

.status-pill-applied {
  background: var(--good-soft);
  color: var(--good);
}

.status-pill-oa {
  background: var(--info-soft);
  color: var(--info);
}

.status-pill-interview {
  background: var(--special-soft);
  color: var(--special);
}

.status-pill-offer {
  background: var(--good-soft);
  color: var(--good);
}

.status-pill-rejected {
  background: var(--danger-soft);
  color: var(--danger);
}

@media (max-width: 40rem) {
  /* The mouse-trailing glow has nothing meaningful to do on touch:
     hide it rather than have a static, off-position blur sitting
     behind the pills for anyone who can't hover at all. */
  .status-demo-glow {
    display: none;
  }
}

/* --- Install tabs (/install) --- */

.os-tabs {
  margin-top: var(--space-7);
}

.os-tablist {
  position: relative;
  display: flex;
  gap: var(--space-2);
  border-bottom: 1px solid var(--border);
}

/* One shared underline that travels between tabs instead of each tab
   toggling its own border colour: motion stays continuous, so switching
   platforms reads as one object moving rather than two states swapping
   (Designing Fluid Interfaces). site.js sets --tab-x / --tab-w to the
   active tab's box; the spring-release curve gives it a light settle at
   the end of the slide. Falls back to sitting under the first tab before
   JS runs, and to an instant move under prefers-reduced-motion. */
.os-tablist::after {
  content: "";
  position: absolute;
  bottom: -1px;
  left: 0;
  height: 2px;
  width: var(--tab-w, 0);
  transform: translateX(var(--tab-x, 0));
  background: var(--accent);
  border-radius: 2px;
  transition:
    transform var(--duration-normal) var(--ease-press-release),
    width var(--duration-normal) var(--ease-press-release);
}

.os-tab {
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-muted);
  border: none;
  background: none;
  transition:
    color var(--duration-normal) var(--ease-out-expo),
    transform var(--duration-normal) var(--ease-press-release);
}

@media (hover: hover) and (pointer: fine) {
  .os-tab:hover {
    color: var(--text);
  }
}

.os-tab:active {
  transform: scale(0.95);
  transition: transform var(--duration-press) var(--ease-press);
}

.os-tab.is-active {
  color: var(--accent);
}

@media (prefers-reduced-motion: reduce) {
  .os-tablist::after {
    transition: none;
  }
}

/* Wraps the panel set only (not the tablist above it) so the absolute
   positioning below resolves against this box, not the whole .os-tabs
   column, otherwise an outgoing panel would jump up and overlap the
   tabs instead of sitting exactly where the incoming one sits. Also used
   by privacy.html's TL;DR/Fine-print pair, which reuses .os-panel as-is. */
.os-panels {
  position: relative;
}

/* Real crossfade, not a one-way fade-in: the outgoing panel stays put and
   fades out while the incoming one fades in over it, instead of vanishing
   via display:none the instant its class flips. The active panel is the
   only one in normal flow (position: static), so it alone sets the
   container's height: same height every switch, no jump between the
   mac/win/npm panels' slightly different content lengths. Inactive panels
   overlay via top/left/right only (no `bottom`), so each keeps its own
   natural height instead of being stretched to match the active one's. */
.os-panel {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  padding-top: var(--space-6);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--duration-normal) var(--ease-standard),
    visibility 0s linear var(--duration-normal);
}

.os-panel.is-active {
  position: static;
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity var(--duration-normal) var(--ease-standard);
}

@media (prefers-reduced-motion: reduce) {
  .os-panel,
  .os-panel.is-active {
    transition: none;
  }
}

.code-block {
  position: relative;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--surface-raised);
  padding: var(--space-4) calc(var(--space-5) + 2.25rem) var(--space-4) var(--space-5);
  overflow-x: auto;
}

.code-block pre {
  margin: 0;
  font-family: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
  font-size: var(--text-sm);
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
}

.copy-button {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  transition:
    transform var(--duration-normal) var(--ease-press-release),
    color var(--duration-quick) var(--ease-out-expo),
    border-color var(--duration-quick) var(--ease-out-expo);
}

.copy-button:hover {
  color: var(--text);
  border-color: var(--border-strong);
}

.copy-button:active {
  transform: scale(0.9);
  transition: transform var(--duration-press) var(--ease-press);
}

/* Completion feedback lands on the actual copy event (site.js adds
   .is-copied): a quick outward bump, in harmony with the checkmark's own
   pop, then settles. */
.copy-button.is-copied {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  animation: copy-confirm var(--duration-normal) var(--ease-press-release);
}

@keyframes copy-confirm {
  0% { transform: scale(0.9); }
  45% { transform: scale(1.12); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .copy-button.is-copied {
    animation: none;
  }
}

/* Icon swap: default shows the copy icon; a successful copy switches to
   a checkmark for a couple seconds (.is-copied, toggled in site.js). Both
   icons are stacked in the same spot (absolute + auto margins, centered
   in the button's own box) and cross-fade with a small pop instead of
   the old instant display:none/inline-flex swap. */
.copy-button svg {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 1rem;
  height: 1rem;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity var(--duration-quick) var(--ease-out-expo), transform var(--duration-quick) var(--ease-out-expo);
}

.copy-button .icon-copy {
  opacity: 1;
  transform: scale(1);
}

.copy-button.is-copied .icon-copy {
  opacity: 0;
  transform: scale(0.6);
}

.copy-button.is-copied .icon-check {
  opacity: 1;
  transform: scale(1);
  color: var(--accent, var(--text));
}

/* --- Pricing --- */

/* --- Local/free features showcase (leads the pricing page, before the
   hosted plans grid) --- */
.local-section {
  padding: var(--space-6) 0 var(--space-8);
}

.local-section-head {
  text-align: center;
  max-width: 34rem;
  margin: 0 auto;
}

.local-section-head h2 {
  font-size: var(--text-2xl);
  letter-spacing: -0.01em;
}

.local-section-head p {
  margin-top: var(--space-3);
  color: var(--text-muted);
}

/* A single centered column, not a left-aligned 2-column grid: matches
   the section heading above it (also centered) instead of reading as a
   separate, differently-aligned block. Each row still lays out
   left-to-right internally (checkmark, then text) so wrapped lines stay
   readable against their own checkmark rather than centered as ragged
   text; it's the row *as a whole* that's centered on the page, via the
   list's own max-width + auto margins. */
.local-features {
  list-style: none;
  max-width: 38rem;
  margin: var(--space-7) auto 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.local-features li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  text-align: left;
  font-size: var(--text-base);
  color: var(--text);
}

.local-features svg {
  flex-shrink: 0;
  width: 1.25rem;
  height: 1.25rem;
  margin-top: 0.2em;
  color: var(--accent);
}

.local-section-cta {
  text-align: center;
  margin-top: var(--space-8);
}


/* --- "Escape your own usage limits" hook, between the local showcase and
   the hosted plans grid: same card treatment as the home page's
   .cta-banner, scoped separately since this one sits mid-page, not at
   the very end. --- */
.usage-hook {
  padding: 0 0 var(--space-8);
}

.usage-hook .wrap {
  max-width: 40rem;
  text-align: center;
  padding: var(--space-7) var(--space-6);
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 8%, var(--surface)), var(--surface));
}

.usage-hook h2 {
  font-size: var(--text-xl);
  letter-spacing: -0.006em;
}

.usage-hook p {
  margin-top: var(--space-3);
  color: var(--text-muted);
}

.pricing-hero {
  padding: var(--space-8) 0 var(--space-4);
  text-align: center;
}

.pricing-hero h1 {
  font-size: var(--text-3xl);
  letter-spacing: -0.014em;
}

.pricing-hero p {
  margin: var(--space-4) auto 0;
  max-width: 34rem;
  color: var(--text-muted);
  font-size: var(--text-lg);
}

.pricing-note {
  text-align: center;
  color: var(--text-faint);
  font-size: var(--text-sm);
  margin-top: var(--space-3);
}

.pricing {
  padding: var(--space-6) 0 var(--space-9);
}

/* Wider than the site's standard 72rem .wrap: 4 cards with a full
   feature list each need more room than that to avoid the skinny,
   cramped columns a standard-width container forces them into. */
.pricing .wrap {
  max-width: 88rem;
}

.pricing-section-head {
  text-align: center;
}

.pricing-section-head h2 {
  font-size: var(--text-2xl);
  letter-spacing: -0.01em;
}

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-5);
  margin-top: var(--space-7);
  align-items: stretch;
}

.price-card {
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  background: var(--surface);
  transition:
    transform var(--duration-quick) var(--ease-out-expo),
    border-color var(--duration-quick) var(--ease-out-expo),
    box-shadow var(--duration-quick) var(--ease-out-expo);
}

.price-card:hover {
  border-color: var(--border-strong);
}

@media (hover: hover) and (pointer: fine) {
  /* Scoped through .pricing-grid: see the .steps .step:hover comment
     above for why: without it, [data-reveal].is-visible's own transform
     wins the cascade tie by source order and this lift never shows. */
  .pricing-grid .price-card:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-lift);
  }
}

.price-card.is-featured {
  border-color: var(--accent);
  border-width: 2px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 6%, var(--surface)), var(--surface));
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 10%, transparent);
  position: relative;
}

.price-card.is-featured:hover {
  border-color: var(--accent-strong);
}

.price-card.is-featured .price-amount .amount {
  font-size: var(--text-4xl);
}

.price-badge {
  position: absolute;
  top: -0.8rem;
  left: 50%;
  transform: translateX(-50%);
  padding: var(--space-1) var(--space-4);
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow: var(--shadow-lift);
  white-space: nowrap;
}

.price-card h3 {
  font-size: var(--text-lg);
  letter-spacing: -0.002em;
}

.price-card-tagline {
  margin-top: var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-sm);
  min-height: 2.5em;
}

.price-amount {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  margin-top: var(--space-5);
}

.price-amount .amount {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 750;
  letter-spacing: -0.02em;
}

.price-amount .period {
  color: var(--text-faint);
  font-size: var(--text-sm);
}

.price-features {
  list-style: none;
  margin: var(--space-6) 0 0;
  padding-top: var(--space-5);
  padding-left: 0;
  padding-right: 0;
  padding-bottom: 0;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  flex: 1;
}

.price-features li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.price-features svg {
  flex-shrink: 0;
  width: 1.1rem;
  height: 1.1rem;
  margin-top: 0.15em;
  color: var(--accent);
}

.price-card .btn {
  justify-content: center;
  margin-top: var(--space-6);
}

@media (max-width: 88rem) {
  /* 4 cards split evenly into a 2x2 grid below the wide pricing .wrap's
     own max-width, once the viewport itself can't fit it comfortably. */
  .pricing-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

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

/* auto-fit + minmax(min(...)) collapses to one column below 44rem on its
 * own, no separate breakpoint needed (and no fight with .pricing-grid's
 * own 40rem rule, which this same element also carries). */
.pricing-grid-teaser {
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  max-width: 48rem;
  margin-left: auto;
  margin-right: auto;
}

/* --- Company marquee ---
   Text wordmarks, not real brand logos/SVGs: using an unrelated
   product's exact stylized logo without any actual relationship to the
   company implies an endorsement or partnership that doesn't exist
   (the same reasoning a "where our alumni work" bar earns honestly with
   real placement data aplyx doesn't have). The caption is worded to
   match what's actually true instead: these are examples of companies
   whose job boards the product can search, not a claim about anyone
   having been hired there through it. */
.marquee {
  padding: var(--space-6) 0;
  overflow: hidden;
  border-bottom: 1px solid var(--border);
}

.marquee-caption {
  text-align: center;
  font-size: var(--text-sm);
  color: var(--text-faint);
  margin: 0 0 var(--space-4);
}

.marquee-track {
  display: flex;
  width: max-content;
  animation: marquee-scroll 32s linear infinite;
}

@media (hover: hover) and (pointer: fine) {
  .marquee:hover .marquee-track {
    animation-play-state: paused;
  }
}

.marquee-group {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

/* Each logo sits in a small, always-light chip rather than directly on
   the page background: some brand marks default to near-black (Notion,
   Brex) and would simply vanish against the dark theme's near-black
   background otherwise. A fixed light backdrop guarantees contrast for
   every logo regardless of its own color or which theme is active,
   without needing a separate asset or currentColor rework per icon. */
.marquee-item {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 var(--space-3);
  padding: var(--space-4) var(--space-6);
  height: 4.5rem;
  border-radius: var(--radius-lg);
  /* Light mode: a dark plate on purpose, literally the dark theme's own
     --surface-raised tone, used as a deliberate pop of contrast against
     the light page.
     Dark mode flips to a LIGHT plate instead (see the override below):
     an equally dark chip there turned out too close to the page's own
     near-black background to read clearly at all. Not pure white either
     way: dimmed a step down from #fff so it doesn't glow in a dark room,
     just enough to stay a clearly distinct box. */
  background: #26221a;
  border: 1px solid rgb(255 255 255 / 0.08);
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.3);
  transition:
    background var(--duration-normal) var(--ease-standard),
    border-color var(--duration-normal) var(--ease-standard);
}

:root[data-theme="dark"] .marquee-item {
  background: #ede8db;
  border-color: rgb(15 20 32 / 0.1);
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.4);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .marquee-item {
    background: #ede8db;
    border-color: rgb(15 20 32 / 0.1);
    box-shadow: 0 2px 8px rgb(0 0 0 / 0.4);
  }
}

.marquee-item img {
  height: 2.25rem;
  width: 2.25rem;
  display: block;
}

/* Notion (#000) and Brex (#212121) are near-black by default. In light
   mode the chip itself is dark, so they need inverting (near-black ->
   near-white) to show up at all. In dark mode the chip flips to light
   (above); on THAT background their native near-black already has
   plenty of contrast, so the invert is switched back off there instead
   of doubling up into a wrong, washed-out light-on-light result. */
.marquee-item img.marquee-logo-invert {
  filter: invert(1);
  transition: filter var(--duration-normal) var(--ease-standard);
}

:root[data-theme="dark"] .marquee-item img.marquee-logo-invert {
  filter: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .marquee-item img.marquee-logo-invert {
    filter: none;
  }
}

@keyframes marquee-scroll {
  from {
    transform: translateX(0);
  }
  to {
    /* Exactly one .marquee-group's width: the two duplicated groups
       are identical, so this lands group 2 precisely where group 1
       started, and the loop point is invisible. */
    transform: translateX(-50%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .marquee-track {
    animation: none;
  }
}

/* --- Changelog (/changelog): renders docs/CHANGELOG.md live off GitHub's
   raw content CDN (changelog.js), so a push to main is a push to this page.
   A vertical timeline: one continuous rail (the container's own ::before)
   threading through each release's dot, matching the sequential, dated
   nature of the content itself rather than a generic card grid. --- */

.changelog-sync-note {
  max-width: 46rem;
  margin: 0 auto var(--space-7);
  text-align: center;
  font-size: var(--text-sm);
  color: var(--text-faint);
}

.changelog-sync-time {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--text-xs);
}

.changelog-timeline {
  position: relative;
  max-width: 46rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
}

.changelog-timeline::before {
  content: "";
  position: absolute;
  top: 0.4rem;
  bottom: 0.4rem;
  left: 0.9375rem;
  width: 2px;
  background: var(--border);
}

.changelog-entry {
  position: relative;
  display: flex;
  gap: var(--space-5);
  animation: changelog-entry-in var(--duration-slow) var(--ease-out) both;
}

.changelog-entry:nth-child(1) {
  animation-delay: 0ms;
}
.changelog-entry:nth-child(2) {
  animation-delay: 40ms;
}
.changelog-entry:nth-child(3) {
  animation-delay: 80ms;
}
.changelog-entry:nth-child(4) {
  animation-delay: 120ms;
}
.changelog-entry:nth-child(5) {
  animation-delay: 160ms;
}
.changelog-entry:nth-child(6) {
  animation-delay: 200ms;
}

@keyframes changelog-entry-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.changelog-entry-marker {
  flex-shrink: 0;
  width: 2rem;
  display: flex;
  justify-content: center;
  padding-top: 0.3rem;
}

.changelog-dot {
  position: relative;
  z-index: 1;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 50%;
  /* A ring in the page's own ground color knocks a gap out of the rail
     behind it, so the line reads as passing behind each dot instead of
     touching it edge-to-edge. */
  box-shadow: 0 0 0 4px var(--ground);
}

/* The dot's actual fill lives on ::after, painted on top of ::before,
   so the ping ring (::before, added below for .is-latest only) can sit
   UNDERNEATH the solid dot at rest and only become visible once it
   expands past the dot's own edge, instead of flashing on top of it. */
.changelog-dot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--accent);
}

/* The "latest" release gets a soft white ring that pings outward and
   fades: an ambient, always-on state indicator (not a response to any
   user action), so it's allowed to run continuously and a little slower
   than a typical UI transition. Scoped to .is-latest only: exactly one
   entry ever has this, so it reads as "this one is live" rather than
   decorating the whole timeline. */
.changelog-entry.is-latest .changelog-dot::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: #fff;
  animation: changelog-ping 1.8s cubic-bezier(0, 0, 0.2, 1) infinite;
}

@keyframes changelog-ping {
  0% {
    transform: scale(1);
    opacity: 0.7;
  }
  100% {
    transform: scale(2.75);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* The global reduced-motion rule (bottom of this file) collapses the
     duration to ~0, but an `infinite` animation at near-zero duration is
     still technically animating; explicit `animation: none` is the only
     way to guarantee a genuinely static dot. */
  .changelog-entry.is-latest .changelog-dot::before {
    animation: none;
    opacity: 0;
  }
}

.changelog-entry-body {
  flex: 1;
  min-width: 0;
}

.changelog-entry-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.changelog-version {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 750;
  letter-spacing: -0.01em;
  color: var(--text);
}

.changelog-version-generic {
  font-size: var(--text-lg);
}

.changelog-latest-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.15em var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.changelog-date {
  font-size: var(--text-sm);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.changelog-tag {
  font-size: var(--text-xs);
  color: var(--text-faint);
  font-style: italic;
}

.changelog-meta {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-faint);
}

.changelog-group {
  margin-top: var(--space-5);
}

.changelog-group-title {
  font-size: var(--text-base);
  margin: 0;
}

.changelog-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.25em var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.01em;
}

.changelog-chip-good {
  background: var(--good-soft);
  color: var(--good);
}
.changelog-chip-info {
  background: var(--info-soft);
  color: var(--info);
}
.changelog-chip-special {
  background: var(--special-soft);
  color: var(--special);
}
.changelog-chip-danger {
  background: var(--danger-soft);
  color: var(--danger);
}
.changelog-chip-neutral {
  background: var(--surface-raised);
  color: var(--text-muted);
  border: 1px solid var(--border);
}

.changelog-group p {
  margin-top: var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.changelog-list {
  margin: var(--space-3) 0 0;
  padding-left: 1.2em;
  color: var(--text-muted);
  font-size: var(--text-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.changelog-list strong,
.changelog-meta strong {
  color: var(--text);
  font-weight: 700;
}

.changelog-list code,
.changelog-meta code,
.changelog-group p code {
  font-family: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
  font-size: 0.9em;
  background: var(--surface-raised);
  border-radius: var(--radius-sm);
  padding: 0.15em 0.4em;
}

.changelog-quote {
  margin: var(--space-3) 0 0;
  padding: var(--space-3) var(--space-4);
  border-left: 3px solid var(--border-strong);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--surface-raised);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.changelog-quote p {
  margin: 0;
}

/* Loading skeleton: shown only until the first fetch (or cache read)
   resolves; never a permanent state. */
.changelog-skeleton .changelog-dot {
  background: var(--border-strong);
}

.skeleton-bar {
  height: 0.9em;
  border-radius: var(--radius-sm);
  background: var(--border);
  animation: aplyx-skeleton-pulse 1.4s ease-in-out infinite;
}

.skeleton-bar + .skeleton-bar {
  margin-top: var(--space-2);
}

.skeleton-bar-title {
  width: 40%;
  height: 1.2em;
  margin-bottom: var(--space-3);
}

@keyframes aplyx-skeleton-pulse {
  0%,
  100% {
    opacity: 0.45;
  }
  50% {
    opacity: 0.9;
  }
}

.changelog-error {
  max-width: 46rem;
  margin: 0 auto;
  padding: var(--space-6);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  text-align: center;
}

.changelog-error p {
  color: var(--text-muted);
}

.changelog-error-actions {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
  flex-wrap: wrap;
  margin-top: var(--space-5);
}

@media (max-width: 30rem) {
  .changelog-entry {
    gap: var(--space-3);
  }
  .changelog-entry-marker {
    width: 1.5rem;
  }
  .changelog-timeline::before {
    left: 0.6875rem;
  }
}

/* --- Footer --- */

.site-footer {
  border-top: 1px solid var(--border);
  padding: var(--space-6) 0;
}

.site-footer .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  font-size: var(--text-sm);
  color: var(--text-faint);
}

.site-footer a {
  color: var(--text-muted);
  text-decoration: none;
  font-weight: 600;
}

.site-footer a:hover {
  color: var(--text);
}

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

/* Blur-to-focus + a slight scale, on top of the plain fade/rise this
   already had, a noticeably more "arriving into focus" feel than a
   flat fade, while staying in the same restrained, non-bouncy motion
   language as the rest of the site (still one easing curve, still no
   scaling past 1). Respects prefers-reduced-motion the same way every
   other transition here does (zeroed durations further down). */
[data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-rise)) scale(0.97);
  filter: blur(6px);
  transition:
    opacity var(--duration-slow) var(--ease-out),
    transform var(--duration-slow) var(--ease-out),
    filter var(--duration-slow) var(--ease-out);
}

[data-reveal].is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
  filter: blur(0);
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* --- Account page (account.html): free hosted tier sign-in/sign-up and
   Tier 0 cached job search, per docs/hosted-paid-tier-plan.md. --- */

.nav-link-account {
  margin-left: auto;
  padding: 0.35em var(--space-4);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  /* Without this, a flex item can shrink below its text's single-line
     width and "Sign in" wraps to two lines; the pill stretches into a
     tall stadium shape and the wrapped lines default to left-aligned
     text, reading as badly off-center. Confirmed live on a real phone
     (not reproducible by resizing a desktop window, which wouldn't go
     narrow enough here to trigger it). */
  white-space: nowrap;
  flex-shrink: 0;
}

/* Signed-in state, applied by nav-auth.js on every page: the real Google
   profile photo when one exists, else an initials chip — never a generic
   placeholder-person icon. .avatar-chip is fully self-contained (sized,
   circular, colored), so the link here just clears the "Sign in" pill's
   own padding/border and lets it show through. */
.nav-avatar-link {
  padding: 0;
  border-color: transparent;
}

/* Identity avatar: shared between this nav link and account.html's
   dashboard sidebar (built by nav-auth.js's buildAvatarNode), so "who am
   I signed in as" reads the same everywhere on the site. */
.avatar-chip {
  --avatar-size: 2.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--avatar-size);
  height: var(--avatar-size);
  border-radius: 50%;
  overflow: hidden;
  background: var(--accent-soft);
  color: var(--accent-strong);
}

.avatar-chip img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar-chip-initials {
  font-family: var(--font-display);
  font-size: calc(var(--avatar-size) * 0.36);
  font-weight: 700;
  letter-spacing: 0.01em;
}

.account-wrap {
  max-width: 34rem;
  margin: 0 auto;
  transition: max-width var(--duration-normal) var(--ease-standard);
}

/* The guided account setup carries more than a login form (profile
   fields, a resume drop, step progress), so it gets real width rather
   than the same cramped column the sign-in card uses. */
.account-wrap:has(#setup-panel:not([hidden])) {
  max-width: 46rem;
}

/* Signed-in dashboard: break out of the centered marketing column
   entirely and take over the viewport as an admin workspace. The warm
   marketing chrome (hero, footer, top-nav links) steps aside; the canvas
   goes cool grey; the shell itself (.dashboard-shell) owns its own
   max-width and internal grid. Scoped by :has() so a signed-out visitor
   and the setup wizard are completely unaffected. */
.account-wrap:has(#dashboard-panel:not([hidden])) {
  max-width: none;
  margin: 0;
  padding: 0;
  transition: none;
}

body:has(#dashboard-panel:not([hidden])) {
  background: var(--dash-canvas);
}

body:has(#dashboard-panel:not([hidden])) .page-hero,
body:has(#dashboard-panel:not([hidden])) .site-footer,
body:has(#dashboard-panel:not([hidden])) .site-nav .nav-links,
body:has(#dashboard-panel:not([hidden])) .site-nav [data-nav-toggle] {
  display: none;
}

body:has(#dashboard-panel:not([hidden])) .site-nav {
  background: var(--dash-surface);
  border-bottom: 1px solid var(--dash-border);
}

body:has(#dashboard-panel:not([hidden])) main .content {
  padding: 0;
}

.account-panel {
  padding: var(--space-6);
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: var(--shadow-lift);
}

#auth-panel {
  max-width: 34rem;
  margin: 0 auto;
}

.account-tabs {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
  padding: var(--space-1);
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
}

.account-tab {
  flex: 1;
  padding: var(--space-2) var(--space-4);
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-muted);
  font-weight: 650;
  font-size: var(--text-sm);
  cursor: pointer;
  transition:
    background var(--duration-quick) var(--ease-out),
    color var(--duration-quick) var(--ease-out),
    transform var(--duration-normal) var(--ease-press-release);
}

.account-tab:active {
  transform: scale(0.96);
  transition: transform var(--duration-press) var(--ease-press);
}

.account-tab.is-active {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-lift);
}

.account-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.account-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-muted);
}

.account-field input {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--ground);
  color: var(--text);
  font-size: var(--text-sm);
  font-family: var(--font-body);
}

.account-field input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.account-submit {
  justify-content: center;
  width: 100%;
}

.ats-account-form-section {
  margin: var(--space-6) 0;
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
}

.ats-account-form-section h2 {
  margin-bottom: var(--space-4);
  font-size: var(--text-lg);
  letter-spacing: -0.002em;
}

.account-message {
  min-height: 1.2em;
  margin-top: var(--space-3);
  font-size: var(--text-sm);
  color: var(--accent);
}

.account-message.is-error {
  color: var(--danger);
}

.account-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0;
  color: var(--text-faint);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.account-divider::before,
.account-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

.account-google {
  width: 100%;
  justify-content: center;
}

.account-fineprint {
  margin-top: var(--space-5);
  color: var(--text-faint);
  font-size: var(--text-xs);
  line-height: 1.6;
}

.account-email {
  font-weight: 650;
}

.account-tier-badge {
  display: inline-block;
  margin-top: var(--space-2);
  padding: 0.2em var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--text-xs);
  font-weight: 700;
}

.dashboard-search {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.job-source-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.job-source-chip {
  padding: 0.3em var(--space-3);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  transition:
    border-color var(--duration-quick) var(--ease-out),
    color var(--duration-quick) var(--ease-out),
    background var(--duration-quick) var(--ease-out),
    transform var(--duration-normal) var(--ease-press-release);
}

.job-source-chip:hover {
  border-color: var(--accent);
}

.job-source-chip:active {
  transform: scale(0.94);
  transition: transform var(--duration-press) var(--ease-press);
}

.job-source-chip.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.job-search-form {
  display: flex;
  gap: var(--space-3);
}

.job-search-form input {
  flex: 1;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--ground);
  color: var(--text);
  font-size: var(--text-sm);
  font-family: var(--font-body);
}

.job-search-form input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.job-results-status {
  margin-top: var(--space-4);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.job-results {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.job-result {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--ground);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--duration-quick) var(--ease-out), transform var(--duration-quick) var(--ease-out-expo);
}

.job-result:hover {
  border-color: var(--accent);
}

@media (hover: hover) and (pointer: fine) {
  .job-result:hover {
    transform: translateY(-2px);
  }
}

.job-result-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.job-result-company {
  font-size: var(--text-xs);
  font-weight: 650;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.job-result-source {
  flex-shrink: 0;
  padding: 0.15em var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
  color: var(--text-faint);
  font-size: 0.65rem;
  font-weight: 600;
}

.job-result-title {
  font-weight: 600;
}

.job-result-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-1);
  color: var(--text-faint);
  font-size: var(--text-xs);
}

/* Loading state: a handful of pulsing placeholder cards instead of
   plain status text, so a search reads as "working," not "did nothing." */
.job-result-skeleton {
  height: 5.5rem;
  background: var(--ground);
  animation: job-result-skeleton-pulse 1.4s ease-in-out infinite;
}

@keyframes job-result-skeleton-pulse {
  0%,
  100% {
    opacity: 0.5;
  }
  50% {
    opacity: 1;
  }
}

/* --- Signed-in dashboard: an admin workspace (account.js's
   startActivitySync / loadMyState drive the live data inside it). Left
   rail for navigation, a top bar carrying the page title + the "Install
   aplyx" prompt, and a stack of cards in the content column. --- */

.dashboard-shell {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  align-items: start;
  align-content: start;
  max-width: 84rem;
  margin: 0 auto;
  min-height: calc(100vh - 4rem);
  overflow-x: clip;
  background: var(--dash-canvas);
  color: var(--dash-text);

  /* Re-point the shared activity/job/form sub-components (.activity-*,
     .job-*, .account-field, .usage-bar, .btn-secondary…) at the cool
     workspace palette in one place, instead of re-styling each. --accent
     and the semantic colors are deliberately left alone. */
  --ground: var(--dash-sunk);
  --surface: var(--dash-surface);
  --surface-raised: color-mix(in srgb, var(--dash-text) 6%, transparent);
  --border: var(--dash-border);
  --border-strong: var(--dash-border-strong);
  --text: var(--dash-text);
  --text-muted: var(--dash-muted);
  --text-faint: var(--dash-faint);
  --shadow-lift: var(--dash-shadow);
}

.dash-rail {
  position: sticky;
  top: 4rem;
  display: flex;
  flex-direction: column;
  min-width: 0;
  height: calc(100vh - 4rem);
  padding: var(--space-5) var(--space-4);
  border-right: 1px solid var(--dash-border);
  background: var(--dash-surface);
}

.dash-rail-nav {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 2px;
}

.dash-rail-group {
  padding: var(--space-4) var(--space-3) var(--space-2);
  color: var(--dash-faint);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.dash-rail-group:first-child {
  padding-top: 0;
}

.dash-rail-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: 0.5rem var(--space-3);
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--dash-muted);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 550;
  text-align: left;
  cursor: pointer;
  transition:
    background var(--duration-quick) var(--ease-out),
    color var(--duration-quick) var(--ease-out),
    transform var(--duration-normal) var(--ease-press-release);
}

.dash-rail-item svg {
  width: 1.05rem;
  height: 1.05rem;
  flex-shrink: 0;
}

.dash-rail-item:hover {
  background: color-mix(in srgb, var(--dash-text) 6%, transparent);
  color: var(--dash-text);
}

.dash-rail-item:active {
  transform: scale(0.98);
  transition: transform var(--duration-press) var(--ease-press);
}

.dash-rail-item.is-active {
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-weight: 650;
}

.dash-rail-foot {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: 1px solid var(--dash-border);
}

.dash-rail-identity {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}

.dash-rail-identity-text {
  min-width: 0;
}

.dash-rail-identity-text .account-email {
  overflow: hidden;
  font-size: var(--text-sm);
  color: var(--dash-text);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-rail-identity-text .account-tier-badge {
  margin-top: var(--space-1);
}

.dash-rail-signout {
  width: 100%;
  justify-content: center;
  font-size: var(--text-sm);
}

.dash-content {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-6) var(--space-6) var(--space-8);
}

.dash-panels,
.dashboard-tab-panel {
  min-width: 0;
}

.dash-topbar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
}

.dash-topbar-heading {
  min-width: 0;
}

.dash-title {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 750;
  letter-spacing: -0.012em;
  color: var(--dash-text);
  text-wrap: balance;
}

.dash-subtitle {
  margin-top: var(--space-2);
  max-width: 62ch;
  color: var(--dash-muted);
  font-size: var(--text-sm);
  line-height: 1.55;
}

.dash-subtitle:empty {
  display: none;
}

/* The "Install aplyx" prompt: a calm pill sitting in the top bar, just
   outside and above the first content card. Shown by account.js only
   when profiles.app_last_seen_at is null (no desktop-app install has
   ever signed into this account). */
.install-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
  align-self: flex-start;
  padding: 0.5rem 1.05rem 0.5rem 0.85rem;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: var(--dash-shadow);
  transition:
    background var(--duration-quick) var(--ease-out),
    transform var(--duration-normal) var(--ease-press-release);
}

.install-cta svg {
  width: 1rem;
  height: 1rem;
}

.install-cta:hover {
  background: var(--accent-strong);
}

.install-cta:active {
  transform: scale(0.97);
  transition: transform var(--duration-press) var(--ease-press);
}

.install-cta:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.dashboard-tab-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.dash-card {
  padding: var(--space-5);
  border: 1px solid var(--dash-border);
  border-radius: var(--radius-lg);
  background: var(--dash-surface);
  box-shadow: var(--dash-shadow);
}

.dash-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.dash-card-head h2 {
  font-size: var(--text-lg);
  font-weight: 650;
  letter-spacing: -0.003em;
  color: var(--dash-text);
}

.dash-card-note {
  margin: 0 0 var(--space-4);
  max-width: 68ch;
  color: var(--dash-muted);
  font-size: var(--text-sm);
  line-height: 1.6;
}

/* Directly under a card heading: tighten the gap the heading's own
   margin-bottom would otherwise leave. Only applies where the note is the
   adjacent sibling, so a note sitting below other content (the Browse
   jobs card, where it follows the search form) keeps normal spacing. */
.dash-card-head + .dash-card-note {
  margin-top: calc(-1 * var(--space-2));
}

.dashboard-search + .dash-card-note {
  margin-top: var(--space-4);
}

/* Stat tiles and the usage bar read as proper cards on the workspace
   canvas (matching .dash-card), not sunk panels; the metric numbers stay
   neutral rather than accent-green so a "Failed" count doesn't read as
   positive. Scoped to the shell so the marketing-side demo is untouched. */
.dashboard-shell .activity-stats {
  margin: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
}

.dashboard-shell .activity-stat {
  background: var(--dash-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--dash-shadow);
}

.dashboard-shell .activity-stat-value {
  color: var(--dash-text);
  font-weight: 750;
}

.dashboard-shell .usage-bar {
  margin: 0;
}

@media (max-width: 60rem) {
  .dashboard-shell {
    grid-template-columns: 1fr;
  }
  .dash-rail {
    position: static;
    height: auto;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3) var(--space-4);
    padding: var(--space-4) var(--space-5);
    border-right: none;
    border-bottom: 1px solid var(--dash-border);
  }
  .dash-rail-foot {
    order: 1;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    flex: 1 1 auto;
    margin-top: 0;
    padding-top: 0;
    border-top: none;
  }
  .dash-rail-signout {
    width: auto;
    flex-shrink: 0;
  }
  .dash-rail-nav {
    order: 2;
    flex-basis: 100%;
    flex-direction: row;
    gap: var(--space-2);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: var(--space-1);
  }
  .dash-rail-group {
    display: none;
  }
  .dash-rail-item {
    flex-shrink: 0;
    width: auto;
    white-space: nowrap;
  }
  .dash-content {
    padding: var(--space-5) var(--space-4) var(--space-7);
  }
}

@media (max-width: 34rem) {
  .dash-topbar {
    flex-direction: column;
  }
}

/* --- Setup walkthrough (account.html #setup-panel): shown instead of
   the dashboard for a brand-new signup (no profiles row yet), in place of
   the single banner-to-a-tab nudge this replaced. See account.js's
   updateSetupGate()/showSetupStep(). --- */

/* Wider than the sign-in card: the setup walkthrough carries a full
   profile form (name, contact, work eligibility, education, demographics,
   targets) plus a resume drop, so the 30rem column the sign-in form uses
   made every field a cramped single stack. */
#setup-panel {
  max-width: 46rem;
  margin: 0 auto;
}

.setup-progress {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}

.setup-progress-label {
  flex-shrink: 0;
  color: var(--text-faint);
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.setup-progress-bar {
  flex: 1;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--border);
  overflow: hidden;
}

.setup-progress-fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--accent);
  transition: width var(--duration-normal) var(--ease-out);
}

.setup-step h2 {
  margin-bottom: var(--space-2);
  font-size: var(--text-xl);
  letter-spacing: -0.006em;
}

/* Each step arrives with the same fade+rise .changelog-entry-in already
   uses elsewhere on the site; replays automatically every time
   showSetupStep() toggles a step's `hidden` attribute, since a step
   re-enters the render tree (display:none -> block) rather than just
   changing position. */
.setup-step:not([hidden]) {
  animation: changelog-entry-in var(--duration-normal) var(--ease-out) both;
}

@media (prefers-reduced-motion: reduce) {
  .setup-step:not([hidden]) {
    animation: none;
  }
}

.setup-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

.setup-checklist {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin: var(--space-5) 0;
}

.setup-checklist-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--text-sm);
}

.setup-checklist-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  font-size: var(--text-xs);
  font-weight: 700;
}

.setup-checklist-icon-ok {
  background: var(--good-soft);
  color: var(--good);
}

.setup-checklist-icon-pending {
  background: var(--border);
  color: var(--text-faint);
}

.setup-checklist-label {
  font-weight: 600;
  color: var(--text);
}

.setup-checklist-detail {
  color: var(--text-muted);
}

/* The setup panel is narrower than the profile tab's own dashboard-width
   form (30rem vs. the dashboard's full main column), so its fields always
   stack label-above-input; the >40rem side-by-side layout below assumes
   more horizontal room than this panel ever has, regardless of viewport
   width. */
.setup-step .profile-form-fields .profile-field {
  grid-template-columns: 1fr;
  gap: var(--space-1);
}

/* Hosted daily-run quota, see account.js's renderUsageBar(). Two
   states: a real progress bar once an active paid subscription exists
   (none do yet; no Stripe integration is live), or a plain free-tier
   note reusing .account-tier-badge, matching the sidebar's own label
   instead of a second, differently-styled badge. */
.usage-bar {
  margin: var(--space-5) 0;
  padding: var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--ground);
}

.usage-bar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.usage-bar-count {
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.usage-bar-track {
  margin-top: var(--space-2);
  height: 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
  overflow: hidden;
}

.usage-bar-fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--accent);
  transition: width var(--duration-normal) var(--ease-out-expo);
}

.usage-bar-free {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.usage-bar-free .account-tier-badge {
  margin-top: 0;
}

.activity-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--space-3);
  margin: var(--space-5) 0;
}

.activity-stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--ground);
}

.activity-stat-value {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 800;
  color: var(--accent);
}

.activity-stat-label {
  color: var(--text-muted);
  font-size: var(--text-xs);
}

.activity-toggle {
  padding: 0.25em var(--space-3);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  transition:
    border-color var(--duration-quick) var(--ease-out),
    color var(--duration-quick) var(--ease-out),
    transform var(--duration-normal) var(--ease-press-release);
}

.activity-toggle:hover {
  border-color: var(--border-strong);
}

.activity-toggle:active {
  transform: scale(0.94);
  transition: transform var(--duration-press) var(--ease-press);
}

.activity-toggle[aria-pressed="true"] {
  border-color: var(--accent);
  color: var(--accent);
}

.activity-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.activity-empty {
  padding: var(--space-4);
  color: var(--text-faint);
  font-size: var(--text-sm);
}

.activity-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--ground);
}

.activity-row-text {
  display: flex;
  flex-direction: column;
  gap: 0.15em;
  flex: 1;
  min-width: 12rem;
}

.activity-row-title {
  font-weight: 600;
}

.activity-row-meta {
  color: var(--text-faint);
  font-size: var(--text-xs);
}

.activity-row-actions {
  display: flex;
  gap: var(--space-2);
  flex-shrink: 0;
}

.activity-action {
  padding: 0.4em var(--space-4);
  font-size: var(--text-xs);
}

.activity-badge {
  flex-shrink: 0;
  padding: 0.2em var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 700;
}

.activity-badge-pending {
  background: var(--info-soft);
  color: var(--info);
}

.activity-badge-good {
  background: var(--good-soft);
  color: var(--good);
}

.activity-badge-danger {
  background: var(--danger-soft);
  color: var(--danger);
}

.activity-badge-muted {
  background: var(--surface-raised);
  color: var(--text-faint);
}

.activity-list-compact .activity-event {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-1);
  border-bottom: 1px solid var(--border);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.activity-event-when {
  flex-shrink: 0;
  color: var(--text-faint);
  font-size: var(--text-xs);
}

@media (max-width: 30rem) {
  .account-panel {
    padding: var(--space-5) var(--space-4);
  }
  .job-search-form {
    flex-direction: column;
  }
  .job-results {
    grid-template-columns: 1fr;
  }
  .activity-row {
    flex-direction: column;
    align-items: stretch;
  }
  .activity-row-actions {
    justify-content: stretch;
  }
  .activity-action {
    flex: 1;
  }
}

/* --- Profile form (account.html "Profile" tab) --- */

.profile-form-fields {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  margin-top: var(--space-5);
}

.profile-fieldset {
  padding: 0;
  border: none;
}

.profile-fieldset legend {
  padding: 0 0 var(--space-3);
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.profile-fieldset + .profile-fieldset {
  padding-top: var(--space-6);
  border-top: 1px solid var(--border);
}

.profile-form-fields .profile-field {
  display: grid;
  grid-template-columns: minmax(12rem, 18rem) 1fr;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-3);
}

.profile-form-fields .profile-field span {
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 500;
}

.profile-form-fields .profile-field input,
.profile-form-fields .profile-field select {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--ground);
  color: var(--text);
  font-size: var(--text-sm);
  font-family: var(--font-body);
}

.profile-form-fields .profile-field input:focus-visible,
.profile-form-fields .profile-field select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.profile-form-actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

@media (max-width: 40rem) {
  .profile-form-fields .profile-field {
    grid-template-columns: 1fr;
    gap: var(--space-1);
  }
}

/* --- City autocomplete (setup walkthrough / Profile "Home location") ---- */

.city-autocomplete {
  position: relative;
}

.city-autocomplete-list {
  position: absolute;
  z-index: 30;
  top: calc(100% + var(--space-2));
  left: 0;
  right: 0;
  margin: 0;
  padding: var(--space-1);
  list-style: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: 0 12px 28px -10px color-mix(in srgb, black 28%, transparent);
  max-height: 15rem;
  overflow-y: auto;
}

.city-autocomplete-option {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--text);
  cursor: pointer;
}

.city-autocomplete-option.is-active {
  background: var(--accent-soft);
  color: var(--accent-strong);
}

/* --- Themed date picker (setup walkthrough / Profile "Date of birth") --- */

.site-datepicker {
  position: relative;
}

.site-datepicker input {
  width: 100%;
  padding-right: calc(var(--space-3) * 2 + 1.25rem);
}

.datepicker-toggle {
  position: absolute;
  right: var(--space-2);
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition:
    color var(--duration-quick) var(--ease-out-expo),
    background var(--duration-quick) var(--ease-out-expo),
    transform var(--duration-normal) var(--ease-press-release);
}

.datepicker-toggle:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--text) 8%, transparent);
}

.datepicker-toggle:active {
  /* Composes with the base rule's translateY(-50%) vertical centering:
     without it, a plain transform: scale() here would override the
     centering and the icon would jump to the box's top edge on press. */
  transform: translateY(-50%) scale(0.88);
  transition: transform var(--duration-press) var(--ease-press);
}

.datepicker-pop {
  position: absolute;
  z-index: 30;
  top: calc(100% + var(--space-2));
  left: 0;
  width: 19.5rem;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: 0 12px 32px -8px color-mix(in srgb, black 30%, transparent);
}

.datepicker-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.datepicker-selects {
  display: flex;
  flex: 1;
  gap: var(--space-2);
}

.datepicker-selects select {
  flex: 1;
  min-width: 0;
  padding: var(--space-1) var(--space-2);
  font: inherit;
  font-size: var(--text-xs);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.datepicker-selects select:first-child {
  flex: 1.5;
}

.datepicker-selects select:focus-visible {
  border-color: var(--accent);
  outline: none;
}

.datepicker-nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  flex-shrink: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: border-color var(--duration-quick) var(--ease-out-expo), transform var(--duration-press) var(--ease-press);
}

.datepicker-nav:hover {
  border-color: var(--border-strong);
}

.datepicker-nav:active {
  transform: scale(0.92);
}

.datepicker-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}

.datepicker-weekdays {
  margin-bottom: 2px;
}

.datepicker-weekday {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 1.6rem;
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.datepicker-day {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 2rem;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background var(--duration-quick) var(--ease-out-expo), color var(--duration-quick) var(--ease-out-expo), transform var(--duration-press) var(--ease-press);
}

.datepicker-day:hover {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

.datepicker-day:active {
  transform: scale(0.9);
}

.datepicker-day.is-selected {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
  .datepicker-nav,
  .datepicker-day {
    transition: background var(--duration-quick) ease, color var(--duration-quick) ease;
  }
}

/* --- Extension page (extension.html): demo mockup + supporting sections.
   The mockup reuses .hero-mockup-chrome (browser chrome) and the exact
   prompt markup/animation from src/extension/src/content.ts's shadow DOM,
   so this is an accurate preview of the real UI, not an invented one,
   same discipline as the homepage's hero TUI and review-queue mockups. --- */

.ext-demo {
  margin-top: var(--space-7);
  display: flex;
  justify-content: center;
}

.ext-demo-frame {
  width: 100%;
  max-width: 34rem;
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  background: #0d0d12;
  overflow: hidden;
  box-shadow: var(--shadow-lift);
  opacity: 0;
  transform: translateY(var(--reveal-rise));
  transition: opacity var(--duration-slow) var(--ease-out), transform var(--duration-slow) var(--ease-out);
}

.ext-demo-frame.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.ext-demo-frame .hero-mockup-chrome {
  background: #17171f;
  border-bottom-color: #2a2a35;
}

.ext-demo-frame .hero-mockup-chrome-title {
  color: #8b8fa3;
}

.ext-demo-page {
  position: relative;
  padding: var(--space-6) var(--space-5);
  min-height: 20rem;
}

.ext-demo-page-line {
  height: 0.6rem;
  border-radius: var(--radius-sm);
  background: #22222c;
  margin-bottom: var(--space-3);
  width: 70%;
}

.ext-demo-page-line-title {
  height: 0.9rem;
  width: 55%;
  background: #2a2a37;
}

.ext-demo-page-line-short {
  width: 40%;
  margin-bottom: var(--space-6);
}

.ext-demo-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 16rem;
}

.ext-demo-field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.ext-demo-field-label {
  font-size: 0.65rem;
  color: #6b6f80;
  font-family: var(--font-body);
}

.ext-demo-field-box {
  height: 1.6rem;
  border-radius: var(--radius-sm);
  border: 1px solid #2a2a35;
  background: #16161d;
  transition: background var(--duration-normal) var(--ease-standard), border-color var(--duration-normal) var(--ease-standard);
}

.ext-demo-field-box.is-filled {
  background: color-mix(in srgb, #7fae86 16%, #16161d);
  border-color: color-mix(in srgb, #7fae86 45%, #16161d);
}

/* The real prompt: same markup/CSS shape as content.ts's shadow-DOM
   panel (Moss palette, top-center slide-down), scaled to sit inside this
   mockup frame instead of a real page's fixed viewport. */
/* Frosted-glass material + the site's own --ease-out-expo curve,
   kept in exact sync with the real prompt's CSS in
   src/extension/src/content.ts, since this mockup's whole purpose is to
   be an accurate preview, not an invented look-alike. */
.ext-prompt {
  position: absolute;
  top: var(--space-4);
  left: 50%;
  width: 15.5rem;
  padding: var(--space-3) var(--space-4);
  border-radius: 16px;
  background: rgba(30, 27, 20, 0.72);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  color: #ede6d6;
  font-family: var(--font-body);
  font-size: 0.75rem;
  line-height: 1.45;
  box-shadow: 0 20px 48px -12px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.07);
  transform: translate(-50%, -10px) scale(0.94);
  opacity: 0;
  transition: transform 0.4s var(--ease-out-expo), opacity 0.3s var(--ease-out-expo);
}

.ext-prompt.is-visible {
  transform: translate(-50%, 0) scale(1);
  opacity: 1;
}

.ext-prompt-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.ext-prompt-head img {
  width: 16px;
  height: 16px;
  border-radius: 4px;
}

.ext-prompt-brand {
  font-weight: 700;
}

.ext-prompt-spacer {
  flex: 1;
}

.ext-prompt-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
  color: #b0a68e;
  font-size: 0.75rem;
}

.ext-prompt-text {
  margin-top: var(--space-3);
  font-weight: 600;
}

.ext-prompt-row {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.ext-prompt-btn {
  flex: 1;
  text-align: center;
  padding: 0.5em 0;
  border-radius: 10px;
  background: #7fae86;
  color: #17140f;
  font-weight: 600;
}

.ext-prompt-btn-secondary {
  background: rgba(255, 255, 255, 0.08);
  color: #ede6d6;
}

.ext-prompt-body {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  transition: max-height 0.3s var(--ease-out-expo), opacity 0.2s ease;
}

.ext-prompt-body.is-open {
  max-height: 6rem;
  opacity: 1;
  margin-top: var(--space-2);
}

.ext-prompt-status {
  color: #b0a68e;
}

.ext-how {
  margin-top: var(--space-9);
}

.ext-how h2,
.ext-boards h2,
.ext-install h2 {
  text-align: center;
  font-size: var(--text-2xl);
  letter-spacing: -0.01em;
}

.ext-boards {
  margin-top: var(--space-9);
  text-align: center;
}

.ext-board-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.ext-board-chip {
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  font-weight: 600;
  color: var(--text-muted);
}

.ext-install {
  margin-top: var(--space-9);
  padding: var(--space-7) var(--space-6);
  border-radius: var(--radius-xl);
  border: 1px solid var(--border);
  background: var(--surface-raised);
  text-align: center;
}

.ext-install p {
  max-width: 34rem;
  margin: var(--space-3) auto var(--space-5);
  color: var(--text-muted);
}

@media (max-width: 30rem) {
  .ext-demo-form {
    max-width: 100%;
  }
  .ext-prompt {
    width: calc(100% - 2rem);
  }
}

/* --- Direct app download (install.html) --- */
/* Layout/spacing only: the actual glass card, buttons, and toggle are
   defined once, near .btn-secondary, so the material has a single
   source of truth instead of two competing definitions. */

.direct-download {
  margin: var(--space-7) 0;
}

.direct-download h2 {
  margin-top: 0;
}
