/* MasterBot — one shared stylesheet for the marketing site, the account
   portal and the admin console. No build step, no CDN dependency, system
   font stack only. Dark techno-minimalist palette lifted from the design
   reference (tarball at /tmp/mbsite) — retheme only: .card, .status,
   .row-form, .stat-grid, .quota-bar, .reveal etc. keep their original
   structure so every page that already uses them inherits the new look
   with no markup change. */

:root {
  /* ---- brand, sampled from the logo ------------------------------------
   *
   * These are not eyeballed. The two logo PNGs were decoded and their pixels
   * histogrammed by hue/saturation/lightness band; the values below are the
   * most-used colours in the mark's saturated mid-range, not the anti-aliased
   * edges around them:
   *
   *   red   core  #990000 .. #a00000   hsl(0, 100%, 30-31%)
   *   red   peak  #fd1a2a              hsl(358, 98%, 55%)
   *   blue  core  #002882              hsl(222, 100%, 25%)
   *   blue  peak  #0181fd / #005efd    hsl(210-218, 100%, 50%)
   *
   * The mark runs red at the upper left into blue at the lower right, which
   * is why --brand-gradient is 135deg and not something arbitrary.
   * -------------------------------------------------------------------- */
  --brand-red: #fd1a2a;
  --brand-red-deep: #990000;
  --brand-blue: #0d7bff;
  --brand-blue-soft: #5aa4ff;
  --brand-blue-deep: #002882;
  --brand-gradient: linear-gradient(135deg, var(--brand-red), var(--brand-blue));

  /* ---- which half of the logo drives the UI ----------------------------
   *
   * Blue is the interactive accent — buttons, links, focus rings, active
   * states — and red is reserved for brand moments (the wordmark, the hero
   * gradient, the "most picked" badge) and for danger.
   *
   * That split is deliberate and worth not "fixing" later: red is the one
   * hue a UI cannot spend freely, because it already means destroy/failed
   * everywhere else on this site — revoke, reset device, an error status, an
   * exhausted quota bar. Making it the button colour too would leave a
   * primary CTA and a delete button reading identically, which is exactly
   * the confusion colour is supposed to prevent. The logo still sets the
   * palette; red just does not get to be the click target.
   * -------------------------------------------------------------------- */
  --accent: var(--brand-blue);
  --accent-soft: var(--brand-blue-soft);
  /* Text ON a filled accent. The old green sat at l=45% and took near-black
     text; this blue is darker, so the same slot has to flip to near-white or
     every primary button loses its label. */
  --accent-fg: #f5f9ff;
  --accent-hover: var(--accent-soft);
  /* Channels only, for the rgba() tints throughout this file. Every accent
     tint reads this rather than hardcoding the colour, so a future retheme is
     these few lines and nothing else. */
  --accent-rgb: 13, 123, 255;
  --brand-red-rgb: 253, 26, 42;

  --line: rgba(255, 255, 255, 0.08);

  /* ---- dark base ----
     Tinted toward the logo's navy rather than the previous green-black, so
     the panels sit under the mark instead of arguing with it. */
  --void: #05070d;
  --panel: #0a0d16;
  --panel-2: #0e1220;
  --bg: var(--void);
  --fg: #f2f5fb;
  --muted: #93a0bb;
  --muted-2: #5f6a83;
  --border: var(--line);
  --border-strong: rgba(255, 255, 255, 0.16);
  --card-bg: var(--panel);

  /* ---- status colours ----
     Error is the logo's red, so a failure still reads as brand rather than a
     foreign hue. Success stays green: it is the one signal that must never be
     mistaken for either brand colour, and a blue "success" next to a blue
     button would be exactly that mistake. */
  --error-bg: rgba(var(--brand-red-rgb), 0.12);
  --error-fg: #ff8a93;
  --error-border: rgba(var(--brand-red-rgb), 0.4);
  --success-bg: rgba(52, 211, 153, 0.1);
  --success-fg: #5ee0ab;
  --success-border: rgba(52, 211, 153, 0.3);
  --info-bg: rgba(148, 163, 184, 0.1);
  --info-fg: #cbd5e1;
  --info-border: rgba(148, 163, 184, 0.28);

  /* Self-hosted (public/fonts/, latin subset only — en/pt is all lang.js
     serves, so latin-ext's Vietnamese/Polish/Croatian glyphs are dead
     weight here). System stack stays as the fallback/FOUT face so layout
     doesn't jump before the woff2 lands. */
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Space Grotesk", var(--font);
  --font-mono: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;

  /* ---- elevation / radius / focus — one scale each, shared by every
     surface and button state (step 5) ---- */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-pill: 999px;
  --elevate-1: 0 1px 2px rgba(0, 0, 0, 0.4);
  --elevate-2: 0 8px 24px rgba(0, 0, 0, 0.45);
  --elevate-3: 0 16px 48px rgba(0, 0, 0, 0.55);
  /* --accent already clears 3:1 against --void (~5.1:1) for a non-text
     focus indicator per WCAG 2.1 SC 1.4.11 — reused rather than a new hue. */
  --focus-ring: 0 0 0 2px var(--void), 0 0 0 4px var(--accent);
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/inter-400.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";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/inter-600.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: "Space Grotesk";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/spacegrotesk-700.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;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  background: var(--bg);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
}

::selection {
  background: var(--accent);
  color: var(--accent-fg);
}

section[id] {
  scroll-margin-top: 5rem;
}

/* ------------------------------------------------------------------ */
/*  Micro-label convention: font-mono, 11px, uppercase, wide tracking  */
/* ------------------------------------------------------------------ */
.micro-label {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.3em;
  color: var(--accent);
}

/* big typographic watermark used behind marketing section headers */
.text-outline {
  color: transparent;
  -webkit-text-stroke: 1px rgba(var(--accent-rgb), 0.13);
  font-family: var(--font-mono);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  user-select: none;
  pointer-events: none;
}

/* ------------------------------------------------------------------ */
/*  Reveal-on-scroll (scroll-reveal.js adds .is-visible)               */
/* ------------------------------------------------------------------ */
[data-reveal] {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

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

@media (prefers-reduced-motion: reduce) {
  [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* The mark ships with a real alpha channel, so it needs no frame: the border,
   radius and tint that used to live here existed only to disguise the opaque
   near-black plate baked into the previous source file, which did not match
   the page background. Without a plate to hide, the glow reads correctly
   straight onto the page and the mark can be drawn larger.
   `contain` rather than `cover` so a transparent mark is never cropped. */
.brand-logo {
  display: block;
  height: 48px;
  width: 48px;
  object-fit: contain;
}

/* language toggle — shared by every page */
.lang-toggle {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: 999px;
  padding: 2px;
  font-family: var(--font-mono);
  font-size: 11px;
}

.lang-toggle button {
  margin: 0;
  padding: 0.35rem 0.65rem;
  min-height: 0;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 11px;
  cursor: pointer;
}

.lang-toggle button.is-active {
  background: var(--accent);
  color: var(--accent-fg);
  font-weight: 700;
}

/* ------------------------------------------------------------------ */
/*  Page shell (portal / admin)                                        */
/* ------------------------------------------------------------------ */
.page {
  max-width: 640px;
  margin: 0 auto;
  padding: 2.5rem 1.5rem 4rem;
}

.page.wide {
  max-width: 960px;
}

h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.6rem;
  margin: 0 0 0.5rem;
  letter-spacing: -0.02em;
}

h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  margin: 2rem 0 0.75rem;
  letter-spacing: -0.01em;
}

p.lede {
  color: var(--muted);
  margin-top: 0;
}

.card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 1.25rem 1.5rem;
  margin-bottom: 1.5rem;
  box-shadow: var(--elevate-1);
}

/* One focus-visible ring for every interactive element that isn't already
   covered by .btn's own (input/select keep their existing outline above —
   this catches links, the nav toggle, and anything else with a tabindex). */
a:focus-visible,
.nav-toggle:focus-visible,
[tabindex]:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

label {
  display: block;
  font-weight: 600;
  font-size: 0.85rem;
  margin: 1rem 0 0.35rem;
  color: var(--fg);
}

label:first-of-type {
  margin-top: 0;
}

input[type="email"],
input[type="password"],
input[type="text"],
input[type="number"],
select {
  width: 100%;
  padding: 0.6rem 0.75rem;
  font-size: 1rem;
  font-family: inherit;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--void);
  color: var(--fg);
}

input:focus,
select:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}

.field-hint {
  color: var(--muted);
  font-size: 0.82rem;
  margin-top: 0.25rem;
}

button {
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  padding: 0.65rem 1.2rem;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--accent-fg);
  cursor: pointer;
  margin-top: 1.1rem;
  transition: box-shadow 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}

button:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  box-shadow: 0 0 24px rgba(var(--accent-rgb), 0.35);
}

button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
}

/* Portal/admin forms use bare <button> (pre-dating .btn from the marketing
   site), not <a>/[tabindex] — the catch-all focus-visible rule above never
   matched them, so the browser default outline showed on every login/
   register/reset/account/admin submit button. Same token, same treatment
   as .btn:focus-visible. */
button:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

button.secondary {
  background: transparent;
  color: var(--fg);
  border-color: var(--border-strong);
}

button.secondary:hover {
  background: rgba(255, 255, 255, 0.05);
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: none;
}

button.danger {
  background: transparent;
  color: var(--error-fg);
  border-color: var(--error-border);
}

button.danger:hover {
  background: var(--error-bg);
  border-color: var(--error-fg);
  box-shadow: none;
}

a {
  color: var(--accent);
}

a:hover {
  color: var(--accent-soft);
}

.status {
  margin-top: 1rem;
  padding: 0.7rem 0.9rem;
  border-radius: 8px;
  border: 1px solid transparent;
  font-size: 0.92rem;
  display: none;
}

.status.visible {
  display: block;
}

.status.info {
  background: var(--info-bg);
  color: var(--info-fg);
  border-color: var(--info-border);
}

.status.success {
  background: var(--success-bg);
  color: var(--success-fg);
  border-color: var(--success-border);
}

.status.error {
  background: var(--error-bg);
  color: var(--error-fg);
  border-color: var(--error-border);
}

/* The releases table's cross-branch conflict row (§25 S) reuses .status.error
   inside a <td> — but the base .status rule is display:none (it is meant for
   the form status banners, which .status.visible un-hides). A table cell must
   be a table cell again, with the padding the banners get from this rule. */
td.status {
  display: table-cell;
  padding: 0.7rem 0.9rem;
}

/* The TestBot drift line goes amber only while there is something to act on
   (TestBot behind DG, or main ahead of it). Same amber as the quota warning;
   red stays reserved for an actual failure. */
.drift-warn {
  color: #f2b84d;
  font-weight: 600;
}

.gap-note {
  background: var(--info-bg);
  color: var(--info-fg);
  border: 1px solid var(--info-border);
  border-radius: 8px;
  padding: 0.75rem 0.9rem;
  font-size: 0.88rem;
  margin: 0.75rem 0 1.25rem;
}

/* Cloudflare renders the Turnstile widget itself; this only reserves its
   space so the form does not jump when the challenge finishes loading. */
.cf-turnstile {
  margin-top: 1rem;
  min-height: 65px;
}

.links {
  margin-top: 1.5rem;
  font-size: 0.92rem;
  color: var(--muted);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

/* Admin's tables run 6-9 columns wide — the *-wrap divs around each table
   already exist in the markup for this: scope the horizontal scroll to the
   table itself at narrow widths instead of letting it blow out the page.
   The suffix selector covers every section's table wrapper, so new sections
   don't need adding here. */
[id$="-wrap"] {
  overflow-x: auto;
}

th, td {
  text-align: left;
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

th {
  color: var(--muted);
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-family: var(--font-mono);
}

.row-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

.row-form input,
.row-form select {
  width: auto;
  min-width: 5.5rem;
  padding: 0.4rem 0.55rem;
}

.row-form button {
  margin-top: 0;
  padding: 0.45rem 0.9rem;
  font-size: 0.85rem;
}

code {
  font-family: var(--font-mono);
  font-size: 0.85em;
  background: rgba(255, 255, 255, 0.06);
  color: var(--accent-soft);
  padding: 0.15em 0.4em;
  border-radius: 4px;
}

.muted {
  color: var(--muted);
}

hr.divider {
  border: none;
  border-top: 1px solid var(--border);
  margin: 2rem 0;
}

/* ── One-time secrets (reveal.js) ──────────────────────────────────────────
   Subscription keys and invite codes are shown exactly once and are
   unrecoverable afterwards, so the panel is deliberately loud — a warning
   colour and a wide monospace field, not something low-contrast that could
   blend into the retheme. Do not soften this. */
.reveal {
  margin-top: 1rem;
  padding: 0.9rem 1rem;
  border: 1px solid #f2b84d;
  border-left: 4px solid #f2b84d;
  border-radius: 6px;
  background: rgba(242, 184, 77, 0.12);
  color: #ffd98a;
}

.reveal-warning {
  margin: 0.35rem 0 0.75rem;
  font-size: 0.9rem;
  font-weight: 600;
}

.reveal-row {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.4rem;
}

.reveal-value {
  flex: 1;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  color: #fff3d6;
}

/* ── Active-time quota bar (account.js) ────────────────────────────────────
   Banded rather than a single gradient: the point is to read "how worried
   should I be" at a glance. */
.quota-bar {
  height: 10px;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
  margin: 0.4rem 0 0.25rem;
}

.quota-fill {
  height: 100%;
  border-radius: 5px;
  transition: width 0.3s ease;
}

.quota-fill.ok {
  background: var(--accent);
}

.quota-fill.warn {
  background: #f2b84d;
}

.quota-fill.low {
  /* The logo's red rather than a generic alarm red — this is the one place
     brand and danger genuinely coincide, and an almost-empty allowance is
     supposed to be alarming. */
  background: var(--brand-red);
}

.quota-meta {
  font-size: 0.85rem;
  color: var(--muted);
}

/* ── Admin stat tiles ───────────────────────────────────────────────── */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.stat-tile {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.9rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.stat-value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
  color: var(--accent);
  font-family: var(--font-mono);
}

.stat-label {
  font-size: 0.75rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* ── All licences: Modules column (admin.js's moduleCell) ──────────────
   An extra module ("+cave") is a manual override the plan does not grant, so
   it reads in the same amber as a warning line; a missing one ("−lure", a
   revoke) is muted — informational, not alarming. The superscript ? marks a
   grant that predates grant tracking and carries its own tooltip. */
.module-extra { color: #fde68a; }
.module-missing { color: var(--muted-2); }
.module-extra-untracked {
  cursor: help;
  font-size: 0.7em;
  vertical-align: super;
  margin-left: 1px;
}

/* ── Admin console pages (the sidebar rail + [data-page]) ──────────────
   The console is one document holding seven [data-page] sections and a rail
   of "#/<id>" links; admin.js shows one section at a time by flipping that
   section's own `hidden` attribute. There is deliberately no display rule
   for [data-page] here: `hidden` is a display:none that any rule of ours
   would outrank, and the sections carry no layout of their own to win back. */
.admin-nav {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 1.25rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
  /* Seven peers on a phone get a sideways-scrolling tab row rather than a
     stack: the rail is chrome, and three wrapped lines of chrome push the
     first table below the fold. Scoping the scroll to the rail — never to
     the page — is also what keeps 375px free of a sideways body scroll,
     the same trick as the [id$="-wrap"] table wrappers above. */
  overflow-x: auto;
  scrollbar-width: none;
}

.admin-nav::-webkit-scrollbar {
  display: none;
}

/* The padding lives on the links and never as an outer margin on them: a
   scrolling flex container drops a start-edge margin, which would silently
   clip the first tab's label. */
.admin-nav a {
  flex: 0 0 auto;
  padding: 0.35rem 0.7rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
}

.admin-nav a:hover {
  color: var(--fg);
  background: var(--card-bg);
}

/* The border is transparent at rest rather than absent, so marking a tab
   current cannot change its width and shift every tab to its right. */
.admin-nav a.is-active {
  color: var(--fg);
  background: rgba(var(--accent-rgb), 0.12);
  border-color: rgba(var(--accent-rgb), 0.4);
}

/* On a phone the sub-links sit in the row like any other tab. */
.admin-nav .admin-nav-sub {
  display: contents;
}

/* Side menu on a wide screen: the same anchors, stacked in a left column.
   #admin-content becomes a two-column grid; the nav spans every row of the
   left column and everything else (secret panel, status line, the pages)
   stays in the right one. Below 860px none of this applies and the rail above
   remains a sideways-scrolling tab row. */
@media (min-width: 860px) {
  #admin-content {
    display: grid;
    grid-template-columns: 13rem minmax(0, 1fr);
    column-gap: 1.5rem;
    align-items: start;
  }

  #admin-content > * {
    grid-column: 2;
  }

  #admin-content > .admin-nav {
    grid-column: 1;
    grid-row: 1 / span 20;
    position: sticky;
    top: 1rem;
    flex-direction: column;
    gap: 0.15rem;
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: 0;
    overflow-x: visible;
  }

  .admin-nav .admin-nav-sub {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding-left: 0.9rem;
  }
}

/* ==================================================================== */
/*  Marketing site (index.html)                                         */
/* ==================================================================== */

.container {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* ---- navbar ---- */
.navbar {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  border-bottom: 1px solid transparent;
  background: transparent;
  transition: background 0.3s ease, border-color 0.3s ease;
}

.navbar.is-scrolled {
  border-bottom-color: var(--line);
  background: rgba(5, 7, 13, 0.88);
  backdrop-filter: blur(16px);
}

.navbar-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 1.5rem;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

/* Unscoped on purpose: the navbar and the footer render the same brand
   lockup from the same markup (index.html statically, shell.js at runtime).
   Scoping these to .navbar left the footer copy falling back to the plain
   link style — accent-blue and underlined, with the wordmark losing its
   mono/tracking treatment and .hl losing the brand red. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  color: var(--fg);
}

.brand .wordmark {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.18em;
}

/* The highlighted half of the wordmark is a brand moment, not a control, so
   it takes the logo's red rather than the interactive blue. */
.brand .wordmark .hl {
  color: var(--brand-red);
}

.nav-links {
  display: none;
  align-items: center;
  gap: 0.25rem;
}

@media (min-width: 1024px) {
  .nav-links { display: flex; }
}

.nav-links a {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.75rem;
  border-radius: 8px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.9rem;
  transition: background 0.2s ease, color 0.2s ease;
}

.nav-links a:hover {
  background: rgba(255, 255, 255, 0.05);
  color: var(--fg);
}

.nav-links a .idx {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--muted-2);
}

.navbar-actions {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

/* sign-in / get-access buttons: hidden on the smallest screens, shown from
   sm up (they also appear, full-width, inside .mobile-nav below). */
.navbar-actions .btn-signin,
.navbar-actions .btn-getaccess {
  display: none;
}

@media (min-width: 640px) {
  .navbar-actions .btn-signin,
  .navbar-actions .btn-getaccess {
    display: inline-flex;
  }
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.55rem 1.1rem;
  border-radius: var(--radius-pill);
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: all 0.2s ease;
}

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

.btn:disabled,
.btn[disabled] {
  cursor: not-allowed;
  opacity: 0.45;
  box-shadow: none;
}

.btn-primary {
  background: var(--accent);
  color: var(--accent-fg);
  box-shadow: var(--elevate-1);
}

.btn-primary:hover {
  box-shadow: 0 0 28px rgba(var(--accent-rgb), 0.4);
  color: var(--accent-fg);
}

.btn-ghost {
  background: var(--panel);
  border-color: var(--line);
  color: var(--fg);
}

.btn-ghost:hover {
  border-color: rgba(var(--accent-rgb), 0.4);
  color: var(--accent);
}

/* Secondary: one step down from primary — filled panel, no gradient/glow,
   for the second-priority action next to a primary CTA. */
.btn-secondary {
  background: var(--panel-2);
  border-color: var(--border-strong);
  color: var(--fg);
}

.btn-secondary:hover {
  border-color: rgba(var(--accent-rgb), 0.5);
  color: var(--fg);
}

/* Danger reuses the brand red already reserved for destroy/failed states
   (see the --accent comment above) rather than inventing a second red. */
.btn-danger {
  background: transparent;
  border-color: var(--error-border);
  color: var(--error-fg);
}

.btn-danger:hover {
  background: var(--error-bg);
  border-color: var(--brand-red);
  color: var(--error-fg);
}

.btn-lg {
  padding: 0.85rem 1.7rem;
  font-size: 1rem;
}

.nav-toggle {
  display: grid;
  place-items: center;
  height: 40px;
  width: 40px;
  padding: 0;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--fg);
  cursor: pointer;
}

@media (min-width: 1024px) {
  .nav-toggle { display: none; }
}

.nav-toggle svg {
  width: 18px;
  height: 18px;
}

.mobile-nav {
  display: none;
  border-bottom: 1px solid var(--line);
  background: rgba(5, 7, 13, 0.96);
  backdrop-filter: blur(16px);
}

.mobile-nav.is-open {
  display: block;
}

.mobile-nav-inner {
  padding: 1rem 1.5rem 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.mobile-nav a {
  display: block;
  padding: 0.75rem 0.5rem;
  border-radius: 8px;
  color: var(--fg);
  text-decoration: none;
  font-size: 1rem;
}

.mobile-nav a:hover {
  background: rgba(255, 255, 255, 0.05);
}

/* ---- hero ---- */
.hero {
  position: relative;
  padding: 8.5rem 0 3rem;
  overflow: hidden;
}

.hero-bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}

/* Duotone wash behind the hero, arranged the way the mark is: red weighted
   up and to the left, blue to the right. Both sit far below any contrast
   threshold — this is atmosphere, nothing is read against it. */
.hero-bg .glow {
  position: absolute;
  border-radius: 999px;
  filter: blur(120px);
  background: rgba(var(--brand-red-rgb), 0.07);
}

.hero-bg .glow-1 { top: -160px; left: 33%; width: 820px; height: 480px; transform: translateX(-50%); }
.hero-bg .glow-2 { top: 33%; left: -128px; width: 288px; height: 288px; background: rgba(var(--brand-red-rgb), 0.05); }
.hero-bg .glow-3 { top: 33%; right: -128px; width: 288px; height: 288px; background: rgba(var(--accent-rgb), 0.04); }

.hero-watermark,
.section-watermark {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  pointer-events: none;
}

.hero-watermark span,
.section-watermark span {
  font-size: 13vw;
}

@media (min-width: 1024px) {
  .hero-watermark,
  .section-watermark {
    display: flex;
  }
}

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  border: 1px solid rgba(var(--accent-rgb), 0.25);
  background: rgba(var(--accent-rgb), 0.06);
  border-radius: 999px;
  padding: 0.4rem 1rem;
}

.pulse-dot {
  height: 8px;
  width: 8px;
  border-radius: 999px;
  background: var(--accent);
  animation: pulse-dot 2.2s ease-out infinite;
}

@keyframes pulse-dot {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.55); }
  70% { box-shadow: 0 0 0 7px rgba(var(--accent-rgb), 0); }
}

.hero-grid {
  display: grid;
  gap: 2.5rem;
  margin-top: 2rem;
  position: relative;
}

@media (min-width: 1024px) {
  .hero-grid { grid-template-columns: 1.05fr 0.95fr; align-items: center; gap: 3.5rem; }
}

.hero-title {
  font-size: clamp(2.4rem, 6vw, 4.2rem);
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1.05;
  margin: 1.5rem 0 0;
}

.hero-title .gradient {
  /* The logo's own sweep, flattened from its 135° diagonal to a horizontal
     run for text: red in, blue out, red again so the animated pan has
     somewhere to return to without a seam. */
  background: linear-gradient(90deg, var(--brand-red), var(--brand-blue), var(--brand-red));
  background-size: 220% auto;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: gradient-pan 6s linear infinite;
}

@keyframes gradient-pan {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

.hero-subtitle {
  margin: 1.25rem 0 0;
  max-width: 34rem;
  color: var(--muted);
  font-size: 1.05rem;
  line-height: 1.65;
}

.hero-ctas {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-top: 2rem;
}

@media (min-width: 640px) {
  .hero-ctas { flex-direction: row; align-items: center; }
}

.hero-meta {
  margin-top: 1rem;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  color: var(--muted-2);
}

/* live terminal */
.terminal {
  position: relative;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: rgba(10, 14, 12, 0.9);
  box-shadow: 0 24px 80px -24px rgba(var(--accent-rgb), 0.18);
  overflow: hidden;
}

.terminal-chrome {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--line);
  padding: 0.65rem 1rem;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

.terminal-dots {
  display: flex;
  gap: 0.35rem;
}

.terminal-dots span {
  height: 10px;
  width: 10px;
  border-radius: 999px;
  background: #3f3f46;
}

.terminal-dots span:last-child {
  background: rgba(var(--accent-rgb), 0.7);
}

.terminal-live {
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--accent);
}

.terminal-body {
  position: relative;
  height: 15rem;
  overflow: hidden;
  padding: 0.75rem 1rem;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.5rem;
}

.terminal-body p {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Log line colours. The mint/magenta pair these used to carry was a generic
   terminal palette and, after the retheme, the only green and the only purple
   left on the page — sitting in the hero, where they pulled hardest against
   the mark. OK and EVT now read in the two brand hues; amber stays on the
   warn line because a warning that is not amber is a warning nobody clocks. */
.term-ok { color: var(--brand-blue-soft); }
.term-default { color: #f4f4f5; }
.term-warn { color: #fde68a; }
.term-event { color: #ff7b85; }
.term-muted { color: #71717a; }

.caret {
  display: inline-block;
  height: 1rem;
  width: 0.5rem;
  margin-left: 2px;
  background: var(--accent);
  vertical-align: -2px;
  animation: caret-blink 1.1s steps(1) infinite;
}

@keyframes caret-blink {
  0%, 45% { opacity: 1; }
  50%, 95% { opacity: 0; }
}

.terminal-status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-top: 1px solid var(--line);
  padding: 0.5rem 1rem;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--muted);
}

.terminal-status .running {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.terminal-status .running .pulse-dot {
  height: 6px;
  width: 6px;
}

/* hero stats */
.hero-stats {
  margin-top: 2.5rem;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
}

@media (min-width: 640px) {
  .hero-stats { grid-template-columns: repeat(4, 1fr); }
}

.hero-stats .stat {
  background: var(--panel);
  padding: 1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.hero-stats .stat-num {
  font-family: var(--font-mono);
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--accent);
}

.hero-stats .stat-label {
  margin-top: 0.25rem;
  font-size: 11px;
  text-align: center;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
}

/* ---- marquee ---- */
.marquee {
  position: relative;
  overflow: hidden;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: rgba(10, 14, 12, 0.5);
  padding: 1rem 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, black 12%, black 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, black 12%, black 88%, transparent);
}

.marquee-track {
  display: flex;
  width: max-content;
  gap: 2.5rem;
  align-items: center;
  animation: marquee-x 36s linear infinite;
}

.marquee:hover .marquee-track {
  animation-play-state: paused;
}

@keyframes marquee-x {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

.marquee-item {
  display: flex;
  align-items: center;
  gap: 2.5rem;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.28em;
  color: var(--muted);
  white-space: nowrap;
}

.marquee-item .dot {
  height: 4px;
  width: 4px;
  transform: rotate(45deg);
  background: rgba(var(--accent-rgb), 0.5);
}

/* ---- generic marketing section shell ---- */
.section {
  padding: 5rem 0;
  position: relative;
}

.section-alt {
  border-top: 1px solid var(--line);
  background: rgba(10, 14, 12, 0.3);
}

.section-header {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

@media (min-width: 1024px) {
  .section-header { flex-direction: row; align-items: flex-end; justify-content: space-between; }
}

.section-eyebrow {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.section-eyebrow .rule {
  height: 1px;
  width: 2rem;
  background: rgba(var(--accent-rgb), 0.6);
}

.section-title {
  margin: 0.75rem 0 0;
  font-size: clamp(1.75rem, 4vw, 2.25rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--fg);
}

.section-desc {
  max-width: 24rem;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.6;
}

@media (min-width: 1024px) {
  .section-desc { text-align: right; }
}

.section-header-center {
  max-width: 40rem;
  margin: 0 auto;
  text-align: center;
}

.section-header-center .section-eyebrow {
  justify-content: center;
}

.section-header-center .section-desc {
  max-width: none;
  text-align: center;
  margin-top: 1rem;
}

/* ---- bento (features) ---- */
.bento {
  margin-top: 2rem;
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 640px) {
  .bento { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .bento { grid-template-columns: repeat(3, 1fr); }
  .bento .span-2 { grid-column: span 2; }
}

.bento-card {
  position: relative;
  overflow: hidden;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 1.5rem;
  min-height: 190px;
  transition: border-color 0.35s ease, background-color 0.35s ease;
}

.bento-card:hover {
  border-color: rgba(var(--accent-rgb), 0.35);
  background-color: rgba(var(--accent-rgb), 0.03);
}

.tag-pill {
  display: inline-block;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--void);
  padding: 0.25rem 0.65rem;
  font-family: var(--font-mono);
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
}

.bento-card h3 {
  margin: 1rem 0 0;
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--fg);
}

.bento-card p {
  margin: 0.6rem 0 0;
  max-width: 26rem;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--muted);
}

/* ---- modules ---- */
.filter-pills {
  margin-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.filter-pill {
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted);
  padding: 0.4rem 0.9rem;
  font-size: 13px;
  cursor: pointer;
  transition: all 0.2s ease;
}

.filter-pill:hover {
  border-color: #52525b;
  color: var(--fg);
}

.filter-pill.is-active {
  border-color: rgba(var(--accent-rgb), 0.5);
  background: rgba(var(--accent-rgb), 0.1);
  color: var(--accent);
  font-weight: 500;
}

.filter-pill .count {
  margin-left: 0.35rem;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--muted-2);
}

.module-grid {
  margin-top: 1.25rem;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}

@media (min-width: 640px) {
  .module-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .module-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (min-width: 1280px) {
  .module-grid { grid-template-columns: repeat(5, 1fr); }
}

.module-card {
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 1rem;
  transition: border-color 0.35s ease, background-color 0.35s ease;
}

.module-card:hover {
  border-color: rgba(var(--accent-rgb), 0.35);
  background-color: rgba(var(--accent-rgb), 0.03);
}

.module-card-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
}

.module-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  width: 36px;
  border-radius: 9px;
  border: 1px solid var(--line);
  background: var(--void);
  color: var(--muted);
}

.module-icon svg {
  height: 18px;
  width: 18px;
}

.module-card-cat {
  font-family: var(--font-mono);
  font-size: 8.5px;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted-2);
}

.module-card h3 {
  margin: 0.75rem 0 0;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--fg);
}

.module-card p {
  margin: 0.3rem 0 0;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--muted);
}

/* ---- setup ---- */
.setup-grid {
  margin-top: 2rem;
  display: grid;
  gap: 1rem;
}

@media (min-width: 1024px) {
  .setup-grid { grid-template-columns: 0.95fr 1.05fr; gap: 1.5rem; }
}

.setup-steps {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.setup-step {
  display: flex;
  gap: 1rem;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 1.25rem;
}

.setup-step-icon {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  width: 44px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--void);
  color: var(--muted);
}

.setup-step-icon svg {
  height: 20px;
  width: 20px;
}

.setup-step h3 {
  display: inline;
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--fg);
}

.setup-step .num {
  margin-left: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--muted-2);
}

.setup-step p {
  margin: 0.4rem 0 0;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--muted);
}

/* ---- accordion (troubleshooting + FAQ, <details>/<summary>) ---- */
.accordion {
  margin-top: 1rem;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: var(--panel);
  overflow: hidden;
}

.accordion-item {
  border-bottom: 1px solid var(--line);
}

.accordion-item:last-child {
  border-bottom: none;
}

.accordion-item summary {
  list-style: none;
  cursor: pointer;
  padding: 1rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--fg);
}

.accordion-item summary::-webkit-details-marker {
  display: none;
}

.accordion-item summary:hover {
  color: var(--accent);
}

.accordion-item summary .chev {
  flex-shrink: 0;
  transition: transform 0.2s ease;
  color: var(--muted);
}

.accordion-item[open] summary .chev {
  transform: rotate(90deg);
}

.accordion-item .accordion-body {
  padding: 0 1.5rem 1.25rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--muted);
}

/* ---- changelog ---- */
.changelog-head {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

@media (min-width: 1024px) {
  .changelog-head { flex-direction: row; align-items: flex-end; justify-content: space-between; }
}

.changelog-nav {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.changelog-nav-btn,
.rail-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  width: 36px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted);
  cursor: pointer;
}

.changelog-nav-btn:hover,
.rail-btn:hover {
  border-color: rgba(var(--accent-rgb), 0.4);
  color: var(--accent);
}

.changelog-strip {
  position: relative;
  margin-top: 2.5rem;
}

.changelog-line {
  position: absolute;
  top: 13px;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.5), var(--line), transparent);
}

.changelog-track {
  position: relative;
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  padding-bottom: 0.75rem;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.changelog-track::-webkit-scrollbar {
  display: none;
}

.changelog-entry {
  position: relative;
  flex-shrink: 0;
  width: 240px;
  scroll-snap-align: start;
}

@media (min-width: 640px) {
  .changelog-entry { width: 270px; }
}

.changelog-dot {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 28px;
  width: 28px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted-2);
}

.changelog-entry.is-latest .changelog-dot {
  border-color: rgba(var(--accent-rgb), 0.6);
  background: rgba(var(--accent-rgb), 0.15);
  color: var(--accent);
}

.changelog-card {
  margin-top: 0.75rem;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 1rem;
}

.changelog-entry.is-latest .changelog-card {
  border-color: rgba(var(--accent-rgb), 0.3);
  background: rgba(var(--accent-rgb), 0.04);
}

.changelog-version {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  font-weight: 600;
  color: #d4d4d8;
}

.changelog-entry.is-latest .changelog-version {
  color: var(--accent);
}

.changelog-tag {
  border-radius: 999px;
  border: 1px solid rgba(var(--accent-rgb), 0.4);
  background: rgba(var(--accent-rgb), 0.1);
  padding: 0.15rem 0.5rem;
  font-family: var(--font-mono);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--accent);
}

.changelog-notes {
  margin: 0.65rem 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.changelog-notes li {
  display: flex;
  gap: 0.6rem;
  font-size: 13px;
  line-height: 1.55;
  color: var(--muted);
}

.changelog-notes li::before {
  content: "";
  flex-shrink: 0;
  margin-top: 0.5rem;
  height: 4px;
  width: 4px;
  transform: rotate(45deg);
  background: #52525b;
}

.changelog-hint {
  margin-top: 0.5rem;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.25em;
  color: var(--muted-2);
}

/* ---- CTA / contact ---- */
.cta-channels {
  margin-top: 2.5rem;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}

@media (min-width: 640px) {
  .cta-channels { grid-template-columns: repeat(2, 1fr); }
}

.cta-channel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 1rem 1.25rem;
  text-decoration: none;
  transition: border-color 0.35s ease, background-color 0.35s ease;
}

.cta-channel:hover {
  border-color: rgba(var(--accent-rgb), 0.35);
  background-color: rgba(var(--accent-rgb), 0.03);
}

.cta-channel-left {
  display: flex;
  align-items: center;
  gap: 0.9rem;
}

.cta-channel-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  width: 40px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--void);
  color: var(--muted);
}

.cta-channel-icon svg {
  height: 18px;
  width: 18px;
}

.cta-channel-label {
  display: block;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--fg);
}

.cta-channel-detail {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

.cta-channel-arrow {
  color: var(--muted-2);
}

/* ---- footer ---- */
.site-footer {
  border-top: 1px solid var(--line);
  background: rgba(10, 14, 12, 0.5);
  padding: 3.5rem 0 2.5rem;
}

.footer-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
}

@media (min-width: 768px) {
  .footer-grid { grid-template-columns: repeat(3, 1fr); }
}

/* The footer has room the 64px navbar does not, so the mark runs larger there. */
.footer-brand .brand-logo {
  height: 72px;
  width: 72px;
}

.footer-brand p {
  margin-top: 1rem;
  max-width: 20rem;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--muted);
}

.footer-brand .designed-for {
  margin-top: 1rem;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  color: var(--muted-2);
}

.footer h3 {
  margin: 0;
}

.footer-col h3 {
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.25em;
  color: var(--muted);
  font-weight: 600;
}

.footer-col ul {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}

.footer-col a {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.9rem;
}

.footer-col a:hover {
  color: var(--accent);
}

.footer-col a .detail {
  margin-left: 0.4rem;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted-2);
}

.footer-bottom {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}

.footer-disclaimer {
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--muted-2);
  max-width: 60rem;
}

.footer-meta {
  margin-top: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted-2);
}

@media (min-width: 640px) {
  .footer-meta { flex-direction: row; align-items: center; justify-content: space-between; }
}

/* ---- responsive safety net ---- */
img, svg {
  max-width: 100%;
}

@media (max-width: 480px) {
  .page, .page.wide { padding-left: 1rem; padding-right: 1rem; }
  .navbar-inner { padding: 0 1rem; }
  .container { padding: 0 1rem; }
}

/* ==================================================================== */
/*  Pricing                                                             */
/* ==================================================================== */

.pricing-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin-top: 3rem;
}

@media (min-width: 640px) {
  .pricing-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  /* Five tiers. Three-up wraps 3+2, which reads as a ladder with a broken
     last rung; five-up keeps the whole ladder on one line at desktop widths
     where there is room for it. */
  .pricing-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0.75rem; }
}

.tier-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.5rem 1.25rem;
  transition: border-color 0.25s ease, transform 0.25s ease;
}

.tier-card:hover {
  border-color: var(--border-strong);
  transform: translateY(-2px);
}

.tier-card.is-featured {
  border-color: rgba(var(--accent-rgb), 0.45);
  background: linear-gradient(180deg, rgba(var(--accent-rgb), 0.06), transparent 55%), var(--panel);
}

.tier-badge {
  position: absolute;
  top: -0.7rem;
  left: 1.25rem;
  /* Brand, not a control — and it has to out-shout the blue CTA sitting
     directly below it inside the same card. */
  background: var(--brand-red);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
}

.tier-card h3 {
  margin: 0;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}

.tier-price {
  margin: 0.5rem 0 0;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--accent);
}

.tier-tagline {
  margin: 0.35rem 0 0;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.5;
}

.tier-meta {
  display: flex;
  gap: 1.25rem;
  margin: 1.1rem 0 0;
  padding: 0.9rem 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.tier-meta dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--fg);
}

.tier-meta dt {
  margin: 0.15rem 0 0;
  font-size: 0.72rem;
  line-height: 1.3;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.tier-features {
  list-style: none;
  margin: 1rem 0 1.5rem;
  padding: 0;
  display: grid;
  gap: 0.5rem;
  font-size: 0.85rem;
  color: var(--muted);
  flex: 1;
}

.tier-features li {
  position: relative;
  padding-left: 1.1rem;
  line-height: 1.45;
}

.tier-features li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--muted-2);
}

.tier-feature-gated {
  color: var(--fg);
}

.tier-feature-gated::before {
  background: var(--accent);
}

/* Marks a page the desktop app has not shipped. src/lib/plans.ts requires
   these to be visible as not-yet-available wherever the tier lists them. */
.tier-soon {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  vertical-align: middle;
}

.tier-cta {
  width: 100%;
  justify-content: center;
}

.pricing-note {
  max-width: 44rem;
  margin: 1.5rem auto 0;
  text-align: center;
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.6;
}

.pay-panel {
  margin-top: 4rem;
  padding: 2rem 1.5rem;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--panel-2);
}

.pay-title {
  margin: 0.75rem 0 0.5rem;
  font-size: 1.4rem;
  letter-spacing: -0.02em;
}

.pay-steps {
  display: grid;
  gap: 1rem;
  margin-top: 2rem;
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .pay-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.pay-step {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1.1rem 1.15rem;
  background: var(--void);
}

.pay-step-num {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--accent);
}

.pay-step h3 {
  margin: 0.5rem 0 0.4rem;
  font-size: 0.98rem;
  letter-spacing: -0.01em;
}

.pay-step p {
  margin: 0;
  color: var(--muted);
  font-size: 0.86rem;
  line-height: 1.55;
}

/* ==================================================================== */
/*  Centring helpers — replace the inline styles index.html used to      */
/*  carry on these three blocks.                                        */
/* ==================================================================== */

.accordion-centered {
  max-width: 48rem;
  margin-left: auto;
  margin-right: auto;
}

.cta-channels-centered {
  max-width: 44rem;
  margin: 2.5rem auto 0;
}

/* One channel now (WhatsApp only — see CONTACTS in site-data.js). A single
   card stretched across a 3-up or 2-up grid reads as a broken row, so the
   track is capped to one card's width and centred instead. Restore the
   multi-column rules here if a second channel is ever added back. */
.cta-channels-centered {
  max-width: 24rem;
  grid-template-columns: minmax(0, 1fr);
}

/* The account portal's copy of the plan ladder. Deliberately not a .rail:
   a horizontal scroller suits a landing page being skimmed, but a settings
   screen should show every tier at once so the viewer can compare against the
   one they hold. Sits inside a .card, so it starts flush rather than with the
   marketing section's 3rem lead-in. */
.plans-grid {
  margin-top: 1.5rem;
}

@media (min-width: 1024px) {
  .plans-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 1360px) {
  .plans-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* The tier the viewer is actually on. Blue is the interactive colour
   everywhere else on the site, so the current-plan marker uses it while the
   red "most picked" badge stays a sales device on the other cards. */
.tier-card.is-current {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.35);
}

.tier-badge-current {
  background: var(--accent);
  color: var(--accent-fg);
}

.tier-current-note {
  margin-top: auto;
  padding-top: 1rem;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-soft);
}

/* ── Hour packs — the "Buy hours" block above the ladder (public/plans.js) ──
   Cards reuse the tier-card's surface, radius, price and badge styles so the
   block reads as the same ladder, one step more concrete. New rules only
   where the shape differs: the tab row and the pack-specific text lines. */
.pack-block {
  margin-top: 2rem;
}

.pack-block-title {
  margin: 0;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}

.pack-block-lede {
  margin: 0.4rem 0 0;
  max-width: 44rem;
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.6;
}

/* A tab row, not a wrapping list: at 375px it scrolls sideways INSIDE the
   card, so the page itself never scrolls sideways. */
.pack-tabs {
  display: flex;
  gap: 0.5rem;
  margin-top: 1.25rem;
  padding-bottom: 0.25rem;
  overflow-x: auto;
  scrollbar-width: thin;
}

.pack-tab {
  flex: 0 0 auto;
  padding: 0.45rem 0.9rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--panel-2);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
}

.pack-tab:hover {
  color: var(--fg);
  border-color: var(--accent);
}

.pack-tab.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-fg);
}

.pack-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
  margin-top: 1rem;
}

@media (min-width: 640px) {
  .pack-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  .pack-cards { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

.pack-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 1.25rem 1rem 1rem;
}

.pack-label {
  margin: 0;
  font-size: 1rem;
  letter-spacing: -0.01em;
}

.pack-hours {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.pack-perhour {
  margin: 0;
  font-size: 0.72rem;
  color: var(--muted-2);
}

/* The server's per-device warning — the one line that must never be dropped. */
.pack-note {
  margin: 0.4rem 0 0;
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.5;
}

.pack-upgrade {
  margin: 0.4rem 0 0;
  color: var(--accent-soft);
  font-size: 0.78rem;
  line-height: 1.5;
}

.pack-cta {
  /* auto pushes every card's CTA to the card foot so a row of packs lines up;
     no padding override, so the button keeps its own vertical metrics. */
  margin-top: auto;
  width: 100%;
  justify-content: center;
}

/* Login's unverified-account row. Hidden until the server returns
   reason: "email_unverified" — see public/login.js. */
.resend-row {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.resend-row p {
  margin: 0;
  font-size: 0.88rem;
  color: var(--muted);
}

.footer-meta-centered {
  justify-content: center;
  margin-top: 1.5rem;
}

/* ==================================================================== */
/*  Portal / admin surface                                              */
/*                                                                      */
/*  These pages now mount the marketing navbar and footer at runtime     */
/*  (shell.js). Everything below is what that costs: the navbar is       */
/*  position:fixed and transparent until scrolled, which works over the  */
/*  landing hero and not over a form, and .page has to clear it.         */
/* ==================================================================== */

body.app .navbar {
  background: rgba(5, 7, 13, 0.85);
  backdrop-filter: blur(16px);
  border-bottom-color: var(--border);
}

body.app .page {
  max-width: 44rem;
  padding: 8rem 1.5rem 5rem;
}

body.app .page.wide {
  max-width: 1180px;
}

/* The console takes the screen: All users runs to ~21 columns, so it gets a wider
   cap than the portal, one baseline for a header row that mixes buttons with
   plain text, and Joined/Email kept to a single line. account.html keeps `wide`. */
body.app .page.console {
  max-width: 1600px;
}

.page.console thead th {
  vertical-align: bottom;
}

#users-table td[data-col="joined"],
#users-table td[data-col="email"] {
  white-space: nowrap;
}

/* Page heading block — the portal equivalent of .section-header, so a form
   page opens with the same eyebrow-and-title rhythm as a landing section. */
.page-head {
  margin-bottom: 2rem;
}

.page-head h1 {
  font-size: clamp(1.8rem, 4vw, 2.4rem);
  line-height: 1.1;
  letter-spacing: -0.03em;
  margin: 0.75rem 0 0.6rem;
}

.page-head p {
  margin: 0;
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1.6;
  max-width: 34rem;
}

body.app .card {
  border-radius: 14px;
  padding: 1.5rem;
}

body.app .card h2 {
  font-size: 1rem;
  letter-spacing: 0.02em;
}

/* The portal never had a footer; give it air above the added one. */
body.app .site-footer {
  margin-top: 2rem;
}

/* ==================================================================== */
/*  Account: licence summary + device list                              */
/* ==================================================================== */

.licence-summary {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem;
  margin: 0 0 1.25rem;
}

@media (min-width: 640px) {
  .licence-summary { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.licence-summary div {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.8rem 0.9rem;
  background: var(--void);
}

.licence-summary dt {
  margin: 0 0 0.3rem;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.licence-summary dd {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  word-break: break-word;
}

.licence-summary dd.is-accent {
  color: var(--accent);
}

/* Wide enough to need its own scroller on a phone rather than widening body. */
#devices-wrap {
  overflow-x: auto;
}

.device-label {
  font-weight: 600;
}

.device-sub {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--muted);
}

td.device-actions {
  text-align: right;
  white-space: nowrap;
}

td.device-actions button {
  margin-top: 0;
  padding: 0.35rem 0.8rem;
  font-size: 0.8rem;
}

.empty-note {
  color: var(--muted);
  font-size: 0.9rem;
  margin: 0.75rem 0 0;
}

/* The manual-fingerprint forms are now a fallback behind a <details>, not
   the primary path. Keep them findable but visually subordinate. */
.fallback-panel > summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 0.85rem;
  padding: 0.4rem 0;
  list-style: none;
}

.fallback-panel > summary::-webkit-details-marker {
  display: none;
}

.fallback-panel > summary:hover {
  color: var(--accent);
}

.fallback-panel[open] > summary {
  margin-bottom: 0.75rem;
}

/* The safety net above this block was written when it was the last rule in
 * the file; body.app rules now follow it, so the narrow-screen padding has
 * to be restated after them or an 8rem desktop inset wins on a phone. */
@media (max-width: 480px) {
  body.app .page,
  body.app .page.wide { padding-left: 1rem; padding-right: 1rem; }
}

/* ==================================================================== */
/*  Horizontal rails                                                     */
/*                                                                       */
/*  Pricing, the features bento, the setup steps and the FAQ read as     */
/*  sideways-scrolling strips on desktop instead of stacking downward —   */
/*  the same mechanism the changelog strip already used, generalised so   */
/*  there is one implementation rather than four.                        */
/*                                                                       */
/*  DESKTOP ONLY, deliberately. Below 1024px every one of these keeps     */
/*  its existing grid/flex stacking: a phone scrolls vertically well and  */
/*  horizontally badly, and a rail there would fight the page's own       */
/*  scroll. Everything in this block is therefore inside the media query, */
/*  and the rail classes are inert on a narrow screen.                    */
/*                                                                       */
/*  Source order matters: these rules override .pricing-grid, .bento and  */
/*  .setup-steps at equal specificity, so this block has to stay AFTER    */
/*  them in the file. Do not move it up.                                  */
/* ==================================================================== */

.rail-wrap {
  position: relative;
  /* initRail() inserts this wrapper AROUND the track, which makes it — not
     the rail — the grid/flex item its section lays out. A grid item's default
     `min-width: auto` is its content width, so without this the setup rail
     forced its column wide enough for all three steps and squeezed the
     troubleshooting column instead of scrolling. This is what makes a rail
     scroll rather than expand; the same declaration on .rail does nothing,
     because .rail is no longer the item. */
  min-width: 0;
}

/* Hidden until the rail actually exists — see the media query below. */
.rail-nav {
  display: none;
}

@media (min-width: 1024px) {
  .rail {
    display: flex;
    /* Explicit, not inherited: .setup-steps is already `display: flex` with
       `flex-direction: column`, so setting only `display: flex` here left it
       stacking downward with a 240px *height* per step — a rail in name only. */
    flex-direction: row;
    gap: 1rem;
    overflow-x: auto;
    padding-bottom: 0.75rem;
    scroll-snap-type: x mandatory;
    /* The rail is driven by the arrow buttons and by trackpad/wheel, so the
       native bar is redundant chrome on a dark panel. Both properties are
       needed — Firefox honours the first, WebKit the pseudo-element. */
    scrollbar-width: none;
  }

  .rail::-webkit-scrollbar {
    display: none;
  }

  /* Fixed width per item: a flex child with a normal `flex: 1` would shrink
     to fit and there would be nothing to scroll. --rail-item is set per
     section below so each strip shows a sensible number of cards. */
  .rail > * {
    flex: 0 0 var(--rail-item, 300px);
    scroll-snap-align: start;
  }

  .rail-nav {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
  }

  .pricing-grid.rail { --rail-item: 300px; margin-top: 0; }
  .bento.rail        { --rail-item: 340px; margin-top: 0; }
  .setup-steps.rail  { --rail-item: 240px; }

  /* The FAQ is the awkward one. Normally .accordion is a single bordered
     box with hairline dividers between rows; as a rail each <details> has to
     become its own card, so the container's border and clipping come off and
     move onto the items. `overflow` is restated rather than inherited from
     .rail because .accordion's `overflow: hidden` shorthand is more specific
     here and would otherwise clip the strip instead of scrolling it. */
  .accordion.rail {
    --rail-item: 340px;
    border: none;
    background: transparent;
    border-radius: 0;
    overflow: visible;
    overflow-x: auto;
    /* Opening one question must not stretch every other card to match it. */
    align-items: flex-start;
    /* Proximity, not mandatory: a card that grows when its <details> opens
       would otherwise yank the strip to a new snap point mid-read. */
    scroll-snap-type: x proximity;
  }

  .accordion.rail .accordion-item {
    border: 1px solid var(--line);
    border-radius: 14px;
    background: var(--panel);
    overflow: hidden;
  }

  /* The centring cap exists for the stacked layout; a rail wants the whole
     container width to scroll through. */
  .accordion.rail.accordion-centered {
    max-width: none;
  }
}

/* ---- compare (compare.html) ----
   A real table for the row/column semantics; the wrapper carries the
   horizontal scroll so the body never does. The MasterBot column is tinted
   with the accent the same way the "is-latest" changelog card is, so the eye
   lands on our column without the other being greyed out. */
.compare-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--panel);
}

.compare {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
  font-size: 0.9rem;
  line-height: 1.55;
}

.compare th,
.compare td {
  padding: 1rem 1.25rem;
  vertical-align: top;
  text-align: left;
  border-bottom: 1px solid var(--line);
}

.compare tbody tr:last-child th,
.compare tbody tr:last-child td {
  border-bottom: 0;
}

.compare thead th {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--void);
}

/* Undo the global admin-table `th` rule (mono, uppercase, small): a row
   header here is a sentence label, not a column heading. */
.compare tbody th {
  width: 22%;
  font-family: var(--font);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--fg);
}

.compare td {
  width: 39%;
  color: var(--muted);
}

.compare .compare-us {
  background: rgba(var(--accent-rgb), 0.04);
  color: var(--fg);
}

.compare thead .compare-us {
  color: var(--accent);
  border-bottom-color: rgba(var(--accent-rgb), 0.3);
}

.compare-note {
  margin: 1rem 0 0;
  font-size: 0.8rem;
  color: var(--muted-2);
}

.compare-cta {
  margin-top: 2.5rem;
  display: flex;
  justify-content: center;
}

/* ── Shared confirm-before-save modal (admin console) ─────────────────── */
.confirm-modal {
  max-width: min(30rem, calc(100vw - 2rem));
  padding: 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card-bg);
  color: var(--fg);
}

.confirm-modal::backdrop {
  background: rgba(0, 0, 0, 0.55);
}

.confirm-modal .confirm-title {
  margin: 0 0 0.5rem;
  font-weight: 600;
}

.confirm-modal ul {
  margin: 0 0 0.75rem;
  padding-left: 1.2rem;
}

.confirm-modal .confirm-warning {
  margin: 0 0 0.75rem;
  color: var(--danger, #e5484d);
}

.confirm-modal .confirm-option {
  display: block;
  margin: 0 0 0.75rem;
}

.confirm-modal .confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}
