/* App layout on top of the vendored Nocturne system. Ours to edit; nocturne.css
   is not (see README.md in this directory).

   Every colour here is a token. No hex, no rgb(), no hsl(), no oklch(), and no
   named colour — design_system_test.rb fails the build on any of them, in this
   file and in the style attributes of our ERB. That is the whole point of adopting
   a system: the day Jeff ships a new drop, the palette moves and this file does
   not have to.

   Sizes are a different matter and are NOT enforced: raw px appears here where a
   spacing token would be the wrong unit (a blur radius, a min-height, a border
   width). Colour is the one the tokens fully cover. */

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* A page that scrolls sideways is a bug, always. Wide content scrolls inside
     its own box instead (see .table-scroll). */
  overflow-x: hidden;
}

main { flex: 1; }

/* The page shell: a CENTERED container whose CONTENT is flush-left. Both halves
   matter. This comment used to claim the container was not centered, which the
   `margin: 0 auto` on the next line contradicts and Jeff's mockup does not do
   either — it centers a 1200px container and left-aligns everything inside it,
   which is what the readme's "asymmetric, whitespace on the right" describes.
   Width matches his 1200px rather than a rounder number of our own. */
.shell {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  /* Jeff's gutter, verbatim: clamp(20px, 5vw, 72px). A flat token here made every
     page sit ~55px further left than the design at desktop width, which is the
     single most visible difference between the two when you flip between them. */
  padding: 96px clamp(20px, 5vw, 72px) 72px;
}

.shell-tight { padding-top: 64px; }

/* A section label above a heading. Was `.card-kicker` borrowed out of a card,
   which meant a card restyle in the next drop would have silently restyled page
   headings. Same look, its own name, ours. */
.kicker {
  display: block;
  font-size: 13px;
  line-height: 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin: 0 0 40px;
}

.shell-narrow {
  max-width: 480px;
  margin: 0;
}

/* Header. `.nav` and `.nav-brand` come from the system; the layout does not.
   Note what is NOT here: no `flex: 1` on the links. `.nav-brand` already carries
   `margin-right: auto`, so a second growing child pushes the actions off the right
   edge and the page scrolls sideways — which is exactly what the first version of
   this file did. */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  row-gap: 12px;
  align-items: center;
  gap: 24px;
  padding: 14px clamp(20px, 5vw, 72px);
  background: color-mix(in srgb, var(--color-bg) 88%, transparent);
  backdrop-filter: blur(14px);
  /* The mockup's nav carries this rule; the vendored `.nav` explicitly sets
     `border-bottom: none`, so it is ours to add back. */
  border-bottom: 1px solid var(--color-divider);
}

/* The brand lockup: a 22px outlined badge holding the initial, then the wordmark
   with "API" in the accent. The badge is the mark Jeff drew; without it the header
   reads as plain text and is the first thing that looks unfinished. */
.nav-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  /* NOT margin-right: auto (which the vendored .nav-brand sets). His nav is
     [brand][links]<spacer>[actions]; an auto margin here pushes the links to the
     right edge along with the actions, which reads as a different navigation. */
  margin-right: 0;
}

.site-nav-spacer { flex: 1; }

/* The nav CTA is an <a> for us and a <button> for him, so the vendored
   `.nav a { color: inherit }` (0,1,1) outranks `.btn-primary` (0,1,0) and painted
   it full text colour instead of accent. His is accent. */
.site-nav .btn-primary { color: var(--color-accent); }
.site-nav .btn-ghost { color: var(--color-accent); }

.brand-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex: none;
  border: 1px solid var(--color-accent);
  border-radius: 6px;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 12px;
  line-height: 1;
  color: var(--color-accent);
}

.brand-word {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 16px;
  line-height: 1;
  letter-spacing: -0.01em;
}

.nav .nav-brand, .nav .nav-brand:hover { color: var(--color-text); }
.brand-word em { font-style: normal; color: var(--color-accent); }

.site-nav-links {
  display: flex;
  align-items: center;
  gap: 22px;
  min-width: 0;
}


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

.site-footer {
  border-top: 1px solid var(--color-divider);
  /* Same gutter as the nav and the shell, or the footer's right-hand item sits
     flush against the viewport edge while everything above it is inset. */
  padding: var(--space-6) clamp(20px, 5vw, 72px);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  justify-content: space-between;
  color: var(--color-neutral-500);
  font-size: 0.85rem;
}

/* Flash messages. Tinted from the ramps, never a raw red or green: on this
   ground a saturated fill is the one thing the system forbids. */
.flashes {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-8);
}

.flash {
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-divider);
  background: var(--color-surface);
  font-size: 0.9rem;
}

.flash-notice { border-color: var(--color-accent-700); }
.flash-alert  { border-color: var(--color-neutral-600); }

/* Stacked form. The system styles the field; the rhythm between fields is ours. */
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.stack-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-6);
}

/* Inside a .stack the parent's gap already separates the rows, so the standalone
   margin would double it. */
.stack > .stack-actions { margin-top: 0; }

/* The one full-bleed saturated band the system allows at page scale. The gradient
   is Jeff's, verbatim: a glow bloom off the top-right over the section ground, not
   the top-to-bottom linear fill an earlier version guessed at. */
.stat-band {
  background:
    radial-gradient(900px 420px at 85% -40%,
      color-mix(in srgb, var(--color-section-glow) 70%, transparent), transparent 64%),
    var(--color-section);
  padding: 70px 0;
}

.stat-band-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 clamp(20px, 5vw, 72px);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
  gap: 42px 28px;
}

.stat-value {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: clamp(34px, 3.4vw, 48px);
  line-height: 56px;
  /* Optical alignment: the design hangs the first glyph slightly left so the
     numeral lines up with the text above it. */
  margin: 0 0 0 -0.055em;
  font-feature-settings: 'tnum' 1;
}

.stat-label {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 64%, transparent);
}

.stat-missing .stat-value { color: var(--color-neutral-600); }
.cell-missing { color: var(--color-neutral-600); }

/* Card grid, used by the marketing page and the styleguide alike. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-6);
}

/* Styleguide. Every specimen sits in a labelled well so a variant that renders
   wrong is visible next to the ones that do not. */
.spec {
  border-top: 1px solid var(--color-divider);
  padding-top: var(--space-6);
  margin-top: calc(var(--space-8) * 2);
}

.spec-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

.spec-name {
  font-family: ui-monospace, monospace;
  font-size: 0.8rem;
  color: var(--color-neutral-500);
  margin: 0 0 var(--space-3);
}

.swatch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: var(--space-3);
}

/* The label sits UNDER the chip, not inside it. Inside, it inherits a colour that
   is unreadable against half the ramp by construction: dark text vanishes on step
   900, light text vanishes on step 100, and --color-text on the `text` swatch is
   white on white. A label you cannot read on the swatch you most need to identify
   is not a styleguide. */
.swatch {
  font-size: 0.7rem;
  font-family: ui-monospace, monospace;
  color: var(--color-neutral-400);
}

.swatch-chip {
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-divider);
  min-height: 56px;
  margin-bottom: var(--space-2);
}

/* Choice groups.
   `.seg` and `.radio` come from the vendored system. `.choice-chip` and
   `.choice-card` are the plan builder's own shapes, which the system does not ship.
   They are styled off `:has(input:checked)` on the LABEL, the same mechanism
   `.seg-opt` uses, so all four shapes behave identically and none of them needs a
   line of JavaScript to look selected. */
.choice-fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

.choice-legend {
  padding: 0;
  margin-bottom: var(--space-4);
  font-size: 0.8rem;
  color: var(--color-neutral-400);
}

.choice-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.choice-group-card {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 215px), 1fr));
  gap: var(--space-4);
}

.choice-chip input, .choice-card input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}

.choice-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-neutral-800);
  background: var(--color-surface);
  cursor: pointer;
  font-size: 0.9rem;
}

.choice-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-8);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-neutral-800);
  background: var(--color-surface);
  cursor: pointer;
  text-align: left;
}

.choice-chip:hover, .choice-card:hover { border-color: var(--color-accent-600); }

/* Selected is an accent OUTLINE, matching the system's primary-action rule: the
   accent is a line here, never a flood. */
.choice-chip:has(input:checked), .choice-card:has(input:checked) {
  border-color: var(--color-accent);
  box-shadow: inset 0 0 0 1px var(--color-accent);
}

.choice-chip:has(input:focus-visible), .choice-card:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.choice-card .choice-label {
  font-family: var(--font-heading);
  font-size: 1.05rem;
}

.choice-description { font-size: 0.85rem; color: var(--color-neutral-300); }
.choice-note { font-size: 0.78rem; color: var(--color-neutral-500); }

/* The plan builder's two columns: the choices, and a summary that sticks. */
.builder {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--space-8) * 2);
  align-items: flex-start;
}

.builder-steps { flex: 1 1 520px; min-width: 0; }

.builder-summary {
  flex: 0 1 320px;
  position: sticky;
  top: calc(var(--space-8) * 3);
}

.builder-step { margin-bottom: calc(var(--space-8) * 2); }

.builder-step-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

.builder-step-number {
  font-family: var(--font-heading);
  color: var(--color-accent);
  font-size: 0.85rem;
}

.builder-step-head h2 { margin: 0; font-size: 1.25rem; }

.summary-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--color-divider);
  font-size: 0.88rem;
}

.summary-row dt { color: var(--color-neutral-400); }
.summary-row dd { margin: 0; text-align: right; }

/* Tables scroll inside their own box; the page body never scrolls sideways. */
.table-scroll {
  overflow-x: auto;
}

/* The hero: two columns that wrap, copy left and the live sample right. Both
   `flex: 1 1 420px`, so they sit side by side above ~900px and stack below with no
   media query. Jeff's numbers throughout. */
.hero {
  display: flex;
  flex-wrap: wrap;
  gap: 56px;
  align-items: center;
}

.hero-copy { flex: 1 1 420px; min-width: 0; }

.hero h1 {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: clamp(42px, 5.2vw, 68px);
  line-height: 1.08;
  letter-spacing: -0.022em;
  /* Hangs the cap edge into the gutter so the headline optically aligns with the
     copy under it. */
  margin: 0 0 0 -0.06em;
}

.hero-lead {
  font-size: 18px;
  line-height: 28px;
  color: color-mix(in srgb, var(--color-text) 80%, transparent);
  max-width: 46ch;
  margin: 32px 0 0;
}

.hero-actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 28px;
}

.hero-note {
  font-size: 13px;
  line-height: 20px;
  color: var(--color-neutral-500);
  margin: 22px 0 0;
}

/* The live sample panel. Everything in it is a real response from our own feed;
   see Web::HomeController. When there is no game to show, the panel is omitted and
   the hero collapses to one column rather than rendering an empty frame. */
.sample {
  flex: 1 1 420px;
  min-width: 0;
  background: var(--color-surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

.sample-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--color-neutral-800);
}

.sample-dot {
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  background: var(--color-accent);
  animation: sample-pulse 2.6s ease-in-out infinite;
}

@keyframes sample-pulse {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 0.9; }
}

.sample-method,
.sample-path,
.sample-meta {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1;
}

.sample-method { color: var(--color-neutral-400); }
.sample-path { color: var(--color-neutral-200); min-width: 0; overflow-wrap: anywhere; }
.sample-meta { margin-left: auto; font-size: 11px; color: var(--color-accent-400); }

.sample-body {
  margin: 0;
  padding: 18px;
  overflow-x: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  line-height: 21px;
  color: var(--color-neutral-200);
}

.sample-body .k { color: var(--color-accent-300); }
.sample-body .v { color: var(--color-accent-400); }
.sample-body .p { color: var(--color-neutral-500); }
.sample-body .s { color: var(--color-neutral-200); }

/* The feature list. Jeff renders these as ruled ROWS — number, title, description,
   baseline-aligned across a hairline rule — not as a card grid. An earlier version
   used cards, which is the largest single structural difference between the design
   and the page. */
.feature-row {
  display: flex;
  flex-wrap: wrap;
  gap: 28px clamp(24px, 4vw, 72px);
  align-items: baseline;
  padding: 36px 0;
}

.feature-row + .feature-row { border-top: 1px solid var(--color-neutral-800); }

.feature-number {
  flex: 0 0 64px;
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 15px;
  line-height: 28px;
  color: var(--color-accent);
  margin: 0;
  font-feature-settings: 'tnum' 1;
}

.feature-title {
  flex: 0 1 340px;
  min-width: 0;
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 24px;
  line-height: 28px;
  letter-spacing: -0.01em;
  margin: 0;
}

.feature-body {
  flex: 1 1 280px;
  min-width: 0;
  font-size: 15.5px;
  line-height: 28px;
  color: color-mix(in srgb, var(--color-text) 78%, transparent);
  margin: 0;
  max-width: 52ch;
}

/* An interior page header (plan builder, docs, styleguide). Jeff sizes these
   DIFFERENTLY from the landing hero — h1 clamp(34,4vw,52)/1.14 rather than
   clamp(42,5.2vw,68)/1.08, and the kicker sits 14px above the heading instead of
   40px. Treating both as one style is what made the interior pages read as
   oversized next to the design. */
.page-head { margin-bottom: 48px; }

.page-head .kicker { margin-bottom: 14px; }

.page-head h1 {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: clamp(34px, 4vw, 52px);
  line-height: 1.14;
  letter-spacing: -0.018em;
  margin: 0 0 12px;
}

.page-head p {
  font-size: 16px;
  line-height: 26px;
  color: color-mix(in srgb, var(--color-text) 78%, transparent);
  max-width: 56ch;
  margin: 0;
}

/* The plan summary's total and the billing line under it. Jeff sets the total at
   500 40px/48px heading with tabular numerals, and the note at 13/20 neutral-500
   six pixels below. */
.plan-total {
  display: block;
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 40px;
  line-height: 48px;
  font-feature-settings: 'tnum' 1;
}

.plan-billing {
  display: block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 13px;
  line-height: 20px;
  color: var(--color-neutral-500);
  margin: 6px 0 24px;
}

/* ============================================================
   Fidelity pass 2. Values below are Jeff's, read from the inline styles in his
   mockup; each block names what it corrects.
   ============================================================ */

/* The page ground is not flat. His body carries a violet bloom off the top right.
   Layer 1 only: his second layer darkens the bottom-left with a black mix, and this
   file may carry no colour literal — the ramp has nothing darker than --color-bg
   (neutral-900 is LIGHTER than the ground), so there is no token that expresses it.
   Omitted deliberately rather than approximated; a --color-shade token in a future
   drop would close it. */
body {
  background:
    radial-gradient(1200px 720px at 82% -160px,
      color-mix(in srgb, var(--color-accent-900) 75%, transparent), transparent 60%),
    var(--color-bg);
  text-wrap: pretty;
}

/* Interior pages sit tighter at the top and looser at the bottom than the landing
   hero: his plan builder is 64px/88px against the hero's 96px/72px. */
.shell-interior { padding: 64px clamp(20px, 5vw, 72px) 88px; }

/* The landing feature list is the inverse again: 88px above, 56px below. */
.shell-features { padding: 88px clamp(20px, 5vw, 72px) 56px; }

/* Idle nav items are muted, not full text colour; only hover and the current page
   take the accent (the vendored rules already do that, and still out-specify this).
   Excluding .btn matters: `line-height: 1` here was overriding the button's own 1.2
   and making the nav CTA 3px shorter than his. */
.site-nav a:not(.btn) {
  font-size: 14px;
  line-height: 1;
}

.site-nav-actions { gap: 24px; }

/* His footer is a centered 1200px block, so the rule above it spans the container
   rather than the whole viewport. */
.site-footer {
  /* `width: 100%` is load-bearing. The footer is a flex CHILD of body, and an auto
     cross-axis margin on a flex item overrides stretch and sizes it to its content:
     without this the footer collapsed to 510px and its rule stopped short on both
     sides. Caught by measuring after the last pass, not by looking. */
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 36px clamp(20px, 5vw, 72px) 56px;
  font-size: 13px;
  line-height: 22px;
  color: var(--color-neutral-600);
  gap: 24px;
}

/* Links deepen to accent-300 on hover, not flat accent. */
a:hover { color: var(--color-accent-300); }

/* --- Choice groups ---------------------------------------------------------- */

/* The legend is for screen readers only. His design has no visible label above the
   options — the numbered step head is the label — so rendering it put a third extra
   line of text on the page. Hidden rather than deleted: the fieldset needs a name. */
.choice-legend,
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.choice-group { gap: 10px; }
.choice-group-card { gap: 14px; }

.choice-chip {
  padding: 10px 15px;
  gap: 9px;
  font-size: 14px;
  line-height: 1;
}

.choice-card {
  padding: 20px 20px 22px;
  gap: 0;
}

.choice-card .choice-label {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 17px;
  line-height: 24px;
  margin-bottom: 8px;
}

.choice-description {
  font-size: 14px;
  line-height: 22px;
  color: color-mix(in srgb, var(--color-text) 74%, transparent);
  margin-bottom: 14px;
}

.choice-note {
  font-size: 12.5px;
  line-height: 18px;
  color: var(--color-neutral-500);
}

/* Selected is an outline drawn OUTSIDE an unchanged border, which is why his
   selected card does not appear to shift. Ours recoloured the border and added an
   inset ring, giving a 2px line on the inside edge instead.
   Keyboard focus keeps its own ring, pushed further out so the two never merge. */
.choice-chip:has(input:checked), .choice-card:has(input:checked) {
  outline: 1.5px solid var(--color-accent);
  border-color: var(--color-neutral-800);
  box-shadow: none;
}

.choice-chip:has(input:focus-visible), .choice-card:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

/* --- Plan builder ----------------------------------------------------------- */

.builder { gap: 48px; }

.builder-step { margin-bottom: 56px; }

.builder-step-head {
  gap: 12px;
  margin-bottom: 18px;
}

.builder-step-number {
  font-size: 13px;
  line-height: 14px;
  font-weight: 500;
}

.builder-step-head h2 { line-height: 26px; }

/* The small print beside a step head and under a step. `.text-muted` was carrying
   this at 15px, a full two steps larger than his 13px. */
.builder-note {
  font-size: 13px;
  line-height: 20px;
  color: var(--color-neutral-500);
  margin: 20px 0 0;
}

.builder-step-head .builder-note { margin: 0; }

.builder-note code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  color: var(--color-neutral-300);
}

/* The over-cap notice. Accent rather than a red: picking three leagues on the free
   tier is a constraint of the tier, not a mistake the customer made, and this system
   has no danger token to borrow. */
.builder-warn {
  color: var(--color-text);
  border-left: 2px solid var(--color-accent);
  padding-left: 12px;
  margin-top: 14px;
}

.builder-warn[hidden] { display: none; }

/* The no-JS recompute. plan_builder.js removes the node outright, so this styles
   what only a scriptless reader sees. */
.builder-recalc { margin-top: 10px; }

.builder-summary {
  flex: 0 1 330px;
  min-width: 290px;
  top: 88px;
}

/* The vendored `.card` is a general-purpose surface; his summary panel is a
   specific one. */
.builder-summary .card,
.account-client {
  padding: 26px 24px 28px;
  gap: 0;
}

.builder-summary .card-kicker,
.account-client .card-kicker {
  font-size: 12px;
  line-height: 14px;
  letter-spacing: 0.06em;
  color: var(--color-neutral-500);
  margin-bottom: 20px;
}

.builder-summary dl { margin: 0; }

/* Rules sit ABOVE each row, not below. The difference is visible at the end of the
   list: his has no rule between the last row and the button; ours drew one. */
.summary-row {
  border-bottom: 0;
  border-top: 1px solid var(--color-neutral-800);
  font-size: 13.5px;
  line-height: 20px;
  padding: 11px 0;
}

.builder-summary .stack-actions { margin-top: 24px; }

.builder-summary .card-meta {
  display: block;
  text-align: center;
  font-size: 12.5px;
  line-height: 20px;
  color: var(--color-neutral-500);
  margin-top: 14px;
}

/* --- Hero sample panel ------------------------------------------------------ */

/* Base colour carries the separators. His colons and commas are unwrapped, so they
   inherit neutral-400; ours wrapped them in the brace colour, one step darker. */
.sample-body {
  padding: 20px 22px;
  font-size: 13px;
  line-height: 22px;
  color: var(--color-neutral-400);
}

/* --- Getting started -------------------------------------------------------- */

/* His third landing section. Padding is the inverse of the feature list above it
   (56/88 against 88/56), and its kicker sits 14px above the heading rather than the
   feed kicker's 40px. */
.shell-steps { padding: 56px clamp(20px, 5vw, 72px) 88px; }

.shell-steps .kicker { margin-bottom: 14px; }

.steps-heading {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 32px;
  line-height: 42px;
  letter-spacing: -0.012em;
  margin: 0 0 40px;
}

.steps-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
  gap: 20px;
}

/* The vendored `.card` is a general-purpose surface again, so the step cards get
   their own metrics the same way the plan summary does. */
.steps-grid .card {
  padding: 26px 24px 28px;
  gap: 0;
}

.steps-grid .card-kicker {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 13px;
  line-height: 14px;
  letter-spacing: normal;
  text-transform: none;
  color: var(--color-accent);
  margin: 0 0 18px;
  font-feature-settings: 'tnum' 1;
}

.steps-grid .card-title {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 18px;
  line-height: 26px;
  margin: 0 0 10px;
}

.steps-grid .card-body {
  font-size: 14.5px;
  line-height: 24px;
  color: color-mix(in srgb, var(--color-text) 76%, transparent);
  margin: 0;
}

.steps-actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 36px;
}

/* --- Fidelity pass 3 -------------------------------------------------------- */

/* The tier price. The loudest element on a call-limit card in the design, and it
   was absent entirely — a pricing page whose price cards showed no price. */
/* Hidden explicitly, on the same rule as .builder-warn: the span is always rendered
   so plan_builder.js has a node to fill, and the class must not out-specify the UA's
   [hidden] the day it gains a `display`. */
.choice-price[hidden] { display: none; }

.choice-price {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 22px;
  line-height: 30px;
  margin-bottom: 12px;
  font-feature-settings: 'tnum' 1;
}

/* Stacked detail lines, not one joined string. */
.choice-details {
  display: flex;
  flex-direction: column;
  font-size: 13px;
  line-height: 20px;
  color: color-mix(in srgb, var(--color-text) 74%, transparent);
}

/* A detail line for something we do not serve yet. It keeps its place and its
   wording so the roadmap survives, and it is dimmed and tagged so it cannot be read
   as a commitment. The tag is what carries the meaning; the dimming only stops the
   unavailable lines outweighing the available ones on the same card. */
/* INLINE, not flex. As a flex row the tag became a sibling of the whole label, so
   "50,000 calls / month" wrapped to two lines with the tag floating vertically
   centred beside the pair. Inline flow puts it after the last word and lets it wrap
   like any other word. */
.choice-detail { display: inline; }

.choice-detail.is-soon { color: var(--color-neutral-500); }

/* What a package will cover but does not yet. After the note, so it does not split
   the description from the line that qualifies it. */
.choice-soon {
  display: block;
  margin-top: 10px;
  font-size: 12.5px;
  line-height: 18px;
  color: var(--color-neutral-500);
}

/* One rule for every "Soon" marker on the page, so the plan cards and the overage
   note cannot drift into different-looking tags. Small and quiet on purpose: three
   of these on one card is common, and at full tag weight they shouted over the
   feature they were qualifying. */
.tag-soon {
  display: inline-block;
  vertical-align: baseline;
  margin-left: 6px;
  font-size: 9.5px;
  line-height: 14px;
  padding: 0 5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-neutral-400);
  border-color: color-mix(in srgb, var(--color-text) 22%, transparent);
}

/* Leading the sentence it qualifies, so it needs the space on the other side. */
.builder-note .tag-soon { margin: 0 6px 0 0; }

/* A tier card leads with its price, so its title is smaller than a package card's
   17px — the design sets it at 16/24. */
.choice-card:has(.choice-price) .choice-label {
  font-size: 16px;
  line-height: 24px;
  margin-bottom: 10px;
}

/* The current nav item is accent. This did NOT work: the earlier rule set the idle
   colour at the same specificity as the vendored
   `.nav a:hover, .nav a[aria-current='page']`, and app.css loads second, so ours won
   on every page and the highlight never fired once. The comment there claimed the
   vendored rules "still out-specify this"; they do not. Excluding both states from
   the idle rule is what actually leaves them to the system. */
.site-nav a:not(.btn):not([aria-current="page"]):not(:hover) {
  color: var(--color-neutral-400);
}

/* Footer links are part of the footer's quiet gray, not the brightest thing in it.
   Ours rendered a purple underlined link where his is muted text. */
.site-footer a {
  color: inherit;
  text-decoration: none;
}

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

/* --- Auth pages ------------------------------------------------------------- */

/* THE BUG BEHIND THE WHITE PILL. The vendored sheet never resets `appearance`
   (grep it: zero hits), and `button_to` is the one place a `.btn` lands on a bare
   <button>. With `appearance: auto` the paint is UA-dependent, so macOS drew the
   Google button as a native white pill with black text — the brightest thing on a
   page in a system that never fills anything near-white. Computed styles said
   "transparent background, light text" the whole time, which is why three property
   audits scored it clean. */
.btn { appearance: none; }

/* Jeff drew no auth screen, so these are composed from what he DID draw:
     - the header pattern from his Docs view (kicker, heading, one line of support
       copy, then the control, flush-left on the page ground)
     - the form's container from his YOUR PLAN card (330px, 26/24/28, hairline ring
       plus ambient shadow, a full-width outlined CTA, a centred 12.5px meta line)
     - the utility-page heading scale from his Dashboard "Overview" (30px/500),
       not the 42px marketing scale the auth pages were using
   A CENTRED card was the obvious alternative and it is wrong here: it would be the
   only centred composition in the product, against a readme that says "left-aligned,
   asymmetric layouts, content hugs the left edge with whitespace on the right". */
.auth-title {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 30px;
  line-height: 38px;
  letter-spacing: -0.012em;
  margin: 0 0 10px;
}

.auth-lede {
  font-size: 15px;
  line-height: 24px;
  color: color-mix(in srgb, var(--color-text) 78%, transparent);
  margin: 0 0 32px;
  max-width: 46ch;
}

.auth-card {
  max-width: 330px;
  padding: 26px 24px 28px;
  gap: 0;
}

.auth-card .card-kicker { display: none; }

/* His card puts 24px between the last row and the CTA. Inside a `.stack` the
   parent's own gap already supplies part of that, so only the remainder is added —
   a flat 24 here stacked on top of the gap and rendered 41. */
.auth-card .stack > .stack-actions { margin-top: calc(24px - var(--space-6)); }

/* The OAuth form is a sibling of the main form, so it needs its own rhythm. */
.auth-card form + form { margin-top: 12px; }

/* `display: block`, not the vendored flex. `.card-meta` is a flex container with a
   6px gap, so a sentence containing a link became three flex items and rendered
   "Create an account ." with the period floating away from the word. A prose line
   needs to be prose. */
.auth-card .card-meta {
  display: block;
  text-align: center;
  font-size: 12.5px;
  line-height: 20px;
  color: var(--color-neutral-500);
  margin-top: 14px;
}

/* "Forgot password?", under the button it belongs to. It used to sit outside the
   card entirely, where it read as a stray link the layout had dropped. */
.auth-aside {
  font-size: 13px;
  line-height: 20px;
  text-align: center;
  margin: 12px 0 0;
}

.auth-aside a { color: var(--color-neutral-500); }
.auth-aside a:hover { color: var(--color-text); }

/* The seam between the credential form and the OAuth button. Without it the two
   stacked as equals and the card asked the reader to compare two full-width buttons
   with nothing saying they are alternatives. */
.auth-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 18px 0 14px;
  font-size: 11.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-neutral-600);
}

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

/* Nocturne's primary action is an accent OUTLINE, so its label carries the accent
   and the secondary's carries full-strength text. At the same weight and width that
   inverts the hierarchy: white out-shouts purple, and on the sign-in card the Google
   button read as the main action. Quieting the secondary's LABEL restores the order
   without filling the primary, which is the rule the system is built on. */
.auth-card .btn-secondary { color: var(--color-neutral-300); }
.auth-card .btn-secondary:hover { color: var(--color-text); }

/* Auth is a utility page, so it opens tighter than a marketing one, and its kicker
   takes the close 14px pairing rather than the feed's 40px.

   TWO COLUMNS: copy left, card right. It was one flush-left column, which put a 330px
   card against roughly three-quarters of an empty viewport and read as a layout that
   had failed rather than as a composition. That came from applying the readme's
   "content hugs the left edge with whitespace on the right" to this page. The sentence
   describes flush-left HEADINGS over content wide enough to hold a column; it was
   never about a page whose entire content is one small card, and Jeff drew no auth
   screen to settle it either way. The shape used here is his own landing hero — copy
   on the left, panel on the right — which keeps the flush-left rule and fills the
   page. Doug's call, 2026-08-06, off the demo recording. */
.shell-auth {
  display: grid;
  /* BOTH TRACKS FIXED, and the leftover falls to the RIGHT of the card. A greedy
     `1fr` left track was the first attempt and it looked worse than what it replaced:
     the copy stayed at the left edge, the card was pushed to the far right, and ~360px
     of dead middle opened between them, because the lede is measure-bound at 46ch and
     never fills a 774px cell. Jeff's landing hero survives a full-width split only
     because its headline is 3 lines at 42px. 420px is that same 46ch measure, so the
     pair sits together and the whitespace lands on the right — which is the readme's
     sentence rendered literally, rather than the hole the greedy track produced.
     The right track matches .auth-card's own max-width so the card fills its cell. */
  grid-template-columns: minmax(0, 420px) minmax(0, 330px);
  justify-content: start;
  gap: clamp(32px, 6vw, 96px);
  align-items: start;
  padding: 64px clamp(20px, 5vw, 72px) 96px;
}

.shell-auth .kicker { margin-bottom: 14px; }

/* The lede already carries max-width: 46ch, so the column is measure-bound, not
   track-bound: it does not stretch into a long line on a wide screen. */
.auth-intro { min-width: 0; }

/* ── the account area ─────────────────────────────────────────────────────────
   Jeff's dashboard: a fixed sidebar on the left, content on the right. The rest of
   the product hugs the left edge with whitespace on the right; this screen is the
   one his design gives a second column, because a signed-in tool needs its own
   navigation.

   Only the API-keys screen is built. Overview, Usage and Subscription are every
   number the app does not record (calls this month, quota consumed, rate-limit
   hits, median latency, busiest endpoints), so they are named in the sidebar and
   marked rather than rendered from invented data. */
.account-shell {
  display: flex;
  align-items: flex-start;
  gap: clamp(24px, 4vw, 56px);
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 40px clamp(20px, 5vw, 72px) 96px;
}

.account-nav {
  flex: 0 0 190px;
  position: sticky;
  top: 40px;
  padding-right: 20px;
  border-right: 1px solid var(--color-divider);
}

.account-nav .kicker { display: block; margin-bottom: 14px; }

.account-nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.account-nav-link,
.account-nav-soon {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  border-radius: var(--radius-sm, 8px);
  font-size: 14px;
  line-height: 20px;
  text-decoration: none;
}

.account-nav-link {
  color: var(--color-neutral-300);
}

.account-nav-link:hover {
  color: var(--color-text);
  background: color-mix(in srgb, var(--color-text) 5%, transparent);
}

/* The current screen, in the system's own accent + raised ground. */
.account-nav-link[aria-current="page"] {
  color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 12%, transparent);
}

/* Not a link, and it should not look like one — no pointer, no hover. */
.account-nav-soon {
  color: var(--color-neutral-600);
  cursor: default;
}

.account-main {
  flex: 1 1 auto;
  min-width: 0;
}

.account-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

/* His Dashboard heading scale (30/38), not the 42px marketing one: this is a tool,
   not a landing page. */
.account-title {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 30px;
  line-height: 38px;
  letter-spacing: -0.012em;
  margin: 0;
}

.account-meta {
  font-size: 13px;
  line-height: 20px;
  color: var(--color-neutral-500);
}

.account-lede {
  font-size: 14px;
  line-height: 23px;
  color: color-mix(in srgb, var(--color-text) 74%, transparent);
  margin: 0 0 26px;
  max-width: 66ch;
}

.account-lede code,
.account-empty code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  color: var(--color-neutral-300);
}

/* Spans the content column, like the keys table under it. At max-width: 380px the
   card stopped less than halfway across while the table below ran the full width, so
   the page changed width halfway down and read as ragged rather than as a column. */
.account-client { margin-bottom: 34px; }

/* An email is long and has no spaces the browser will break on. Without this the
   value ran to the card's edge and, on a narrower card, straight out of it. */
.account-client .summary-row dd { overflow-wrap: anywhere; }

/* The stacked default, which is also what the card falls back to on a narrow screen.
   The three-across band below is a min-width exception layered on top. */
.account-client-rows { display: flex; flex-direction: column; }

/* Three across, not three stacked: the same band the Overview already uses for its
   three figures, which is what lets the card fill the content column without
   stretching three short rows over 800px of rule.

   A MIN-WIDTH query, against this file's max-width convention, and deliberately. The
   band has to strip `.summary-row`'s rules, and those come from BOTH stylesheets —
   app.css sets border-bottom, the vendored sheet sets border-top. Written as a
   max-width override it has to put back a vendored value nobody here owns; the first
   attempt zeroed only the bottom and left three broken hairlines across the card.
   Layering the exception on means the fallback is the untouched original. */
@media (min-width: 901px) {
  .account-client-rows {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(20px, 4vw, 56px);
  }

  /* Label over value. `.summary-row`'s shared space-between is right for the plan
     builder, where a row IS one wide line, and wrong for a figure in a band. */
  .account-client-rows .summary-row {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    padding: 0;
    border: 0;
  }

  .account-client-rows .summary-row dd { text-align: left; }
}

.account-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.account-section-head h2 {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 18px;
  line-height: 26px;
  margin: 0;
}

/* Two buttons in a cell, each its own form (button_to), so they need to sit on one
   line rather than stacking as block-level forms. */
.account-key-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

.account-key-actions form { margin: 0; }

.btn-sm {
  padding: 5px 11px;
  font-size: 12.5px;
  line-height: 18px;
}

.cell-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
}

.account-empty {
  font-size: 14px;
  line-height: 23px;
  color: var(--color-neutral-500);
  margin: 0;
}

/* The one screen the raw token appears on. Loud on purpose: it is shown once and
   cannot be recovered. */
/* No lede sits between the heading and this card, unlike the key list, so it
   carries its own top margin. */
.token-card {
  max-width: 560px;
  margin-top: 22px;
}

.token-reveal {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 15px;
  line-height: 24px;
  word-break: break-all;
  padding: 14px 16px;
  border-radius: var(--radius-sm, 8px);
  border: 1px solid var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 10%, transparent);
  color: var(--color-text);
}

.token-warn {
  font-size: 13px;
  line-height: 21px;
  color: var(--color-neutral-400);
  margin: 14px 0 0;
}

/* The sidebar becomes a row above the content rather than a squeezed column. */
@media (max-width: 720px) {
  /* `align-items: stretch`, not the flex-start the desktop layout uses. Once the
     shell turns into a COLUMN the cross axis is horizontal, so flex-start sizes
     each child to its own content — and the widest child is the key table, which
     dragged the whole page to 581px inside a 375px viewport and made the body
     scroll sideways. */
  .account-shell {
    flex-direction: column;
    align-items: stretch;
    gap: 24px;
  }

  .account-nav {
    flex: none;
    width: 100%;
    position: static;
    padding-right: 0;
    padding-bottom: 16px;
    border-right: 0;
    border-bottom: 1px solid var(--color-divider);
  }

  .account-nav ul { flex-direction: row; flex-wrap: wrap; gap: 4px; }
}

/* ── the account Overview (Jeff's dashboard, view 4) ──────────────────────────
   His layout: a wide usage card on the left, a stacked pair on the right, three
   stat tiles under them, then tables. Rebuilt with only the figures we record —
   see Usage::Summary for which those are and why the rest render N/A. */
.overview-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  /* `start`, not the default stretch. The usage card is shorter than the two
     stacked cards beside it, and stretching it left a third of the card empty
     below the chart. */
  align-items: start;
  gap: 18px;
  margin-bottom: 18px;
}

.overview-side {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.overview-grid .card { padding: 22px 24px 24px; gap: 0; }

.overview-grid .card-kicker,
.account-section-title .card-kicker {
  font-size: 11px;
  line-height: 14px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-neutral-500);
  margin-bottom: 14px;
}

/* His 40/48 figure. Tabular numerals so a changing count does not reflow. */
.overview-figure {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 40px;
  line-height: 48px;
  margin: 0;
  font-feature-settings: 'tnum' 1;
}

.overview-plan {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 22px;
  line-height: 30px;
  margin: 0;
}

.overview-sub {
  font-size: 13px;
  line-height: 20px;
  color: var(--color-neutral-500);
  margin: 6px 0 18px;
}

.overview-key {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 14px;
  line-height: 22px;
  padding: 10px 12px;
  border-radius: var(--radius-sm, 8px);
  border: 1px solid var(--color-divider);
  background: color-mix(in srgb, var(--color-text) 4%, transparent);
  overflow-wrap: anywhere;
}

.overview-key-absent { color: var(--color-neutral-500); }

.overview-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: 18px;
  margin-bottom: 34px;
}

.overview-tiles .stat {
  padding: 20px 22px 22px;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}

/* The tiles reuse StatComponent, whose landing-page scale is 48px. That is a
   marketing size; inside a card it overpowers the label. */
.overview-tiles .stat-value { font-size: 28px; line-height: 36px; margin-left: 0; }
.overview-tiles .stat-label { margin-top: 10px; font-size: 11px; }

/* Follows a table or a tile row, so it carries the separation itself rather than
   colliding with whatever sits above it. */
.account-section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 34px;
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: 18px;
  line-height: 26px;
  margin-bottom: 14px;
}

/* ── the meter ────────────────────────────────────────────────────────────────
   A track plus a fill. The EMPTY state is the whole reason this is a component:
   a fill at 0% width and a value we do not have look identical, and only one of
   them is a fact. Unknown gets a dashed track and no fill at all. */
.meter {
  /* The initial value for the proportion MeterComponent writes inline. Declared here
     rather than living only as the `var(--fill, 0%)` fallback further down, so the
     property has one owner and a reader can find where it comes from. A meter with no
     fill is 0%, and the component renders `.meter-empty` (below) for the case where
     the proportion is UNKNOWN — the two are different states and only one is 0. */
  --fill: 0%;
  display: block;
  width: 100%;
  height: 6px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--color-text) 10%, transparent);
  overflow: hidden;
}

/* The component writes `--fill` rather than a width, so the axis is the
   stylesheet's decision: horizontal here, vertical on a chart bar below. */
.meter-fill {
  display: block;
  width: var(--fill, 0%);
  height: 100%;
  border-radius: inherit;
  background: var(--color-accent);
}

/* Not a zero-width bar. A dashed, hollow track reads as "no reading", which is
   what it is. */
.meter-empty {
  background: transparent;
  border: 1px dashed color-mix(in srgb, var(--color-text) 18%, transparent);
}

/* ── the 14-day chart ─────────────────────────────────────────────────────────
   Bars are meters stood on end: the slot is the full height and the meter is
   rotated so its fill grows from the bottom. Reusing the primitive means a day we
   cannot account for gets the same hollow treatment as an unknown quota, for free. */
.usage-chart { margin-top: 22px; }

.usage-bars {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  height: 92px;
}

.usage-bar-slot {
  flex: 1 1 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  min-width: 0;
}

/* The meter turned vertical: full width of its slot, height driven by the fill. */
.usage-bar {
  width: 100%;
  height: 100%;
  border-radius: var(--radius-sm, 6px);
  display: flex;
  align-items: flex-end;
  background: color-mix(in srgb, var(--color-text) 7%, transparent);
}

/* An unknown day. The dashed outline is load-bearing, not decoration: a KNOWN zero
   renders a fill of height 0, which is invisible, so without the dash an unknown
   day and an idle day would draw the identical empty column. The track behind it is
   dropped so the dash is the only thing the eye reads. */
.usage-bar.meter-empty {
  background: transparent;
  border-color: color-mix(in srgb, var(--color-text) 13%, transparent);
}

.usage-bar .meter-fill {
  width: 100%;
  /* Same `--fill`, other axis. */
  height: var(--fill, 0%);
  align-self: flex-end;
  background: var(--color-accent);
}

.usage-chart-caption,
.usage-chart-empty {
  font-size: 12px;
  line-height: 18px;
  color: var(--color-neutral-500);
  margin: 10px 0 0;
}

@media (max-width: 900px) {
  .overview-grid { grid-template-columns: minmax(0, 1fr); }

  /* One column again, card under the copy. Below this width there is no whitespace
     left to use, and a 330px track beside a shrinking measure squeezes the heading
     rather than balancing it. */
  .shell-auth {
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
  }

}
