/* ============================================================================
   LANDING ARCHETYPES
   ----------------------------------------------------------------------------
   WHY THIS FILE EXISTS

   The landing page ran seventeen sections through one unit: eyebrow, two-tone
   headline, three-line paragraph, row of three or four cards. Read the words and
   it is fine; look at the silhouette and the whole page is a single shape
   repeated until it ends. That is what reads as bland, and no amount of colour
   or spacing fixes a rhythm problem.

   This file supplies SIX section archetypes so a section's shape is chosen by
   what its content actually is. The card grid survives as one of the six rather
   than being the default for everything.

     .lx-proof     full-bleed visual, caption over it   (a result worth seeing)
     .lx-split     text one side, held visual the other (3+ paragraphs)
     .lx-data      wide aligned table                    (numbers to compare)
     .lx-diptych   two panes, hard split                 (a real before/after)
     .lx-note      margin column + body                  (caveats and scope)
     .lx-qa        answer-first disclosure list          (questions)

   A useful side effect: a split with a held visual physically cannot hold a
   260-word answer, so the copy shrinks as a consequence of the layout rather
   than needing a separate editing pass.

   ----------------------------------------------------------------------------
   TOKENS

   Colour, spacing and radii are flux (css/flux/flux.css): --surface-*, --text-*,
   --border, --accent, the --g* spacing scale and --r-* radii. No literal colours,
   so the visitor's saved accent hue and theme keep working. The TYPE scale is the
   one exception, for the reason set out immediately below.

   Note also that --g* lives under [data-density="..."] and this page sets no
   data-density attribute, so those tokens are undefined here and the literal
   fallbacks are what actually apply. They are the "normal" preset values.

   Flux defines its palette under [data-theme="light"|"dark"], NOT on :root, so
   every var() below carries a literal fallback. This is not defensive padding -
   an undefined var() resolves to nothing and the browser reverts to its own
   default, which is how --ff-sans going missing rendered 213 pages in Times.
   The fallbacks are the light-theme values, matching the theme bootstrap in
   index.html which sets data-theme="light" before first paint.

   Load AFTER flux.css and theme-bridge.css, BEFORE nothing else that styles
   .landing-*. Every selector is prefixed .lx- so it cannot collide with the
   existing .landing-* rules in app.css and landing-pages.css during a
   section-by-section migration.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   LOCAL TYPE SCALE - and why it is not flux's

   flux.css declares its scale as e.g.
       --t-xl: clamp(1.8rem,1.4rem+1.5vw,2.6rem);
   CSS math functions require WHITESPACE around + and -, because "+1.5vw" would
   otherwise parse as a signed number. Without the spaces the whole clamp() is
   invalid, the font-size declaration is dropped, and the element keeps its
   inherited size. Measured in Chrome on this page:

       clamp(1.8rem,1.4rem+1.5vw,2.6rem)     -> 16px   (invalid, dropped)
       clamp(1.8rem, 1.4rem + 1.5vw, 2.6rem) -> 41.6px (correct)

   All eight --t-* tokens carry the defect, including --t-base, which flux's own
   `body { font-size: var(--t-base) }` depends on - so body text across every
   flux page is falling back to the browser default rather than the design scale.

   A var() FALLBACK cannot rescue this. Fallbacks fire when a custom property is
   undefined; --t-xl is very much defined, just to a value that fails to parse.
   That is why the section headings rendered at 16px despite carrying fallbacks.

   Fixing flux.css itself is the real repair, but it changes computed type on
   every page that loads flux, so it is not something to slip into a landing
   redesign. This file therefore carries its own corrected scale, same intent and
   same ratios, valid syntax. If flux is fixed later these can simply alias it.
   -------------------------------------------------------------------------- */
:root {
  --lx-t-xs:   clamp(0.72rem, 0.68rem + 0.2vw, 0.78rem);
  --lx-t-sm:   clamp(0.82rem, 0.76rem + 0.3vw, 0.92rem);
  --lx-t-base: clamp(0.95rem, 0.90rem + 0.25vw, 1.06rem);
  --lx-t-md:   clamp(1.12rem, 1.00rem + 0.5vw, 1.32rem);
  --lx-t-lg:   clamp(1.40rem, 1.20rem + 0.9vw, 1.85rem);
  --lx-t-xl:   clamp(1.80rem, 1.40rem + 1.5vw, 2.60rem);
  --lx-t-2xl:  clamp(2.20rem, 1.70rem + 2.2vw, 3.60rem);
}

/* ── shared section frame ─────────────────────────────────────────────────── */

.lx {
  /* 4.5rem, not the --g9 6rem this started at. Section padding stacks: 6rem
     bottom against 6rem top put TWELVE rem of nothing between every pair of
     sections, which on a 1080-tall viewport is a quarter of a screen of blank
     between "The short version" and the section after it. The alternating ground
     is already doing the separating work, so the padding does not have to.
     Measured on the rebuilt page, not guessed. */
  padding: 4.5rem var(--g5, 1.5rem);
  background: var(--surface-0, #fdfdfe);
}
/* Two same-ground sections in a row have no colour change to mark the seam, so
   they keep a hairline. Adjacent alternating sections do not need one. */
.lx + .lx:not(.lx-alt) { border-top: 1px solid var(--border-subtle, #e9ecf3); }
.lx-alt + .lx-alt { border-top: 1px solid var(--border-subtle, #e9ecf3); }
/* Alternating ground is what separates one section from the next now that they
   are no longer all the same shape. Applied by nth-of-type at the container
   level rather than by hand per section, so inserting a section cannot break
   the alternation. */
.lx-alt { background: var(--surface-1, #f4f5f8); }
.lx-dark {
  background: var(--surface-0, #0c0f18);
  /* Dark bands are used for VISUAL sections only (renders, plots, terminals),
     where a light ground would wash the output out. Forced dark regardless of
     theme because the renders themselves are dark-on-dark. */
  color-scheme: dark;
}
/* CENTRED CONTAINERS, Bootstrap-style. Every constrained block gets auto inline
   margins so it sits in the middle of the section rather than hugging the left
   edge of a centred wrapper - which is what a max-width with no auto margin
   actually does, and why the FAQ list and the intro copy read as left-shifted.

   Section HEADERS are centred as text too, matching the existing hero. Long-form
   body copy inside the archetypes stays left-aligned: centred ragged text is
   materially harder to read past a couple of lines, and every archetype below is
   built to hold paragraphs. Centred block, left-aligned prose. */
.lx-inner { max-width: 1200px; margin-inline: auto; }
.lx-narrow { max-width: 780px; margin-inline: auto; }

.lx-head { text-align: center; margin-inline: auto; margin-bottom: var(--g7, 3rem); }

.lx-eyebrow {
  font-size: var(--lx-t-xs);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--accent, #0078d4);
  margin-bottom: var(--g3, .75rem);
  text-align: center;
}
.lx-h2 {
  font-size: var(--lx-t-xl);
  line-height: 1.15;
  letter-spacing: -.02em;
  font-weight: 600;
  color: var(--text-primary, #1a1f2e);
  text-wrap: balance;
  margin-bottom: var(--g3, .75rem);
  text-align: center;
}
/* The two-tone headline is the page's one real signature. Kept, but it now
   appears on fewer sections, which is what makes it read as emphasis again. */
.lx-h2 em { font-style: normal; color: var(--accent, #0078d4); }
.lx-sub {
  font-size: var(--lx-t-md);
  color: var(--text-secondary, #4a5568);
  max-width: 68ch;
  margin-inline: auto;
  margin-bottom: var(--g7, 3rem);
  text-align: center;
  /* balance, not pretty: pretty only protects the LAST line from an orphan, and at
     this measure a two-line sub kept splitting 90/10 and stranding a word or two on
     its own row. balance evens the lines instead. */
  text-wrap: balance;
}
/* Consecutive subs are one lede, not two sections.
   The 3rem bottom margin is right when a sub is followed by cards or a table, and
   wrong when it is followed by ANOTHER sub - there it becomes a 48px canyon and
   three consecutive sentences read as three unrelated blocks (worst on Access
   Tiers, which runs three of them in a row). So the big gap is kept for the last
   sub in a run, and only the ones with a sub right after them pull tight.
   Browsers without :has() fall back to the old spacing, which is loose, not broken. */
.lx-sub:has(+ .lx-sub) { margin-bottom: var(--g2, .5rem); }

.lx-body { color: var(--text-secondary, #4a5568); max-width: 62ch; }
.lx-body + .lx-body { margin-top: var(--g4, 1rem); }

/* Inside a split the header is part of a column, so it must NOT be centred -
   it would fight the paragraph beneath it. Scoped override rather than a
   separate class, so the archetype markup stays uniform. */
.lx-split .lx-eyebrow,
.lx-split .lx-h2,
.lx-split .lx-sub { text-align: left; margin-inline: 0; }

/* ── 01 · PROOF · full-bleed visual ───────────────────────────────────────── */

/* Full width, no competing text. For output this product can generate and a
   screenshot cannot fake. The caption sits ON the visual so the visual is never
   reduced to an illustration of a paragraph beside it. */
.lx-proof { padding: 0; position: relative; background: #080b14; }
.lx-proof > canvas,
.lx-proof > img,
.lx-proof > video {
  display: block;
  width: 100%;
  height: clamp(240px, 38vw, 460px);
  object-fit: cover;
}
.lx-proof-cap {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--g7, 3rem) var(--g5, 1.5rem) var(--g5, 1.5rem);
  background: linear-gradient(to top, rgba(8, 11, 20, .94), rgba(8, 11, 20, 0));
  color: #eef2fa;
}
.lx-proof-cap-inner { max-width: 1200px; margin: 0 auto; }
.lx-proof-cap b {
  display: block;
  font-size: var(--lx-t-md);
  font-weight: 600;
  margin-bottom: var(--g1, .25rem);
}
.lx-proof-cap span { font-size: var(--lx-t-sm); color: #a7b6cd; }

/* ── 02 · SPLIT · text + held visual ──────────────────────────────────────── */

.lx-split {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: var(--g8, 4rem);
  align-items: center;
}
/* Reversing by nth-child on the parent means the zig-zag survives a section
   being inserted or removed, which hand-applied .reverse classes never do. */
.lx-split-r { grid-template-columns: 1fr 1.05fr; }
.lx-split-r .lx-split-art { order: -1; }
.lx-split-art {
  border-radius: var(--r-lg, 14px);
  overflow: hidden;
  background: #0c1120;
  min-height: 320px;
  position: relative;
  box-shadow: 0 1px 2px oklch(var(--shadow-base, 25% .02 260) / .06),
              0 18px 48px oklch(var(--shadow-base, 25% .02 260) / .10);
}
.lx-split-art > canvas,
.lx-split-art > img,
.lx-split-art > video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Figures pulled out of the prose. Three at most: past that it is a table. */
.lx-figs { display: flex; flex-wrap: wrap; gap: var(--g6, 2rem); margin-top: var(--g5, 1.5rem); }
.lx-fig b {
  display: block;
  font-size: var(--lx-t-lg);
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--text-primary, #1a1f2e);
  font-variant-numeric: tabular-nums;
}
.lx-fig span { font-size: var(--lx-t-xs); color: var(--text-muted, #78839a); }

/* ── 03 · DATA · aligned comparison table ─────────────────────────────────── */

/* Numbers that invite comparison belong in a table. The validation figures were
   previously prose inside a collapsed FAQ row, where the one thing a reader
   wants to do - scan the deviation column - was impossible. */
.lx-data-wrap {
  margin-inline: auto;
  overflow-x: auto;                      /* the table scrolls, never the page */
  border: 1px solid var(--border, #dfe3ec);
  border-radius: var(--r-lg, 14px);
  background: var(--surface-0, #fdfdfe);
}
.lx-data {
  border-collapse: collapse;
  width: 100%;
  /* No min-width. An earlier 560px floor forced a horizontal scrollbar on the
     security table, whose second column is prose and should simply wrap. The
     .lx-data-wrap overflow-x stays as the safety net for genuinely wide tables
     (the validation figures), so the page body still never scrolls sideways. */
  font-size: var(--lx-t-sm);
}
/* Prose columns wrap; number columns do not (see td.n / td.ok below). */
.lx-data td { overflow-wrap: anywhere; }
.lx-data th,
.lx-data td {
  text-align: left;
  padding: var(--g3, .75rem) var(--g5, 1.5rem);
  border-bottom: 1px solid var(--border-subtle, #e9ecf3);
}
.lx-data th {
  font-size: var(--lx-t-xs);
  text-transform: uppercase;
  letter-spacing: .07em;
  font-weight: 700;
  color: var(--text-muted, #78839a);
  background: var(--surface-1, #f4f5f8);
}
.lx-data tr:last-child td { border-bottom: none; }
.lx-data td.n { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Semantic, not the accent: a deviation figure is a judgement about the result,
   and must not read as brand colour. */
.lx-data td.ok {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: oklch(52% .13 150);
  white-space: nowrap;
}
.lx-data td.warn { font-variant-numeric: tabular-nums; font-weight: 600; color: oklch(58% .14 60); }

/* A qualifier hung under a verdict cell.
   td.ok is nowrap on purpose - a verdict is a short phrase and must not break
   across lines. The security table needs to say "Your device only" AND explain
   what that means, and putting the explanation inside the verdict cell overflowed
   the table (the row scrolled horizontally and the sentence was unreadable).
   So the verdict keeps its colour and its nowrap, and the qualifier drops to its
   own line in muted body weight, where it wraps normally. */
.lx-data td .lx-data-note {
  display: block;
  margin-top: 3px;
  white-space: normal;
  font-weight: 400;
  font-variant-numeric: normal;
  font-size: var(--lx-t-xs);
  color: var(--text-muted, #78839a);
}

/* ── 04 · DIPTYCH · two panes, hard split ─────────────────────────────────── */

/* Already the sharpest thing on the page: a job queued on the cluster beside
   the same problem rendered locally. It argues without a sentence of copy. */
.lx-diptych {
  max-width: 1100px;
  margin-inline: auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--border, #dfe3ec);
  border: 1px solid var(--border, #dfe3ec);
  border-radius: var(--r-lg, 14px);
  overflow: hidden;
}
.lx-pane { background: #0c1120; min-width: 0; }
.lx-pane-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--g3, .75rem);
  padding: var(--g2, .5rem) var(--g4, 1rem);
  background: #131a2c;
  border-bottom: 1px solid #222c46;
  font-family: var(--ff-mono, 'JetBrains Mono', monospace);
  font-size: var(--lx-t-xs);
  color: #8fa1bd;
}
.lx-tag {
  font-weight: 700;
  letter-spacing: .06em;
  font-size: .68rem;
  padding: 2px 7px;
  border-radius: var(--r-sm, 4px);
  white-space: nowrap;
}
.lx-tag-cold { background: #3a1620; color: #ff92a3; }
.lx-tag-warm { background: #0f2c1e; color: #6ee7a8; }
.lx-pane pre {
  margin: 0;
  padding: var(--g4, 1rem);
  font-family: var(--ff-mono, 'JetBrains Mono', monospace);
  font-size: var(--lx-t-xs);
  line-height: 1.75;
  color: #8fa1bd;
  overflow-x: auto;
}
.lx-pane > canvas, .lx-pane > img { display: block; width: 100%; height: 220px; object-fit: cover; }

/* ── 05 · NOTE · margin column + body ─────────────────────────────────────── */

/* For the honest caveats. This product's credibility rests on them, and they
   were set in the same grey as the sales copy. A margin column gives a
   qualification its own place: visible, permanent, and plainly not marketing. */
.lx-note {
  max-width: 1040px;
  margin-inline: auto;
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: var(--g7, 3rem);
  align-items: start;
}
.lx-note aside {
  font-size: var(--lx-t-sm);
  color: var(--text-muted, #78839a);
  border-left: 2px solid var(--accent, #0078d4);
  padding-left: var(--g4, 1rem);
}
.lx-note aside b {
  display: block;
  font-size: var(--lx-t-xs);
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--accent, #0078d4);
  margin-bottom: var(--g2, .5rem);
}
.lx-note .lx-note-body p { color: var(--text-secondary, #4a5568); max-width: 64ch; }
.lx-note .lx-note-body p + p { margin-top: var(--g4, 1rem); }
.lx-note .lx-note-body strong { color: var(--text-primary, #1a1f2e); font-weight: 600; }

/* ── 06 · QA · answer-first disclosure ────────────────────────────────────── */

/* The collapsed row answers its own question. Eleven rows of bare questions
   tell a reader nothing until they open all eleven, which is why the FAQ read
   as a wall despite already being collapsible. */
.lx-qa-list {
  max-width: 900px;
  margin-inline: auto;
  border: 1px solid var(--border, #dfe3ec);
  border-radius: var(--r-lg, 14px);
  overflow: hidden;
  background: var(--surface-0, #fdfdfe);
}
.lx-qa { border-bottom: 1px solid var(--border-subtle, #e9ecf3); }
.lx-qa:last-child { border-bottom: none; }
.lx-qa > summary {
  list-style: none;
  cursor: pointer;
  padding: var(--g4, 1rem) calc(var(--g8, 4rem) - .5rem) var(--g4, 1rem) var(--g5, 1.5rem);
  position: relative;
}
.lx-qa > summary::-webkit-details-marker { display: none; }
.lx-qa > summary:hover { background: var(--surface-1, #f4f5f8); }
.lx-qa > summary:focus-visible { outline: 2px solid var(--accent, #0078d4); outline-offset: -2px; }
.lx-qa-q {
  font-size: var(--lx-t-base);
  font-weight: 600;
  color: var(--text-primary, #1a1f2e);
  margin-bottom: var(--g1, .25rem);
}
.lx-qa-a { font-size: var(--lx-t-sm); color: var(--text-secondary, #4a5568); }
.lx-qa-a strong { color: var(--text-primary, #1a1f2e); font-weight: 600; }
.lx-qa > summary::after {
  content: '';
  position: absolute;
  right: var(--g5, 1.5rem);
  top: 1.55rem;
  width: 8px; height: 8px;
  border-right: 2px solid var(--text-muted, #78839a);
  border-bottom: 2px solid var(--text-muted, #78839a);
  transform: rotate(45deg);
  transition: transform calc(180ms * var(--anim-speed, 1)) var(--ease, ease);
}
.lx-qa[open] > summary::after { transform: rotate(-135deg); border-color: var(--accent, #0078d4); }
.lx-qa-more {
  padding: 0 var(--g5, 1.5rem) var(--g5, 1.5rem);
  font-size: var(--lx-t-sm);
  color: var(--text-secondary, #4a5568);
}
.lx-qa-more p { max-width: 66ch; }
.lx-qa-more p + p { margin-top: var(--g3, .75rem); }

/* ── stat strip ───────────────────────────────────────────────────────────── */

.lx-stats {
  max-width: 1040px;
  margin-inline: auto;
  display: grid;
  text-align: center;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--g5, 1.5rem);
  padding: var(--g6, 2rem) 0;
  border-top: 1px solid var(--border-subtle, #e9ecf3);
  border-bottom: 1px solid var(--border-subtle, #e9ecf3);
}
.lx-stat b {
  display: block;
  font-size: var(--lx-t-lg);
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--accent, #0078d4);
  font-variant-numeric: tabular-nums;
}
.lx-stat span { display: block; font-size: var(--lx-t-xs); color: var(--text-muted, #78839a); margin-top: var(--g1, .25rem); }

/* ── responsive ───────────────────────────────────────────────────────────── */

@media (max-width: 900px) {
  .lx { padding: var(--g7, 3rem) var(--g4, 1rem); }
  /* Collapsing to one column must ALSO clear the order swap, or the reversed
     variant stacks its visual above the heading it belongs to. */
  .lx-split, .lx-split-r { grid-template-columns: 1fr; gap: var(--g5, 1.5rem); }
  .lx-split-r .lx-split-art { order: 0; }
  .lx-split-art { min-height: 240px; }
  .lx-note { grid-template-columns: 1fr; gap: var(--g4, 1rem); }
  .lx-diptych { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  .lx-qa > summary::after { transition: none; }
}

/* ── definition list ──────────────────────────────────────────────────────── */

/* Replaces stacks of h3 + paragraph pairs, which render as six equal-weight
   blocks with nothing to distinguish term from answer. Two columns on wide
   viewports so five short entries do not become five full-width bands. */
.lx-defs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--g5, 1.5rem) var(--g8, 4rem);
  max-width: 1040px;
  margin-inline: auto;
}
.lx-defs > div { padding-left: var(--g4, 1rem); border-left: 2px solid var(--border-subtle, #e9ecf3); }
.lx-defs dt {
  font-size: var(--lx-t-base);
  font-weight: 600;
  color: var(--text-primary, #1a1f2e);
  margin-bottom: var(--g2, .5rem);
}
.lx-defs dd { margin: 0; font-size: var(--lx-t-sm); color: var(--text-secondary, #4a5568); }

/* ── card grid ────────────────────────────────────────────────────────────── */

/* KEPT, as one archetype of six rather than the shape every section defaulted
   to. Legitimate when the content genuinely is a list of peers: four platform
   capabilities, four audiences, ten domains. Not legitimate for prose, which is
   what .lx-split and .lx-note are for.

   Text is LEFT-aligned inside a centred grid. The old cards centred their body
   copy, which is why four-line descriptions were hard to scan - a centred ragged
   edge on both sides gives the eye no consistent start position. */
.lx-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--g5, 1.5rem);
  max-width: 1200px;
  margin-inline: auto;
}
.lx-card {
  background: var(--surface-0, #fdfdfe);
  border: 1px solid var(--border, #dfe3ec);
  border-radius: var(--r-lg, 14px);
  padding: var(--card-pad, 1.5rem);
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: var(--g2, .5rem);
}
.lx-alt .lx-card { background: var(--surface-0, #fdfdfe); }
.lx-card-icon { font-size: 1.4rem; line-height: 1; color: var(--accent, #0078d4); margin-bottom: var(--g1, .25rem); }
.lx-card h3 {
  font-size: var(--lx-t-base);
  font-weight: 600;
  color: var(--text-primary, #1a1f2e);
  margin: 0;
  letter-spacing: -.01em;
}
.lx-card p { font-size: var(--lx-t-sm); color: var(--text-secondary, #4a5568); margin: 0; }
/* Numbered variant, ONLY where the content is a real sequence. The three-step
   funnel is; a list of four capabilities is not, and numbering it would assert
   an order that does not exist. */
.lx-card-step { counter-increment: lx-step; }
.lx-cards-steps { counter-reset: lx-step; }
.lx-card-step .lx-card-icon::before {
  content: counter(lx-step);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem; height: 1.9rem;
  border-radius: var(--r-full, 9999px);
  background: var(--accent-subtle, #e8f2fb);
  color: var(--accent, #0078d4);
  font-size: var(--lx-t-sm);
  font-weight: 700;
}

/* Centred action row under a section. */
.lx-actions { display: flex; gap: var(--g3, .75rem); flex-wrap: wrap; justify-content: center; margin-top: var(--g6, 2rem); }

/* ── domain chips ─────────────────────────────────────────────────────────── */

/* Ten domains with an icon, a name and a one-line note. Denser than .lx-cards
   because each entry is a label rather than a paragraph, and a full card per
   domain would give ten items the visual weight of ten arguments. */
.lx-chips {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--g3, .75rem);
  max-width: 1100px;
  margin-inline: auto;
}
.lx-chip {
  display: flex;
  align-items: flex-start;
  gap: var(--g3, .75rem);
  padding: var(--g3, .75rem) var(--g4, 1rem);
  background: var(--surface-0, #fdfdfe);
  border: 1px solid var(--border, #dfe3ec);
  border-radius: var(--r-md, 8px);
  text-align: left;
}
.lx-chip > i { font-size: 1rem; line-height: 1.45; flex: 0 0 auto; }
.lx-chip b { display: block; font-size: var(--lx-t-sm); font-weight: 600; color: var(--text-primary, #1a1f2e); }
.lx-chip small { display: block; font-size: var(--lx-t-xs); color: var(--text-muted, #78839a); margin-top: 2px; }
/* Add-on marker. A semantic amber, deliberately not the accent, so "costs extra"
   never reads as "featured". */
.lx-chip em { font-style: normal; color: oklch(58% .14 60); font-weight: 600; }

/* ── screenshots ──────────────────────────────────────────────────────────── */

/* Replaces a per-section <style> block that redefined borders, shadows and a
   mono font with its own hard-coded fallbacks - several of them dark values on a
   light page. The chrome bar is deliberate: a browser frame says "this is the
   product running", which a bare screenshot does not. */
.lx-shots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: var(--g6, 2rem);
  max-width: 1120px;
  margin-inline: auto;
}
.lx-shots figure { margin: 0; }
.lx-shot-frame {
  border: 1px solid var(--border, #dfe3ec);
  border-radius: var(--r-lg, 14px);
  overflow: hidden;
  background: var(--surface-0, #fdfdfe);
  box-shadow: 0 1px 2px oklch(var(--shadow-base, 25% .02 260) / .06),
              0 18px 44px oklch(var(--shadow-base, 25% .02 260) / .12);
}
.lx-shot-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: var(--g2, .5rem) var(--g3, .75rem);
  background: var(--surface-1, #f4f5f8);
  border-bottom: 1px solid var(--border, #dfe3ec);
}
.lx-dot { width: 10px; height: 10px; border-radius: var(--r-full, 9999px); flex: 0 0 auto; }
.lx-shot-cap {
  font-size: var(--lx-t-xs);
  color: var(--text-muted, #78839a);
  margin-left: var(--g2, .5rem);
  font-family: var(--ff-mono, ui-monospace, monospace);
}
.lx-shot-frame img { display: block; width: 100%; height: auto; }
.lx-shots figcaption {
  font-size: var(--lx-t-sm);
  color: var(--text-secondary, #4a5568);
  margin-top: var(--g3, .75rem);
  text-align: left;
  max-width: 52ch;
}
.lx-shots figcaption strong { color: var(--text-primary, #1a1f2e); font-weight: 600; }

/* ── centring legacy blocks inside a reframed section ─────────────────────── */

/* Sections that kept their original markup (pricing tiers, the free-download
   block, the feedback form) still carry their own max-widths with no auto
   margin, so they sat hard against the left edge of a centred container. Measured
   on the rebuilt page: .landing-free-snippet is 680px wide inside a 1200px
   .lx-inner with margin-left 0.

   Auto inline margins on every direct child fixes exactly that case and is inert
   for children that already fill the container, where auto resolves to zero. */
.lx-inner > * { margin-inline: auto; }

/* Legacy action rows are flex containers that default to flex-start. Centred to
   match the archetype .lx-actions row rather than leaving two conventions on one
   page. Scoped to .lx so nothing in the application shell is touched. */
.lx .landing-free-actions,
.lx .landing-tier-actions,
.lx .landing-cta-row { justify-content: center; }

/* ── proof-card evidence strips ───────────────────────────────────────────── */

/* Each of the six cards under the hero makes ONE specific, checkable claim, and
   each now shows that claim instead of only asserting it.
;
   Why not screenshots, which was the obvious idea: the cards are 389px wide, so
   an app panel scaled into one is an illegible grey rectangle, and six in a row
   is noise rather than evidence. There are also only two screenshot assets in
   the repo. A citation, a deviation, an overflow boundary and a distribution are
   all legible at this size and are the actual substance of the claims.

   Everything here is markup and CSS - no images, no canvas, no requests - so it
   costs nothing under the zero-egress policy and stays crisp at any zoom. */
/* Card shell. These five rules lived in an inline <style> in index.html, which was
   fine while the cards existed only there. They now sit inside #landing-platform,
   which the section tooling writes to BOTH index.html and app.html, and app.html
   never had the inline copy - so the cards would have rendered unstyled there.
   The evidence-strip rules below were always shared; the shell just caught up.

   Three across is the design, not a consequence of the container: an auto-fit grid
   packed six into a row the moment these left the 1200px hero wrapper, and each card
   became a column of cramped 13px text. */
.proof-grid {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(3, 1fr);
  margin: var(--g5, 1.5rem) 0 var(--g7, 3rem);
}
@media (max-width: 900px) { .proof-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .proof-grid { grid-template-columns: 1fr; } }

/* LEFT-ALIGNED, and no accent rail. The grid sits inside a centring parent, so the
   card copy was centred while the evidence strip under it was left-aligned: two
   alignments per card, and a centred ragged edge gives the eye no consistent place
   to start each line.

   The navy/red left rails went for the same reason the red hairline came off the
   nav - a saturated accent repeated six times competes with the content instead of
   marking it. A flat border with a hover lift lets the mono evidence strip be the
   only coloured thing in the card, which is the part worth looking at. */
.proof-card {
  background: var(--surface-0, #fff);
  border: 1px solid var(--border, #e2e8f0);
  border-radius: 10px;
  padding: 16px 18px;
  min-width: 0;
  text-align: left;
  transition: box-shadow .18s ease, border-color .18s ease, transform .18s ease;
}
.proof-card:hover {
  border-color: var(--border-strong, #cbd5e1);
  box-shadow: 0 6px 18px rgba(15, 23, 42, .07);
  transform: translateY(-2px);
}
@media (prefers-reduced-motion: reduce) {
  .proof-card, .proof-card:hover { transition: none; transform: none; }
}
.proof-h {
  text-align: left;
  margin: 0 0 7px;
  font-size: 15px;
  line-height: 1.35;
  color: #233061;
  font-weight: 700;
  overflow-wrap: anywhere;
}
.proof-b {
  text-align: left;
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-secondary, #475569);
}

.proof-ev {
  margin-top: auto;
  padding-top: var(--g3, .75rem);
  border-top: 1px dashed var(--border, #dfe3ec);
  font-family: var(--ff-mono, ui-monospace, monospace);
  font-size: 11.5px;
  line-height: 1.7;
  text-align: left;
  color: var(--text-secondary, #4a5568);
}
/* The cards centre their body copy; the evidence strip must not inherit that -
   aligned figures are the entire point of showing them. */
.proof-ev * { text-align: left; }
.proof-ev-row { display: flex; justify-content: space-between; gap: var(--g3, .75rem); }
.proof-ev-row + .proof-ev-row { margin-top: 2px; }
.proof-ev-k { color: var(--text-muted, #78839a); }
.proof-ev-v { font-variant-numeric: tabular-nums; color: var(--text-primary, #1a1f2e); font-weight: 600; }
.proof-ev-ok { color: oklch(52% .13 150); font-weight: 700; }
.proof-ev-bad { color: oklch(55% .17 25); font-weight: 700; }
.proof-ev-cite {
  display: inline-block;
  background: var(--accent-subtle, #e8f2fb);
  color: var(--accent, #0078d4);
  border-radius: var(--r-sm, 4px);
  padding: 1px 6px;
  font-weight: 600;
}
/* Benford bars. Pure CSS: ten spans whose heights ARE the expected first-digit
   distribution, so the picture is the law rather than a decoration of it. */
.proof-ev-bars { display: flex; align-items: flex-end; gap: 3px; height: 34px; margin-top: 4px; }
.proof-ev-bars i {
  flex: 1;
  background: var(--accent, #0078d4);
  opacity: .75;
  border-radius: 1px 1px 0 0;
  display: block;
}
.proof-ev-bars i.off { background: oklch(55% .17 25); opacity: .9; }
.proof-ev-scale { display: flex; gap: 3px; margin-top: 2px; }
.proof-ev-scale span { flex: 1; text-align: center; font-size: 9px; color: var(--text-muted, #78839a); }

/* ══ HERO BACKDROP - LIGHT ═══════════════════════════════════════════════════
   The hero sits on the light landing ground. That is the site convention and it
   stays: landing light, application panels dark.

   Its intended backdrop never rendered. The CSS art was scoped to .hero-slider,
   an element that stopped existing when the rotating slider became .hero-static,
   so the rule matched nothing and the hero has been bare white.

   This revives that original idea properly rather than replacing it. The tick
   spacing is true log10 - the axis tiles once per decade - because log space is
   where the engine actually works, which makes the backdrop domain-relevant
   rather than ornamental. It is a gradient, so it costs no request, scales to
   any width and prints cleanly.

   Contrast is deliberately low. It sits behind the largest type on the site and
   must never compete with it: at these alphas it reads as texture at a glance
   and resolves into a measured axis only if you look for it.
   ══════════════════════════════════════════════════════════════════════════ */
.hero-static {
  position: relative;
  isolation: isolate;
}
.hero-static::before {
  content: '';
  position: absolute;
  inset: -2rem -6% -1rem;
  z-index: -1;
  pointer-events: none;
  /* Decade ticks. Positions are log10(1..10) as percentages, so the gaps narrow
     toward the right exactly as a log axis does. Tinted with the accent hue
     rather than an arbitrary teal, so a visitor's saved accent carries through. */
  background-image:
    linear-gradient(90deg,
      oklch(62% .12 var(--accent-hue, 251) / .16) 0 .18%, transparent .18% 30.10%,
      oklch(62% .12 var(--accent-hue, 251) / .10) 30.10% 30.28%, transparent 30.28% 47.71%,
      oklch(62% .12 var(--accent-hue, 251) / .10) 47.71% 47.89%, transparent 47.89% 60.21%,
      oklch(62% .12 var(--accent-hue, 251) / .10) 60.21% 60.39%, transparent 60.39% 69.90%,
      oklch(62% .12 var(--accent-hue, 251) / .09) 69.90% 70.08%, transparent 70.08% 77.82%,
      oklch(62% .12 var(--accent-hue, 251) / .09) 77.82% 78.00%, transparent 78.00% 84.51%,
      oklch(62% .12 var(--accent-hue, 251) / .08) 84.51% 84.69%, transparent 84.69% 90.31%,
      oklch(62% .12 var(--accent-hue, 251) / .08) 90.31% 90.49%, transparent 90.49% 95.42%,
      oklch(62% .12 var(--accent-hue, 251) / .08) 95.42% 95.60%, transparent 95.60% 100%),
    /* A soft wash so the ticks fade out at the edges instead of stopping dead. */
    radial-gradient(120% 100% at 50% 40%,
      oklch(96% .03 var(--accent-hue, 251) / .55) 0%,
      oklch(99% .005 var(--accent-hue, 251) / 0) 70%);
  /* Ticks fade top and bottom so they never collide with the nav or the cards. */
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 68%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 68%, transparent);
}
/* The lead sits above the art; without a stacking context of its own the ticks
   can cross the descenders on the headline. */
.landing-hero > .landing-hero-inner { position: relative; z-index: 1; }
.hero-static > .hero-lead,
.hero-slider .hero-slides { position: relative; z-index: 1; }

/* ══ FOOTER - DARK ══════════════════════════════════════════════════════════
   A dark footer on a light page. Not a theme change: the footer is chrome, and
   ending the page on a solid dark band is the convention almost every product
   site uses because it closes the document instead of letting it fade out.

   Palette is VS Code Dark+ , which is the reference Joe asked for and a sane one
   - it is tuned for long looking at small text:
     #1e1e1e  editor background   -> the footer ground
     #252526  panel               -> the input well
     #cccccc  UI foreground       -> body text
     #858585  muted               -> secondary text
     #3794ff  link blue           -> links (raised from the site accent, which is
                                     tuned for white ground and goes muddy here)
     #3c3c3c  border

   Literal hex, not tokens, on purpose. The footer must stay dark whatever theme
   the visitor has chosen, so it cannot inherit --surface-* the way the rest of
   the page does.
   ══════════════════════════════════════════════════════════════════════════ */
.landing-footer,
.gf {
  background: #1e1e1e;
  border-top: 1px solid #3c3c3c;
  color: #cccccc;
}
.landing-footer .landing-logo,
.gf .gf-logo { color: #e8e8e8; }
.landing-footer .landing-logo i,
.gf .gf-logo i { color: #3794ff; }
.landing-footer .landing-logo .landing-accent,
.gf .gf-logo .ac { color: #3794ff; }

.landing-footer .landing-footer-copy,
.gf p { color: #9d9d9d; }
.landing-footer .landing-footer-copy a,
.gf a { color: #cccccc; }
.landing-footer .landing-footer-copy a:hover,
.gf a:hover { color: #3794ff; }
.landing-footer .landing-footer-citation { color: #858585; }
.landing-footer .landing-footer-citation a { color: #6fbf8b; }
/* The advisory credit needs its own entry here for the same reason every line above
   has one: the footer is dark and the base rule paints it with --text-dim, a LIGHT
   theme token that resolves to a near-black on this ground. It rendered as a barely
   visible smudge. Slightly brighter than the copyright line, because it is a real
   credential rather than boilerplate, and still well below the links. */
.landing-footer .landing-footer-advisor { color: #b4b4b4; }

/* Subscribe form. Its label and input carried inline light-theme colours that
   would have survived any stylesheet rule, so those are stripped from the markup
   and the styling lives here instead. */
.landing-footer #landing-subscribe label { color: #9d9d9d; }
/* !important, reluctantly and specifically. theme-bridge.css sets
   input { background: var(--bg-input) !important } across every input type, so
   no amount of specificity here wins - an !important declaration outranks a
   more specific normal one. Matching its weight is the only way to give the
   footer input a dark well, and the selector is narrow enough that it cannot
   reach any other input on the site. */
.landing-footer #landing-subscribe input[type="email"] {
  background: #252526 !important;
  border: 1px solid #3c3c3c !important;
  color: #e8e8e8 !important;
  border-radius: var(--r-sm, 4px);
  padding: 7px 11px;
  min-width: 220px;
  font-size: 13px;
}
.landing-footer #landing-subscribe input[type="email"]::placeholder { color: #6e6e6e; }
.landing-footer #landing-subscribe input[type="email"]:focus-visible {
  outline: 2px solid #3794ff;
  outline-offset: 1px;
  border-color: #3794ff;
}
.landing-footer #landing-subscribe .landing-btn-ghost {
  background: #2d2d30;
  border: 1px solid #3c3c3c;
  color: #e8e8e8;
}
.landing-footer #landing-subscribe .landing-btn-ghost:hover { background: #37373d; border-color: #4a4a4a; }
.landing-footer #landing-sub-status { color: #9d9d9d; }

/* Mobile collapse toggle inherits the same treatment. */
.landing-footer .landing-footer-toggle { color: #cccccc; border-color: #3c3c3c; background: #2d2d30; }

/* ── hero lattice host ────────────────────────────────────────────────────── */

/* The three.js canvas that js/hero-lattice.js mounts into. The renderer is
   created with alpha:true and clearAlpha 0, so the page's own light ground shows
   through and this stays a LIGHT surface - the lattice is accent-hue line work
   over --surface-0, never a dark panel. */
/* The lattice and HUD are children of the hero SECTION so they span the whole
   band - full width, full height, behind the copy AND the proof cards. Anchoring
   them to the lead block instead confined them to a centred strip. */
.landing-hero { position: relative; isolation: isolate; }
.hero-static,
.hero-slider { position: relative; }
.hero-lattice {
  position: absolute;
  inset: 0;            /* full bleed: the hero section IS the frame */
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
.hero-lattice canvas { display: block; width: 100%; height: 100%; }
/* Fade at every edge so the geometry dissolves into the page instead of sitting
   in a box. Masking the HOST, not the canvas, keeps WebGL out of the compositing
   path for the fade. */
.hero-lattice {
  /* Was a tight radial that cropped the mesh to a centre blob. Now a gentle
     edge fade only, so the lattice fills the band and dissolves at the borders
     rather than sitting in a visible oval. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent),
                      linear-gradient(to bottom, transparent, #000 12%, #000 84%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent),
              linear-gradient(to bottom, transparent, #000 12%, #000 84%, transparent);
  mask-composite: intersect;
}
/* The copy sits above the art and needs its own stacking context, or the lines
   cross the descenders on the headline. */
.hero-static > .hero-lead { position: relative; z-index: 1; }

/* ── hero HUD ─────────────────────────────────────────────────────────────── */

/* The measurement furniture: edge ticks, numeric readouts and a reticle. This is
   what makes the reference read as instrumentation rather than as an abstract
   node graph, and it is the half that carries the meaning - GDBS measures things
   and reports the figure, so the backdrop is an instrument face.

   DOM and CSS rather than drawn into the WebGL canvas: text in WebGL needs a font
   atlas and renders soft, whereas this stays crisp at any zoom, scales with the
   type scale, and costs nothing to change. Muted ink on the light ground - the
   HUD must sit under the headline, never compete with it. */
.hero-hud {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  font-family: var(--ff-mono, ui-monospace, monospace);
  font-size: 10px;
  letter-spacing: .04em;
  color: oklch(55% .04 var(--accent-hue, 251) / .55);
}
.hero-hud-col {
  position: absolute;
  top: 12%;
  bottom: 12%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-start;
  gap: .5rem;
}
.hero-hud-col.l { left: 2.5%; }
.hero-hud-col.r { right: 2.5%; align-items: flex-end; text-align: right; }
.hero-hud-col span { display: block; font-variant-numeric: tabular-nums; white-space: nowrap; }
.hero-hud-col span::before {
  content: '';
  display: inline-block;
  width: 14px;
  height: 1px;
  background: currentColor;
  vertical-align: middle;
  margin-right: 6px;
  opacity: .7;
}
.hero-hud-col.r span::before { display: none; }
.hero-hud-col.r span::after {
  content: '';
  display: inline-block;
  width: 14px;
  height: 1px;
  background: currentColor;
  vertical-align: middle;
  margin-left: 6px;
  opacity: .7;
}
/* Reticle. Two hairlines and a ring - the cheapest possible way to say
   "something is being measured here". */
.hero-hud-reticle {
  position: absolute;
  right: 9%;
  top: 46%;
  width: 54px;
  height: 54px;
  transform: translateY(-50%);
  opacity: .5;
}
.hero-hud-reticle::before,
.hero-hud-reticle::after {
  content: '';
  position: absolute;
  background: currentColor;
}
.hero-hud-reticle::before { left: 50%; top: 0; bottom: 0; width: 1px; }
.hero-hud-reticle::after { top: 50%; left: 0; right: 0; height: 1px; }
.hero-hud-ring {
  position: absolute;
  inset: 28%;
  border: 1px solid currentColor;
  border-radius: 50%;
}
/* The HUD is fine furniture; below the breakpoint it becomes clutter competing
   with a headline that now wraps to four lines. */
@media (max-width: 900px) { .hero-hud { display: none; } }

/* ── staggered section backdrops ──────────────────────────────────────────── */

/* Mount for js/section-backdrop.js. A canvas dropped in as the first child of
   any .lx section, painted with whatever generator data-bg names.

   The point is STAGGER: a backdrop every few sections with gaps between, so the
   page has rhythm in its grounds the way it now has rhythm in its section
   shapes. Wallpaper on every section would be worse than none. */
.lx-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* Fades at all four edges so the drawing dissolves into the page instead of
     ending on a visible rectangle. */
  -webkit-mask-image: radial-gradient(115% 105% at 50% 50%, #000 30%, transparent 82%);
  mask-image: radial-gradient(115% 105% at 50% 50%, #000 30%, transparent 82%);
}
/* A section holding a backdrop needs a positioning context, and its content has
   to sit above the canvas. Scoped to sections that actually carry one, so no
   other section pays for a stacking context it does not use. */
.lx:has(> .lx-bg) { position: relative; }
.lx:has(> .lx-bg) > .lx-inner { position: relative; z-index: 1; }
/* :has() is well supported now, but a browser without it would stack the canvas
   OVER the copy, which is unreadable rather than merely plain. These two rules
   are the same intent expressed without :has(), and cost nothing where :has()
   already applied them. */
.lx-has-bg { position: relative; }
.lx-has-bg > .lx-inner { position: relative; z-index: 1; }

/* ── audience router (tabs) ───────────────────────────────────────────────── */

/* Tabs are used here for the ONE case they are right for: parallel alternatives
   the reader picks exactly one of. A visitor is a lawyer OR a physicist OR an
   analyst; making all three scroll past the other two is the complaint this
   section exists to fix.
;
   They are NOT used to store narrative. Hiding prose behind a tab does not
   reduce it, it just moves the wall somewhere less visible - and costs the deep
   link, which on a page selling "check it yourself" is a real loss. That is why
   each panel is a WORKED EXAMPLE with a citation, not an essay.

   Deep links still work: js/audience-tabs.js opens the panel named in the URL
   hash, so /#legal lands an attorney on the garnishment table. */
.lx-tabs { max-width: 1000px; margin-inline: auto; }
.lx-tablist {
  display: flex;
  gap: var(--g2, .5rem);
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: var(--g6, 2rem);
}
.lx-tab {
  appearance: none;
  border: 1px solid var(--border, #dfe3ec);
  background: var(--surface-0, #fdfdfe);
  color: var(--text-secondary, #4a5568);
  font: inherit;
  font-size: var(--lx-t-sm);
  font-weight: 600;
  padding: .55rem 1.15rem;
  border-radius: var(--r-full, 9999px);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  transition: background .15s var(--ease, ease), border-color .15s var(--ease, ease), color .15s var(--ease, ease);
}
.lx-tab:hover { border-color: var(--accent, #0078d4); color: var(--text-primary, #1a1f2e); }
.lx-tab[aria-selected="true"] {
  background: var(--accent, #0078d4);
  border-color: var(--accent, #0078d4);
  color: var(--accent-text, #fff);
}
.lx-tab:focus-visible { outline: 2px solid var(--accent, #0078d4); outline-offset: 2px; }
.lx-tab small { font-weight: 400; opacity: .75; }

.lx-panel[hidden] { display: none; }
.lx-panel-lead {
  font-size: var(--lx-t-md);
  color: var(--text-secondary, #4a5568);
  text-align: center;
  max-width: 60ch;
  margin: 0 auto var(--g5, 1.5rem);
  text-wrap: pretty;
}
.lx-panel-lead strong { color: var(--text-primary, #1a1f2e); font-weight: 600; }
.lx-panel-foot {
  font-size: var(--lx-t-sm);
  color: var(--text-muted, #78839a);
  text-align: center;
  max-width: 66ch;
  margin: var(--g4, 1rem) auto 0;
}
/* Tabs stack to full width on narrow screens rather than becoming a cramped
   horizontal scroller, which is where this pattern usually falls apart. */
@media (max-width: 600px) {
  .lx-tablist { flex-direction: column; }
  .lx-tab { justify-content: center; }
}

/* ── SITE MODAL ────────────────────────────────────────────────────────────────
   One shell, two users: the FAQ (fetched from /faq.html) and the feedback form (which
   stays in the DOM because landing.js binds #f-feedback with no null guard, so removing
   the markup would throw and take every listener after it down with it). Named .vsm
   rather than .faqm because it is no longer the FAQ's. */
.vsm { position: fixed; inset: 0; z-index: 3000; display: flex; align-items: center; justify-content: center; padding: 24px; }
.vsm.hidden { display: none; }
.vsm-backdrop { position: absolute; inset: 0; background: rgba(15, 23, 42, .55); backdrop-filter: blur(2px); }
.vsm-panel {
  position: relative;
  width: min(860px, 100%);
  max-height: min(84vh, 900px);
  display: flex;
  flex-direction: column;
  background: var(--surface-0, #fff);
  border: 1px solid var(--border, #dfe3ec);
  border-radius: var(--r-lg, 14px);
  box-shadow: 0 24px 64px rgba(15, 23, 42, .22);
  overflow: hidden;
}
.vsm-head {
  display: flex; align-items: center; gap: 16px;
  padding: 18px 22px;
  border-bottom: 1px solid var(--border-subtle, #e9ecf3);
}
.vsm-title { margin: 0; flex: 1; font-size: var(--lx-t-lg); font-weight: 700; color: var(--text-primary, #1a1f2e); }
.vsm-x {
  flex: none; width: 32px; height: 32px; line-height: 1;
  font-size: 22px; cursor: pointer;
  background: transparent; border: 1px solid var(--border, #dfe3ec);
  border-radius: var(--r-sm, 6px); color: var(--text-muted, #78839a);
}
.vsm-x:hover { background: var(--surface-1, #f4f5f8); color: var(--text-primary, #1a1f2e); }
/* The BODY scrolls, not the panel, so the header and the full-page link stay put
   while eleven answers move under them. */
.vsm-body { overflow-y: auto; padding: 6px 22px 18px; }
.vsm-loading { padding: 32px 0; text-align: center; color: var(--text-muted, #78839a); }
.vsm-foot {
  padding: 14px 22px;
  border-top: 1px solid var(--border-subtle, #e9ecf3);
  background: var(--surface-1, #f4f5f8);
}
.vsm-full { font-size: var(--lx-t-sm); font-weight: 600; color: var(--accent, #0078d4); text-decoration: none; }
.vsm-full:hover { text-decoration: underline; }
@media (max-width: 600px) {
  .vsm { padding: 0; }
  .vsm-panel { width: 100%; max-height: 100vh; border-radius: 0; border: 0; }
}

/* Floating close button, for a dialog whose content brings its own heading (the
   feedback form already says "We Want to Hear From You", so a second title bar
   above it would be two headings arguing). */
.vsm-x-float { position: absolute; top: 12px; right: 12px; z-index: 2; }
/* The feedback panel holds a form, not a scrolling list, so it sizes to content. */
#feedback-modal .vsm-panel { max-height: min(92vh, 980px); }
#feedback-modal .vsm-body { padding: 26px 26px 22px; }
#feedback-modal #landing-feedback { padding: 0; background: none; }

/* ── HERO AFFILIATION STRIP ───────────────────────────────────────────────────
   Sits between the CTAs and the stat row. Muted on purpose: these marks earn
   trust, they do not make the argument, and a bright badge row directly under
   the headline would outrank the thing it is supposed to support.

   The logo is greyscaled and dimmed at rest and comes up on hover, which keeps
   a third-party brand from competing with our own accent while still being
   clearly legible. */
.hero-affil {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 14px;
  margin: 0 auto 34px;
  max-width: 760px;
}
.hero-affil-logo {
  height: 42px;
  width: auto;
  filter: grayscale(1);
  opacity: .75;
  transition: filter .2s ease, opacity .2s ease;
}
/* Only the PenFed mark needs this: it is a JPEG on white, so multiply drops the box
   onto the hero gradient. The ASBTDC mark is already transparent and needs nothing. */
.hero-affil-logo[src$=".jpg"] { mix-blend-mode: multiply; }
.hero-affil:hover .hero-affil-logo { filter: grayscale(0); opacity: 1; }
.hero-affil-txt {
  font-size: 12px;
  letter-spacing: .04em;
  color: var(--text-muted, #78839a);
  white-space: nowrap;
}
.hero-affil-sep {
  width: 1px;
  height: 14px;
  background: var(--border, #dfe3ec);
}
@media (max-width: 640px) {
  /* Dividers between wrapped rows read as stray marks, so they go and the gap
     does the separating instead. */
  .hero-affil-sep { display: none; }
  .hero-affil { gap: 10px 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-affil-logo { transition: none; }
}

/* PenFed mark + "Veteran Entrepreneur Program" are ONE affiliation, not two.
   VEP has no mark of its own - it runs under the Foundation brand, which is how
   PenFed's own site presents it - so the programme name is a caption to the logo
   rather than a separate entry with a divider between them. */
.hero-affil-unit { display: inline-flex; align-items: center; gap: 9px; }
.hero-affil-cap {
  font-size: 11px;
  line-height: 1.25;
  letter-spacing: .03em;
  white-space: normal;   /* the <br> sets the break; nowrap would fight it */
  text-align: left;
}

/* The PenFed unit is a LINK to the cohort press release, not decoration - a reader
   can check the affiliation rather than take the badge's word for it. Styled to
   inherit so it reads as a credit, with the underline held back until hover. */
a.hero-affil-unit { text-decoration: none; color: inherit; }
a.hero-affil-unit .hero-affil-cap { text-decoration: none; }
a.hero-affil-unit:hover .hero-affil-cap { text-decoration: underline; text-underline-offset: 2px; }
a.hero-affil-unit:hover .hero-affil-logo { filter: grayscale(0); opacity: 1; }
a.hero-affil-unit:focus-visible { outline: 2px solid var(--accent, #0078d4); outline-offset: 4px; border-radius: 4px; }

/* ── TESTIMONIAL WALL ─────────────────────────────────────────────────────────
   Rendered by js/testimonials-wall.js from /api/testimonials/published. The
   section carries .hidden in the markup and the script removes it only when there
   are three or more, so an empty wall never ships. */
.tw-grid {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(3, 1fr);
  margin-top: var(--g5, 1.5rem);
}
@media (max-width: 900px) { .tw-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .tw-grid { grid-template-columns: 1fr; } }

.tw-card {
  margin: 0;
  padding: 18px 20px;
  background: var(--surface-0, #fff);
  border: 1px solid var(--border, #e2e8f0);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
.tw-stars { color: oklch(72% .15 75); font-size: 12px; letter-spacing: 1px; }
/* An unearned star is drawn, not omitted, so a 3/5 reads as three of five rather
   than as three of three. */
.tw-stars .tw-off { color: var(--border, #dfe3ec); }
.tw-h {
  margin: 0;
  font-size: 15px;
  line-height: 1.35;
  font-weight: 700;
  color: #233061;
  overflow-wrap: anywhere;
}
.tw-q {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--text-secondary, #475569);
  overflow-wrap: anywhere;
}
.tw-by {
  margin-top: auto;                       /* attributions line up across a row */
  padding-top: 10px;
  border-top: 1px dotted var(--border, #dfe3ec);
  font-size: 12.5px;
  color: var(--text-primary, #1a1f2e);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.tw-role { font-weight: 400; color: var(--text-muted, #78839a); }

/* ── PLAN COMPARISON TABLE ────────────────────────────────────────────────────
   Sits under the pricing cards, where Linear, Notion and MyCase all put theirs.
   Reuses .lx-data so it matches the other tables on the page rather than
   introducing a fourth table style. */
.cmp-wrap { max-width: 1100px; margin: var(--g8, 4rem) auto 0; }
.cmp-h {
  font-size: var(--lx-t-lg);
  font-weight: 700;
  text-align: center;
  color: var(--text-primary, #1a1f2e);
  margin: 0 0 8px;
}
.cmp-note {
  text-align: center;
  font-size: var(--lx-t-sm);
  color: var(--text-muted, #78839a);
  margin: 0 0 var(--g5, 1.5rem);
}
.cmp-note a { color: var(--accent, #0078d4); }
/* Tier name over its price, so the header carries the number a reader is comparing
   against without a second row of cells to keep aligned. */
.cmp-table thead th { text-align: center; vertical-align: bottom; }
.cmp-table thead th:first-child { text-align: left; }
.cmp-table thead th span {
  display: block;
  margin-top: 2px;
  font-size: var(--lx-t-sm);
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  color: var(--accent, #0078d4);
}
.cmp-table td { text-align: center; }
.cmp-table td:first-child { text-align: left; }
/* Category rows are signposts, not data - given weight and a tinted ground so a
   reader can find "Legal and compliance" while scrolling a 25-row table. */
.cmp-table .cmp-cat td {
  text-align: left;
  font-weight: 700;
  font-size: var(--lx-t-sm);
  color: var(--text-primary, #1a1f2e);
  background: var(--surface-1, #f4f5f8);
  border-top: 1px solid var(--border, #dfe3ec);
}
/* A dash is not an absence of information, it is the answer "no". Muted so the row
   still reads as deliberate rather than unfinished. */
.cmp-table td.no { color: var(--text-muted, #78839a); font-weight: 400; }
@media (max-width: 720px) {
  /* The wrapper already scrolls; shrink the type so fewer readers have to. */
  .cmp-table { font-size: var(--lx-t-xs); }
  .cmp-table td, .cmp-table th { padding: var(--g2, .5rem) var(--g3, .75rem); }
}
