/* ============================================================
   Airbrx — design system
   theme.js stamps .light-mode / .dark-mode before first paint,
   so no prefers-color-scheme query is needed here.
   ============================================================ */

/* ---- palette: light (also the no-JS default) --------------- */
:root,
:root.light-mode {
  --ground:      #ffffff;
  --ground-alt:  #f4f3f1;
  --card:        #ffffff;

  --ink:         #2b2b2b;
  --text:        #545049;
  /* 5.20:1 on --ground, 4.69:1 on --ground-alt. Was #8d8781 at 3.55:1, which
     failed AA — and this token carries .fineprint, the reassurance line that
     sits directly under the primary CTA. Same warm gray, one step down. */
  --muted:       #716c66;

  --line:        #e4e0da;
  --line-soft:   #eeebe6;

  /* The brand orange is a *fill*. It stays exactly #fd6c1d on every button,
     rule, icon, chip and border, which is where the brand actually lives.
     Two companion tokens carry the cases where a fill color cannot do the
     job of a text color:
       --accent-text  small orange type on a light ground (links, eyebrows).
                      #fd6c1d is 2.86:1 there and simply cannot be read.
                      5.06:1 on --ground, 4.56:1 on --ground-alt.
       --on-accent    the label sitting on an orange fill. White was 2.86:1;
                      this is 6.33:1 — and it is the value the dark palette
                      below has always used. Light mode was the odd one out. */
  --accent:      #fd6c1d;
  --accent-text: #bb4c08;
  --accent-d:    #e25a10;
  --accent-soft: rgba(253, 108, 29, .09);
  --accent-line: rgba(253, 108, 29, .34);
  --on-accent:   #1a1512;

  /* semantic — separate from the brand accent on purpose */
  --ok:          #1f7d5c;
  --ok-soft:     rgba(31, 125, 92, .11);
  --warn:        #8a6d1f;
  --warn-soft:   rgba(160, 124, 28, .13);
  --stop:        #a33b2a;
  --stop-soft:   rgba(163, 59, 42, .10);

  --shadow:      0 1px 2px rgba(43, 43, 43, .04), 0 8px 24px rgba(43, 43, 43, .06);
  --shadow-lg:   0 2px 6px rgba(43, 43, 43, .06), 0 18px 44px rgba(43, 43, 43, .10);

  /* shadow *colors*, for rules that compose their own offsets */
  --shadow-tint:        rgba(43, 43, 43, .08);
  --shadow-tint-soft:   rgba(43, 43, 43, .05);
  --shadow-tint-strong: rgba(43, 43, 43, .12);
}

/* ---- palette: dark ----------------------------------------- */
:root.dark-mode {
  --ground:      #171412;
  --ground-alt:  #1f1c19;
  --card:        #221f1d;

  --ink:         #f3f0ec;
  --text:        #b4aca4;
  --muted:       #847b73;

  --line:        #332f2b;
  --line-soft:   #2a2724;

  /* lifted a step so it holds on a near-black ground */
  --accent:      #ff8340;
  /* 7.49:1 on --ground already, so orange type needs no darkening here —
     the split exists for the light palette's benefit, not this one. */
  --accent-text: #ff8340;
  --accent-d:    #fd6c1d;
  --accent-soft: rgba(255, 131, 64, .12);
  --accent-line: rgba(255, 131, 64, .36);
  --on-accent:   #1a1512;

  --ok:          #4fbf94;
  --ok-soft:     rgba(79, 191, 148, .13);
  --warn:        #d6ac4a;
  --warn-soft:   rgba(214, 172, 74, .13);
  --stop:        #e2735c;
  --stop-soft:   rgba(226, 115, 92, .13);

  --shadow:      0 1px 2px rgba(0, 0, 0, .30), 0 8px 24px rgba(0, 0, 0, .28);
  --shadow-lg:   0 2px 6px rgba(0, 0, 0, .34), 0 18px 44px rgba(0, 0, 0, .38);

  --shadow-tint:        rgba(0, 0, 0, .38);
  --shadow-tint-soft:   rgba(0, 0, 0, .28);
  --shadow-tint-strong: rgba(0, 0, 0, .50);
}

/* ---- non-color tokens ------------------------------------- */
:root {
  /* Inter is the brand face; not self-hosted yet, so the stack
     falls back to the platform grotesque. */
  --font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, 'Cascadia Mono', 'SF Mono', Menlo, Consolas, monospace;

  /* type scale — fluid, 1.25 ratio at the top end */
  --t-xs:   .78rem;
  --t-sm:   .89rem;
  --t-base: 1rem;
  --t-lg:   1.13rem;
  --t-xl:   clamp(1.25rem, 1.8vw, 1.4rem);
  --t-2xl:  clamp(1.5rem, 2.6vw, 1.9rem);
  --t-3xl:  clamp(1.9rem, 3.6vw, 2.5rem);
  --t-4xl:  clamp(2.3rem, 5.2vw, 3.4rem);

  /* spacing */
  --s-1: .25rem;  --s-2: .5rem;   --s-3: .75rem;  --s-4: 1rem;
  --s-5: 1.5rem;  --s-6: 2rem;    --s-7: 3rem;    --s-8: 4rem;
  --s-9: 6rem;

  --radius:    8px;
  --radius-sm: 5px;
  --radius-lg: 12px;

  --w-page: 76rem;
  --w-prose: 40rem;

  --nav-h: 4.25rem;
}

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

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

/* The nav is sticky, so an anchor jump lands its target underneath it. On
   /scan.html#the-query that hid the "SNOWFLAKE SQL" label and the Copy
   button behind the header — you arrived mid-page with no indication of
   which dialect you were about to copy. */
[id] { scroll-margin-top: calc(var(--nav-h) + 1.25rem); }

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  color: var(--ink);
  font-weight: 700;
  line-height: 1.14;
  letter-spacing: -.022em;
  margin: 0 0 var(--s-4);
  text-wrap: balance;
}
h1 { font-size: var(--t-4xl); letter-spacing: -.03em; line-height: 1.05; }
h2 { font-size: var(--t-3xl); }
h3 { font-size: var(--t-xl); letter-spacing: -.015em; }
h4 { font-size: var(--t-lg); letter-spacing: -.01em; }

p { margin: 0 0 var(--s-4); }

a { color: var(--accent-text); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: .18em; }

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

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

code, kbd, samp {
  font-family: var(--mono);
  font-size: .88em;
  background: var(--ground-alt);
  border: 1px solid var(--line-soft);
  border-radius: 3px;
  padding: .08em .34em;
  color: var(--ink);
}
pre {
  background: var(--ground-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s-4);
  overflow-x: auto;
  font-family: var(--mono);
  font-size: var(--t-sm);
  line-height: 1.55;
}
pre code { background: none; border: none; padding: 0; }

hr { border: none; border-top: 1px solid var(--line); margin: var(--s-7) 0; }

/* ============================================================
   layout
   ============================================================ */
.container {
  width: 100%;
  max-width: var(--w-page);
  margin-inline: auto;
  padding-inline: clamp(1.15rem, 4vw, 2.5rem);
}
.container--prose { max-width: calc(var(--w-prose) + 5rem); }

.section { padding-block: clamp(3rem, 7vw, var(--s-9)); }
.section--alt { background: var(--ground-alt); }
.section--tight { padding-block: clamp(2rem, 4vw, var(--s-7)); }

.stack > * + * { margin-top: var(--s-4); }
.stack-lg > * + * { margin-top: var(--s-6); }

.grid { display: grid; gap: var(--s-5); }
@media (min-width: 700px) {
  .grid--2 { grid-template-columns: repeat(2, 1fr); }
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
  /* Two-up in the middle range: four plan cards side by side below ~1100px
     squeezes the prices to the point where the tier stops being scannable. */
  .grid--4 { grid-template-columns: repeat(2, 1fr); }
  .grid--split { grid-template-columns: 1.05fr .95fr; gap: var(--s-7); align-items: center; }
}

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

.prose { max-width: var(--w-prose); }
.prose > p { margin-bottom: var(--s-4); }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--on-accent);
  padding: var(--s-3) var(--s-4); border-radius: 0 0 var(--radius-sm) 0;
  font-weight: 600;
}
.skip-link:focus { left: 0; text-decoration: none; }

/* ============================================================
   type helpers
   ============================================================ */
.eyebrow {
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin: 0 0 var(--s-3);
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.eyebrow--muted { color: var(--muted); }

.lede { font-size: var(--t-lg); line-height: 1.55; color: var(--text); }
.lede strong { color: var(--ink); font-weight: 600; }

.fineprint { font-size: var(--t-xs); line-height: 1.5; color: var(--muted); }

.tabular { font-variant-numeric: tabular-nums; }

/* ============================================================
   buttons
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  font: inherit;
  font-weight: 600;
  font-size: var(--t-sm);
  line-height: 1;
  padding: .78rem 1.25rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  background: none;
  color: inherit;
  transition: background-color .14s ease, border-color .14s ease, color .14s ease;
}
.btn:hover { text-decoration: none; }

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

.btn-ghost { border-color: var(--line); color: var(--ink); }
.btn-ghost:hover { border-color: var(--muted); background: var(--ground-alt); }

.btn-lg { font-size: var(--t-base); padding: .95rem 1.6rem; }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* ============================================================
   cards, chips, stats
   ============================================================ */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s-5);
  box-shadow: var(--shadow);
}
.card--flat { box-shadow: none; }
.card--accent { border-left: 3px solid var(--accent); border-radius: 0 var(--radius) var(--radius) 0; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: .35em;
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  padding: .2em .55em;
  border-radius: 3px;
  background: var(--ground-alt);
  color: var(--muted);
  white-space: nowrap;
}
.chip--ok   { color: var(--ok);   background: var(--ok-soft); }
.chip--warn { color: var(--warn); background: var(--warn-soft); }
.chip--stop { color: var(--stop); background: var(--stop-soft); }
.chip--accent { color: var(--accent-text); background: var(--accent-soft); }

.stat { border-left: 2px solid var(--line); padding-left: var(--s-3); }
.stat--hot { border-left-color: var(--accent); }
.stat .stat-v {
  font-size: var(--t-2xl); font-weight: 700; color: var(--ink);
  line-height: 1; letter-spacing: -.025em; font-variant-numeric: tabular-nums;
}
.stat--hot .stat-v { color: var(--accent-text); }
.stat .stat-k { font-size: var(--t-sm); margin-top: var(--s-2); line-height: 1.35; }
.stat .stat-s { font-family: var(--mono); font-size: var(--t-xs); color: var(--muted); margin-top: .15rem; }

/* ============================================================
   tables
   ============================================================ */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
}
table { border-collapse: collapse; width: 100%; font-size: var(--t-sm); }
th, td { text-align: left; padding: .72rem .95rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
th {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
  border-bottom: 1px solid var(--line);
}
tbody tr:last-child td { border-bottom: none; }

/* ============================================================
   site nav
   `.nav-links` and `a.btn-primary` are load-bearing: theme.js
   inserts its toggle by those selectors.
   ============================================================ */
.site-nav {
  position: sticky; top: 0; z-index: 50;
  background: var(--ground);
  border-bottom: 1px solid var(--line);
}
.site-nav .container {
  display: flex; align-items: center; gap: var(--s-5);
  min-height: var(--nav-h);
}
.nav-logo { display: flex; align-items: center; flex: none; }
.nav-logo img { height: 1.94rem; width: auto; }

/* One lock-up per theme; the inactive one leaves the layout. Light is the
   default so a classless <html> still resolves.
   `display` belongs to these rules alone -- setting it on `.nav-logo img`
   as well outranks them and renders both marks at once. */
.logo-light { display: block; }
.logo-dark  { display: none; }
:root.dark-mode .logo-light { display: none; }
:root.dark-mode .logo-dark  { display: block; }

.nav-links {
  display: flex; align-items: center; gap: var(--s-5);
  margin-left: auto;
}
.nav-links > a {
  font-size: var(--t-sm); color: var(--text); font-weight: 500; white-space: nowrap;
}
.nav-links > a:hover { color: var(--ink); text-decoration: none; }
.nav-links > a.active { color: var(--ink); font-weight: 600; }
.nav-links > a.btn-primary { color: var(--on-accent); }

/* Bold is wider than regular, so the active page's 600 weight used to nudge
   every link to its right. Each link stacks a hidden bold copy of its own
   label under the visible text, reserving the bold width from first paint —
   going bold then changes the glyphs and nothing else. */
.nav-links > a[data-label] {
  display: inline-flex; flex-direction: column; align-items: center;
}
.nav-links > a[data-label]::after {
  content: attr(data-label);
  font-weight: 600;
  height: 0; overflow: hidden; visibility: hidden; pointer-events: none;
}
/* `.nav-links > a` outranks `.btn-ghost` on color, so Log in needs its own. */
.nav-links > a.nav-login { color: var(--ink); }

/* ---- section search, at the top of the page content --------- */
/* Docs and The Cache open with a search row — first thing in the hero (hub
   pages) or main (article pages) — right-aligned so the box sits under the
   Log in / CTA pair, and sized to match that pair's combined width. Each
   section's script scopes it: docs-search.js searches the docs manifest,
   articles-search.js searches The Cache. */
/* Anchored to the top of the hosting section rather than flowing inside it —
   the heroes and article shells all carry 120-140px of top padding, which
   would otherwise push the row into the middle of the section. */
.docs-search-row {
  display: flex; justify-content: flex-end;
  position: absolute; top: 1.1rem; left: 0; right: 0;
  z-index: 5;
}
/* The heroes are already position:relative; the article shells need to
   become the anchor when they host the row. */
.docs-article-page:has(.docs-search-row),
.article-page:has(.docs-search-row),
.api-layout:has(.docs-search-row) { position: relative; }
.docs-search-box {
  position: relative; display: flex; align-items: center;
  width: 20rem; max-width: 100%;
}
.docs-search-icon {
  position: absolute; left: .65rem; font-size: .78rem; color: var(--muted); pointer-events: none;
}
.docs-search-input {
  font: inherit; font-size: var(--t-sm);
  background: var(--ground-alt); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .42rem 2rem .42rem 1.95rem;
  width: 100%;
}
.docs-search-input:focus { border-color: var(--accent); outline: none; background: var(--ground); }
.docs-search-input::placeholder { color: var(--muted); }
.docs-search-kbd {
  position: absolute; right: .5rem;
  font-family: var(--mono); font-size: .68rem; color: var(--muted);
  background: var(--ground); border: 1px solid var(--line); border-radius: 3px;
  padding: .05rem .3rem; pointer-events: none;
}
.docs-search-results {
  display: none;
  position: absolute; top: calc(100% + .4rem); left: 0; right: 0;
  max-height: 26rem; overflow-y: auto;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  z-index: 60;
}
/* The hub heroes clip overflow for their gradient decor; the dropdown must
   escape. ::before stays inset, so nothing else leaks. */
.docs-hero:has(.docs-search-row),
.cache-hero:has(.docs-search-row) { overflow: visible; }
/* docs-search.js toggles -open on show/close and does not clear the list on
   close — so visibility keys off the class, not :empty. */
.docs-search-results-open { display: block; }
.docs-search-results:empty { display: none; }

/* The rows are generated markup (docs-search.js). Every class below has to
   exist here, or the dropdown degrades to a wall of bare orange links —
   which is what happened when these were dropped porting docs.css into
   this file. */
.docs-search-result {
  display: block; padding: .7rem 1rem;
  color: inherit;
  border-bottom: 1px solid var(--line-soft);
}
.docs-search-result:last-child { border-bottom: none; }
.docs-search-result:hover,
.docs-search-result-active { background: var(--accent-soft); text-decoration: none; }
.docs-search-result-title {
  font-size: var(--t-sm); font-weight: 600; color: var(--ink);
  margin-bottom: .2rem;
}
.docs-search-result-meta {
  display: flex; align-items: center; gap: .6rem; margin-bottom: .3rem;
}
.docs-search-section {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}
.docs-search-chip {
  display: inline-flex; align-items: center; gap: .3rem;
  font-size: var(--t-xs); font-weight: 500;
  color: var(--accent-text); background: var(--accent-soft);
  padding: .1rem .5rem; border-radius: 999px;
}
.docs-search-chip i { font-size: .65rem; }
.docs-search-result-summary {
  font-size: var(--t-xs); color: var(--muted); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.docs-search-empty {
  padding: 1.2rem 1rem; text-align: center;
  color: var(--muted); font-size: var(--t-sm);
}

@media (max-width: 960px) {
  .docs-search-box { width: 100%; }
  .docs-search-kbd { display: none; }
}

.nav-theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.1rem; height: 2.1rem; padding: 0;
  background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--muted); cursor: pointer; font-size: .85rem; flex: none;
}
.nav-theme-toggle:hover { color: var(--ink); border-color: var(--muted); }
/* The toggle ships in the static markup twice — once in the nav links
   (desktop) and once in the bar beside the hamburger (mobile) — so no script
   has to insert or relocate it after first paint, which is what made the nav
   shift on every load. The media query below flips which one shows. */
.nav-theme-toggle--bar { display: none; }

.nav-toggle {
  display: none; align-items: center; justify-content: center;
  width: 2.3rem; height: 2.3rem; margin-left: auto;
  background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--ink); cursor: pointer; font-size: 1rem;
}

@media (max-width: 960px) {
  .nav-toggle { display: inline-flex; }
  .nav-links {
    display: none;
    position: absolute; left: 0; right: 0; top: 100%;
    /* gap:0 — the rows carry their own dividers and padding now. A gap on top
       of those is what made the list read as floating headings. */
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--ground);
    border-bottom: 1px solid var(--line);
    /* Reads as a panel over the page rather than part of it. */
    box-shadow: var(--shadow-lg);
    padding: var(--s-2) clamp(1.15rem, 4vw, 2.5rem) var(--s-5);
    margin-left: 0;
    /* A long menu on a short phone has to scroll inside itself, not push the
       page. dvh so the mobile URL bar doesn't cut the last row off. */
    max-height: calc(100dvh - var(--nav-h));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .nav-links.open { display: flex; }

  /* Rows, not a stack of headings. The gap-separated version read as six
     unrelated links floating on the page — dividers and a tighter rhythm are
     what make a drawer look like a menu rather than a list. */
  .nav-links > a {
    padding-block: var(--s-3);
    border-bottom: 1px solid var(--line-soft);
    font-size: var(--t-base);
    display: flex; align-items: center;
    min-height: 2.75rem;   /* comfortable tap target */
  }
  .nav-links > a:active { background: var(--ground-alt); }

  /* In the drawer each link is a full-width row, so bold can't push a
     neighbor — undo the width-reserving column so rows stay left-aligned. */
  .nav-links > a[data-label] { flex-direction: row; }
  .nav-links > a[data-label]::after { content: none; }

  /* The buttons are the one thing that is not a row: they sit below the list,
     with air above them, so they read as actions rather than extra links. */
  .nav-links > a.nav-login,
  .nav-links > a.btn-primary {
    justify-content: center;
    padding-block: .85rem;
    margin-top: var(--s-4);
    border-bottom: none;
    /* The desktop rule sets nowrap so the row can't break mid-label. Stretched
       to full width in the drawer that has the opposite effect: a long CTA
       forces the panel wider than the screen instead of wrapping inside it. */
    white-space: normal; text-align: center;
  }
  /* When Log in leads the pair, only it carries the air above; the CTA sits
     close beneath it so the two read as one group of actions. */
  .nav-links > a.nav-login + a.btn-primary { margin-top: var(--s-2); }

  /* At this width the bar copy of the toggle takes over from the one in the
     drawer — same square as the hamburger it sits beside. `margin-left: auto`
     is what pushes the pair to the right of the logo. */
  .nav-links > .nav-theme-toggle { display: none; }
  .nav-theme-toggle--bar { display: inline-flex; }
  /* The bar's `gap: var(--s-5)` is spacing for nav links. At this width the
     only flex children left are the logo and two icon buttons — the drawer is
     absolute and takes no part — and 1.5rem between the pair reads as two
     unrelated controls. `margin-left: auto` on the toggle still holds them to
     the right, so tightening the gap costs the logo nothing. */
  .site-nav .container { gap: var(--s-2); }
  .site-nav .container > .nav-theme-toggle {
    margin-left: auto;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    width: 2.3rem; height: 2.3rem;
  }
  /* The hamburger's own `margin-left: auto` would otherwise push it away from
     the toggle and split the pair across the bar. */
  .site-nav .container > .nav-theme-toggle + .nav-toggle { margin-left: 0; }
}

/* ============================================================
   footer
   ============================================================ */
.site-footer {
  background: var(--ground-alt);
  border-top: 1px solid var(--line);
  padding-block: var(--s-7) var(--s-5);
  margin-top: var(--s-8);
}
.footer-grid {
  display: grid; gap: var(--s-6);
  grid-template-columns: 1fr;
}
@media (min-width: 700px) {
  .footer-grid { grid-template-columns: 1.4fr repeat(3, 1fr); gap: var(--s-7); }
}
.footer-brand img { height: 1.5rem; width: auto; margin-bottom: var(--s-3); }
.footer-brand p { font-size: var(--t-sm); color: var(--muted); max-width: 26ch; margin: 0; }
.footer-col h4 {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 var(--s-3);
}
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col li { margin-bottom: var(--s-2); }
.footer-col a { font-size: var(--t-sm); color: var(--text); }
.footer-col a:hover { color: var(--ink); }
.footer-base {
  margin-top: var(--s-6); padding-top: var(--s-4);
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5);
  font-size: var(--t-xs); color: var(--muted);
}

/* ---- placeholder — scaffold only, loud on purpose ---------- */
.placeholder {
  border: 1px dashed var(--accent-line);
  background: var(--accent-soft);
  border-radius: var(--radius);
  padding: var(--s-4) var(--s-5);
  color: var(--text);
}
.placeholder::before {
  content: "PLACEHOLDER";
  display: block;
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 700;
  letter-spacing: .14em; color: var(--accent-text);
  margin-bottom: var(--s-2);
}
.placeholder p:last-child { margin-bottom: 0; }
.placeholder .ref {
  font-family: var(--mono); font-size: var(--t-xs); color: var(--muted);
  display: block; margin-top: var(--s-3);
}

/* ============================================================
   scan flow
   ============================================================ */

/* ---- engine chooser ---------------------------------------- */
/* ---- sticky scan context bar -------------------------------
   Carries the selected engine, the chosen route and a switch for the rest of
   the page, once the picker itself has scrolled away. Fixed, so it costs no
   layout while hidden; visibility keeps it out of the tab order until then. */
.scan-context {
  position: fixed; left: 0; right: 0; top: var(--nav-h); z-index: 30;
  background: var(--card); border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 3px var(--shadow-tint-soft);
  visibility: hidden; opacity: 0; transform: translateY(-.4rem);
  transition: opacity .18s ease, transform .18s ease, visibility .18s;
}
.scan-context.is-stuck { visibility: visible; opacity: 1; transform: none; }
/* On this page an anchor can land while the bar is also showing, so targets
   have to clear the nav *and* the bar. The selector matches only where the
   bar exists, so no other page pays for it. Erring long is deliberate:
   overshooting leaves harmless space above, undershooting hides the thing
   they navigated to — which is the bug this whole row exists to fix. */
.scan-context ~ main [id] { scroll-margin-top: calc(var(--nav-h) + 4.75rem); }
@media (prefers-reduced-motion: reduce) {
  .scan-context { transition: none; }
}
.scan-context-inner {
  display: flex; align-items: center; gap: var(--s-4);
  min-height: 3.5rem; padding-block: .55rem;
}
/* Sans and sentence case, not the mono caption treatment the rest of the
   chrome uses: this is the name of the thing you are standing in, and it has
   to be legible at a glance rather than squinted at. */
.scan-context-title {
  display: inline-flex; align-items: center; gap: .55rem; margin-right: auto;
  font-size: var(--t-base); font-weight: 700; color: var(--ink);
  letter-spacing: -.01em; line-height: 1.2;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scan-context-title > i { color: var(--accent-text); font-size: 1.05em; flex: none; }

/* Segmented control. Both routes stay one press away for the whole page. */
.ctx-toggle {
  display: inline-flex; flex: none;
  background: var(--ground-alt);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 2px;
}
.ctx-toggle > button {
  font: inherit; font-size: var(--t-xs); font-weight: 600;
  padding: .35rem .7rem; white-space: nowrap; cursor: pointer;
  background: none; border: none; border-radius: 3px;
  color: var(--muted);
  transition: background-color .14s ease, color .14s ease;
}
.ctx-toggle > button:hover { color: var(--ink); }
.ctx-toggle > button[aria-pressed="true"] {
  background: var(--card); color: var(--ink);
  box-shadow: 0 1px 2px var(--shadow-tint-soft);
}
.ctx-toggle .lbl-short { display: none; }

@media (max-width: 48rem) {
  .ctx-toggle .lbl-full { display: none; }
  .ctx-toggle .lbl-short { display: inline; }
}
/* Last thing to go. Below this the title alone would be truncated to nothing
   by the controls, and knowing which scanner you are in matters more than
   being able to change route without scrolling. */
@media (max-width: 26rem) {
  .ctx-toggle { display: none; }
}

/* The whole scanner is one object: pick an engine, pick a route, use it. The
   tile owns the border, radius and shadow so the rows inside it read as
   sections of one card rather than three things that happen to be stacked. */
.scan-tile {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); overflow: hidden; box-shadow: var(--shadow);
}

/* Row one. Deliberately still the biggest thing in the tile — it is the first
   decision and the one everything below it depends on — but joined to the
   rows beneath rather than floating above them. */
.engine-pick { display: grid; gap: 0; border-bottom: 1px solid var(--line); }
@media (min-width: 620px) { .engine-pick { grid-template-columns: 1fr 1fr; } }

.engine-card {
  display: flex; align-items: center; gap: var(--s-4);
  text-align: left; width: 100%;
  background: var(--ground-alt); color: inherit;
  border: none; border-bottom: 3px solid transparent; border-radius: 0;
  padding: var(--s-4) var(--s-5); cursor: pointer;
  font: inherit;
  transition: background-color .14s ease, border-color .14s ease, color .14s ease;
}
/* Same divider idiom as .method-tab below, and it has to follow the grid:
   the columns collapse to one at 620px, so the seam turns horizontal. */
.engine-card + .engine-card { border-left: 1px solid var(--line); }
@media (max-width: 619.98px) {
  .engine-card + .engine-card { border-left: none; border-top: 1px solid var(--line); }
}
.engine-card:hover { background: var(--card); }
/* Selected reads as "this row is lifted out of the strip": card ground plus an
   accent underline. Identical to how .method-tab marks itself a few rows down,
   so the tile has one idea of what selected looks like rather than two. The
   old full-box accent border cannot work now that the cards share edges. */
.engine-card[aria-selected="true"] {
  background: var(--card);
  border-bottom-color: var(--accent);
}
.engine-card i { font-size: 1.5rem; color: var(--muted); flex: none; transition: color .14s ease; }
.engine-card[aria-selected="true"] i { color: var(--accent-text); }
.engine-card .name { font-weight: 700; color: var(--muted); display: block; transition: color .14s ease; }
.engine-card[aria-selected="true"] .name { color: var(--ink); }
.engine-card .where { font-size: var(--t-sm); color: var(--muted); display: block; margin-top: .1rem; }

/* ---- numbered steps ----------------------------------------
   A real sequence, so the numbers carry meaning. */
.steps { list-style: none; margin: 0; padding: 0; }
.step {
  display: grid; grid-template-columns: 2.4rem 1fr; gap: 0 var(--s-4);
  padding-bottom: var(--s-6); position: relative;
}
.step:last-child { padding-bottom: 0; }
.step:not(:last-child)::before {
  content: ""; position: absolute;
  left: 1.2rem; top: 2.6rem; bottom: .6rem;
  border-left: 1px dashed var(--line);
}
.step-n {
  width: 2.4rem; height: 2.4rem; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent-text);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-weight: 700; font-size: var(--t-sm);
  flex: none; position: relative; z-index: 1;
}
.step-body { padding-top: .25rem; min-width: 0; }
.step-body h3 { margin-bottom: var(--s-2); }
.step-time {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 700;
  letter-spacing: .1em; color: var(--accent-text); display: block; margin-bottom: .2rem;
}

/* ---- code panel --------------------------------------------- */
/* HubSpot renders the form into this frame, so it has to hold the width and
   reserve height — otherwise the card collapses and then jumps once the embed
   arrives. min-height is the reserved space, not a floor on the form.

   The embed also ships its own palette, built for a white page. Its label and
   heading colour measures 1.71:1 against our dark card — invisible, which is
   exactly what dark mode rendered. We cannot restyle inside the embed: it is
   their markup and may sit in a shadow root, so any override we wrote could
   silently stop applying on their next release. Giving it the ground it was
   designed for is the fix that cannot break that way (8.93:1 there).
   Same panel in both themes, so in dark mode it reads as a deliberate inset
   rather than a hole. color-scheme keeps native controls light inside it. */
.hs-form-frame {
  width: 100%; min-height: 320px; margin-top: var(--s-4);
  background: #f7f7f6;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s-4);
  color-scheme: light;
}

.codepanel { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
/* Wraps rather than crushing: the head now carries a dialect label, an engine
   switch and a copy button that names the dialect, which is more than fits on
   one line on a narrow viewport. */
.codepanel-head {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  padding: .6rem .9rem; background: var(--ground-alt); border-bottom: 1px solid var(--line);
}
/* This is the title of the code block, and it has to win the row. It is the
   only thing telling a deep-linked visitor whose SQL they are about to copy,
   so it is set as a heading — ink, bold, a step up in size — rather than as
   the muted caption it used to be. */
.codepanel-head .t {
  display: inline-flex; align-items: center; gap: .45rem;
  font-family: var(--mono); font-size: var(--t-sm); letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink); font-weight: 700; margin-right: auto;
}
.codepanel-head .t > i { color: var(--accent-text); font-size: .9em; }

/* Icon only, and quieter than the title on purpose. Naming the other engine
   here put "Databricks" in the panel head while the panel was showing
   Snowflake, which is exactly the confusion this row exists to prevent. The
   name now lives in aria-label and the tooltip. */
.btn-switch {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--muted); font-size: var(--t-xs);
  width: 1.9rem; height: 1.72rem; padding: 0; cursor: pointer; flex: none;
}
.btn-switch:hover { color: var(--accent-text); border-color: var(--accent); }
.codepanel pre { border: none; border-radius: 0; margin: 0; max-height: 22rem; }
.btn-copy {
  display: inline-flex; align-items: center; gap: .4rem;
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
  background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--text); padding: .3rem .6rem; cursor: pointer; flex: none;
}
.btn-copy:hover { color: var(--ink); border-color: var(--muted); }
.btn-copy[data-copied] { color: var(--ok); border-color: var(--ok); }

/* ---- paste target ------------------------------------------- */
.pastebox {
  width: 100%; min-height: 8rem; resize: vertical;
  font-family: var(--mono); font-size: var(--t-sm); line-height: 1.5;
  background: var(--card); color: var(--ink);
  border: 1px dashed var(--line); border-radius: var(--radius);
  padding: var(--s-4);
}
.pastebox:focus { border-style: solid; border-color: var(--accent); outline: none; }
.pastebox::placeholder { color: var(--muted); font-family: var(--font); }

.paste-status {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); color: var(--muted); margin-top: var(--s-3); min-height: 1.5rem;
}
.paste-status i { font-size: .8rem; }
.paste-status[data-state="ready"] { color: var(--ok); }
.paste-status[data-state="error"] { color: var(--stop); }

/* ---- connect form ------------------------------------------- */
/* Any author `display` outranks the UA stylesheet's `[hidden] { display:none }`,
   so every component that sets one silently breaks the attribute — .field did,
   which is how an empty warehouse dropdown showed before connecting. Stated
   once, globally, rather than patched per component. */
[hidden] { display: none !important; }

.field { display: block; margin-bottom: var(--s-4); }
.field > span {
  display: block; font-size: var(--t-sm); font-weight: 600; color: var(--ink);
  margin-bottom: var(--s-2);
}
.field > .hint { font-weight: 400; color: var(--muted); font-size: var(--t-xs); margin-top: -.15rem; margin-bottom: var(--s-2); }
.field input {
  width: 100%; font: inherit; font-size: var(--t-sm);
  background: var(--ground); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .7rem .85rem;
}
.field input:focus { border-color: var(--accent); outline: none; }
.field input::placeholder { color: var(--muted); }

/* ---- result console ----------------------------------------- */
.console {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-lg);
}
.console-head {
  display: flex; align-items: center; gap: var(--s-3);
  padding: .8rem 1rem; background: var(--ground-alt); border-bottom: 1px solid var(--line);
}
.console-head > i { color: var(--accent-text); flex: none; }
.console-head .host {
  font-family: var(--mono); font-size: var(--t-xs); color: var(--text);
  flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.console-meter { padding: .85rem 1rem; border-bottom: 1px solid var(--line); }
.console-meter .lbl {
  display: flex; justify-content: space-between; gap: var(--s-3);
  font-family: var(--mono); font-size: var(--t-xs); color: var(--muted); margin-bottom: .5rem;
}
.console-bar { height: .28rem; background: var(--ground-alt); border-radius: 2px; overflow: hidden; }
.console-bar > i { display: block; height: 100%; border-radius: 2px; background: var(--accent); }

.console-rows { padding: .25rem 1rem; }
.console-row {
  display: flex; align-items: baseline; gap: var(--s-4);
  padding: .7rem 0; border-bottom: 1px solid var(--line-soft);
}
.console-row:last-child { border-bottom: none; }
.console-row .k { font-size: var(--t-sm); color: var(--text); flex: 1; line-height: 1.3; }
.console-row .v {
  font-weight: 700; color: var(--ink); flex: none;
  font-variant-numeric: tabular-nums; letter-spacing: -.015em;
}
.console-row--big { padding: .95rem 0 .85rem; }
.console-row--big .k { color: var(--ink); font-weight: 600; }
.console-row--big .v { font-size: var(--t-2xl); color: var(--accent-text); letter-spacing: -.03em; line-height: 1; }

.console-claim {
  display: flex; align-items: center; gap: var(--s-3);
  padding: .85rem 1rem; background: var(--accent-soft); border-top: 1px solid var(--line);
}
.console-claim > i { color: var(--accent-text); flex: none; }
.console-claim .t { font-size: var(--t-sm); color: var(--ink); flex: 1; line-height: 1.3; }
.console-claim .go { font-size: var(--t-sm); font-weight: 600; color: var(--accent-text); flex: none; white-space: nowrap; }
.console-illus {
  font-size: var(--t-xs); color: var(--muted); line-height: 1.45;
  padding: .6rem 1rem .75rem; border-top: 1px solid var(--line); margin: 0;
}

/* ---- method tabs: one tile, two ways in --------------------- */
/* Frameless: .scan-tile carries the border, radius and shadow for the whole
   scanner. This used to be its own card, which is what made the engine row
   above look like it belonged to something else. */
.methods { background: var(--card); }
.method-tabs {
  display: flex; background: var(--ground-alt); border-bottom: 1px solid var(--line);
}
.method-tab {
  flex: 1 1 0; min-width: 0;
  display: flex; align-items: center; gap: var(--s-3);
  text-align: left; font: inherit; cursor: pointer;
  background: none; border: none; border-bottom: 2px solid transparent;
  padding: var(--s-4) var(--s-5);
  color: var(--muted);
}
.method-tab + .method-tab { border-left: 1px solid var(--line); }
.method-tab:hover { color: var(--ink); }
.method-tab[aria-selected="true"] {
  background: var(--card); color: var(--ink); border-bottom-color: var(--accent);
}
.method-tab > i { font-size: 1.05rem; flex: none; }
.method-tab[aria-selected="true"] > i { color: var(--accent-text); }
.method-tab .lbl { display: block; font-weight: 700; font-size: var(--t-sm); line-height: 1.25; }
.method-tab .sub { display: block; font-size: var(--t-xs); color: var(--muted); margin-top: .12rem; }

.method-panel { padding: var(--s-5); }
.method-panel[hidden] { display: none; }

@media (max-width: 560px) {
  .method-tabs { flex-direction: column; }
  .method-tab + .method-tab { border-left: none; border-top: 1px solid var(--line); }
  .method-tab[aria-selected="true"] { border-bottom-color: transparent; box-shadow: inset 3px 0 0 var(--accent); }
}

/* ---- boundary diagram --------------------------------------- */
.figure { margin: 0; }
/* An SVG scaled to a phone shrinks its type with it. Scroll the diagram
   instead so the labels stay at a readable size. */
.figure-scroll { overflow-x: auto; }
.figure svg { display: block; width: 100%; min-width: 640px; height: auto; }
.figure figcaption {
  font-size: var(--t-xs); color: var(--muted); line-height: 1.5;
  margin-top: var(--s-3); text-align: center;
}
.dg-yours    { fill: none; stroke: var(--ok); stroke-width: 1.5; stroke-dasharray: 7 5; }
.dg-mask     { fill: var(--ground); }
.dg-yours-lb { fill: var(--ok); font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .12em; }
.dg-box      { fill: var(--card); stroke: var(--line); stroke-width: 1.5; }
.dg-title    { fill: var(--ink); font-size: 14px; font-weight: 700; }
.dg-sub      { fill: var(--muted); font-size: 11.5px; }
.dg-gw       { fill: var(--accent); }
.dg-gw-lb    { fill: var(--on-accent); font-size: 12px; font-weight: 700; letter-spacing: .04em; }
.dg-gw-note  { fill: var(--accent); font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .1em; }
.dg-flow     { stroke: var(--muted); stroke-width: 1.5; fill: none; }
.dg-arrow    { fill: var(--muted); }
.dg-flow-lb  { fill: var(--muted); font-size: 11.5px; }

/* ---- token lifecycle ---------------------------------------- */
.lifecycle {
  display: grid; gap: var(--s-4);
  grid-template-columns: 1fr; margin: var(--s-5) 0;
}
@media (min-width: 640px) { .lifecycle { grid-template-columns: repeat(3, 1fr); gap: 0; } }
.lc {
  position: relative; text-align: center;
  padding: var(--s-5) var(--s-4);
  background: var(--card); border: 1px solid var(--line);
}
.lifecycle .lc:first-child { border-radius: var(--radius) var(--radius) 0 0; }
.lifecycle .lc:last-child  { border-radius: 0 0 var(--radius) var(--radius); }
@media (min-width: 640px) {
  .lc + .lc { border-left: none; }
  .lifecycle .lc:first-child { border-radius: var(--radius) 0 0 var(--radius); }
  .lifecycle .lc:last-child  { border-radius: 0 var(--radius) var(--radius) 0; }
}
.lc > i { font-size: 1.35rem; color: var(--accent-text); }
.lc.gone > i { color: var(--ok); }
.lc .t { display: block; font-weight: 700; color: var(--ink); margin-top: var(--s-3); font-size: var(--t-sm); }
.lc .d { display: block; font-size: var(--t-xs); color: var(--muted); margin-top: var(--s-2); line-height: 1.45; }

/* ---- objection / answer ------------------------------------- */
.qa {
  display: grid; grid-template-columns: 2.6rem 1fr; gap: 0 var(--s-4);
  padding-bottom: var(--s-6);
}
.qa + .qa { border-top: 1px solid var(--line); padding-top: var(--s-6); }
.qa .n {
  width: 2.6rem; height: 2.6rem; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent-text);
  display: flex; align-items: center; justify-content: center;
  font-size: .95rem; flex: none;
}
.qa .q {
  grid-column: 2;
  font-size: var(--t-xl); font-weight: 700; color: var(--muted);
  letter-spacing: -.015em; line-height: 1.28; margin: .15rem 0 var(--s-4);
  text-wrap: balance;
}
.qa .q::before { content: "\201C"; }
.qa .q::after  { content: "\201D"; }
.qa .a { grid-column: 2; margin: 0; }
.qa .a strong { color: var(--ink); }
.qa .a + .a { margin-top: var(--s-3); }
.qa .kicker {
  grid-column: 2;
  margin-top: var(--s-4); padding: var(--s-3) var(--s-4);
  background: var(--accent-soft); border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-weight: 600; color: var(--ink);
}
@media (max-width: 560px) {
  .qa { grid-template-columns: 1fr; }
  .qa .q, .qa .a, .qa .kicker { grid-column: 1; }
  .qa .n { margin-bottom: var(--s-3); }
}

/* ---- never/always list -------------------------------------- */
.ledger { list-style: none; margin: 0; padding: 0; }
.ledger li {
  display: flex; gap: var(--s-3); align-items: baseline;
  padding: var(--s-3) 0; border-bottom: 1px solid var(--line-soft);
}
.ledger li:last-child { border-bottom: none; }
.ledger i { flex: none; font-size: .82rem; }
.ledger .no i { color: var(--stop); }
.ledger .yes i { color: var(--ok); }
.ledger strong { color: var(--ink); }

/* ---- trust note --------------------------------------------- */
.trustnote {
  display: flex; gap: var(--s-3); align-items: flex-start;
  background: var(--ok-soft); border-radius: var(--radius);
  padding: var(--s-4) var(--s-5);
}
.trustnote > i { color: var(--ok); font-size: 1.1rem; flex: none; margin-top: .15rem; }
.trustnote p { margin: 0; font-size: var(--t-sm); line-height: 1.55; }
.trustnote p + p { margin-top: var(--s-2); }
.trustnote strong { color: var(--ink); }

/* ---- pattern ledger ----------------------------------------- */
.lrows { display: grid; gap: var(--s-3); }
.lrow {
  display: grid; grid-template-columns: 1fr; gap: var(--s-2);
  background: var(--card); border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--s-4) var(--s-5);
}
@media (min-width: 760px) {
  .lrow { grid-template-columns: 15rem 1fr auto; align-items: center; gap: var(--s-5); }
}
.lrow .what { font-weight: 700; color: var(--ink); display: flex; align-items: center; gap: var(--s-3); }
.lrow .what i { color: var(--muted); width: 1.2rem; text-align: center; flex: none; }
.lrow .edge { font-size: var(--t-sm); }
.lrow .edge strong { color: var(--ink); }
.lrow .edge code { font-family: var(--mono); font-size: var(--t-sm); color: var(--ink); }
.lrow--raw { border-left-color: var(--accent); background: var(--accent-soft); }
.lrow--raw .what, .lrow--raw .what i { color: var(--accent-text); }

/* ---- arithmetic strip --------------------------------------- */
.calc { display: flex; flex-wrap: wrap; align-items: stretch; gap: var(--s-3); }
.calc .op { display: flex; align-items: center; color: var(--muted); font-weight: 700; }
.calc .term {
  flex: 1 1 7rem; text-align: center;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--s-4) var(--s-3);
}
.calc .term .v {
  font-size: var(--t-2xl); font-weight: 700; color: var(--ink);
  line-height: 1; font-variant-numeric: tabular-nums;
}
.calc .term .k { font-size: var(--t-xs); color: var(--muted); margin-top: var(--s-2); line-height: 1.35; }
.calc .term--bad  { border-color: var(--stop); background: var(--stop-soft); }
.calc .term--bad .v { color: var(--stop); }
.calc .term--good { border-color: var(--accent-line); background: var(--accent-soft); }
.calc .term--good .v { color: var(--accent-text); }

/* ---- source note under a figure ----------------------------- */
.srcnote { font-size: var(--t-xs); color: var(--muted); line-height: 1.6; }
.srcnote strong { color: var(--text); }
.srcnote + .srcnote { margin-top: var(--s-2); }

/* ---- diagram tokens beyond the trust-page set ---------------- */
.dg-hot     { fill: var(--accent-soft); stroke: var(--accent-line); stroke-width: 1.5; }
.dg-stop    { fill: var(--stop-soft);   stroke: var(--stop);        stroke-width: 1.5; }
.dg-ok      { fill: var(--ok-soft);     stroke: var(--ok);          stroke-width: 1.5; }
.dg-tag       { font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: .08em; }
.dg-tag--stop { fill: var(--stop); }
.dg-tag--hit  { fill: var(--ok); }
.dg-tag--miss { fill: var(--muted); }
.dg-dash    { stroke: var(--muted); stroke-width: 1.5; fill: none; stroke-dasharray: 5 4; }
/* traffic that never meets the gateway. Deliberately not .dg-dash: that one
   means "off the query path" (control plane, log writes), and this is very
   much a query path — just not ours. Longer dash, lighter, so where the two
   cross it reads as a crossing rather than a junction. */
.dg-direct  { stroke: var(--muted); stroke-width: 1.5; fill: none; stroke-dasharray: 2 5; stroke-linecap: round; opacity: .75; }
.dg-direct-a { fill: var(--muted); opacity: .75; }

/* crossover bars */
.dg-bar-auto  { fill: var(--accent); }
.dg-bar-human { fill: var(--ground-alt); stroke: var(--line); stroke-width: 1; }
.dg-inbar     { font-size: 13px; font-weight: 700; fill: var(--on-accent); }
.dg-inbar--h  { fill: var(--ink); }
.dg-cap       { fill: var(--ink); font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: .1em; }
.dg-cap--hot  { fill: var(--accent); }
.dg-cap-m     { fill: var(--muted); font-weight: 600; letter-spacing: .04em; }
.dg-hatch     { stroke: var(--muted); stroke-width: 1; opacity: .5; }
.dg-unknown   { fill: none; stroke: var(--accent-line); stroke-width: 1.5; stroke-dasharray: 6 5; }
.dg-unk       { fill: var(--accent); font-family: var(--mono); font-size: 12px; font-weight: 700; letter-spacing: .1em; }
.dg-foot      { fill: var(--muted); font-size: 11.5px; }

/* donut — arcs are dashed segments of one circle, so the whole
   chart inherits the theme with no redraw on themechange. */
.dg-arc     { fill: none; stroke-width: 38; }
.dg-arc--1  { stroke: var(--accent); }
.dg-arc--2  { stroke: var(--accent-d); opacity: .6; }
.dg-arc--3  { stroke: var(--accent-line); }
.dg-arc--4  { stroke: var(--muted); opacity: .55; }
.dg-ctr-v   { fill: var(--accent); font-size: 42px; font-weight: 800; letter-spacing: -.02em; }
.dg-ctr-k   { fill: var(--muted); font-size: 12px; font-weight: 600; }

/* ---- legend for a figure ------------------------------------ */
.keys { display: grid; gap: var(--s-2); margin-top: var(--s-4); }
@media (min-width: 560px) { .keys { grid-template-columns: repeat(2, 1fr); } }
.keys div { display: flex; align-items: center; gap: var(--s-3); font-size: var(--t-sm); }
.keys i { width: .7rem; height: .7rem; border-radius: 2px; flex: none; }
.keys b { margin-left: auto; color: var(--ink); font-variant-numeric: tabular-nums; }

/* ---- the scan tool: wide by default -------------------------- *
 * This page is a tool, not an article, and it is opened on a laptop far more
 * often than on a phone. The prose measure that suits the rest of the site
 * turned it into one long column of scroll, so the panels use the width
 * instead: form beside its help, steps side by side, results as a row.      */

.panel-cols { display: grid; gap: var(--s-6); }
@media (min-width: 900px) {
  /* The form is the work; the help is support, so it gets the narrower half. */
  .panel-cols { grid-template-columns: 1.15fr .85fr; gap: var(--s-7); align-items: start; }
}

.tokenhelp {
  background: var(--ground-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s-5);
}
.tokenhelp h3 { margin-bottom: var(--s-2); }
.tokenhelp pre { max-height: none; }
/* An anchor can't be :disabled, and a dead link that looks live is worse than
   one that plainly isn't ready yet. */
.btn.is-disabled { opacity: .5; pointer-events: none; }

/* The paste steps carry very uneven weight: step 1 holds a SQL block, step 3 a
   textarea, step 2 is one sentence. Three equal columns squeezed the query into
   a third of the width — cut off horizontally AND scrolling vertically — while
   step 2 sat in mostly empty space. So it splits two ways instead: the query on
   the left with room to read it, the paste box on the right spanning both rows.
   Reads as "here is the statement → here is where its answer goes". */
@media (min-width: 1000px) {
  .steps--split {
    display: grid;
    grid-template-columns: 1.1fr .9fr;
    column-gap: var(--s-7);
    align-items: start;
  }
  .steps--split .step:nth-child(1) { grid-column: 1; grid-row: 1; }
  .steps--split .step:nth-child(2) { grid-column: 1; grid-row: 2; padding-bottom: 0; }
  .steps--split .step:nth-child(3) { grid-column: 2; grid-row: 1 / span 2; padding-bottom: 0; }

  /* Step 2 is last in its column, so its connector would dangle into nothing.
     Step 3 never had one (:not(:last-child) already excludes it). */
  .steps--split .step:nth-child(2)::before { display: none; }

  /* Both panels get the height the extra width bought them. The min-height on
     the query matters as much as the max: the statement is the thing being
     asked to trust, and a box scrolled down to six visible lines reads as
     something to skip rather than something to read. */
  .steps--split .codepanel pre { min-height: 26rem; max-height: 38rem; }
  .steps--split .pastebox { min-height: 20rem; }
}

/* Results: the console and the ranked rules read better beside each other than
   stacked, once there is room for both. */
@media (min-width: 1100px) {
  .results-cols { display: grid; grid-template-columns: .8fr 1.2fr; gap: var(--s-7); align-items: start; }
}

/* ---- rule pills --------------------------------------------- *
 * Eight selectors in a row. The .method-tab component this first reused was
 * built for two: at eight the labels wrapped mid-phrase and the rule ids
 * clipped. These are single-line and wrap as a set instead, which keeps every
 * label readable at any width and needs no horizontal scroll.                */
.rulepills { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.rulepill {
  display: inline-flex; align-items: center; gap: var(--s-2);
  white-space: nowrap;
  font: inherit; font-size: var(--t-sm); font-weight: 600;
  color: var(--text); background: var(--card);
  border: 1px solid var(--line); border-radius: 100px;
  padding: .45rem .9rem; cursor: pointer;
  transition: border-color .14s ease, color .14s ease, background .14s ease;
}
.rulepill i { color: var(--muted); font-size: .85em; }
.rulepill:hover { border-color: var(--accent-line); color: var(--ink); }
.rulepill[aria-selected="true"] {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
}
.rulepill[aria-selected="true"] i { color: var(--on-accent); }

/* ---- people ------------------------------------------------- */
.profile { display: grid; gap: var(--s-6); }
@media (min-width: 760px) {
  .profile { grid-template-columns: 16rem 1fr; gap: var(--s-7); align-items: start; }
}
.profile-photo {
  width: 100%; max-width: 16rem; height: auto; display: block;
  border-radius: var(--radius-lg); border: 1px solid var(--line);
}

/* A team card is a link, so it has to lose the link underline and keep the
   card's own color rather than inheriting the accent. */
.person { display: block; color: inherit; text-decoration: none; }
.person:hover { border-color: var(--accent-line); }
.person img {
  width: 100%; height: auto; display: block;
  border-radius: var(--radius); margin-bottom: var(--s-4);
}
.person h3 { margin-bottom: var(--s-1); }

/* ---- kit surfaces ------------------------------------------- *
 * kit/v1/kit.css styles the product components this site mounts (sign-in,
 * the Terms dialog, the tenant picker). It ships a light-only palette, so
 * without this its cards stay white while the site flips — and `--ink` goes
 * near-white on them, which is invisible text rather than an ugly one.
 * Only the kit's own token names are restated here; site tokens are already
 * handled above and win by load order.                                     */
:root.dark-mode {
  --white:         #221f1d;
  --surface:       #1f1c19;
  --body:          #b4aca4;
  --border:        rgba(255, 255, 255, .09);
  --border-strong: rgba(255, 255, 255, .16);
  --accent-tint:   rgba(255, 131, 64, .12);
  --accent-tint2:  rgba(255, 131, 64, .18);
}

/* The kit mounts dialogs into this, and requires it to exist and to outlive
   any single render. */
#modal-root:empty { display: none; }

/* A header that exists for screen readers only — the rule-selection column,
   where a visible label would just repeat the checkbox beside it. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* ============================================================
   motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* ============================================================
   pricing — plan cards and the ABU meter
   The model is two-part: a platform fee for the control plane, and a
   published per-GB meter for the work. The page has to show both, or it
   describes a subscription business we are not running.
   ============================================================ */
.plan { display: flex; flex-direction: column; }
.plan .ledger { flex: 1 1 auto; }

.plan-price {
  font-size: var(--t-3xl); font-weight: 700; color: var(--ink);
  letter-spacing: -.03em; line-height: 1.05; margin: 0;
}
/* The qualifier rides at label size so the figure is what gets read first. */
.plan-price span {
  font-size: var(--t-xs); font-weight: 600; color: var(--muted);
  letter-spacing: 0; margin-left: .15rem; white-space: nowrap;
}
.plan-who {
  font-size: var(--t-sm); color: var(--text);
  margin: .3rem 0 var(--s-3); padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line-soft);
}
/* The adoption stage this tier represents. Muted and last: it is the sales
   narrative, not something a buyer is choosing between. */
.plan-stage {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text); margin: var(--s-4) 0 0;
}

/* The meter band. The one dark surface on the marketing site, and it earns it:
   these are the published rates the whole "no savings-share" claim rests on. */
.meter {
  background: var(--ink); color: var(--ground);
  border-radius: var(--radius); padding: var(--s-6);
  display: grid; gap: var(--s-5);
}
@media (min-width: 900px) {
  .meter { grid-template-columns: 20rem 1fr; gap: var(--s-7); align-items: center; }
}
.meter .eyebrow { color: var(--accent); }
.meter h2 { color: var(--ground); font-size: var(--t-xl); margin-bottom: var(--s-3); }
.meter-lede p:last-child { font-size: var(--t-sm); opacity: .8; margin: 0; }

.meter-rates { display: grid; gap: var(--s-4); }
@media (min-width: 640px) { .meter-rates { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .meter-rates { grid-template-columns: repeat(4, 1fr); } }

.meter-rate { padding-left: var(--s-4); border-left: 1px solid rgba(255, 255, 255, .18); }
.meter-rate--hot { border-left-color: var(--accent); }
.meter-rate .v {
  font-size: var(--t-xl); font-weight: 700; color: var(--ground);
  letter-spacing: -.02em; margin: 0; line-height: 1.1;
}
.meter-rate--hot .v { color: var(--accent); }
.meter-rate .v span {
  font-size: var(--t-xs); font-weight: 600; opacity: .7;
  letter-spacing: 0; margin-left: .1rem;
}
.meter-rate .k { font-size: var(--t-xs); margin: .3rem 0 0; opacity: .82; line-height: 1.45; }

.meter-notes {
  list-style: none; margin: var(--s-4) 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-6);
  font-size: var(--t-xs); color: var(--muted);
}

.h1-accent { color: var(--accent-text); }

/* ---- pricing above the fold --------------------------------
   The plan row is the page. On a 1080p laptop the default hero pushed the
   cards to roughly 825px, so a buyer met a headline and a gap rather than
   four prices. These trim the run-up without shrinking the plans. */
.h1-pricing {
  font-size: clamp(2rem, 3.4vw, 2.85rem);
  margin-bottom: var(--s-3);
}
.lede--tight { max-width: 46rem; margin-bottom: var(--s-6); }

/* .ledger li is padding: var(--s-3) 0 site-wide; four rows of that inside a
   plan card is 96px of padding alone. Tightened here only — the ledger is
   used for prose lists elsewhere where the air is doing real work.
   No !important needed: this block is later in the file at equal weight. */
.plan .ledger li { padding-block: .45rem; }
.plan-stage { margin-top: var(--s-3); }

/* ---- split rows that should hang from the top ---------------
   .grid--split centres its columns, which is right for the homepage hero
   where two roughly equal blocks sit side by side. It is wrong wherever one
   column is much taller than the other: the short column floats halfway down
   and the row reads as misaligned rather than deliberate. */
.grid--split.grid--top { align-items: start; }

/* Contact channels. A supporting column beside the form, so the headings step
   down from the h2 the form itself carries — same markup, less shout. */
.channels .card { padding: var(--s-4) var(--s-5); }
.channels .card h2 {
  font-size: var(--t-lg); letter-spacing: -.01em;
  display: flex; align-items: baseline; gap: var(--s-3); margin-bottom: var(--s-2);
}
.channels .card h2 > i { font-size: .85em; color: var(--accent-text); flex: none; }
.channels .card p { margin-bottom: var(--s-2); font-size: var(--t-sm); }
.channels .card p:last-child { margin-bottom: 0; }

/* One action under the plan row. Four per-card buttons asked the reader to
   commit to a tier before they had finished comparing them; the tier is a
   choice they can make inside the app. */
.plans-cta {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-3);
  margin-top: var(--s-6); text-align: center;
}
.plans-cta .fineprint { margin: 0; }

/* A full-width child measures its flex container, so the row has to shrink to
   its content for "as wide as the two above" to mean anything — as a block it
   would span the whole prose column instead. The parent centres text, which
   is what re-centres the inline-flex row. */
.btn-row--stack { display: inline-flex; }
.btn-row--stack > .btn-full { flex: 1 0 100%; }
