/* World Supply Express - wholesale collectables, Leonia NJ
   Dark only, token-driven. The light scheme was removed 2026-09-23 with the
   space homepage, which is dark by design.
   Every colour pair in here was contrast-measured; ratios noted where they matter. */

/* ── Self-hosted type ───────────────────────────────────────
   Latin-subset variable woff2. No third-party font request, which is what
   lets cookies.html honestly say nothing leaves this site. */
@font-face {
  font-family: 'Inter var';
  src: url('fonts/inter.woff2') format('woff2-variations');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Archivo var';
  src: url('fonts/archivo.woff2') format('woff2-variations');
  font-weight: 600 800;
  font-style: normal;
  font-display: swap;
}

/* ── Tokens ─────────────────────────────────────────────────
   Dark is the default. Ratios are against --base unless stated.
   The palette comes from the logo (photos/new logo.png): a cool near-black
   plate, a white-to-blue ribbon W. Neutrals are cool to match the plate, and
   the accent is the logo's blue, #4854FB. It replaced the warm board and
   safety-yellow scheme on 2026-09-23, when the new logo came in. */
:root {
  --base:      #05070F;   /* page, deep navy from the homepage mock - never pure #000 */
  --base-2:    #0D0F16;   /* banded section */
  --surface:   #131620;   /* solid card */

  --head:      #F4F6FC;   /* headings        18.6:1 */
  --text:      #CED3E0;   /* body            13.4:1, 12.1:1 on --surface */
  --muted:     #959CB2;   /* meta             7.3:1, 6.6:1 on --surface */

  /* Logo blue. White sits on it at 5.3:1, and the fill clears 3:1 against the
     page (3.8:1), so it defines its own boundary in both schemes and
     --accent-br can simply equal it. Accent TEXT on dark needs a lighter tint
     than the fill: --accent-lt. */
  --accent:    #4854FB;   /* CTA fill, white on it 5.3:1 */
  --accent-hi: #3A45E8;   /* CTA hover, white on it 6.6:1 */
  --accent-br: #4854FB;   /* CTA border, same as the fill */
  --accent-lt: #8C9CFF;   /* accent text      7.9:1, 7.2:1 on --surface */
  --plate:     #262B3B;   /* image placeholder fill - not text */
  /* Caution. Orange against the blue accent: the two are close in luminance,
     so hue is what separates them, and blue vs orange is as far apart as hue
     gets. */
  --amber:     #F59E4B;   /* caution text     9.4:1 */

  --signal:    #F4F6FC;   /* focus ring, ticks  18.6:1 */

  --hair:      #1C2030;   /* decorative rules only */
  --edge:      #5E667E;   /* interactive borders, 3.5:1, 3.2:1 on --surface (WCAG 1.4.11) */

  /* Flat card; the names are historic, see .glass. */
  --glass-dim:  #11131B;
  --glass-tint: rgba(255, 255, 255, 0);
  --glass-edge: #242938;
  --glass-inset: inset 0 1px 0 rgba(255, 255, 255, .05);   /* card top edge catching light */

  /* Liquid Glass, controls layer only: buttons, nav pills, the header and the
     dock. Content cards stay opaque (see .glass). --ctl-* is the lensed fill of
     a glass control, top to bottom; --spec is its specular top rim. --bar is
     the frosted material under the sticky header and the dock, the only places
     content actually scrolls beneath, so the only places blur buys anything. */
  --ctl-top:    rgba(255, 255, 255, .10);
  --ctl-bot:    rgba(255, 255, 255, .03);
  --ctl-top-hi: rgba(255, 255, 255, .17);
  --ctl-bot-hi: rgba(255, 255, 255, .06);
  --spec:       rgba(255, 255, 255, .20);
  --bar:        rgba(7, 8, 12, .66);
  --bar-edge:   rgba(255, 255, 255, .09);

  --on-accent: #FFFFFF;   /* white on logo blue, 5.3:1 */
  --on-amber:  #07080C;   /* never white on amber */

  /* Continuous, generous corners in the Apple manner. Controls are capsules. */
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 28px;
  --r-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(0,0,0,.5);
  --shadow-md: 0 2px 6px rgba(0,0,0,.45);
  --shadow-lg: 0 6px 18px -6px rgba(0,0,0,.6);

  /* One easing curve for the whole site. Fast out, long settle. */
  --ease: cubic-bezier(.16, 1, .3, 1);

  --f-display: 'Archivo var', 'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --f-text: 'Inter var', 'Inter', 'Helvetica Neue', Arial, sans-serif;

  --wrap: 1180px;
  --pad: 28px;

  color-scheme: dark;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 90px;   /* sticky header must not cover anchor targets */
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--base);
  color: var(--text);
  font: 400 17px/1.65 var(--f-text);
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* No gradient mesh. It was four fixed radial blobs behind everything, and it was
   the single loudest "generated" signal on the page - the background did more
   talking than the copy. Flat --base instead, with the only tonal shift coming
   from the banded --base-2 sections, which is a structural cue rather than
   decoration. This also retires the reason the glass dimming layer existed. */

/* hyphens: display type is sized in rem so it still grows when a visitor
   raises their default font size. At that point a single long word
   ("Collectables", "Accessibility") can exceed the viewport, and nothing can
   shrink below its min-content width - the page would gain horizontal
   scrolling (WCAG 1.4.10 Reflow). Hyphenation lets the word break instead.
   Never triggers at normal sizes. It has to be overflow-wrap: anywhere, not
   break-word: both break identically when laying out lines, but only anywhere
   is allowed to reduce the min-content width, which is the number the grid
   track is actually sized from. */
h1, h2, h3 {
  font-family: var(--f-display);
  font-weight: 800;
  letter-spacing: -.025em;
  color: var(--head);
  margin: 0;
  hyphens: auto;
  overflow-wrap: anywhere;
  text-wrap: balance;
}
p { margin: 0 0 1em; }
a { color: inherit; }
::selection { background: var(--accent); color: #fff; }

/* Every tappable thing, not only .btn. manipulation drops the 300ms Safari
   spends waiting to see if a tap was a double-tap zoom, and the default tap
   highlight is a grey rectangle that ignores border-radius. */
a, button, summary, label {
  touch-action: manipulation;
  -webkit-tap-highlight-color: rgba(72, 84, 251, .22);   /* logo blue */
}

/* Accent inline links. Underlined at rest, so hover has to move something
   else: --head plus a thicker rule reads in both schemes. Grouped here because
   five separate blocks were each missing it. */
.close-mail a:hover, .wl-note a:hover,
.doc-in a:hover, .nf-links a:hover, .notice a:hover {
  color: var(--head);
  text-decoration-thickness: 2px;
}

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 var(--pad); }

/* Two-tone focus ring. The inner ring is --signal, which is near-white on dark and
   ink on light, so it is the highest-contrast colour available in either mode;
   the base-coloured outer halo separates it from whatever it lands on. Both
   rings sit outside the element, so the inner one only has to beat --base -
   which is what lets it read on the blue CTA fill too. */
:focus-visible {
  outline: 3px solid var(--signal);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--base);
  border-radius: var(--r-sm);
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 80;
  background: var(--accent); color: var(--on-accent);
  padding: 12px 18px; font-weight: 600; border-radius: var(--r-sm);
  text-decoration: none;
}
.skip:focus { left: 12px; top: 12px; }

/* ── Panel primitive ────────────────────────────────────────
   background-color paints the dim layer, background-image paints the tint on
   top of it. One element, two composited layers, order guaranteed by CSS.
   Still translucent, so it picks up whichever band it sits on, but the
   backdrop-filter is gone: with a flat background there is nothing behind a
   panel to blur, so the filter cost a compositor layer per card and bought
   nothing but a saturate() nobody could see. */
.glass {
  background-color: var(--glass-dim);
  background-image: linear-gradient(var(--glass-tint), var(--glass-tint));
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md), var(--glass-inset);
}

/* ── Photo slots ────────────────────────────────────────────────
   One primitive for every image on the page. Every file is downloaded and
   served from this domain, never hotlinked - hotlinking would falsify the
   "images are served from this domain" line in cookies.html, and img-src
   'self' would block it anyway.
   The slot owns the crop: aspect-ratio fixes the shape, object-fit: cover
   fills it, so a replacement photo of any size drops in without a reflow.
   Deliberately no min-height. aspect-ratio plus a definite min-height gives
   the box a min-content inline size of min-height x ratio, which any grid
   track sized from auto then has to honour - that blew the inventory grid
   past the viewport at 320px and at large default font sizes. The photo is
   the only child and it is absolutely positioned, so there is no content
   left to protect from collapsing and nothing to gain by keeping it. */
.shot {
  position: relative; margin: 0; overflow: hidden;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--edge); border-radius: var(--r-lg);
  /* Shows only while the photo loads or if one is ever missing. A flat tone
     reads as an empty slot, which is what it is; a gradient would read as
     decoration. */
  background: var(--plate);
}
.shot > img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; border-radius: inherit;
}
/* This used to be a tiny uppercase label with a glowing cyan dot in front of
   it, which is the single most flagged generated-hero signature there is - the
   detector raised it on four pages and it never had an answer. Same words, same
   markup on all eight pages, restyled as a stencilled origin line: no dot, a
   flat rule instead, and the label set on the baseline like a crate marking.
   Kept as ::before so nothing in the DOM had to change.

   Restyled a second time, and the size is the whole fix. Spaced caps are the
   house idiom - the wordmark, the section labels and the spec table all speak
   it - so the caps were never the tell. The tell was the SIZE: 12.5px tracked
   caps floating above a 50px headline is a whisper, and a tiny tracked
   micro-label above an oversized hero is the default generated hero. At 15px it
   sits in the same size class as the 15.5px wordmark and reads as a marking
   printed on the page rather than a chip hovering over it. */
.eyebrow {
  display: inline-flex; align-items: center; gap: 12px;
  font-size: 15px; font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent-lt);   /* body-sized, so needs 4.5:1. 7.9:1 dark, 6.7:1 light */
  margin: 0 0 22px;
}
/* align-self and the margin, rather than letting align-items: center handle it.
   On one line those look identical, but the hero label wraps to two lines at
   320px, and centring the rule against a two-line block parks it in the gutter
   between them pointing at nothing. This pins it to the middle of the FIRST
   line at any width: .825em is half the 1.65 line-height, less 1px for half the
   rule's own thickness, so it tracks the font-size instead of hard-coding 11px. */
.eyebrow::before {
  content: ""; width: 26px; height: 2px; border-radius: 0;
  background: var(--accent-lt); flex: none;
  align-self: flex-start; margin-top: calc(.825em - 1px);
}

/* ── Buttons ────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  min-height: 48px; padding: 12px 22px;
  font: 600 16px/1.1 var(--f-text); text-decoration: none; white-space: nowrap;
  border: 1px solid transparent; border-radius: var(--r-pill);
  cursor: pointer;
  transition: background-color .22s var(--ease), border-color .22s var(--ease),
              color .22s var(--ease), transform .22s var(--ease), box-shadow .22s var(--ease);
}
.btn svg { width: 17px; height: 17px; flex: none; }
.btn-lg { min-height: 56px; padding: 16px 30px; font-size: 17px; }

/* --glow is gone, not retuned. A tinted halo under a filled button is a
   generated-UI tell and the detector flagged it on every page; the button is
   grounded by its border now instead.
   The border is --accent-br. The fill is the only thing defining the button's
   shape, so WCAG 1.4.11 wants it at 3:1 against the page; the logo blue clears
   that in both schemes (3.75:1 dark, 5.5:1 light), so the token equals the
   fill today. It stays separate so a lighter accent can get a darker border
   without touching every button. */
.btn-call {
  /* Tinted glass. The gradient only ever darkens from --accent, so white text
     never sits on anything lighter than the measured 5.3:1 / 6.0:1 fill. The
     rim and the drop shadow are neutral, not blue: no tinted halo. */
  background: var(--accent) linear-gradient(180deg, var(--accent), var(--accent-hi));
  color: var(--on-accent); border-color: var(--accent-br);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.30), inset 0 -1px 0 rgba(0,0,0,.18), var(--shadow-sm);
}
.btn-call:hover {
  background: var(--accent-hi);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.36), inset 0 -1px 0 rgba(0,0,0,.2), var(--shadow-md);
}

/* Outlined buttons rely on their border to exist, so it must be --edge (3.9:1),
   never --hair. */
.btn-ghost {
  /* Clear glass: a lensed fill, a specular top rim, and the --edge border that
     keeps the capsule at 3:1 on its own. */
  background: linear-gradient(180deg, var(--ctl-top), var(--ctl-bot));
  color: var(--head); border-color: var(--edge);
  box-shadow: inset 0 1px 0 var(--spec), var(--shadow-sm);
}
.btn-ghost:hover {
  background: linear-gradient(180deg, var(--ctl-top-hi), var(--ctl-bot-hi));
  box-shadow: inset 0 1px 0 var(--spec), var(--shadow-md);
}
/* The generic :focus-visible halo is a box-shadow, so on a button, which now
   has its own, it must be restated or the glass shadow would erase it. */
.btn:focus-visible { box-shadow: 0 0 0 6px var(--base); }

@media (prefers-reduced-motion: no-preference) {
  .btn:hover { transform: translateY(-1px); }
  /* scale, not translateY(0). Returning to rest only reads as a press if the
     pointer lifted the button first, and a touch device never hovers - a tap
     used to give no feedback at all. */
  .btn:active { transform: scale(.97); }
}

.cta-row { display: flex; flex-wrap: wrap; gap: 12px; margin: 32px 0 16px; }

/* ── Header ─────────────────────────────────────────────────── */
.topbar {
  position: sticky; top: 0; z-index: 40;
  background-color: var(--bar);
  border-bottom: 1px solid var(--bar-edge);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .topbar, .dock {
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    backdrop-filter: blur(24px) saturate(180%);
  }
}
.topbar-in { display: flex; align-items: center; gap: 28px; min-height: 76px; }

.brand {
  display: flex; align-items: center; gap: 12px; min-height: 48px;
  text-decoration: none; margin-right: auto;
}
/* logo-w.png: the bare W, cut from photos/new logo.png with its black plate
   made clear, as the homepage mock draws it. Safe now the site is dark only;
   on a light page the W's near-white left stroke would vanish. */
.brand-mark { width: 44px; height: 24px; flex: none; }
/* Tracking goes positive and the name goes uppercase. Negative tracking on a
   display face is the standard generated-startup wordmark; a distributor's name
   is stencilled onto the side of a box, which means spaced-out caps. Slightly
   smaller than before because caps read larger at the same point size. */
.brand-text {
  font: 800 15.5px/1.15 var(--f-display); letter-spacing: .04em; display: block;
  text-transform: uppercase; color: var(--head);
}
.brand-text em {
  display: block; font: 500 11px/1.4 var(--f-text); font-style: normal;
  letter-spacing: .13em; text-transform: uppercase; color: var(--muted); margin-top: 3px;
}

.topnav { display: flex; align-items: center; gap: 6px; }
/* inline-flex plus min-height because accessibility.html claims every button,
   phone number and standalone link is at least 44px tall. These computed to
   42.8px, so the claim was false. .topbar-in already has a 76px min-height, so
   the fix costs no layout. */
.topnav a {
  display: inline-flex; align-items: center; min-height: 44px;
  text-decoration: none; font-size: 15px; font-weight: 500; color: var(--text);
  padding: 9px 12px; border: 1px solid transparent; border-radius: var(--r-pill);
  white-space: nowrap;   /* 12px + the 1px capsule border keeps the old 13px footprint */
  transition: background-color .22s var(--ease), color .22s var(--ease), border-color .22s var(--ease);
}
.topnav a:hover { color: var(--head); background: var(--ctl-bot-hi); }
/* Current page sits in a glass capsule. The capsule's --edge border is a
   shape, so colour is not the only signal. */
.topnav a[aria-current="page"] {
  color: var(--head); border-color: var(--edge);
  background: linear-gradient(180deg, var(--ctl-top), var(--ctl-bot));
  box-shadow: inset 0 1px 0 var(--spec);
}

.topbar-call { min-height: 44px; padding: 10px 18px; font-size: 15px; }

/* ── Hero copy, shared by the product pages ───────────────── */
.lede { max-width: 34em; margin-top: 26px; font-size: 19px; line-height: 1.62; }
.cta-note { font-size: 14.5px; color: var(--muted); margin: 0; }

/* Carton barcode. Deliberately NOT a progress bar: a filled meter reads as a
   real measurement, and there is no metric here to measure. */
.spec-bars {
  height: 38px; opacity: .45;
  background: repeating-linear-gradient(90deg,
    var(--muted) 0 2px, transparent 2px 5px, var(--muted) 5px 8px, transparent 8px 13px,
    var(--muted) 13px 14px, transparent 14px 18px, var(--muted) 18px 22px, transparent 22px 24px);
}

/* ── Sections ───────────────────────────────────────────────── */
.section { padding: 92px 0; }
.section-alt { background: var(--base-2); border-top: 1px solid var(--hair); border-bottom: 1px solid var(--hair); }
.sec-h { font-size: clamp(1.95rem, 3.4vw, 2.7rem); line-height: 1.05; }
.sec-sub { margin: 16px 0 0; max-width: 40em; color: var(--muted); font-size: 17.5px; }
/* .sec-sub zeroes its own bottom margin, which is right when it is the last
   thing in a section header and wrong the moment body copy follows it. */
.sec-sub + p { margin-top: 22px; }

/* ── Accounts split ────────────────────────────────────────── */
.split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .85fr); gap: 60px; align-items: start; }

.ticks { list-style: none; margin: 34px 0 0; padding: 0; }
.ticks li {
  position: relative; padding: 13px 0 13px 36px; border-bottom: 1px solid var(--hair);
  font-size: 17.5px;
}
.ticks li::before {
  content: ""; position: absolute; left: 3px; top: 21px;
  width: 13px; height: 7px;
  border-left: 2.5px solid var(--signal); border-bottom: 2.5px solid var(--signal);
  transform: rotate(-45deg);
}

.callout { padding: 34px 32px; }
.callout h3 { font-size: 1.5rem; }
.callout p { margin: 16px 0 0; color: var(--muted); }
/* .callout p is --muted, so a <b> inside one inherits muted rather than --head
   and the verdict stops reading as a verdict. Matches how .notice b behaves. */
.callout p b { color: var(--head); }
.callout-b { color: var(--accent-lt) !important; font-weight: 600; }

/* ── Ordering steps ────────────────────────────────────────── */
.steps { counter-reset: s; list-style: none; margin: 52px 0 0; padding: 0; max-width: 900px; }
.steps li {
  counter-increment: s; position: relative; padding: 26px 0 26px 86px;
  border-top: 1px solid var(--hair);
}
.steps li:last-child { border-bottom: 1px solid var(--hair); }
.steps li::before {
  content: counter(s, decimal-leading-zero);
  position: absolute; left: 0; top: 24px;
  font: 800 2.2rem/1 var(--f-display); letter-spacing: -.04em;
  color: var(--accent-lt);
}
.steps h3 { font-size: 1.2rem; font-weight: 700; }
.steps p { margin: 8px 0 0; color: var(--muted); max-width: 42em; }

/* ── Trust band ────────────────────────────────────────────── */
.trust { padding: 30px 0 92px; }
.trust-in { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.trust-in > div { padding: 26px 26px 24px; }
.trust-in b {
  display: block; font: 800 1.1rem/1.3 var(--f-display); letter-spacing: -.02em;
  color: var(--head);
}
.trust-in span { display: block; margin-top: 6px; font-size: 15px; color: var(--muted); }

/* ── FAQ ───────────────────────────────────────────────────── */
.faq { margin-top: 44px; max-width: 880px; }
.faq details {
  border: 1px solid var(--hair); border-radius: var(--r-md);
  margin-bottom: 10px; background: var(--surface);
  transition: border-color .22s var(--ease);
}
.faq details[open] { border-color: var(--edge); }
.faq summary {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 20px 24px; cursor: pointer; list-style: none;
  font: 700 1.08rem/1.35 var(--f-display); letter-spacing: -.02em; color: var(--head);
  border-radius: var(--r-md);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; flex: none; width: 15px; height: 15px;
  background:
    linear-gradient(var(--signal), var(--signal)) center/15px 2.5px no-repeat,
    linear-gradient(var(--signal), var(--signal)) center/2.5px 15px no-repeat;
  transition: transform .22s var(--ease);
}
.faq details[open] summary::after { background-size: 15px 2.5px, 0 0; }
.faq summary:hover { color: var(--accent-lt); }
.faq p { margin: 0 24px 22px; color: var(--muted); max-width: 62ch; }

/* ── Closing CTA ───────────────────────────────────────────── */
.close { padding: 30px 0 96px; }
.close-in { padding: 60px 48px; text-align: center; border-radius: var(--r-xl); }
.close h2 { font-size: clamp(2rem, 4vw, 3rem); line-height: 1.03; margin-inline: auto; max-width: 20em; }
.close-in > p { margin: 16px auto 0; color: var(--muted); font-size: 18px; max-width: 34em; }
.close .cta-row { justify-content: center; }
.close-mail { margin: 0; font-size: 15.5px; }
.close-mail a {
  display: inline-flex; align-items: center; min-height: 44px;
  color: var(--accent-lt); text-decoration-thickness: 1px; text-underline-offset: 3px;
}

/* ── Want-list form ─────────────────────────────────────────────
   action is a mailto:, so the list opens in the visitor's own mail client
   already addressed to Sam and no server and no third party ever receives
   it. That is the only submit target that keeps the privacy and cookie
   pages true; a hosted form handler would be a cross-origin POST of a
   shop's contact details. Needs form-action mailto: in the CSP - see
   vercel.json. It sits below the call and text buttons on purpose: phone
   is still how most shops actually buy. */
.wl { max-width: 580px; margin: 0 auto; text-align: left; }
/* Sentence case, and it is a correction rather than a preference. This is a
   whole sentence ("Tell us what you would put on it"), not a label, and a
   32-character sentence set in tracked caps is the one thing the all-caps rule
   is actually about: we read words by their shape and uppercase removes it.
   Every other caps run on this site is a 4-to-12 character label. So this one
   becomes what it always was - the heading over the form - and it gets display
   weight instead of tracking to carry that. */
.wl-lead {
  margin: 0 0 22px; padding-top: 30px; border-top: 1px solid var(--glass-edge);
  font: 700 19px/1.3 var(--f-display); letter-spacing: -.015em;
  color: var(--head); text-align: center;
}
.wl-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
.wl-f { margin: 0 0 16px; }
.wl-f label {
  display: block; margin-bottom: 7px;
  font: 600 12.5px/1.4 var(--f-text); letter-spacing: .09em; text-transform: uppercase;
  color: var(--head);
}
.wl-f input, .wl-f textarea {
  display: block; width: 100%; min-height: 48px; padding: 12px 14px;
  /* 16px floor: anything smaller and iOS Safari zooms the page on focus. */
  font: 400 16px/1.45 var(--f-text);
  color: var(--head); background: var(--surface);
  /* The border is the only thing that defines the field, so it has to be
     --edge (3.9:1) to satisfy WCAG 1.4.11, never --hair. */
  border: 1px solid var(--edge); border-radius: var(--r-sm);
  transition: border-color .22s var(--ease);
}
.wl-f input:hover, .wl-f textarea:hover { border-color: var(--head); }
.wl-f textarea { min-height: 118px; resize: vertical; }
.wl-f ::placeholder { color: var(--muted); opacity: 1; }
.wl .btn { width: 100%; }
.wl-note { margin: 14px 0 0; font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.wl-note a { color: var(--accent-lt); text-underline-offset: 3px; }

/* ── Pull Sheet page ────────────────────────────────────────────
   Landing page for an unbuilt product, /pull-sheet. Full chrome, so it reuses
   the topbar, the four-column footer and the mobile dock unchanged. */
/* Two columns. .ps-hero-in sits on the .wrap itself, so the grid inherits the
   page gutter instead of nesting a second box inside it. Copy-heavy because the
   right-hand card is a narrow ticket. */
.ps-hero { padding: 84px 0 22px; }
.ps-hero-in {
  display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, .72fr);
  gap: 56px; align-items: center;
}
.ps-hero-copy { min-width: 0; }
.ps-hero h1 { font-size: clamp(2.6rem, 5.8vw, 4.3rem); line-height: 1; }

/* ── Blank pull ticket (hero visual) ──────────────────────────
   The paper form Pull Sheet would replace. Every field is an empty rule on
   purpose: see the comment in pull-sheet.html. Flat card, one hairline, a
   stamped accent header band - no glass and no gradient. */
.ticket {
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-md); box-shadow: var(--shadow-md); overflow: hidden;
}
.ticket-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 13px 18px; background: var(--accent); color: var(--on-accent);
  font: 700 11.5px/1.3 var(--f-text); letter-spacing: .12em; text-transform: uppercase;
}
.ticket-grid { margin: 0; padding: 22px 18px 4px; }
.ticket-grid dt {
  font: 700 10.5px/1.3 var(--f-text); letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted);
}
/* The field itself. A rule with nothing written on it is the honest version of
   this card - anything in here would be an invented case count or date. --edge
   rather than --hair because the rule IS the field, so it has to be visible. */
.ticket-grid dd {
  margin: 0 0 17px; height: 19px; border-bottom: 1px solid var(--edge);
}
.ticket .spec-bars { margin: 4px 18px 0; }
.ticket-foot {
  margin: 0; padding: 13px 18px 15px; border-top: 1px solid var(--hair);
  font: 700 10.5px/1.3 var(--f-text); letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted);
}

/* Collapses well above the phone breakpoint: the ticket has five labelled rows
   and stops being legible long before 700px. */
@media (max-width: 980px) {
  .ps-hero-in { grid-template-columns: minmax(0, 1fr); gap: 40px; }
  .ticket { max-width: 420px; }
}
/* One line between the h1 and the lede carrying the actual promise. Display
   font so it reads as part of the headline rather than as body copy. */
.ps-tagline {
  margin: 22px 0 0; font: 800 clamp(1.35rem, 2.9vw, 1.95rem)/1.2 var(--f-display);
  letter-spacing: -.025em; color: var(--head); text-wrap: balance;
}
.ps-hero .lede { margin-top: 18px; }

/* App frame. Chrome around the sample view so it reads as a picture of an
   application and not as this site listing its inventory, which is the
   difference between a mockup and a breach of terms.html section 3.
   .glass carries no padding, so every part of the frame sets its own. */
.ps-app { margin-top: 52px; overflow: hidden; }
.ps-app-bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 12px;
  padding: 15px 22px; border-bottom: 1px solid var(--glass-edge);
}
.ps-app-mark { display: flex; flex: none; color: var(--accent-lt); }
.ps-app-mark svg { width: 21px; height: 21px; }
.ps-app-name { font: 800 15px/1 var(--f-display); letter-spacing: -.015em; color: var(--head); }
/* Amber, because amber is this site's "not final yet" colour everywhere else.
   Uppercased in CSS. */
.ps-tag {
  padding: 5px 11px; border: 1px solid var(--edge); border-radius: var(--r-pill);
  font: 600 11px/1 var(--f-text); letter-spacing: .1em; text-transform: uppercase;
  color: var(--amber); white-space: nowrap;
}
.ps-scroll { overflow-x: auto; overscroll-behavior-x: contain; }
/* The global focus ring rounds whatever it lands on; on a full-width scroll
   pane that reads as the frame changing shape. */
.ps-scroll:focus-visible { border-radius: 0; }

/* No min-width, deliberately. A min-width here is exactly what would force a
   phone to scroll sideways, and it would also blow the caption box wider than
   the visible frame. The two least important columns drop out instead, at the
   breakpoints at the end of this block. */
.ps-table {
  width: 100%; border-collapse: collapse;
  font-size: 15.5px; font-variant-numeric: tabular-nums;
}
/* The strongest honesty device on the page: programmatically bound to the data,
   announced before any row, visible, and it survives a screenshot. Not
   decoration, and it does not get shortened. */
/* max-width is not decoration here. Measured at 1280px without it, this line
   ran to 152 characters, against 65 for .lede, 76 for .sec-sub and 76 for
   .ps-foot-n eleven lines below it inside the same frame. The caption is the
   sentence on this page that most needs to actually get read, so it gets the
   same measure as everything else rather than double it.
   The divider that used to be this element's border-bottom now lives on the
   header row's border-top instead: max-width shrinks the <caption> box itself,
   so a border here would have stopped at 562px and left the line dangling
   halfway across a 1122px table. On the th cells it spans the table at every
   width, and it describes the header row rather than the caption anyway. */
.ps-cap {
  caption-side: top; text-align: left; padding: 17px 22px 15px;
  max-width: 562px;             /* about 76 characters at 13.5px */
  font-size: 13.5px; line-height: 1.55; color: var(--muted);
}
.ps-table thead th {
  text-align: left; padding: 11px 18px; white-space: nowrap;
  font: 600 11px/1.3 var(--f-text); letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted);
  /* border-top is the caption divider, moved off .ps-cap so it spans the whole
     table instead of stopping at the caption's measure. See .ps-cap above. */
  border-top: 1px solid var(--hair); border-bottom: 1px solid var(--hair);
}
.ps-table tbody th, .ps-table tbody td {
  padding: 15px 18px; text-align: left; vertical-align: middle;
  border-bottom: 1px solid var(--hair);
}
.ps-table tbody tr:last-child th,
.ps-table tbody tr:last-child td { border-bottom: 0; }
.ps-table tbody th { font-weight: 400; }
.ps-set {
  display: block; font: 700 16px/1.25 var(--f-display);
  letter-spacing: -.015em; color: var(--head);
}
.ps-line {
  display: block; margin-top: 4px; font-size: 12.5px; font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase; color: var(--accent-lt);
}
.ps-n { display: block; font-size: 13.5px; color: var(--muted); white-space: nowrap; }
.ps-c-window { color: var(--muted); font-size: 14.5px; white-space: nowrap; }

/* ── Allocation bar ─────────────────────────────────────────────
   A real <progress>, and the reason is the CSP. style-src is 'self' with no
   'unsafe-inline' (vercel.json), so a per-row fill width cannot be an inline
   style, and putting the numbers into utility classes would move the data out
   of the markup where nobody editing a row would think to look. <progress>
   keeps them in value and max and the browser computes the fill.
   Fills with what is still OPEN, not what is claimed, so more bar means more
   product and the graphic agrees with the sentence beside it.
   aria-hidden in the markup: the cell already says "18 of 24 cases open" in
   text, so the bar only repeats it - the same reasoning that marks the hero
   spec panel decorative at index.html:98.
   Compare .spec-bars above, which is deliberately NOT a bar because that card
   has no metric to measure. This one does, and the caption says it is a
   sample. */
.ps-bar {
  -webkit-appearance: none;
          appearance: none;
  display: block; width: 100%; max-width: 220px; min-width: 84px;
  height: 8px; margin: 0 0 7px; border: none; border-radius: 2px;
  overflow: hidden;             /* clips the fill's far corners in Firefox */
  background: var(--hair);      /* track: Firefox, and the fallback everywhere */
  color: var(--accent-lt);      /* forced-colors and legacy engines fall back here */
}
/* Vendor pseudo-elements cannot share a selector list - one unrecognised pseudo
   invalidates the whole rule - so these stay as separate blocks. */
.ps-bar::-webkit-progress-bar   { background: var(--hair);      border-radius: 2px; }
.ps-bar::-webkit-progress-value { background: var(--accent-lt); border-radius: 2px; }
.ps-bar::-moz-progress-bar      { background: var(--accent-lt); border-radius: 2px; }

/* Nearly closed. Amber is a second signal, never the only one: the row also
   carries a chip that says "Closing" in words. Must stay after the rules
   above, which it overrides. */
.ps-bar-low::-webkit-progress-value { background: var(--amber); }
.ps-bar-low::-moz-progress-bar      { background: var(--amber); }

/* Not optional. A <progress> with no value attribute is indeterminate, and
   every engine animates that forever, which would falsify "nothing on the site
   flashes, autoplays or moves on its own" in accessibility.html. Every bar on
   the page carries a value; this is the net under the one that someday will
   not. */
.ps-bar:indeterminate { animation: none; }
.ps-bar:indeterminate::-webkit-progress-bar { animation: none; background-image: none; }
.ps-bar:indeterminate::-moz-progress-bar    { animation: none; background-image: none; }

/* High contrast mode replaces the fill with a system colour and can flatten
   the track into it. The count is in text beside the bar, so nothing is lost;
   the outline just keeps the graphic from disappearing entirely. */
@media (forced-colors: active) {
  .ps-bar { border: 1px solid CanvasText; }
}

/* Status chips. The word is the signal and the colour reinforces it, never the
   other way round - accessibility.html says colour is never the only thing
   carrying meaning. Not interactive, so none of these is a hit target. */
.ps-chip {
  display: inline-flex; align-items: center; padding: 6px 12px;
  border: 1px solid var(--edge); border-radius: var(--r-pill);
  font: 600 11.5px/1 var(--f-text); letter-spacing: .08em;
  text-transform: uppercase; white-space: nowrap;
}
.ps-chip-open { color: var(--accent-lt); }
.ps-chip-low  { color: var(--amber); }
.ps-chip-done { color: var(--muted); }
/* Second copy of the chip, folded into the set cell for narrow screens where
   the status column drops out. display:none removes it from the accessibility
   tree too, so exactly one copy is ever exposed. */
.ps-chip-in { display: none; margin-top: 9px; }

.ps-app-foot {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 14px; padding: 18px 22px; border-top: 1px solid var(--glass-edge);
}
.ps-foot-n { margin: 0; max-width: 40em; font-size: 14px; color: var(--muted); }

/* The "not yet" list. Same skeleton as .ticks, but the marker is a flat dash in
   --muted rather than a cyan check, so the list reads as limits at a glance.
   Shape, not colour, doing the work. */
.ps-nots { list-style: none; margin: 34px 0 0; padding: 0; }
.ps-nots li {
  position: relative; padding: 13px 0 13px 36px;
  border-bottom: 1px solid var(--hair); font-size: 17.5px;
}
.ps-nots li::before {
  content: ""; position: absolute; left: 3px; top: 27px;
  width: 15px; height: 0; border-top: 2.5px solid var(--muted);
}

/* Column drop-out, widest to narrowest. The window column goes first because a
   date is the least load-bearing thing on the row; the status column goes
   second and its chip reappears inside the set cell. Below 640px the table is
   two columns, set and cases open, which fits a 320px screen without
   scrolling. */
@media (max-width: 900px) { .ps-c-window { display: none; } }
@media (max-width: 640px) {
  .ps-c-status { display: none; }
  .ps-chip-in  { display: inline-flex; }
  /* Measured: at 320px the two remaining columns wanted 294px inside a 278px
     pane, and this nowrap was the whole 16px of it - a nowrap phrase sets its
     column's intrinsic minimum width. Letting it wrap is what keeps the table
     off .ps-scroll's scrollbar on the narrowest screen WCAG 1.4.10 asks about.
     It stays nowrap above this width, where "18 of 24 cases open" reading as
     one unit is worth the column. */
  .ps-n { white-space: normal; }
}

/* ── Footer ────────────────────────────────────────────────── */
.foot { background: var(--base-2); border-top: 1px solid var(--hair); padding: 60px 0 32px; }
/* Column widths are set by their longest unbreakable content, not evenly: at
   1fr each, the contact column was too narrow for the email address. */
/* Measured needs at full width: the email is 304px on one line, "Business
   conduct" about 125px. The ratios below clear each with a little headroom;
   column 1 takes what is left and wraps freely. */
.foot-in { display: grid; grid-template-columns: 1.2fr 1.55fr 1fr; gap: 32px; }
.foot p { margin: 0 0 .6em; font-size: 15.5px; color: var(--muted); }
.foot-name {
  font: 600 12px/1.4 var(--f-text) !important; letter-spacing: .12em; text-transform: uppercase;
  color: var(--head) !important; margin-bottom: 14px !important;
}
.foot-links { list-style: none; margin: 0; padding: 0; }
/* inline-block with symmetric padding, not inline-flex: 11 + 22 + 11 reaches
   the same 44px target, but keeps the content as ordinary inline text. In a
   flex container each text run becomes its own flex item and the <wbr> in the
   email address stops acting as a soft break, so the address wrapped even
   where the column had room for it. */
.foot-links a {
  display: inline-block; padding: 11px 0; line-height: 22px; min-height: 44px;
  font-size: 15.5px; color: var(--text);
  text-decoration-thickness: 1px; text-underline-offset: 3px;
}
.foot-links a:hover { color: var(--accent-lt); }
.foot-legal {
  position: relative; margin-top: 48px; padding-top: 24px;
  display: flex; flex-wrap: wrap; gap: 8px 40px; justify-content: space-between;
}
/* The rule is drawn inset by --pad rather than as a border on .wrap itself,
   which would stretch it across the wrap's padding and overhang the footer
   columns above it by 28px on each side. */
.foot-legal::before {
  content: ""; position: absolute; top: 0; left: var(--pad); right: var(--pad);
  border-top: 1px solid var(--hair);
}
.foot-legal p { margin: 0; font-size: 13.5px; }

/* ── Sticky mobile dock ────────────────────────────────────── */
.dock {
  /* A floating glass capsule over the content, like a tab bar. Its blur is set
     with the topbar's. */
  position: fixed; left: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom));
  z-index: 50; display: none; gap: 8px; padding: 7px;
  background-color: var(--bar);
  border: 1px solid var(--bar-edge); border-radius: var(--r-pill);
  box-shadow: inset 0 1px 0 var(--bar-edge), var(--shadow-lg);
}
.dock .btn { flex: 1; min-height: 50px; }

/* ── Legal / document pages ────────────────────────────────── */
/* padding-top/padding-bottom longhands, and it is a bug fix rather than a style
   preference. Both of these classes land on an element that is ALSO .wrap, and
   .wrap's only job is `padding: 0 var(--pad)`. A `padding: 64px 0 8px` shorthand
   here is same-specificity and later in the file, so it silently reset the sides
   to zero and every legal page ran its body copy flush into the viewport edge at
   any width where .wrap's auto margins had nothing left to absorb - which is
   every phone. .nf on the 404 had it too. Longhands touch only the axis they
   mean, so the gutter survives. Never put a padding shorthand on a .wrap. */
/* Longhands only - see the note above .doc-head about padding shorthands on .wrap. */
.breadcrumb { padding-top: 22px; padding-bottom: 0; }
.breadcrumb ol {
  list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px;
  font-size: 13.5px; color: var(--muted);
}
.breadcrumb li { display: flex; align-items: center; gap: 6px; }
.breadcrumb li:not(:last-child)::after { content: "/"; color: var(--hair); }
.breadcrumb a { color: var(--muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--accent-lt); text-decoration: underline; text-underline-offset: 3px; }
.breadcrumb [aria-current="page"] { color: var(--text); }

.doc-head { padding-top: 64px; padding-bottom: 8px; }
.doc-head h1 { font-size: clamp(2.1rem, 4.6vw, 3.1rem); line-height: 1.04; }
.doc-meta { margin: 18px 0 0; font-size: 14.5px; color: var(--muted); }

.doc { padding-top: 28px; padding-bottom: 88px; }
.doc-in { max-width: 760px; }
.doc-in h2 {
  font-size: 1.4rem; line-height: 1.2; margin: 44px 0 0;
  scroll-margin-top: 100px;
}
.doc-in h2:first-of-type { margin-top: 24px; }
.doc-in h3 { font-size: 1.08rem; font-weight: 700; margin: 26px 0 0; }
.doc-in p { margin: 14px 0 0; }
.doc-in ul, .doc-in ol { margin: 14px 0 0; padding-left: 22px; }
.doc-in li { margin: 8px 0; }
.doc-in a { color: var(--accent-lt); text-decoration-thickness: 1px; text-underline-offset: 3px; }
.doc-in strong { color: var(--head); font-weight: 600; }
.doc-in dt { font-weight: 600; color: var(--head); margin-top: 16px; }
.doc-in dd { margin: 6px 0 0; }

/* Visible until Sam signs off on the drafted terms. Delete the markup when done. */
.notice {
  margin: 28px 0 0; padding: 18px 20px;
  border: 1px solid var(--edge); border-left: 3px solid var(--amber);
  border-radius: var(--r-md); background: var(--surface);
  font-size: 15.5px; color: var(--text);
}
.notice b { color: var(--head); }
.notice p { margin: 8px 0 0; }
.notice p:first-child { margin: 0; }
/* Child-of-.wrap, so this is the /pull-sheet notice only. The three legal-page
   notices sit inside a .doc-in that is already narrower than this cap, so they
   are untouched - which matters, because those pages were meant to stay put.
   Inside a full-width .wrap the paragraphs ran to 133 characters at 1280px,
   against 76 for every other body class on the page. Capping the box rather
   than the text on purpose: a 1124px box holding a 620px column of text leaves
   an empty right half that reads as a mistake, where a narrower box left-
   aligned under the app frame reads as an annotation on it. */
.wrap > .notice { max-width: 660px; }
/* The notice on /pull-sheet is not inside a .doc-in, so its links inherited
   nothing. Same declarations as .doc-in a above, so the legal pages, where
   both selectors match, do not change. */
.notice a { color: var(--accent-lt); text-decoration-thickness: 1px; text-underline-offset: 3px; }

.doc-nav { margin: 40px 0 0; padding-top: 26px; border-top: 1px solid var(--hair); }
.doc-nav p { margin: 0 0 12px; font-size: 12px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.doc-nav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px 10px; }
.doc-nav a {
  display: inline-flex; align-items: center; min-height: 44px; padding: 8px 15px;
  border: 1px solid var(--hair); border-radius: var(--r-pill);
  font-size: 14.5px; font-weight: 500; text-decoration: none; color: var(--text);
  background: linear-gradient(180deg, var(--ctl-top), var(--ctl-bot));
  box-shadow: inset 0 1px 0 var(--spec);
}
.doc-nav a:hover { border-color: var(--edge); color: var(--head); }
/* Flat, not a pill. The current page is marked by a solid accent fill, the
   same object as the CTA. */
.doc-nav a[aria-current="page"] {
  border-color: var(--accent-br); color: var(--on-accent);
  background: var(--accent);
}

/* ── 404 ───────────────────────────────────────────────────── */
.nf { padding-top: 88px; padding-bottom: 104px; }   /* also a .wrap - see .doc-head */
.nf h1 { font-size: clamp(2.2rem, 5vw, 3.4rem); line-height: 1.03; }
.nf > p { margin: 20px 0 0; max-width: 34em; font-size: 18px; }
.nf-links { font-size: 15.5px; color: var(--muted); }
.nf-links a { color: var(--accent-lt); text-underline-offset: 3px; }

/* Slim footer for document pages */
.foot-slim { border-top: 1px solid var(--hair); padding: 28px 0 34px; }
.foot-slim-in { display: flex; flex-wrap: wrap; gap: 10px 32px; justify-content: space-between; align-items: center; }
.foot-slim p { margin: 0; font-size: 13.5px; color: var(--muted); }
.foot-slim a { color: var(--text); text-underline-offset: 3px; }
.foot-slim a:hover { color: var(--accent-lt); }

/* ── Scroll reveal ─────────────────────────────────────────────
   Native scroll-driven animation. No JS. Browsers without support just render
   the content visible, which is the correct fallback. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .reveal {
      animation: reveal-in linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 55%;
    }
    @keyframes reveal-in {
      from { opacity: 0; transform: translateY(18px); }
      to   { opacity: 1; transform: none; }
    }
  }
}

/* ── Responsive ────────────────────────────────────────────── */
@media (max-width: 1000px) {
  .split { grid-template-columns: 1fr; gap: 40px; }
  .cards { grid-template-columns: 1fr; }
  .trust-in { grid-template-columns: 1fr; }
  .foot-in { grid-template-columns: 1fr 1fr; }
}

/* The nav has five links now, and it does not fit below this. Measured
   intrinsic widths: brand 236.9 + gap + five links 495.8 + gap + call button
   175.9. It used to hide at 860px, which was already 57px too late for four
   links - the header was quietly being squeezed between 860 and 917px. There is
   no hamburger; the Pull Sheet teaser on the home page is the mobile path to
   /pull-sheet, and every other link is a section of a page you are already on.
   Six links since /flipcheck: measured, the row stops wrapping at ~1088px, so it
   hides below 1120px for headroom. The FlipCheck band on the home page is the
   phone path there, same as Pull Sheet's. */
@media (max-width: 1119px) {
  .topnav { display: none; }
}

/* At 1024px the five-link row clears .wrap's 968px of content by about 3px,
   which is not enough to survive a font fallback or a zoom step. Tightening the
   two gaps buys ~28px and makes the fit real rather than lucky. */
/* Measured, not guessed. The five-link header wants 1017.1px of intrinsic
   width and .wrap gives 1009px at a 1024px viewport, so it was 8.1px short and
   the shortfall did not show up as an overflow - flex just squeezed the items
   and the text wrapped inside them, giving "WORLD / SUPPLY EXPRESS" and
   "Who we / supply" on two cramped lines. Tightening the two gaps buys ~28px
   and stepping the wordmark down buys ~26px more. The wordmark is the right
   thing to shrink because the wordmark is what grew: setting it uppercase with
   positive tracking widened it by 25.6px, which is what ate the headroom in
   the first place. */
@media (max-width: 1180px) {
  .topbar-in { gap: 20px; }
  .topnav { gap: 2px; }
  .brand { gap: 10px; }
  .brand-text { font-size: 14px; letter-spacing: .02em; }
  .brand-text em { letter-spacing: .11em; }
}

@media (max-width: 700px) {
  body { font-size: 16.5px; padding-bottom: calc(88px + env(safe-area-inset-bottom)); }
  .wrap { --pad: 20px; padding: 0 20px; }
  .dock { display: flex; }
  .topbar-in { min-height: 64px; gap: 14px; }
  /* Uppercase with positive tracking is wider than the mixed-case wordmark this
     replaced, so the narrow step drops further than it used to. */
  .brand-text { font-size: 14px; letter-spacing: .03em; }
  .brand-text em { display: none; }
  .topbar-call { font-size: 0; gap: 0; padding: 9px 13px; min-width: 46px; }
  .topbar-call svg { width: 19px; height: 19px; }
  .lede { font-size: 17.5px; }
  .cta-row .btn { flex: 1 1 100%; }
  .section { padding: 62px 0; }
  .card { padding: 26px 24px 24px; }
  .steps li { padding: 22px 0 22px 64px; }
  .steps li::before { font-size: 1.75rem; top: 22px; }
  .callout { padding: 28px 24px; }
  .close { padding: 20px 0 62px; }
  .close-in { padding: 40px 24px; }
  .ps-hero { padding: 48px 0 12px; }
  .ps-app { margin-top: 34px; }
  .ps-app-bar { padding: 13px 16px; }
  .ps-cap { padding: 15px 16px 13px; }
  .ps-table thead th,
  .ps-table tbody th,
  .ps-table tbody td { padding-left: 16px; padding-right: 16px; }
  .ps-app-foot { padding: 16px; }
  .ps-app-foot .btn { flex: 1 1 100%; }
  /* Two half-width fields do not fit a phone; one per row instead. */
  .wl-row { grid-template-columns: 1fr; }
  .nf { padding-top: 56px; padding-bottom: 72px; }
  .faq summary { padding: 18px 20px; }
  .faq p { margin: 0 20px 20px; }
  .foot-in { grid-template-columns: 1fr; gap: 30px; }
  .doc-head { padding-top: 44px; padding-bottom: 4px; }
}

/* ── FlipCheck page ─────────────────────────────────────────────
   /flipcheck, a product page for resellers. Same chrome as /pull-sheet; the
   only new colour is --profit, and it is only ever used on a profit figure so
   green keeps meaning "money you keep". Hue ~117, a money green, far from
   the blue accent so the two never read as the same signal.
   Dark #6CC46A: 8.4:1 on --surface, 9.3:1 on --base.
   Light #2A6B2A: 6.5:1 on --surface, 6.0:1 on --base, 5.5:1 on --base-2. Mobile first: base rules are
   the phone layout, min-width queries widen it. */
:root { --profit: #6CC46A; }

/* Phone mockup (hero). Decorative, aria-hidden in the markup. */
.fc-phone {
  width: 100%; max-width: 310px; margin: 0 auto; padding: 10px;
  background: var(--base-2); border: 1px solid var(--edge); border-radius: 38px;
  box-shadow: var(--shadow-lg);
}
.fc-screen {
  background: var(--surface); border: 1px solid var(--hair); border-radius: 29px;
  padding: 14px 18px 22px; overflow: hidden;
}
.fc-notch { width: 84px; height: 20px; margin: 0 auto 14px; border-radius: 10px; background: var(--base-2); }
.fc-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font: 700 11px/1.3 var(--f-text); letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.fc-tag {
  padding: 4px 9px; border: 1px solid var(--edge); border-radius: var(--r-pill);
  font-size: 10px; letter-spacing: .1em; color: var(--text);
}
.fc-item { display: flex; align-items: center; gap: 14px; margin: 18px 0 16px; }
.fc-thumb {
  flex: none; width: 58px; height: 58px; border-radius: var(--r-md);
  background: var(--plate); display: grid; place-items: center; color: var(--on-accent);
}
.fc-thumb svg { width: 34px; height: 34px; }
.fc-item b { display: block; font: 700 16px/1.25 var(--f-display); letter-spacing: -.015em; color: var(--head); }
.fc-item span { display: block; margin-top: 3px; font-size: 13px; color: var(--muted); }
.fc-rows { margin: 0; font-size: 14.5px; font-variant-numeric: tabular-nums; }
.fc-rows div {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 9px 0; border-top: 1px solid var(--hair);
}
.fc-rows dt { color: var(--muted); }
.fc-rows dd { margin: 0; font-weight: 600; color: var(--head); }
.fc-profit {
  margin: 14px 0 0; padding-top: 14px; border-top: 2px solid var(--head);
  font: 800 28px/1.1 var(--f-display); letter-spacing: -.03em; color: var(--profit);
  font-variant-numeric: tabular-nums;
}

/* How it works: stacked on phones, a row of three from 768px. */
.fc-steps {
  counter-reset: s; list-style: none; margin: 44px 0 0; padding: 0;
  display: grid; gap: 0;
}
.fc-steps li { counter-increment: s; padding: 26px 0; border-top: 1px solid var(--hair); }
.fc-steps li::before {
  content: counter(s, decimal-leading-zero); display: block; margin-bottom: 14px;
  font: 800 2rem/1 var(--f-display); letter-spacing: -.04em; color: var(--accent-lt);
}
.fc-steps h3 { font-size: 1.3rem; font-weight: 700; }
.fc-steps p { margin: 8px 0 0; color: var(--muted); }

/* Features: 1, then 2, then 4 across. */
.fc-grid { margin-top: 44px; display: grid; gap: 16px; }
.fc-card { padding: 26px 24px; }
.fc-card h3 { font-size: 1.2rem; font-weight: 700; line-height: 1.2; }
.fc-card p { margin: 10px 0 0; color: var(--muted); font-size: 16px; }

/* Pricing */
.fc-prices { margin-top: 44px; display: grid; gap: 16px; align-items: stretch; }
.fc-price { position: relative; display: flex; flex-direction: column; padding: 30px 26px 26px; }
.fc-price h3 { font-size: 1.15rem; font-weight: 700; }
.fc-amt { margin: 12px 0 0; font: 800 2.8rem/1 var(--f-display); letter-spacing: -.04em; color: var(--head); }
.fc-amt small { margin-left: 4px; font: 500 15px/1 var(--f-text); letter-spacing: 0; color: var(--muted); }
.fc-list { list-style: none; margin: 20px 0 26px; padding: 0; flex: 1; }
.fc-list li { position: relative; padding: 8px 0 8px 28px; font-size: 16px; border-top: 1px solid var(--hair); }
.fc-list li::before {
  content: ""; position: absolute; left: 3px; top: 15px; width: 11px; height: 6px;
  border-left: 2px solid var(--signal); border-bottom: 2px solid var(--signal); transform: rotate(-45deg);
}
.fc-price .btn { width: 100%; }
/* Pro: a 2px accent border plus the chip, so "most popular" is not colour alone. */
.fc-pop { border: 2px solid var(--accent-br); padding: 29px 25px 25px; }   /* -1px for the thicker border */
.fc-chip {
  position: absolute; top: -13px; left: 24px; padding: 5px 12px;
  background: var(--accent) linear-gradient(180deg, var(--accent), var(--accent-hi));
  color: var(--on-accent); border: 1px solid var(--accent-br);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.30);
  border-radius: var(--r-pill); font: 700 11.5px/1.2 var(--f-text); letter-spacing: .1em; text-transform: uppercase;
}

/* Waitlist. The function redirects to #joined or #signup-error, and :target
   is what shows them - no script needed, which the CSP would block anyway. */
.wl-f select {
  display: block; width: 100%; min-height: 48px; padding: 12px 40px 12px 14px;
  font: 400 16px/1.45 var(--f-text); color: var(--head);
  border: 1px solid var(--edge); border-radius: var(--r-sm);
  appearance: none; -webkit-appearance: none;
  background:
    linear-gradient(45deg, transparent 50%, var(--head) 50%) calc(100% - 21px) 50% / 6px 6px no-repeat,
    linear-gradient(135deg, var(--head) 50%, transparent 50%) calc(100% - 15px) 50% / 6px 6px no-repeat,
    var(--surface);
  transition: border-color .22s var(--ease);
}
.wl-f select:hover { border-color: var(--head); }
/* The open list is drawn by the OS; pin its colours so it matches the field. */
.wl-f option { color: var(--head); background: var(--surface); }
/* The home and Pull Sheet forms have a call row above them; this one does not,
   so it needs its own gap above the rule. Scoped by id so those two stay put. */
#waitlist .wl { margin-top: 30px; }
.fc-joined, .fc-err { display: none; }
.fc-joined:target, .fc-err:target { display: block; }
/* The redirect lands focus here so a screen reader announces it. It is not a
   control, so no focus ring - the border already marks it. */
.fc-joined:focus, .fc-err:focus { outline: none; box-shadow: none; }
.close-in:has(.fc-joined:target) .wl { display: none; }
.fc-joined {
  max-width: 480px; margin: 30px auto 0; padding: 26px 24px;
  border: 2px solid var(--profit); border-radius: var(--r-lg); background: var(--surface);
}
.fc-joined h3 { font-size: 1.4rem; color: var(--head); }
.fc-joined p { margin: 8px 0 0; color: var(--text); }
.fc-err {
  max-width: 580px; margin: 26px auto 0; padding: 14px 18px; text-align: left;
  border: 1px solid var(--edge); border-left: 3px solid var(--amber);
  border-radius: var(--r-md); background: var(--surface); color: var(--text); font-size: 15.5px;
}
.fc-err b { color: var(--head); }

@media (min-width: 640px) {
  .fc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 768px) {
  .fc-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 32px; }
  .fc-steps li { padding: 28px 0 0; border-top-width: 2px; }
}
@media (min-width: 900px) {
  .fc-prices { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
}
@media (min-width: 1024px) {
  .fc-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Reduce transparency: every glass material falls back to a solid one. */
@media (prefers-reduced-transparency: reduce) {
  .topbar, .dock { background-color: var(--base); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .btn-ghost, .topnav a[aria-current="page"], .doc-nav a { background: var(--surface); }
}

/* ── Home: space hero and FlipCheck showcase ─────────────────────
   Built from the owner's mock, samhomepage.png. Everything here is CSS over
   flat --base: the sky is layered gradients, the planet is one rimmed circle,
   and the phones are boxes. The only rasters are w-hero.png and one photo. */
.sky {
  position: relative; overflow: hidden; isolation: isolate;
  padding: 104px 0 118px;
  background:
    radial-gradient(1px 1px at 8% 18%, rgba(255,255,255,.5), transparent 60%),
    radial-gradient(1px 1px at 22% 72%, rgba(255,255,255,.35), transparent 60%),
    radial-gradient(1px 1px at 41% 12%, rgba(255,255,255,.4), transparent 60%),
    radial-gradient(1.5px 1.5px at 57% 64%, rgba(255,255,255,.45), transparent 60%),
    radial-gradient(1px 1px at 66% 8%, rgba(255,255,255,.35), transparent 60%),
    radial-gradient(1px 1px at 83% 26%, rgba(255,255,255,.5), transparent 60%),
    radial-gradient(1.5px 1.5px at 93% 58%, rgba(255,255,255,.4), transparent 60%),
    radial-gradient(1px 1px at 31% 44%, rgba(255,255,255,.3), transparent 60%),
    radial-gradient(ellipse 30% 44% at 74% 40%, rgba(72, 84, 251, .34), transparent 70%),
    linear-gradient(180deg, #05070F, #070A18 70%, #05070F);
}
/* Fades the planet into the flat page below, so the section has no bottom edge. */
.sky::after {
  content: ""; position: absolute; z-index: -1; left: 0; right: 0; bottom: 0; height: 180px;
  background: linear-gradient(transparent, var(--base)); pointer-events: none;
}
.sky-in {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  align-items: start;
}
.sky-kicker {
  margin: 0 0 24px;
  font: 600 12px/1.5 var(--f-text); letter-spacing: .2em; text-transform: uppercase;
  color: var(--accent-lt);
}
.sky h1 { font-size: clamp(2.7rem, 5vw, 4.4rem); line-height: 1; letter-spacing: -.035em; }
/* Two lines, as in the mock: each line stays whole and may run into the art column. */
@media (min-width: 1001px) { .sky h1 { white-space: nowrap; } }
/* color stays a real, measured value (--accent-lt, 8.0:1) for anything that
   ignores the fill; the gradient is painted through text-fill only. */
.sky-hl {
  color: var(--accent-lt);
  background: linear-gradient(90deg, #6E7CFF, #9AA8FF);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.sky-lede { max-width: 33em; margin: 26px 0 0; font-size: 18px; line-height: 1.6; color: var(--text); }
.sky .cta-row { margin: 36px 0 0; }
.sky-stats { display: flex; margin: 50px 0 0; }
.sky-stats div {
  display: flex; flex-direction: column-reverse;
  padding: 0 34px; border-left: 1px solid var(--glass-edge);
}
.sky-stats div:first-child { padding-left: 0; border-left: 0; }
.sky-stats dd { margin: 0; font: 800 30px/1.1 var(--f-display); letter-spacing: -.02em; color: var(--accent-lt); }
.sky-stats dt { margin-top: 6px; font-size: 14px; color: var(--text); }

/* Flex column so the tracked names, wider than the W, overflow evenly on both sides. */
.sky-art { position: relative; display: flex; flex-direction: column; align-items: center; margin-top: 30px; }
/* The planet. A circle far wider than the W, centred under it, so only a
   shallow arc of its top shows. Its top sits just under the W's feet; the
   mask fades the rim out toward the left, as in the mock. */
.sky-art::before {
  content: ""; position: absolute; z-index: -1;
  width: max(86vw, 900px); aspect-ratio: 1; border-radius: 50%;
  left: 50%; top: 165px; transform: translateX(-50%);
  background: radial-gradient(circle at 50% 0, #18226A, #0A0F2E 12%, #05070F 30%);
  box-shadow: 0 -2px 0 rgba(170, 185, 255, .95), 0 -6px 18px rgba(110, 125, 255, .6),
              0 -30px 80px rgba(72, 84, 251, .45), inset 0 12px 40px rgba(72, 84, 251, .35);
  -webkit-mask-image: linear-gradient(90deg, transparent 8%, #000 42%);
  mask-image: linear-gradient(90deg, transparent 8%, #000 42%);
}
.sky-w {
  display: block; width: min(100%, 340px); height: auto;
  filter: drop-shadow(0 18px 50px rgba(72, 84, 251, .45));
}
.sky-name, .sky-sub {
  margin: 0; font: 500 12.5px/1 var(--f-text); letter-spacing: .5em; text-transform: uppercase;
  color: var(--accent-lt); padding-left: .5em; white-space: nowrap;   /* padding balances the trailing tracking */
}
.sky-name { margin-top: 64px; }
.sky-sub { margin-top: 18px; }
.sky-rail {
  position: absolute; right: -56px; margin: 0;
  font: 600 10.5px/1 var(--f-text); letter-spacing: .24em; text-transform: uppercase; color: var(--muted);
  top: -60px; list-style: none; padding: 40px 0 70px 16px;
  border-left: 1px solid var(--glass-edge); display: grid; gap: 12px;
}

.pillars-sec { padding: 12px 0 92px; }
.pillars { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
.pillar {
  padding: 30px 28px 20px; border: 1px solid var(--glass-edge); border-radius: var(--r-md);
  background: linear-gradient(180deg, rgba(255,255,255,.035), rgba(255,255,255,.01)), var(--base);
  box-shadow: var(--glass-inset);
}
.pillar-ic, .show-ic {
  display: inline-grid; place-items: center; width: 46px; height: 46px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--glass-edge); color: var(--accent-lt);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06);
}
.pillar-ic svg, .show-ic svg { width: 22px; height: 22px; }
.pillar h3 { margin: 22px 0 10px; font-size: 1.2rem; font-weight: 700; letter-spacing: -.01em; }
.pillar p { margin: 0; color: var(--muted); font-size: 16px; line-height: 1.55; }
.pillar-link {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px; margin-top: 12px;
  font-weight: 600; font-size: 15.5px; color: var(--accent-lt); text-decoration: none;
}
.pillar-link:hover { text-decoration: underline; text-underline-offset: 4px; }

.show {
  position: relative; padding: 40px 0 96px; overflow: hidden;
  background: radial-gradient(ellipse 36% 52% at 26% 52%, rgba(72, 84, 251, .16), transparent 70%);
}
.show-in { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: 56px; align-items: center; }
.show-art { position: relative; height: 600px; perspective: 1600px; }
.ph {
  position: absolute; width: 280px; height: 560px; padding: 11px;
  border-radius: 46px; background: #0B0D16; border: 1px solid #2A2F45;
  box-shadow: inset 0 0 0 5px #141828, 0 50px 90px -30px rgba(0,0,0,.9);
}
.ph-scr {
  height: 100%; border-radius: 36px; overflow: hidden; padding: 16px 16px 18px;
  background: linear-gradient(180deg, #0F1222, #0A0C16);
}
.ph-front { left: 50%; top: 10px; transform: translateX(-50%) rotateY(14deg) rotateZ(-7deg); z-index: 2; }
.ph-back { filter: blur(1.5px); opacity: .5; }
.ph-back .ph-scr {
  background:
    linear-gradient(#262B3F, #262B3F) 20px 90px / 55% 10px no-repeat,
    linear-gradient(var(--plate), var(--plate)) 20px 130px / 80px 110px no-repeat,
    linear-gradient(#262B3F, #262B3F) 20px 270px / 70% 8px no-repeat,
    linear-gradient(#262B3F, #262B3F) 20px 296px / 50% 8px no-repeat,
    linear-gradient(#262B3F, #262B3F) 20px 322px / 60% 8px no-repeat,
    linear-gradient(180deg, #0F1222, #0A0C16);
}
.ph-l { left: -30px; top: 110px; transform: rotateZ(-10deg) scale(.86); }
.ph-r { right: -10px; top: 60px; transform: rotateZ(-6deg) scale(.86); }
.ph-notch { width: 92px; height: 24px; margin: 0 auto 16px; border-radius: 12px; background: #000; }
.ph-logo { display: block; width: 30px; height: 16px; }
.ph-title { margin: 8px 0 14px; font: 700 20px/1.1 var(--f-display); letter-spacing: -.02em; color: var(--head); }
.ph-search {
  display: flex; align-items: center; gap: 8px; height: 42px; padding: 0 6px 0 12px;
  border-radius: 12px; background: #151A2B; border: 1px solid #262C44;
  font-size: 12.5px; color: var(--muted);
}
.ph-search svg { width: 15px; height: 15px; flex: none; }
.ph-search .ph-scan {
  width: 30px; height: 30px; margin-left: auto; padding: 6px;
  border-radius: 9px; border: 1px solid #333A58; color: var(--head);
}
.ph-card { margin-top: 18px; padding: 14px; border-radius: 16px; background: #121628; border: 1px solid #232842; }
.ph-item { display: flex; align-items: center; gap: 12px; }
.ph-item img { width: 64px; height: 64px; flex: none; border-radius: 10px; object-fit: cover; }
.ph-item b { display: block; font: 700 14px/1.3 var(--f-text); color: var(--head); }
.ph-item span { display: block; margin-top: 4px; font-size: 12px; color: var(--muted); }
.ph-rows { margin: 14px 0 14px; }
.ph-rows div { display: flex; justify-content: space-between; align-items: baseline; padding: 7px 0; font-size: 12.5px; }
.ph-rows dt { color: var(--text); }
.ph-rows dd { margin: 0; font-weight: 600; color: var(--head); }
.ph-rows .ph-profit { font: 800 16px/1 var(--f-display); color: var(--profit); }
.ph-btn {
  display: block; padding: 10px; text-align: center; border-radius: var(--r-pill);
  border: 1px solid #3A4270; font-size: 12.5px; font-weight: 600; color: var(--head);
}

.show-copy .eyebrow { font-size: 12.5px; letter-spacing: .2em; }
.show-copy h2 { font-size: clamp(2.4rem, 4.4vw, 3.7rem); line-height: 1.02; letter-spacing: -.035em; }
.show-lede { max-width: 28em; margin: 22px 0 0; font-size: 18px; line-height: 1.6; color: var(--text); }
.show-copy .cta-row { margin: 32px 0 0; }
.show-feats { list-style: none; display: flex; flex-wrap: wrap; gap: 16px 0; margin: 40px 0 0; padding: 0; }
.show-feats li {
  display: flex; align-items: center; gap: 14px; padding: 0 26px;
  border-left: 1px solid var(--glass-edge); font-weight: 600; color: var(--head);
}
.show-feats li:first-child { padding-left: 0; border-left: 0; }
.show-ic { color: var(--head); }

@media (max-width: 1280px) { .sky-rail { display: none; } }
@media (max-width: 1000px) {
  .sky { padding: 64px 0 80px; }
  .sky-in { grid-template-columns: minmax(0, 1fr); }
  .sky-art { margin: 40px 0 0; }
  .sky-w { width: min(70%, 300px); }
  .sky-art::before { top: 150px; }
  .sky-name { margin-top: 34px; }
  .pillars { grid-template-columns: minmax(0, 1fr); }
  .show-in { grid-template-columns: minmax(0, 1fr); }
  .show-copy { order: -1; }
  .show-art { height: 560px; }
}
@media (max-width: 700px) {
  .sky-stats div { padding: 0 16px; }
  .sky-stats dd { font-size: 24px; }
  .sky-stats dt { font-size: 12.5px; }
  .sky-name, .sky-sub { font-size: 11px; letter-spacing: .36em; }
  .ph-back { display: none; }
  .show-art { height: 540px; }
  .show-feats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .show-feats li { flex-direction: column; padding: 0; gap: 10px; }
}
