/*
 * QuickSync rebuild — shared tokens and primitives for the lab prototypes.
 *
 * The live site resolves to 22 distinct text colours, 13 border radii and 13
 * font sizes across eight pages, including four near-blacks and the browser's
 * default link blue. Every value below has exactly one job, and the prototypes
 * may not introduce a literal colour, radius or size outside this file. That
 * constraint is the point of the exercise: whatever survives review becomes
 * theme/assets/design-tokens.css and the colour constants in build/lib.php.
 *
 * Token names deliberately match the app's existing --qs-* set in
 * staticfiles/css/tokens.css where they mean the same thing, so the marketing
 * site and the product stop drifting apart.
 */

:root {
  /* Ink ramp — one near-black, one body grey, one caption grey. No others. */
  --ink: #0B1220;
  --body: #475569;
  /* Was #64748B, which is 4.76:1 on white and passes, but drops to 4.35:1 on
     --accent-tint and 4.38:1 on --bad-tint — under AA on the two surfaces a
     caption is most likely to sit on inside a callout. Two steps down the same
     slate ramp clears every ground in this file with margin (4.75:1 worst case,
     on --accent-tint) and stays a clear step lighter than --body, which is the
     only job the token has. */
  --muted: #5E6E84;
  --line: #E3E8EF;
  --line-strong: #D5DBE5;
  --surface: #F7F9FC;
  --white: #FFFFFF;
  /* Page ground. A hair off pure white so cards read as raised. */
  --canvas: #FCFCFD;

  /* On dark */
  --on-dark: #CBD5E1;
  --on-dark-dim: #94A3B8;
  /* Third step of the on-dark ramp: legible but recessive, for cells that mean
     "not applicable". Lives here rather than in fx.css because A/B/C use it
     and never load the effects layer. */
  --muted-on-dark: #5A6B84;
  --ink-deep: #060B15;

  /* One accent. The site currently runs three blues: #0C63FD in the Elementor
     kit, #1365FD in site.webmanifest and #3b82f6 in the app's tokens.css.
     This is the marketing brand blue; the manifest and app should follow it. */
  --accent: #0C63FD;
  --accent-hover: #0A50CE;
  --accent-tint: #EFF5FF;
  --accent-on-dark: #6FA5FF;

  /* Semantic only — never decorative. */
  --ok: #0F7B4F;
  --ok-tint: #ECFDF3;
  --warn: #B54708;
  --warn-tint: #FFFAEB;
  --bad: #B42318;
  --bad-tint: #FEF3F2;

  /* Tick and cross marks, so the checklist and the comparison table draw the
     same glyph. The stroke colour is baked into the data URI because an SVG
     referenced through url() cannot read currentColor; keep these two in step
     with --ok and --bad above. */
  --icon-tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230F7B4F' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
  --icon-cross: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B42318' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E");

  /* Type scale. Integers only: the live site carries 15/17/21/52/60px
     off-scale steps, and rakt.in carries 13.5/14.5/17.5px half-pixel ones. */
  --t-xs: 13px;
  --t-sm: 14px;
  --t-base: 16px;
  --t-lg: 18px;
  --t-xl: 20px;
  --t-2xl: 26px;
  --t-3xl: 34px;
  --t-display: clamp(34px, 4.6vw, 56px);

  --lh-tight: 1.1;
  --lh-snug: 1.3;
  --lh-body: 1.6;
  /* Long-form only. 1.6 is right for a landing page, where the longest run of
     text is a three-line lede; across a 3,000-word article at the same measure
     it reads as crowded, which is the complaint this exists to answer. */
  --lh-prose: 1.75;

  /* Two radii and a pill. Not thirteen. */
  --r-sm: 8px;
  --r-md: 14px;
  --r-full: 9999px;

  /* 4px base */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;

  --shadow-sm: 0 1px 2px rgba(11, 18, 32, .06);
  --shadow-md: 0 8px 24px -8px rgba(11, 18, 32, .12);
  --shadow-lg: 0 24px 60px -20px rgba(11, 18, 32, .22);

  --font: 'Figtree', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, monospace;

  --container: 1200px;
}

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

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

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: var(--lh-body);
  color: var(--body);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Self-hosted rather than fetched from fonts.googleapis.com. The Google
   stylesheet is render-blocking on a third-party origin, and on throttled
   mobile that DNS + TLS + CSS round trip alone pushed LCP from 2.1s to 2.5s —
   over the threshold Google grades as good, on the ranking factor Google
   grades it with. One variable file covers 400-800. Figtree is OFL-1.1.
   Pages must preload assets/figtree-latin.woff2 to avoid a second-hop chain. */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('assets/figtree-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

img, svg { max-width: 100%; }
a { color: inherit; text-decoration: none; }

.skip {
  position: absolute; left: var(--s-4); top: -100px; z-index: 100;
  padding: var(--s-3) var(--s-4); border-radius: var(--r-sm);
  background: var(--ink); color: var(--white);
  font-size: var(--t-sm); font-weight: 600;
}
.skip:focus { top: var(--s-4); }

h1, h2, h3, h4 {
  margin: 0;
  color: var(--ink);
  font-weight: 700;
  letter-spacing: -.022em;
  line-height: var(--lh-snug);
}
h1 { font-size: var(--t-display); line-height: var(--lh-tight); letter-spacing: -.032em; }
h2 { font-size: var(--t-3xl); letter-spacing: -.028em; }
h3 { font-size: var(--t-xl); }
p { margin: 0; }

/* ---------- layout ---------- */

.wrap {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 var(--s-5);
}
.section { padding: var(--s-9) 0; }
.section--tight { padding: var(--s-8) 0; }
.section--surface { background: var(--surface); }
.section--ink { background: var(--ink); }
@media (max-width: 720px) {
  .section { padding: var(--s-8) 0; }
}

.text-center { text-align: center; }
.mx-auto { margin-inline: auto; }
.mt-auto { margin-top: auto; }
.pad-4 { padding: var(--s-4); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }

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

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-8);
  align-items: center;
}
/* Grid items default to min-width:auto, so a wide child (the mockup's table)
   refuses to shrink and drags its sibling column off-screen with it. */
.split > *, .hero__grid > * { min-width: 0; }
.split--wide-right { grid-template-columns: 5fr 7fr; }
.split--wide-left { grid-template-columns: 7fr 5fr; }
@media (max-width: 900px) {
  .split, .split--wide-right, .split--wide-left { grid-template-columns: 1fr; gap: var(--s-6); }
  .split__media--first { order: -1; }
}

.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-4); }
@media (max-width: 900px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* ---------- type primitives ---------- */

.eyebrow {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--accent);
}
.eyebrow--dim { color: var(--muted); }
.eyebrow--on-dark { color: var(--accent-on-dark); }

.lede { font-size: var(--t-lg); color: var(--body); max-width: 46em; }
.small { font-size: var(--t-sm); }
.caption { font-size: var(--t-sm); color: var(--muted); }
.on-dark { color: var(--on-dark); }
.on-dark-dim { color: var(--on-dark-dim); }
.ink-invert { color: var(--white); }

/* ---------- controls ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 44px;
  padding: 0 var(--s-5);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.btn--primary { background: var(--accent); color: var(--white); }
.btn--primary:hover { background: var(--accent-hover); }
.btn--ghost { background: var(--white); color: var(--ink); border-color: var(--line); }
.btn--ghost:hover { border-color: var(--muted); }
.btn--on-dark { background: rgba(255,255,255,.08); color: var(--white); border-color: rgba(255,255,255,.18); }
.btn--on-dark:hover { background: rgba(255,255,255,.14); }
.btn--lg { min-height: 50px; padding: 0 var(--s-6); font-size: var(--t-base); }

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

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 5px var(--s-3);
  border-radius: var(--r-full);
  font-size: var(--t-xs);
  font-weight: 600;
  background: var(--accent-tint);
  color: var(--accent);
}
.pill--on-dark { background: rgba(111,165,255,.14); color: var(--accent-on-dark); }
.pill--ok { background: var(--ok-tint); color: var(--ok); }
.pill--warn { background: var(--warn-tint); color: var(--warn); }
.pill--bad { background: var(--bad-tint); color: var(--bad); }

/* ---------- surfaces ---------- */

.card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-5);
}
.card--on-dark { background: rgba(255,255,255,.04); border-color: rgba(255,255,255,.1); }
.card--flat { border-color: transparent; background: var(--surface); }

.checklist { list-style: none; margin: 0; padding: 0; }
.checklist li {
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
  padding: var(--s-2) 0;
  font-size: var(--t-base);
}
.checklist li::before {
  content: '';
  flex: none;
  width: 18px; height: 18px;
  margin-top: 3px;
  border-radius: var(--r-full);
  background: var(--ok-tint) var(--icon-tick) center/11px no-repeat;
}
.checklist--on-dark li { color: var(--on-dark); }

/* Inline "read more" link. Deliberately a text link rather than a button: these
   point at the feature and integration pages, and the anchor text is doing
   internal-linking work that a generic "Learn more" button would waste. */
.link-more {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-sm); font-weight: 600; color: var(--accent);
}
.link-more span { transition: transform .2s var(--fx-ease-out, ease-out); }
.link-more:hover { text-decoration: underline; }
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .link-more:hover span { transform: translateX(3px); }
}

/* ---------- product mockup chrome ---------- */

.frame {
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--white);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.frame--on-dark { border-color: rgba(255,255,255,.12); }
.frame__bar {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 10px var(--s-3);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.frame__dot { width: 9px; height: 9px; border-radius: var(--r-full); background: var(--line-strong); }
.frame__url {
  flex: 1;
  margin-left: var(--s-2);
  padding: 3px var(--s-3);
  border-radius: var(--r-full);
  background: var(--white);
  border: 1px solid var(--line);
  font-size: 11px;
  color: var(--muted);
  text-align: center;
}

/* A miniature of the real product. The app's nav is Products / Orders /
   Settings (dashboard/templates/dashboard/sidebar/sidebar_urls.html); the
   mockups must not invent screens the product does not have. */
.app { display: grid; grid-template-columns: 148px 1fr; min-height: 340px; font-size: var(--t-xs); }
.app__side { background: var(--surface); border-right: 1px solid var(--line); padding: var(--s-4) var(--s-3); }
.app__brand { --mark: 18px; display: flex; align-items: center; gap: calc(var(--mark) * 29 / 108); font-weight: 800; color: var(--ink); font-size: var(--t-sm); margin-bottom: var(--s-5); }
.app__mark { width: 18px; height: 18px; border-radius: 5px; background: var(--accent); }
.app__nav { display: flex; flex-direction: column; gap: 2px; }
.app__nav span { padding: 6px var(--s-2); border-radius: 6px; color: var(--muted); }
.app__nav span.is-active { background: var(--accent-tint); color: var(--accent); font-weight: 600; }
.app__main { padding: var(--s-4); }
.app__row { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s-4); }
.app__title { font-weight: 700; color: var(--ink); font-size: var(--t-sm); }

.kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-2); margin-bottom: var(--s-4); }
.kpi { border: 1px solid var(--line); border-radius: var(--r-sm); padding: 9px 10px; }
.kpi__k { font-size: 10px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); }
.kpi__v { font-size: var(--t-lg); font-weight: 700; color: var(--ink); line-height: 1.2; }

.tbl { width: 100%; border-collapse: collapse; }
.tbl th {
  text-align: left;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--muted);
  font-weight: 600;
  padding: 0 0 7px;
  border-bottom: 1px solid var(--line);
}
.tbl td { padding: 8px 0; border-bottom: 1px solid var(--line); color: var(--body); font-size: 11px; }
.tbl td:first-child { color: var(--ink); font-weight: 600; }
.tbl .num { font-variant-numeric: tabular-nums; }

.chan { display: inline-flex; align-items: center; gap: 5px; }
.chan img { width: 13px; height: 13px; object-fit: contain; }

/* On a phone the mockup drops the chrome that carries no information: the
   sidebar (already established elsewhere on the page) and the last column. */
@media (max-width: 700px) {
  .app { grid-template-columns: 1fr; min-height: 0; }
  .app__side { display: none; }
  .app__main { padding: var(--s-3); }
  .kpis { grid-template-columns: repeat(2, 1fr); }
  .tbl th:last-child, .tbl td:last-child { display: none; }
  .frame__url { font-size: 10px; }
}

/* ---------- comparison table ---------- */

.vs { width: 100%; border-collapse: separate; border-spacing: 0; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.vs th { text-align: left; padding: var(--s-3) var(--s-4); font-size: var(--t-sm); font-weight: 700; }
.vs th.is-bad { background: var(--bad-tint); color: var(--bad); }
.vs th.is-ok { background: var(--ok-tint); color: var(--ok); }
.vs td { padding: var(--s-3) var(--s-4); font-size: var(--t-sm); border-top: 1px solid var(--line); vertical-align: top; }
.vs td.is-bad { background: color-mix(in srgb, var(--bad-tint) 45%, white); }
.vs td.is-ok { background: color-mix(in srgb, var(--ok-tint) 45%, white); }
@media (max-width: 720px) { .vs th, .vs td { padding: var(--s-3); font-size: var(--t-xs); } }

/* Marked variant. The column washes above put the whole signal in the colour,
   which reads as a warning panel rather than a comparison and leaves nothing
   for a colour-blind reader. Here the tick and the cross carry it and the
   table keeps the page's own white-on-hairline surface. The tinted base stays
   for the lettered prototypes, which are frozen. */
.vs--marks th,
.vs--marks td { position: relative; padding-left: calc(var(--s-4) + var(--s-6)); }
.vs--marks th.is-bad,
.vs--marks th.is-ok { background: var(--surface); color: var(--ink); }
.vs--marks td.is-bad,
.vs--marks td.is-ok { background: var(--white); }
.vs--marks th.is-ok,
.vs--marks td.is-ok { border-left: 1px solid var(--line); }

.vs--marks th::before,
.vs--marks td::before {
  content: '';
  position: absolute;
  left: var(--s-4);
  /* Centres the 18px mark on the first line of an --t-sm/--lh-body cell. */
  top: calc(var(--s-3) + 2px);
  width: 18px; height: 18px;
  border-radius: var(--r-full);
}
.vs--marks .is-ok::before { background: var(--ok-tint) var(--icon-tick) center/11px no-repeat; }
.vs--marks .is-bad::before { background: var(--bad-tint) var(--icon-cross) center/11px no-repeat; }

/* Two columns of glyph-indented text are too narrow to read on a phone, so the
   mark moves above its cell and gives the copy the full column back. */
@media (max-width: 560px) {
  .vs--marks th,
  .vs--marks td { padding: var(--s-3); }
  .vs--marks th::before,
  .vs--marks td::before { position: static; display: block; margin-bottom: var(--s-2); }
}

/* ---------- logo strip ---------- */

.logos { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-6); }
/* Fixed box, not auto width: these marks range from a square Square tile to a
   very wide Wix wordmark, and `height` alone leaves the strip lopsided. */
.logos img { height: 24px; width: 84px; object-fit: contain; opacity: .62; filter: grayscale(1); }
.logos--on-dark img { opacity: .5; filter: brightness(0) invert(1); }

/* ---------- site chrome ---------- */

.nav { display: flex; align-items: center; justify-content: space-between; gap: var(--s-5); padding: var(--s-4) 0; }
.nav__links { display: flex; gap: var(--s-5); font-size: var(--t-sm); font-weight: 500; }
/* --mark drives the whole lockup; see .brand-word for the ratios. */
.nav__brand { --mark: 26px; display: flex; align-items: center; gap: calc(var(--mark) * 29 / 108); font-weight: 800; color: var(--ink); font-size: var(--t-lg); letter-spacing: -.02em; }
.nav__mark { width: 26px; height: 26px; border-radius: 7px; background: var(--accent); }
.nav--on-dark .nav__brand { color: var(--white); }

/* quicksync-mark.svg is a black tile carrying white glyphs and suits light
   surfaces; against --ink-deep the tile recedes, so the footer swaps in
   quicksync-mark-dark.svg — the same 108x108 geometry with the tile dropped and
   the glyphs in currentColor. Sharing the geometry is what keeps the two the
   same optical size in the 26px box, so the lockup does not shift between the
   header and the footer. The tile's corner radius is 0.09 of its side in the
   artwork's own shape layer, so it lives in the asset and scales with it —
   do not add border-radius here or the two compound. */
.brand-mark { width: var(--mark, 26px); height: var(--mark, 26px); object-fit: contain; }
/* The wordmark sets "Quick" in DIN Condensed Bold and "Sync" in Variane Script.
   It is artwork, not type, so it is an image and never the page font.
   Its box is 274x130 against a 108 tile, and is padded at the top so its centre
   sits on the tile's; align-items: center then reproduces the original lockup's
   alignment exactly, with the 'y' descender hanging below the tile as drawn. */
.brand-word { width: calc(var(--mark, 26px) * 274 / 108); height: calc(var(--mark, 26px) * 130 / 108); object-fit: contain; }
/* Header and footer wrap the mark in <picture> to pick a cut by pixel density.
   Without this the wrapper generates a line box, the img sits on its baseline,
   and the font's descender space makes the lockup 6px taller. */
.nav__brand picture { display: flex; }
/* The lemniscate needs 38 device pixels before its loops stay open, so at 26px
   it wants a 1.47x display and below that it is a handful of blue pixels that
   also costs the letters room. Each surface therefore has a pair: the tiled
   quicksync-mark-sm.svg under the light nav, the transparent
   quicksync-mark-dark-sm.svg under the footer — .ink-invert sets a text colour
   and would leave a black tile sitting on the dark surface. The 16-18px sizes
   below would need better than 2x, which is rare enough that they are simply
   fixed on the small cut. inc/chrome.php derives all of this from the same 38
   for the live theme, so a new size there is right without consulting this. */
.brand-mark--app { width: 18px; height: 18px; }
.brand-mark--xs { width: 16px; height: 16px; }
.nav--on-dark .nav__links { color: var(--on-dark); }

/* The menu control exists only below the breakpoint, where .nav__links stops
   being a row and becomes the panel it opens. Deliberately the same element
   rather than a second copy of the list: a duplicate would announce the
   primary menu twice on every page, and the two would drift apart the first
   time a menu item changed. It sits outside .btn-row so the rule below that
   drops the secondary button on a phone cannot reach it. */
.nav__toggle { display: none; }
.nav__toggle-bars,
.nav__toggle-bars::before,
.nav__toggle-bars::after { width: 18px; height: 2px; border-radius: var(--r-full); background: currentColor; }
.nav__toggle-bars { position: relative; display: block; }
.nav__toggle-bars::before,
.nav__toggle-bars::after { content: ''; position: absolute; left: 0; }
.nav__toggle-bars::before { top: -6px; }
.nav__toggle-bars::after { top: 6px; }

/* A second sign-in, inside the panel, because the phone rule at the end of this
   file drops the header's own from .btn-row. Unlike the menu list above, a
   duplicate is safe here: the two copies are never shown at the same width, and
   display:none takes the hidden one out of the accessibility tree as well, so
   "Sign in" is still announced exactly once at every width. */
.nav__signin { display: none; }

@media (max-width: 860px) {
  .nav__links { display: none; }
  .nav__toggle { display: inline-flex; width: 44px; padding: 0; margin-left: auto; flex: none; }
  /* Positioned against .topbar, which is sticky and so is the containing
     block. Out of flow on purpose: opening the menu must not move the page
     under the reader's thumb. */
  .nav__links.is-open {
    display: flex;
    flex-direction: column;
    gap: 0;
    position: absolute;
    top: 100%; left: 0; right: 0;
    max-height: 80vh;
    overflow-y: auto;
    padding: var(--s-2) var(--s-5) var(--s-4);
    background: var(--white);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-md);
    font-size: var(--t-base);
  }
  /* 48px rows rather than the 44px floor: a menu is a column of adjacent
     targets, where a near-miss lands on the neighbouring item. */
  .nav__links.is-open a {
    display: flex;
    align-items: center;
    min-height: 48px;
    padding: 0 var(--s-3);
    border-radius: var(--r-sm);
    color: var(--ink);
  }
  .nav__links.is-open a:hover { background: var(--surface); }
  /* home.css gives each desktop link a hover underline the width of its label.
     Across a full-width row it reads as a rule under the item rather than as
     an affordance, and the row's own hover ground already answers the pointer. */
  .nav__links.is-open a::after { content: none; }
  /* The rule above sets display on every anchor in the panel and outranks the
     base .nav__signin, so the panel copy has to be hidden again here. From 561
     to 860 the header still carries its own. */
  .nav__links.is-open .nav__signin { display: none; }
}

/* One action in the header on a phone, so sign-in moves into the menu panel.
   It is not in the footer and never was: the footer has Product, Integrations,
   Company and legal, and no account link at all. */
@media (max-width: 560px) {
  .nav { padding: var(--s-3) 0; gap: var(--s-3); }
  .nav .btn-row .btn:not(.btn--primary) { display: none; }
  .nav__links.is-open .nav__signin { display: flex; }
  /* The lockup is an image now, so font-size no longer shrinks it; --mark does.
     26px leaves a 99px lockup competing with the trial button at 375px. */
  .nav__brand { --mark: 22px; font-size: var(--t-base); }
}

/* At 320 the brand, the menu control and the trial button want 19px more than
   the line has, and flex takes the shortfall out of the logo: the mark squeezes
   from 22px to 15px and the artwork distorts. The button's side padding is the
   only slack here that costs nothing else. */
@media (max-width: 360px) {
  .nav { gap: var(--s-2); }
  .nav .btn { padding: 0 var(--s-3); }
}

.foot { background: var(--ink-deep); color: var(--on-dark-dim); padding: var(--s-8) 0 var(--s-6); font-size: var(--t-sm); }
/* .nav__brand hard-codes --ink and is declared after .ink-invert, so any text
   in the footer's brand link renders near-black on near-black without this.
   The wordmark itself is an image and carries its own colour per variant. */
.foot .nav__brand { color: var(--white); }
.foot a:hover { color: var(--white); }
.foot__cols { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: var(--s-6); }
@media (max-width: 780px) { .foot__cols { grid-template-columns: 1fr 1fr; } }
.foot__h { color: var(--white); font-weight: 600; font-size: var(--t-sm); margin-bottom: var(--s-3); }
.foot__list { display: flex; flex-direction: column; gap: var(--s-2); }
.foot__bar { margin-top: var(--s-7); padding-top: var(--s-5); border-top: 1px solid rgba(255,255,255,.1); display: flex; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; font-size: var(--t-xs); }

/* ---------- motion ----------
   Transform only, so the text is legible on the very first frame. Fading in
   from opacity 0 would reproduce in miniature the thing this rebuild exists to
   fix: the live site hides 71 elements behind .elementor-invisible waiting for
   scroll JS, and the stat cards render as empty boxes on a fast load. Every
   .reveal here is an above-the-fold block, so it never gets to be invisible. */
@media (prefers-reduced-motion: no-preference) {
  .reveal { animation: reveal .5s cubic-bezier(.22,.61,.36,1) both; }
  @keyframes reveal {
    from { transform: translateY(10px); }
    to { transform: none; }
  }
}

/* ---------- lab index only ---------- */

.lab-note { background: var(--warn-tint); border-bottom: 1px solid var(--warn); color: var(--warn); font-size: var(--t-sm); }
.lab-note .wrap { padding-top: 10px; padding-bottom: 10px; display: flex; gap: var(--s-3); align-items: center; flex-wrap: wrap; }

/* ---------- page furniture ----------
   The announcement bar, sticky header state, section heads, setup steps, panel
   and ledger, pricing cards, FAQ and closing CTA. (The nav and footer proper are
   in the "site chrome" section above.) These were written into home.css
   when the homepage was the only page, then copied into p-shared.css so the
   other 62 pages could have them without loading home.css. The single copy now
   lives here, which every page already loads.

   Only rules that were byte-identical in both copies were promoted, and only
   top-level ones: a rule inside @media cannot move to an earlier stylesheet
   without changing which of two equal-specificity media rules wins the tie, so
   the responsive breakpoints stay with their own layer.

   Where the homepage's variant genuinely differs it stays in home.css and
   overrides this, since home.css loads later: the stuck-header backdrop blur,
   the gradient hairline inside a panel card, an 11px ledger timestamp against
   this layer's --t-xs, and the step number's top offset. The FAQ open animation
   is not one of them: home.css and p-shared.css declare their own @keyframes
   faq-in because neither layer can see the other's, but both travel 4px. Change
   one and change the other. */

.ann {
  background: var(--ink);
  color: var(--on-dark);
  font-size: var(--t-xs);
}
.ann .wrap { display: flex; align-items: center; justify-content: center; gap: var(--s-3); padding-top: 9px; padding-bottom: 9px; flex-wrap: wrap; }
.ann b { color: var(--white); font-weight: 600; }
.ann a { color: var(--accent-on-dark); font-weight: 600; }
.ann a:hover { text-decoration: underline; }
.topbar {
  position: sticky; top: 0; z-index: 50;
  background: var(--white);
  border-bottom: 1px solid transparent;
  transition: background-color .3s var(--fx-ease), border-color .3s var(--fx-ease), box-shadow .3s var(--fx-ease);
}
.section-head { max-width: 46em; }
.section-head--center { margin-inline: auto; text-align: center; }
/* Balanced to match the h2 above it, which already carries .fx-balance.
   Not `pretty`: at 390 the lede runs past Chrome's balancing cap so this is a
   no-op there, and the last-line orphan it leaves is accepted rather than fixed. */
.section-head--center .lede { margin-inline: auto; text-wrap: balance; }
.section-head h2 { font-size: clamp(28px, 3.6vw, 46px); letter-spacing: -.035em; }
.todo { border: 1px dashed var(--warn); background: var(--warn-tint); color: var(--warn); border-radius: var(--r-sm); padding: var(--s-3) var(--s-4); font-size: var(--t-sm); }
.pairs { margin-top: var(--s-7); padding-top: var(--s-6); border-top: 1px solid var(--line); }
.pairs h3 { font-size: var(--t-lg); margin-bottom: var(--s-2); }
.pairs__list { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-4); }
.pairs__list a {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px var(--s-3); border-radius: var(--r-full);
  border: 1px solid var(--line); background: var(--white);
  font-size: var(--t-sm); color: var(--body);
  transition: border-color .2s var(--fx-ease), color .2s var(--fx-ease);
}
.pairs__list a img { width: 14px; height: 14px; object-fit: contain; flex: none; }
.pairs__list a:hover { border-color: var(--accent); color: var(--accent); }
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-5); counter-reset: step; }
.step { position: relative; padding-top: var(--s-6); border-top: 2px solid var(--line); counter-increment: step; }
.step h3 { font-size: var(--t-lg); margin-bottom: var(--s-2); }
.step p { font-size: var(--t-sm); }
.step__meta { margin-top: var(--s-3); font-size: var(--t-xs); color: var(--muted); font-family: var(--mono); }
.panel-card { border-radius: var(--r-md); border: 1px solid var(--line); background: var(--white); box-shadow: var(--shadow-md); overflow: hidden; }
.panel-card__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line); background: var(--surface); }
.panel-card__title { font-size: var(--t-sm); font-weight: 700; color: var(--ink); display: flex; align-items: center; gap: var(--s-2); }
.panel-card__title img { width: 16px; height: 16px; object-fit: contain; }
.panel-card__body { padding: var(--s-4); }
.ledger { display: flex; flex-direction: column; gap: 6px; }
.ledger__row { display: grid; grid-template-columns: 1fr auto auto; gap: var(--s-3); align-items: center; padding: 10px var(--s-3); border-radius: var(--r-sm); border: 1px solid var(--line); font-size: var(--t-sm); }
.ledger__row.is-hit { border-color: color-mix(in srgb, var(--ok) 40%, transparent); background: var(--ok-tint); }
.ledger__row.is-src { border-color: var(--accent); background: var(--accent-tint); }
/* Waiting rows stay readable: opacity .5 dropped contrast to ~2:1. */
.ledger__row.is-wait .ledger__chan,
.ledger__row.is-wait .ledger__n,
.ledger__row.is-wait .ledger__t { color: var(--muted); }
.ledger__chan { display: flex; align-items: center; gap: var(--s-2); color: var(--ink); font-weight: 500; }
.ledger__chan img { width: 16px; height: 16px; object-fit: contain; }
.ledger__n { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink); }
.ledger__t.is-ok { color: var(--ok); }
.price-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-4); align-items: stretch; }
.price-card {
  display: flex; flex-direction: column; gap: var(--s-4);
  padding: var(--s-5); border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--white);
  transition: border-color .25s var(--fx-ease), transform .25s var(--fx-ease-out), box-shadow .25s var(--fx-ease);
}
.price-card.is-featured { border-color: var(--accent); box-shadow: var(--shadow-md); }
.price-card .row-between { min-height: var(--s-6); }
.price-card .btn { margin-top: auto; }
.price-card__amt { font-size: var(--t-3xl); font-weight: 800; color: var(--ink); letter-spacing: -.03em; line-height: 1; }
.price-card__amt span { font-size: var(--t-sm); font-weight: 500; color: var(--muted); letter-spacing: 0; }
.price-card .checklist li { padding: var(--s-1) 0; font-size: var(--t-sm); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary { cursor: pointer; list-style: none; padding: var(--s-4) 0; font-weight: 600; color: var(--ink); font-size: var(--t-lg); display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+'; font-family: var(--mono); font-size: var(--t-xl); color: var(--muted); flex: none; line-height: 1; }
.faq details[open] summary::after { content: '\2212'; }
.faq p { padding: 0 0 var(--s-4); max-width: 62em; }
.cta-final { text-align: center; padding: var(--s-6) 0; }
.cta-final h2 { font-size: clamp(30px, 4.4vw, 54px); letter-spacing: -.04em; line-height: 1.05; max-width: 16em; margin-inline: auto; }
/* Scoped to the block, not to the `.lede.mx-auto.text-center` utility trio the
   markup happens to use: `.fx-cta-band` carries the same trio and must not balance. */
.cta-final .lede { text-wrap: balance; }
