/* ---------------------------------------------------------------------------
 * Stitch marketing site — stylesheet.
 *
 * Every colour, radius, duration and spacing value here is a var() from
 * tokens.css, which is generated from the design-system export
 * (scripts/build-marketing-tokens.mjs). The only literals in this file are the
 * three marketing display rungs below and geometry that is not a token.
 *
 * No build step, no framework, no JavaScript. Loaded from the document with a
 * plain <link>, so the CSP can be style-src 'self' with no 'unsafe-inline'.
 *
 * Three regions, owned by three slices of the redesign. Do not write outside
 * your own:
 *   === shell ===           slice 1  (this file's foundations, header, footer)
 *   === hero + cut strip == slice 2
 *   === body sections ===   slice 3
 * ------------------------------------------------------------------------- */

/* === shell =============================================================== */

/* --- Fonts -------------------------------------------------------------
 * Five faces, and exactly five: Switzer 600, Inter 400/500, JetBrains Mono
 * 400/500 (spec 4.3). All self-hosted from this origin, so font-src is 'self'
 * and the page makes no third-party request at all.
 *
 * Switzer Medium (500) and Bold (700) remain on disk under fonts/ for other
 * surfaces but are deliberately NOT declared here: nothing on this page sets
 * them, and an undeclared face cannot be accidentally fetched.
 *
 * Switzer — Fontshare licence, which permits self-hosting.
 * Inter, JetBrains Mono — SIL Open Font License 1.1; the licence text ships
 * beside the files at fonts/Inter-OFL.txt and fonts/JetBrainsMono-OFL.txt.
 */
@font-face {
  font-family: "Switzer";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/Switzer-Semibold.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/Inter-Regular.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;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/Inter-Medium.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;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/JetBrainsMono-Regular.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;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/JetBrainsMono-Medium.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;
}

/* --- The marketing-only scale extension (spec 2.1) ----------------------
 * The DS type scale is the app's scale and tops out at 30px. The marketing
 * h1 needs more. Exactly three rungs, all on the 8px grid, named so they
 * obviously are not app tokens and cannot leak into web/.
 */
:root {
  --text-display-1: 3rem;      /* 48px — hero h1 at >=1024 */
  --text-display-1-md: 2.5rem; /* 40px — hero h1, 640–1023 */
  --text-display-2: 2rem;      /* 32px — section h2 at >=1024, hero h1 at <=639 */

  /* The hero grid and the peek escape the 1024px reading cap (spec 2). */
  --page-max-wide: 80rem;      /* 1280 */

  --gutter: 1.5rem;            /* 24px; 20px at <=640 */
}

/* --- Reset and base ---------------------------------------------------- */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: hsl(var(--st-ink-950));
  color: hsl(var(--st-paper));
  font-family: var(--font-sans);
  font-feature-settings: var(--font-feature-ui);
  font-size: var(--text-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  margin: 0;
  line-height: 1.1;
}

p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }

a { color: inherit; text-decoration: none; transition: var(--transition-colors); }

:focus-visible {
  outline: 2px solid hsl(var(--ring));
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-xs);
}

/* --- The shared image rule (spec 2.3) ----------------------------------
 * ONE implementation for every content image on the site: a <picture> with
 * AVIF/WebP/JPEG sources and an <img> that fills its box, cropped like CSS
 * `background-size: cover` with a per-image focal point in --pos.
 *
 *   <div class="media" style="--pos: 30% 20%">   <!-- or a class -->
 *     <picture>
 *       <source type="image/avif" srcset="/img/x.avif">
 *       <source type="image/webp" srcset="/img/x.webp">
 *       <img src="/img/x.jpg" width="264" height="500" alt="" decoding="async">
 *     </picture>
 *   </div>
 *
 * Lives in the shell, not in a slice block, because BOTH the cut strip
 * (slice 2) and the peek (slice 3) use it and neither may reach into the
 * other's region. Each slice supplies only its own --pos values and sources.
 * CSS background-image is not used for content imagery anywhere.
 */
.media { position: relative; overflow: hidden; }
.media picture { display: contents; }
.media img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 30%);
}

/* --- Motifs ------------------------------------------------------------ */

/* The seam: Stitch's one signature graphic. Dashed Thread, 4 on / 3 off, 2px.
   It always marks a seam between two things — never decorative filler. */
.seam {
  height: 2px;
  background-image: repeating-linear-gradient(
    90deg,
    hsl(var(--st-thread-500)) 0 4px,
    transparent 4px 7px
  );
}

/* The tick texture: faint 1px rules every 24px. */
.ticks::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    90deg,
    hsl(var(--st-ink-700) / 0.22) 0 1px,
    transparent 1px 24px
  );
  -webkit-mask-image: linear-gradient(to bottom, hsl(0 0% 0% / 0.8), transparent 70%);
  mask-image: linear-gradient(to bottom, hsl(0 0% 0% / 0.8), transparent 70%);
  pointer-events: none;
}

.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

/* Touch hit-slop, verbatim from the DS (tokens/controls.css). Expands the real
   tap region of a visually-small control on a coarse pointer without changing
   how big it looks on a mouse. The 36px header CTA carries it. */

/* Moved here 2026-09-03 (integration) from the three-rung :root above, which
   is the marketing-only TYPE SCALE extension (spec 2.1) and had no business
   holding a hit-area inset. It lives with the two blocks that are its only
   readers instead. Same value, same cascade: the two coarse-pointer blocks
   below are the only things that read it, and a custom property on :root is
   in scope for the whole document however far down it is declared. */
:root { --slop: calc(-1 * var(--hitslop, 12px)); }

.st-hitslop { position: relative; }
@media (pointer: coarse) {
  .st-hitslop::after { content: ""; position: absolute; inset: calc(-1 * var(--hitslop, 12px)); }
}

/* Ruling 2026-09-02: every control gets a >=44px hit area on a COARSE pointer
   at ANY width. The charter's floor said "<=640", so a tablet at 641-1024 got
   21px nav and footer links and 40px hero CTAs.

   Slop, never size -- ::before overlays that paint nothing and take no space,
   so only the hit box moves, for either pointer type. ::before, originally
   because .nav-link.is-current's seam owned ::after (that seam went in I6),
   and still ::before so .st-hitslop keeps ::after. Each axis grows only where
   it is needed and the gap can absorb 12px, so no two hit areas overlap
   (working in the slice 4 PR). Two blocks, because <=640 already has a floor
   from the width rules, and stacking a second on the phone footer's
   zero-row-gap column would make each link eat its neighbour's taps -- the
   same reason .st-hitslop is off there. Verified 390/700/820/1024. */

/* Footer links are the one control too NARROW as well as too short ("Terms",
   41.25px), on a phone too, where the height is already fixed -- so the
   horizontal half runs at every coarse width, the vertical half at 641+. */
@media (pointer: coarse) {
  .footer-links a { position: relative; }
  .footer-links a::before { content: ""; position: absolute; inset-block: 0; inset-inline: var(--slop); }
}

@media (pointer: coarse) and (min-width: 641px) {
  .nav-link, .header-signin, .hero-cta .btn { position: relative; }
  .nav-link::before, .header-signin::before, .hero-cta .btn::before { content: ""; position: absolute; inset-block: var(--slop); inset-inline: 0; }
  .footer-links a::before { inset-block: var(--slop); }
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.625rem 1rem;
  background: hsl(var(--st-thread-500));
  color: hsl(var(--st-ink-950));
  font-weight: var(--weight-medium);
  border-radius: 0 0 var(--radius-md) 0;
}
.skip-link:focus { left: 0; }

/* #main has tabindex="-1" so the skip link moves focus (index.html); Chrome
   then :focus-visible-rings the whole page. #main is never Tab-reachable, so
   nothing reachable loses its ring. */
#main:focus { outline: none; }

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

.wrap {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* The hero grid and the peek only. Everything else caps at the reading width. */
.wrap-wide { max-width: var(--page-max-wide); }

section { position: relative; }

.section-pad { padding-block: 6rem; }

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: hsl(var(--st-ink-400));
}

/* 16px, not 18px: spec 2.1 puts "Lede / hero sub" at --text-base at every
   breakpoint, and the hero sub already was. */
.lede {
  color: hsl(var(--st-ink-200));
  font-size: var(--text-base);
  line-height: 1.5;
}

.muted { color: hsl(var(--st-ink-400)); }

.divider { height: 1px; background: hsl(var(--st-ink-700)); }

/* --- Buttons ----------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: var(--control-lg);
  padding-inline: var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  /* 500, not 600: Inter 600 is not one of the five declared faces, so asking
     for it renders synthetically emboldened Inter 500. The mock sets buttons
     at weight 500 anyway (use-website.html:20). */
  font-weight: var(--weight-medium);
  white-space: nowrap;
  cursor: pointer;
  transition: var(--transition-colors);
}

.btn-primary {
  background: hsl(var(--st-thread-500));
  color: hsl(var(--st-ink-950));
}
.btn-primary:hover { background: hsl(var(--st-thread-300)); }

.btn-ghost {
  background: transparent;
  border-color: hsl(var(--st-ink-700));
  color: hsl(var(--st-paper));
}
.btn-ghost:hover { background: hsl(var(--st-ink-850)); border-color: hsl(var(--st-ink-600)); }

/* 36px / 14px — the DS's --control-sm. Under the DS's 40px tappable floor, so
   anything wearing it also wears .st-hitslop (spec 2.9 #12). */
.btn-sm { height: var(--control-sm); padding-inline: 0.875rem; font-size: var(--text-sm); }

.link-quiet { color: hsl(var(--st-ink-400)); font-size: var(--text-sm); }
.link-quiet:hover { color: hsl(var(--st-paper)); }

/* The header's Sign in sits beside the nav links and matches them at ink-200
   (spec 3.1, mock:73). ink-400 is the muted floor and is right for the FOOTER
   links (spec 3.2), not here. */
.header-signin { color: hsl(var(--st-ink-200)); }

/* Spool is information, never an action. */

/* --- The lockup --------------------------------------------------------
 * Rendered from vector, never typed. Clip-path ids are namespaced per
 * instance (hdr-* / ftr-*) because SVG ids are document-global. The two
 * halves must never be independently recolourable, so the geometry is locked
 * and only the two currentColor layers are set here. No seam under the
 * wordmark — the logotype carries its own cut.
 */
.lockup { display: inline-flex; align-items: center; }
.lockup svg { flex: none; }
.lockup .mark { color: hsl(var(--st-thread-500)); }
.lockup .word { color: hsl(var(--st-paper)); }

.lockup-header { gap: 11px; }
.lockup-footer { gap: 9.9px; }

/* --- Header ------------------------------------------------------------
 * The only fixed element on the page. Its 10px backdrop blur is a documented
 * marketing-site exception to the DS's "blur in exactly two places" rule
 * (spec 3.1): without it the nav is unreadable over the hero glow.
 */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  min-height: var(--header-height);
  display: flex;
  align-items: center;
  background: hsl(var(--st-ink-950) / 0.85);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid hsl(var(--st-ink-700));
}

/* No blur (older Safari, Firefox with the pref off): 85% ink-950 lets the hero
   glow read through and the nav loses contrast, so the bar goes opaque. Both
   spellings are tested — Safari shipped -webkit- years before the plain one. */
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .site-header { background: hsl(var(--st-ink-950)); }
}

.header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: var(--header-height);
}

/* The lockup is a link home, so the 44px floor applies. Flexing the anchor
   blockifies it out of its line box, so the lockup MOVES down 3.25px at every
   width (top 12.95 -> 16.20 at 1280) -- kept, because it lands on the header's
   optical centre rather than 3.75px above it. min-height, not a slop: the
   anchor has room to grow and no neighbour to overlap. */
.header-home { display: flex; align-items: center; }
@media (max-width: 640px), (pointer: coarse) {
  .header-home { min-height: var(--control-lg); }
}

.header-nav {
  display: flex;
  align-items: center;
  gap: 1.75rem;
  font-size: var(--text-sm);
}

.header-links { display: flex; align-items: center; gap: 1.75rem; }

.nav-link { color: hsl(var(--st-ink-200)); }
.nav-link:hover { color: hsl(var(--st-paper)); }

/* No active-item seam. The mock's (use-website.html:17) shipped static, which
   is wrong for most of the scroll; dropped in I6, and no aria-current either.
   Restoring it means restoring the scroll-spy with it. DECISIONS.md. */

.header-actions { display: flex; align-items: center; gap: var(--space-3); }

/* --- Mobile drawer (<=640) ---------------------------------------------
 * CSS only: a <details>/<summary>, which is keyboard- and screen-reader-
 * correct for free and needs no ARIA bookkeeping. Hidden above 640.
 *
 * Two accepted consequences of having no script (spec 3.1):
 *  - tapping a link cannot close it, so it stays open; the panel is positioned
 *    so the anchor target is still reached.
 *  - <details> cannot transition its open height without script, so the open
 *    state animates opacity and a small rise over --duration-slow instead.
 * It is a nav, not a modal: it does not trap scroll or lock the body.
 */
.drawer { display: none; }

.drawer-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-lg);
  height: var(--control-lg);
  border-radius: var(--radius-md);
  color: hsl(var(--st-ink-200));
  cursor: pointer;
  list-style: none;
}
.drawer-toggle::-webkit-details-marker { display: none; }
.drawer-toggle:hover { color: hsl(var(--st-paper)); }
.drawer-toggle svg { width: 20px; height: 20px; }

.drawer-toggle .icon-close,
.drawer[open] .drawer-toggle .icon-open { display: none; }
.drawer[open] .drawer-toggle .icon-close { display: block; }

.drawer-panel {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background: hsl(var(--st-ink-900));
  border-bottom: 1px solid hsl(var(--st-ink-700));
  box-shadow: var(--shadow-lg);
}

.drawer[open] .drawer-panel {
  animation: drawer-open var(--duration-slow) var(--ease-out);
}

@keyframes drawer-open {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}

.drawer-panel a {
  display: flex;
  align-items: center;
  min-height: var(--control-lg);
  padding-inline: 1.25rem;
  font-size: var(--text-sm);
  color: hsl(var(--st-ink-200));
  border-top: 1px solid hsl(var(--st-ink-700));
}
.drawer-panel a:first-child { border-top: 0; }
.drawer-panel a:hover { color: hsl(var(--st-paper)); }

@media (prefers-reduced-motion: reduce) {
  .drawer[open] .drawer-panel { animation: none; }
}

/* --- Footer ------------------------------------------------------------ */

.site-footer {
  border-top: 1px solid hsl(var(--st-ink-700));
  padding-block: 3rem;
}
.footer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-6);
}
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  font-size: var(--text-sm);
}
/* ink-600 is reserved for borders and dividers and is NEVER used for text:
   #544E44 on the ink-950 canvas is 2.4:1 and fails AA (spec 4.8). ink-400 is
   the muted floor at 5.8:1. */
.footer-legal {
  margin-top: 1.75rem;
  font-size: var(--text-xs);
  color: hsl(var(--st-ink-400));
}

/* --- Shell responsive --------------------------------------------------- */

@media (max-width: 780px) {
  .section-pad { padding-block: 4.5rem; }
}

@media (max-width: 640px) {
  :root { --gutter: 1.25rem; }  /* 20px, the charter's mobile gutter */

  .section-pad { padding-block: 4rem; }

  /* Links move into the drawer; Start a project stays in the bar, always
     visible, and grows to the 44px touch floor outright. */
  .header-links,
  .header-signin { display: none; }
  .drawer { display: block; }
  .header-nav { gap: var(--space-2); }
  .header-actions { gap: var(--space-2); }
  .btn-sm { height: var(--control-lg); }
  /* The CTA measures 44px here, so it needs no hit-slop — and the slop's
     ::after is positioned, so it would paint over the drawer toggle's left
     edge and eat taps on it. Spec 2.9 #12 only asks for it at 36px. */
  .st-hitslop::after { content: none; }

  .footer-row { flex-direction: column; align-items: flex-start; }
  .footer-links { gap: 0 var(--space-6); }
  .footer-links a { display: flex; align-items: center; min-height: var(--control-lg); }
}

/* === hero + cut strip ====================================================
 * SLICE 2 OWNS THIS BLOCK. Nothing outside it may be edited from slice 2, and
 * nothing inside it may be edited from slice 1 or 3.
 *
 * Spec 2.2 (hero layout, copy, CTAs, metrics row) and spec 2.3 (the cut strip:
 * geometry, the kerf, the seam, four keyframes, the reduced-motion resting
 * state, the 200px mobile variant). The live hero's pill (P15), foot (P16),
 * schematic strip (P14), tick texture and clamp()ed h1 are all retired here.
 */

.hero {
  /* The page's one and only background treatment: a radial Thread glow from
     the top edge at 14% (spec 2.2, use-website.html:13). The DS's own
     --glow-thread is the same idea drawn as a circle; the mock's ellipse is
     what ships, so it is written out rather than taken from the token. */
  background-image: radial-gradient(
    60% 50% at 50% -10%,
    hsl(var(--st-thread-500) / 0.14),
    transparent 70%
  );
  /* 40px top under the 56px sticky header; 0 bottom — the peek is what closes
     the hero (spec 2.2, departure 14). */
  padding-block: 2.5rem 0;
  /* The seam overshoots the strip by 4% at each end; keep that off the page's
     horizontal scroll. */
  overflow: hidden;
}

/* Two columns, vertically centred (mock:24). 2026-09-05, hero-alignment:
   the hero left the .wrap-wide cap (Jack: same width as the rest of the
   content), so the two-column split now happens inside the 1024px wrap.
   The columns were 1.05fr/1fr, which pinned the copy at ~509px and
   wrapped .proof's metrics row (it needs 583px; DECISIONS.md I6 rules the
   row one line and the words exact) back onto two lines. 1.5fr/1fr gives
   the copy 585.6px in the 976px content box -- clears 583 with the
   strip still wide enough to read. */
.hero-grid {
  display: grid;
  grid-template-columns: 1.5fr 1fr;
  gap: var(--space-10);
  align-items: center;
}

.hero h1 {
  font-size: var(--text-display-1);
  line-height: 1.02;
  letter-spacing: -0.025em;
  max-width: 11ch;
  text-wrap: balance;
  margin: 0;
}
/* "the cut." — the accent, reset out of italic exactly as the mock does. */
.hero h1 em {
  font-style: normal;
  color: hsl(var(--st-thread-500));
}

.hero-sub {
  /* 16px, not the mock's 15px: 15px is not a DS rung (spec 2.9 #2).
     The 16px above is the mock's own gap (mock:31, h1{margin:0 0 16px}). The
     spec's "22px below the h1" was the sub's own bottom margin transcribed one
     gap up; 2.2 is corrected to match this. */
  margin: 1rem 0 0;
  font-size: var(--text-base);
  line-height: 1.5;
  color: hsl(var(--st-ink-400));
  max-width: 42ch;
}

.hero-cta {
  display: flex;
  align-items: center;
  gap: var(--space-2_5);
  margin-top: 1.375rem;
  white-space: nowrap;
}
/* 40px tall, 18px side padding (mock:20). The shell's .btn is the DS's 44px
   touch rung; the hero's pair sit at the DS's 40px control height. */
.hero-cta .btn {
  height: var(--control-md);
  padding-inline: 1.125rem;
}

.hero-hint {
  margin-left: var(--space-2);
  font-size: var(--text-xs);
  /* ink-400, not the mock's ink-600: ink-600 is 2.4:1 on this canvas and fails
     AA at 12px. ink-600 is borders and dividers only (spec 4.8, 2.9 #4). */
  color: hsl(var(--st-ink-400));
}

/* The metrics row. Geometry and typography are the mock's; the numbers are
   measured (spec 2.2, D3). Labels ink-400 with figures ink-200 keeps the
   mock's "figures lighter than their labels" relationship after both were
   lifted off ink-600. */
.proof {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.625rem;
  margin-top: 1.375rem;
  font-size: var(--text-2xs);
  color: hsl(var(--st-ink-400));
}
.proof b {
  color: hsl(var(--st-ink-200));
  font-weight: var(--weight-medium);
}

/* --- The cut strip (spec 2.3) ------------------------------------------
 * One strip of footage, cut once. .halves is the coordinate space; everything
 * inside it is absolute. The two .half elements are 125px windows onto the
 * SAME 250px reel — .a shows its top, .b its bottom — which is what makes this
 * a cut and not two rows of thumbnails.
 */
.strip {
  position: relative;
  height: 300px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.halves { position: relative; height: 250px; }

.half {
  position: absolute;
  left: 0;
  right: 0;
  height: 125px;
  overflow: hidden;
}
/* The kerf: two parallel edges 4px apart, the cut rising left-to-right.
 *
 * Safari, by analysis only -- no WebKit runs on the build machine. Unprefixed
 * clip-path on HTML shipped in Safari 13.1 / iOS 13.4, so no -webkit- twin.
 * Of WebKit's two clip-path traps one is avoided and ONE IS PRESENT:
 *   avoided -- a clipPath in an EXTERNAL SVG. The lockup's are inline and
 *     same-document (index.html:83, :504); calc() in polygon() is an ordinary
 *     <length-percentage> and is no trap at all.
 *   PRESENT -- clipping an element that is ALSO a scroll container: .half is
 *     overflow: hidden above, with a 250px .reel in a 125px box, and carries
 *     the clip-path below. That is the shape WebKit renders inconsistently.
 *     Correct in Chrome.
 * SO BEFORE CUTOVER, OPEN THIS PAGE IN SAFARI AND LOOK AT THE STRIP: both
 * halves showing footage, the 4px kerf straight, the lower half offset right.
 * If wrong, move overflow: hidden to an inner wrapper so the clipped element
 * is not the scroller. */
.half.a {
  top: 0;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 4px), 0 100%);
}
.half.b {
  top: 125px;
  clip-path: polygon(0 4px, 100% 0, 100% 100%, 0 100%);
  animation: cut-step 6s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}

.reel {
  position: absolute;
  left: 0;
  right: 0;
  height: 250px;
  display: flex;
  gap: var(--space-1_5);
  padding-inline: var(--space-1_5);
}
.half.a .reel { top: 0; }
.half.b .reel { top: -125px; }

/* Each frame renders ~132x250 at the 1280px cap — portrait, not landscape.
   That is what sets the 264x500 export (spec 2.3). */
.frame {
  flex: 1;
  min-width: 0;
  border-radius: var(--radius-sm);
  background: hsl(var(--st-ink-850));
  border: 1px solid hsl(var(--st-ink-700) / 0.6);
}
/* Keeps the timecode legible over any frame. It carries a z-index because the
   <img> is a positioned child: without one the gradient would paint under the
   image, where the mock's background-image had it painting over. */
.frame::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top, hsl(var(--st-ink-950) / 0.7), transparent 45%);
}
/* The one named off-scale size on the site (spec 2.1): 10px, here and nowhere
   else. A diegetic detail of the strip, inside an aria-hidden subtree. */
.frame::after {
  content: attr(data-tc);
  position: absolute;
  z-index: 2;
  left: var(--space-2);
  bottom: var(--space-1_5);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  letter-spacing: 0.02em;
  color: hsl(var(--st-ink-400));
}

/* The four focal points, re-derived for these compositions (D6). Each export
   is already cut to its own 264x500 window, so --pos re-crops only where the
   rendered box is not 264:500: narrower at <=640 (80x168), where the X value
   holds the subject, and WIDER between 641 and 1023, where the strip spans the
   full column and the Y value decides what is trimmed off the top and bottom.
   Both are checked at 390, 820 and 1440.
   f1 the subject, f2 the hands on the wheel, f3 the subject's hands at the
   table, f4 the wheel and the window. Interview, b-roll, interview, b-roll,
   as the mock has it. */
.frame.f1 { --pos: 48% 50%; }
.frame.f2 { --pos: 55% 50%; }
.frame.f3 { --pos: 68% 50%; }
.frame.f4 { --pos: 42% 50%; }

/* The seam. .seam (the shell motif) supplies the 2px dashed Thread; this adds
   only the overshoot, the position and the rotation. -1.2deg is the mock's
   drawn value, not the logotype's 2.5deg: the strip is far wider than it is
   tall, and a literal 2.5deg would drop the seam ~20px and eat the frames. If
   the strip is ever re-proportioned, hold the 4px kerf and re-derive this. */
.cut-seam {
  position: absolute;
  left: -4%;
  right: -4%;
  top: 123px;
  transform: rotate(-1.2deg);
  transform-origin: 50% 50%;
  animation: cut-seam 6s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}

.blade {
  position: absolute;
  top: 104px;
  width: 2px;
  height: 40px;
  border-radius: 1px;
  background: hsl(var(--st-thread-500));
  box-shadow: 0 0 18px 2px hsl(var(--st-thread-500) / 0.6);
  animation: cut-blade 6s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}

/* Thread-filled, and the only Thread fill on the page that is not a control.
   A stated exception (spec 2.2): outlined, it reads as a chip you can click. */
.cut-tag {
  position: absolute;
  right: 0;
  top: -22px;
  z-index: 3;
  padding: 3px var(--space-2);
  border-radius: var(--radius-full);
  background: hsl(var(--st-thread-500));
  color: hsl(var(--st-ink-950));
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  opacity: 0;
  animation: cut-tag 6s ease infinite;
}

.cut-keep {
  position: absolute;
  right: var(--space-1_5);
  bottom: -26px;
  display: flex;
  align-items: center;
  gap: var(--space-1_5);
  font-size: var(--text-2xs);
  color: hsl(var(--st-ink-400));
}
/* Spool is information, never an action. */
.cut-keep b {
  color: hsl(var(--st-spool-500));
  font-weight: var(--weight-medium);
}

/* The four keyframes, one 6s cycle. These sit outside the DS motion rungs
   (--duration-slow is 200ms) on purpose: this is the marketing-only exception
   the spec documents, recorded here so it is not "corrected" into a 200ms
   ease-out later. Any fifth @keyframes block in this stylesheet, beyond these
   and the drawer's transition, is a defect (spec 2.3). */
@keyframes cut-blade {
  0%, 8%    { left: -2%;  opacity: 0; }
  12%       { opacity: 1; }
  42%       { left: 100%; opacity: 1; }
  46%, 100% { left: 100%; opacity: 0; }
}
@keyframes cut-seam {
  0%, 10%   { clip-path: inset(0 100% 0 0); }
  42%       { clip-path: inset(0); }
  90%, 100% { clip-path: inset(0); }
}
@keyframes cut-step {
  0%, 44%   { transform: translateX(0); }
  56%, 86%  { transform: translateX(3.5%); }
  96%, 100% { transform: translateX(0); }
}
@keyframes cut-tag {
  0%, 50%   { opacity: 0; transform: translateY(4px); }
  58%, 84%  { opacity: 1; transform: translateY(0); }
  92%, 100% { opacity: 0; }
}

/* Reduced motion: not "no animation" but THE CUT ALREADY MADE — seam fully
   drawn, lower half stepped, tag visible, blade gone. The cut is the message;
   a static cut still carries it, a static uncut strip does not. This is the
   acceptance criterion for the section (spec 2.3, 4.8), not a nicety. */
@media (prefers-reduced-motion: reduce) {
  .half.b, .cut-seam, .blade, .cut-tag { animation: none; }
  .half.b { transform: translateX(3.5%); }
  .cut-seam { clip-path: inset(0); }
  .cut-tag { opacity: 1; transform: none; }
  .blade { display: none; }
}

/* One column to 1279, not 1023: the metrics row needs 583px and two-column
   .hero-copy is only 509px at 1024. Columns only -- the h1 keeps its 48px rung
   to 1024 (spec 2.1). DECISIONS.md, I6. */
@media (max-width: 1279px) {
  .hero-grid { grid-template-columns: 1fr; }
}

/* --- Hero, 640-1023 (the h1 rung; the gap runs on down through <=640) -- */
@media (max-width: 1023px) {
  .hero-grid { gap: var(--space-8); }
  /* Source order is already headline -> sub -> CTAs -> metrics -> strip: on a
     phone the words land first (spec 2.2). */
  .hero h1 { font-size: var(--text-display-1-md); line-height: 1.05; }
}

/* --- Hero and the 200px strip: <=640 layout, <=639 type ---------------- */
/* TWO MOBILE BOUNDARIES, both the spec's own: TYPE steps at <=639 (spec 2.1),
   LAYOUT and chrome at <=640 (spec 2, 2.2, 2.6, 2.7, 3.1). Every <=640 block
   is layout, every <=639 block is type. Why: DECISIONS.md, I6. */

@media (max-width: 640px) {   /* layout */
  .hero { padding-block: 2.5rem 2rem; }

  /* Both buttons full width and stacked at the charter's 50px mobile CTA
     height; the hint moves onto its own line beneath. */
  .hero-cta {
    flex-direction: column;
    align-items: stretch;
    white-space: normal;
  }
  .hero-cta .btn { width: 100%; height: 50px; }
  .hero-hint { margin-left: 0; }

  .proof { gap: var(--space-3) 1.625rem; }

  /* 200px. Four frames are kept: at 390 each is ~80px wide, comfortably over
     the 64px floor below which a timecode stops being readable. The kerf stays
     4px and the step stays 3.5%. */
  .strip { height: 200px; }
  .halves { height: 168px; }
  .half { height: 84px; }
  .half.b { top: 84px; }
  .reel { height: 168px; }
  .half.b .reel { top: -84px; }
  .cut-seam { top: 82px; }
  .blade { top: 69px; height: 30px; }
  /* The keep line moves below the strip and left-aligns. */
  .cut-keep { left: 0; right: auto; bottom: -24px; }
}

@media (max-width: 639px) {   /* type */
  .hero h1 { font-size: var(--text-display-2); line-height: 1.08; max-width: 14ch; }
}

/* === body sections =======================================================
 * SLICE 3 OWNS THIS BLOCK. Nothing outside it may be edited from slice 3, and
 * nothing inside it may be edited from slice 1 or 2.
 *
 * The peek (spec 2.5), How it cuts and Product (spec 2.4), the principles
 * (spec 2.6), Pricing (spec 2.7) and the closing CTA (spec 2.8).
 *
 * Two rules run through all of it:
 *   - Type is DS rungs only, stepped by breakpoint. No clamp() on a vw unit:
 *     a clamp renders arbitrary sizes, and every rendered size here has to be
 *     a token value (spec 2.1).
 *   - ink-600 is borders and dividers, never text (spec 4.8). Card body copy
 *     never goes below 14px and never below ink-400: ink-400 on the ink-850
 *     card is 4.9:1, the site's tightest passing surface.
 */

/* --- Section heads ------------------------------------------------------ */

.section-head { max-width: 38rem; }
.section-head h2 {
  margin-top: 0.875rem;
  font-size: var(--text-display-2);   /* 32px */
  line-height: 1.1;
}
.section-head p { margin-top: var(--space-4); }

/* --- The peek (spec 2.5) ------------------------------------------------
 * The product appears once, under a seam, as itself. The band is 48px inside
 * the page gutters and capped at 864px — the native width of the design
 * system's cutting-room export. The spec's 1280px cap waits on a >=2560px
 * re-export from the rebuilt app; until then, capping beats upscaling.
 */

#peek { padding-top: var(--space-6); }

.peek-inset { padding-inline: 3rem; }   /* 48px inside the page gutters */

.peek {
  max-width: 864px;
  margin-inline: auto;
  height: 110px;
  border: 1px solid hsl(var(--st-ink-700) / 0.7);
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  background: hsl(var(--st-ink-950));
  /* The only glow on the page besides the hero's is this upward one, at 6%. */
  box-shadow: 0 -20px 60px hsl(var(--st-thread-500) / 0.06);
}

/* The shared .media rule crops with object-fit: cover, which would scale the
   screenshot down to fit a narrow band. The peek must not scale: it is a peek
   at a dense UI at its true size, cropping at the right edge on a phone
   (spec 2.5). object-fit: none draws it at intrinsic size, anchored top-left. */
.peek img { object-fit: none; object-position: left top; }

/* The seam the product appears under. Sits above the fade. */
.peek .seam-top {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: 1;
}

/* Fades the lower half of the frame into the canvas, so only the top ~110px
   ever reads: it is a peek, not a screenshot. */
.peek::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, transparent 55%, hsl(var(--st-ink-950)));
  pointer-events: none;
}

/* Right-edge fade (I6). object-fit: none anchors the export top-left, so below
   864px the edge cut mid-glyph. Spec 2.5 allows the crop; the missing fade was
   the defect. 1007, not 1023: from 1008 up the whole 864 shows. A second layer
   on ::after (first, so it paints over), not ::before -- which is free, but
   would need its own inset, z-index and pointer-events. DECISIONS.md, I6. */
@media (max-width: 1007px) {
  .peek::after {
    background:
      linear-gradient(to left, hsl(var(--st-ink-950)), transparent 56px),
      linear-gradient(to bottom, transparent 55%, hsl(var(--st-ink-950)));
  }
}

/* --- How it cuts (spec 2.4) --------------------------------------------- */

.steps {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-10);
}

.step .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  color: hsl(var(--st-thread-500));
  letter-spacing: 0.04em;
}
/* A legitimate seam: it marks the boundary between a step's label and its
   content (readme:143-148), not decorative filler. */
.step .seam { margin-block: var(--space-3) var(--space-4); }
.step h3 { font-size: var(--text-xl); }
.step p {
  margin-top: var(--space-3);
  color: hsl(var(--st-ink-400));
  font-size: var(--text-sm);
}

/* --- Cards (spec 2.4, used by Product and the principles) ---------------
 * radius 12, ink-850 fill, 1px border at 60%, shadow-sm, 24px padding, and
 * nothing else (readme:150-153). No hover: a card that is not a link gets no
 * hover state at all, and a hover treatment used as a resting state is not a
 * state.
 */

.features,
.principles {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-6);
}
/* The principles are a coda, not a section: no eyebrow, no h2, so no head to
   sit under. */
.principles { margin-top: 0; }

.card {
  border: 1px solid hsl(var(--st-ink-700) / 0.6);
  border-radius: var(--radius);
  background: hsl(var(--st-ink-850));
  box-shadow: var(--shadow-sm);
  padding: var(--space-6);
}
.card h3 { font-size: var(--text-lg); }
/* Spec 2.1 puts card h3 at 20px, stepping to 18px at <=639. The principles
   are the exception: spec 2.6 pins them at 18px at every width, because they
   are a coda and must not out-shout the sections above them. The shared base
   above is therefore 18px (what the principles want) and Product opts up. */
.features .card h3 { font-size: var(--text-xl); }
.card p {
  margin-top: var(--space-3);
  color: hsl(var(--st-ink-400));
  font-size: var(--text-sm);
}

/* --- Pricing (spec 2.7) -------------------------------------------------
 * Both cards stay ink-850. Pro is marked by a 2px dashed Thread seam across
 * its top edge — the DS's own signature graphic marking the boundary between
 * the two plans. Not a Thread border (readme:151-152 gives that to a card
 * that is a whole link, on hover) and not an ink-800 fill (ink-400 on ink-800
 * is 4.46:1, under the AA floor, and the notes and bullets here are 14px
 * ink-400).
 */

.plans {
  margin-top: 3rem;
  max-width: 50rem;   /* 800px, centred */
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-6);
  align-items: stretch;
}

.plan {
  position: relative;
  border: 1px solid hsl(var(--st-ink-700) / 0.6);
  border-radius: var(--radius);
  background: hsl(var(--st-ink-850));
  box-shadow: var(--shadow-sm);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.plan-pro::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2px;
  border-radius: var(--radius) var(--radius) 0 0;
  background-image: repeating-linear-gradient(
    90deg,
    hsl(var(--st-thread-500)) 0 4px,
    transparent 4px 7px
  );
}

.plan-name { font-size: var(--text-lg); }
.plan-price {
  margin-top: var(--space-3);
  display: flex;
  align-items: baseline;
  gap: var(--space-1_5);
}
/* 30px. Live's 36px is off the scale and steps down (spec 2.7). */
.plan-price .amount {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-medium);
  font-size: var(--text-3xl);
  letter-spacing: -0.01em;
  line-height: 1;
}
.plan-price .per { color: hsl(var(--st-ink-400)); font-size: var(--text-sm); }
.plan-note {
  margin-top: var(--space-3);
  color: hsl(var(--st-ink-400));
  font-size: var(--text-sm);
}

.plan ul { display: grid; gap: var(--space-3); font-size: var(--text-sm); }
.plan li {
  display: grid;
  grid-template-columns: 5px 1fr;
  gap: var(--space-3);
  align-items: start;
  color: hsl(var(--st-ink-200));
}
.plan li::before {
  content: "";
  width: 5px;
  height: 5px;
  margin-top: 0.625rem;
  border-radius: var(--radius-full);
  background: hsl(var(--st-thread-500));
}
/* Every count is mono with tabular figures, like every other number on the
   page (spec 2.1). */
.plan li .val { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

.plan .btn { width: 100%; margin-top: auto; }

/* ink-400, not ink-600: ink-600 never carries text (spec 4.8). */
.plans-foot {
  margin-top: var(--space-5);
  max-width: 50rem;
  margin-inline: auto;
  font-size: var(--text-xs);
  color: hsl(var(--st-ink-400));
}

/* --- Closing CTA (spec 2.8) ---------------------------------------------
 * No texture and no glow. The hero's Thread glow is the page's one and only
 * background treatment; the tick texture is scoped to empty states, unloaded
 * poster frames and the auth canvas, and a closing CTA is none of those.
 */

.closing {
  border-top: 1px solid hsl(var(--st-ink-700));
  padding-block: 6rem;
}
.closing-inner { text-align: center; }
.closing h2 { font-size: var(--text-display-2); line-height: 1.1; }
.closing p { margin: var(--space-5) auto 0; max-width: 40rem; }
.closing-cta {
  margin-top: var(--space-8);
  display: flex;
  justify-content: center;
}

/* --- Body responsive ----------------------------------------------------
 * Type steps by breakpoint, on rungs: h2 32 -> 30 -> 24.
 */

@media (max-width: 1023px) {
  .section-head h2,
  .closing h2 { font-size: var(--text-3xl); }   /* 30px */
}

@media (max-width: 900px) {
  .features { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 780px) {
  .steps { grid-template-columns: 1fr; gap: var(--space-8); }
  .closing { padding-block: 4.5rem; }
}

/* 640, not 639: at 640 the page already wore mobile gutters while these grids
   were 3/2/2 (184px principle cards). Specs 2.6 and 2.7 say one column AT 640. */
@media (max-width: 640px) {   /* layout */
  .features,
  .plans { grid-template-columns: 1fr; }
  .principles { grid-template-columns: 1fr; gap: var(--space-4); }

  .closing { padding-block: 4rem; }

  /* 40px inside the gutters, 88px tall. The screenshot is wide and dense, so
     it is not scaled to fit — it crops at the right edge (spec 2.5). */
  .peek-inset { padding-inline: 2.5rem; }
  .peek { height: 88px; }
}

/* Stays at 639: spec 2.1 puts h2 at 30px and h3 at 20px AT 640. */
@media (max-width: 639px) {   /* type */
  .section-head h2,
  .closing h2 { font-size: var(--text-2xl); }   /* 24px */

  .step h3,
  .features .card h3 { font-size: var(--text-lg); }
}
