/* =========================================================================
   wireagent — showcase / landing page (lp-*)

   Rebuilt 2026-08-13. A real marketing page, self-contained: it borrows the
   design TOKENS from styles.css and the generic .btn / .logo / .pill, but
   never the console's structural classes — the coupling that made the old
   front door fragile.

   THIS PAGE HAS ITS OWN THEME. By request the landing moves to an
   "outreach / night" temperature: a deep INDIGO base with the product's
   ELECTRIC GREEN kept as the one accent — so it reads as a communication
   tool while staying coherent with the green console it shows. The palette
   is redefined only under `body.lp`, so the console (which never loads this
   file) and the sign-in page (no .lp class) are untouched.

   Type is embedded, not merely named: Fraunces — a soft, high-contrast serif
   — for display (roman + its true italic, used for the hero emphasis), and
   Inter for body. Self-hosted variable woff2 under /fonts. Product shots are
   the real console (public/shots/*), framed as app windows.
   ========================================================================= */

@font-face {
  font-family: "Fraunces";
  src: url("/fonts/fraunces.woff2") format("woff2");
  font-weight: 200 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Fraunces";
  src: url("/fonts/fraunces-italic.woff2") format("woff2");
  font-weight: 200 900; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "InterVar";
  src: url("/fonts/inter.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}

/* ---- the landing's own palette: indigo base, green accent ---- */
body.lp {
  --void:          #05070f;
  --bg:            #0a0f1e;
  --surface:       #0f1730;
  --surface-2:     #15203c;
  --surface-3:     #1c294a;
  --border:        #232f4e;
  --border-strong: #33436b;
  --grey:          #6b7aa0;
  --grey-2:        #8b9ac0;
  --grey-3:        #c4d0ec;
  --text:          #eaf0ff;
  --muted:         #9daecf;   /* secondary prose on indigo */
  --faint:         #808fb6;   /* micro-labels */
  --control-border:#3b4a70;
  --glass:         rgba(130, 150, 225, .05);
  --glass-strong:  rgba(130, 150, 225, .08);
  --glass-edge:    rgba(140, 162, 230, .16);
  --glass-spec:    rgba(200, 215, 255, .16);
  --lift-glass:    0 24px 70px -22px rgba(0, 1, 8, .8);
  /* The accent is WHITE now (by request), not green — a monochrome,
     editorial look on the indigo base. --green-ink becomes a dark indigo so
     text on the white accent (buttons, step numbers) stays legible. The
     green console screenshots keep their own colour; white is neutral enough
     to sit beside them without clashing. */
  --green:         #ffffff;
  --green-bright:  #f2f5ff;
  --green-ink:     #0a1024;
  /* the landing's own motion curve: a long settle, for reveals and glides */
  --ease-out:      cubic-bezier(.16, 1, .3, 1);

  font-family: "InterVar", var(--sans);
  background: var(--bg);
}

/* A cooler aurora: indigo light with a green pulse, so the glass has an
   indigo world to refract and the green still reads as the signal. */
body.lp::before {
  background:
    radial-gradient(42vmax 42vmax at 14% 2%,  rgba(255, 255, 255, .12), transparent 60%),
    radial-gradient(52vmax 52vmax at 86% 98%, rgba(66, 92, 214, .34), transparent 62%),
    radial-gradient(38vmax 38vmax at 74% 8%,  rgba(210, 220, 255, .06), transparent 66%),
    radial-gradient(46vmax 46vmax at 8% 84%,  rgba(88, 70, 200, .20), transparent 64%);
}

/* Film grain over the whole page — the finish that separates painted light
   from a flat gradient. One tiled SVG turbulence on a fixed layer: it costs
   a single composite, not a loop, so reduced-motion needs no exception. It
   sits far below the intro splash (z 100) and never intercepts the pointer. */
body.lp::after {
  content: ""; position: fixed; inset: 0; z-index: 60; pointer-events: none;
  opacity: .05;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

/* The document scrollbar wears the theme. Standard properties only — adding
   the -webkit pseudos as well would flip Chromium back to the legacy path. */
html:has(body.lp) { scrollbar-color: #2e3c64 #0a0f1e; }

/* Selection inverts to the accent: white ground, indigo ink. */
body.lp ::selection { background: #e9eeff; color: #0a1024; }

/* Display type on the headline surfaces; Inter carries the body. The logo
   wordmark is deliberately NOT here — it keeps its original sans wordmark
   (styles.css .logo__word), by request. */
.lp-h1, .lp-section__head h2, .lp-feature h2,
.lp-final h2, .lp-step__t, .cs-h1, .cs-signin h2, .cs-legal h1, .cs-legal h2 {
  font-family: "Fraunces", var(--serif, Georgia, serif);
}

.lp-wrap { width: 100%; max-width: 72rem; margin-inline: auto; padding-inline: var(--gutter); }
.lp section { scroll-margin-top: 84px; }

/* ── sticky nav, in the three-part grammar of a product site: brand on the
   left, section links dead centre, then a quiet sign-in and one white pill
   on the right. The centre column is `auto` between two `1fr` flanks so the
   links centre against the PAGE, not against what the brand leaves over. */
.lp-nav {
  position: sticky; top: 0; z-index: 20;
  background: rgba(10, 15, 30, .72);
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  border-bottom: 1px solid var(--glass-edge);
  transition: background .3s var(--ease), border-color .3s var(--ease), box-shadow .3s var(--ease);
}
/* once the page moves, the bar takes weight: deeper ground, firmer edge */
.lp-nav.is-scrolled {
  background: rgba(7, 11, 23, .88);
  border-bottom-color: rgba(140, 162, 230, .22);
  box-shadow: 0 18px 48px -30px rgba(0, 1, 8, .9);
}
/* the bar's bottom edge IS a wire: reading progress runs along it as a
   thin current (lp-motion.js writes --progress). Position feedback, so it
   needs no reduced-motion switch; without JS it simply is not there. */
.lp-nav::after {
  content: ""; position: absolute; left: 0; bottom: -1px; height: 1px;
  width: calc(var(--progress, 0) * 100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .7));
  pointer-events: none;
}
@supports not (backdrop-filter: blur(1px)) { .lp-nav { background: var(--surface); } }
.lp-nav__inner {
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: 16px; padding-block: 12px;
}
.lp-nav__links { display: flex; gap: 4px; align-items: center; justify-content: center; }
.lp-nav__links a {
  color: var(--muted); text-decoration: none; font-size: 14px; font-weight: 600;
  padding: 8px 13px; border-radius: var(--r-pill);
  transition: color .16s var(--ease), background .16s var(--ease);
}
.lp-nav__links a:hover { color: var(--text); background: rgba(140, 162, 230, .1); }
/* the scrollspy marks where the reader actually is */
.lp-nav__links a[aria-current="true"] { color: var(--text); background: rgba(140, 162, 230, .12); }
.lp-nav__cta { display: flex; gap: 16px; align-items: center; justify-content: flex-end; }
.lp-nav__signin {
  color: var(--muted); text-decoration: none; font-size: 14px; font-weight: 600;
  transition: color .16s var(--ease);
}
.lp-nav__signin:hover { color: var(--text); }
/* 860, not 800, and not 720 before that. The three-column bar needs room to lay
   itself out; below that the sign-in CTA hangs past the right edge and the WHOLE
   PAGE scrolls sideways. Measured at 720: the document was 797px wide at every
   viewport from 721 to 796, exactly where a 768px tablet in portrait sits, which
   is the single most common tablet width there is.

   MOVED TO 860 ON 2026-08-24, because the same defect had come back one
   breakpoint up. Measured at an 820px viewport: the three grid tracks resolved
   to 115.6 + 619.9 + 72.6px, which with two 10px gaps and the wrap padding needs
   about 860px, so the sign-in CTA's right edge sat at 860 and the document
   scrolled sideways at every viewport from 801 to 859.

   WHY IT CAME BACK, because the reason is the useful part: the band rule below
   was written for SEVEN links and there are now eight. The eighth consumed the
   room the tighter pills had bought. Tightening them further would have fitted
   eight back inside 801px with about 5px to spare, which is not a fix, it is the
   same bug rescheduled for the next link anybody adds.

   So the collapse now ends where the links genuinely fit rather than where they
   last fitted. Nothing is lost between 801 and 859 that was not already lost at
   800: the hero CTA row carries Plans, the demo and the feature tour at every
   width, which is the reason collapsing is acceptable here at all. */
@media (max-width: 860px) {
  .lp-nav__inner { grid-template-columns: auto 1fr; }
  .lp-nav__links { display: none; }
}

/*
   The band the collapse leaves behind, 861 to 960.

   Above 960 the links lay out in one row. Below it they did not, and because
   .lp-nav__links a had no white-space rule the labels wrapped INSIDE their own
   pills instead: measured 2026-08-23, the sticky bar stood at 108px from 810 to
   849 and 86px from 850 to 959, against 63px at 960 and up. On an 820px tablet
   that is a tenth of the viewport held permanently by a bar.

   nowrap alone would have been the wrong fix on its own: with nothing able to
   wrap, the row overflows the middle column and takes the whole page sideways,
   which is precisely the defect the note above records twice. So the labels are
   pinned AND the row is given the room to hold them: less tracking, tighter
   pills, a smaller gap. The type stays at 13px, above the 12px floor the width
   sweep enforces.

   The lower bound moved 801 -> 861 with the collapse, and the pills lost two
   more pixels of padding each. That is deliberate slack rather than tidying: at
   861 the row now has real headroom instead of the single pixel it would have
   had, so the next link added here degrades the spacing before it breaks the
   page. `npm run probe:widths` is what catches it either way.
*/
@media (min-width: 861px) and (max-width: 960px) {
  .lp-nav__inner { gap: 10px; }
  .lp-nav__links { gap: 1px; }
  .lp-nav__links a { white-space: nowrap; font-size: 13px; padding: 8px 5px; letter-spacing: -.01em; }
}

/* ── hero: a LEFT-aligned statement over the console on its own painted
   stage. The eyebrow and lead came back 2026-08-17 by request — a page
   that never says what the product IS reads slick, not trustworthy. */
.lp-hero { padding-block: clamp(40px, 7vw, 88px) 28px; text-align: left; display: grid; gap: 22px; justify-items: start; }
.lp-lead { color: var(--muted); font-size: clamp(15.5px, 2vw, 18px); line-height: 1.65; max-width: 56ch; text-wrap: pretty; }
.lp-h1 { font-size: clamp(32px, 4.6vw, 54px); line-height: 1.08; letter-spacing: -.012em; font-weight: 550; max-width: 24ch; color: #d9e0fb; }
/* the emphasis is a true serif italic in pure white — the loveliness IS the
   emphasis now that the accent is monochrome, no green to lean on */
.lp-h1 em { font-style: italic; font-weight: 500; color: #ffffff; }
.lp-cta-row { display: flex; gap: 12px; flex-wrap: wrap; }
.btn--lg { min-height: 50px; padding: 13px 28px; font-size: 15px; }

/* The stage: an aurora painted in CSS, edge to edge of the column, with
   the framed console centred on it. Our answer to setting the product on
   art — same composition, our own weather. */
.lp-stage {
  width: 100%;
  perspective: 1400px;
  padding: clamp(20px, 4.5vw, 60px) clamp(16px, 4vw, 56px);
  border-radius: 14px;
  border: 1px solid var(--glass-edge);
  background:
    radial-gradient(60% 90% at 16% 8%,  rgba(255, 255, 255, .16), transparent 60%),
    radial-gradient(75% 110% at 86% 92%, rgba(66, 92, 214, .5), transparent 66%),
    radial-gradient(50% 80% at 74% 6%,  rgba(160, 178, 255, .16), transparent 60%),
    linear-gradient(165deg, #1a2650, #0c1226);
}

/* Hero entrance: a short staggered rise on load. Pure CSS, so it needs no
   JS — and prefers-reduced-motion switches it off entirely below. */
.lp-hero > * { animation: lp-rise .7s var(--ease) both; }
.lp-hero > *:nth-child(1) { animation-delay: .02s; }
.lp-hero > *:nth-child(2) { animation-delay: .09s; }
.lp-hero > *:nth-child(3) { animation-delay: .16s; }
.lp-hero > *:nth-child(4) { animation-delay: .23s; }
.lp-hero > *:nth-child(5) { animation-delay: .30s; }
.lp-hero > *:nth-child(6) { animation-delay: .37s; }
@keyframes lp-rise { from { opacity: 0; transform: translateY(20px); filter: blur(10px); } to { opacity: 1; transform: none; filter: none; } }

/* ── framed product window: the console, shown as itself */
.lp-window {
  width: 100%; max-width: 56rem; margin-inline: auto;
  border-radius: 14px; overflow: hidden;
  border: 1px solid var(--glass-edge);
  box-shadow: var(--lift-glass);
  background: var(--surface);
}
/* No window chrome. The frame had a Mac bar (three traffic lights on a
   slim indigo strip) from 2026-09-02 until 2026-09-04, when the owner
   looked at the four replicas and removed it ("it just looks so bad for
   all the diagrams"). Each replica now opens straight on its own rail;
   the frame is the rounded border and the lift, nothing above it. */
/* ── living console replicas. Each window frames the ACTUAL console,
   rebuilt as landing-local markup (lpc-*): the same app bar, tabs, tiles,
   cards, charts, donuts, meters and rows the product draws, with the same
   colours and geometry copied from styles.css by value. Copied, never
   shared: the 2026-08-13 front door reused console classes and broke
   silently whenever the console changed. A replica may go cosmetically
   stale; it must never go blank.

   Each replica is designed at 1120x780 and scaled to its window by
   --shot-scale (lp-motion.js measures the box; the fallback keeps it
   close without JS). The UI is ALIVE: bars grow, donuts sweep, the pace
   meter fills, rows deal in, the session dot beats. One-shot builds run
   once; only the state dots loop. Reduced motion stills everything. */
.lp-shot { --shot-scale: .47; position: relative; aspect-ratio: 1120 / 780; overflow: hidden; background: #0d1326; }
.lp-window--hero .lp-shot { --shot-scale: .8; }
.lpc {
  position: absolute; top: 0; left: 0; width: 1120px; height: 780px;
  transform-origin: 0 0; transform: scale(var(--shot-scale, .5));
  display: grid; align-content: start; gap: 18px; padding: 22px 26px;
  font-family: "InterVar", var(--sans); font-size: 16px; color: var(--text);
  pointer-events: none; user-select: none;
}

/* ── the rail: the console's grouped left navigation, as it is since
   2026-09-02 (public/index.html, .side/.rail, painted by console.css).
   The held session, dark, beside the light column. Absolute inside the
   canvas so the column's rows flow exactly as they always did, with the
   canvas padded to clear it. Colours copied from console.css by value. */
.lpc-rail {
  position: absolute; top: 0; left: 0; bottom: 0; width: 200px;
  display: flex; flex-direction: column; gap: 10px;
  padding: 18px 12px 16px;
  /* ONE night blue, opaque, no light behind it: #15203c is the console's
     --surface-2, the same hex its rail, its hero tile and its menus paint
     since the 2026-09-02 night polish (console.css). The replicas used a
     96% tint with a radial glow, which was the very "different shade of
     blue" the owner had just had removed from the product. */
  background: #15203c;
  border-right: 1px solid rgba(140, 162, 230, .22);
  color: #f6f7f9;
}
.lpc-brand { position: relative; display: flex; align-items: center; gap: 8px; padding: 0 6px; font-weight: 650; letter-spacing: -.02em; font-size: 17px; color: #fff; }
.lpc-brand svg { width: 24px; height: 24px; }
.lpc-search {
  position: relative; display: flex; align-items: center; gap: 7px;
  padding: 7px 9px; border-radius: 8px;
  border: 1px solid rgba(140, 162, 230, .3); background: rgba(255, 255, 255, .06);
  font-size: 12px; color: #9daecf; white-space: nowrap;
}
.lpc-search svg { width: 14px; height: 14px; flex: none; }
.lpc-search kbd { margin-left: auto; font-family: var(--mono); font-size: 9.5px; padding: 1px 5px; border-radius: 4px; border: 1px solid rgba(140, 162, 230, .3); color: #808fb6; }
.lpc-nav { position: relative; display: grid; gap: 2px; margin-top: 2px; }
.lpc-group { display: block; margin: 10px 8px 3px; font-family: var(--mono); font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase; color: #808fb6; }
.lpc-item {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 8px; border-radius: 8px;
  font-size: 13.5px; font-weight: 600; color: #b8c3e3; white-space: nowrap;
}
.lpc-item::before { content: ""; width: 12px; height: 12px; flex: none; border-radius: 3.5px; border: 1.6px solid currentColor; opacity: .75; }
.lpc-item--on { background: rgba(255, 255, 255, .1); color: #fff; }
.lpc-item--last { margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(140, 162, 230, .2); border-radius: 0 0 8px 8px; }
.lpc-who { position: relative; display: flex; align-items: center; gap: 9px; margin-top: auto; padding: 0 4px; }
/* A generated avatar is a LIGHT disc everywhere, the accent-soft tile with
   accent initials, exactly what app.js fallbackAvatar draws since the
   2026-09-02 night polish; a navy disc on a white row was one of the eight
   defects that pass removed. */
.lpc-ava { width: 32px; height: 32px; border-radius: 50%; flex: none; display: grid; place-items: center; background: #e9ecf8; border: 1px solid transparent; font-family: var(--mono); font-size: 12px; font-weight: 700; color: #3446a8; }
.lpc-rail .lpc-ava { background: #e9ecf8; border-color: rgba(140, 162, 230, .35); color: #3446a8; }
.lpc-who b { font-size: 13px; font-weight: 600; display: block; color: #fff; }
.lpc-who small { font-size: 11.5px; color: #9daecf; display: flex; align-items: center; gap: 5px; }
.lpc-dot { width: 7px; height: 7px; border-radius: 50%; background: #fff; flex: none; animation: lpc-beat 2.6s var(--ease) infinite; }
@keyframes lpc-beat { 0%, 100% { box-shadow: 0 0 0 3px rgba(255,255,255,.18); } 50% { box-shadow: 0 0 0 5px rgba(255,255,255,.06); } }

/* page head: the h1 and the status pills, exactly as the console sets them */
.lpc-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.lpc-head .lpc-h { display: block; font-family: var(--serif); font-size: 30px; font-weight: 540; letter-spacing: -.012em; }
.lpc-head p { color: var(--muted); font-size: 13.5px; margin-top: 2px; }
.lpc-pills { display: flex; gap: 8px; }
.lpc-pill { font-size: 12px; font-weight: 600; font-family: var(--mono); padding: 4px 10px; border-radius: var(--r-pill); border: 1px solid var(--control-border); background: rgba(255,255,255,.06); color: var(--muted); display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.lpc-pill--good { background: rgba(255,255,255,.12); color: #fff; border-color: rgba(255,255,255,.5); }

/* stat tiles */
.lpc-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.lpc-stats--two { grid-template-columns: repeat(2, minmax(0, 14rem)); }
.lpc-stat { position: relative; overflow: hidden; display: flex; align-items: center; gap: 13px; padding: 14px 16px; background: linear-gradient(150deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, .02)); border: 1px solid var(--glass-edge); border-radius: 12px; }
.lpc-chip { width: 34px; height: 34px; border-radius: 9px; flex: none; display: grid; place-items: center; background: rgba(255, 255, 255, .1); border: 1px solid rgba(255, 255, 255, .2); color: #fff; }
.lpc-chip svg { width: 17px; height: 17px; }
.lpc-stat b { font-family: var(--mono); font-size: 23px; font-weight: 700; letter-spacing: -.02em; color: var(--white); line-height: 1.12; display: block; }
.lpc-stat small { color: var(--faint); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; white-space: nowrap; }
.lpc-stat--hero { border-color: rgba(255, 255, 255, .34); background: linear-gradient(150deg, rgba(255, 255, 255, .12), rgba(255, 255, 255, .02)); }
.lpc-stat--hero::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: #fff; }
.lpc-stat--hero .lpc-chip { background: #fff; border-color: #fff; color: var(--green-ink, #0a1024); }

/* the live auto-connect bar, pulsing exactly like the console's */
.lpc-live { display: flex; align-items: center; gap: 12px; padding: 12px 16px; border: 1px solid rgba(255, 255, 255, .34); border-radius: 12px; background: linear-gradient(150deg, rgba(255, 255, 255, .12), rgba(255, 255, 255, .02)); font-size: 14px; }
.lpc-live i { width: 9px; height: 9px; border-radius: 50%; flex: none; background: #fff; box-shadow: 0 0 8px #fff; animation: lpc-pulse 1.6s ease-in-out infinite; }
.lpc-live .lpc-ghostbtn { margin-left: auto; }
@keyframes lpc-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* cards, figures, buttons */
.lpc-card { border: 1px solid var(--glass-edge); border-radius: 14px; background: rgba(255, 255, 255, .035); box-shadow: inset 0 1px 0 var(--glass-spec); padding: 20px 22px; display: grid; gap: 14px; align-content: start; }
.lpc-ch { font-size: 17px; font-weight: 650; display: flex; align-items: center; gap: 8px; }
.lpc-sechead { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.lpc-go { font-size: 13px; font-weight: 600; color: var(--muted); }
.lpc-fighead { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 13.5px; font-weight: 600; }
.lpc-fighead small { font-weight: 400; color: var(--faint); font-size: 12px; font-family: var(--mono); }
.lpc-btn { display: inline-flex; align-items: center; justify-content: center; padding: 7px 16px; border-radius: var(--r-pill); font-size: 13px; font-weight: 600; background: #fff; color: var(--green-ink, #0a1024); flex: none; }
.lpc-ghostbtn { display: inline-flex; align-items: center; justify-content: center; padding: 6px 14px; border-radius: var(--r-pill); font-size: 13px; font-weight: 600; background: rgba(255,255,255,.04); border: 1px solid var(--control-border); color: var(--text); flex: none; }
.lpc-input { display: flex; align-items: center; padding: 10px 14px; border-radius: 8px; background: rgba(0, 0, 0, .32); border: 1px solid var(--control-border); color: var(--text); font-size: 14px; flex: 1; }
.lpc-input span { color: var(--faint); }
.lpc-caret { display: inline-block; width: 1.5px; height: 16px; background: #fff; margin-left: 2px; animation: lpc-caret 1.1s steps(1) infinite; }
@keyframes lpc-caret { 50% { opacity: 0; } }

/* charts: white bars growing from the baseline, staggered like a live load */
.lpc-chart { display: flex; align-items: flex-end; gap: 6px; height: 150px; padding-top: 6px; }
.lpc-chart i { flex: 1; border-radius: 3px 3px 0 0; background: var(--green-dim, #8fa2dd); transform-origin: bottom; animation: lpc-grow .7s var(--ease-out, var(--ease)) both; animation-delay: calc(.15s + var(--i, 0) * .05s); }
.lpc-chart i.hot { background: #fff; }
@keyframes lpc-grow { from { transform: scaleY(0); } }

/* the pace meter on its deep track, filling to its measured week */
.lpc-meter { height: 12px; border-radius: var(--r-pill); background: var(--green-deep, #1e2a52); overflow: hidden; }
/* the fill animates by transform, not width: no layout per frame */
.lpc-meter i { display: block; height: 100%; border-radius: var(--r-pill); background: #fff; width: var(--w, 60%); transform-origin: 0 50%; animation: lpc-fill 1.4s var(--ease-out, var(--ease)) .3s both; }
@keyframes lpc-fill { from { transform: scaleX(0); } }

/* donuts: segments swept in by dasharray, the hero figure in the hole */
.lpc-donut { justify-self: center; }
.lpc-donut circle { fill: none; stroke-width: 18; }
.lpc-donut .t { stroke: rgba(255, 255, 255, .07); }
.lpc-donut .s1 { stroke: #fff; }
.lpc-donut .s2 { stroke: var(--green-dim, #8fa2dd); }
.lpc-donut .s3 { stroke: var(--grey, #6b7aa0); }
.lpc-donut .s1, .lpc-donut .s2, .lpc-donut .s3 { animation: lpc-sweep 1.1s var(--ease-out, var(--ease)) both; animation-delay: calc(.3s + var(--i, 0) * .18s); }
.lpc-donut text { fill: var(--white); font-family: var(--mono); font-weight: 700; font-size: 26px; text-anchor: middle; }
.lpc-donut .sub { fill: var(--faint); font-size: 10px; font-weight: 400; letter-spacing: .12em; text-transform: uppercase; }
@keyframes lpc-sweep { from { stroke-dasharray: 0 264; } }

/* the growth line, drawn on via dashoffset like the console's */
.lpc-line path.l { fill: none; stroke: #fff; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; stroke-dasharray: 1100; animation: lpc-draw 1.5s var(--ease-out, var(--ease)) .2s both; }
.lpc-line path.a { fill: #fff; opacity: .08; }
.lpc-line circle { fill: #fff; stroke: #0d1326; stroke-width: 2; }
@keyframes lpc-draw { from { stroke-dashoffset: 1100; } }

/* people rows: avatar, name, sub, an action on the right */
.lpc-row { display: flex; align-items: center; gap: 12px; padding: 11px 2px; border-top: 1px solid rgba(255, 255, 255, .08); animation: lpc-deal .5s var(--ease-out, var(--ease)) both; animation-delay: calc(.2s + var(--i, 0) * .12s); }
.lpc-row:first-of-type { border-top: none; }
.lpc-row .lpc-ava { width: 38px; height: 38px; }
.lpc-row > div { flex: 1; min-width: 0; }
.lpc-row b { font-size: 14.5px; font-weight: 600; display: block; }
.lpc-row small { color: var(--muted); font-size: 12.5px; }
.lpc-when { font-family: var(--mono); font-size: 12px; color: var(--faint); flex: none; }
@keyframes lpc-deal { from { opacity: 0; transform: translateY(8px); } }

/* tags on lead rows */
.lpc-tag { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; font-weight: 600; padding: 2px 9px; border-radius: var(--r-pill); border: 1px solid var(--control-border); background: rgba(255,255,255,.04); color: var(--grey-3); margin-right: 4px; }
.lpc-tag--warm { background: #fff; color: var(--green-ink, #0a1024); border-color: #fff; }

/* the sent state arriving: pill crossfades once, like a run completing */
.lpc-swap { position: relative; flex: none; }
.lpc-swap .lpc-pill { position: relative; }
.lpc-swap .lpc-pill--good { position: absolute; inset: 0; justify-content: center; opacity: 0; animation: lpc-arrive .4s var(--ease) 2.4s forwards; }
@keyframes lpc-arrive { to { opacity: 1; } }

/* ── the light column (console.css, copied by value). Since 2026-09-02 the
   console reads on tinted white beside the dark rail, and the replicas
   follow: every rule below re-inks a component above that assumed the dark
   ground, with console.css's own values (paper #f6f7fb, card #fff, ink
   #0a1024, ink-2 #3d4a73, ink-3 #56628a, rule #dfe4f1, rule-strong #8792b5,
   accent #3446a8, accent-2 #7d8bd0, accent-soft #e9ecf8, dark card #15203c,
   which is also the rail: one night blue, since the 2026-09-02 night polish).
   The rail keeps its own colours above; nothing here reaches into it. */
.lp-shot { background: #f6f7fb; }
.lpc {
  padding: 24px 28px 22px 228px;
  color: #0a1024;
  --text: #0a1024; --muted: #3d4a73; --faint: #56628a; --white: #0a1024;
  --surface-3: #e4e9f6; --control-border: #8792b5; --glass-edge: #dfe4f1; --glass-spec: transparent;
  --green-ink: #ffffff; --green-dim: #7d8bd0; --green-deep: #dfe4f1; --grey: #b7c0da; --grey-3: #3d4a73;
}
.lpc-head .lpc-h { font-weight: 500; color: #0a1024; }
.lpc-card { background: #fff; border-color: transparent; box-shadow: 0 8px 22px -14px rgba(10, 16, 36, .32), 0 1px 3px -1px rgba(10, 16, 36, .10); }
.lpc-card--row { display: flex; align-items: center; justify-content: space-between; padding: 16px 22px; }
.lpc-ch { font-family: var(--serif); font-weight: 500; font-size: 22px; letter-spacing: -.018em; }
.lpc-ico { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; background: #e9ecf8; color: #3446a8; flex: none; }
.lpc-ico svg { width: 16px; height: 16px; }
.lpc-mini { display: flex; gap: 22px; font-size: 13.5px; color: #3d4a73; }
.lpc-mini b { font-family: var(--mono); font-weight: 700; color: #0a1024; margin-right: 4px; }
.lpc-stat { background: #fff; border-color: transparent; box-shadow: 0 8px 22px -14px rgba(10, 16, 36, .32), 0 1px 3px -1px rgba(10, 16, 36, .10); }
.lpc-chip { background: #e9ecf8; border-color: transparent; color: #3446a8; }
.lpc-stat--hero { background: #15203c; border-color: transparent; box-shadow: 0 24px 50px -24px rgba(10, 16, 36, .6); }
.lpc-stat--hero::before { display: none; }
.lpc-stat--hero .lpc-chip { background: #fff; border-color: #fff; color: #3446a8; }
.lpc-stat--hero b { color: #fff; }
.lpc-stat--hero small { color: #9daecf; }
.lpc-live { background: #eef1fa; border-color: #dfe4f1; }
.lpc-live i { background: #3446a8; box-shadow: 0 0 0 3px rgba(52, 70, 168, .18); }
.lpc-live b { font-weight: 600; }
.lpc-btn { background: #3446a8; color: #fff; }
.lpc-ghostbtn { background: transparent; border-color: #8792b5; color: #0a1024; }
.lpc-input { background: #fff; border-color: #8792b5; color: #0a1024; }
.lpc-caret { background: #0a1024; }
.lpc-pill { background: #eef1fa; color: #3d4a73; border-color: #8792b5; }
.lpc-pill--good { background: #3446a8; color: #fff; border-color: #3446a8; }
.lpc-chart i { background: #7d8bd0; }
.lpc-chart i.hot { background: #3446a8; }
.lpc-meter { background: #dfe4f1; }
.lpc-meter i { background: #3446a8; }
.lpc-donut .t { stroke: #eef1fa; }
.lpc-donut .s1 { stroke: #3446a8; }
.lpc-donut .s2 { stroke: #7d8bd0; }
.lpc-donut .s3 { stroke: #b7c0da; }
.lpc-donut text { fill: #0a1024; }
.lpc-line path.l { stroke: #3446a8; }
.lpc-line path.a { fill: #3446a8; opacity: .1; }
.lpc-line circle { fill: #3446a8; stroke: #fff; }
.lpc-row { border-top-color: #dfe4f1; }
.lpc-tag { background: #eef1fa; color: #3d4a73; border-color: transparent; }
.lpc-tag--warm { background: #3446a8; color: #fff; border-color: #3446a8; }
/* the view switcher under a page head (People: Connections / Leads / Boxes) */
.lpc-views { display: inline-flex; gap: 2px; padding: 4px; border-radius: var(--r-pill); background: #eef1fa; border: 1px solid #dfe4f1; justify-self: start; }
.lpc-views span { display: inline-flex; align-items: center; padding: 6px 14px; border-radius: var(--r-pill); font-size: 13px; font-weight: 600; color: #3d4a73; }
.lpc-views .lpc-views__on { background: #fff; color: #0a1024; box-shadow: 0 1px 3px -1px rgba(10, 16, 36, .18), 0 0 0 1px #dfe4f1; }
.lpc-views b { display: inline-grid; place-items: center; min-width: 22px; height: 18px; margin-left: 7px; padding: 0 6px; border-radius: var(--r-pill); background: #3446a8; color: #fff; font-family: var(--mono); font-size: 10.5px; }
/* the booking form on the Calendar card */
.lpc-form { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.lpc-form .lpc-input { color: #56628a; }

/* ── the legibility pass (2026-09-04). The frames the landing gives these
   replicas draw the 1120px design at 0.40 to 0.55, so 13px console text
   lands at 5 to 7px and #56628a on paper reads as grey smudge. The first
   answer was to widen the frames (88rem wrap, 1.7fr / 1.9fr tracks); the
   owner's verdict was "absolutely massive" and the frames went back to
   their 2026-09-03 size. So the DESIGN carries the legibility instead:
   no word below 12.5px (the rail's group kickers and the shortcut key are
   the two ornaments allowed under it), ink two steps darker, rules and strokes drawn at 2
   design px so they survive as a real pixel after the scale, tiles with
   an edge and not only a shadow, and the chart palette deepened so the
   bars and the line stand off the paper. Every value here overrides one
   above; the geometry (1120 x 780, the rail at 200, the column offset)
   is untouched, so lp-motion.js and the canvas rules need nothing new.
   These classes exist on landing.html only. */
.lpc {
  font-size: 17px;
  --muted: #2b3660; --faint: #3d4a73;
  --glass-edge: #cbd3e8; --control-border: #7a86ad;
  --green-dim: #5f70c9; --grey: #a3aed0; --grey-3: #2b3660;
}
/* the rail: brighter labels, a heavier nav weight, a rule you can see.

   THE ROWS TIGHTENED ON 2026-09-11, from 7px of vertical padding to 5px, and
   it is arithmetic rather than taste. The rails were redrawn to match the
   console as it is: Approval, Scheduled and Analysis came off, Fit, Boxes,
   Analytics and Integrations went on, and the list went from ten items to
   fifteen. At 7px that is about 813 design units of rail inside a 780-unit
   canvas, and .lp-shot clips what overflows, so Settings and the signed-in
   foot were cut off the bottom of the three feature replicas. It renders
   without erroring and looks like a design choice, which is the class of
   defect this whole file's comments exist to stop repeating. The type size is
   untouched at 15px, which is what test/landing-layout.mjs pins. */
.lpc-rail { border-right-color: rgba(140, 162, 230, .36); }
.lpc-brand { font-size: 18px; }
.lpc-search { font-size: 13px; color: #b8c3e3; border-color: rgba(140, 162, 230, .45); background: rgba(255, 255, 255, .08); padding: 8px 10px; overflow: hidden; }
/* The shortcut key is in the markup (the console shows one) but at 13px the
   placeholder alone fills the 176px pill; a clipped "Ctr" is worse than none. */
.lpc-search kbd { display: none; }
.lpc-group { font-size: 11px; letter-spacing: .12em; color: #9daecf; margin: 12px 8px 4px; }
.lpc-item { font-size: 15px; color: #d5dcf0; padding: 5px 9px; }
.lpc-item::before { width: 13px; height: 13px; border-width: 2px; opacity: 1; }
.lpc-item--on { background: rgba(255, 255, 255, .14); }
.lpc-item--last { border-top-color: rgba(140, 162, 230, .32); }
.lpc-ava { font-size: 13px; }
.lpc-who b { font-size: 14px; }
.lpc-who small { font-size: 12.5px; color: #b8c3e3; }
.lpc-dot { width: 8px; height: 8px; }
/* the page head and the pills */
.lpc-head .lpc-h { font-size: 32px; }
.lpc-head p { font-size: 15px; color: #2b3660; }
.lpc-pill { font-size: 13px; padding: 5px 11px; background: #eef1fa; color: #2b3660; border-color: #7a86ad; }
/* stat tiles: a real edge, a larger figure, a label that is a word and not a
   whisper. The uppercase 11px label is what read as nothing at 0.49. */
.lpc-stat { border: 2px solid #d5dcec; padding: 14px 16px; gap: 14px; }
.lpc-stat b { font-size: 27px; }
.lpc-stat small { font-size: 12.5px; letter-spacing: .06em; color: #3d4a73; }
.lpc-stat--hero { border-color: #15203c; }
.lpc-stat--hero small { color: #c2cbe6; }
.lpc-chip { width: 36px; height: 36px; }
.lpc-chip svg { width: 18px; height: 18px; }
/* cards: the same edge; the section headings a step larger */
.lpc-card { border: 2px solid #d5dcec; }
.lpc-ch { font-size: 23px; }
.lpc-go { font-size: 14.5px; color: #2b3660; }
.lpc-mini { font-size: 15px; color: #2b3660; gap: 24px; }
.lpc-fighead { font-size: 15px; }
.lpc-fighead small { font-size: 13px; color: #3d4a73; }
.lpc-btn { font-size: 14.5px; padding: 8px 18px; }
.lpc-ghostbtn { font-size: 14.5px; padding: 7px 15px; border-color: #7a86ad; }
.lpc-input { font-size: 15.5px; border-color: #7a86ad; border-width: 1.5px; }
.lpc-input span { color: #3d4a73; }
.lpc-live { font-size: 15.5px; border: 2px solid #d5dcec; }
.lpc-live i { width: 10px; height: 10px; }
/* charts: deeper bars with air between them, a heavier line, a thicker
   donut ring, a taller pace meter. All of these were drawn at a weight
   that suited 1120px and thinned to nothing at half of it. */
/* The larger type below the chart (pace head, meter, note, donut) needs the
   height back somewhere on the 870 hero canvas, or the donut's label is cut
   at the frame's edge. The bars give it: 150 to 126 costs nothing legible. */
.lpc-chart { gap: 8px; height: 126px; }
.lpc-chart i { background: #5f70c9; border-radius: 4px 4px 0 0; }
.lpc-chart i.hot { background: #2a3a96; }
.lpc-meter { height: 16px; background: #d5dcec; }
.lpc-meter i { background: #2a3a96; }
/* Chrome draws the uppercase caption a few units lower than its box says
   and the 150-unit svg clipped the lower half of INVITATIONS at any weight
   above the old one; the card has 30 units of room below, so let it out. */
.lpc-donut svg { overflow: visible; }
.lpc-donut circle { stroke-width: 24; }
.lpc-donut .t { stroke: #e4e9f6; }
.lpc-donut .s1 { stroke: #2a3a96; }
.lpc-donut .s2 { stroke: #5f70c9; }
.lpc-donut .s3 { stroke: #a3aed0; }
.lpc-donut text { font-size: 30px; }
.lpc-donut .sub { font-size: 13px; letter-spacing: .08em; }
.lpc-line path.l { stroke: #2a3a96; stroke-width: 3.5; }
.lpc-line path.a { fill: #2a3a96; opacity: .12; }
.lpc-line circle { fill: #2a3a96; stroke-width: 3; }
/* rows, tags, the view switcher */
.lpc-row { padding: 12px 2px; border-top: 2px solid #e4e9f6; }
.lpc-row:first-of-type { border-top: none; }
.lpc-row b { font-size: 16px; }
.lpc-row small { font-size: 14px; color: #2b3660; }
.lpc-when { font-size: 13px; color: #3d4a73; }
.lpc-row .lpc-ava { width: 40px; height: 40px; font-size: 13px; }
.lpc-tag { font-size: 12.5px; padding: 2px 9px; color: #2b3660; background: #e4e9f6; }
.lpc-views { border: 2px solid #d5dcec; }
.lpc-views span { font-size: 14.5px; color: #2b3660; }
.lpc-views b { font-size: 12px; min-width: 24px; height: 20px; }
.lpc-form .lpc-input { color: #3d4a73; }

/* ── the Inbox replica (2026-09-11) ────────────────────────────────────
   Three primitives the second feature row needed and the kit did not have:
   the two-column page, a conversation turn, and the block the console draws
   under a thread. Everything else in that replica is lpc-card, lpc-row,
   lpc-ava, lpc-pill, lpc-btn, lpc-ghostbtn and lpc-fighead, unchanged.

   The 14px floor on a bubble is not decoration. These replicas draw at about
   0.49, so 14px lands at just under 7 real pixels and anything smaller is the
   "almost fading out" the owner rejected on 2026-09-04. The message text is
   the one thing in this replica a reader must actually be able to read, so it
   is the thing that may not be shrunk to make the card fit. */
.lpc-inbox { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); gap: 18px; align-items: start; }
.lpc-bubble {
  max-width: 86%; padding: 12px 16px; border-radius: 14px;
  font-size: 14px; line-height: 1.5; color: #0a1024;
  background: #eef1fa; border: 2px solid #d5dcec;
  border-bottom-left-radius: 5px;
}
/* Yours sits to the right in the accent, theirs to the left on paper, which
   is the same handedness the console's own thread uses. */
.lpc-bubble--you {
  margin-left: auto; background: #3446a8; border-color: #3446a8; color: #fff;
  border-bottom-left-radius: 14px; border-bottom-right-radius: 5px;
}
.lpc-brief {
  display: grid; gap: 8px; padding: 14px 16px; border-radius: 12px;
  background: #eef1fa; border: 2px solid #d5dcec;
}
.lpc-brief p { margin: 0; font-size: 15px; line-height: 1.5; color: #2b3660; }
.lpc-brief p b { color: #0a1024; font-weight: 650; }
.lpc-brief .lpc-row { border-top-color: #d5dcec; }

/* THE HERO CANVAS IS TALLER THAN THE OTHERS. landing.css draws the hero
   shot at 1120 / 900 while the canvas above is 780 high, so the hero showed
   120 units of bare paper under the rail and the column. Invisible while the
   shot and the canvas were the same dark colour; the light paper exposed it
   (owner, 2026-09-02: "a gap at the bottom"). The canvas matches the shot,
   and the last card takes the extra height so the column reads as full.

   THREE TRACKS SINCE 2026-09-11, not four. The stat tile row came off the
   hero with the rebuild, because the real Home has none: those figures moved
   inside the Outreach card when Analytics took the charts. So the canvas
   holds the page head, the plan card and the Outreach card. A fourth 1fr
   track with nothing in it would have swallowed every spare unit and put the
   gap back at the foot, which is the exact fault this rule exists to fix. */
.lp-window--hero .lpc { height: 900px; grid-template-rows: auto auto minmax(0, 1fr); }
.lp-window--hero .lpc > .lpc-card:last-child { align-content: space-between; }

/* The hero window sits on a soft glow. Its motion is scroll-LINKED, not
   looped: lp-motion.js tips it back a few degrees and settles it flat as it
   scrolls into place, so the movement answers the reader's hand instead of
   idling forever. Without that script (or under reduced motion) it simply
   stands flat — the transform below is only ever written by JS. */
.lp-window--hero {
  position: relative;
  box-shadow: var(--lift-glass), 0 40px 120px -40px rgba(210, 220, 255, .4);
  transform-origin: 50% 100%;
  will-change: transform;
}

/* ── the guarantees ledger: one continuous strip ruled into cells by
   hairlines — a table of record, not a row of icon tiles. Each cell is a
   mono index, an oversized serif figure, and its label. The strip's own
   border clips the cells' reveal, so they rise into view as if from
   behind the rule. */
.lp-trust { padding-block: clamp(44px, 7vw, 80px) 8px; display: grid; gap: 18px; }
.lp-band__label { text-align: center; color: var(--faint); font-size: 13px; }
.lp-band {
  display: grid; grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--glass-edge); border-radius: 14px; overflow: hidden;
  background: rgba(130, 150, 225, .04);
}
.lp-stat { position: relative; display: grid; justify-items: start; text-align: left; gap: 7px; padding: 24px 26px 20px; }
.lp-stat:nth-child(n+2) { border-left: 1px solid var(--glass-edge); }
.lp-stat__ix { font-family: var(--mono); font-size: 10.5px; letter-spacing: .16em; color: var(--faint); }
.lp-stat__n { font-family: "Fraunces", var(--serif, Georgia, serif); font-size: clamp(30px, 3.4vw, 42px); font-weight: 480; letter-spacing: -.02em; line-height: 1; color: var(--white); font-variant-numeric: tabular-nums; margin-top: 4px; }
.lp-stat__l { font-size: 13px; color: var(--muted); }
@media (max-width: 860px) {
  .lp-band { grid-template-columns: 1fr 1fr; }
  .lp-stat:nth-child(n+2) { border-left: 0; }
  .lp-stat:nth-child(2n) { border-left: 1px solid var(--glass-edge); }
  .lp-stat:nth-child(n+3) { border-top: 1px solid var(--glass-edge); }
}
@media (max-width: 460px) {
  .lp-band { grid-template-columns: 1fr; }
  .lp-stat:nth-child(2n) { border-left: 0; }
  .lp-stat:nth-child(n+2) { border-top: 1px solid var(--glass-edge); }
}

/* ── section scaffolding. Heads are LEFT-aligned now — every section opens
   with a heading block at the column's left edge, not a centred slab. */
.lp-section { padding-block: clamp(36px, 5.5vw, 64px); }
.lp-section__head { display: grid; gap: 12px; justify-items: start; text-align: left; margin-bottom: 34px; max-width: 46rem; }
/* kickers speak the ledger's language now: mono, indexed, quietly ruled —
   the section numbers 01 to 04 walk down the page like ledger lines */


.lp-section__head h2 { font-size: clamp(26px, 3.6vw, 38px); line-height: 1.12; letter-spacing: -.01em; font-weight: 560; max-width: 24ch; }
.lp-section__head p { color: var(--muted); max-width: 54ch; font-size: 16px; line-height: 1.6; }

/* ── feature panels: each feature is a full-width rounded PANEL — the
   framed console on its own small painted stage on one side, a title, two
   lines and one arrow link on the other. `--flip` mirrors it. No number
   watermarks, no checklists: title, sentence, link. */
.lp-feature {
  display: grid; grid-template-columns: 1fr 1.3fr;
  gap: clamp(24px, 4vw, 56px); align-items: center;
  border: 1px solid var(--glass-edge); border-radius: 18px;
  background: linear-gradient(165deg, rgba(20, 27, 52, .6), rgba(12, 17, 34, .35));
  padding: clamp(20px, 3.5vw, 44px);
  margin-block: 20px;
}
.lp-feature--flip { grid-template-columns: 1.3fr 1fr; }
.lp-feature--flip .lp-feature__media { order: -1; }
@media (max-width: 860px) {
  .lp-feature, .lp-feature--flip { grid-template-columns: 1fr; }
  .lp-feature--flip .lp-feature__media { order: 0; }
}
.lp-feature h2 { font-size: clamp(22px, 2.7vw, 29px); line-height: 1.14; letter-spacing: -.015em; font-weight: 560; }
.lp-feature > div > p { color: var(--muted); margin-top: 12px; line-height: 1.65; max-width: 38ch; }
/* the media side carries its own weather, one step brighter than the page */
.lp-feature__media {
  margin: 0; padding: clamp(14px, 2.5vw, 30px);
  border-radius: 12px;
  background:
    radial-gradient(70% 100% at 20% 10%, rgba(255, 255, 255, .12), transparent 60%),
    radial-gradient(80% 110% at 85% 95%, rgba(66, 92, 214, .45), transparent 66%),
    linear-gradient(160deg, #192450, #0d1329);
  border: 1px solid var(--glass-edge);
}
.lp-feature__media .lp-window { margin-top: 0; }
/* the arrow link: a drawn SVG arrow, not a glyph. At rest the shaft is a
   stub shy of its head; on hover the underline draws under the words, the
   shaft completes the wire, and the head travels — the link literally
   connects. */
.lp-more {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 18px;
  color: var(--green); font-weight: 600; font-size: 14.5px; text-decoration: none;
}
.lp-more span {
  padding-bottom: 2px;
  background: linear-gradient(currentColor, currentColor) 0 100% / 0% 1px no-repeat;
  transition: background-size .35s var(--ease-out, var(--ease));
}
.lp-more:hover span { background-size: 100% 1px; }
.lp-more__arr { width: 20px; height: 10px; flex: none; overflow: visible; }
.lp-more__arr .a-shaft { stroke-dasharray: 14; stroke-dashoffset: 8; transition: stroke-dashoffset .35s var(--ease-out, var(--ease)); }
.lp-more:hover .a-shaft { stroke-dashoffset: 0; }
.lp-more__arr .a-head { transition: transform .35s var(--ease-out, var(--ease)); }
.lp-more:hover .a-head { transform: translateX(3px); }

/* ── principles: bordered cards straight on the page — the geometry of a
   quote wall, carrying the refusals instead of endorsements. No icon chips:
   a mono index on the left, the bare stroke mark on the right, then air,
   then the words. Hover brightens the ink and the light finds the pointer
   (the ::after specular below) — the card itself does not jump. */
.lp-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 14px; }
.lp-card { position: relative; padding: 24px; border: 1px solid var(--glass-edge); border-radius: 14px; background: rgba(130, 150, 225, .04); transition: border-color .25s var(--ease), background .25s var(--ease); }
.lp-card:hover { border-color: rgba(200, 214, 255, .34); background: rgba(130, 150, 225, .07); }
.lp-card__top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 38px; color: var(--faint); transition: color .25s var(--ease); }
.lp-card:hover .lp-card__top { color: var(--grey-3); }
.lp-card__ix { font-family: var(--mono); font-size: 10.5px; letter-spacing: .16em; }
.lp-card__top svg { width: 22px; height: 22px; flex: none; }
.lp-card h3 { font-size: 17px; font-weight: 600; }
.lp-card p { color: var(--muted); font-size: 14px; margin-top: 8px; line-height: 1.6; }

/* liquid-glass hover: a soft specular that follows the pointer across the
   card (lp-motion.js writes --mx/--my). Positional feedback, not animation,
   so it needs no reduced-motion switch; without the script it simply glows
   from the resting position. */
.lp-card::after, .lp-stat::after, .lp-step::after, .lp-grant__node::after, .lp-grant__cap::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: radial-gradient(230px circle at var(--mx, 50%) var(--my, 30%), rgba(205, 218, 255, .1), transparent 65%);
  opacity: 0; transition: opacity .3s var(--ease);
}
.lp-card:hover::after, .lp-stat:hover::after, .lp-step:hover::after, .lp-grant__node:hover::after, .lp-grant__cap:hover::after { opacity: 1; }

/* ── how it works: three cards standing as STATIONS ON THE WIRE — the
   logo's cable drawn at section scale. The catenary sags between three
   terminal nodes (one per card), draws itself in on reveal, and then a
   faint current runs along it. Numbered coins are gone; each card leads
   with a bare oversized serif numeral. */
.lp-steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
@media (max-width: 720px) { .lp-steps { grid-template-columns: 1fr; } }
.lp-step { position: relative; display: grid; justify-items: start; text-align: left; gap: 10px; align-content: start; padding: 24px; border: 1px solid var(--glass-edge); border-radius: 14px; background: rgba(130, 150, 225, .04); transition: border-color .25s var(--ease); }
.lp-step:hover { border-color: rgba(200, 214, 255, .3); }
.lp-step__n { font-family: "Fraunces", var(--serif, Georgia, serif); font-weight: 440; font-size: 38px; line-height: 1; color: var(--white); }
.lp-step__t { font-size: 17px; font-weight: 600; }
.lp-step p { color: var(--muted); font-size: 14px; line-height: 1.55; }

/* The wire itself. Visible-by-default is the contract: only under
   .js-reveal (script armed, motion allowed) does it start undrawn, so a
   blocked script or reduced motion shows the finished cable, never a blank
   band. pathLength=100 on the paths makes the dash arithmetic unit-free. */
.lp-steps-wire { margin-bottom: -2px; }
.lp-steps-wire svg { display: block; width: 100%; height: 56px; overflow: visible; }
@media (max-width: 720px) { .lp-steps-wire { display: none; } }
/* The final cable is a FLOW row, not an overlay: the last grid child of
   .lp-final, below the whisper line, sinking into the section's bottom
   padding. Keep it in flow — as an absolute overlay it cut across the
   button and the whisper at every viewport width. */
.lp-final__wire { justify-self: center; width: min(1400px, 108vw); max-width: none; margin-top: clamp(2px, 1.5vw, 14px); margin-bottom: calc(-1 * clamp(36px, 6vw, 84px)); pointer-events: none; }
.lp-final__wire svg { display: block; width: 100%; height: clamp(64px, 11vw, 140px); overflow: visible; }

.w-cable {
  fill: none; stroke: rgba(205, 218, 255, .3); stroke-width: 1.5;
  vector-effect: non-scaling-stroke; stroke-linecap: round;
  stroke-dasharray: 100 100; stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.4s var(--ease-out, var(--ease)) .15s;
}
.w-current {
  fill: none; stroke: rgba(255, 255, 255, .75); stroke-width: 1.5;
  vector-effect: non-scaling-stroke; stroke-linecap: round;
  stroke-dasharray: .7 5.3; opacity: .55;
  transition: opacity .8s var(--ease) 1.2s;
}
.w-node { fill: #fff; transition: opacity .5s var(--ease) 1s; }
.w-halo { fill: rgba(255, 255, 255, .14); transition: opacity .5s var(--ease) 1s; }
.js-reveal .lp-steps-wire:not(.is-in) .w-cable,
.js-reveal .lp-grant:not(.is-in) .w-cable,
.js-reveal .lp-final:not(.is-in) .w-cable { stroke-dashoffset: 100; }
.js-reveal .lp-steps-wire:not(.is-in) .w-current, .js-reveal .lp-steps-wire:not(.is-in) .w-node, .js-reveal .lp-steps-wire:not(.is-in) .w-halo,
.js-reveal .lp-grant:not(.is-in) .w-current, .js-reveal .lp-grant:not(.is-in) .w-node, .js-reveal .lp-grant:not(.is-in) .w-halo,
.js-reveal .lp-final:not(.is-in) .w-current, .js-reveal .lp-final:not(.is-in) .w-node, .js-reveal .lp-final:not(.is-in) .w-halo { opacity: 0; }
.js-reveal .lp-steps-wire.is-in .w-current, .js-reveal .lp-grant.is-in .w-current, .js-reveal .lp-final.is-in .w-current { animation: lp-current 2.8s linear infinite; }
@keyframes lp-current { to { stroke-dashoffset: -6; } }

/* A replica below the fold holds its build until its panel reveals, so the
   bars grow and the donuts sweep in front of the reader, not off-screen.
   Without JS nothing is paused and every replica simply stands finished. */
.js-reveal [data-reveal]:not(.is-in) .lpc :is(.lpc-chart i, .lpc-meter i, .lpc-donut .s1, .lpc-donut .s2, .lpc-donut .s3, .lpc-line path.l, .lpc-row, .lpc-swap .lpc-pill--good) { animation-play-state: paused; }

/* ── google: the grant, drawn (#google) ────────────────────────────────
   One source, one cable, two capabilities. It reuses the .w-* cable
   vocabulary above rather than growing a second one, which is why
   .lp-grant had to be named in those three reveal selectors: the classes
   are shared, the gating is per-container. Below 860px the cable is
   dropped rather than squashed, exactly as .lp-steps-wire drops at 720,
   because a two-branch cable folded into a phone column draws a shape
   that means nothing. */
.lp-grant {
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(90px, 12vw, 190px) minmax(0, 1.15fr);
  align-items: center;
  gap: clamp(10px, 2vw, 22px);
  margin-bottom: clamp(28px, 4vw, 46px);
}
.lp-grant__node, .lp-grant__cap {
  position: relative;  /* the shared specular ::after above anchors here */
  padding: 22px 24px; border: 1px solid var(--glass-edge); border-radius: 14px;
  background: rgba(130, 150, 225, .04);
  transition: border-color .25s var(--ease), background .25s var(--ease);
}
.lp-grant__node:hover, .lp-grant__cap:hover { border-color: rgba(200, 214, 255, .34); background: rgba(130, 150, 225, .07); }
.lp-grant__ix { display: block; font-family: var(--mono); font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--faint); margin-bottom: 8px; }
.lp-grant__t { display: block; font-size: 17px; font-weight: 600; }
.lp-grant__node p, .lp-grant__cap p { color: var(--muted); font-size: 14px; line-height: 1.6; margin-top: 7px; }
.lp-grant__wire svg { display: block; width: 100%; height: clamp(150px, 20vw, 210px); overflow: visible; }
.lp-grant__caps { display: grid; gap: 12px; }
.lp-grant__cap { display: flex; gap: 14px; align-items: flex-start; }
.lp-grant__ico {
  width: 38px; height: 38px; border-radius: 10px; flex: none; display: grid; place-items: center;
  background: rgba(255, 255, 255, .08); border: 1px solid var(--control-border); color: #fff;
}
.lp-grant__ico svg { width: 20px; height: 20px; }
.lp-grant__cap code {
  display: inline-block; margin-top: 10px; font-family: var(--mono); font-size: 11.5px;
  color: var(--grey-3); background: rgba(0, 0, 0, .3); border: 1px solid var(--control-border);
  border-radius: var(--r-pill); padding: 2px 11px;
}
@media (max-width: 860px) {
  .lp-grant { grid-template-columns: 1fr; }
  .lp-grant__wire { display: none; }
}

/* ── google: the consent widget ────────────────────────────────────────
   The marketing argument made visually. Two lists: what is asked for,
   then the longer one that is never asked for, because the refusals are
   the part worth reading. The rows deal in and their marks draw
   themselves, staggered by --i on each <li> and inherited by the paths
   inside it; below the fold the whole build holds until the panel
   reveals, the same contract the .lpc replicas keep. */
.lp-consent {
  position: relative; overflow: hidden;
  border: 1px solid var(--glass-edge); border-radius: 16px;
  background: linear-gradient(160deg, rgba(140, 162, 230, .1), rgba(10, 15, 30, .5));
  box-shadow: inset 0 1px 0 var(--glass-spec), 0 22px 50px -30px rgba(0, 0, 0, .9);
  padding: 22px 24px 16px; display: grid; gap: 12px; align-content: start;
}
.lp-consent__head { display: flex; align-items: baseline; gap: 10px; }
.lp-consent__ix { font-family: var(--mono); font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--faint); }
.lp-consent__ix--no { color: var(--grey, #6b7aa0); }
.lp-consent__t { font-size: 15px; font-weight: 650; }
.lp-consent__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 11px; }
.lp-consent__row { display: flex; align-items: flex-start; gap: 11px; }
.lp-consent__row b { display: block; font-size: 14px; font-weight: 600; }
.lp-consent__row small { display: block; font-family: var(--mono); font-size: 11.5px; color: var(--faint); margin-top: 3px; }
.lp-consent__list--no b { color: var(--grey-3); font-weight: 500; }
.lp-consent__tick, .lp-consent__cross { width: 21px; height: 21px; flex: none; margin-top: 1px; }
.lp-consent__tick { color: #fff; }
.lp-consent__cross { color: var(--grey, #6b7aa0); }
.lp-consent__rule { height: 1px; background: var(--glass-edge); margin: 3px 0 1px; }
.lp-consent__foot {
  display: flex; align-items: center; gap: 9px; font-size: 12.5px; color: var(--muted);
  border-top: 1px solid var(--glass-edge); padding-top: 13px; margin-top: 3px;
}
.lp-consent__revoke { width: 7px; height: 7px; border-radius: 50%; flex: none; background: #fff; box-shadow: 0 0 10px rgba(255, 255, 255, .7); animation: lpc-pulse 2.4s ease-in-out infinite; }
.lp-consent__row { animation: lp-consent-in .5s var(--ease-out, var(--ease)) both; animation-delay: calc(.1s + var(--i, 0) * .1s); }
.lp-consent__tick path, .lp-consent__cross path { stroke-dasharray: 30; animation: lp-consent-draw .55s var(--ease-out, var(--ease)) both; animation-delay: calc(.26s + var(--i, 0) * .1s); }
@keyframes lp-consent-in { from { opacity: 0; transform: translateY(10px); } }
@keyframes lp-consent-draw { from { stroke-dashoffset: 30; } }
.js-reveal [data-reveal]:not(.is-in) :is(.lp-consent__row, .lp-consent__tick path, .lp-consent__cross path, .lp-consent__revoke) { animation-play-state: paused; }

/* ── google: two replica primitives the Calendar card needed ───────────
   A three-up stat band (the kit had four-up and two-up only) and the row
   that pairs the filled queue meter with its push button. */
.lpc-stats--three { grid-template-columns: repeat(3, 1fr); }
.lpc-pushrow { display: flex; align-items: center; gap: 16px; }

/* ── faq: native <details> in the card language — works with no JS, and
   the straight answers are the trust device, so nothing decorates them. */
.lp-faq { display: grid; gap: 12px; max-width: 46rem; }
.lp-faq details {
  border: 1px solid var(--glass-edge); border-radius: 14px;
  background: rgba(130, 150, 225, .04);
  transition: border-color .18s var(--ease);
}
.lp-faq details:hover, .lp-faq details[open] { border-color: rgba(255, 255, 255, .3); }
.lp-faq summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  padding: 18px 22px;
  font-size: 15.5px; font-weight: 600; color: var(--text);
}
.lp-faq summary::-webkit-details-marker { display: none; }
.lp-faq summary::after {
  content: "+"; flex: none;
  font-family: var(--mono); font-size: 17px; color: var(--faint);
  transition: transform .18s var(--ease);
}
.lp-faq details[open] summary::after { transform: rotate(45deg); }
.lp-faq details p { padding: 0 22px 20px; color: var(--muted); font-size: 14.5px; line-height: 1.7; max-width: 62ch; }

/* ── final call to action: open space, one giant display line, one white
   pill, and a monospace whisper of how it actually runs. No card. Beneath
   it all, the wire at page scale: a deep catenary between two terminals,
   drawn on reveal, current running — the logo become landscape, as the
   section's horizon rather than its background. */
/*
   grid-template-columns is minmax(0, 1fr) rather than the implicit `auto`,
   and that single declaration is what stops the whisper line being cut off
   on a phone. An auto track sizes to the widest item's max-content, and one
   of the items here is the wire at `width: min(1400px, 108vw)` — deliberately
   wider than the panel, since it bleeds into the padding. That inflated the
   track to 108vw, and `justify-items: center` then centred the text inside a
   track wider than the viewport, so `overflow: hidden` clipped it. Measured
   2026-08-23 before the fix: the tagline ran 24px past the viewport at 430,
   42px at 390, 45px at 360. minmax(0, 1fr) pins the track to the content box;
   the wire still overflows it and is still clipped exactly as before, which
   is the look it was always drawn for.
*/
.lp-final { overflow: hidden; text-align: center; display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; justify-items: center; padding: clamp(64px, 10vw, 140px) var(--gutter) clamp(56px, 8vw, 110px); }
.lp-final h2 { font-size: clamp(38px, 6.5vw, 76px); line-height: 1.04; letter-spacing: -.015em; font-weight: 540; max-width: 16ch; color: #d9e0fb; }
.lp-final h2 em { font-style: italic; color: #ffffff; }
.lp-final code { font-family: var(--mono); color: var(--faint); font-size: 12.5px; background: none; border: none; padding: 0; max-width: 100%; }

/* ── footer: link columns only over a near-void base, then a bottom bar. */
.lp-footer { border-top: 1px solid var(--glass-edge); margin-top: 24px; background: linear-gradient(180deg, transparent, rgba(5, 7, 15, .6)); }
.lp-foot { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(24px, 4vw, 48px); padding-block: 52px 40px; }
@media (max-width: 640px) { .lp-foot { grid-template-columns: 1fr; gap: 32px; } }
.lp-foot__tag { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--green); font-family: var(--mono); }
.lp-foot__tag i { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 10px var(--green); }
.lp-foot__col { display: grid; gap: 11px; align-content: start; justify-items: start; }
.lp-foot__col h3 { font-size: 11px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--faint); font-family: var(--sans); }
.lp-foot__col a { color: var(--grey-3); text-decoration: none; font-size: 14px; transition: color .15s var(--ease); }
.lp-foot__col a:hover { color: var(--green); }
.lp-foot__bar { border-top: 1px solid var(--glass-edge); padding-block: 20px; display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; color: var(--faint); font-size: 12.5px; }
.lp-foot__bar span { font-family: var(--mono); }
/* The social buttons beside the copyright (2026-09-10). Three round glass
   buttons, icon only, in the footer's own language: the same edge and faint
   ink as the bar, the same green on hover as the column links. 40px is the
   tap floor test/width-sweep.mjs holds every public page to; they shipped at
   36 because that sweep is not in the deploy gate, and were caught the day
   after.
   They sit on the LEFT, next to the copyright, not at the right end of the
   bar: the survey dock (lp-agent.css) is fixed to the bottom-right corner
   and a button placed there is under it whenever the reader reaches the end
   of the page, which is the only time they would press it. Measured, not
   guessed: at 1440 the dock covered all three. At 640 and under the bar
   centres, the row drops onto its own line under the copyright, and the bar
   gains enough bottom padding to clear the dock's launcher, which spans the
   bottom of a phone screen. */
.lp-foot__social { display: flex; align-items: center; gap: 8px; margin-inline: 8px auto; }
.lp-foot__social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 50%;
  color: var(--grey-3);
  border: 1px solid var(--glass-edge);
  background: linear-gradient(160deg, rgba(140, 162, 230, .09), rgba(140, 162, 230, .02));
  transition: color .15s var(--ease), border-color .15s var(--ease), transform .15s var(--ease);
}
.lp-foot__social a:hover, .lp-foot__social a:focus-visible { color: var(--green); border-color: var(--border-strong); transform: translateY(-1px); }
.lp-foot__social a:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
.lp-foot__social svg { display: block; }
@media (max-width: 640px) { .lp-foot__bar { justify-content: center; text-align: center; padding-bottom: 88px; } .lp-foot__social { width: 100%; justify-content: center; margin-inline: 0; } }

/* ── scroll reveal — scoped to .js-reveal so a failed or absent script can
   never leave content invisible (a print, a screenshot, a blocked script all
   render everything). The class is added only after the observer is armed. */
.js-reveal [data-reveal] { opacity: 0; transform: translateY(26px); filter: blur(7px); transition: opacity .5s var(--ease-out, var(--ease)), transform .7s var(--ease-out, var(--ease)), filter .55s var(--ease-out, var(--ease)); }
.js-reveal [data-reveal].is-in { opacity: 1; transform: none; filter: none; }
/* stagger children of the band, steps and grid so a row deals in */
.js-reveal .lp-band [data-reveal]:nth-child(2), .js-reveal .lp-steps [data-reveal]:nth-child(2), .js-reveal .lp-grid [data-reveal]:nth-child(2) { transition-delay: .08s; }
.js-reveal .lp-band [data-reveal]:nth-child(3), .js-reveal .lp-steps [data-reveal]:nth-child(3), .js-reveal .lp-grid [data-reveal]:nth-child(3) { transition-delay: .16s; }
.js-reveal .lp-band [data-reveal]:nth-child(4), .js-reveal .lp-grid [data-reveal]:nth-child(4) { transition-delay: .24s; }
/* There was a fifth step here between 2026-09-07 and 2026-09-09, for the Team
   card. The pricing grid is back to four cards, so the cascade ends at four
   again and nothing on the site has a fifth revealed child. */

@media (prefers-reduced-motion: reduce) {
  .js-reveal [data-reveal] { opacity: 1 !important; transform: none !important; filter: none !important; transition: none !important; }
  .lp-hero > * { animation: none !important; }
  .cs-inner > *, .cs-access[open] .cs-signin { animation: none !important; }
  .w-current { animation: none !important; }
  .lpc *, .lpc { animation: none !important; }
  /* the consent widget stands finished: every row present, every mark
     drawn, the revoke dot lit and still. The list IS the argument, so it
     must never depend on motion to be readable. */
  .lp-consent__row, .lp-consent__revoke { animation: none !important; }
  .lp-consent__tick path, .lp-consent__cross path { animation: none !important; stroke-dasharray: none !important; }
  /* the button's specular sweep is a travelling element, not a colour
     fade — under reduced motion it does not run (the brightness hover
     from styles.css still answers the pointer) */
  body.lp .btn--primary::after { display: none; }
}

/* =========================================================================
   Coming-soon gate (the pre-launch front door). Uses the landing's theme
   (body.lp: indigo base, Fraunces display, white accent, aurora). The
   public sees this; a signed-in dev is served the real landing instead.
   ========================================================================= */
.cs { min-height: 100dvh; display: grid; grid-template-rows: auto 1fr auto; }
.cs-top { padding-block: 22px; }
.cs-top .lp-wrap { display: flex; justify-content: center; }
.cs-main { display: grid; place-items: center; padding: 32px var(--gutter) 56px; }
.cs-inner { display: grid; justify-items: center; text-align: center; gap: 22px; width: 100%; max-width: 42rem; }
.cs-inner > * { animation: lp-rise .7s var(--ease) both; }
.cs-inner > *:nth-child(2) { animation-delay: .08s; }
.cs-inner > *:nth-child(3) { animation-delay: .16s; }
.cs-inner > *:nth-child(4) { animation-delay: .24s; }

.cs-h1 { font-size: clamp(46px, 9vw, 92px); line-height: 1; letter-spacing: -.02em; font-weight: 500; color: #d9e0fb; }
.cs-h1 em { font-style: italic; color: #ffffff; }
.cs-lead { color: var(--muted); font-size: clamp(15px, 2vw, 18px); line-height: 1.6; max-width: 46ch; text-wrap: balance; }

/* the waiting list — the gate's one call to action: a glass pill holding an
   email field and the white button, in the same language as everything else.
   The pitch sits with the form, not in the lead — and `balance` keeps both
   prose lines even instead of stranding a widow word. */
.cs-wait-block { display: grid; justify-items: center; gap: 16px; width: 100%; margin-top: 12px; }
.cs-wait__pitch { color: var(--muted); font-size: 15px; line-height: 1.6; max-width: 52ch; text-wrap: balance; }
.cs-wait {
  display: flex; align-items: center; gap: 8px;
  width: min(30rem, 100%); padding: 6px 6px 6px 0;
  border: 1px solid var(--glass-edge); border-radius: var(--r-pill);
  background: linear-gradient(160deg, rgba(140, 162, 230, .09), rgba(140, 162, 230, .02));
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  box-shadow: var(--lift-glass);
  transition: border-color .16s var(--ease);
}
@supports not (backdrop-filter: blur(1px)) { .cs-wait { background: var(--surface); } }
.cs-wait:focus-within { border-color: var(--border-strong); }
.cs-wait input {
  flex: 1; min-width: 0;
  /* 16px for the same reason as the select below: under 16 and iOS zooms the
     page on focus and never restores it. */
  font: inherit; font-size: 16px; color: var(--text);
  background: transparent; border: 0; outline: none;
  padding: 10px 12px 10px 18px;
}
.cs-wait input::placeholder { color: var(--faint); }
.cs-wait .btn { flex-shrink: 0; }
.cs-wait__note { width: min(30rem, 100%); text-align: left; }
.cs-wait__note--done { text-align: center; }

/* The optional question. Deliberately quieter than the email row: it sits
   below the pill, in muted text, so it reads as an aside rather than a second
   thing to get past. A field that looks required is a field that costs
   signups, and this one is worth nothing if it deters the address. */
.cs-wait__why {
  display: grid; gap: 6px; justify-items: center;
  width: min(30rem, 100%);
  font-size: 13px; color: var(--faint);
}
.cs-wait__why select {
  width: 100%;
  /* 16px, not 14. Under 16 and iOS Safari zooms the page the moment this is
     tapped, and never zooms back — on the one screen whose entire job is to
     take a sign-up. */
  font: inherit; font-size: 16px; color: var(--muted);
  min-height: var(--tap, 40px);
  background: var(--surface); border: 1px solid var(--glass-edge);
  border-radius: var(--r-sm, 10px);
  padding: 9px 12px; outline: none;
  transition: border-color .16s var(--ease);
}
.cs-wait__why select:focus-visible { border-color: var(--border-strong); color: var(--text); }
@media (max-width: 560px) {
  .cs-wait { flex-direction: column; align-items: stretch; gap: 8px; padding: 10px; border-radius: 24px; }
  .cs-wait input { text-align: center; padding: 10px; }
}

/* the dev sign-in — a compact glass card in the same language */
.cs-signin {
  width: min(23rem, 100%); margin: 16px auto 0; text-align: left;
  border: 1px solid var(--glass-edge); border-radius: 18px; padding: 22px;
  background: linear-gradient(160deg, rgba(140, 162, 230, .09), rgba(140, 162, 230, .02));
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  box-shadow: var(--lift-glass);
  display: grid; gap: 14px;
}
@supports not (backdrop-filter: blur(1px)) { .cs-signin { background: var(--surface); } }
.cs-signin h2 { font-size: 19px; font-weight: 500; letter-spacing: -.01em; }
.cs-signin .muted { font-size: 12.5px; }

/* Bottom header: a quiet footer bar with the terms and the subtle sign-in. */
.cs-foot { border-top: 1px solid var(--glass-edge); padding-block: 16px; background: rgba(10, 15, 30, .4); }
.cs-foot__row { display: flex; align-items: center; justify-content: space-between; gap: 10px 22px; flex-wrap: wrap; color: var(--faint); font-size: 12.5px; }
.cs-foot__meta { font-family: var(--mono); }
.cs-foot__nav { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.cs-foot__nav a { color: var(--faint); text-decoration: none; transition: color .15s var(--ease); }
.cs-foot__nav a:hover { color: var(--text); }

/* The team sign-in is a SUBTLE footer link that opens a small popover above
   it — a native <details>, so it works with no JavaScript. */
.cs-access { position: relative; }
.cs-access__toggle {
  list-style: none; cursor: pointer;
  color: var(--faint); font-size: 12.5px;
  transition: color .15s var(--ease);
}
.cs-access__toggle::-webkit-details-marker { display: none; }
.cs-access__toggle::marker { content: ""; }
.cs-access__toggle:hover, .cs-access[open] .cs-access__toggle { color: var(--text); }
/* Hide the content when closed EXPLICITLY: `.cs-signin { display: grid }` is
   author CSS and overrides the UA rule that normally collapses a closed
   <details>, so the box would otherwise stay visible. */
.cs-access:not([open]) .cs-signin { display: none; }
.cs-access[open] .cs-signin {
  position: absolute; right: 0; bottom: calc(100% + 16px); z-index: 20;
  width: min(23rem, 88vw); margin: 0;
  animation: lp-rise .3s var(--ease) both;
}
@media (max-width: 560px) {
  .cs-access[open] .cs-signin { position: fixed; left: 50%; right: auto; bottom: 64px; transform: translateX(-50%); }
}

/* ── legal pages (Terms & Conditions / Privacy) — same theme, readable prose */
.cs-legal { max-width: 46rem; margin-inline: auto; padding: 40px var(--gutter) 72px; }
.cs-legal h1 { font-size: clamp(32px, 5vw, 48px); line-height: 1.06; letter-spacing: -.015em; font-weight: 500; color: #d9e0fb; }
.cs-legal .cs-updated { display: block; margin-top: 10px; color: var(--faint); font-family: var(--mono); font-size: 12.5px; }
.cs-legal h2 { font-size: clamp(20px, 3vw, 25px); letter-spacing: -.01em; font-weight: 500; margin-top: 40px; color: var(--text); }
.cs-legal p, .cs-legal li { color: var(--muted); line-height: 1.75; font-size: 15.5px; }
.cs-legal p { margin-top: 14px; max-width: 60ch; }
.cs-legal ul { margin: 14px 0 0; padding-left: 20px; display: grid; gap: 8px; max-width: 60ch; }
.cs-legal a { color: var(--text); text-decoration-color: var(--control-border); }
.cs-legal a:hover { text-decoration-color: var(--text); }
.cs-legal .cs-back { display: inline-flex; align-items: center; gap: 6px; margin-bottom: 26px; color: var(--faint); text-decoration: none; font-size: 13px; }
.cs-legal .cs-back:hover { color: var(--text); }

/* ------------------------------------------------------------- login */

.site--centred { min-height: 100vh; display: grid; place-items: center; padding: 32px 24px; }
.signin { width: min(25rem, 100%); display: grid; gap: 16px; }
.centred { text-align: center; }
.site__brand { text-decoration: none; color: var(--text); }
.site__brand--big { justify-content: center; margin-bottom: 4px; }
.site__brand--big .logo__word { font-size: 21px; }

/* ------------------------------------------------------------- intro */

.intro {
  position: fixed; inset: 0; z-index: 100;
  display: grid; place-items: center;
  background: radial-gradient(60vmax 60vmax at 50% 45%, #0d1117, var(--void) 70%);
  backdrop-filter: blur(30px) saturate(160%);
  -webkit-backdrop-filter: blur(30px) saturate(160%);
}
.intro__inner { display: grid; justify-items: center; gap: 18px; }

.intro__mark {
  color: var(--white);
  border-radius: 14px;
  box-shadow: 0 0 0 1px rgba(255,255,255,.14), 0 30px 80px -30px rgba(255,255,255,.5);
  animation: intro-mark 1s var(--ease) both;
}
@keyframes intro-mark {
  from { opacity: 0; transform: scale(.86); filter: blur(14px); }
  to   { opacity: 1; transform: none;       filter: none; }
}

.intro__word {
  font-size: 22px; font-weight: 600; color: var(--text); letter-spacing: -.03em;
  animation: intro-word .9s var(--ease) .22s both;
}
@keyframes intro-word {
  from { opacity: 0; letter-spacing: .42em; filter: blur(6px); }
  to   { opacity: 1; letter-spacing: -.03em; filter: none; }
}

.intro__line {
  height: 1px; width: 0;
  background: linear-gradient(90deg, transparent, var(--green), transparent);
  animation: intro-line .8s var(--ease) .5s both;
}
@keyframes intro-line { from { width: 0; opacity: 0; } to { width: 180px; opacity: 1; } }

.intro--out { animation: intro-out .7s var(--ease) both; pointer-events: none; }
@keyframes intro-out { to { opacity: 0; transform: scale(1.06); } }

@media (prefers-reduced-motion: reduce) {
  .intro__mark, .intro__word, .intro__line { animation: none !important; }
}

/* =========================================================================
   Finish — the last coat, kept at the end of the file so it wins the
   cascade over the per-element colour declarations above.
   ========================================================================= */

/* Fraunces is a variable optical font and the display lines use the cut
   almost nobody ships: opsz pushed to the 144pt master, softened terminals,
   and the WONK axis on — the swashy alternate forms that make the face
   ours rather than the default everyone else installs. Body text is
   untouched; this is display-size only. */
body.lp { font-optical-sizing: auto; }
.lp-h1, .lp-final h2 { font-variation-settings: "opsz" 144, "SOFT" 30, "WONK" 1; }

/* Display ink: a faint vertical gradient through the display glyphs — light
   falling on type rather than flat fill. The italic em keeps a brighter
   ramp so it still reads as the emphasis.

   The padding is load-bearing, not rhythm: with background-clip:text the
   glyphs are windows onto the element's own background, and these headings
   run tight line-heights (`.cs-h1` is 1), so Fraunces' ascenders and deep
   descenders overflow the painted box — every part outside it renders
   TRANSPARENT, which cut the g off "Launching". The padding stretches the
   painted box past the glyph extents; the negative margins give the space
   straight back so the layout does not move. The inline `em` needs only the
   padding — vertical padding on an inline paints without shifting lines. */
.lp-h1, .lp-final h2, .lp-section__head h2, .cs-h1 {
  background: linear-gradient(180deg, #f4f7ff 12%, #b7c4ea 96%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  text-wrap: balance;
  padding-block: .16em .3em;
  margin-block: -.16em -.3em;
}
.lp-h1 em, .lp-final h2 em, .cs-h1 em {
  background: linear-gradient(180deg, #ffffff 30%, #dee6ff);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  padding-block: .16em .3em;
}

/* The primary pill catches the light once on hover: a dark specular sweeps
   across the white, like a reflection crossing enamel. The return trip is
   instant so repeated hovers always start clean. */
body.lp .btn { position: relative; overflow: hidden; }
body.lp .btn--primary::after {
  content: ""; position: absolute; inset: -2px; pointer-events: none;
  background: linear-gradient(105deg, transparent 42%, rgba(10, 16, 36, .12) 50%, transparent 58%);
  transform: translateX(-130%); transition: none;
}
body.lp .btn--primary:hover::after { transform: translateX(130%); transition: transform .65s var(--ease); }

/* Glided anchor links move focus into their target section (lp-motion.js
   sets tabindex=-1); the section itself should not wear a focus ring — the
   reader's next Tab lands on its first control, which is the point. */
body.lp :is(section, main)[tabindex="-1"]:focus { outline: none; }

/* Paper sees everything: once .js-reveal is armed, a print taken mid-scroll
   would otherwise ship the unrevealed sections blank. */
@media print {
  .js-reveal [data-reveal] { opacity: 1 !important; transform: none !important; filter: none !important; }
  body.lp::after { display: none; }
}

/* Sub-page layout (pr-*). Shared by every page that is not the landing or the
   console: the comparison hub and the head-to-head pages. Same card language
   as the principles grid, in a single reading column. Anything LIFTED here is
   lifted, never coloured: colour stays reserved for warn and error. */
.pr-main { padding: 96px 0 120px; display: grid; gap: 72px; }
.pr-h1 { font-size: clamp(40px, 6vw, 68px); line-height: 1.04; letter-spacing: -.02em; }
.pr-lead { color: var(--muted); font-size: 17px; line-height: 1.7; max-width: 58ch; text-wrap: pretty; }

/* the anchor: three terms, read as a sum rather than as a table */
.pr-anchor { display: grid; gap: 18px; }
.pr-sum { display: grid; gap: 0; border: 1px solid var(--border); border-radius: 18px; overflow: hidden; }
.pr-sum > div {
  display: flex; flex-wrap: wrap; gap: 8px 24px; justify-content: space-between; align-items: baseline;
  padding: 18px 22px; border-bottom: 1px solid var(--border);
}
.pr-sum > div:last-child { border-bottom: 0; }
.pr-sum dt { color: var(--muted); font-size: 15px; }
.pr-sum dd { margin: 0; color: var(--text); font-size: 15px; }
.pr-sum dd b { font-family: var(--mono, ui-monospace, monospace); font-size: 17px; }
.pr-note { color: var(--faint); font-size: 14px; line-height: 1.7; max-width: 62ch; }
.pr-note--small { font-size: 13px; }

/* the plans */
.pr-plans { display: grid; gap: 26px; }
.pr-plans__h { margin: 0; }
.pr-grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); align-items: start; }
.pr-card {
  display: grid; gap: 14px; align-content: start;
  padding: 26px 24px; border: 1px solid var(--border); border-radius: 18px;
  background: var(--surface);
}
.pr-card--lead { border-color: var(--border-strong); box-shadow: var(--lift-glass); }
.pr-card h3 { margin: 0; font-size: 20px; }
.pr-price { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 0; }
.pr-price b { font-family: var(--mono, ui-monospace, monospace); font-size: 40px; line-height: 1; letter-spacing: -.02em; }
.pr-price span { color: var(--faint); font-size: 13px; }
.pr-price-alt { margin: -8px 0 0; color: var(--faint); font-size: 13px; }
.pr-list { margin: 0; padding-left: 1.1em; display: grid; gap: 8px; color: var(--muted); font-size: 14.5px; line-height: 1.6; }
.pr-fit { margin: 4px 0 0; color: var(--faint); font-size: 13.5px; line-height: 1.6; }
.pr-founding {
  padding: 16px 20px; border: 1px solid var(--border); border-radius: 14px;
  color: var(--muted); font-size: 14.5px; line-height: 1.7;
}

/* the refusal, given the same weight as the plans */
.pr-never { display: grid; gap: 16px; }
.pr-never__lead { color: var(--muted); font-size: 16px; line-height: 1.7; max-width: 60ch; }
.pr-never__list { margin: 0; padding-left: 1.1em; display: grid; gap: 12px; color: var(--muted); font-size: 15px; line-height: 1.7; max-width: 68ch; }
.pr-never__list b { color: var(--text); }

/* faq, reusing the details language from the landing */
.pr-faq { display: grid; gap: 12px; }
.pr-q { border: 1px solid var(--border); border-radius: 14px; padding: 14px 18px; background: var(--surface); }
.pr-q summary { cursor: pointer; font-size: 15.5px; color: var(--text); }
.pr-q p { margin: 10px 0 0; color: var(--muted); font-size: 14.5px; line-height: 1.7; max-width: 64ch; }

.pr-cta { display: grid; gap: 14px; justify-items: start; }
.pr-cta h2 { font-size: clamp(28px, 4vw, 42px); margin: 0; }
.pr-cta p { color: var(--muted); font-size: 16px; line-height: 1.7; max-width: 56ch; margin: 0; }
.pr-cta__row { margin-top: 6px; }

/* the server-rendered waiting-list figure, quiet by design */
.lp-proof { color: var(--faint); font-size: 13.5px; text-align: center; margin: 4px 0 0; }

/* comparison tables. A table is the right element here: it is a table of
   record, read across rows, and it is what a reader searching "x vs y" came
   for. It scrolls inside its own box so the page body never scrolls sideways
   on a phone. */
.cmp-scroll { overflow-x: auto; border: 1px solid var(--border); border-radius: 18px; }
.cmp { width: 100%; border-collapse: collapse; font-size: 14.5px; min-width: 34rem; }
.cmp th, .cmp td { padding: 14px 18px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--border); }
.cmp thead th { color: var(--text); font-size: 13px; letter-spacing: .04em; text-transform: uppercase; }
.cmp tbody th { color: var(--muted); font-weight: 500; width: 34%; }
.cmp tbody td { color: var(--text); }
.cmp tbody tr:last-child th, .cmp tbody tr:last-child td { border-bottom: 0; }

/* The hub matrix carries six columns, so it needs more room before it starts
   scrolling, and the row labels are tool names rather than criteria: narrower,
   and set in the page's own ink rather than muted. */
.cmp--wide { min-width: 52rem; }
.cmp--wide tbody th { width: auto; white-space: nowrap; color: var(--text); font-weight: 500; }
.cmp__cap { caption-side: top; text-align: left; padding: 0 18px 12px; color: var(--faint); font-size: 13px; line-height: 1.6; }
/* Our own row is marked, quietly. A comparison that shouts about its author is
   read as an advert; one that simply labels itself is read as a source.

   It is marked a little harder than it once was: at fifteen rows the old 8%
   wash was lost the moment the table scrolled, and a row a reader cannot find
   is a row that may as well not be highlighted. The accent rail down the left
   survives scrolling because it is drawn on the row label, which is the cell
   still on screen when the matrix is panned sideways. */
.cmp__self th, .cmp__self td { background: rgba(130, 150, 225, .12); }
.cmp__self th { color: var(--white); font-weight: 600; }
.cmp__self > th:first-child { box-shadow: inset 3px 0 0 var(--white); }
.cmp__self td b { color: var(--white); font-weight: 600; }
.cmp__flag {
  display: inline-block; margin-left: 6px; padding: 1px 6px;
  border: 1px solid var(--glass-edge); border-radius: 999px;
  font-size: 11px; color: var(--faint); white-space: nowrap;
}

/* The scoreboard above the matrix. A fifteen-row table is scanned rather than
   read, and a reader who scans it without knowing which columns carry risk
   comes away thinking the cheapest row won. These four tiles state the count
   before the scrolling starts, in the same card idiom the rest of the site
   uses, so it reads as a summary of the table rather than as a banner. */
.cmp-score {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.cmp-score__i {
  display: grid; gap: 4px; align-content: start;
  padding: 16px 18px;
  border: 1px solid var(--border); border-radius: 14px;
  background: var(--surface);
}
.cmp-score__i b {
  font-family: var(--serif); font-weight: 500; line-height: 1;
  font-size: clamp(1.6rem, 1.1rem + 1.6vw, 2.1rem);
  color: var(--white);
}
.cmp-score__i span { color: var(--muted); font-size: 13.5px; line-height: 1.55; text-wrap: pretty; }

@media (max-width: 640px) {
  .pr-main { padding: 72px 0 88px; gap: 56px; }
  .pr-sum > div { flex-direction: column; gap: 4px; }
  /* Two up rather than one: these are short figures, and a single column turns
     four tiles into a screen and a half of scrolling before the matrix. */
  .cmp-score { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .cmp-score__i { padding: 13px 14px; }
  .cmp-score__i span { font-size: 12.5px; }
}

/* ---------------------------------------------------------------- plans */
/* Four cards in the same lp-grid the other sections use, so the pricing
   section inherits the responsive behaviour rather than inventing its own.
   White is the single accent per the house palette; the picked plan is
   marked with it and with nothing else. */

.lp-plan { display: flex; flex-direction: column; gap: 10px; }
.lp-plan h3 { margin: 0; }
.lp-plan__price { display: flex; align-items: baseline; gap: 8px; margin: 2px 0 4px; }
.lp-plan__price b { font-family: var(--serif); font-size: clamp(1.9rem, 1.2rem + 2vw, 2.6rem); font-weight: 500; line-height: 1; color: var(--white); }
.lp-plan__price small { font-size: .82rem; color: var(--muted); }
/* The per-month equivalent shown under an annual price. Quiet on purpose: it
   explains the headline figure rather than competing with it. */
.lp-plan__note { margin: -2px 0 2px; font-size: .88rem; line-height: 1.45; color: var(--muted); }
.lp-plan__note b { color: var(--white); font-weight: 500; }
.lp-plan__list { list-style: none; margin: auto 0 0; padding: 12px 0 0; border-top: 1px solid var(--glass-edge); display: grid; gap: 7px; }
.lp-plan__list li { position: relative; padding-left: 18px; font-size: .92rem; color: var(--muted); }
.lp-plan__list li::before { content: ""; position: absolute; left: 2px; top: .52em; width: 6px; height: 6px; border-radius: 50%; background: var(--white); opacity: .55; }

.lp-plans__note { margin: 18px 0 0; max-width: 62ch; font-size: .92rem; color: var(--faint); }
@media (prefers-reduced-motion: reduce) { .lp-plan { transition: none; } }

/* The checkout gate. White is the single accent, so the buy action wears it
   and nothing else on the card does. */
.lp-plan__buy { display: block; margin-top: 14px; padding: 11px 16px; border-radius: 10px;
  border: 1px solid rgba(200, 214, 255, .40); background: rgba(130, 150, 225, .10);
  color: var(--white); font-size: .94rem; text-align: center; text-decoration: none;
  transition: border-color .25s var(--ease), background .25s var(--ease); }
.lp-plan__buy:hover { border-color: rgba(200, 214, 255, .70); background: rgba(130, 150, 225, .18); }
.lp-plan__buy:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .lp-plan__buy { transition: none; } }

/* ------------------------------------------------------ the billing switch
   Monthly or yearly, driven entirely by two radios and :checked sibling
   selectors. NO SCRIPT, deliberately: the one page whose whole job is to state
   prices correctly should not be able to show the wrong one because a .js file
   failed to load or ran late.

   Both price states stay in the markup and one is hidden, rather than being
   swapped in at runtime. That is what lets the tests keep asserting every
   quoted price and every Stripe link against src/plans.js; a script that
   injected prices would leave those assertions inspecting an empty page. */
.lp-billing { border: 0; margin: 0 0 22px; padding: 0; display: flex; justify-content: center; }
/* Visually hidden, still announced. */
.lp-billing__legend { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; }
/* The radios keep their place in the accessibility tree and stay focusable;
   they are only moved out of sight. display:none would make the switch
   unreachable by keyboard, which is the usual way this pattern gets broken. */
.lp-billing__radio { position: absolute; opacity: 0; pointer-events: none; }
.lp-billing__switch { display: inline-flex; gap: 4px; padding: 4px;
  border: 1px solid var(--glass-edge); border-radius: 999px;
  background: rgba(130, 150, 225, .06); }
.lp-billing__opt { display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 9px 18px; border-radius: 999px; font-size: .94rem; color: var(--muted);
  transition: color .2s var(--ease), background .2s var(--ease); }
.lp-billing__opt:hover { color: var(--white); }
.lp-billing__save { font-size: .78rem; padding: 2px 8px; border-radius: 999px;
  background: rgba(130, 150, 225, .22); color: var(--white); }
#billing-monthly:checked ~ .lp-billing__switch .lp-billing__opt[for="billing-monthly"],
#billing-yearly:checked ~ .lp-billing__switch .lp-billing__opt[for="billing-yearly"] {
  background: rgba(200, 214, 255, .16); color: var(--white); }
/* Focus drawn with outline so forced-colors modes keep it. */
.lp-billing__radio:focus-visible ~ .lp-billing__switch .lp-billing__opt[for="billing-monthly"],
.lp-billing__radio:focus-visible ~ .lp-billing__switch .lp-billing__opt[for="billing-yearly"] {
  outline: 2px solid var(--white); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .lp-billing__opt { transition: none; } }

/* Which state each card shows. Monthly is the default.

   Scoped with :has() on the SECTION rather than a `~` sibling combinator from
   the radio, and that is not a style preference: the radios live inside the
   <fieldset> that groups them, while .lp-plans is a sibling of that fieldset,
   so the radio and the cards are not siblings and `~` would silently never
   match. The switch would have rendered perfectly and done nothing.

   If no CSS applied at all, every card would show BOTH prices rather than
   none, which is the safe direction to fail in on a pricing page. */
.lp-plans .is-yearly { display: none; }
#pricing:has(#billing-yearly:checked) .lp-plans .is-monthly { display: none; }
#pricing:has(#billing-yearly:checked) .lp-plans p.lp-plan__price.is-yearly { display: flex; }
#pricing:has(#billing-yearly:checked) .lp-plans p.lp-plan__note.is-yearly { display: block; }
#pricing:has(#billing-yearly:checked) .lp-plans a.lp-plan__buy.is-yearly { display: block; }

/* The picked plan says so in words. A border alone is not a label: it reads
   as decoration to a screen reader and as nothing at all to anyone who is
   not comparing the cards side by side. */

/* ------------------------------------------------- the picked plan

   The recommendation has to survive being one of four cards in a row, so it
   does four things at once rather than one thing loudly: it sits higher than
   its neighbours, it is brighter at the top edge where the eye lands, it
   wears the badge ON that edge rather than inside the card, and its buy
   button is the only filled one on the page.

   All of it is the white accent at different opacities. No second colour
   enters the palette to say "this one", because the moment a plan card has
   its own hue the section stops being wireagent's and starts being every
   other pricing page.

   The lift is 10px and the grid gets padding-top to match, so the raised
   card and its badge are never clipped by the section above. */
.lp-plans { align-items: stretch; padding-top: 18px; }

/* AN EXPLICIT LADDER RATHER THAN AUTO-FIT. Added 2026-09-07, when Single made
   it five tiers; four again since 2026-09-09, when Team came off sale. The
   reasoning below was written for five and holds for four: the counts used
   must divide the card count, so no width leaves one card alone.

   `.lp-grid` is `repeat(auto-fit, minmax(15rem, 1fr))`, which is 240px per
   card. The wrap is 72rem with 32px gutters, so there is 1088px to fill and
   auto-fit takes four columns: 1088 fits (1088+16)/(240+16) = 4.3 of them.
   Five would need 1264px. The fifth card therefore dropped to a row of its
   own and left 828 x 553px of empty page beside it, measured on the live
   site, which is about three card slots.

   WHY EXPLICIT COLUMNS AND NOT A SMALLER minmax. Lowering the track to 12.5rem
   does give five here, but auto-fit then picks whatever fits at every other
   width, and with FIVE items any count that is not 5, 3 or 1 orphans one card:
   four leaves 4+1, two leaves 2+2+1. The ladder below only ever uses counts
   that divide five without a remainder, so there is no width at which one card
   sits alone.

   1140px is where five 200px cards plus their gaps stop fitting inside the
   gutters. Below it, 3+2 is deliberate rather than accidental, and the cards
   get BIGGER (352px) rather than smaller.

   Scoped to `.lp-plans`, never `.lp-grid`, because that class is shared and
   nothing else on the site has five children. */
/* FOUR AGAIN, 2026-09-09. Team came off the page, so the counts below divide
   four: 4, 2, 1. A 3-column fallback would orphan the fourth card, which
   test/markup.mjs forbids by asserting SOLD.length % columns is never 1. */
.lp-plans { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1140px) { .lp-plans { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px)  { .lp-plans { grid-template-columns: minmax(0, 1fr); } }

/* THE TWO GROUP LABELS, 2026-09-11.

   The labels sit INTERLEAVED in the markup, each immediately before its own
   pair of cards, so reading order matches visual order at every width. That
   makes the DESKTOP case the one needing explicit placement: both labels are
   pinned to row 1, at columns 1 and 3, and the cards then auto-flow into row 2
   because row 1 is full.

   Both fallbacks are simply a full-row span, which puts each label directly
   above its own pair. The first version did the opposite, spanning two columns
   from a labels-first markup order, and it was wrong at both fallbacks:
   `span 2` IS a full row at two columns, so the labels stacked together above
   all four cards, and at one column it conjured an implicit second column and
   paired the cards back up. Measured in a browser, not reasoned about.

   The cards themselves are untouched. This adds rows, never columns, which is
   what keeps the repeat(4) assertion in test/markup.mjs meaningful. */
.lp-plans__group {
  grid-row: 1;
  margin: 0;
  align-self: end;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .10em;
  text-transform: uppercase;
  color: var(--faint);
}
.lp-plans__group--solo { grid-column: 1 / span 2; }
.lp-plans__group--team { grid-column: 3 / span 2; }
@media (max-width: 1140px) {
  .lp-plans__group { grid-row: auto; grid-column: 1 / -1; }
  .lp-plans__group--team { margin-top: 14px; }
}

.lp-plan--pick {
  position: relative;
  border-color: rgba(255, 255, 255, .34);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .07) 0%, rgba(255, 255, 255, 0) 42%),
    rgba(130, 150, 225, .10);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, .06),
    0 18px 40px -22px rgba(5, 7, 15, .9);
  transform: translateY(-10px);
  transition: transform .25s var(--ease), border-color .25s var(--ease);
}

/* A hairline of pure white along the top edge. One pixel, and it is the
   thing that actually reads as "this one" from across the page. */
.lp-plan--pick::before {
  content: "";
  position: absolute;
  left: 14px;
  right: 14px;
  top: -1px;
  height: 1px;
  background: linear-gradient(90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, .85) 50%,
    rgba(255, 255, 255, 0) 100%);
}

.lp-plan--pick:hover { transform: translateY(-13px); border-color: rgba(255, 255, 255, .48); }

/* The badge straddles the top border, so it labels the card instead of
   sitting inside it as another line of text competing with the plan name. */
.lp-plan__badge {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  padding: 5px 12px;
  border: 1px solid rgba(255, 255, 255, .34);
  border-radius: var(--r-pill);
  background: #131c38;
  color: var(--white);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* The picked plan's own call to action is filled rather than outlined: it is
   the one button on this section we are actually recommending someone press. */
.lp-plan--pick .lp-plan__buy {
  border-color: rgba(255, 255, 255, .55);
  background: rgba(255, 255, 255, .14);
  font-weight: 600;
}
.lp-plan--pick .lp-plan__buy:hover {
  border-color: var(--white);
  background: rgba(255, 255, 255, .22);
}

/* Reduced motion keeps the raised position, because the lift is layout and
   hierarchy rather than decoration, and drops only the movement. */
@media (prefers-reduced-motion: reduce) {
  .lp-plan--pick, .lp-plan--pick:hover { transition: none; transform: translateY(-10px); }
}

/* Below the grid's breakpoint the cards stack, and a card lifted 10px into
   the one above it just looks like a mistake. */
@media (max-width: 52rem) {
  .lp-plan--pick, .lp-plan--pick:hover { transform: none; }
  .lp-plans { padding-top: 14px; }
}




/* The little index numerals beside section headings were 10 to 10.5px — under
   the floor at every width, scaling or no scaling. */
.lp-stat__ix, .lp-card__ix, .lp-grant__ix, .lp-consent__ix { font-size: 11px; }

/* =========================================================================
   Touch targets on the public pages — added 2026-08-21
   -------------------------------------------------------------------------
   The undersized-target count barely moved across a full 320-1600px sweep:
   30 on the landing page at 320px and 37 at 1600px. So these were never
   breakpoint problems, and there is no width at which they were fine. They
   get asked about with (pointer: coarse) instead, which is a question about
   the hand rather than about the window, and desktop density is untouched.
   ========================================================================= */
@media (pointer: coarse) {
  /* The brand lockup is the way back to the front door from every page, and
     it was a 28px-tall strip. */
  .logo, .site__brand { min-height: var(--tap, 40px); }

  /* Footer navigation. These are block links in a stacked list, so they are
     NOT the inline-in-a-sentence case WCAG 2.5.8 exempts: they are a menu,
     and a menu of 20px rows is a menu you miss. */
  .cs-foot__nav a, .lp-foot__col a, .lp-foot a {
    min-height: var(--tap, 40px);
    display: flex;
    align-items: center;
  }
}

/* The landing nav links measured 38px tall — two short of the standard, which
   is the kind of miss that only a measurement finds. */
@media (pointer: coarse) {
  .lp-nav__links a { min-height: var(--tap, 40px); }
}

/* =========================================================================
   Comparison tables on a phone
   -------------------------------------------------------------------------
   These are already correct: .cmp-scroll contains them, so the PAGE never
   scrolls sideways and nothing is clipped. They are merely uncomfortable —
   .cmp--wide is 832px of six-column matrix inside a 328px window, so reading
   row nine means dragging sideways and losing sight of which row it was.

   The row label sticks to the left edge instead. The reader keeps the name of
   the thing being compared in view while the columns move under it, which is
   the actual complaint — not the scrolling itself.

   Below 720px the table stops being a table and becomes one card per row: the
   row name is the card heading and each value sits under the name of its own
   column, printed from the data-label that every cell now carries. The
   <table> element and the desktop rendering are untouched, so the semantics
   and the screen-reader experience are the same table they always were.
   ========================================================================= */
@media (max-width: 720px) {
  .cmp-scroll { border: 0; border-radius: 0; overflow: visible; }
  .cmp { min-width: 0; display: block; }
  .cmp thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .cmp tbody, .cmp tr, .cmp th, .cmp td { display: block; }

  .cmp tbody tr {
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 4px 16px 12px;
    margin-bottom: 14px;
    background: rgba(130, 150, 225, .04);
  }
  .cmp tbody tr.cmp__self { border-color: var(--border-strong); background: rgba(200, 214, 255, .07); }

  .cmp tbody th {
    width: auto;
    font-size: 1.02rem;
    font-weight: 650;
    color: var(--text);
    padding: 14px 0 10px;
    border-bottom: 1px solid var(--border);
    white-space: normal;
  }

  .cmp tbody td {
    padding: 11px 0 0;
    border-bottom: 0;
    display: grid;
    gap: 2px;
  }
  /* the column heading the cell lost when the row became a card */
  .cmp tbody td::before {
    content: attr(data-label);
    font-family: var(--mono);
    font-size: .66rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--faint);
  }
  .cmp tbody td:empty { display: none; }

  /* With the table set to `display: block` the caption loses its table-caption
     box and collapses to its own min-content — one word per line down the left
     edge. It has to be told it is a block now. */
  .cmp__cap { display: block; width: 100%; padding-inline: 0; }
}

/* ============================================================ book a demo
 *
 * The booking panel on the front door. Two columns on a wide screen (pick a
 * time, then say who you are) collapsing to one below 820px, because the two
 * halves are sequential rather than parallel: you cannot fill the form in
 * until you have chosen, and side by side at phone width would put the form
 * above the fold and the times below it.
 *
 * The form stays HIDDEN until a time is chosen, so the panel asks one thing at
 * a time. A demo form that opens with four empty fields beside a grid of times
 * is a wall; this is a question, and then a question.
 */
.lp-demo {
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  gap: clamp(16px, 2.4vw, 30px);
  align-items: start;
  padding: clamp(16px, 2.2vw, 24px);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  background: var(--glass);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  box-shadow: var(--lift-glass);
}

.lp-demo__h {
  margin: 0 0 6px;
  font-size: 1rem;
  letter-spacing: -.01em;
  color: var(--text);
}

.lp-demo__zone,
.lp-demo__note,
.lp-demo__small {
  margin: 0 0 14px;
  font-size: .82rem;
  color: var(--muted);
}

.lp-demo__mail { font-size: .82rem; color: var(--green-dim); }

/* The day chips. One row; the chosen one reads as lit, the rest as quiet.
   Tap height like every control, but the row is one row, which is the whole
   point: the section used to list seven days of times at once. */
.lp-demo__days {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 16px;
}

/* A day is a calendar tile: weekday over the number. The chosen one is lit
   white with indigo ink, the same inversion the chosen time uses, so the two
   selections read as one system. */
.lp-demo__daychip {
  display: grid;
  justify-items: center;
  gap: 3px;
  min-height: var(--tap);
  min-width: 58px;
  padding: 8px 10px 7px;
  border: 1px solid var(--control-border);
  border-radius: 12px;
  background: rgba(130, 150, 225, .06);
  color: var(--muted);
  font: inherit;
  cursor: pointer;
  transition: border-color .18s var(--ease), color .18s var(--ease), background .18s var(--ease), transform .18s var(--ease);
}

.lp-demo__dow {
  font-family: var(--mono);
  font-size: .62rem;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.lp-demo__dom {
  font-size: 1.2rem;
  font-weight: 600;
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.lp-demo__daychip:hover { color: var(--text); border-color: var(--white); transform: translateY(-1px); }

.lp-demo__daychip.is-on,
.lp-demo__daychip.is-on .lp-demo__dom {
  color: var(--green-ink);
}
.lp-demo__daychip.is-on {
  background: var(--white);
  border-color: var(--white);
}

.lp-demo__day + .lp-demo__day { margin-top: 16px; }

.lp-demo__dayname {
  margin: 0 0 8px;
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--faint);
}

/* The times are an even grid of tiles under the chosen day, capped so three
   or four never stretch across the whole column. */
.lp-demo__times {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: 8px;
  max-width: 380px;
}

/* A time is a control, so it is sized like one: the 40px tap target the rest
   of the product uses, never smaller, however tight the grid gets. */
.lp-demo__time {
  min-height: var(--tap);
  width: 100%;
  padding: 0 10px;
  border: 1px solid var(--control-border);
  border-radius: 12px;
  background: rgba(130, 150, 225, .06);
  color: var(--text);
  font: inherit;
  font-size: .88rem;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: border-color .18s var(--ease), background .18s var(--ease), color .18s var(--ease);
}

.lp-demo__time:hover { border-color: var(--white); }

.lp-demo__time.is-on {
  background: var(--white);
  border-color: var(--white);
  color: var(--green-ink);
  font-weight: 600;
}

.lp-demo__form { display: grid; gap: 12px; }

.lp-demo__chosen {
  margin: 0;
  font-size: .9rem;
  color: var(--muted);
}
.lp-demo__chosen b { color: var(--text); }

.lp-demo__field { display: grid; gap: 5px; }

.lp-demo__field > span {
  font-size: .78rem;
  color: var(--muted);
}
.lp-demo__field i { font-style: normal; color: var(--faint); }

.lp-demo__field input,
.lp-demo__field textarea {
  min-height: var(--tap);
  padding: 9px 12px;
  border: 1px solid var(--control-border);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: .9rem;
}

.lp-demo__field textarea { resize: vertical; }

.lp-demo__field input:focus-visible,
.lp-demo__field textarea:focus-visible {
  outline: 2px solid var(--white);
  outline-offset: 1px;
}

/* The honeypot. Positioned away rather than display:none, because some bots
   skip fields they can see are not rendered, and aria-hidden on the wrapper
   keeps it away from anyone using a screen reader. */
.lp-demo__pot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.lp-demo__msg {
  margin: 0;
  font-size: .84rem;
  color: var(--muted);
}
.lp-demo__msg--bad { color: var(--bad); }

/* What to expect: the second column until a time is chosen. A quieter pane
   than the picker, so the eye lands on the tiles first. */
.lp-demo__aside {
  padding: 18px 20px 20px;
  border: 1px solid var(--glass-edge);
  border-radius: 14px;
  background: rgba(130, 150, 225, .04);
}

.lp-demo__ix {
  margin: 0 0 12px;
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--faint);
}

.lp-demo__facts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 11px;
}

.lp-demo__facts li {
  position: relative;
  padding-left: 18px;
  font-size: .9rem;
  line-height: 1.45;
  color: var(--muted);
}

.lp-demo__facts li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .55em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 8px rgba(255, 255, 255, .5);
}

.lp-demo__facts b { color: var(--text); font-weight: 600; }

/* The confirmation replaces both columns, so it spans them. */
.lp-demo__done { grid-column: 1 / -1; }

/* The floor: Google Calendar's own booking page, shown when the picker has no
   times to offer. It spans both columns because there is no form beside it.
   The frame is white by Google's choice, so it sits on the dark glass as a
   lit pane rather than a dark object on white. */
.lp-demo__google { grid-column: 1 / -1; display: grid; gap: 14px; }
.lp-demo__google .lp-demo__note { margin: 0; }

.lp-demo__frame {
  display: block;
  width: 100%;
  height: min(520px, 70vh);
  border: 0;
  border-radius: 14px;
  background: #fff;
}

.lp-demo__open { justify-self: start; }

.lp-demo__done h3 {
  margin: 0 0 6px;
  font-size: 1.15rem;
  color: var(--text);
}
.lp-demo__done p { margin: 0 0 8px; font-size: .9rem; color: var(--muted); }
.lp-demo__done:focus-visible { outline: 2px solid var(--white); outline-offset: 6px; }

.lp-demo__donewhen {
  font-family: var(--mono);
  font-size: .95rem;
  color: var(--text);
}

/* The second column is never empty now: the expectations pane holds it until
   a time is chosen and the form takes over, so the grid stays two columns
   and only narrow screens stack. */
@media (max-width: 820px) {
  .lp-demo { grid-template-columns: 1fr; }
}


/* ==========================================================================
   /updates: the product update log (2026-09-04)

   One column of entries on the same night ground as the rest of the site.
   No card sits on white and nothing here is a dark object on a light one:
   an entry is a faint glass panel on the same indigo, which is the grammar
   the console and the launching page already use.

   The version badge is the only accented object. It says what wireagent is
   on, which is the first question the page exists to answer.
   ========================================================================== */

.up-main { padding: 40px var(--gutter) 72px; }
.up-wrap { width: 100%; max-width: 46rem; margin-inline: auto; }

.up-head { display: grid; gap: 12px; margin-bottom: 40px; }
.up-version {
  justify-self: start;
  padding: 5px 12px;
  border: 1px solid var(--glass-edge); border-radius: var(--r-pill);
  background: linear-gradient(160deg, rgba(140, 162, 230, .14), rgba(140, 162, 230, .04));
  color: #d9e0fb; font-family: var(--mono); font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
}
.up-h1 {
  font-size: clamp(34px, 6vw, 56px); line-height: 1.05; letter-spacing: -.02em;
  font-weight: 500; color: #d9e0fb; margin: 0;
}
.up-lead { color: var(--muted); font-size: 16px; line-height: 1.65; max-width: 54ch; margin: 0; }

/* ------------------------------------------------------------- the feed */

.up-feed { display: grid; gap: 18px; }
.up-empty { color: var(--faint); font-size: 14px; }

.up-entry {
  padding: 24px 24px 26px;
  border: 1px solid var(--glass-edge); border-radius: 18px;
  background: linear-gradient(160deg, rgba(140, 162, 230, .07), rgba(140, 162, 230, .015));
}
.up-entry__meta { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.up-entry__date { color: var(--faint); font-family: var(--mono); font-size: 12.5px; }
.up-tag {
  padding: 3px 9px; border-radius: var(--r-pill);
  border: 1px solid var(--glass-edge); color: var(--muted);
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
}
.up-entry__t { font-size: clamp(20px, 3vw, 26px); line-height: 1.2; font-weight: 500; margin: 0 0 8px; }
.up-entry__s { color: var(--muted); font-size: 15px; line-height: 1.65; margin: 0 0 14px; max-width: 60ch; }

.up-items { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
.up-items li {
  position: relative; padding-left: 18px;
  color: var(--muted); font-size: 14.5px; line-height: 1.6;
}
.up-items li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 6px; height: 6px; border-radius: 50%;
  background: rgba(140, 162, 230, .55);
}

.up-group { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--glass-edge); }
.up-group__t {
  font-size: 12px; letter-spacing: .09em; text-transform: uppercase;
  font-family: var(--mono); color: #d9e0fb; font-weight: 500; margin: 0 0 10px;
}

.up-remove {
  margin-top: 16px; padding: 6px 12px;
  border: 1px solid var(--glass-edge); border-radius: var(--r-pill);
  background: none; color: var(--faint); font-size: 12.5px; cursor: pointer;
  transition: color .15s var(--ease), border-color .15s var(--ease);
}
.up-remove:hover:not(:disabled) { color: var(--text); border-color: rgba(140, 162, 230, .4); }
.up-remove:disabled { opacity: .5; cursor: default; }

/* ---------------------------------------------------------- the composer */

.up-compose {
  margin-bottom: 32px; padding: 22px 22px 24px;
  border: 1px solid var(--glass-edge); border-radius: 18px;
  background: linear-gradient(160deg, rgba(140, 162, 230, .1), rgba(140, 162, 230, .02));
}
.up-compose__h {
  font-size: 12px; letter-spacing: .09em; text-transform: uppercase;
  font-family: var(--mono); color: var(--muted); font-weight: 500; margin: 0 0 16px;
}
.up-form { display: grid; gap: 14px; }
.up-field { display: grid; gap: 6px; }
.up-field--split { grid-template-columns: 1fr 1fr; gap: 12px; }
.up-field--split > div { display: grid; gap: 6px; }
.up-label { color: var(--faint); font-size: 12.5px; }
.up-input {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--glass-edge); border-radius: 12px;
  background: rgba(10, 15, 30, .45); color: var(--text);
  font: inherit; font-size: 14.5px;
}
.up-input::placeholder { color: var(--faint); }
.up-input:focus-visible { outline: 2px solid rgba(140, 162, 230, .55); outline-offset: 1px; }
.up-input--area { resize: vertical; line-height: 1.55; }
.up-form__foot { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.up-msg { color: var(--faint); font-size: 13px; margin: 0; }

@media (max-width: 560px) {
  .up-field--split { grid-template-columns: 1fr; }
  .up-entry { padding: 20px 18px 22px; }
}
