/* ============================================================
   ReplySuit design system — Material 3
   ------------------------------------------------------------
   One stylesheet, ~60 pages. Class names are deliberately stable
   so every existing page inherits changes made here — page-level
   <style> blocks only ever reach for the tokens below, never for
   literal colours, which is what makes the dark theme work
   everywhere without touching each page.

   The system follows Material Design 3: colour roles generated
   from a seed, tonal surface containers instead of borders, the
   M3 elevation and shape scales, state layers for interaction,
   and M3 motion curves. Typography keeps the brand faces (Plus
   Jakarta Sans for display, Inter for text) on the M3 type scale.

   Layout:
     1. Fonts          6. Nav + theme toggle   11. Panels, forms, tables
     2. Tokens         7. Hero                 12. Status + stats
     3. Base           8. Sections + cards     13. App shell
     4. Typography     9. Footer               14. Overlays
     5. Buttons       10. Auth                 15–17. Marketing, blog, motion
   ============================================================ */

/* ---------- 1. Fonts -------------------------------------------------
   Self-hosted, not Google-hosted: the CSP in src/server.ts pins
   font-src to 'self', and shipping the files ourselves also removes a
   third-party round trip on first paint. Both faces are variable and
   licensed under the SIL OFL (see public/fonts/OFL.txt).
   ------------------------------------------------------------------ */

@font-face {
  font-family: "Inter var";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/inter-latin.woff2") format("woff2");
  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: "Inter var";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/inter-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Jakarta var";
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url("/fonts/plus-jakarta-sans-latin.woff2") format("woff2");
  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: "Jakarta var";
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url("/fonts/plus-jakarta-sans-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- 2. Tokens ------------------------------------------------
   Two layers.

   --md-sys-*  The Material 3 system tokens. Colour roles were generated
               with @material/material-color-utilities from the brand
               seed #7c3aed using SchemeFidelity, which keeps the seed
               itself as primary-container instead of drifting to a
               greyed violet. Deviations from the raw output:
                 - tertiary is the brand coral (#f43f5e) as an
                   unblended custom colour, not fidelity's orange;
                 - secondary-container uses primary tone 90/30 rather
                   than fidelity's chroma-matched tone, which is too
                   loud for chips, notices and nav indicators;
                 - success and warning are M3 custom colours harmonised
                   to the seed.
               To regenerate, feed the seed to the same library.

   Legacy aliases (--brand, --ink, --line, ...) map the names pages
   already use onto M3 roles. Pages' inline <style> blocks depend on
   them, so they stay. Note --brand is now the M3 primary: in the dark
   theme it is a LIGHT violet, so text on it must use --on-brand, never
   a literal #fff.

   The dark theme redefines only --md-sys-color-* and the few aliases
   whose role differs by theme.
   ------------------------------------------------------------------ */

:root {
  color-scheme: light;

  /* --- M3 colour roles: light --- */
  --md-sys-color-primary: #630ed4;
  --md-sys-color-on-primary: #ffffff;
  --md-sys-color-primary-container: #7c3aed;
  --md-sys-color-on-primary-container: #ede0ff;
  --md-sys-color-inverse-primary: #d2bbff;
  --md-sys-color-secondary: #6a4fa0;
  --md-sys-color-on-secondary: #ffffff;
  --md-sys-color-secondary-container: #eaddff;
  --md-sys-color-on-secondary-container: #4c00a9;
  --md-sys-color-tertiary: #bc0b3b;
  --md-sys-color-on-tertiary: #ffffff;
  --md-sys-color-tertiary-container: #ffdadb;
  --md-sys-color-on-tertiary-container: #40000d;
  --md-sys-color-error: #ba1a1a;
  --md-sys-color-on-error: #ffffff;
  --md-sys-color-error-container: #ffdad6;
  --md-sys-color-on-error-container: #93000a;
  --md-sys-color-success: #006c49;
  --md-sys-color-on-success: #ffffff;
  /* Chroma pulled to 18 (the library returns 49). At full chroma the
     mint shouts next to the error and warning containers, which sit at
     13–14, and the "sent" chip ends up louder than "failed". */
  --md-sys-color-success-container: #cce9d7;
  --md-sys-color-on-success-container: #002113;
  --md-sys-color-warning: #974806;
  --md-sys-color-on-warning: #ffffff;
  --md-sys-color-warning-container: #ffdbc8;
  --md-sys-color-on-warning-container: #321300;

  --md-sys-color-surface: #fef7ff;
  --md-sys-color-surface-dim: #dfd7e6;
  --md-sys-color-surface-bright: #fef7ff;
  --md-sys-color-surface-container-lowest: #ffffff;
  --md-sys-color-surface-container-low: #f9f1ff;
  --md-sys-color-surface-container: #f3ebfa;
  --md-sys-color-surface-container-high: #ede5f4;
  --md-sys-color-surface-container-highest: #e8dfee;
  --md-sys-color-on-surface: #1d1a24;
  --md-sys-color-on-surface-variant: #4a4455;
  --md-sys-color-outline: #7b7487;
  --md-sys-color-outline-variant: #ccc3d8;
  --md-sys-color-inverse-surface: #332f39;
  --md-sys-color-inverse-on-surface: #f6eefc;
  --md-sys-color-scrim: #000000;
  --md-sys-color-shadow: #000000;

  /* A raised surface that still separates from the page in both
     themes: lowest (white) in light, container-high in dark. */
  --md-surface-raised: var(--md-sys-color-surface-container-lowest);

  /* --- M3 state layers (opacity of the content colour) --- */
  --md-state-hover: .08;
  --md-state-focus: .1;
  --md-state-pressed: .1;
  --md-state-dragged: .16;

  /* --- M3 elevation --- */
  --md-elevation-0: none;
  --md-elevation-1: 0 1px 2px 0 rgba(0, 0, 0, .3), 0 1px 3px 1px rgba(0, 0, 0, .15);
  --md-elevation-2: 0 1px 2px 0 rgba(0, 0, 0, .3), 0 2px 6px 2px rgba(0, 0, 0, .15);
  --md-elevation-3: 0 1px 3px 0 rgba(0, 0, 0, .3), 0 4px 8px 3px rgba(0, 0, 0, .15);
  --md-elevation-4: 0 2px 3px 0 rgba(0, 0, 0, .3), 0 6px 10px 4px rgba(0, 0, 0, .15);
  --md-elevation-5: 0 4px 4px 0 rgba(0, 0, 0, .3), 0 8px 12px 6px rgba(0, 0, 0, .15);

  /* --- M3 shape --- */
  --md-shape-none: 0;
  --md-shape-xs: 4px;
  --md-shape-sm: 8px;
  --md-shape-md: 12px;
  --md-shape-lg: 16px;
  --md-shape-xl: 28px;
  --md-shape-full: 9999px;

  /* --- M3 motion --- */
  --md-easing-standard: cubic-bezier(.2, 0, 0, 1);
  --md-easing-emphasized-decelerate: cubic-bezier(.05, .7, .1, 1);
  --md-easing-emphasized-accelerate: cubic-bezier(.3, 0, .8, .15);
  --md-duration-short: .15s;
  --md-duration-medium: .3s;
  --md-duration-long: .5s;

  /* --- Type --- */
  --font-sans: "Inter var", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: "Jakarta var", "Inter var", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;

  /* ================= Legacy aliases ================= */

  --brand: var(--md-sys-color-primary);
  --on-brand: var(--md-sys-color-on-primary);
  --brand-hover: color-mix(in srgb, var(--md-sys-color-primary), var(--md-sys-color-on-primary) 8%);
  --brand-dark: var(--md-sys-color-on-secondary-container);
  --brand-soft: var(--md-sys-color-secondary-container);
  --brand-line: #d2bbff;
  --brand-2: var(--md-sys-color-secondary);
  --accent: var(--md-sys-color-tertiary);
  --accent-soft: var(--md-sys-color-tertiary-container);
  --link: var(--md-sys-color-primary);

  --ink: var(--md-sys-color-on-surface);
  --ink-soft: var(--md-sys-color-on-surface-variant);
  /* on-surface-variant, not outline: pages use --muted for small body
     text, and outline (a border role) only reaches 4.26:1 in light. */
  --muted: var(--md-sys-color-on-surface-variant);
  --line: var(--md-sys-color-outline-variant);
  --line-strong: var(--md-sys-color-outline);
  --bg: var(--md-sys-color-surface);
  --bg-soft: var(--md-sys-color-surface-container);
  --surface: var(--md-sys-color-surface-container-low);
  --surface-2: var(--md-sys-color-surface-container);
  --surface-glass: var(--md-sys-color-surface-container);

  --ok: var(--md-sys-color-success);
  --ok-soft: var(--md-sys-color-success-container);
  --warn: var(--md-sys-color-warning);
  --warn-soft: var(--md-sys-color-warning-container);
  --danger: var(--md-sys-color-error);
  --danger-soft: var(--md-sys-color-error-container);

  /* Used only for decorative text (the hero accent, the logo dot) and
     the soft blog CTA wash — M3 components themselves are flat. */
  --grad: linear-gradient(115deg, var(--md-sys-color-primary) 0%, var(--md-sys-color-tertiary) 100%);
  --grad-soft: linear-gradient(115deg, var(--md-sys-color-secondary-container) 0%, var(--md-sys-color-tertiary-container) 100%);

  --radius-lg: var(--md-shape-xl);
  --radius: var(--md-shape-md);
  --radius-sm: var(--md-shape-sm);
  --radius-xs: var(--md-shape-xs);

  --shadow-sm: var(--md-elevation-1);
  --shadow: var(--md-elevation-2);
  --shadow-lg: var(--md-elevation-3);
  --shadow-brand: var(--md-elevation-1);
  --ring: 0 0 0 1px var(--md-sys-color-primary);

  --ease: var(--md-easing-standard);
  --fast: var(--md-duration-short);
  --med: var(--md-duration-medium);
}

/* Dark values are declared twice on purpose: once behind the OS
   preference (for visitors who never touch the toggle) and once behind
   an explicit [data-theme="dark"] (which must also win when the OS is
   set to light). Keep the two blocks in sync. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --md-sys-color-primary: #d2bbff;
    --md-sys-color-on-primary: #3f008e;
    --md-sys-color-primary-container: #7c3aed;
    --md-sys-color-on-primary-container: #ede0ff;
    --md-sys-color-inverse-primary: #732ee4;
    --md-sys-color-secondary: #d2bbff;
    --md-sys-color-on-secondary: #3b1e6f;
    --md-sys-color-secondary-container: #523787;
    --md-sys-color-on-secondary-container: #eaddff;
    --md-sys-color-tertiary: #ffb2b7;
    --md-sys-color-on-tertiary: #67001b;
    /* Containers levelled to chroma ~45 at tone 30, the same as
       secondary and warning: the library returns 79 for error and 74
       for tertiary, which makes a failure banner shout next to an
       identical-status success one. All four land near 7.2:1. */
    --md-sys-color-tertiary-container: #7b2c35;
    --md-sys-color-on-tertiary-container: #ffdadb;
    --md-sys-color-error: #ffb4ab;
    --md-sys-color-on-error: #690005;
    --md-sys-color-error-container: #7b2d27;
    --md-sys-color-on-error-container: #ffdad6;
    --md-sys-color-success: #6ddba7;
    --md-sys-color-on-success: #003824;
    --md-sys-color-success-container: #005236;
    --md-sys-color-on-success-container: #cce9d7;
    --md-sys-color-warning: #ffb68a;
    --md-sys-color-on-warning: #522300;
    --md-sys-color-warning-container: #743400;
    --md-sys-color-on-warning-container: #ffdbc8;

    --md-sys-color-surface: #15121b;
    --md-sys-color-surface-dim: #15121b;
    --md-sys-color-surface-bright: #3c3742;
    --md-sys-color-surface-container-lowest: #100d16;
    --md-sys-color-surface-container-low: #1d1a24;
    --md-sys-color-surface-container: #221e28;
    --md-sys-color-surface-container-high: #2c2833;
    --md-sys-color-surface-container-highest: #37333e;
    --md-sys-color-on-surface: #e8dfee;
    --md-sys-color-on-surface-variant: #ccc3d8;
    --md-sys-color-outline: #958da1;
    --md-sys-color-outline-variant: #4a4455;
    --md-sys-color-inverse-surface: #e8dfee;
    --md-sys-color-inverse-on-surface: #332f39;

    --md-surface-raised: var(--md-sys-color-surface-container-high);
    --brand-line: #523787;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --md-sys-color-primary: #d2bbff;
  --md-sys-color-on-primary: #3f008e;
  --md-sys-color-primary-container: #7c3aed;
  --md-sys-color-on-primary-container: #ede0ff;
  --md-sys-color-inverse-primary: #732ee4;
  --md-sys-color-secondary: #d2bbff;
  --md-sys-color-on-secondary: #3b1e6f;
  --md-sys-color-secondary-container: #523787;
  --md-sys-color-on-secondary-container: #eaddff;
  --md-sys-color-tertiary: #ffb2b7;
  --md-sys-color-on-tertiary: #67001b;
  /* Levelled to chroma ~45 at tone 30 — see the note in the media-query
     copy of this block. */
  --md-sys-color-tertiary-container: #7b2c35;
  --md-sys-color-on-tertiary-container: #ffdadb;
  --md-sys-color-error: #ffb4ab;
  --md-sys-color-on-error: #690005;
  --md-sys-color-error-container: #7b2d27;
  --md-sys-color-on-error-container: #ffdad6;
  --md-sys-color-success: #6ddba7;
  --md-sys-color-on-success: #003824;
  --md-sys-color-success-container: #005236;
  --md-sys-color-on-success-container: #cce9d7;
  --md-sys-color-warning: #ffb68a;
  --md-sys-color-on-warning: #522300;
  --md-sys-color-warning-container: #743400;
  --md-sys-color-on-warning-container: #ffdbc8;

  --md-sys-color-surface: #15121b;
  --md-sys-color-surface-dim: #15121b;
  --md-sys-color-surface-bright: #3c3742;
  --md-sys-color-surface-container-lowest: #100d16;
  --md-sys-color-surface-container-low: #1d1a24;
  --md-sys-color-surface-container: #221e28;
  --md-sys-color-surface-container-high: #2c2833;
  --md-sys-color-surface-container-highest: #37333e;
  --md-sys-color-on-surface: #e8dfee;
  --md-sys-color-on-surface-variant: #ccc3d8;
  --md-sys-color-outline: #958da1;
  --md-sys-color-outline-variant: #4a4455;
  --md-sys-color-inverse-surface: #e8dfee;
  --md-sys-color-inverse-on-surface: #332f39;

  --md-surface-raised: var(--md-sys-color-surface-container-high);
  --brand-line: #523787;
}

/* ---------- 3. Base --------------------------------------------------- */

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-sans);
  font-feature-settings: "cv05" 1, "ss03" 1;  /* single-storey l, rounder quotes */
  color: var(--md-sys-color-on-surface);
  margin: 0;
  line-height: 1.6;
  background: var(--md-sys-color-surface);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  transition: background var(--med) var(--ease), color var(--med) var(--ease);
}

::selection { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }

/* A slim, themed scrollbar. Firefox takes the two-value shorthand. */
* { scrollbar-width: thin; scrollbar-color: var(--md-sys-color-outline-variant) transparent; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--md-sys-color-outline-variant); border-radius: var(--md-shape-full);
  border: 3px solid var(--md-sys-color-surface); background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--md-sys-color-outline); background-clip: padding-box; }

img, svg, video { max-width: 100%; }

a { color: var(--md-sys-color-primary); text-decoration: none; transition: color var(--fast) var(--ease); }
a:hover { color: var(--brand-hover); }

/* M3 focus indicator: a 3px ring in secondary, offset from the component
   so it never merges with a filled shape. Uses `outline` so it follows
   each element's own border-radius. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--md-sys-color-secondary);
  outline-offset: 2px;
}

/* ---------- 4. Typography -------------------------------------------
   The M3 type scale (display / headline / title / body / label), set
   in the brand faces. Headlines stay fluid via clamp() so the same
   markup reads correctly from a 360px phone to a wide desktop.
   ------------------------------------------------------------------ */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.25;
  color: var(--md-sys-color-on-surface);
  text-wrap: balance;
}
h1 { letter-spacing: -.018em; }

code, kbd, samp, pre { font-family: var(--font-mono); font-size: .9em; }

/* Numerals in data contexts line up column-to-column. */
.stat .n, .num, table td, .price, .badge { font-variant-numeric: tabular-nums; }

/* ---------- 5. Buttons -----------------------------------------------
   M3 common buttons. Variant mapping:
     .btn            filled
     .btn.tonal      filled tonal
     .btn.secondary  outlined
     .btn.ghost      text
     .btn.danger     filled, error
     .btn.grad       the page's hero action: primary-container + elevation
   Interaction is a state layer (::after, painted in the label colour)
   rather than a colour swap, which is what keeps hover/focus/press
   consistent across every variant and both themes.
   ------------------------------------------------------------------ */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  min-height: 2.5rem;
  font-family: inherit;
  font-weight: 500;
  font-size: .875rem;
  line-height: 1.25rem;
  letter-spacing: .00625em;
  padding: .5rem 1.5rem;
  border: 1px solid transparent;
  border-radius: var(--md-shape-full);
  cursor: pointer;
  background: var(--md-sys-color-primary);
  color: var(--md-sys-color-on-primary);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  isolation: isolate;
  box-shadow: var(--md-elevation-0);
  transition: box-shadow var(--fast) var(--ease), background var(--fast) var(--ease),
              border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.btn::after {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--fast) var(--ease);
  pointer-events: none;
}
.btn:hover { color: var(--md-sys-color-on-primary); box-shadow: var(--md-elevation-1); }
.btn:hover::after { opacity: var(--md-state-hover); }
.btn:focus-visible::after { opacity: var(--md-state-focus); }
.btn:active::after { opacity: var(--md-state-pressed); }
.btn:active { box-shadow: var(--md-elevation-0); }

.btn.tonal {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}
.btn.tonal:hover { color: var(--md-sys-color-on-secondary-container); }

.btn.secondary {
  background: transparent;
  color: var(--md-sys-color-primary);
  border-color: var(--md-sys-color-outline);
}
.btn.secondary:hover { color: var(--md-sys-color-primary); box-shadow: none; }

.btn.ghost {
  background: transparent;
  color: var(--md-sys-color-primary);
  padding-left: .75rem; padding-right: .75rem;
}
.btn.ghost:hover { color: var(--md-sys-color-primary); box-shadow: none; }

.btn.danger { background: var(--md-sys-color-error); color: var(--md-sys-color-on-error); }
.btn.danger:hover { color: var(--md-sys-color-on-error); }

.btn.grad {
  background: var(--md-sys-color-primary-container);
  color: var(--md-sys-color-on-primary-container);
  box-shadow: var(--md-elevation-1);
}
.btn.grad:hover { color: var(--md-sys-color-on-primary-container); box-shadow: var(--md-elevation-2); }

/* M3 extra-small: 32px tall, 12px sides. The wider 16px of the standard
   size squeezes the columns these sit in (the account card's name, row
   action buttons in tables). */
.btn.small { min-height: 2rem; padding: .3rem .75rem; font-size: .8125rem; }
.btn.small.ghost { padding-left: .6rem; padding-right: .6rem; }
.btn.large { min-height: 3.5rem; padding: .9rem 2rem; font-size: 1rem; }
.btn.block { width: 100%; }

/* M3 disabled: on-surface at 12% container, 38% label. */
.btn:disabled, .btn[aria-disabled="true"] {
  background: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);
  color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);
  border-color: transparent;
  box-shadow: none; cursor: not-allowed;
}
.btn.secondary:disabled, .btn.secondary[aria-disabled="true"] {
  background: transparent;
  border-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);
}
.btn.ghost:disabled, .btn.ghost[aria-disabled="true"] { background: transparent; }
.btn:disabled::after { display: none; }

/* Ripple origin is set by ui.js from the click coordinates. Painted in
   the label colour, like the state layer. */
.btn .ripple {
  position: absolute; z-index: -1;
  border-radius: 50%;
  transform: scale(0);
  background: currentColor;
  opacity: .16;
  animation: ripple .55s var(--md-easing-standard) forwards;
  pointer-events: none;
}
@keyframes ripple { to { transform: scale(2.4); opacity: 0; } }

/* ---------- 6. Nav (top app bar) + theme toggle ---------------------- */

.nav {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
  min-height: 4rem;
  padding: .5rem 1.5rem;
  background: var(--md-sys-color-surface);
  transition: background var(--med) var(--ease);
}
/* ui.js sets this once the page has scrolled. M3's scrolled top app bar
   changes tone to surface-container rather than growing a shadow. */
.nav.is-stuck { background: var(--md-sys-color-surface-container); }

.brand {
  font-family: var(--font-display);
  font-weight: 800; font-size: 1.25rem;
  color: var(--md-sys-color-on-surface);
  display: inline-flex; align-items: center; gap: .05rem;
  letter-spacing: -.02em;
}
.brand:hover { color: var(--md-sys-color-on-surface); }
.brand .dot {
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}

.nav-links { display: flex; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.nav-links a {
  position: relative; display: block;
  color: var(--md-sys-color-on-surface-variant);
  font-size: .875rem; font-weight: 500; letter-spacing: .00625em;
  padding: .625rem .9rem; border-radius: var(--md-shape-full);
  isolation: isolate; overflow: hidden;
}
.nav-links a::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--md-sys-color-on-surface); opacity: 0;
  transition: opacity var(--fast) var(--ease);
}
.nav-links a:hover { color: var(--md-sys-color-on-surface); }
.nav-links a:hover::after { opacity: var(--md-state-hover); }

.nav-actions { display: flex; gap: .5rem; align-items: center; }

/* M3 standard icon button. Both glyphs are stacked and cross-faded, so
   the button never changes size as the theme flips. */
.theme-toggle {
  position: relative;
  width: 40px; height: 40px; flex-shrink: 0;
  display: grid; place-items: center;
  border: 0; border-radius: var(--md-shape-full);
  background: transparent; color: var(--md-sys-color-on-surface-variant);
  cursor: pointer; padding: 0;
  isolation: isolate; overflow: hidden;
}
.theme-toggle::after, .nav-toggle::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--md-sys-color-on-surface-variant); opacity: 0;
  transition: opacity var(--fast) var(--ease);
}
.theme-toggle:hover::after, .nav-toggle:hover::after { opacity: var(--md-state-hover); }
.theme-toggle:active::after, .nav-toggle:active::after { opacity: var(--md-state-pressed); }
.theme-toggle svg { position: absolute; width: 20px; height: 20px; transition: opacity var(--med) var(--ease), transform var(--med) var(--ease); }
.theme-toggle .i-sun  { opacity: 0; transform: rotate(-70deg) scale(.5); }
.theme-toggle .i-moon { opacity: 1; transform: rotate(0) scale(1); }
:root[data-theme="dark"] .theme-toggle .i-sun  { opacity: 1; transform: rotate(0) scale(1); }
:root[data-theme="dark"] .theme-toggle .i-moon { opacity: 0; transform: rotate(70deg) scale(.5); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .i-sun  { opacity: 1; transform: rotate(0) scale(1); }
  :root:not([data-theme="light"]) .theme-toggle .i-moon { opacity: 0; transform: rotate(70deg) scale(.5); }
}

/* Hamburger — three bars that fold into an X. Hidden above 860px. */
.nav-toggle {
  display: none;
  width: 40px; height: 40px;
  border: 0; border-radius: var(--md-shape-full);
  background: transparent; cursor: pointer; padding: 0;
  position: relative; isolation: isolate; overflow: hidden;
}
.nav-toggle span {
  position: absolute; left: 50%; width: 18px; height: 2px;
  background: var(--md-sys-color-on-surface-variant); border-radius: 2px;
  transition: transform var(--med) var(--ease), opacity var(--fast) var(--ease);
}
.nav-toggle span:nth-child(1) { transform: translate(-50%, -6px); }
.nav-toggle span:nth-child(2) { transform: translate(-50%, 0); }
.nav-toggle span:nth-child(3) { transform: translate(-50%, 6px); }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translate(-50%, 0) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translate(-50%, 0) rotate(-45deg); }

@media (max-width: 860px) {
  .nav { padding: .5rem 1rem; }
  .nav-toggle { display: block; }

  /* The drawer: a panel under the bar rather than a full-screen
     takeover, so the page stays visible behind it. */
  .nav-links {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; gap: .15rem;
    padding: .5rem .75rem 1rem;
    background: var(--md-sys-color-surface-container-low);
    border-radius: 0 0 var(--md-shape-lg) var(--md-shape-lg);
    box-shadow: var(--md-elevation-2);
    opacity: 0; visibility: hidden;
    transform: translateY(-8px);
    transition: opacity var(--med) var(--md-easing-emphasized-decelerate),
                transform var(--med) var(--md-easing-emphasized-decelerate), visibility var(--med);
  }
  .nav-links.open { opacity: 1; visibility: visible; transform: translateY(0); }
  .nav-links a { padding: .9rem 1rem; font-size: .9375rem; }

  /* Keep the primary CTA; the secondary one is reachable in the drawer. */
  .nav-actions .btn.secondary { display: none; }
}

/* ---------- 7. Hero -------------------------------------------------
   Flat M3 surface with a faint tonal wash from the primary and
   tertiary roles. The wash drifts slowly; the drift is dropped under
   prefers-reduced-motion.
   ------------------------------------------------------------------ */

.hero {
  position: relative;
  text-align: center;
  padding: clamp(3.5rem, 9vw, 6.5rem) 1.25rem clamp(2.5rem, 6vw, 4rem);
  overflow: hidden;
  isolation: isolate;
}
.hero::before, .hero::after {
  content: "";
  position: absolute; z-index: -1;
  border-radius: 50%;
  filter: blur(80px);
  opacity: .18;
  pointer-events: none;
}
.hero::before {
  width: 48rem; height: 30rem; top: -16rem; left: 50%;
  margin-left: -30rem;
  background: radial-gradient(circle, var(--md-sys-color-primary-container) 0%, transparent 68%);
  animation: drift-a 22s var(--ease) infinite alternate;
}
.hero::after {
  width: 40rem; height: 26rem; top: -12rem; left: 50%;
  margin-left: -6rem;
  background: radial-gradient(circle, var(--md-sys-color-tertiary) 0%, transparent 68%);
  opacity: .12;
  animation: drift-b 19s var(--ease) infinite alternate;
}
@keyframes drift-a { to { transform: translate(4rem, 2rem) scale(1.12); } }
@keyframes drift-b { to { transform: translate(-4rem, 3rem) scale(1.08); } }

/* display-large at the top of the clamp (57/64). */
.hero h1 {
  font-size: clamp(2.25rem, 5.6vw, 3.5625rem);
  line-height: 1.12;
  margin: 0 0 1.1rem;
  font-weight: 700;
  letter-spacing: -.022em;
}
.hero h1 .accent {
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.hero p {
  font-size: clamp(1rem, 1.9vw, 1.375rem);
  color: var(--md-sys-color-on-surface-variant);
  max-width: 40rem;
  margin: 0 auto 1.8rem;
  text-wrap: pretty;
}
.hero .cta { display: flex; gap: .75rem; justify-content: center; flex-wrap: wrap; }

/* M3 assist chip above the headline: "Official Meta API", etc. */
.eyebrow {
  display: inline-flex; align-items: center; gap: .5rem;
  min-height: 2rem;
  padding: .3rem 1rem .3rem .75rem;
  margin-bottom: 1.3rem;
  border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--md-shape-sm);
  background: var(--md-sys-color-surface-container-low);
  font-size: .875rem; font-weight: 500; letter-spacing: .00625em;
  color: var(--md-sys-color-on-surface);
}
.eyebrow .dot-live {
  width: .5rem; height: .5rem; border-radius: 50%;
  background: var(--md-sys-color-success); flex-shrink: 0;
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--md-sys-color-success) 60%, transparent);
  animation: pulse 2.4s ease-out infinite;
}
@keyframes pulse {
  70% { box-shadow: 0 0 0 .55rem transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* Three claims under the hero CTAs. Deliberately plain text rather than
   logos we do not have — it is the numbers that reassure, not chrome. */
.hero-proof {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: .6rem 2.2rem;
  list-style: none; margin: 2.6rem 0 0; padding: 0;
  color: var(--md-sys-color-on-surface-variant); font-size: .875rem;
}
.hero-proof li { display: flex; align-items: baseline; gap: .4rem; }
.hero-proof b {
  font-family: var(--font-display); font-weight: 700; font-size: 1rem;
  color: var(--md-sys-color-on-surface); letter-spacing: -.01em;
}

/* ---------- 8. Sections, cards, pricing ------------------------------ */

.container { max-width: 1080px; margin: 0 auto; padding: 0 1.25rem; }

/* Bare .narrow is a form column (auth cards, single-field panels).
   .container.narrow is a *prose* column, and 480px is far too tight a
   measure for running text — 760px lands around 75 characters. */
.narrow { max-width: 480px; }
.container.narrow { max-width: 760px; }

.section { padding: clamp(2.5rem, 6vw, 4.5rem) 0; }
/* headline-medium → headline-large */
.section h2 {
  text-align: center;
  font-size: clamp(1.75rem, 3.2vw, 2.25rem);
  line-height: 1.22;
  margin: 0 0 .5rem;
  scroll-margin-top: 5rem;
}
.section .sub {
  text-align: center; color: var(--md-sys-color-on-surface-variant);
  margin: 0 auto 2.2rem; max-width: 44rem;
  text-wrap: pretty;
}

.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; }

/* M3 elevated card. The state layer (::before) replaces the old pointer
   highlight; ui.js still writes --mx/--my, which is now simply unused. */
.card {
  position: relative;
  border: 0;
  border-radius: var(--md-shape-md);
  padding: 1.5rem;
  background: var(--md-sys-color-surface-container-low);
  box-shadow: var(--md-elevation-1);
  overflow: hidden;
  transition: box-shadow var(--fast) var(--ease);
}
.card::before {
  content: "";
  position: absolute; inset: 0;
  background: var(--md-sys-color-on-surface);
  opacity: 0;
  transition: opacity var(--fast) var(--ease);
  pointer-events: none;
}
.card:hover { box-shadow: var(--md-elevation-2); }
.card:hover::before { opacity: var(--md-state-hover); }
.card > * { position: relative; }
/* title-medium / body-medium */
.card h3 { margin: .75rem 0 .35rem; font-size: 1.0625rem; line-height: 1.5; font-weight: 600; }
.card p { margin: 0; color: var(--md-sys-color-on-surface-variant); font-size: .875rem; line-height: 1.45; }
.card .ico {
  display: grid; place-items: center;
  width: 2.75rem; height: 2.75rem;
  border-radius: var(--md-shape-md);
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
  font-size: 1.35rem; line-height: 1;
}

.pricing {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
  gap: 1rem; max-width: 1000px; margin: 0 auto;
  align-items: start;
}
/* Outlined card; the featured plan is elevated with a primary outline. */
.plan {
  position: relative;
  border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--md-shape-lg);
  padding: 1.75rem; background: var(--md-sys-color-surface);
  transition: box-shadow var(--fast) var(--ease);
}
.plan:hover { box-shadow: var(--md-elevation-1); }
.plan h3 { font-size: 1.375rem; line-height: 1.27; font-weight: 600; margin: 0 0 .5rem; }
.plan.featured {
  border: 2px solid var(--md-sys-color-primary);
  background: var(--md-sys-color-surface-container-low);
  box-shadow: var(--md-elevation-2);
}
.plan.featured:hover { box-shadow: var(--md-elevation-3); }
/* "Most popular" label, as an M3 filled chip straddling the border. */
.plan.featured::before {
  content: "Most popular";
  position: absolute; top: 0; left: 50%;
  transform: translate(-50%, -50%);
  background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary);
  font-size: .75rem; font-weight: 600; letter-spacing: .03em;
  padding: .3rem .85rem; border-radius: var(--md-shape-sm);
  white-space: nowrap;
}
.plan .price {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4vw, 2.8125rem); font-weight: 700; letter-spacing: -.02em;
}
.plan .price small { font-size: .875rem; font-weight: 500; color: var(--md-sys-color-on-surface-variant); letter-spacing: 0; }
.plan ul { list-style: none; padding: 0; margin: 1.1rem 0; }
.plan li {
  position: relative; padding: .36rem 0 .36rem 1.75rem;
  color: var(--md-sys-color-on-surface-variant); font-size: .9375rem;
}
/* Tick in a tonal circle. The circle is ::before and the tick is a mask
   on ::after, so both take their colour from tokens and the dark theme
   needs no second copy of the SVG. */
.plan li::before, .plan li::after {
  content: "";
  position: absolute; left: 0; top: .62rem;
  width: 1.15rem; height: 1.15rem; border-radius: 50%;
}
.plan li::before { background: var(--md-sys-color-secondary-container); }
.plan li::after {
  background: var(--md-sys-color-on-secondary-container);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / .72rem no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / .72rem no-repeat;
}
.plan .btn { width: 100%; }

/* ---------- 9. Footer ------------------------------------------------ */

.footer {
  padding: 2.4rem 1.25rem;
  text-align: center;
  color: var(--md-sys-color-on-surface-variant);
  font-size: .875rem;
  background: var(--md-sys-color-surface-container);
}
.footer p { margin: 0; line-height: 2.1; }
.footer a { color: var(--md-sys-color-on-surface-variant); }
.footer a:hover { color: var(--md-sys-color-primary); }

/* ---------- 10. Auth -------------------------------------------------- */

.auth-wrap {
  position: relative;
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 2rem 1.25rem;
  overflow: hidden; isolation: isolate;
  background: var(--md-sys-color-surface-container);
}
.auth-wrap::before {
  content: "";
  position: absolute; z-index: -1;
  width: 45rem; height: 32rem; top: -14rem; left: 50%; margin-left: -22.5rem;
  border-radius: 50%; filter: blur(90px); opacity: .2;
  background: radial-gradient(circle, var(--md-sys-color-primary-container) 0%, transparent 65%);
  animation: drift-a 22s var(--ease) infinite alternate;
}
.auth-card {
  width: 100%; max-width: 26rem;
  background: var(--md-surface-raised);
  border-radius: var(--md-shape-xl);
  padding: 2.25rem;
  box-shadow: var(--md-elevation-1);
  animation: rise var(--md-duration-long) var(--md-easing-emphasized-decelerate) both;
}
@keyframes rise { from { opacity: 0; transform: translateY(14px); } }
.auth-card h1 { font-size: 1.75rem; line-height: 1.29; margin: 0 0 .35rem; }
.auth-card .sub { color: var(--md-sys-color-on-surface-variant); margin: 0 0 1.6rem; font-size: .875rem; }
.auth-card .btn { width: 100%; }

.field { margin-bottom: 1rem; }
.field label {
  display: block; font-size: .8125rem; font-weight: 500; letter-spacing: .01em;
  margin-bottom: .35rem; color: var(--md-sys-color-on-surface-variant);
}
/* M3 outlined text field. Focus thickens the outline to 2px with an
   inset shadow, so the box never shifts by a pixel. */
.field input {
  width: 100%; min-height: 3.25rem; padding: .8rem 1rem;
  border: 1px solid var(--md-sys-color-outline); border-radius: var(--md-shape-xs);
  font: inherit; color: var(--md-sys-color-on-surface); background: transparent;
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.field input:hover { border-color: var(--md-sys-color-on-surface); }
.field input:focus { outline: none; border-color: var(--md-sys-color-primary); box-shadow: var(--ring); }

.auth-alt { text-align: center; margin-top: 1.1rem; font-size: .875rem; color: var(--md-sys-color-on-surface-variant); }
.error { color: var(--md-sys-color-error); font-size: .8125rem; min-height: 1.2em; margin-bottom: .4rem; }

/* Outlined button with a neutral label, as Google's own sign-in button. */
.google-btn {
  display: flex; align-items: center; justify-content: center; gap: .6rem;
  width: 100%; background: transparent; color: var(--md-sys-color-on-surface);
  border: 1px solid var(--md-sys-color-outline); box-shadow: none;
}
.google-btn:hover { background: transparent; color: var(--md-sys-color-on-surface); box-shadow: none; }
.auth-divider {
  display: flex; align-items: center; gap: .8rem;
  margin: 1.1rem 0 .3rem; color: var(--md-sys-color-on-surface-variant); font-size: .75rem;
  letter-spacing: .03em;
}
.auth-divider::before, .auth-divider::after { content: ""; flex: 1; height: 1px; background: var(--md-sys-color-outline-variant); }

/* ---------- 11. Panels, forms, tables --------------------------------- */

.dash { max-width: 960px; margin: 1.5rem auto; padding: 0 1.25rem; }

/* M3 filled card: a tonal container, no outline, no shadow at rest. */
.panel {
  border: 0;
  border-radius: var(--md-shape-md);
  padding: 1.5rem 1.6rem;
  margin: 1rem 0;
  background: var(--md-sys-color-surface-container-low);
  transition: box-shadow var(--fast) var(--ease);
}
/* title-large */
.panel > h2 {
  font-size: 1.1875rem; line-height: 1.4; margin: 0 0 1rem;
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
}
.panel > h2 .muted { font-weight: 500; }
.panel .panel-desc { margin: -.7rem 0 1.1rem; color: var(--md-sys-color-on-surface-variant); font-size: .875rem; }

label { color: var(--md-sys-color-on-surface-variant); }
.panel label, .modal label {
  display: block; font-size: .8125rem; margin: .95rem 0 .35rem;
  font-weight: 500; letter-spacing: .01em; color: var(--md-sys-color-on-surface-variant);
}
.panel label:first-of-type { margin-top: 0; }

.panel input, .panel select, .panel textarea,
.modal input, .modal select, .modal textarea,
input[type=text], input[type=url], input[type=datetime-local], input[type=email],
input[type=password], input[type=number], input[type=search], textarea, select {
  padding: .7rem .9rem;
  border: 1px solid var(--md-sys-color-outline);
  border-radius: var(--md-shape-xs);
  font: inherit;
  color: var(--md-sys-color-on-surface);
  /* background-color, never the `background` shorthand: the shorthand
     resets background-image and would wipe the custom <select> arrow set
     below, which is more specific than a bare `select` rule. Transparent,
     as the M3 outlined field sits on whatever container holds it. */
  background-color: transparent;
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease),
              background-color var(--fast) var(--ease);
}
/* Native option lists paint their own background; give them the
   surface so light text never lands on the OS's white popup. */
select option { background-color: var(--md-sys-color-surface-container); color: var(--md-sys-color-on-surface); }
.panel input, .panel select, .panel textarea,
.modal input, .modal select, .modal textarea { width: 100%; }
.panel input::placeholder, .panel textarea::placeholder,
.modal input::placeholder, .modal textarea::placeholder { color: var(--md-sys-color-on-surface-variant); opacity: .7; }
.panel input:hover, .panel select:hover, .panel textarea:hover,
.modal input:hover, .modal select:hover, .modal textarea:hover { border-color: var(--md-sys-color-on-surface); }
.panel input:focus, .panel select:focus, .panel textarea:focus,
.modal input:focus, .modal select:focus, .modal textarea:focus,
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--md-sys-color-primary); box-shadow: var(--ring);
}
.panel textarea, .modal textarea { resize: vertical; min-height: 3.4rem; }
.panel input[type=checkbox] {
  width: auto; margin-right: .45rem; accent-color: var(--md-sys-color-primary);
  vertical-align: -1px; cursor: pointer;
}
input[type=checkbox], input[type=radio] { accent-color: var(--md-sys-color-primary); cursor: pointer; }
input[type=checkbox]:focus, input[type=radio]:focus { box-shadow: none; }

/* Native select arrow, redrawn so it matches in both themes. The stroke
   colour (on-surface-variant) is baked into the data URI, so the dark
   theme needs its own copy rather than a token. */
select {
  appearance: none; -webkit-appearance: none;
  padding-right: 2.25rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%234a4455'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .6rem center;
  background-size: 1.25rem;
}
:root[data-theme="dark"] select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ccc3d8'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ccc3d8'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
  }
}

/* The browser's own "Choose File" chip ignores the surrounding theme, so
   it is redrawn as an M3 tonal button. */
input[type=file] { padding: .45rem .55rem; cursor: pointer; }
input[type=file]::file-selector-button {
  font: inherit; font-weight: 500; font-size: .875rem;
  margin-right: .75rem; padding: .45rem 1rem;
  border: 0; border-radius: var(--md-shape-full);
  background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container);
  cursor: pointer;
  transition: box-shadow var(--fast) var(--ease);
}
input[type=file]::file-selector-button:hover { box-shadow: var(--md-elevation-1); }

.row { display: flex; gap: .75rem; flex-wrap: wrap; align-items: center; }
.row > * { flex: 1; min-width: 160px; }
.actions-row { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: 1.1rem; }

.grid-2 { display: grid; grid-template-columns: 1fr; gap: 1rem; }
@media (min-width: 900px) { .grid-2 { grid-template-columns: 1fr 1fr; align-items: start; } }

/* M3 data table. */
.table-wrap {
  overflow-x: auto; margin-top: .9rem;
  border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--md-shape-md);
  background: var(--md-sys-color-surface);
}
table { width: 100%; border-collapse: collapse; font-size: .875rem; }
.table-wrap table { margin-top: 0; }
th, td { padding: .75rem 1rem; text-align: left; }
th {
  position: sticky; top: 0; z-index: 1;
  font-size: .8125rem; letter-spacing: .01em;
  color: var(--md-sys-color-on-surface-variant); font-weight: 600;
  background: var(--md-sys-color-surface-container);
  border-bottom: 1px solid var(--md-sys-color-outline-variant);
  white-space: nowrap;
}
td { border-bottom: 1px solid var(--md-sys-color-outline-variant); color: var(--md-sys-color-on-surface); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background var(--fast) var(--ease); }
tbody tr:hover { background: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, transparent); }

/* ---------- 12. Status, helpers, stats -------------------------------- */

.muted { color: var(--md-sys-color-on-surface-variant); font-size: .85rem; }

/* M3 chips: 8px shape, label-medium. */
.pill {
  display: inline-block; padding: .15rem .6rem; border-radius: var(--md-shape-sm);
  font-size: .75rem; font-weight: 600; letter-spacing: .03em;
  background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container);
}

.status {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .2rem .65rem; border-radius: var(--md-shape-sm);
  font-size: .75rem; font-weight: 600; letter-spacing: .03em; text-transform: capitalize;
  background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface-variant);
  white-space: nowrap;
}
.status::before { content: ""; width: .45rem; height: .45rem; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.status.ok, .status.published, .status.sent, .status.replied {
  background: var(--md-sys-color-success-container); color: var(--md-sys-color-on-success-container);
}
.status.warn, .status.scheduled, .status.processing, .status.received, .status.matched {
  background: var(--md-sys-color-warning-container); color: var(--md-sys-color-on-warning-container);
}
.status.danger, .status.failed, .status.skipped {
  background: var(--md-sys-color-error-container); color: var(--md-sys-color-on-error-container);
}
/* Work in progress reads better as a live indicator than a static dot. */
.status.processing::before, .status.scheduled::before { animation: pulse-dot 1.6s ease-in-out infinite; }
@keyframes pulse-dot { 50% { opacity: .3; transform: scale(.75); } }

/* M3 large badge. */
.badge {
  display: inline-block; min-width: 1rem; text-align: center;
  padding: 0 .3rem; border-radius: var(--md-shape-full);
  font-size: .6875rem; line-height: 1rem; font-weight: 600; letter-spacing: .03em;
  background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary);
}

.empty { text-align: center; color: var(--md-sys-color-on-surface-variant); padding: 2rem .5rem; font-size: .875rem; }

.notice {
  padding: .8rem 1rem; border-radius: var(--md-shape-md);
  font-size: .875rem; margin: .2rem 0 .7rem;
  animation: rise var(--md-duration-medium) var(--md-easing-emphasized-decelerate) both;
}
.notice.info { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }
.notice.ok { background: var(--md-sys-color-success-container); color: var(--md-sys-color-on-success-container); }
.notice.err { background: var(--md-sys-color-error-container); color: var(--md-sys-color-on-error-container); }

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); gap: .75rem; margin: .4rem 0; }
.stat {
  position: relative; overflow: hidden;
  border-radius: var(--md-shape-md);
  padding: 1rem 1.1rem; background: var(--md-sys-color-surface-container);
  transition: box-shadow var(--fast) var(--ease);
}
.stat:hover { box-shadow: var(--md-elevation-1); }
/* headline-small */
.stat .n {
  font-family: var(--font-display);
  font-size: 1.5rem; font-weight: 700; letter-spacing: -.01em; line-height: 1.33;
  color: var(--md-sys-color-on-surface);
}
.stat .l { color: var(--md-sys-color-on-surface-variant); font-size: .75rem; letter-spacing: .03em; margin-top: .15rem; }

/* ---------- 13. App shell -------------------------------------------- */

.app-shell { display: flex; min-height: 100vh; background: var(--md-sys-color-surface); }

/* M3 standard navigation drawer. */
.sidebar {
  width: 256px; flex-shrink: 0;
  padding: .75rem;
  background: var(--md-sys-color-surface-container-low);
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
.sidebar .brand { padding: 1rem 1rem 1.1rem; font-size: 1.25rem; }
.sidebar nav { display: flex; flex-direction: column; gap: 0; }
.sidebar nav a {
  position: relative;
  display: flex; align-items: center;
  min-height: 3.5rem;
  padding: 0 1rem;
  border-radius: var(--md-shape-full);
  color: var(--md-sys-color-on-surface-variant);
  font-weight: 500; font-size: .875rem; letter-spacing: .00625em;
  isolation: isolate; overflow: hidden;
}
.sidebar nav a::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--md-sys-color-on-surface); opacity: 0;
  transition: opacity var(--fast) var(--ease);
}
.sidebar nav a:hover { color: var(--md-sys-color-on-surface); }
.sidebar nav a:hover::after { opacity: var(--md-state-hover); }
.sidebar nav a.active {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
  font-weight: 600;
}
.sidebar nav a.active::after { background: var(--md-sys-color-on-secondary-container); }

.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* M3 small top app bar. */
.topbar {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 4rem;
  padding: .5rem 1.75rem;
  background: var(--md-sys-color-surface);
  position: sticky; top: 0; z-index: 10;
}
.topbar select {
  padding: .45rem 2.1rem .45rem .75rem;
  border: 1px solid var(--md-sys-color-outline); border-radius: var(--md-shape-sm);
  font: inherit; font-size: .875rem; max-width: 260px; background-color: transparent;
}
.topbar .right { display: flex; align-items: center; gap: .75rem; }

.content { padding: 1.5rem 1.9rem 3rem; max-width: 1040px; width: 100%; }
/* headline-medium */
.content > h1 { font-size: 1.75rem; line-height: 1.29; margin: 0 0 .3rem; }
.content > h1 + .muted { margin: 0 0 1.1rem; display: block; }

@media (max-width: 760px) {
  .app-shell { flex-direction: column; }
  .sidebar {
    width: auto; height: auto; position: sticky; top: 0; z-index: 20;
    padding: .5rem .75rem .6rem;
  }
  .sidebar .brand { padding: .4rem .5rem .5rem; }
  /* Horizontal, swipeable row of filter-chip-style items — the vertical
     drawer would eat the screen. */
  .sidebar nav {
    flex-direction: row; flex-wrap: nowrap;
    gap: .4rem; overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .sidebar nav::-webkit-scrollbar { display: none; }
  .sidebar nav a {
    /* flex-shrink off, or the items compress to fit and clip their
       labels instead of letting the rail scroll. */
    flex: 0 0 auto;
    white-space: nowrap; min-height: 2rem; padding: 0 .9rem; font-size: .8125rem;
    border-radius: var(--md-shape-sm);
    border: 1px solid var(--md-sys-color-outline-variant);
  }
  .sidebar nav a.active { border-color: transparent; }
  /* The account select plus the action group cannot both hold their
     width on a phone, so let the bar wrap and the select give way. */
  .topbar { padding: .5rem 1rem; flex-wrap: wrap; }
  .topbar select { max-width: 100%; flex: 1 1 12rem; min-width: 0; }
  .content { padding: 1.25rem 1rem 2.5rem; }
}

/* ---------- 14. Overlays: dialog, snackbar, menu --------------------- */

.modal-backdrop {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
  background: color-mix(in srgb, var(--md-sys-color-scrim) 32%, transparent);
  animation: fade var(--md-duration-short) var(--ease) both;
}
@keyframes fade { from { opacity: 0; } }
/* M3 basic dialog. */
.modal {
  width: min(35rem, 100%); max-height: 90vh; overflow-y: auto;
  background: var(--md-sys-color-surface-container-high);
  border-radius: var(--md-shape-xl);
  padding: 1.5rem;
  box-shadow: var(--md-elevation-3);
  animation: pop var(--md-duration-medium) var(--md-easing-emphasized-decelerate) both;
}
@keyframes pop { from { opacity: 0; transform: translateY(-12px) scale(.96); } }
/* headline-small */
.modal h2 { margin: 0 0 .5rem; font-size: 1.5rem; line-height: 1.33; }
.modal .modal-actions { display: flex; gap: .5rem; margin-top: 1.5rem; justify-content: flex-end; }
.modal .field-error { color: var(--md-sys-color-error); margin: .5rem 0 0; font-size: .8125rem; }

/* M3 snackbar, stacked bottom-right. Created by toast() in ui.js.
   Snackbars are inverse-surface; ok/err get a small leading marker
   in the inverse-appropriate tone, since the body colours don't flip. */
.toast-wrap {
  position: fixed; z-index: 80;
  right: 1rem; bottom: 1rem;
  display: flex; flex-direction: column; gap: .5rem;
  pointer-events: none;
  max-width: min(24rem, calc(100vw - 2rem));
}
.toast {
  display: flex; align-items: center; gap: .65rem;
  min-height: 3rem;
  padding: .75rem 1rem;
  border-radius: var(--md-shape-xs);
  background: var(--md-sys-color-inverse-surface);
  color: var(--md-sys-color-inverse-on-surface);
  box-shadow: var(--md-elevation-3);
  font-size: .875rem; line-height: 1.43; letter-spacing: .0156em;
  pointer-events: auto;
  animation: toast-in var(--md-duration-medium) var(--md-easing-emphasized-decelerate) both;
}
.toast.ok::before, .toast.err::before {
  content: ""; width: .5rem; height: .5rem; border-radius: 50%; flex-shrink: 0;
}
.toast.ok::before { background: #6ddba7; }
.toast.err::before { background: #ffb4ab; }
:root[data-theme="dark"] .toast.ok::before { background: #006c49; }
:root[data-theme="dark"] .toast.err::before { background: #ba1a1a; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .toast.ok::before { background: #006c49; }
  :root:not([data-theme="light"]) .toast.err::before { background: #ba1a1a; }
}
.toast.leaving { animation: toast-out var(--md-duration-short) var(--md-easing-emphasized-accelerate) both; }
@keyframes toast-in { from { opacity: 0; transform: translateY(1rem); } }
@keyframes toast-out { to { opacity: 0; transform: translateY(.5rem); } }

/* M3 menu. */
.hashtag-suggest {
  display: flex; flex-direction: column;
  margin-top: .15rem; margin-bottom: .5rem;
  padding: .5rem 0;
  border-radius: var(--md-shape-xs);
  background: var(--md-sys-color-surface-container); overflow: hidden auto; max-height: 14rem;
  box-shadow: var(--md-elevation-2);
  animation: rise var(--md-duration-short) var(--md-easing-emphasized-decelerate) both;
}
.hashtag-opt {
  display: flex; justify-content: space-between; align-items: center; gap: .8rem;
  min-height: 2.75rem; padding: .5rem .75rem; border: 0;
  background: none; font: inherit; font-size: .875rem; color: var(--md-sys-color-on-surface); text-align: left; cursor: pointer;
  transition: background var(--fast) var(--ease);
}
.hashtag-opt:hover { background: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, transparent); }
.hashtag-opt.active { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }

/* ============================================================
   15. Marketing pages (/, /pricing, /instagram-comment-to-dm,
   /manychat-alternative, /blog). App screens never use these.
   ============================================================ */

.breadcrumb { font-size: .875rem; color: var(--md-sys-color-on-surface-variant); margin-bottom: 1.1rem; }
.breadcrumb a { color: var(--md-sys-color-on-surface-variant); }
.breadcrumb a:hover { color: var(--md-sys-color-primary); text-decoration: underline; }

.lede {
  font-size: clamp(1rem, 2vw, 1.1875rem);
  color: var(--md-sys-color-on-surface-variant); max-width: 62ch; margin: 0 auto 1.7rem;
  text-wrap: pretty;
}

.steps { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); margin-top: 1.8rem; }
.step {
  position: relative;
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-shape-md); padding: 1.5rem; text-align: left;
  transition: box-shadow var(--fast) var(--ease);
}
.step:hover { box-shadow: var(--md-elevation-1); }
.step-n {
  display: grid; place-items: center;
  width: 2.25rem; height: 2.25rem; border-radius: var(--md-shape-full);
  background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary);
  font-family: var(--font-display); font-weight: 700; font-size: .9375rem;
  margin-bottom: .9rem;
}
.step h3 { margin: 0 0 .45rem; font-size: 1.0625rem; font-weight: 600; }
.step p { margin: 0; color: var(--md-sys-color-on-surface-variant); font-size: .9375rem; }

figure { margin: 1.5rem 0 0; }
figcaption { font-size: .8125rem; color: var(--md-sys-color-on-surface-variant); margin-top: .7rem; text-align: center; }

.trust {
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-shape-lg); padding: 1.6rem 1.8rem; text-align: left;
}
.trust h3 { margin: 1.4rem 0 .5rem; font-size: 1.0625rem; font-weight: 600; }
.trust h3:first-child { margin-top: 0; }
.trust p { margin: 0 0 .7rem; color: var(--md-sys-color-on-surface-variant); }
.trust p:last-child { margin-bottom: 0; }

.who { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); margin-top: 1.6rem; }
.who-card {
  background: var(--md-sys-color-surface);
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-shape-md); padding: 1.3rem; text-align: left;
  transition: box-shadow var(--fast) var(--ease);
}
.who-card:hover { box-shadow: var(--md-elevation-1); }
.who-card h3 { margin: 0 0 .4rem; font-size: 1rem; font-weight: 600; }
.who-card p { margin: 0; font-size: .875rem; color: var(--md-sys-color-on-surface-variant); }

/* M3 expansion list. */
.faq { max-width: 780px; margin: 1.6rem auto 0; text-align: left; }
.faq details {
  background: var(--md-sys-color-surface-container-low);
  border-radius: var(--md-shape-md); margin-bottom: .5rem;
  transition: box-shadow var(--fast) var(--ease), background var(--fast) var(--ease);
}
.faq details[open] { background: var(--md-sys-color-surface-container); }
.faq summary {
  cursor: pointer; padding: 1rem 3rem 1rem 1.25rem;
  font-weight: 600; list-style: none; position: relative;
  border-radius: var(--md-shape-md);
  color: var(--md-sys-color-on-surface);
}
.faq summary:hover { background: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, transparent); }
.faq summary::-webkit-details-marker { display: none; }
/* A "+" drawn from two bars, so it can rotate into a "−". */
.faq summary::after, .faq summary::before {
  content: ""; position: absolute; right: 1.25rem; top: 50%;
  width: .75rem; height: 2px; border-radius: 2px;
  background: var(--md-sys-color-on-surface-variant);
  transition: transform var(--med) var(--ease), opacity var(--fast) var(--ease);
}
.faq summary::before { transform: translateY(-50%); }
.faq summary::after { transform: translateY(-50%) rotate(90deg); }
.faq details[open] summary::after { transform: translateY(-50%) rotate(0deg); opacity: 0; }
.faq details > p {
  margin: 0; padding: 0 1.25rem 1.15rem; color: var(--md-sys-color-on-surface-variant);
  animation: reveal-p var(--md-duration-medium) var(--md-easing-emphasized-decelerate) both;
}
@keyframes reveal-p { from { opacity: 0; transform: translateY(-5px); } }

.cmp { width: 100%; border-collapse: collapse; font-size: .9375rem; }
.cmp th, .cmp td { padding: .85rem 1rem; border-bottom: 1px solid var(--md-sys-color-outline-variant); text-align: left; vertical-align: top; }
.cmp th { background: var(--md-sys-color-surface-container); font-size: .8125rem; letter-spacing: .01em; color: var(--md-sys-color-on-surface-variant); }
.cmp td:first-child { font-weight: 600; color: var(--md-sys-color-on-surface); }
.cmp tbody tr { transition: background var(--fast) var(--ease); }
.cmp tbody tr:hover { background: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, transparent); }
.cmp .yes { color: var(--md-sys-color-success); font-weight: 600; }
.cmp .no { color: var(--md-sys-color-on-surface-variant); }

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: 1rem; top: 1rem; z-index: 100; background: var(--md-sys-color-surface-container-high);
  padding: .65rem 1.1rem; border-radius: var(--md-shape-sm); box-shadow: var(--md-elevation-3);
}

/* ---- Product previews -------------------------------------------------
   Faithful renderings of the real rule editor and a delivered DM, built
   from the same tokens as the app rather than exported as screenshots:
   crisp at any DPI, readable by crawlers, correct in both themes, and
   impossible to drift out of date silently. Field labels mirror
   public/rules.html exactly.
   --------------------------------------------------------------------- */

.ui-preview {
  background: var(--md-surface-raised); border-radius: var(--md-shape-lg);
  box-shadow: var(--md-elevation-3); overflow: hidden; text-align: left;
  max-width: 560px; margin: 0 auto;
  transition: box-shadow var(--med) var(--ease);
}
.ui-preview:hover { box-shadow: var(--md-elevation-4); }
.ui-bar {
  display: flex; align-items: center; gap: .4rem; padding: .7rem .9rem;
  background: var(--md-sys-color-surface-container);
}
.ui-bar i { width: 10px; height: 10px; border-radius: 50%; background: var(--md-sys-color-outline-variant); display: block; }
.ui-bar i:nth-child(1) { background: #ff5f57; }
.ui-bar i:nth-child(2) { background: #febc2e; }
.ui-bar i:nth-child(3) { background: #28c840; }
.ui-bar span { margin-left: .5rem; font-size: .75rem; color: var(--md-sys-color-on-surface-variant); font-weight: 500; }
.ui-body { padding: 1.15rem 1.25rem 1.35rem; }
.ui-field { margin-bottom: .9rem; }
.ui-field:last-child { margin-bottom: 0; }
.ui-field > label {
  display: block; font-size: .75rem; font-weight: 500; letter-spacing: .03em;
  color: var(--md-sys-color-on-surface-variant); margin-bottom: .32rem;
}
.ui-input {
  border: 1px solid var(--md-sys-color-outline); border-radius: var(--md-shape-xs);
  padding: .55rem .75rem; font-size: .875rem; color: var(--md-sys-color-on-surface); background: transparent;
}
.ui-input.is-select { display: flex; justify-content: space-between; align-items: center; color: var(--md-sys-color-on-surface-variant); }
.ui-input.is-select::after {
  content: ""; width: 1.1rem; height: 1.1rem; flex-shrink: 0;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.ui-input.is-active { border-color: var(--md-sys-color-primary); box-shadow: var(--ring); }
.ui-row { display: grid; grid-template-columns: 1fr 1fr; gap: .7rem; }
.ui-chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.ui-chip {
  background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container);
  border-radius: var(--md-shape-sm);
  padding: .2rem .65rem; font-size: .8125rem; font-weight: 500;
}
.ui-note { font-size: .75rem; color: var(--md-sys-color-on-surface-variant); margin: .38rem 0 0; }

/* The comment → DM sequence. Steps fade in one after another once
   scrolled into view (ui.js adds .in), which is the clearest way to
   show that the DM is a *consequence* of the comment. */
.chat-preview { max-width: 400px; margin: 0 auto; text-align: left; display: grid; gap: .6rem; }
.chat-step { font-size: .75rem; font-weight: 500; letter-spacing: .03em; color: var(--md-sys-color-on-surface-variant); margin: 0; }
.bubble { border-radius: var(--md-shape-lg); padding: .8rem .95rem; font-size: .875rem; line-height: 1.5; }
.bubble.them {
  background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface);
  border-bottom-left-radius: var(--md-shape-xs);
}
.bubble.us {
  background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary);
  border-bottom-right-radius: var(--md-shape-xs);
}
.bubble .who { display: block; font-size: .75rem; font-weight: 600; color: var(--md-sys-color-on-surface-variant); margin-bottom: .25rem; }
.bubble.us .who { color: color-mix(in srgb, var(--md-sys-color-on-primary) 80%, transparent); }
/* Depicts Instagram's own DM card, which is light regardless of our
   theme — hence literal colours. */
.dm-card {
  background: #fff; border-radius: var(--md-shape-md);
  padding: .75rem .85rem; margin-top: .6rem; color: #1d1a24;
  box-shadow: var(--md-elevation-1);
}
.dm-card b { display: block; font-size: .85rem; margin-bottom: .5rem; }
.dm-card span {
  display: block; text-align: center;
  background: #eaddff; color: #4c00a9;
  border-radius: var(--md-shape-full); padding: .45rem;
  font-size: .82rem; font-weight: 600;
}
.chat-arrow { text-align: center; color: var(--md-sys-color-on-surface-variant); font-size: 1.1rem; line-height: 1; margin: 0; }

/* Sequenced entrance for the chat steps. Needs its own keyframes with an
   explicit `to`: the shared `rise` only declares `from`, so its end state
   would resolve to these children's own opacity:0 and nothing would show. */
.chat-preview.seq > * { opacity: 0; transform: translateY(10px); }
@keyframes seq-in { to { opacity: 1; transform: none; } }
.chat-preview.seq.in > * { animation: seq-in var(--md-duration-long) var(--md-easing-emphasized-decelerate) forwards; }
.chat-preview.seq.in > *:nth-child(1) { animation-delay: .05s; }
.chat-preview.seq.in > *:nth-child(2) { animation-delay: .2s; }
.chat-preview.seq.in > *:nth-child(3) { animation-delay: .55s; }
.chat-preview.seq.in > *:nth-child(4) { animation-delay: .7s; }
.chat-preview.seq.in > *:nth-child(5) { animation-delay: .9s; }

/* ---- 16. Blog (generated by scripts/build-blog.ts) ---- */

.post-meta { font-size: .875rem; color: var(--md-sys-color-on-surface-variant); margin: -.4rem 0 1.7rem; }
.post-body { text-align: left; font-size: 1.0625rem; line-height: 1.75; color: var(--md-sys-color-on-surface); }
.post-body > :first-child { margin-top: 0; }
.post-body h2 { font-size: 1.5rem; color: var(--md-sys-color-on-surface); margin: 2.4rem 0 .75rem; line-height: 1.33; text-align: left; }
.post-body h3 { font-size: 1.1875rem; color: var(--md-sys-color-on-surface); margin: 1.8rem 0 .5rem; }
.post-body p { margin: 0 0 1.1rem; }
.post-body ul, .post-body ol { margin: 0 0 1.1rem; padding-left: 1.35rem; }
.post-body li { margin-bottom: .5rem; }
.post-body li::marker { color: var(--md-sys-color-primary); }
.post-body strong { color: var(--md-sys-color-on-surface); font-weight: 650; }
.post-body a { color: var(--md-sys-color-primary); text-decoration: underline; text-decoration-color: var(--brand-line); text-underline-offset: 3px; }
.post-body a:hover { text-decoration-color: currentColor; }
.post-body blockquote {
  margin: 1.4rem 0; padding: 1rem 1.25rem;
  border-left: 4px solid var(--md-sys-color-primary);
  background: var(--md-sys-color-surface-container);
  border-radius: 0 var(--md-shape-md) var(--md-shape-md) 0;
  color: var(--md-sys-color-on-surface);
}
.post-body blockquote p { margin: 0; }
.post-body table {
  width: 100%; border-collapse: collapse; margin: 1.4rem 0; font-size: .9375rem;
  display: block; overflow-x: auto;
}
.post-body th, .post-body td { padding: .65rem .85rem; border-bottom: 1px solid var(--md-sys-color-outline-variant); text-align: left; vertical-align: top; }
.post-body th { background: var(--md-sys-color-surface-container); font-size: .8125rem; letter-spacing: .01em; color: var(--md-sys-color-on-surface-variant); white-space: nowrap; }
.post-body code {
  background: var(--md-sys-color-surface-container-high);
  border-radius: var(--md-shape-xs); padding: .12rem .35rem; font-size: .87em; color: var(--md-sys-color-on-surface);
}
.post-body hr { border: none; border-top: 1px solid var(--md-sys-color-outline-variant); margin: 2.2rem 0; }

.post-cta {
  margin-top: 2.8rem; padding: 1.75rem 1.8rem; text-align: left;
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
  border-radius: var(--md-shape-xl);
}
.post-cta h2 { margin: 0 0 .5rem; font-size: 1.375rem; text-align: left; color: inherit; }
.post-cta p { margin: 0 0 1.2rem; color: inherit; font-size: .9375rem; }

.post-list { list-style: none; margin: 0; padding: 0; text-align: left; }
.post-item {
  padding: 1.5rem 0; border-bottom: 1px solid var(--md-sys-color-outline-variant);
}
.post-item:last-child { border-bottom: none; }
.post-item-date { font-size: .75rem; font-weight: 600; letter-spacing: .03em; color: var(--md-sys-color-primary); margin: 0 0 .4rem; }
.post-item h2 { margin: 0 0 .45rem; font-size: 1.375rem; line-height: 1.32; text-align: left; }
.post-item h2 a { color: var(--md-sys-color-on-surface); }
.post-item h2 a:hover { color: var(--md-sys-color-primary); }
.post-item p { margin: 0 0 .6rem; color: var(--md-sys-color-on-surface-variant); font-size: .9375rem; }
/* The arrow is drawn here, not written into the generated markup, so it can
   animate on hover — build-blog.ts emits the label alone. */
.post-item-more { font-size: .875rem; font-weight: 500; color: var(--md-sys-color-primary); display: inline-flex; align-items: center; gap: .3rem; }
.post-item-more::after { content: "→"; transition: transform var(--fast) var(--ease); }
.post-item:hover .post-item-more::after { transform: translateX(3px); }

article.narrow { max-width: 720px; }

/* ---------- 17. Motion ------------------------------------------------
   .reveal is applied in markup; ui.js adds .in as the element scrolls
   into view. The `no-js` guard means content is never hidden if the
   script fails to load — the class only takes effect once ui.js has
   marked the document as script-enabled.
   ------------------------------------------------------------------ */

html.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--md-duration-long) var(--md-easing-emphasized-decelerate),
              transform var(--md-duration-long) var(--md-easing-emphasized-decelerate);
}
html.js .reveal.in { opacity: 1; transform: none; }
html.js .reveal.d1 { transition-delay: .08s; }
html.js .reveal.d2 { transition-delay: .16s; }
html.js .reveal.d3 { transition-delay: .24s; }
html.js .reveal.d4 { transition-delay: .32s; }

/* Everything decorative collapses to a cut. Functional transitions
   (focus rings, hover colour) survive at a duration users perceive as
   instant. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  html.js .reveal { opacity: 1; transform: none; }
  .hero::before, .hero::after, .auth-wrap::before { animation: none; }
}

/* Printing: drop the furniture, keep the words. */
@media print {
  .nav, .sidebar, .topbar, .footer, .post-cta, .toast-wrap, .theme-toggle, .nav-toggle { display: none !important; }
  body { background: #fff; color: #000; }
  .panel, .card, .plan { box-shadow: none; border: 1px solid #ccc; }
}
