/* Public site + client portal.
 *
 * Deliberately separate from style.css, which dresses the staff dashboard and is
 * not touched by this. Two stylesheets rather than one because they answer to
 * different constraints: the dashboard optimises for information density on a
 * screen an operator stares at all day, this one has to sell something in about
 * four seconds.
 *
 * Self-hosted and self-contained, same rule as style.css - no CDN, no webfont
 * fetch, nothing that a `default-src 'self'` CSP would have to be widened for.
 * Every font here is one the OS already has.
 *
 * There is no JavaScript on any page using this file. The page transitions,
 * scroll reveals, marquee and disclosure widgets below are all CSS, which is why
 * the enforced `script-src 'none'` in app/main.py survives the redesign intact.
 * Anything here that a browser doesn't support degrades to a static, legible
 * page rather than a broken one - test that claim by disabling the feature, not
 * by assuming it.
 */

/* ---------------------------------------------------------------- tokens -- */

:root {
  color-scheme: dark light;

  /* oklch, so the accent ramp stays perceptually even as lightness moves -
     the same steps in hsl() go muddy through the blues. */
  --accent: oklch(72% 0.17 195);
  --accent-bright: oklch(84% 0.16 190);
  --accent-deep: oklch(52% 0.15 205);
  --accent-alt: oklch(70% 0.19 295);

  --bg: oklch(16% 0.02 250);
  --bg-raised: oklch(20% 0.025 250);
  --bg-sunken: oklch(13% 0.02 250);
  --surface: oklch(22% 0.025 252);
  --surface-hover: oklch(26% 0.03 252);

  --fg: oklch(96% 0.005 250);
  --fg-muted: oklch(74% 0.015 250);
  --fg-dim: oklch(60% 0.015 250);

  --line: oklch(30% 0.02 250);
  --line-bright: oklch(40% 0.03 250);

  --ok: oklch(76% 0.16 155);
  --warn: oklch(80% 0.15 85);
  --danger: oklch(68% 0.19 25);

  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI Variable Display",
    "Segoe UI", Inter, Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, "Cascadia Code", "SF Mono", "JetBrains Mono", Menlo,
    Consolas, monospace;

  /* Fluid type. clamp() rather than breakpoints so headings scale continuously -
     a hero that jumps two sizes at 768px reads as a template. */
  --step-0: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  --step-1: clamp(1.2rem, 1.1rem + 0.5vw, 1.4rem);
  --step-2: clamp(1.5rem, 1.3rem + 1vw, 2rem);
  --step-3: clamp(2rem, 1.6rem + 2vw, 3rem);
  --step-4: clamp(2.6rem, 1.9rem + 3.4vw, 4.5rem);

  --radius: 14px;
  --radius-sm: 8px;
  --radius-lg: 24px;

  --shell: 1180px;
  --shell-narrow: 720px;

  --shadow: 0 1px 2px oklch(0% 0 0 / 0.3), 0 8px 24px -12px oklch(0% 0 0 / 0.5);
  --shadow-lg: 0 2px 4px oklch(0% 0 0 / 0.3), 0 24px 60px -20px oklch(0% 0 0 / 0.6);

  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* Three states, not two: system (no attribute), explicit light, explicit dark.
   `data-theme` is stamped on <html> from a cookie the account page writes - see
   app/web/theme.py. It has to be three, because a toggle that can only say "light" or
   "dark" takes away the ability to follow the OS, which is what most people actually
   want and what this shipped with.

   The two blocks below are the same declarations twice and have to stay in step. The
   duplication is unavoidable without a preprocessor: one is inside a media query and
   one is not, and there is no selector that means "either of these". Changing a light
   value means changing it in both places.

   The guard on the first is what makes an explicit *dark* choice win on a machine set
   to light - without :not([data-theme="dark"]) the media query would repaint it. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --accent: oklch(52% 0.14 205);
    --accent-bright: oklch(45% 0.15 205);
    --accent-deep: oklch(38% 0.12 210);
    --accent-alt: oklch(50% 0.19 295);

    --bg: oklch(98.5% 0.003 250);
    --bg-raised: oklch(100% 0 0);
    --bg-sunken: oklch(96% 0.005 250);
    --surface: oklch(100% 0 0);
    --surface-hover: oklch(97% 0.006 250);

    --fg: oklch(22% 0.015 250);
    --fg-muted: oklch(45% 0.015 250);
    --fg-dim: oklch(58% 0.012 250);

    --line: oklch(90% 0.006 250);
    --line-bright: oklch(82% 0.01 250);

    --ok: oklch(52% 0.14 155);
    --warn: oklch(58% 0.13 75);
    --danger: oklch(53% 0.19 25);

    --shadow: 0 1px 2px oklch(50% 0.02 250 / 0.08), 0 8px 24px -12px oklch(50% 0.02 250 / 0.18);
    --shadow-lg: 0 2px 4px oklch(50% 0.02 250 / 0.08), 0 24px 60px -20px oklch(50% 0.02 250 / 0.22);
  
  }
}

/* Explicit light, which must also beat a system set to dark. Keep in step with above. */
:root[data-theme="light"] {
    --accent: oklch(52% 0.14 205);
    --accent-bright: oklch(45% 0.15 205);
    --accent-deep: oklch(38% 0.12 210);
    --accent-alt: oklch(50% 0.19 295);

    --bg: oklch(98.5% 0.003 250);
    --bg-raised: oklch(100% 0 0);
    --bg-sunken: oklch(96% 0.005 250);
    --surface: oklch(100% 0 0);
    --surface-hover: oklch(97% 0.006 250);

    --fg: oklch(22% 0.015 250);
    --fg-muted: oklch(45% 0.015 250);
    --fg-dim: oklch(58% 0.012 250);

    --line: oklch(90% 0.006 250);
    --line-bright: oklch(82% 0.01 250);

    --ok: oklch(52% 0.14 155);
    --warn: oklch(58% 0.13 75);
    --danger: oklch(53% 0.19 25);

    --shadow: 0 1px 2px oklch(50% 0.02 250 / 0.08), 0 8px 24px -12px oklch(50% 0.02 250 / 0.18);
    --shadow-lg: 0 2px 4px oklch(50% 0.02 250 / 0.08), 0 24px 60px -20px oklch(50% 0.02 250 / 0.22);
  
}

/* ----------------------------------------------------------------- reset -- */

*, *::before, *::after { box-sizing: border-box; }

body, h1, h2, h3, h4, p, ul, ol, figure, blockquote, dl, dd { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-block-start: 5rem;
}

body {
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--fg);
  background: var(--bg);
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

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

:where(h1, h2, h3, h4) {
  line-height: 1.1;
  letter-spacing: -0.022em;
  font-weight: 620;
  text-wrap: balance;
}

p { text-wrap: pretty; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 0.2em; }
a:hover { color: var(--accent-bright); }

:focus-visible {
  outline: 2px solid var(--accent-bright);
  outline-offset: 3px;
  border-radius: 4px;
}

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

/* Cross-document view transitions. No JS involved - the browser animates between
   two full page loads. Browsers without it simply navigate. */
@view-transition { navigation: auto; }

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

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: clamp(1rem, 4vw, 2.5rem);
}

.shell--narrow { max-width: var(--shell-narrow); }

main { flex: 1; }

.section { padding-block: clamp(3.5rem, 9vw, 7rem); }
.section--tight { padding-block: clamp(2rem, 5vw, 3.5rem); }
.section--sunken { background: var(--bg-sunken); border-block: 1px solid var(--line); }

.stack > * + * { margin-block-start: var(--gap, 1rem); }
.stack--lg { --gap: 1.75rem; }
.stack--sm { --gap: 0.5rem; }

.cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap, 0.75rem);
  align-items: center;
}

.grid-auto {
  display: grid;
  gap: clamp(1rem, 2.5vw, 1.75rem);
  grid-template-columns: repeat(auto-fit, minmax(min(270px, 100%), 1fr));
}

.split {
  display: grid;
  gap: clamp(1.5rem, 5vw, 4rem);
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .split { grid-template-columns: 1.1fr 0.9fr; }
  .split--even { grid-template-columns: 1fr 1fr; }
}

.section-head { max-width: 62ch; margin-block-end: clamp(2rem, 4vw, 3rem); }
.section-head h2 { font-size: var(--step-3); }
.section-head p { color: var(--fg-muted); font-size: var(--step-1); margin-block-start: 0.75rem; }

/* ------------------------------------------------------------ typography -- */

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.775rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}

.eyebrow::before {
  content: "";
  inline-size: 1.75rem;
  block-size: 1px;
  background: linear-gradient(90deg, transparent, var(--accent));
}

.lede { font-size: var(--step-1); color: var(--fg-muted); max-width: 58ch; }
.muted { color: var(--fg-muted); }
.dim { color: var(--fg-dim); }
.small { font-size: 0.85rem; }

.gradient-text {
  background: linear-gradient(100deg, var(--fg) 20%, var(--accent-bright) 55%, var(--accent-alt));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ------------------------------------------------------------------ nav -- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in oklab, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  border-block-end: 1px solid var(--line);
  view-transition-name: topbar;
}

.topbar__inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-block-size: 4rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 640;
  letter-spacing: -0.02em;
  color: var(--fg);
  text-decoration: none;
  margin-inline-end: auto;
}

.brand__mark {
  inline-size: 1.9rem;
  block-size: 1.9rem;
  border-radius: 7px;
  background: linear-gradient(135deg, var(--accent), var(--accent-alt));
  display: grid;
  place-items: center;
  color: oklch(15% 0.02 250);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.95rem;
  box-shadow: 0 0 0 1px oklch(100% 0 0 / 0.12) inset;
}

.navlinks { display: flex; gap: 1.4rem; align-items: center; list-style: none; margin: 0; padding: 0; }
.navlinks a { color: var(--fg-muted); text-decoration: none; font-size: 0.925rem; }
.navlinks a:hover { color: var(--fg); }

/* CSS-only mobile nav: a checkbox plus :has() on the ancestor. No script. */
.nav-toggle { display: none; }
.nav-toggle__label { display: none; }

@media (max-width: 800px) {
  .nav-toggle__label {
    display: grid;
    place-items: center;
    inline-size: 2.5rem;
    block-size: 2.5rem;
    border: 1px solid var(--line-bright);
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--fg);
  }
  .navlinks {
    display: none;
    position: absolute;
    inset-inline: 0;
    top: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0.5rem;
    background: var(--bg-raised);
    border-block-end: 1px solid var(--line);
  }
  .navlinks a, .navlinks .btn { padding: 0.7rem 0.9rem; width: 100%; justify-content: flex-start; }
  .topbar:has(.nav-toggle:checked) .navlinks { display: flex; }
}

/* ----------------------------------------------------------- nav dropdown -- */

/* Grouped nav menus. No script anywhere in this app (`script-src 'none'`, F19), so the
   open state is :hover and :focus-within and nothing else. The trigger is a real anchor
   to the section's landing page, which is what keeps the whole thing usable on touch,
   where :hover is unreliable and :focus-within needs a tap that would have navigated
   anyway. */
.navmenu { position: relative; }

.navmenu__trigger { display: inline-flex; align-items: center; gap: 0.35rem; }

/* Drawn in CSS rather than an SVG or a glyph so it inherits currentColor and needs no
   font that might not be present. */
.navmenu__caret {
  inline-size: 0.34rem;
  block-size: 0.34rem;
  border-inline-end: 1.5px solid currentColor;
  border-block-end: 1.5px solid currentColor;
  transform: translateY(-1px) rotate(45deg);
  opacity: 0.7;
  transition: transform 0.18s var(--ease-out);
}
.navmenu:hover .navmenu__caret,
.navmenu:focus-within .navmenu__caret { transform: translateY(1px) rotate(225deg); }

.navmenu__panel {
  position: absolute;
  inset-block-start: calc(100% + 0.55rem);
  inset-inline-start: 0;
  z-index: 40;
  min-inline-size: 12rem;
  margin: 0;
  padding: 0.35rem;
  list-style: none;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);

  /* visibility, not display:none - display is not transitionable, and a panel that
     pops in with no fade reads as a rendering glitch at this size. */
  visibility: hidden;
  opacity: 0;
  transform: translateY(-0.25rem);
  transition: opacity 0.15s var(--ease), transform 0.15s var(--ease-out), visibility 0.15s;
}

/* Anchored to the right edge instead, so the last menu in the bar cannot push the
   page into horizontal scroll. */
.navmenu--trailing .navmenu__panel { inset-inline-start: auto; inset-inline-end: 0; }

.navmenu:hover .navmenu__panel,
.navmenu:focus-within .navmenu__panel {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
}

/* The fade is decoration; the menu opening is not. Anyone who has asked for less motion
   gets the panel immediately, and - the reason this is worth writing rather than
   assuming - `visibility` only leaves `hidden` as the transition advances, so anywhere
   transitions do not actually run the menu would otherwise stay invisible while every
   other rule says it is open. */
@media (prefers-reduced-motion: reduce) {
  .navmenu__panel { transition: none; }
  .navmenu__caret { transition: none; }
}

/* The gap between trigger and panel would otherwise drop the hover on the way down. */
.navmenu__panel::before {
  content: "";
  position: absolute;
  inset-block-end: 100%;
  inset-inline: 0;
  block-size: 0.55rem;
}

.navmenu__panel a {
  display: block;
  padding: 0.5rem 0.65rem;
  border-radius: 6px;
  color: var(--fg-muted);
  white-space: nowrap;
}
.navmenu__panel a:hover,
.navmenu__panel a:focus-visible {
  background: var(--surface-hover);
  color: var(--fg);
}

/* Log out is a POST with a CSRF token, so it is a form and a <button> rather than a
   link - and it has to read as another row of the menu, not as a button dropped into
   one. Everything here is undoing button-ness; the :hover treatment is deliberately the
   same declaration block as the anchors above rather than a lookalike. */
.navmenu__panel form { margin: 0; }
.navmenu__panel form button {
  display: block;
  inline-size: 100%;
  padding: 0.5rem 0.65rem;
  border: none;
  border-radius: 6px;
  background: none;
  box-shadow: none;
  color: var(--fg-muted);
  font: inherit;
  font-size: 0.925rem;
  text-align: start;
  white-space: nowrap;
  cursor: pointer;
}
.navmenu__panel form button:hover,
.navmenu__panel form button:focus-visible {
  background: var(--surface-hover);
  color: var(--fg);
}

/* Below the portal's nav breakpoint the topbar is the CSS-only hamburger list, which is
   a vertical stack with no hover to speak of. Flatten the panels into it: always open,
   in flow, indented under their trigger. */
@media (max-width: 800px) {
  .navmenu { position: static; }
  .navmenu__caret { display: none; }
  .navmenu__panel {
    position: static;
    visibility: visible;
    opacity: 1;
    transform: none;
    min-inline-size: 0;
    margin-inline-start: 0.9rem;
    padding: 0;
    background: none;
    border: none;
    border-inline-start: 1px solid var(--line);
    border-radius: 0;
    box-shadow: none;
  }
  .navmenu__panel::before { content: none; }
  /* The trigger duplicates its panel's first item once flattened. */
  .navmenu__panel > li:first-child { display: none; }
}

/* --------------------------------------------------------------- buttons -- */

.btn {
  --btn-bg: var(--accent);
  --btn-fg: oklch(16% 0.02 250);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.7rem 1.35rem;
  border: 1px solid transparent;
  border-radius: 100px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: inherit;
  font-weight: 600;
  font-size: 0.94rem;
  letter-spacing: -0.01em;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 0.18s var(--ease-out), box-shadow 0.18s var(--ease),
    background-color 0.18s var(--ease), border-color 0.18s var(--ease);
}

.btn:hover {
  color: var(--btn-fg);
  transform: translateY(-1px);
  box-shadow: 0 6px 20px -8px color-mix(in oklab, var(--accent) 70%, transparent);
}
.btn:active { transform: translateY(0); }

.btn--lg { padding: 0.9rem 1.9rem; font-size: 1rem; }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--fg);
  border-color: var(--line-bright);
}
.btn--ghost:hover { background: var(--surface-hover); box-shadow: none; }

.btn--quiet {
  --btn-bg: transparent;
  --btn-fg: var(--fg-muted);
  border-color: transparent;
  padding-inline: 0.6rem;
}
.btn--quiet:hover { --btn-fg: var(--fg); background: var(--surface-hover); box-shadow: none; }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* ---------------------------------------------------------------- surface -- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.25rem, 3vw, 1.85rem);
  box-shadow: var(--shadow);
}

.card--interactive { transition: border-color 0.2s var(--ease), transform 0.2s var(--ease-out); }
.card--interactive:hover { border-color: var(--line-bright); transform: translateY(-2px); }

/* Gradient hairline border. A pseudo-element with a masked gradient rather than
   border-image, because border-image and border-radius do not compose. */
.card--lit { position: relative; isolation: isolate; }
.card--lit::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(140deg,
    color-mix(in oklab, var(--accent) 60%, transparent),
    transparent 40%,
    transparent 60%,
    color-mix(in oklab, var(--accent-alt) 45%, transparent));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  pointer-events: none;
}

.card__title { font-size: var(--step-1); }
.card__icon {
  inline-size: 2.4rem;
  block-size: 2.4rem;
  border-radius: 10px;
  display: grid;
  place-items: center;
  background: color-mix(in oklab, var(--accent) 16%, transparent);
  color: var(--accent-bright);
  margin-block-end: 1rem;
}

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

.hero {
  position: relative;
  overflow: clip;
  padding-block: clamp(4rem, 12vw, 8.5rem) clamp(3rem, 8vw, 6rem);
  isolation: isolate;
}

/* Layered gradient mesh. Three radials at different hues, plus a conic sweep -
   cheaper and sharper than shipping a background image, and it recolours itself
   from the tokens when the theme flips. */
.hero::before {
  content: "";
  position: absolute;
  inset: -30% -10% auto -10%;
  block-size: 130%;
  z-index: -2;
  background:
    radial-gradient(50% 45% at 18% 22%, color-mix(in oklab, var(--accent) 30%, transparent), transparent 70%),
    radial-gradient(45% 40% at 82% 12%, color-mix(in oklab, var(--accent-alt) 26%, transparent), transparent 68%),
    radial-gradient(60% 50% at 55% 85%, color-mix(in oklab, var(--accent-deep) 22%, transparent), transparent 72%);
  filter: blur(20px);
  opacity: 0.85;
}

/* Grid rule overlay, masked to fade out - reads as "instrumentation" without a
   background image. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(to right, color-mix(in oklab, var(--fg) 7%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in oklab, var(--fg) 7%, transparent) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(120% 90% at 50% 0%, #000 25%, transparent 75%);
  pointer-events: none;
}

.hero__inner { position: relative; max-width: 46rem; }

.hero h1 {
  font-size: var(--step-4);
  letter-spacing: -0.04em;
  font-weight: 680;
  margin-block: 1.25rem 1.5rem;
}

.hero .lede { font-size: clamp(1.05rem, 0.98rem + 0.5vw, 1.3rem); }

.hero__proof {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.75rem;
  margin-block-start: 2.5rem;
  color: var(--fg-dim);
  font-size: 0.875rem;
}
.hero__proof li { list-style: none; display: flex; align-items: center; gap: 0.45rem; }
.hero__proof li::before { content: "✓"; color: var(--ok); font-weight: 700; }

/* -------------------------------------------------------- target capture -- */

.target-form {
  margin-block-start: 2.5rem;
  max-width: 34rem;
}

.target-field {
  display: flex;
  gap: 0.5rem;
  padding: 0.45rem;
  background: var(--surface);
  border: 1px solid var(--line-bright);
  border-radius: 100px;
  box-shadow: var(--shadow-lg);
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}

.target-field:focus-within {
  border-color: var(--accent);
  box-shadow: var(--shadow-lg), 0 0 0 4px color-mix(in oklab, var(--accent) 18%, transparent);
}

/* `input[type="text"]` in the forms section below matches this element too, at the
   same specificity (0,1,1) and later in the file - so it would win, and the hero
   input would render as a bordered box sitting inside the pill. Qualifying by
   attribute here takes this to (0,2,1) so the intent survives regardless of source
   order. Same reason the padding is set as a shorthand: the generic rule sets
   `padding`, and a bare `padding-inline` would lose the block axis to it. */
.target-field input[type="text"] {
  flex: 1;
  min-inline-size: 0;
  inline-size: auto;
  border: none;
  background: none;
  border-radius: 0;
  color: var(--fg);
  font-family: var(--font-mono);
  font-size: 0.95rem;
  padding: 0.55rem 1rem;
}
.target-field input[type="text"]:focus { outline: none; box-shadow: none; border-color: transparent; }
.target-field input[type="text"]::placeholder { color: var(--fg-dim); }

@media (max-width: 520px) {
  .target-field { flex-direction: column; border-radius: var(--radius); }
  .target-field input[type="text"] { padding: 0.7rem 0.9rem; }
  .target-field .btn { width: 100%; }
}

.target-form__hint { margin-block-start: 0.85rem; font-size: 0.85rem; color: var(--fg-dim); }

/* ----------------------------------------------------------------- steps -- */

.steps { counter-reset: step; display: grid; gap: 0; }

.step {
  counter-increment: step;
  display: grid;
  grid-template-columns: 3rem 1fr;
  gap: 1.25rem;
  padding-block: 1.6rem;
  border-block-end: 1px solid var(--line);
  align-items: start;
}
.step:last-child { border-block-end: none; }

.step::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent);
  padding-block-start: 0.2rem;
}

.step h3 { font-size: 1.075rem; margin-block-end: 0.35rem; }
.step p { color: var(--fg-muted); max-width: 62ch; }

/* --------------------------------------------------------------- pricing -- */

.price-card { display: flex; flex-direction: column; }
.price-card--featured { border-color: color-mix(in oklab, var(--accent) 45%, var(--line)); }

.price-card__amount {
  font-size: var(--step-3);
  font-weight: 660;
  letter-spacing: -0.035em;
  line-height: 1;
  margin-block: 0.75rem 0.25rem;
}
.price-card__amount small { font-size: 0.9rem; font-weight: 500; color: var(--fg-dim); letter-spacing: 0; }

.price-card ul { list-style: none; padding: 0; margin-block: 1.25rem; display: grid; gap: 0.6rem; }
.price-card li { display: flex; gap: 0.6rem; align-items: flex-start; font-size: 0.925rem; color: var(--fg-muted); }
.price-card li::before { content: "→"; color: var(--accent); flex: none; }
.price-card .btn { margin-block-start: auto; }

.badge {
  display: inline-block;
  padding: 0.2em 0.7em;
  border-radius: 100px;
  font-size: 0.7rem;
  font-weight: 660;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  background: color-mix(in oklab, var(--accent) 18%, transparent);
  color: var(--accent-bright);
  border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent);
}

/* ---------------------------------------------------------------- tables -- */

.table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); }

table.data { width: 100%; border-collapse: collapse; font-size: 0.925rem; }
table.data th, table.data td { text-align: left; padding: 0.8rem 1rem; border-block-end: 1px solid var(--line); }
table.data thead th {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--fg-dim);
  font-weight: 620;
  background: var(--bg-sunken);
}
table.data tbody tr:last-child td { border-block-end: none; }
table.data tbody tr { transition: background-color 0.15s var(--ease); }
table.data tbody tr:hover { background: var(--surface-hover); }

/* ------------------------------------------------------------------ pill -- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25em 0.7em;
  border-radius: 100px;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  white-space: nowrap;
}
.pill::before { content: ""; inline-size: 6px; block-size: 6px; border-radius: 50%; background: currentColor; }

.pill--ok { color: var(--ok); background: color-mix(in oklab, var(--ok) 14%, transparent); border-color: color-mix(in oklab, var(--ok) 30%, transparent); }
.pill--warn { color: var(--warn); background: color-mix(in oklab, var(--warn) 14%, transparent); border-color: color-mix(in oklab, var(--warn) 30%, transparent); }
.pill--danger { color: var(--danger); background: color-mix(in oklab, var(--danger) 14%, transparent); border-color: color-mix(in oklab, var(--danger) 30%, transparent); }
.pill--info { color: var(--accent-bright); background: color-mix(in oklab, var(--accent) 14%, transparent); border-color: color-mix(in oklab, var(--accent) 30%, transparent); }
.pill--neutral { color: var(--fg-dim); background: color-mix(in oklab, var(--fg) 8%, transparent); border-color: var(--line); }

/* ----------------------------------------------------------------- forms -- */

.field { display: block; margin-block-end: 1.35rem; }
.field > .field__label { display: block; font-weight: 600; font-size: 0.9rem; margin-block-end: 0.4rem; }
.field__hint { display: block; font-size: 0.825rem; color: var(--fg-dim); margin-block-start: 0.4rem; }

.input, input[type="text"], input[type="email"], input[type="password"],
input[type="url"], input[type="number"], select, textarea {
  inline-size: 100%;
  padding: 0.65rem 0.85rem;
  background: var(--bg-sunken);
  border: 1px solid var(--line-bright);
  border-radius: var(--radius-sm);
  color: var(--fg);
  font: inherit;
  font-size: 0.95rem;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

textarea { min-block-size: 6rem; resize: vertical; font-family: var(--font-mono); font-size: 0.9rem; }

.input:focus, input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 16%, transparent);
}

.choice {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color 0.15s var(--ease), background-color 0.15s var(--ease);
}
.choice:hover { background: var(--surface-hover); }
.choice input { margin-block-start: 0.25rem; accent-color: var(--accent); flex: none; inline-size: auto; }
.choice:has(input:checked) { border-color: var(--accent); background: color-mix(in oklab, var(--accent) 7%, transparent); }
.choice__title { font-weight: 600; display: block; }
.choice__desc { font-size: 0.85rem; color: var(--fg-muted); }

/* Preview-deployment banner (app/web/branding.preview_notice). Above the topbar and
   in flow rather than fixed, so it pushes the page down instead of covering anything -
   a notice that hides the nav is a notice somebody removes for the wrong reason.
   Not dismissible: the enforced script-src 'none' rules out a close button, and a
   standing condition should not be dismissible anyway. */
.preview-banner {
  background: color-mix(in oklab, var(--warn) 14%, var(--bg-sunken));
  border-block-end: 1px solid color-mix(in oklab, var(--warn) 45%, transparent);
  /* The signal lives in the text, not the fill. Mixing an amber into a near-black
     background at any tasteful percentage returns something all but neutral - measured
     at 16% it came back a=-0.004 b=0.008, i.e. grey - so the bar read as chrome rather
     than as a notice.

     Mixed toward --fg rather than used raw. Bare var(--warn) measured 9.1:1 on the dark
     palette but only 3.3:1 on the light one, which fails AA for 14px text; --fg is the
     near-black or near-white that palette already calls readable, so mixing toward it
     darkens the amber on light and lightens it on dark from a single declaration.
     Measured after: 11.2:1 dark, 5.9:1 light, both past AA for 14px text. */
  color: color-mix(in oklab, var(--warn) 62%, var(--fg));
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.45;
  padding-block: 0.6rem;
  text-align: center;
}

.flash {
  padding: 0.85rem 1.1rem;
  border-radius: var(--radius-sm);
  background: color-mix(in oklab, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
  color: var(--fg);
  margin-block-end: 1rem;
  font-size: 0.925rem;
}

/* Honeypot field (app/web/antibot.py). Hidden from people, present for bots.
   Not display:none - some bots skip those; this is off-screen but focusable-safe
   via tabindex/aria in the markup. */
.hp-field {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

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

.footer {
  border-block-start: 1px solid var(--line);
  background: var(--bg-sunken);
  padding-block: 3rem 2rem;
  margin-block-start: auto;
  font-size: 0.9rem;
}
.footer__grid { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); }
.footer h4 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--fg-dim); margin-block-end: 0.85rem; }
.footer ul { list-style: none; padding: 0; display: grid; gap: 0.5rem; }
.footer a { color: var(--fg-muted); text-decoration: none; }
.footer a:hover { color: var(--fg); }
.footer__base {
  margin-block-start: 2.5rem;
  padding-block-start: 1.5rem;
  border-block-start: 1px solid var(--line);
  color: var(--fg-dim);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
}

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

/* Scroll-driven reveal. animation-timeline: view() means the browser drives this
   off the element's own position in the scrollport - no IntersectionObserver, no
   script. Guarded by @supports so browsers without it never apply the opening
   `opacity: 0`, which would otherwise leave the page permanently blank. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      animation: reveal-in linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 26%;
    }
  }
}

@keyframes reveal-in {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: none; }
}

/* The scanline that runs down the hero divider - the one piece of ambient motion
   on the page, and it stops entirely under reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .scanline {
    position: relative;
    overflow: hidden;
    block-size: 1px;
    background: var(--line);
  }
  .scanline::after {
    content: "";
    position: absolute;
    inset-block: 0;
    inline-size: 33%;
    background: linear-gradient(90deg, transparent, var(--accent), transparent);
    animation: scan 6s var(--ease) infinite;
  }
}

@keyframes scan {
  from { transform: translateX(-120%); }
  to { transform: translateX(400%); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  @view-transition { navigation: none; }
}

/* ----------------------------------------------------------------- utils -- */

.visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.text-center { text-align: center; }
.mx-auto { margin-inline: auto; }
.full { inline-size: 100%; }
