/* ============================================================================
   JobSight — Direction 02 designed homepage
   Internal design draft, 3 September 2026. Built to docs/57.

   Fonts: Lastik (display, licensed) + Inter (body, OFL), both self-hosted from the
   package's own bundle. Nothing is fetched from a network.

   Colour note — the orange ramp. #f58220 is the brand orange and it carries the
   page: button fills, chart bars, glow, chips, rules. It cannot legally carry
   text on the near-white paper (2.49:1). So two darker steps of the same hue do
   the text work: #d96a08 for display-size accent words (3.34:1, AA large) and
   #b45309 for links and small accent text (4.81:1, AA normal). Every pair on
   this page is measured in verification/direction-02/probes/.
   ========================================================================== */

/* Lastik is the client-requested display face, supplied by That That Type
   under order 2365032443448524763 (JobSight LLC commercial license, Sep 8).
   The original WOFF2 is served unchanged. Both faces are preloaded. Block
   preserves the existing entrance, which waits for document.fonts.ready. */
@font-face {
  font-family: "Lastik";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("assets/fonts/lastik-free.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: block;
  src: url("assets/fonts/inter-var.woff2") format("woff2");
}
/* Georgia is adjusted to Lastik's measured text width and vertical metrics.
   Inter's previously verified fallback metrics remain unchanged. */
@font-face {
  font-family: "Lastik Fallback";
  src: local("Georgia");
  size-adjust: 93.188%;
  ascent-override: 81.127%;
  descent-override: 24.037%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial"), local("Helvetica Neue");
  size-adjust: 107.19%;
  ascent-override: 90.49%;
  descent-override: 22.39%;
  line-gap-override: 0%;
}

:root {
  /* ground + ink */
  --paper: #fbfaf8;
  --paper-warm: #f6f2ec;
  --paper-cool: #f0f1f0;
  --white: #ffffff;
  --ink: #22262b;
  --ink-deep: #1b2027;
  --ink-soft: #5b636c;
  --ink-mute: #666e77;
  --rule: rgba(34, 38, 43, 0.13);
  --rule-soft: rgba(34, 38, 43, 0.08);

  /* brand */
  --orange: #f58220;
  --orange-edge: #c9660f;
  --orange-display: #d96a08;
  --orange-text: #a85009;
  --orange-soft: #f2c299;
  --navy: #34495e;
  --navy-deep: #2a3c4e;
  --steel: #738ba4;
  --steel-ink: #66809a;

  /* type */
  --jobsight-display: "Lastik", "Lastik Fallback", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --body: "Inter", "Inter Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  /* T1 solves to 48px at 390 and 94px at 1440 — Billow's measured scale */
  --t1: clamp(2.25rem, 1.932rem + 4.381vw, 5.875rem);
  --t2: clamp(1.95rem, 1.336rem + 2.476vw, 3.75rem);
  --t3: clamp(1.25rem, 1.13rem + 0.5vw, 1.5rem);
  --lead: clamp(1.0625rem, 1.02rem + 0.19vw, 1.1875rem);
  --base: 1.0625rem;

  /* rhythm */
  --wrap: 1240px;
  --gut: clamp(20px, 4.2vw, 48px);

  /* depth. One light source, above and slightly left.
     Shadow ink is the product frames' own navy and, under a brand-orange
     object, the brand's own warm — never black. A neutral grey cast on warm
     paper reads as dirt; a cast carrying the ink's own hue reads as shade. */
  --sh: 21, 33, 48;
  --sh-warm: 132, 66, 12;
  /* ROUND 3 — THE ELEVATION SYSTEM. Owner review 3: "there still needs to be
     more depth to the website. It still feels flat." Depth here is not one
     shadow. It is five parts under a single light source above and slightly
     left, and every object in the set is some combination of them.

       AMBIENT  wide, soft and far — the room the object is standing in
       KEY      the light source's own cast, offset down and short
       CONTACT  tight and dark where the object meets the paper; this is the
                part that stops an object floating, and round 1 had none of it
       EDGE     a 1px inner highlight along the top edge, where the light
                catches the lip
       SURFACE  a gradient lighter at the top; a flat single hex has no light
                falling on it at all

     Measured from the reference's own frames and cards on the day
     (verification/direction-02/reference/): it builds its depth from a tinted
     eight-stop ladder with a growing negative spread over a translucent
     surface. These are the same idea at four stops, in JobSight's ink. */
  --amb-1: 0 3px 8px -3px rgba(var(--sh), 0.07);
  --amb-2: 0 12px 26px -10px rgba(var(--sh), 0.12);
  --amb-3: 0 30px 60px -22px rgba(var(--sh), 0.17);
  --amb-4: 0 66px 120px -46px rgba(var(--sh), 0.26);
  --key-1: 0 1px 3px -1px rgba(var(--sh), 0.09);
  --key-2: 0 5px 12px -4px rgba(var(--sh), 0.12);
  --key-3: 0 14px 24px -10px rgba(var(--sh), 0.14);
  --con-1: 0 1px 1px rgba(var(--sh), 0.10);
  --con-2: 0 1px 2px rgba(var(--sh), 0.16);
  --con-3: 0 2px 4px -1px rgba(var(--sh), 0.20);
  --edge: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --edge-soft: inset 0 1px 0 rgba(255, 255, 255, 0.6);
  --edge-ink: inset 0 1px 0 rgba(255, 255, 255, 0.10);

  /* four steps, each composed from the parts above */
  --elev-1: var(--con-1), var(--key-1), var(--amb-1);
  --elev-2: var(--con-2), var(--key-2), var(--amb-2);
  --elev-3: var(--con-2), var(--key-2), var(--amb-2), var(--amb-3);
  --elev-4: var(--con-3), var(--key-3), var(--amb-2), var(--amb-3), var(--amb-4);

  /* the round-1 names stay, so every object already asking for a lift steps up
     with the system instead of being rewritten one at a time */
  --lift-1: var(--elev-1);
  --lift-2: var(--elev-2);
  --lift-3: var(--elev-3);

  /* SURFACE. Every band ground is a gradient, never one hex, so the light
     reads across the page rather than stopping at each band's edge.

     Each band keeps its own colour as the middle stop, so no band changed
     hue. The floor is set by measurement, not by eye: the first pass ran a
     warm band down to #f2ece3 and the probe caught two 15px --ink-mute lines
     on compare.html at 4.48:1 and 4.43:1. The darkest stop of every surface is
     now the darkest ground on which --ink-mute #666e77, the tightest ink in
     the set at normal size, still clears 4.5:1 — warm 4.56, cool 4.55, paper
     4.72. --ink-soft holds 5.36 or better and --orange-text 4.84 or better on
     every stop; --mute is a large-text colour and clears 3:1 throughout. */
  --surf-paper: linear-gradient(180deg, #fefdfc 0%, #fbfaf8 42%, #f7f4f0 100%);
  --surf-warm:  linear-gradient(180deg, #fcf9f5 0%, #f6f2ec 34%, #f5f0e8 100%);
  --surf-cool:  linear-gradient(180deg, #f6f7f6 0%, #f0f1f0 34%, #eff1ee 100%);
  --surf-raise: linear-gradient(180deg, #ffffff 0%, #fdfbf9 52%, #f4efe8 100%);

  /* VIGNETTE. The dark closers were one flat hex over their whole width, which
     is the flattest object in the set at the moment a reader leaves the page.
     A little light in the middle and a fall to the corners gives the band a
     shape without giving it an edge. It only darkens, so white text on it can
     only gain contrast. */
  --vignette: radial-gradient(118% 88% at 50% 30%, rgba(255, 255, 255, 0.05) 0%,
    rgba(255, 255, 255, 0) 46%, rgba(0, 0, 0, 0.26) 100%);
  --vignette-soft: radial-gradient(120% 92% at 50% 30%, rgba(255, 255, 255, 0.04) 0%,
    rgba(255, 255, 255, 0) 48%, rgba(0, 0, 0, 0.22) 100%);
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  /* the header sits ABOVE the open sheet: the brand stays put and the burger
     stays reachable as the X. Round 1 had the sheet on top, which hid the only
     visible way to close it. */
  --z-menu: 50;
  --z-sticky: 60;
  /* the film sits over everything, including the sticky header, because while
     it is open it is the only thing on the page a reader is doing. */
  --z-film: 70;
}

/* ── reset ───────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--base);
  line-height: 1.62;
  font-feature-settings: "kern" 1, "liga" 1, "cv05" 1;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, svg { max-width: 100%; }
h1, h2, h3, h4, p, dl, dd, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
button { font: inherit; color: inherit; background: none; border: 0; }
a { color: inherit; }
table { border-collapse: collapse; width: 100%; }
.sprite { position: absolute; width: 0; height: 0; }
.sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip {
  position: absolute; left: 12px; top: -60px; z-index: 100;
  padding: 10px 16px; background: var(--ink); color: #fff; border-radius: 10px;
  font-size: 0.9375rem; text-decoration: none; transition: top 0.18s var(--ease);
}
.skip:focus { top: 12px; }
:focus-visible { outline: 2px solid var(--orange-text); outline-offset: 3px; border-radius: 4px; }

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); }
.mute { color: var(--steel-ink); }

/* ── type ────────────────────────────────────────────────────────────────── */
h1, h2 {
  font-family: var(--jobsight-display);
  font-weight: 400;
  letter-spacing: -0.032em;
  line-height: 1.02;
  text-wrap: balance;
}
h1 { font-size: var(--t1); }
h2 { font-size: var(--t2); line-height: 1.04; }
h3 {
  font-family: var(--body); font-size: var(--t3); font-weight: 600;
  letter-spacing: -0.014em; line-height: 1.25;
}
p { text-wrap: pretty; }

/* ── buttons and links: THE DECK ─────────────────────────────────────────────
   Round 3. Owner review 3, 3 September 2026, 14:25 MDT, verbatim: "The buttons
   don't pop enough on this show, and they still feel like they don't know what
   the word is. I'm thinking we need to go with a more rounded look and more
   depth … It still feels flat, and we want to avoid feeling flat."

   Two faults, and they are the same fault. Round 2's control put four stage
   segments under its label, and a bar under a label is the shape of a progress
   meter rather than of a button, so the control did not say what it was. And it
   took its weight from a hard 2px edge and the ground it stood on, which is a
   deliberate flatness. The stage rail is gone from every control in the set.

   THE DECK is a solid slab standing in the same light as the page's product
   frames: a surface lighter at the top and darker at the bottom, a 1px edge
   light along the top lip, a hairline ring one step darker than the fill, and a
   layered cast — contact, key, ambient — in the brand's own warm ink under an
   orange control and the frames' navy under a paper one. Hover raises it 2px
   and every layer grows; active sinks it, the gradient inverts so the face
   reads concave, and the cast collapses to the contact shadow alone.

   The job is unmistakable: the label comes first, then a chevron, and the
   chevron travels forward on hover. That travel is the one bespoke mark kept.

   Radius 14px (12px small, 16px large) — rounded, and nowhere near a capsule at
   54px tall, so Brady's "not a fan of pills" still holds.
   Concepts 4, 5 and 6 and the pick: buttons.html, reasoning in docs/59 §23.
   ------------------------------------------------------------------------- */
.btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 54px; padding: 15px 24px; border-radius: 14px;
  font-size: 1rem; font-weight: 600; letter-spacing: -0.008em; line-height: 1.2;
  text-decoration: none; white-space: nowrap;
  transition: transform 0.34s var(--ease), box-shadow 0.34s var(--ease),
              background-color 0.3s var(--ease), background-image 0.3s var(--ease),
              border-color 0.3s var(--ease), color 0.3s var(--ease);
}
/* the chevron. rotate(45deg) and then translate(t, -t) is a pure horizontal
   move of t·√2 on screen, so the cue travels forward and does not drift. */
.btn::after {
  content: ""; flex: 0 0 auto; width: 7px; height: 7px; margin-left: -1px;
  border-right: 2px solid currentColor; border-top: 2px solid currentColor;
  transform: rotate(45deg); transition: transform 0.34s var(--ease);
}
/* ROUND 5 — everything that MOVES under a pointer is gated on there being one.
   A touch screen reports a hover the moment a finger lands and keeps reporting
   it until something else is touched, so an ungated lift is a card that stays
   lifted after the reader has moved on. Measured on the head of 4 September:
   a tap on a blog card left it raised and its cast deepened, indefinitely.
   Focus is never gated with it — a keyboard on a touch device is still a
   keyboard, so every :focus-visible half of these pairs stays outside. */
.btn:focus-visible::after { transform: rotate(45deg) translate(2px, -2px); }
@media (hover: hover) and (pointer: fine) {
  .btn:hover::after { transform: rotate(45deg) translate(2px, -2px); }
}

.btn--sm { min-height: 44px; padding: 11px 18px; border-radius: 12px; gap: 9px; font-size: 0.9375rem; }
.btn--lg { min-height: 60px; padding: 18px 30px; border-radius: 16px; gap: 11px; font-size: 1.0625rem; }

/* the fill keeps the brand orange as its middle stop, so #f58220 still carries
   the page; the lighter top and darker bottom are the light on it. Charcoal
   #1b2027 measures 7.44:1 on the top stop, 6.31:1 on the brand mid and 5.57:1
   on the bottom stop — AA at normal size across the whole surface. */
.btn--primary {
  color: var(--ink-deep);
  background-image: linear-gradient(180deg, #ff9436 0%, #f58220 46%, #ea7714 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.44), inset 0 0 0 1px rgba(157, 78, 8, 0.5),
    0 1px 2px rgba(var(--sh-warm), 0.30), 0 4px 10px -3px rgba(var(--sh-warm), 0.30),
    0 14px 28px -10px rgba(var(--sh-warm), 0.26), 0 30px 60px -28px rgba(var(--sh-warm), 0.36);
}
@media (hover: hover) and (pointer: fine) {
.btn--primary:hover {
  transform: translateY(-2px);
  background-image: linear-gradient(180deg, #ffa04a 0%, #f88a2a 46%, #ee7c19 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.52), inset 0 0 0 1px rgba(157, 78, 8, 0.5),
    0 2px 3px rgba(var(--sh-warm), 0.28), 0 8px 16px -4px rgba(var(--sh-warm), 0.30),
    0 22px 40px -12px rgba(var(--sh-warm), 0.28), 0 44px 80px -32px rgba(var(--sh-warm), 0.38);
}
}
/* :active is NOT gated — a press must answer a finger */
.btn--primary:active {
  transform: translateY(1px);
  background-image: linear-gradient(180deg, #e5730f 0%, #ef7d18 58%, #f98c2c 100%);
  box-shadow: inset 0 3px 7px rgba(112, 54, 4, 0.55), inset 0 0 0 1px rgba(140, 68, 6, 0.7),
    0 1px 1px rgba(var(--sh-warm), 0.20);
}

.btn--secondary {
  color: var(--ink);
  background-image: linear-gradient(180deg, #ffffff 0%, #fdfbf8 55%, #f4efe8 100%);
  box-shadow: inset 0 1px 0 #fff, inset 0 0 0 1px rgba(34, 38, 43, 0.14),
    0 1px 2px rgba(var(--sh), 0.10), 0 4px 10px -3px rgba(var(--sh), 0.11),
    0 14px 28px -12px rgba(var(--sh), 0.15);
}
.btn--secondary::after { color: var(--orange-text); }
@media (hover: hover) and (pointer: fine) {
.btn--secondary:hover {
  transform: translateY(-2px);
  box-shadow: inset 0 1px 0 #fff, inset 0 0 0 1px rgba(34, 38, 43, 0.20),
    0 2px 3px rgba(var(--sh), 0.10), 0 8px 16px -4px rgba(var(--sh), 0.12),
    0 24px 44px -14px rgba(var(--sh), 0.18);
}
}
/* :active is NOT gated — a press must answer a finger */
.btn--secondary:active {
  transform: translateY(1px);
  background-image: linear-gradient(180deg, #f1ece5 0%, #f8f5f1 100%);
  box-shadow: inset 0 3px 6px rgba(var(--sh), 0.14), inset 0 0 0 1px rgba(34, 38, 43, 0.16),
    0 1px 1px rgba(var(--sh), 0.08);
}

/* the quiet weight is the tertiary control: no fill, no cast, a label on a
   hairline with the same chevron. The rule is drawn as a pseudo-element rather
   than as text-decoration so it stops short of the chevron. */
.btn--quiet {
  margin-top: 14px; min-height: 44px; padding: 12px 2px; gap: 9px; border-radius: 8px;
  color: var(--orange-text); background: none;
}
.btn--quiet::before {
  content: ""; position: absolute; left: 2px; right: 18px; bottom: 9px; height: 1px;
  background: rgba(168, 80, 9, 0.34); transition: background-color 0.3s var(--ease);
}
.btn--quiet:hover { color: #8d430a; transform: none; }
.btn--quiet:hover::before { background: rgba(141, 67, 10, 0.92); }
.btn--quiet:active { transform: translateY(1px); }
.close__acts .btn--quiet { margin-top: 0; }

.btn:focus-visible { outline: 2px solid var(--ink-deep); outline-offset: 3px; }
.btn--quiet:focus-visible { border-radius: 6px; }

/* on the dark closer. The primary keeps its own light and takes a warm ambient
   so it still lifts off ink; the quiet weight turns white. */
.on-ink.btn--primary {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.46), inset 0 0 0 1px rgba(157, 78, 8, 0.42),
    0 1px 2px rgba(0, 0, 0, 0.5), 0 6px 14px -4px rgba(0, 0, 0, 0.55),
    0 20px 46px -14px rgba(245, 130, 32, 0.32);
}
.btn--quiet.on-ink { color: rgba(255, 255, 255, 0.9); }
.btn--quiet.on-ink::before { background: rgba(255, 255, 255, 0.34); }
.btn--quiet.on-ink:hover { color: #fff; }
.btn--quiet.on-ink:hover::before { background: rgba(255, 255, 255, 0.85); }
.on-ink:focus-visible { outline-color: #fff; }

/* disabled. WCAG 1.4.3 exempts an inactive control, so this is the one surface
   in the set that is allowed under the AA floor; nothing on the five pages
   uses it today. */
.btn[aria-disabled="true"], .btn:disabled {
  pointer-events: none; opacity: 0.45; transform: none;
  background-image: linear-gradient(180deg, #f0ece6, #e9e4dc); color: #6d757d;
  box-shadow: inset 0 0 0 1px rgba(34, 38, 43, 0.10);
}

/* ── form field. DIRECTOR'S RULING, 3 September. The two forms in the set —
   the comparison page's demo gate and the blog's mailing row — were the last
   default-looking objects on any of the five pages: a bordered grey box with
   the browser's own focus glow on it, which is the control every site built
   on a system ships. Both are now one control, written ON a rule instead of
   inside a box, on whichever ground the band gives it.

   The rule is the page's dominant device — the ledger, the contents rail, the
   claims row and the closer's list are all things strung on hairlines — so a
   field written on one belongs to the page rather than to the browser. The
   label above it is the T7 label (13px, caps, 0.10em, 600 — docs/39). The
   hairline lights on focus, and the page's own focus ring lands on top of it;
   no inset glow, no ring of the browser's choosing.

   Neither form is wired. Both say so, in the page's voice, when you press the
   button: the wiring is R2-03, after the Zoho contract (docs/43 D-27). ──── */
.onrule {
  --onrule-line: var(--rule);
  --onrule-hover: rgba(34, 38, 43, 0.34);
  --onrule-lit: var(--orange-edge);
  --onrule-label: var(--ink-mute);
  --onrule-ink: var(--ink);
}
.onrule--ink {
  --onrule-line: rgba(255, 255, 255, 0.32);
  --onrule-hover: rgba(255, 255, 255, 0.62);
  --onrule-lit: var(--orange);
  --onrule-label: #c3ced9;
  --onrule-ink: #ffffff;
}
.onrule label {
  display: block;
  font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.1em; line-height: 1.4;
  text-transform: uppercase; color: var(--onrule-label);
}
.onrule input {
  width: 100%; margin-top: 5px; padding: 3px 0 10px;
  font: inherit; font-size: var(--base); line-height: 1.4; color: var(--onrule-ink);
  background: none; border: 0; border-radius: 0;
  border-bottom: 1px solid var(--onrule-line);
  transition: border-color 0.3s var(--ease);
}
.onrule input:hover { border-bottom-color: var(--onrule-hover); }
.onrule input:focus { border-bottom-color: var(--onrule-lit); }
.onrule input:focus-visible { outline: 2px solid var(--orange-text); outline-offset: 4px; border-radius: 2px; }
.onrule--ink input:focus-visible { outline-color: #fff; }

/* the honest state. Nothing here posts anywhere yet, so pressing the button
   says exactly that, on the same lit segment the rest of the set uses to mark
   the live thing. Hidden until the button is pressed; with JavaScript off it
   never appears, because with JavaScript off the button does nothing. */
.formstate {
  align-items: baseline; gap: 11px;
  font-size: 0.8125rem; line-height: 1.5; color: var(--ink);
}
.formstate:not([hidden]) { display: flex; }
.formstate::before {
  content: ""; flex: 0 0 14px; height: 3px; margin-top: 0.56em; background: var(--orange);
}
.formstate:focus { outline: 0; }
.formstate--ink { color: rgba(255, 255, 255, 0.88); }

.lnk { color: var(--ink); text-decoration: none; font-size: 0.9375rem; font-weight: 500; }
.lnk:hover { color: var(--orange-text); }

/* ── header ──────────────────────────────────────────────────────────────── */
/* ROUND 3 — depth. At the top of the page the header is part of the paper and
   carries nothing: no shadow, no rule, no surface. The moment it stops being
   part of the paper and starts floating over it, it says so — it lifts. The
   surface picks up a gradient that is lighter along its top edge, a white
   hairline appears under the rule as the light catches its underside lip, and a
   three-part cast falls onto the page below it. The board rail is untouched. */
/* ROUND 5 — the blur belongs to the floating state, not to the bar.
   backdrop-filter was declared on .hdr unconditionally, which put a full-width
   composited layer under the header for the life of every page and, in Safari,
   re-sampled the whole width of the viewport behind it on every scrolled frame.
   Chromium named it: the layer list at the top of all five pages carried
   "1456x91 StickyPosition BackdropFilter" while the reader had not scrolled at
   all. The header's own note above says that at the top of the page it is part
   of the paper and carries nothing — and at the top of the page there is
   nothing behind it to blur, because the bar is over the head of the hero.
   So the blur arrives with the float, and the transition it arrives on is the
   same 400ms the surface already uses, so the two resolve together. */
.hdr {
  position: sticky; top: 0; z-index: var(--z-sticky);
  background-color: rgba(251, 250, 248, 0.82);
  border-bottom: 1px solid transparent;
  transition: border-color 0.4s var(--ease), background-color 0.4s var(--ease),
              background-image 0.5s var(--ease), box-shadow 0.5s var(--ease),
              -webkit-backdrop-filter 0.4s var(--ease), backdrop-filter 0.4s var(--ease);
}
.hdr.is-stuck {
  backdrop-filter: saturate(1.6) blur(14px);
  -webkit-backdrop-filter: saturate(1.6) blur(14px);
  border-bottom-color: rgba(34, 38, 43, 0.07);
  background-color: rgba(251, 250, 248, 0.93);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.78) 0%, rgba(255, 255, 255, 0) 72%);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.72),
    0 1px 2px rgba(var(--sh), 0.05), 0 6px 16px -6px rgba(var(--sh), 0.10),
    0 18px 36px -20px rgba(var(--sh), 0.16);
}
.hdr__in {
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding: 16px var(--gut);
  display: flex; align-items: center; gap: 30px;
  transition: padding 0.4s var(--ease);
}
.hdr.is-stuck .hdr__in { padding-block: 11px; }
.hdr__brand { display: flex; align-items: center; flex: 0 0 auto; }
.hdr__brand img { height: 40px; width: auto; display: block; }
/* THE BOARD. Round 2: the nav is a board and the same orange node that ends the
   buttons' stage rail rides this one. It rests at the start of the line and
   advances to whichever item you are on, dragging the filled segment behind it,
   so hovering the last item fills the rail — a job reaching Collections. There
   is no "current page" on a one-page design, so the rest position is the start
   of the line rather than a false current item. Concepts: buttons.html. */
.hdr__nav { position: relative; display: flex; gap: 28px; margin-right: auto; }
.hdr__nav a {
  position: relative; font-size: 0.9375rem; font-weight: 500; color: var(--ink-soft);
  text-decoration: none; padding-block: 4px; transition: color 0.3s var(--ease);
}
.hdr__nav a:hover { color: var(--ink); }
.hdr__rail, .hdr__seg, .hdr__node { position: absolute; pointer-events: none; }
.hdr__rail { left: 0; right: 0; bottom: -9px; height: 1px; background: var(--rule); }
.hdr__seg {
  left: 0; bottom: -9px; height: 1.5px; background: var(--orange);
}
.hdr__node {
  left: -4px; bottom: -12.5px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--orange); box-shadow: 0 0 0 3px var(--paper);
  transition: transform 0.5s var(--ease);
}
.hdr.is-stuck .hdr__node { box-shadow: 0 0 0 3px #fbfaf8; }
.hdr__end { display: flex; align-items: center; gap: 18px; }

.burger { display: none; position: relative; padding: 8px; margin-right: -8px; cursor: pointer; }
/* the drawn box is 22x14 in an 38x30 button, which is under the 44px target the
   rest of the controls hold. This gives it a real 44x44 without changing the
   header's height. Measured: verification/direction-02/probes/probe-result.json */
.burger::after {
  content: ""; position: absolute; left: 50%; top: 50%; width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}
/* three rails of unequal length with the middle one orange: a board seen edge
   on, not a hamburger in a box. It still resolves to an X, because that
   convention is worth keeping. */
.burger__box { display: block; width: 22px; height: 14px; position: relative; }
.burger__box i {
  position: absolute; left: 0; width: 22px; height: 1.6px; background: var(--ink); border-radius: 2px;
  transition: transform 0.45s var(--ease), opacity 0.2s linear;
}
.burger__box i:nth-child(1) { top: 0; }
.burger__box i:nth-child(2) { top: 6px; width: 14px; background: var(--orange); }
.burger__box i:nth-child(3) { top: 12px; }
.burger[aria-expanded="true"] .burger__box i:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.burger[aria-expanded="true"] .burger__box i:nth-child(2) { opacity: 0; transform: scaleX(0.2); }
.burger[aria-expanded="true"] .burger__box i:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.menu {
  position: fixed; inset: 0; z-index: var(--z-menu);
  background-color: rgba(251, 250, 248, 0.97);
  /* ROUND 3 — the sheet has a ground and a light, not one flat wash. The header
     sits above it (--z-sticky beats --z-menu), so the header's own cast falls
     onto the top of the sheet; the surface is lightest under that edge and
     settles into warm paper at the foot of the screen. */
  background-image:
    radial-gradient(128% 52% at 50% 0%, rgba(255, 255, 255, 0.92) 0%, rgba(255, 255, 255, 0) 64%),
    linear-gradient(180deg, rgba(251, 250, 248, 0) 48%, rgba(240, 233, 223, 0.9) 100%);
  box-shadow: inset 0 16px 26px -16px rgba(var(--sh), 0.30);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  padding: 108px var(--gut) 48px;
  display: flex; flex-direction: column; gap: 8px;
}
.menu[hidden] { display: none; }
.menu__nav { position: relative; display: flex; flex-direction: column; padding-left: 24px; }
.menu__rail { position: absolute; left: 0; top: 6px; bottom: 6px; width: 1px; background: var(--rule); pointer-events: none; }
.menu__node {
  position: absolute; left: -3.5px; top: 6px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--orange); pointer-events: none;
  box-shadow: 0 0 0 3px rgba(251, 250, 248, 0.92), 0 1px 2px rgba(var(--sh), 0.30),
    0 4px 10px -3px rgba(245, 130, 32, 0.5);
  transition: transform 0.5s var(--ease);
}
.menu__nav a {
  display: block; overflow: hidden; padding: 14px 0;
  border-bottom: 1px solid var(--rule-soft);
  font-family: var(--jobsight-display); font-size: 2rem; letter-spacing: -0.03em;
  text-decoration: none;
}
.menu__nav a span { display: block; }
.menu__cta { align-self: flex-start; margin-top: 28px; margin-left: 24px; }

/* ── shared: the light. Two lobes, never centred, never behind a heading. ── */
.bloom { position: absolute; pointer-events: none; z-index: 0; }

/* R2-02 — light on an object's edge, at any height. A pair of 280px bands sit
   centred on the object's top and bottom edges, so a core is always cut by the
   thing it is lighting: warm above, cool below, and never a lobe floating free
   in open paper (docs/59 §5.1 — occlusion is what makes it read as light).
   Used on the inner pages' product frames and on the comparison page's gate
   panel; the homepage's hand-placed .bloom--hero stays as the owner ruled it. */
.fglow { position: absolute; left: -7%; right: -7%; height: 280px; pointer-events: none; z-index: 0; }
.fglow--top {
  top: -118px;
  background:
    radial-gradient(15% 44% at 13% 52%, rgba(245, 130, 32, 0.82) 0%, rgba(245, 130, 32, 0.62) 27%, rgba(245, 130, 32, 0.16) 57%, rgba(245, 130, 32, 0) 75%),
    radial-gradient(12% 36% at 87% 56%, rgba(245, 130, 32, 0.56) 0%, rgba(245, 130, 32, 0.40) 25%, rgba(245, 130, 32, 0) 73%),
    radial-gradient(40% 78% at 50% 64%, rgba(242, 194, 153, 0.32), rgba(242, 194, 153, 0) 76%);
}
.fglow--bot {
  bottom: -140px;
  background:
    radial-gradient(17% 44% at 26% 48%, rgba(102, 128, 154, 0.52) 0%, rgba(102, 128, 154, 0.38) 26%, rgba(102, 128, 154, 0) 76%),
    radial-gradient(13% 36% at 72% 44%, rgba(102, 128, 154, 0.32) 0%, rgba(102, 128, 154, 0) 74%),
    radial-gradient(42% 74% at 46% 40%, rgba(242, 194, 153, 0.26), rgba(242, 194, 153, 0) 76%);
}
.bloom--hero {
  /* Round 2 — the light gets a shape. Round 1 made a warm haze that read as
     nice paper rather than as light (§5.1). Three changes:

     1. Two TIGHT cores instead of three soft lobes. Each holds near full
        strength to a quarter of its radius and then falls away fast, so the
        light has an edge instead of a gradient.
     2. The cores sit on the board's top shoulders, so the frame's own edge
        cuts them. An occluded core reads as light in a room; a free-floating
        one reads as a gradient.
     3. A COOL counterpoint — steel, the frame's own chrome colour — sits below
        and left of the warm. Warm needs something to be warm against, and one
        light source has a warm key and a cool shadow side.

     OWNER RULING 2 — James, 2026-09-03 10:15 MDT. Round 2's cool side was doing
     its job quietly; docs/59 listed a louder version as the one place Billow
     still has more event than we do, and asked how much blue the page could
     carry. The answer is one visible step, and this is it:

       · the lobe under the board goes 0.42 → 0.62 and gains a plateau of its
         own (0.52 held to 26% of the radius), so it has an edge instead of a
         slope — the same treatment the warm cores already had;
       · the lobe beside the frame goes 0.26 → 0.40 and widens a little;
       · a third, very quiet lobe picks up the board's right shoulder, so the
         cool wraps the frame instead of only pooling on one side.

     What did NOT change: both loud lobes stay under and beside the product
     frame, the falloff still reaches zero (no hard edge anywhere), and there is
     still no cool panel behind the headline — the copy sits on plain paper. The
     text pairs are re-proven by pixel sampling, not asserted.

     Nothing saturated reaches the reassurance line: the cores start below it
     and the peach base falls to zero above it. Measured, not asserted — the
     probe samples the pixels under that line's own glyph boxes. */
  left: 50%; top: -152px; width: min(1700px, 118vw); height: 900px; transform: translateX(-50%);
  background:
    radial-gradient(34% 22% at 33% 85%, rgba(102, 128, 154, 0.62) 0%, rgba(102, 128, 154, 0.52) 26%, rgba(102, 128, 154, 0.17) 54%, rgba(102, 128, 154, 0) 78%),
    radial-gradient(17% 26% at 11.5% 49%, rgba(102, 128, 154, 0.40) 0%, rgba(102, 128, 154, 0.30) 24%, rgba(102, 128, 154, 0) 74%),
    radial-gradient(15% 17% at 88% 78%, rgba(102, 128, 154, 0.30) 0%, rgba(102, 128, 154, 0.20) 26%, rgba(102, 128, 154, 0) 74%),
    radial-gradient(18.5% 20% at 16% 21.5%, rgba(245, 130, 32, 0.90) 0%, rgba(245, 130, 32, 0.74) 27%, rgba(245, 130, 32, 0.19) 57%, rgba(245, 130, 32, 0) 74%),
    radial-gradient(13% 15.5% at 84.5% 26%, rgba(245, 130, 32, 0.78) 0%, rgba(245, 130, 32, 0.61) 24%, rgba(245, 130, 32, 0.15) 55%, rgba(245, 130, 32, 0) 72%),
    radial-gradient(46% 40% at 47% 62%, rgba(242, 194, 153, 0.54), rgba(242, 194, 153, 0) 74%);
}
.bloom--margins {
  right: -12%; top: -6%; width: min(900px, 74vw); height: 780px;
  background:
    radial-gradient(40% 44% at 58% 44%, rgba(245, 130, 32, 0.40), rgba(245, 130, 32, 0.10) 56%, rgba(245, 130, 32, 0) 78%),
    radial-gradient(58% 54% at 32% 64%, rgba(242, 194, 153, 0.42), rgba(242, 194, 153, 0) 74%);
}
.bloom--field {
  left: -20%; top: 2%; width: min(620px, 96vw); height: 700px;
  background:
    radial-gradient(42% 46% at 48% 48%, rgba(245, 130, 32, 0.34), rgba(245, 130, 32, 0.09) 56%, rgba(245, 130, 32, 0) 78%),
    radial-gradient(60% 56% at 62% 62%, rgba(242, 194, 153, 0.40), rgba(242, 194, 153, 0) 76%);
}
.bloom--ai {
  right: -22%; top: -14%; width: min(880px, 92vw); height: 720px;
  background:
    radial-gradient(42% 44% at 56% 46%, rgba(245, 130, 32, 0.36), rgba(245, 130, 32, 0.09) 58%, rgba(245, 130, 32, 0) 80%),
    radial-gradient(58% 54% at 38% 62%, rgba(242, 194, 153, 0.38), rgba(242, 194, 153, 0) 76%);
}
@media (max-width: 980px) {
  .bloom--margins { right: -22%; top: auto; bottom: -8%; width: min(760px, 108vw); height: 560px; opacity: 0.55; }
  .bloom--field { left: -34%; top: auto; bottom: -6%; width: min(700px, 106vw); height: 520px; opacity: 0.6; }
  .bloom--ai { right: -30%; top: auto; bottom: -10%; width: min(700px, 104vw); height: 520px; opacity: 0.6; }
  .bloom--hero { opacity: 0.85; }
}
.bloom--close {
  right: -4%; bottom: -50%; width: min(980px, 96vw); height: 780px;
  background:
    radial-gradient(42% 46% at 58% 50%, rgba(245, 130, 32, 0.42), rgba(245, 130, 32, 0) 74%),
    radial-gradient(64% 56% at 36% 66%, rgba(184, 90, 12, 0.26), rgba(184, 90, 12, 0) 78%);
}

/* ── shared: frames. Outer shell, inner core, one light source. ─────────────
   ROUND 3. Every drawn frame now carries the whole elevation: a glass
   highlight along its top lip where the light catches the shell, then contact,
   key and ambient underneath it. Round 1 gave them a single soft cast in
   neutral grey with no contact term at all, which is why a 1220px product
   board could sit on the paper and still read as printed on it. */
.frame {
  position: relative; z-index: 1;
  padding: 6px; border-radius: 20px;
  background: linear-gradient(180deg, #ffffff 0%, #fbf8f4 46%, #f3efe9 100%);
  border: 1px solid rgba(34, 38, 43, 0.10);
  box-shadow: var(--edge), var(--elev-4);
  transition: transform 0.7s var(--ease), box-shadow 0.7s var(--ease);
}
.frame--panel { border-radius: 18px; box-shadow: var(--edge), var(--elev-3); }
.frame--mini { border-radius: 15px; padding: 5px; box-shadow: var(--edge-soft), var(--elev-2); }
.fade { -webkit-mask-image: linear-gradient(180deg, #000 0, #000 76%, rgba(0, 0, 0, 0) 100%);
        mask-image: linear-gradient(180deg, #000 0, #000 76%, rgba(0, 0, 0, 0) 100%); }

.chrome {
  display: flex; align-items: center; gap: 12px; height: 30px; padding: 0 12px;
  border-radius: 13px 13px 0 0;
  background: linear-gradient(180deg, #fdfcfa, #f4f1ec);
  border: 1px solid rgba(34, 38, 43, 0.09); border-bottom: 0;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
.chrome__dots { display: flex; gap: 5px; }
.chrome__dots i { width: 8px; height: 8px; border-radius: 50%; background: rgba(34, 38, 43, 0.15); }
.chrome__dots i:first-child { background: rgba(245, 130, 32, 0.42); }
.chrome__url {
  flex: 1; max-width: 250px; margin-inline: auto; text-align: center;
  font-size: 10px; letter-spacing: 0.01em; color: #5f6970;
  background: #fff; border: 1px solid rgba(34, 38, 43, 0.08); border-radius: 6px; padding: 2px 8px;
}

/* ── hero ────────────────────────────────────────────────────────────────── */
.hero { position: relative; overflow-x: clip; padding-top: clamp(48px, 7vw, 96px); }
.hero__copy { position: relative; z-index: 2; text-align: center; max-width: 1000px; }
.hero__kick {
  font-size: 0.875rem; font-weight: 500; letter-spacing: 0.005em; color: var(--ink-mute);
  margin-bottom: clamp(20px, 2.6vw, 34px);
}
.dot {
  display: inline-block; vertical-align: 0.06em; margin-right: 10px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--orange);
  box-shadow: 0 0 0 4px rgba(245, 130, 32, 0.16);
}
.hero__h { margin-inline: auto; max-width: 18ch; }
.hero__h em { font-style: normal; color: var(--orange-display); position: relative; }
.hero__h em::after {
  content: ""; position: absolute; left: 2%; right: 2%; bottom: 0.1em; height: 0.055em;
  background: var(--orange); border-radius: 2px; opacity: 0.34;
}
.hero__lead {
  margin: clamp(22px, 2.4vw, 32px) auto 0; max-width: 48ch;
  font-size: var(--lead); line-height: 1.55; color: var(--ink-soft);
}
.hero__acts { display: flex; justify-content: center; gap: 12px; margin-top: clamp(28px, 3vw, 40px); flex-wrap: wrap; }
.hero__note { margin-top: 20px; font-size: 0.875rem; color: var(--ink-soft); }

.stage { position: relative; margin-top: clamp(38px, 4.4vw, 64px); }
.stage > .wrap { max-width: 1336px; }
.stage .wrap { position: relative; z-index: 1; }
.frame--wide { padding: 5px; }
.appbox { position: relative; overflow: hidden; border-radius: 13px; height: calc(660px * var(--k, 1)); }
.app {
  width: 1220px; height: 660px; display: flex; overflow: hidden;
  transform-origin: top left; transform: scale(var(--k, 1));
  border-radius: 13px; border: 1px solid rgba(34, 38, 43, 0.09);
  background: #f7f8f9;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
}
@media (max-width: 1179px) { :root { --k: 0.78; } }
@media (max-width: 1023px) { :root { --k: 0.66; } }
@media (max-width: 767px)  { :root { --k: 0.58; } }

/* ── the phone stage. DIRECTOR'S RULING, 3 September ─────────────────────
   Scaling a drawn desktop board to fit a phone stops working before it stops
   shrinking. At 390 the homepage board was scaled to 0.58 and was still 370px
   wider than its frame, so the right-hand columns were cut mid-word; the role
   page's queue was cut by 374px. On the desk that overhang is the
   composition's own idea — the queue is meant to run off the right edge. On a
   phone it reads as a rendering fault.

   So below 768px the frame stops being a window that crops and becomes a
   stage you push sideways. (The first ruling drew the line at 620px; the
   second, on 3 September, moved it to 767px, because 621–767 was the same
   defect at a width nobody had called a phone — the boards still overhung by
   roughly 150px at 621 and the role page's queue by 102px at 767. Nothing
   changes at 768 and above.)

   · the mock goes back to its own scale (--k: 1), because its 9px floor was
     authored at that scale and 0.58 of 9px is 5px — the reason it was
     illegible, not the reason it was cut;
   · the frame runs to both edges of the screen, which is what buys the first
     column its full width at rest, and is a move the set already makes (the
     role page's closer runs its week schedule out to both edges);
   · the box scrolls horizontally, on its own, with the page's own scrolling
     left alone (overscroll-behavior stops the chain, and the page still
     measures 0px of overflow at every width);
   · the right edge dissolves into the frame's own ground, so the board says
     "there is more this way" rather than "this was cut". app.js lifts the
     dissolve when the stage is scrolled to its end, because at the end there
     is no more, and a fade that lies is worse than no fade.

   The selector is doubled up (.appbox[data-w]) so this beats the per-page
   overflow:hidden rules further down the sheet without moving a page-scoped
   class into the shared block. Until 5 September 2026 .appbox.appbox--list rode
   with it, for the drawn jobs list; that list is a screenshot now and its stage
   is .shotbox--bleed, below.

   The phone frames — the drawn mobile app recreations — are exempt: they are
   already phone-width and they are the one thing on the page that is meant to
   be seen whole. */
.appbox[data-w] { --stage-fade: 76px; }
@media (max-width: 767px) {
  .frame.frame--wide, .frame.frame--list {
    width: auto; max-width: none; margin-inline: calc(-1 * var(--gut));
  }
  .appbox[data-w] {
    --k: 1; --kl: 1;
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--stage-fade)),
                        rgba(0, 0, 0, 0.5) calc(100% - var(--stage-fade) * 0.42), rgba(0, 0, 0, 0) 100%);
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--stage-fade)),
                rgba(0, 0, 0, 0.5) calc(100% - var(--stage-fade) * 0.42), rgba(0, 0, 0, 0) 100%);
  }
  .appbox[data-w]::-webkit-scrollbar { width: 0; height: 0; }
  .appbox.is-end { -webkit-mask-image: none; mask-image: none; }
}

/* ── the same stage, asked for by width instead of by a media query ──────────
   DIRECTOR'S RULING, 4 September. Between 768px and 1440px a drawn board could
   still be wider than its box, and there the box does not scroll, so the right
   of the board was simply gone: 102px of the role page's jobs list at 768,
   52px at 820, 627px at 1180 (docs/59 §30.4). app.js §1 now measures every
   frame against its box and, where shrinking the rest of the way would take
   the mock below four fifths of the size it is drawn at, puts .is-stage on the
   box instead. These are the phone stage's own declarations, so a stage is a
   stage wherever the reader meets one: the mock at its own scale, the box
   scrolling on its own with the page's scrolling left alone, and the right
   edge dissolving into the frame's ground until there is nothing left to
   reach. Nothing here can fire at 1440 and above — app.js will not set the
   class there. */
.appbox.is-stage {
  --k: 1; --kl: 1;
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--stage-fade)),
                      rgba(0, 0, 0, 0.5) calc(100% - var(--stage-fade) * 0.42), rgba(0, 0, 0, 0) 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--stage-fade)),
              rgba(0, 0, 0, 0.5) calc(100% - var(--stage-fade) * 0.42), rgba(0, 0, 0, 0) 100%);
}
.appbox.is-stage::-webkit-scrollbar { width: 0; height: 0; }
.appbox.is-stage.is-end { -webkit-mask-image: none; mask-image: none; }

/* ── the real product, inside the approved frame ─────────────────────────────
   OWNER RULING, 5 September 2026: "you integrate everything into the demo to
   show what it would really look like, meaning integrated template." Every
   drawn frame that has a real desktop screen behind it now holds the capture
   itself. The screens were shot in JobSight's own sample company — the whole
   desktop set on 5 September 2026 at double resolution — and what each one is,
   and what is off about it, is written out in
   verification/direction-02/demo-account/shots/INDEX.md.

   The frame SHELL does not change. Same lit top lip, same contact, key and
   ambient cast, same corner radius, same browser bar where the page had one,
   and the hero keeps its own fade-out at the bottom. Only what is inside the
   shell is different.

   HOW A CROP IS WRITTEN, and why it is not object-fit. The obvious way to show
   one window of a picture is `object-fit: cover` with an `object-position`.
   It does not work here: cover scales the picture until it covers the box and
   then trims ONE axis, so it can only ever show the crop a person asked for
   when the crop already matches the file in width or in height. Three of these
   nine crops match in neither, and cover quietly showed the whole screen
   instead — the sidebar, the Sign Out button and the resources panel the crop
   was written to leave out. It was invisible in a still and obvious the moment
   the frame was measured.

   So the crop is arithmetic instead. Each frame names six numbers: the file's
   own size (--iw, --ih), the crop's origin in that file (--cx, --cy) and the
   crop's size (--cw, --ch). The window (.shotwin) takes the crop's aspect
   ratio and clips; inside it the whole picture is laid out at
   file ÷ crop of the window's own size and pushed up and left by the crop's
   origin. Every length is a percentage of the window, so the same six numbers
   are right at every screen width, at any resolution, and a re-capture of the
   same screen at 2x drops in over the file with nothing else to change.

   Because a frame is never allowed to be wider than its crop is in the
   capture's own pixels, no picture is ever enlarged past 1:1. */
.appbox.shotbox {
  height: auto;
  aspect-ratio: var(--cw) / var(--ch);
  border: 1px solid rgba(34, 38, 43, 0.09);
  background: #f7f8f9;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
}
.shotwin {
  display: block; position: relative;
  width: 100%; height: 100%;
  overflow: hidden; border-radius: inherit;
}
.shotwin .shot {
  position: absolute;
  left:   calc(var(--cx) / var(--cw) * -100%);
  top:    calc(var(--cy) / var(--ch) * -100%);
  width:  calc(var(--iw) / var(--cw) * 100%);
  height: calc(var(--ih) / var(--ch) * 100%);
  max-width: none;
}
.frame--mini .shotbox { border-radius: 11px; }

/* one line per frame: the file, then the crop, in the capture's own pixels.

   --dpr is how many file pixels the capture holds per CSS pixel of the screen
   it is a picture of. Every one of these files is a 2x capture from 5 September
   now, so all nine sets of six numbers are twice the size of the window they
   describe. Only two rules read --dpr — the phone stage's height and the
   bleeding frame's height — and both of them mean "the crop at life size",
   which is --ch / --dpr, not --ch. Nothing else changes: every other length
   here is a share of the crop, so it is right at any resolution. */
.shotbox       { --dpr: 1; }
.shot--board   { --iw: 2110; --ih: 1108; --cx: 0;   --cy: 0;   --cw: 2110; --ch: 1108; --dpr: 2; } /* 01  jobs board, three stages */
.shot--cost    { --iw: 1800; --ih: 1640; --cx: 0;   --cy: 0;   --cw: 1800; --ch: 1162; --dpr: 2; } /* 11b Arriaga 2426, work in progress */
.shot--sched   { --iw: 2880; --ih: 1800; --cx: 348; --cy: 190; --cw: 2100; --ch: 1180; --dpr: 2; } /* 03  the week, days only */
.shot--ai      { --iw: 1800; --ih: 1640; --cx: 0;   --cy: 330; --cw: 1800; --ch: 1064; --dpr: 2; } /* 09  JobSightAI summary and money */
.shot--list    { --iw: 2426; --ih: 1074; --cx: 0;   --cy: 0;   --cw: 2426; --ch: 1074; --dpr: 2; } /* 02  jobs list, all nine columns */
.shot--week    { --iw: 2880; --ih: 1800; --cx: 0;   --cy: 110; --cw: 2448; --ch: 1145; --dpr: 2; } /* 03  the week, with the menu */
.shot--jobfin  { --iw: 1800; --ih: 1640; --cx: 0;   --cy: 0;   --cw: 1800; --ch: 581;  --dpr: 2; } /* 04  Arriaga 2426, the money strip */
.shot--inv     { --iw: 2000; --ih: 1660; --cx: 0;   --cy: 0;   --cw: 2000; --ch: 1660; --dpr: 2; } /* 07  the Add New Invoice window, whole */
.shot--aistory { --iw: 1800; --ih: 1640; --cx: 0;   --cy: 334; --cw: 1800; --ch: 1160; --dpr: 2; } /* 09b JobSightAI, the lower half */

/* Three frames used to be capped to 910px, the width of the 898px job-panel
   captures, because the homepage's JobSightAI slot, the features page's AI story
   slot and its job-financials slot are all wider than that, and a frame wider
   than its screenshot could only fill itself by enlarging it. The note above
   those caps said they came off the day a 2x capture existed. All three of those
   frames hold an 1800px capture now, so they fill their slots again and the
   composition is back to what it was drawn as: 1129px, 1130px and 875px at a
   1440 screen, which frame-fit reads as 0.627, 0.628 and 0.486 of the file's own
   pixels. */

/* ── a real screen on a phone: the same stage, not a smaller picture ─────────
   OWNER RULING, 5 September 2026. Below 768px a drawn board goes back to its
   own scale and its box becomes a stage a finger drags — the ruling of
   3 September, above. A capture had no such trick: it simply shrank, and the
   homepage board landed 378px wide on a 390px screen, which is a picture of a
   board rather than something a person can read.

   It takes the same stage now. The window goes back to the crop's own pixels
   (capped at 620px tall, so no single frame owns a phone screen), the box
   scrolls on its own with the page's scrolling left alone, and the right edge
   dissolves into the frame's ground until app.js lifts the dissolve at the end
   of the travel — the same `.appbox.is-end` rule above, because a shot box is
   an .appbox. A stage is a stage wherever a reader meets one. */
.shotbox { --stage-fade: 76px; --shot-h: min(calc(var(--ch) / var(--dpr) * 1px), 620px); }
@media (max-width: 767px) {
  .appbox.shotbox {
    aspect-ratio: auto;
    height: var(--shot-h);
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--stage-fade)),
                        rgba(0, 0, 0, 0.5) calc(100% - var(--stage-fade) * 0.42), rgba(0, 0, 0, 0) 100%);
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--stage-fade)),
                rgba(0, 0, 0, 0.5) calc(100% - var(--stage-fade) * 0.42), rgba(0, 0, 0, 0) 100%);
  }
  .shotbox .shotwin { width: auto; height: 100%; aspect-ratio: var(--cw) / var(--ch); }
  .shotbox::-webkit-scrollbar { width: 0; height: 0; }

  /* The first thing in the box is the first thing a reader sees, and the ruling
     of 3 September says that has to be a whole column rather than half of one.
     Two of these captures open on the product's own left menu — 350 file pixels
     of dark chrome and no information — which on a 390px screen is half the
     picture. On a phone the crop starts past it, so the board opens on Lead and
     the week on Sunday. The menu is one drag away, and it is the only thing on
     a phone that any of these six numbers changes.

     The jobs list is not in that list any more. Its 5 September file is cut
     past the menu already (docs/59 §35.9), so it opens on Missing Info at every
     width and needs no override of its own. */
  .shot--board { --cx: 350; --cw: 1760; }   /* 2x: the menu is 350 file pixels wide */
  .shot--week  { --cx: 348; --cw: 2100; }   /* 2x: the menu is 348 file pixels wide */
  /* the demo's two layers take the same override, below, where they are
     declared — a phone rule written up here would be beaten by them. */
}

/* ── the hero frame stopped being a still ────────────────────────────────────
   OWNER RULING, 5 September 2026: "the demo is meant more to be the hero item."
   So the frame at the top of the homepage plays the product demo — the silent
   film made by driving JobSight's own sample company through a scripted tour and
   rendering it a frame at a time. The frame SHELL does not change: same lit top
   lip, same contact, key and ambient cast, same corner radius, same browser bar,
   same fade-out at the bottom, same settle on load and same lift under a
   pointer. Only what is inside the shell moved.

   TWO LAYERS, ONE WINDOW. The window holds a picture and a film, in that order:

     · `img.shot` is the poster, `jobsight-demo-poster.jpg`, 1920 x 970. It is
       what the page paints before the film has a byte, what a reader with
       reduced motion is shown, what a browser with no JavaScript keeps, and what
       every page capture in verification/ shows. It is also the only hero media
       fetched before the loop is asked for.
     · `.demo__v` is the film, on top, transparent until it has data. Its first
       frame is the poster, so the swap has nothing to see.

   THE SIX NUMBERS, TWICE. The set's crop arithmetic (§ above) is written for one
   picture in one window; here it is written twice, once per layer, in ONE
   coordinate space — the 1920 master's. That is what makes the two layers land
   on each other rather than near each other.

     · The poster is already the crop, so its six numbers are an identity:
       1920 x 970, origin 0,0, window 1920 x 970.
     · The film is 1920 x 1200 and carries a band of the app's own paper above
       and below the board — the recorder rendered the app's 1440 x 900 window
       centred on paper. Measured off frame one: the board starts 115 master
       pixels down and is 970 tall. So the film's window is the same 1920 x 970,
       taken at 0,115 out of a 1920 x 1200 picture, and the poster's own pixels
       and the film's own pixels sit on top of each other exactly.

   Because 1200/970 x 970/1920 is 0.625 and 0.625 is 1/1.6, the film is laid out
   at its own aspect ratio by construction: nothing here stretches it.

   --dpr is 4/3, and it is not a guess. The film is rendered from the app's own
   1440 x 900 viewport at double resolution and delivered at 1920 x 1200, so one
   master pixel is 3/4 of a CSS pixel of the screen it is a picture of. Only the
   phone stage reads it. The hero frame is 1324px wide at a 1440 screen against a
   1920-pixel master, so the picture is never enlarged; the widest it is ever
   asked to be is 0.69 of its own pixels. */
.shot--demo { --iw: 1920; --ih: 970; --cx: 0; --cy: 0; --cw: 1920; --ch: 970; --dpr: 1.3333; }
.demo__v {
  --iw: 1920; --ih: 1200; --cx: 0; --cy: 115; --cw: 1920; --ch: 970;
  position: absolute; display: block; border: 0; background: none;
  left:   calc(var(--cx) / var(--cw) * -100%);
  top:    calc(var(--cy) / var(--ch) * -100%);
  width:  calc(var(--iw) / var(--cw) * 100%);
  height: calc(var(--ih) / var(--ch) * 100%);
  max-width: none;
  /* nothing is painted until there is something to paint. A video element with
     no data is transparent in every engine this set is checked in, but "is
     transparent" is not a thing to rely on when the picture underneath is the
     whole hero, so the layer is held at zero and turned on by app.js on the
     element's own loadeddata. It goes back to zero on emptied, which is what
     the parity gate uses to put the frame back to its poster. */
  opacity: 0;
}
.demo__v.is-live { opacity: 1; }

/* On a phone the frame is a stage a finger drags, and the ruling of 3 September
   says the first thing in it has to be a whole column rather than half of one.
   The film opens on the app's own left menu, which is 232 master pixels of dark
   chrome and no information, so below 768px the window starts past it and the
   board opens on Lead. Both layers are written in the master's own pixels, so
   both take the same origin and the same width, and they stay on top of each
   other. This is the same override .shot--board and .shot--week take, written
   here rather than beside them because these two declarations come later in the
   sheet and would otherwise beat it. */
@media (max-width: 767px) {
  .shot--demo, .demo__v { --cx: 232; --cw: 1688; }
}

/* The control under the frame. A silent twenty-second loop is auto-updating
   moving content, so WCAG 2.2.2 asks for a way to stop it and this is that way
   — not an extra, a requirement. It is the same control both ways round: with
   reduced motion on, the loop never starts and this is how a reader who wants it
   starts it; with motion on, the loop is running and this is how it is stopped.
   It carries the quiet weight of the Deck (§8), centred under the frame and
   clear of the hero fade, and app.js reveals it, so a page with no JavaScript
   never shows a control that cannot do anything. */
.demo__act { margin-top: 18px; text-align: center; }
.demo__act .btn--quiet { margin-top: 0; }

/* ── the film at full size ───────────────────────────────────────────────────
   "Watch the demo" opens the whole three-minute film over the page. One
   component, one place, and the plainest one that does the job: a dark ground,
   the film on it at its own 1920 x 1200, a shared player rail, and a
   close control. The captions are printed into the picture, so there is no
   caption track to wire and nothing to fail to load.

   The poster is the same 1920 x 970 file the hero uses, and object-fit: contain
   over the film's own paper colour is what makes it land where frame one lands:
   contained in a 16:10 box, a 1920 x 970 picture sits 115 down and 970 tall,
   which is exactly where the board is in the film. So the poster does not jump
   when the film starts. */
.lb {
  position: fixed; inset: 0; z-index: var(--z-film);
  display: flex; align-items: center; justify-content: center;
  padding: clamp(16px, 4vw, 56px);
}
.lb[hidden] { display: none; }
.lb__back { position: absolute; inset: 0; background: rgba(20, 23, 27, 0.86); }
/* the box is bounded by the screen's height as well as its width, so a short
   window shows a smaller film rather than a film with its feet cut off. 1.6 is
   the film's own aspect ratio, so the bound is written once and holds. */
.lb__box { position: relative; width: min(1200px, 100%, calc((100vh - 64px - 2 * clamp(16px, 4vw, 56px)) * 1.6)); }
@supports (height: 100dvh) {
  .lb__box { width: min(1200px, 100%, calc((100dvh - 64px - 2 * clamp(16px, 4vw, 56px)) * 1.6)); }
}
.lb__v {
  display: block; width: 100%; aspect-ratio: 1920 / 1200;
  object-fit: contain; background: #fcfaf8;
  border-radius: 14px; outline: none;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4), 0 30px 60px -18px rgba(0, 0, 0, 0.55);
}
/* the close control is a Deck at the small size, with the label spoken rather
   than printed: two bars crossed, drawn rather than typed, so no glyph has to
   be trusted to centre itself. */
.lb__x {
  position: absolute; top: -14px; right: -14px;
  width: 44px; height: 44px; padding: 0; border: 0; border-radius: 12px; cursor: pointer;
  color: var(--ink-deep);
  background-image: linear-gradient(180deg, #ffffff 0%, #fdfbf8 55%, #f4efe8 100%);
  box-shadow: inset 0 1px 0 #fff, inset 0 0 0 1px rgba(34, 38, 43, 0.14),
    0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 18px -6px rgba(0, 0, 0, 0.4);
  transition: transform 0.34s var(--ease), box-shadow 0.34s var(--ease);
}
.lb__x span { position: absolute; inset: 0; }
.lb__x span::before, .lb__x span::after {
  content: ""; position: absolute; left: 13px; top: 21px; width: 18px; height: 2px;
  border-radius: 1px; background: currentColor;
}
.lb__x span::before { transform: rotate(45deg); }
.lb__x span::after { transform: rotate(-45deg); }
@media (hover: hover) and (pointer: fine) {
  .lb__x:hover { transform: translateY(-2px); }
}
.lb__x:active { transform: translateY(1px); }
.lb__x:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
@media (max-width: 767px) {
  .lb__x { top: -10px; right: -10px; }
}

/* Shared full-film controls. The rail sits below the picture, clear of burned
   captions, and borrows the close control's paper, lip and cast. No new motion. */
.player { position: relative; min-width: 0; }
.player__controls {
  display: flex; align-items: center; gap: 12px; min-height: 64px;
  padding: 10px 12px; border-radius: 0 0 14px 14px;
  color: var(--ink-deep); background: #fcfaf8;
  box-shadow: inset 0 1px 0 #fff, 0 3px 6px rgba(34, 38, 43, 0.12);
}
.player__controls[hidden], .player__status[hidden] { display: none; }
.player__button {
  display: grid; place-items: center; flex: 0 0 44px;
  width: 44px; height: 44px; padding: 0; border: 0; border-radius: 12px;
  color: var(--ink-deep); cursor: pointer;
  background: linear-gradient(180deg, #fff 0%, #fdfbf8 55%, #f4efe8 100%);
  box-shadow: inset 0 1px 0 #fff, inset 0 0 0 1px rgba(34, 38, 43, 0.18),
    0 1px 2px rgba(34, 38, 43, 0.16), 0 5px 10px -5px rgba(34, 38, 43, 0.24);
}
.player__button svg { width: 22px; height: 22px; fill: currentColor; }
.player__button[data-player-fullscreen] svg { fill: none; stroke: currentColor; stroke-width: 2; }
.player__button:active { background: #ece4d9; box-shadow: inset 0 1px 3px rgba(34, 38, 43, 0.24); }
.player__button:disabled { opacity: 0.5; cursor: default; }
.player__seek {
  appearance: none; -webkit-appearance: none; flex: 1; min-width: 32px;
  width: 100%; height: 44px; margin: 0; padding: 0; cursor: pointer;
  background: transparent; color: #b45309;
}
.player__seek::-webkit-slider-runnable-track {
  height: 6px; border-radius: 3px;
  background: linear-gradient(to right, #b45309 var(--played, 0%), #80766a var(--played, 0%));
}
.player__seek::-moz-range-track { height: 6px; border-radius: 3px; background: #80766a; }
.player__seek::-moz-range-progress { height: 6px; border-radius: 3px; background: #b45309; }
.player__seek::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none; width: 18px; height: 18px;
  margin-top: -6px; border: 2px solid #b45309; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(34, 38, 43, 0.3);
}
.player__seek::-moz-range-thumb {
  width: 14px; height: 14px; border: 2px solid #b45309; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(34, 38, 43, 0.3);
}
.player__seek:disabled { opacity: 0.5; cursor: default; }
.player__button:focus-visible, .player__seek:focus-visible { outline: 2px solid #b45309; outline-offset: 3px; }
.player__time { flex: 0 0 auto; font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.player__status { margin: 0; padding: 12px; color: var(--ink-deep); background: #fcfaf8; font: 14px/1.5 var(--body); }
.player:fullscreen { display: flex; flex-direction: column; justify-content: center; width: 100%; height: 100%; background: #14171b; }
.player:fullscreen video { min-height: 0; max-height: calc(100% - 64px); object-fit: contain; box-shadow: none; }
.player:fullscreen .player__controls { flex: 0 0 auto; }
@media (max-width: 767px) {
  .player__controls { gap: 8px; padding-inline: 8px; }
}

/* ── the one frame that is meant to run off the page ─────────────────────────
   The role page's jobs list bleeds off the right edge on purpose; that is the
   desk composition's own idea and the ruling of 4 September leaves it alone
   (§32). Its box is 709px at a 1440 screen, and a 2426px capture shrunk into
   that would be 0.29 of its own pixels — unreadable. So this one is sized by
   its height and clipped by the frame, exactly as the drawn list was: the
   first columns whole, the rest running past the edge.
   Below 1440, where the ruling does allow a change, the clip becomes the
   stage, so nothing is left unreachable. */
.appbox.shotbox--bleed { aspect-ratio: auto; height: calc(var(--ch) / var(--dpr) * 1px); }
.shotbox--bleed .shotwin { width: auto; height: 100%; aspect-ratio: var(--cw) / var(--ch); }
@media (max-width: 1439px) {
  .appbox.shotbox--bleed {
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--stage-fade)),
                        rgba(0, 0, 0, 0.5) calc(100% - var(--stage-fade) * 0.42), rgba(0, 0, 0, 0) 100%);
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--stage-fade)),
                rgba(0, 0, 0, 0.5) calc(100% - var(--stage-fade) * 0.42), rgba(0, 0, 0, 0) 100%);
  }
  .shotbox--bleed::-webkit-scrollbar { width: 0; height: 0; }
  .shotbox--bleed.is-end { -webkit-mask-image: none; mask-image: none; }
}

/* ── the phone app, from JobSight's own App Store listing ───────────────────
   OWNER RULING, 5 September 2026: the drawn phone frames "looked low quality
   on both sides"; every product frame is to show the real product.

   The five phone screens could not be shot the way the nine desktop screens
   were — a desktop browser cannot open the iOS app — so they come from the
   client's own public App Store listing (JobSight Restoration Software,
   id1641802388), fetched 5 September 2026 and kept at
   verification/direction-02/demo-account/appstore/ with the URL each one came
   from. Those files are marketing composites: a headline, a coloured ground and
   an iPhone mockup with the real app screen inside it. Only the screen is used.
   The headline, the ground, the hand, the bezel and the mockup's own dynamic
   island are cropped away before the file reaches this folder; the crop box for
   each is written down in assets/real/app/MANIFEST.md.

   From here they behave exactly like the desktop captures: the same six numbers,
   the same arithmetic window, the same 1:1 ceiling, and `frame-fit.mjs` measures
   them because a phone screen is an `.appbox.shotbox` too. */
.shot--ph-clock   { --iw: 998; --ih: 2091; --cx: 0; --cy: 0;   --cw: 998; --ch: 2091; } /* iphone-06  Time Clock, whole screen */
.shot--ph-jobs    { --iw: 998; --ih: 2091; --cx: 0; --cy: 38;  --cw: 998; --ch: 2053; } /* iphone-02  Jobs, from the search bar down */
.shot--ph-receipt { --iw: 998; --ih: 2076; --cx: 0; --cy: 0;   --cw: 998; --ch: 2076; --ph-bg: #acacac; } /* iphone-08  Capture Receipt, whole screen, dimmed behind its scan */
.shot--ph-day     { --iw: 998; --ih: 2091; --cx: 0; --cy: 42;  --cw: 998; --ch: 978;  } /* iphone-09  Schedule, the date bar to the midday call */
.shot--ph-photos  { --iw: 998; --ih: 2091; --cx: 0; --cy: 344; --cw: 998; --ch: 504;  } /* iphone-04  Photos, the two capture buttons and the first row */

/* A phone capture does not need the stage the desktop captures take below
   768px, and giving it one would be worse than not. The stage exists because a
   1481px board fitted into a 390px screen lands at 0.31 of its own pixels and
   stops being readable (§33.3). These files are 998px wide for a screen the
   phone itself draws at 430 CSS pixels, so a 350px-wide frame on a 390px screen
   still shows the app at 0.81 of life size. It fits, so it is fitted. */
@media (max-width: 767px) {
  .appbox.shotbox--phone {
    aspect-ratio: var(--cw) / var(--ch); height: auto; overflow-x: hidden;
    -webkit-mask-image: none; mask-image: none;
  }
  .shotbox--phone .shotwin { width: 100%; height: 100%; aspect-ratio: auto; }
}

/* The shell is unchanged — same lit lip, same cast, same corner, same lift
   under a pointer. Inside it the screen loses the frame border a shot box
   carries, because the bezel is already the border, and it takes the capture's
   own top colour rather than the drawn screens' warm grey.

   THE STATUS BAR STAYS DRAWN, and it has to. The App Store composites cover the
   top of the screen with the mockup's own dynamic island, so the crop has to
   start below it — and the island is device art, not the product, so it does
   not go on the page. The shell's own status bar, which is the phone's
   furniture and not the product's, sits above the capture exactly as it did
   above the drawings and gives the app's first row a straight edge to start on.

   TWO CAPTURES ALSO GIVE UP THEIR TITLE ROW. On the jobs screen and the
   schedule the app's own title row runs up underneath the island — the jobs
   hamburger starts four pixels above where the island ends — so a crop that
   keeps the whole row is not available and a crop that keeps most of it shows a
   hamburger with two and a half bars. Both start below the row instead: the
   jobs screen opens on `Search Jobs…` and the schedule on `Fri 7/24/2026`, each
   of which says what the screen is, and both keep the app's tab bar at the
   foot. The other three screens carry their title rows whole.

   The screen is no longer a fixed 100 / 208 either. It is the status bar plus
   the capture's own shape, so nothing is stretched and no crop has to be chosen
   to fit a ratio that was drawn before the pictures existed. */
.phone__screen.shotbox {
  aspect-ratio: auto; height: auto;
  border: 0; box-shadow: none; background: var(--ph-bg, #fff);
}
.phone__screen.shotbox .shotwin { height: auto; aspect-ratio: var(--cw) / var(--ch); }

/* A phone capture is never shown wider than a phone. The set's own phone
   shells stop at 336px, and a screen the phone itself draws at 430 CSS pixels
   looks like a phone held at arm's length at that width and like a poster of a
   phone at 600. So the two frames that are not inside a shell — the
   field-capture card and the field technician's tab — stop at the same place
   and sit at the left of their column, exactly as the third callout card
   already does at 1179 and below. The crops are then written so both frames
   land on the height their neighbours already have: the card matches the job
   costing card beside it, and the tab matches the operations tab's own frame,
   so switching tabs still does not move the page. */
.minif--field  { max-width: 348px; }
.frame--phone  { max-width: 348px; }

/* The field-capture card hands its whole body to the screen. */
.minif__body--shot { display: block; padding: 0; min-height: 0; }
.minif__body--shot .shotbox { border-radius: 0; border-width: 1px 0 0; }

/* chips and check boxes — the drawn app's small parts, still used by the drawn
   frames that remain on role.html and features.html */
.chip { background: #eef2f5; color: #4b5a68; border-radius: 4px; padding: 3px 7px; font-size: 10px; white-space: nowrap; }
.chip--out { border: 1px solid #d7dee4; background: #fff; display: inline-flex; align-items: center; gap: 5px; }

/* icons — 1.25px strokes drawn from currentColor, no icon library */
.ic { width: 12px; height: 12px; flex: 0 0 12px; display: inline-block; opacity: 0.85; background: currentColor; }
.ic-cal  { -webkit-mask: var(--m-cal) center/contain no-repeat;  mask: var(--m-cal) center/contain no-repeat; }
.ic-tools{ -webkit-mask: var(--m-tools) center/contain no-repeat;mask: var(--m-tools) center/contain no-repeat; }
.ic-doc  { -webkit-mask: var(--m-doc) center/contain no-repeat;  mask: var(--m-doc) center/contain no-repeat; }
.ic-clock{ -webkit-mask: var(--m-clock) center/contain no-repeat;mask: var(--m-clock) center/contain no-repeat; }
.ic-menu { -webkit-mask: var(--m-menu) center/contain no-repeat;  mask: var(--m-menu) center/contain no-repeat; }
.ic-phone{ -webkit-mask: var(--m-phone) center/contain no-repeat; mask: var(--m-phone) center/contain no-repeat; }
.ic-list { -webkit-mask: var(--m-list) center/contain no-repeat;  mask: var(--m-list) center/contain no-repeat; }
.ic-chat { -webkit-mask: var(--m-chat) center/contain no-repeat;  mask: var(--m-chat) center/contain no-repeat; }
:root {
  --m-cal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.3'%3E%3Crect x='2' y='3' width='12' height='11' rx='1.5'/%3E%3Cpath d='M2 6.5h12M5.5 1.6v2.6M10.5 1.6v2.6'/%3E%3C/svg%3E");
  --m-tools: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.3'%3E%3Cpath d='M10.6 2.2a3.2 3.2 0 0 0-3 5.2L2.6 12.4l1.4 1.4 5-5a3.2 3.2 0 0 0 4.1-4.3l-2 2-1.6-1.6z'/%3E%3C/svg%3E");
  --m-doc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.3'%3E%3Cpath d='M3.4 2h6l3.2 3.2V14H3.4z'/%3E%3Cpath d='M9.2 2v3.4h3.2M5.6 8.6h5M5.6 11h4'/%3E%3C/svg%3E");
  --m-clock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.3'%3E%3Ccircle cx='8' cy='8' r='6.2'/%3E%3Cpath d='M8 4.4V8l2.6 1.6'/%3E%3C/svg%3E");
  --m-menu: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.5'%3E%3Cpath d='M2.4 4.4h11.2M2.4 8h11.2M2.4 11.6h11.2'/%3E%3C/svg%3E");
  --m-phone: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.3'%3E%3Cpath d='M3 3.4c0-.8.7-1.4 1.4-1.4h1.4l1.2 2.8-1.4 1c.7 1.6 2 2.9 3.6 3.6l1-1.4 2.8 1.2v1.4c0 .8-.6 1.4-1.4 1.4A9.6 9.6 0 0 1 3 3.4z'/%3E%3C/svg%3E");
  --m-list: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.3'%3E%3Cpath d='M6 4.2h8M6 8h8M6 11.8h8M2.4 4.2h.8M2.4 8h.8M2.4 11.8h.8'/%3E%3C/svg%3E");
  --m-chat: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.3'%3E%3Cpath d='M2.4 3.4h11.2v7.2H7.6L4.4 13.4v-2.8H2.4z'/%3E%3C/svg%3E");
}

/* ── works with ──────────────────────────────────────────────────────────── */
/* ROUND 4. §21 item 5 and §27 item 4 both named this the flattest band in the
   set, and round 3's answer — putting it on a lit ground — was still not a
   composition. It is a shelf now. The ground arrives from nothing at the top so
   the hero's light still bleeds down onto it, holds the set's cool surface
   through the middle, and leaves from nothing at the foot, so the band has a
   body without gaining two hard edges. Under the row of names is the page's own
   rule-with-a-lit-segment — the same device the docket's head and the ledger's
   key carry — so the strip joins the page's grammar instead of floating in the
   middle of it. The lit stop is 0.94 over paper, which lands above #eff1ee, the
   darkest ground round 3 measured --ink-mute safe on. */
.works {
  position: relative; z-index: 2;
  padding-block: clamp(20px, 3.4vw, 48px) clamp(56px, 7vw, 96px);
  background-image: linear-gradient(180deg,
    rgba(239, 241, 238, 0) 0%, rgba(239, 241, 238, 0.94) 34%,
    rgba(239, 241, 238, 0.94) 72%, rgba(239, 241, 238, 0) 100%);
}
.works__in {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 22px;
  padding-bottom: clamp(20px, 2.2vw, 28px);
}
.works__in::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--rule);
}
.works__in::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 56px; height: 3px; background: var(--orange);
}
.works__lab { font-size: 0.875rem; color: var(--ink-mute); }
.works__list {
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
  align-items: center; gap: 24px clamp(18px, 3vw, 44px); width: 100%;
  padding: 12px 0;
}
.works__list li { display: flex; align-items: center; justify-content: center; min-width: 0; }
.works__list img { display: block; width: 100%; height: 42px; max-width: 162px; object-fit: contain; }

/* ── one job, one record ─────────────────────────────────────────────────── */
.record { overflow-x: clip; padding-block: clamp(72px, 9vw, 128px) clamp(76px, 9vw, 120px); }
.open { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); gap: clamp(24px, 5vw, 76px); align-items: end; }
.open--tight { grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr); }
.open__h { text-wrap: initial; }
.open__lead { font-size: var(--lead); line-height: 1.58; color: var(--ink-soft); max-width: 46ch; padding-bottom: 0.35em; }

.three {
  margin-top: clamp(48px, 6vw, 84px);
  display: grid; grid-template-columns: 1.06fr 1fr 1.1fr;
  gap: clamp(28px, 4vw, 56px);
}
.cal { position: relative; padding-left: clamp(20px, 2.4vw, 34px); }
.cal:first-child { padding-left: 0; }
.cal + .cal::before {
  content: ""; position: absolute; left: 0; top: 4px; bottom: 18%; width: 1px;
  background: linear-gradient(180deg, rgba(34, 38, 43, 0.20), rgba(34, 38, 43, 0));
}
.cal h3 { margin-top: 26px; }
@media (min-width: 1101px) {
  .cal:nth-child(2) { padding-top: 34px; }
  .cal:nth-child(3) { padding-top: 14px; }
}
.cal p { margin-top: 10px; font-size: 1rem; line-height: 1.6; color: var(--ink-soft); max-width: 34ch; }

/* mini frames inside the three callouts */
.minif {
  border-radius: 12px; overflow: hidden;
  background: linear-gradient(180deg, #ffffff 0%, #fcfbf9 100%);
  border: 1px solid rgba(34, 38, 43, 0.10);
  box-shadow: var(--edge-soft), var(--elev-2);
  transition: transform 0.7s var(--ease), box-shadow 0.7s var(--ease);
}
/* gated with the rest of the lifts; the shared three-pixel rule supersedes it */
@media (hover: hover) and (pointer: fine) {
  .cal:hover .minif { transform: translateY(-6px); box-shadow: var(--edge), var(--elev-3); }
}
.minif__bar {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 11px; background: linear-gradient(180deg, #fbfaf8, #f2efe9);
  border-bottom: 1px solid rgba(34, 38, 43, 0.08);
  font-size: 11px; font-weight: 600; color: #4d565f;
}
.minif__live { color: #8f4408; background: rgba(245, 130, 32, 0.16); border-radius: 4px; padding: 1px 6px; font-size: 9.5px; }
.minif__body { padding: 12px; display: grid; gap: 9px; min-height: 142px; align-content: start; }
.mrow { display: flex; align-items: center; gap: 8px; font-size: 11px; color: #55606a; }
.mrow--split { justify-content: space-between; padding: 7px 9px; background: #f7f8f9; border-radius: 6px; }
.mrow--split b { font-size: 12px; color: #2f3a45; }
.mrow--sub { justify-content: space-between; font-size: 10px; color: #5f6970; }
.mrow__t { font-weight: 600; color: #2f3a45; }
.mrow__m { margin-left: auto; font-size: 9.5px; color: #5f6970; background: #f0f2f4; border-radius: 4px; padding: 1px 5px; }
.ok { color: #1f6b4a; font-weight: 600; }
.mbar { height: 5px; border-radius: 3px; background: #eceef0; overflow: hidden; }
.mbar i { display: block; height: 100%; width: var(--w, 100%); background: linear-gradient(90deg, #fbb267, #f58220); }
.mbtn {
  justify-self: start; background: var(--orange); color: var(--ink-deep);
  font-size: 11px; font-weight: 700; padding: 6px 12px; border-radius: 7px;
  box-shadow: 0 2px 6px -2px rgba(184, 90, 12, 0.5);
}
.bigpct {
  font-family: var(--jobsight-display); font-size: 34px; letter-spacing: -0.035em; line-height: 1;
  color: var(--ink); display: flex; align-items: baseline; gap: 9px;
}
.bigpct span { font-family: var(--body); font-size: 10.5px; letter-spacing: 0; color: #5f6970; }
.cchart { display: grid; gap: 5px; }
.cchart__b { display: flex; align-items: flex-end; gap: 8px; height: 48px; border-bottom: 1px solid #e6e9eb; }
.cchart__b i { flex: 1; height: var(--w); background: #ccd6de; border-radius: 3px 3px 0 0; }
.cchart__b i.on { background: linear-gradient(180deg, #fbb267, #f58220); }
.cchart__x { display: flex; gap: 8px; }
.cchart__x span { flex: 1; text-align: center; font-size: 9.5px; color: #5f6970; letter-spacing: 0.02em; }

/* ── margins band ────────────────────────────────────────────────────────── */
.margins {
  position: relative; overflow: hidden;
  background: linear-gradient(180deg, var(--paper) 0%, var(--paper-warm) 22%, var(--paper-warm) 78%, var(--paper) 100%);
  padding-block: clamp(84px, 11vw, 152px);
}
.margins__grid {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  gap: clamp(36px, 5vw, 76px); align-items: center;
}
.margins__copy p { margin-top: 22px; font-size: var(--lead); line-height: 1.58; color: var(--ink-soft); max-width: 42ch; }
.ticks { margin-top: 26px; display: grid; gap: 12px; }
.ticks li { position: relative; padding-left: 30px; font-size: 1rem; color: var(--ink); }
.ticks li::before {
  content: ""; position: absolute; left: 0; top: 0.44em; width: 15px; height: 9px;
  border: solid var(--orange-text); border-width: 0 0 2px 2px; transform: rotate(-45deg);
  border-radius: 1px;
}
.margins__panel { position: relative; }

/* costing panel */

.wip {
  position: absolute; right: -34px; bottom: -72px; width: 236px;
  background: linear-gradient(180deg, #ffffff 0%, #fbf9f7 100%);
  border: 1px solid rgba(34, 38, 43, 0.09); border-radius: 12px; padding: 14px 16px;
  box-shadow: var(--edge), var(--elev-3); z-index: 2;
}
.wip__k { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #5f6970; margin-bottom: 9px; }
.wip__r { display: flex; justify-content: space-between; align-items: baseline; padding: 5px 0; font-size: 12.5px; color: #5b636c; border-top: 1px solid #f1f2f3; }
.wip__r:first-of-type { border-top: 0; }
.wip__r b { color: #2f3a45; font-variant-numeric: tabular-nums; }
.wip__r--warn b { color: #8f4408; }

/* ── client-approved customer voices ────────────────────────────────────── */
.voices { padding-block: clamp(72px, 8vw, 112px); background: var(--paper-warm); }
.voices__head { max-width: 760px; }
.voices__eyebrow { font-size: .875rem; color: var(--ink-soft); }
.voices__head h2 { margin-top: 18px; }
.voices__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(24px, 3.5vw, 48px); margin-top: clamp(36px, 4vw, 56px); }
.voice { display: flex; flex-direction: column; min-width: 0; padding-top: 24px; border-top: 1px solid var(--rule); }
.voice__rating { color: var(--orange-text); font-size: 1.25rem; letter-spacing: .12em; }
.voice blockquote { margin: 20px 0 28px; font-size: 1rem; line-height: 1.65; color: var(--ink); }
.voice__person { display: flex; align-items: center; gap: 14px; margin-top: auto; }
.voice__person img { display: block; width: 60px; height: 60px; border-radius: 50%; flex: 0 0 60px; }
.voice__person strong { display: block; font-size: .9375rem; }
.voice__person > span > span { display: block; margin-top: 4px; font-size: .8125rem; line-height: 1.4; color: var(--ink-soft); }
.ftr__assurance { display: flex; align-items: center; gap: 12px; margin-top: 22px; color: var(--ink-soft); font-size: .875rem; }
.ftr__assurance img { display: block; width: 86px; height: 86px; }
@media (max-width: 760px) {
  .works__list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 22px 24px; }
  .works__lab { text-align: center; }
  .voices__grid { grid-template-columns: 1fr; gap: 32px; }
  .voice blockquote { max-width: 58ch; }
}
@media (max-width: 420px) {
  .works__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hdr__brand img { height: 36px; }
  .field__art--photos { padding-left: 24px; padding-bottom: 40px; }
}

/* ── field / phone band ──────────────────────────────────────────────────── */
.field { position: relative; overflow-x: clip; padding-block: clamp(96px, 11vw, 148px) clamp(80px, 10vw, 128px); }
.field__grid {
  display: grid; grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
  gap: clamp(40px, 6vw, 92px); align-items: center;
}
.field__art { position: relative; min-height: 520px; display: flex; align-items: center; justify-content: center; }
.field__copy > p:not(.field__pull) { margin-top: 20px; font-size: var(--lead); line-height: 1.58; color: var(--ink-soft); max-width: 44ch; }
.field__pull {
  position: relative; margin-top: 30px; text-indent: -0.44em;
  font-family: var(--jobsight-display); font-size: clamp(1.2rem, 1rem + 0.72vw, 1.62rem);
  line-height: 1.3; letter-spacing: -0.022em; color: var(--ink); max-width: 26ch;
}
.field__pull::after {
  content: ""; display: block; width: 34px; height: 2px; margin-top: 22px;
  background: var(--orange); border-radius: 2px;
}

.field__art--photos { display: block; min-height: 0; padding: 0 0 56px 46px; }
.field__photo { position: relative; overflow: hidden; border-radius: 14px; box-shadow: var(--elev-3); }
.field__photo img { display: block; width: 100%; height: auto; }
.field__photo--context { position: absolute; width: 38%; left: 0; bottom: 0; border: 5px solid var(--paper); transform: rotate(-4deg); box-shadow: var(--elev-2); }

.phones { position: relative; width: max-content; }
.phone { --pw: clamp(268px, 23vw, 310px); position: relative; }
.phone--front { z-index: 2; }
.phone--back {
  position: absolute; left: 0; top: 50%; z-index: 1;
  transform: translate(-54%, -50%) rotate(-5deg) scale(0.84);
  transform-origin: center;
}
.phone--back .phone__screen { filter: saturate(0.94); }
/* one phone on its own, rather than the homepage's overlapped pair. Shared,
   because more than one page uses it (role.html and features.html). */
.phone--solo { --pw: clamp(272px, 25vw, 336px); }
.phone__shell {
  width: var(--pw); font-size: calc(var(--pw) / 23);
  border-radius: 2.6em; padding: 0.34em;
  background: linear-gradient(160deg, #3c444d 0%, #1e242b 46%, #333b43 100%);
  box-shadow: var(--elev-4), var(--edge-soft), inset 0 0 0 1px rgba(255, 255, 255, 0.10);
}
.phone--back .phone__shell { box-shadow: var(--elev-3); }
.phone__screen {
  position: relative; border-radius: 2.3em; overflow: hidden; background: #f4f2ee;
  aspect-ratio: 100 / 208; display: flex; flex-direction: column;
}
.ph__top {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.75em 1.3em 0.2em; font-size: 0.72em; font-weight: 700; color: #2f3a45;
}
.ph__sig { display: flex; align-items: flex-end; gap: 0.16em; }
.ph__sig i { width: 0.22em; background: #2f3a45; border-radius: 0.1em; }
.ph__sig i:nth-child(1) { height: 0.3em; }
.ph__sig i:nth-child(2) { height: 0.46em; }
.ph__sig i:nth-child(3) { height: 0.62em; }
.ph__nav {
  display: flex; align-items: center; gap: 0.7em; padding: 0.5em 1em 0.7em; background: #fff;
  font-size: 0.98em; font-weight: 700; color: #232b33;
}
.ph__nav .ic { width: 1em; height: 1em; flex: 0 0 1em; }
.ph__nav span:nth-of-type(1) { margin-inline: auto; }
.pill {
  display: inline-block; margin-top: 0.4em; font-size: 0.68em; font-weight: 700;
  color: #2b1a08; background: var(--orange); border-radius: 0.5em; padding: 0.24em 0.66em;
}
.pill--lead { background: rgba(245, 130, 32, 0.18); color: #8f4408; }
.ph__tabs {
  position: relative; margin: 0.6em 0.8em 0.7em; padding: 0.5em 1em; background: #fff;
  border-radius: 1.6em; display: flex; justify-content: space-between; align-items: center;
  box-shadow: 0 2px 8px rgba(28, 32, 38, 0.10);
}
.ph__tabs .ic { width: 1.05em; height: 1.05em; flex: 0 0 1.05em; color: #6d7780; }
.ph__tabs .ic.is-on { color: var(--orange); }
.ph__badge {
  position: absolute; right: 2.4em; top: 0.1em; font-size: 0.68em; font-weight: 700;
  background: #c1402c; color: #fff; border-radius: 1em; padding: 0.14em 0.44em;
}

/* ── roles ───────────────────────────────────────────────────────────────── */
.roles { overflow-x: clip; padding-block: clamp(80px, 10vw, 144px); background: var(--surf-paper); }
.roles__grid {
  margin-top: clamp(40px, 5vw, 68px);
  display: grid; grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
  gap: clamp(32px, 4vw, 64px); align-items: start;
}
.roles__list { display: grid; border-top: 1px solid var(--rule-soft); }
.rtab {
  display: flex; align-items: baseline; gap: 14px; width: 100%; text-align: left; cursor: pointer;
  padding: clamp(16px, 1.8vw, 22px) 4px; border-bottom: 1px solid var(--rule-soft);
  transition: padding-left 0.5s var(--ease), color 0.35s var(--ease);
}
.rtab__n {
  font-family: var(--jobsight-display); font-size: clamp(1.35rem, 1.05rem + 1.1vw, 2rem);
  letter-spacing: -0.03em; line-height: 1.06; color: var(--ink-mute);
  transition: color 0.35s var(--ease);
}
.rtab__s { margin-left: auto; font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: #6a727b; transition: color 0.35s var(--ease); }
.rtab:hover .rtab__n { color: var(--ink); }
.rtab.is-on { padding-left: 18px; }
.rtab.is-on .rtab__n { color: var(--ink); }
.rtab.is-on .rtab__s { color: var(--orange-text); }
.rtab.is-on { position: relative; }
.rtab.is-on::before {
  content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 3px; height: 46%; border-radius: 2px; background: var(--orange);
}

.roles__panels { position: relative; }
@media (min-width: 981px) { .roles__panels { min-height: 448px; } }
.rpanel[hidden] { display: none; }
.rpanel__line { font-size: var(--lead); line-height: 1.55; color: var(--ink-soft); max-width: 52ch; margin-bottom: 26px; }
.mini { background: linear-gradient(180deg, #ffffff 0%, #fcfbf9 100%); border-radius: 11px;
  border: 1px solid rgba(34, 38, 43, 0.08); overflow: hidden;
  box-shadow: var(--edge-soft), var(--elev-1); }
.mini__h {
  display: flex; align-items: center; justify-content: space-between; padding: 11px 15px;
  background: linear-gradient(180deg, #fdfcfb, #f7f6f3); border-bottom: 1px solid #eeeff1;
  font-size: 12.5px; font-weight: 700; color: #38424c;
}
.mini__k { font-weight: 600; font-size: 11px; color: #5f6970; background: #f2f3f5; border-radius: 5px; padding: 2px 8px; }
.mini__k--ok { color: #1f6b4a; background: rgba(45, 138, 96, 0.12); }
.mini__f { display: flex; justify-content: space-between; align-items: baseline; padding: 12px 15px; background: #fafafa; border-top: 1px solid #eeeff1; font-size: 12px; color: #6b737c; }
.mini__f b { font-size: 15px; color: #2f3a45; }
.sched { padding: 12px 15px; display: grid; gap: 9px; }
.srow { display: flex; align-items: center; gap: 12px; }
.srow__n { flex: 0 0 76px; font-size: 11.5px; color: #5b636c; }
.sbar {
  height: 24px; border-radius: 5px; margin-left: var(--s); width: var(--w);
  background: linear-gradient(90deg, #fbb267, var(--orange)); color: #2b1a08;
  font-size: 10.5px; font-weight: 700; display: flex; align-items: center; padding-inline: 8px;
  white-space: nowrap; overflow: hidden;
}
.sbar--2 { background: linear-gradient(90deg, #46606f, #587284); color: #fff; }
.sbar--3 { background: linear-gradient(90deg, #b0bcc6, #93a2b0); color: #1b242c; }
.sbar--4 { background: linear-gradient(90deg, #d8dee4, #b9c3cb); color: #38424c; }
.ltbl { font-size: 12px; }
.ltbl tr { border-bottom: 1px solid #f1f2f4; }
.ltbl tr:last-child { border-bottom: 0; }
.ltbl th, .ltbl td { padding: 10px 8px; text-align: left; font-weight: 400; color: #5b636c; }
.ltbl th { padding-left: 15px; font-weight: 600; color: #2f3a45; }
.ltbl .num { text-align: right; font-variant-numeric: tabular-nums; color: #2f3a45; font-weight: 600; }
.ltbl .st { padding-right: 15px; text-align: right; font-size: 11px; color: #5f6970; }
.st--ok { color: #1f6b4a; }
.st--warn { color: #8f4408; }
/* Round 2 — the role panels stop being one table five times (§5.2). Five
   shapes now, no shape twice: Operations a day schedule, Finance a flow,
   Field technicians a checklist, Project managers the one table, Business
   development a visit calendar. */

/* Finance — money follows the job along one line. The rail and the node are
   the same device as the buttons and the nav, at product scale. */
.flow { padding: 13px 15px 10px; }
.flow__rail { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); align-items: center; height: 12px; }
.flow__rail::before {
  content: ""; position: absolute; left: 12.5%; right: 12.5%; top: 50%; height: 2.5px; margin-top: -1.25px;
  background: linear-gradient(90deg, var(--orange), rgba(245, 130, 32, 0.4)); border-radius: 2px;
}
.flow__rail i {
  justify-self: center; position: relative; width: 11px; height: 11px; border-radius: 50%;
  background: var(--orange); box-shadow: 0 0 0 3px #fff;
}
.flow__cols { display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; margin-top: 10px; }
.flow__c { display: grid; gap: 2px; text-align: center; }
.flow__c b { font-size: 11.5px; font-weight: 600; color: #2f3a45; }
.flow__c span { font-size: 9.5px; color: #6b737c; }
.flow__c em { font-style: normal; font-size: 10.5px; color: #5b636c; font-variant-numeric: tabular-nums; }
.flow__split {
  display: flex; justify-content: space-between; padding: 7px 15px;
  background: #fafafa; border-block: 1px solid #eeeff1; font-size: 10px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: #6b737c;
}
.taskrows--tight { padding-block: 4px; }
.taskrows--tight .tr { padding-block: 6px; }

/* Business development — where the driving hours go, not another ledger. */
.vcal { padding: 9px 15px 7px; }
.vcal__d { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; }
.vcal__d span { font-size: 9.5px; font-weight: 600; color: #8a929a; text-align: center; }
.vcal__g { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-top: 3px; }
.vcal__c {
  position: relative; min-height: 32px; border: 1px solid #eef0f2; border-radius: 6px;
  padding: 4px 0 0; text-align: center;
}
.vcal__c b { font-size: 10.5px; font-weight: 500; color: #6b737c; font-variant-numeric: tabular-nums; }
.vcal__c u {
  position: absolute; left: 6px; right: 6px; bottom: 5px; height: 2.5px;
  border-radius: 2px; background: var(--orange);
}
.vcal__c--past { background: #fafbfb; }
.vcal__c--past b { color: #a2a9b0; }
.vcal__c--we { background: #f7f8f9; border-color: #f0f2f4; }
.vcal__c--today { border-color: rgba(52, 73, 94, 0.4); background: rgba(52, 73, 94, 0.05); }
.vcal__c--today b { color: #2f3a45; font-weight: 700; }
.vcal__c--v { border-color: rgba(245, 130, 32, 0.42); background: rgba(245, 130, 32, 0.07); }
.vcal__c--v b { color: #2f3a45; font-weight: 600; }

.taskrows { padding: 12px 15px; display: grid; gap: 2px; }
.tr { display: flex; align-items: center; gap: 10px; padding: 8px 0; font-size: 12px; color: #4f5862; border-bottom: 1px solid #f4f5f6; }
.tr:last-child { border-bottom: 0; }
.tr span { margin-left: auto; font-size: 11px; color: #5f6970; }
.tr i { width: 13px; height: 13px; border-radius: 50%; border: 1.5px solid #ccd3d9; flex: 0 0 13px; }
.tr--done i { background: #2d8a60; border-color: #2d8a60; position: relative; }
.tr--done i::after { content: ""; position: absolute; left: 4px; top: 1px; width: 3px; height: 6px; border: solid #fff; border-width: 0 1.5px 1.5px 0; transform: rotate(42deg); }
.tr--now i { border-color: var(--orange); box-shadow: 0 0 0 3px rgba(245, 130, 32, 0.16); }
.tr--now { color: #2f3a45; font-weight: 600; }
.tr--late { color: #8f4408; }
.tr--late i { border-color: #c07a35; border-style: dashed; }
.tr--late span { color: #8f4408; }

/* ── AI band ─────────────────────────────────────────────────────────────── */
.ai {
  position: relative; overflow: hidden;
  background: linear-gradient(180deg, var(--paper) 0%, var(--paper-cool) 20%, var(--paper-cool) 82%, var(--paper) 100%);
  padding-block: clamp(84px, 10vw, 136px);
}
/* Round 2 — this band no longer mirrors the margins band, and it breaks the
   copy-left / frame-right rhythm the middle of the page had settled into
   (§5.3). It stacks instead: heading, then one panel across the full measure
   with the financials rail built into it rather than floating off a corner,
   then the three capabilities as a caption under it. */
.ai__stack { position: relative; z-index: 1; display: grid; gap: clamp(34px, 4vw, 52px); }
.ai__top {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.84fr);
  gap: clamp(36px, 5vw, 92px); align-items: start;
}
.ai__head { max-width: 34ch; }
.ai__top .ailist > div:last-child { border-bottom: 1px solid var(--rule-soft); }
.ai__lead { margin-top: 22px; font-size: var(--lead); line-height: 1.58; color: var(--ink-soft); max-width: 56ch; }
.ai__kick {
  display: inline-flex; align-items: center; gap: 8px; margin-bottom: 16px;
  font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--orange-text);
}
.ai__kick::before { content: ""; width: 20px; height: 1.5px; background: var(--orange); }
.ailist { display: grid; }
.ailist > div { display: grid; grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr); gap: 14px; padding: 14px 0; border-top: 1px solid var(--rule-soft); }
.ailist dt { font-weight: 650; font-size: 0.9375rem; color: var(--ink); }
.ailist dd { margin: 0; font-size: 0.9375rem; color: var(--ink-soft); }

.ai__art { position: relative; }
.ai__panel { position: relative; z-index: 1; }

/* ── FAQ ─────────────────────────────────────────────────────────────────── */
.faq { padding-block: clamp(80px, 10vw, 128px); }
.faq__head { max-width: 46ch; }
.faq__head p { margin-top: 16px; font-size: var(--lead); color: var(--ink-soft); }
.faq__list { margin-top: clamp(36px, 4vw, 56px); border-top: 1px solid var(--rule-soft); max-width: 1080px; }
.faq details { border-bottom: 1px solid var(--rule-soft); }
.faq summary {
  display: flex; align-items: center; gap: 24px; cursor: pointer; list-style: none;
  padding: clamp(18px, 2vw, 26px) 8px clamp(18px, 2vw, 26px) clamp(4px, 2vw, 28px);
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem); font-weight: 550; letter-spacing: -0.012em;
  transition: color 0.3s var(--ease), padding-left 0.5s var(--ease);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: var(--orange-text); }
.faq summary i {
  margin-left: auto; position: relative; width: 15px; height: 15px; flex: 0 0 15px;
}
.faq summary i::before, .faq summary i::after {
  content: ""; position: absolute; left: 0; top: 7px; width: 15px; height: 1.6px;
  background: var(--ink-soft); border-radius: 2px; transition: transform 0.45s var(--ease), background-color 0.3s var(--ease);
}
.faq summary i::after { transform: rotate(90deg); }
.faq details[open] summary i::after { transform: rotate(0deg); }
.faq details[open] summary i::before, .faq details[open] summary i::after { background: var(--orange); }
.faq__a { padding: 0 clamp(4px, 2vw, 28px) clamp(22px, 2.4vw, 30px); max-width: 74ch; }
.faq__a p { color: var(--ink-soft); font-size: 1rem; line-height: 1.65; }

/* ── closer ──────────────────────────────────────────────────────────────── */
/* ROUND 3 — a vignette. The dark closers were one flat hex across their whole
   width, which made them the flattest objects in the set at the exact moment a
   reader leaves the page. A little light in the middle and a fall to the
   corners gives the band a shape without giving it an edge. The vignette only
   darkens, so the white copy on it can only gain contrast; the warm glow that
   bleeds in from the corner is an element and still sits on top of it. */
/* ROUND 4, §27 item 3: the dark bands had a shape and no light on them. Each
   now takes the elevation system's own edge light on its top lip — the line the
   light draws where a dark slab meets the paper it is lying on — using
   --edge-ink, which round 3 defined and never used. */
.close {
  position: relative; overflow: hidden; color: #fff;
  background-color: var(--ink-deep); background-image: var(--vignette);
  padding-block: clamp(76px, 9vw, 116px);
  box-shadow: var(--edge-ink);
}
.close__in {
  position: relative; z-index: 1;
  display: flex; align-items: flex-end; justify-content: space-between; gap: clamp(32px, 5vw, 72px); flex-wrap: wrap;
}
.close h2 { color: #fff; }
.close p { margin-top: 18px; font-size: var(--lead); color: rgba(255, 255, 255, 0.72); max-width: 40ch; }
.close__acts { display: flex; align-items: center; gap: 26px; flex-wrap: wrap; padding-bottom: 6px; }

/* ── footer ──────────────────────────────────────────────────────────────── */
.ftr { background: var(--surf-paper); border-top: 1px solid var(--rule-soft); padding-top: clamp(52px, 6vw, 76px); }
.ftr__in { display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.6fr); gap: clamp(32px, 5vw, 72px); }
.ftr__brand > img { height: 36px; width: auto; display: block; }
.ftr__brand p { margin-top: 16px; font-size: 0.875rem; line-height: 1.6; color: var(--ink-mute); max-width: 34ch; }
.ftr__cols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(24px, 3vw, 48px); }
.ftr__cols h3 { font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink); margin-bottom: 14px; }
.ftr__cols a { display: block; padding: 5px 0; font-size: 0.9375rem; color: var(--ink-soft); text-decoration: none; }
.ftr__cols a:hover { color: var(--orange-text); }
.ftr__cols a[aria-current] { color: var(--ink); font-weight: 600; }

/* the by-role column. Director's ruling, 3 September: Operations is the one
   built template this round, so it is the one door. The other four are named
   on the same stage rail The Advance is cut from — one segment lit for the
   route that exists, four unlit for the routes that do not — and they are
   plain items, not links. A reader can read the set without being promised a
   page that is not there. The reason sits beside them for the reviewer only. */
.ftr__role {
  display: flex; align-items: baseline; gap: 11px;
  padding: 5px 0; font-size: 0.9375rem; line-height: 1.5;
}
.ftr__role::before {
  content: ""; flex: 0 0 12px; height: 3px; margin-top: 0.56em;
  background: var(--rule); transition: background-color 0.3s var(--ease);
}
.ftr__role--open::before { background: var(--orange); }
.ftr__role a { display: inline; padding: 0; color: var(--ink); font-weight: 600; }
.ftr__role span { color: var(--ink-mute); }
.ftr__role--open:hover::before { background: var(--orange-edge); }
.ftr__soon {
  margin-top: 12px; padding-left: 23px; max-width: 21ch;
  font-size: 0.8125rem; line-height: 1.5; color: var(--ink-mute);
}
.ftr__bot {
  margin-top: clamp(40px, 5vw, 64px); padding-block: 22px; border-top: 1px solid var(--rule-soft);
  display: flex; justify-content: space-between; gap: 18px; flex-wrap: wrap;
  font-size: 0.875rem; color: var(--ink-mute);
}
.ftr__links { display: flex; gap: 22px; }
.ftr__links a { color: var(--ink-mute); text-decoration: none; }
.ftr__links a:hover { color: var(--orange-text); }

.stamp {
  max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gut) 30px;
  font-size: 0.8125rem; line-height: 1.55; color: #6a727b;
}

/* ══ MOTION — ROUND 4 ══════════════════════════════════════════════════════
   Owner review 4, 3 September 2026, 16:05 MDT, verbatim: "The artifact for the
   demo they want also needs to pop. Look for other key areas where you can
   strategically add depth and micro movement."

   The budget was set by measuring the reference again, this time for motion.
   billow.so, read live: 0 running animations, 1 @keyframes in the whole site
   and it is Framer's loading spinner, 5 declared transitions of which exactly
   one is authored — a 0.6s link underline on cubic-bezier(0.16, 1, 0.3, 1),
   which is the curve this stylesheet already calls --ease. No loop, no ticker,
   no parallax, no scroll-linked transform. Its "drifting clouds" are painted
   gradients and its "fading media" is a mask. So the reference barely moves,
   and the quality a reader reads as motion is in fact depth.

   Round 4 spends the budget accordingly: ONE orchestrated moment per page, one
   product-true beat per drawn frame that runs once per visit, short quiet
   micro-interactions on controls and surfaces, and nothing else.

   The laws, and every rule below obeys them:
     1. Transform and opacity only for anything that runs on its own. Colour and
        box-shadow move under a pointer or a focus ring and nowhere else.
     2. One orchestrated moment per page. Everything else 150–420ms on --ease,
        entrances staggered no wider than 60ms.
     3. Nothing moves that the reader is trying to read. No heading, no body
        copy, no rotor inside an <h1> (docs/59 §28 records that decision).
     4. Nothing loops except the hero light, which changes opacity only.
     5. Every beat runs ONCE, when its band is first seen, and never re-arms.
     6. Reduced motion is the finished state, drawn complete, with nothing left
        transparent, displaced, unfilled or undrawn — and it is byte-for-byte
        where the motion-on page ends up.

   Everything below is scoped to [data-motion="on"], which app.js writes only
   when reduced motion is off. With the script off, or the preference set, none
   of the pre-states exist and the page is simply finished.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. the two entrances, retimed ──────────────────────────────────────────
   Round 1 ran the hero at 850/950ms with 90ms steps and the bands at 800/900ms.
   That is slower than the page reads and wider than law 2 allows, and it was
   the one place the set moved like a slideshow. */
[data-motion="on"] .r { opacity: 0; transform: translateY(16px); }
[data-motion="on"] .r.is-in {
  opacity: 1; transform: none;
  transition: opacity 0.52s var(--ease), transform 0.52s var(--ease);
  transition-delay: calc((var(--i, 0)) * 55ms);
}
[data-motion="on"] .rv { opacity: 0; transform: translateY(14px); }
[data-motion="on"] .rv.is-in {
  opacity: 1; transform: none;
  transition: opacity 0.38s var(--ease), transform 0.38s var(--ease);
}

/* ── 2. THE BEATS — one mechanism for every sequenced once-only moment ───────
   A container marked .beat has its children numbered by app.js into --n, and
   each child runs one animation whose delay is its index times one step. Four
   modifiers change what moves; they compose, and all four are transform and
   opacity only:

     (bare)        the child itself rises and fades      role's job rows, the AI lines
     .beat--still  the child holds still                 pair it with one of the two below
     .beat--rule   the child's own hairline draws        the ledger, the six-family index
     .beat--grow   the child's bars grow from the axis   the referral chart
     .beat--fill   the child's bar fills from the left   the cost tables

   Because it is one mechanism, the motion probe can enumerate the whole set by
   four keyframe names, and a beat that fired twice would be visible as a second
   animation on the same target. */
[data-motion="on"] .beat > * {
  opacity: 0;
  transform: translate(var(--beat-x, 0px), var(--beat-y, 9px));
}
[data-motion="on"] .beat.is-in > * {
  animation: beat var(--beat-d, 0.3s) var(--ease) both;
  animation-delay: calc(var(--n, 0) * var(--beat-s, 46ms));
}
[data-motion="on"] .beat--still > * { opacity: 1; transform: none; }
[data-motion="on"] .beat--still.is-in > * { animation: none; }

[data-motion="on"] .beat.beat--rule > *::after { transform: scaleX(0); }
[data-motion="on"] .beat--rule.is-in > *::after {
  animation: draw-x var(--beat-d, 0.3s) var(--ease) both;
  animation-delay: calc(var(--n, 0) * var(--beat-s, 46ms));
}
[data-motion="on"] .beat.beat--grow > *::before,
[data-motion="on"] .beat.beat--grow > *::after { transform: scaleY(0); }
[data-motion="on"] .beat--grow.is-in > *::before,
[data-motion="on"] .beat--grow.is-in > *::after {
  animation: grow-y var(--beat-d, 0.48s) var(--ease) both;
  animation-delay: calc(var(--n, 0) * var(--beat-s, 40ms));
}
[data-motion="on"] .beat.beat--fill .rbar i { transform: scaleX(0); }
[data-motion="on"] .beat--fill.is-in .rbar i {
  animation: draw-x var(--beat-d, 0.52s) var(--ease) both;
  animation-delay: calc(var(--n, 0) * var(--beat-s, 60ms));
}
/* the bar families are drawn from their own left edge, which is where a
   bar starts. Declared outside the motion gate so the origin is right whether
   or not the bar ever animates. */
.beat--fill .rbar i { transform-origin: left center; }
.beat--rule > *::after { transform-origin: left center; }
.beat--grow > *::before, .beat--grow > *::after { transform-origin: bottom center; }

@keyframes beat   { to { opacity: 1; transform: none; } }
@keyframes draw-x { to { transform: scaleX(1); } }
@keyframes grow-y { to { transform: scaleY(1); } }

/* ── 3. THE SETTLE — an object arrives and its cast catches up ───────────────
   NOTE on the gating throughout §2–§6: every pre-state is written against the
   motion class itself (.beat.beat--rule, .settle.settle--cast, .hero__h.r), not
   against the modifier alone. That is what lets app.js take a finished beat
   back out of the system by removing one class: the pre-state stops matching,
   the animation stops existing, and the element drops off its own compositor
   layer. A layer left alive draws its text with grey antialiasing instead of
   subpixel, which on a 94px display headline is visible — and it is also what
   would stop the reduced-motion page and the settled motion page from being
   the same picture. See app.js §7 finish().
   The hero moment on the homepage and on the blog index. The object rises the
   last few pixels into place while the shadow underneath it resolves.

   The cast moves off the frame's own box-shadow and onto a pseudo-element so
   it can be FADED rather than repainted — law 1. The finished state is exactly
   round 3's `var(--edge), var(--elev-4)`: the edge light stays on the frame,
   the ladder sits on the pseudo, and inset: -1px puts the pseudo on the border
   box rather than the padding box, so the cast is where it always was. */
.settle--cast { position: relative; }
.settle--cast::after {
  content: ""; position: absolute; inset: -1px; border-radius: inherit;
  box-shadow: var(--elev-4); pointer-events: none;
}
.frame.settle--cast { box-shadow: var(--edge); }
[data-motion="on"] .settle { transform: translateY(10px); }
[data-motion="on"] .settle.settle--fade { opacity: 0; }
[data-motion="on"] .settle.settle--cast::after { opacity: 0; }
[data-motion="on"] .settle.is-in { animation: settle 0.64s var(--ease) 0.12s both; }
[data-motion="on"] .settle--fade.is-in { animation: settle-fade 0.64s var(--ease) 0.12s both; }
[data-motion="on"] .settle--cast.is-in::after { animation: fade-in 0.64s var(--ease) 0.12s both; }
@keyframes settle      { to { transform: none; } }
@keyframes settle-fade { to { transform: none; opacity: 1; } }
@keyframes fade-in     { to { opacity: 1; } }

/* ── 4. the accent word's underline draws, last ─────────────────────────────
   The headline itself never moves (law 3). Its one orange mark does: the rule
   under "clear" draws left to right as the closing beat of the hero settle, so
   the accent moves and the reading does not. */
[data-motion="on"] .hero__h.r em::after { transform: scaleX(0); }
.hero__h em::after { transform-origin: left center; }
[data-motion="on"] .hero__h.is-in em::after { animation: draw-x 0.56s var(--ease) 0.42s both; }

/* ── 5. the hero light breathes. The one loop in the set ────────────────────
   Opacity and nothing else, 13 seconds a half-cycle — slower than a reader
   notices a change and shorter than a page session, so the band is never twice
   the same and is never caught moving. */
[data-motion="on"] .bloom--hero { animation: breathe 13s ease-in-out infinite alternate; }
@keyframes breathe { from { opacity: 0.88; } to { opacity: 1; } }

/* ── 6b. the real board lands in its frame ──────────────────────────────────
   ROUND 6, 5 September 2026. The board is a photograph of the product now, so
   there are no cards to move: M-11's job travelling Lead into Mitigation was a
   beat on drawn DOM, and it went with the drawing. The moment stays, on the
   object that is actually there. The picture rises the last fourteen pixels
   into its frame, and while it is still moving it throws a shadow on the frame
   under it which closes up as it lands. 520ms, once, on first scroll-in.

   The shadow is on a pseudo-element and the beat is its OPACITY, not the
   shadow itself — law 1, the same reason the settle's cast moved onto a pseudo
   in §3 above. A repainted box-shadow is a paint on every frame of the
   animation; a faded one is a composited layer. The pseudo sits inside the
   window because the box clips, so the cast reads along the inner edges, which
   is where a picture lifted off its frame would actually show one.

   It ends exactly where it started, the way the drawn card's ring did, so the
   rest state is untouched and a reader with reduced motion is shown the same
   picture. The pre-state is written against the motion class itself, so when
   app.js sheds `arrive` the pre-state stops matching and the element drops off
   its own compositor layer (app.js §7 finish, and the note at §3 above). */
.arrive::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  box-shadow: inset 0 12px 20px -12px rgba(var(--sh), 0.38),
              inset 0 2px 4px -2px rgba(var(--sh), 0.26);
  opacity: 0; pointer-events: none;
}
[data-motion="on"] .arrive .shot { transform: translateY(14px); opacity: 0; }
[data-motion="on"] .arrive.is-in .shot { animation: arrive-shot 0.52s var(--ease) both; }
[data-motion="on"] .arrive.is-in::after { animation: arrive-press 0.52s var(--ease) both; }
@keyframes arrive-shot  { to { transform: none; opacity: 1; } }
@keyframes arrive-press { 0% { opacity: 0; } 52% { opacity: 1; } 100% { opacity: 0; } }

/* ── 6b. the phone's screen gives under the pointer ─────────────────────────
   Eight pixels, on the list only: the status bar and the app's own nav hold
   still while the rows travel under them, which is what makes it read as the
   list scrolling rather than as the phone sliding. Gated on the media query
   rather than on data-motion so it still works with the script off, and simply
   does not exist under the preference — a scroll that jumps is worse than a
   scroll that never happens.

   5 September 2026: it does not exist on a photographed screen either.
   `.shotwin` is one flat picture — nothing to hold still, nothing to travel
   under it — so sliding it would open an eight-pixel gap at the foot of the
   frame rather than reveal a row. It is excluded, and the shell's own 3px lift
   carries the whole of those frames' answer to a pointer. The one drawn phone
   left, role.html's message thread, still does the eight pixels. */
@media (prefers-reduced-motion: no-preference) {
  .phone__screen > *:not(.ph__top):not(.ph__nav):not(.ph__tabs):not(.sr):not(.shotwin) {
    transition: transform 0.42s var(--ease);
  }
  .phone:focus-within .phone__screen > *:not(.ph__top):not(.ph__nav):not(.ph__tabs):not(.sr):not(.shotwin) {
    transform: translateY(-8px);
  }
  /* the eight-pixel travel is a desk-pointer affordance; on a finger the drawn
     boards below 768px are scrollable stages instead and the phone mocks sit
     still, which is the honest answer rather than a screen stuck mid-scroll */
  @media (hover: hover) and (pointer: fine) {
    .phone:hover .phone__screen > *:not(.ph__top):not(.ph__nav):not(.ph__tabs):not(.sr):not(.shotwin) {
      transform: translateY(-8px);
    }
  }
}
/* Everything the OS or the app pins stays pinned — the status bar, the app's own
   nav, the tab bar and the floating action button — so the rows travel under
   them. Written as an exclusion rather than as a class on every scrolling child,
   because the drawn phones do not share one list structure and the fixed
   furniture is the short, stable half of the list. */

/* ── 7. reduced motion — the composed static state ──────────────────────────
   app.js never writes data-motion="on" under the preference, so none of the
   pre-states above exist at all: no opacity 0, no displacement, no scaleX(0).
   The rules that draw the finished object — the cast pseudo, the ledger's
   hairlines, the index's rules, the ring at rest — are all declared OUTSIDE the
   gate, so what reduced motion renders is what motion-on renders when it stops.
   This block only stands guard over anything that slips through. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
  .r, .rv { opacity: 1 !important; transform: none !important; }
  .beat > *, .beat .rbar i { opacity: 1 !important; transform: none !important; }
  .beat--rule > *::after, .beat--grow > *::before, .beat--grow > *::after { transform: none !important; }
  .settle, .settle--fade { opacity: 1 !important; transform: none !important; }
  .settle--cast::after { opacity: 1 !important; }
  .arrive .shot { opacity: 1 !important; transform: none !important; }
  .arrive::after { opacity: 0 !important; }
}

/* ── responsive ──────────────────────────────────────────────────────────── */
@media (max-width: 1100px) {
  .three { grid-template-columns: 1fr 1fr; gap: 40px 34px; }
  .cal:nth-child(3) { grid-column: 1 / -1; padding-left: 0; }
  .cal:nth-child(3)::before { display: none; }
  .cal:nth-child(3) .minif { max-width: 440px; }
  .cal:nth-child(3) .minif__body { min-height: 0; }
  .wip { position: static; width: auto; margin-top: 16px; box-shadow: var(--lift-2); display: flex; gap: 20px; align-items: center; flex-wrap: wrap; }
  .wip__k { margin: 0; }
  .wip__r { border-top: 0; gap: 10px; }
}
@media (max-width: 980px) {
  .hdr__nav { display: none; }
  .hdr__end .lnk, .hdr__end .btn--primary { display: none; }
  .burger { display: block; }
  .hdr__brand { margin-right: auto; }
  .open, .open--tight { grid-template-columns: 1fr; gap: 20px; }
  .open__lead { padding-bottom: 0; }
  .margins__grid, .field__grid, .roles__grid, .ai__top { grid-template-columns: 1fr; }
  .field__grid { gap: 52px; }
  .field__art { order: 2; min-height: 0; }
  .field__copy { order: 1; }
  .ftr__in { grid-template-columns: 1fr; }
  .roles__panels { margin-top: 8px; }
}
@media (max-width: 720px) {
  .hero__h br, .open__h br, .margins__copy h2 br, .field__copy h2 br, .ai__head h2 br, .close h2 br { display: none; }
  .hero__h { max-width: 14ch; }
  .three { grid-template-columns: 1fr; gap: 44px; }
  .cal, .cal:nth-child(3) { padding-left: 0; }
  .cal + .cal::before { display: none; }
  .cal p { max-width: none; }
  .close__in { flex-direction: column; align-items: flex-start; }
  .ftr__cols { grid-template-columns: 1fr 1fr; }
  .rtab__s { display: none; }
  .phones { width: 100%; }
  .phone--back { display: none; }
  .field__art { justify-content: flex-start; }
  .phone { --pw: min(310px, max(268px, 82vw)); }
  .ailist > div { grid-template-columns: 1fr; gap: 4px; }
}
@media (max-width: 420px) {
  .ftr__cols { grid-template-columns: 1fr; }
  .btn { width: 100%; }
  .btn--quiet { width: auto; }
  .hero__acts .btn { width: auto; flex: 1 1 auto; }
  .menu__cta { width: auto; }
}

/* ══════════════════════════════════════════════════════════════════════════
   R2-02 — THE OTHER FOUR TEMPLATES
   role.html · features.html · compare.html · blog.html

   Same system, same two faces, same buttons, same board menu. What changes per
   page is the composition, because a page that is the homepage with different
   words is not a template, it is a paint job.

   Shared to all four: a quieter page hero (the homepage keeps the 94px H1 to
   itself — it should be the loudest thing in the set), and a "you are here"
   node in the nav, which round 2 could not have because there was only one page.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── shared: the inner-page hero ─────────────────────────────────────────── */
:root {
  /* solves to 40px at 390 and 68px at 1440 — a clear step under the homepage */
  --t1b: clamp(2.5rem, 2.028rem + 1.928vw, 4.25rem);
}
.phero { position: relative; overflow-x: clip; padding-top: clamp(40px, 5.4vw, 76px); }
.phero__h { font-size: var(--t1b); max-width: 17ch; text-wrap: initial; }
.phero__h em { font-style: normal; color: var(--orange-display); position: relative; }
.phero__h em::after {
  content: ""; position: absolute; left: 2%; right: 2%; bottom: 0.1em; height: 0.055em;
  background: var(--orange); border-radius: 2px; opacity: 0.34;
}
.phero__lead {
  margin-top: clamp(18px, 2vw, 26px); max-width: 46ch;
  font-size: var(--lead); line-height: 1.58; color: var(--ink-soft);
}
.phero__acts { display: flex; gap: 12px; margin-top: clamp(24px, 2.6vw, 34px); flex-wrap: wrap; }
.phero__note { margin-top: clamp(22px, 2.4vw, 30px); max-width: 42ch; font-size: 0.875rem; line-height: 1.6; color: var(--ink-mute); }

/* the nav marker finally has a true "you are here": five pages exist now, so
   the node rests on the current one instead of at the start of the line. */
.hdr__nav a[aria-current] { color: var(--ink); font-weight: 600; }
.menu__nav a[aria-current] span { color: var(--orange-display); }

/* the review strip under the stamp — internal only, never a shipped element */
.stamp p + p { margin-top: 10px; }
.stamp__set { display: flex; flex-wrap: wrap; align-items: center; gap: 0 18px; }
.stamp__set span { color: #6a727b; }
.stamp__set a { color: #4a525b; text-decoration: none; border-bottom: 1px solid var(--rule); padding-bottom: 1px; }
.stamp__set a:hover { color: var(--orange-text); border-color: var(--orange); }
.stamp__set a[aria-current] { color: var(--orange-text); border-color: var(--orange); }

/* ── the client flag ──────────────────────────────────────────────────────
   The same five files serve two readers. Internal is the default and nothing
   about it changes. With data-mode="client" on <html> — set by hand, or by
   ?client=1 through the head script on every page — the internal furniture
   the 3 September sweep listed comes off in one place: the visible stamp and
   with it the set row's link to the button sheet, and every sentence written
   for the reviewer rather than for a restoration contractor, which is exactly
   two of them (docs/58 16.5 and 16.27, both marked data-internal in role.html).
   The <title> and the meta description are swapped by the same switch.
   These two rules run before first paint, so nothing internal is ever drawn;
   app.js then takes the same nodes out of the document, so they are gone from
   the DOM as well as from the page. No line is deleted from any source file,
   so no file has to be forked to send one. */
[data-mode="client"] .stamp { display: none !important; }
[data-mode="client"] [data-internal] { display: none !important; }

/* ══ ROUND 4 — MICRO-INTERACTIONS ══════════════════════════════════════════
   The set already answered a pointer on its controls: The Deck rises two
   pixels, its chevron travels three, the press sinks and the cast collapses;
   the nav node rides to the item under the pointer; the sticky header's cast
   resolves as it starts to float. What it did not do was answer a pointer on
   its SURFACES — a drawn frame, a panel, a card, a field, a footer row — which
   is most of what a reader actually points at on these five pages.

   This block is last in the file on purpose: it adds hover, focus and open
   states to components declared far above it, and a later rule at equal
   specificity is how those states are meant to win.

   The law it obeys: surfaces move, text does not. Nothing here moves a heading,
   a paragraph, a list or a table cell. A card lifts as a whole; the words
   inside it hold still relative to it.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. frames, panels and cards lift, and their cast grows with them ────────
   Three pixels, the same distance for every surface in the set, so a reader
   learns one behaviour rather than five. The cast steps up exactly one rung of
   the round-3 elevation ladder — the object moved further from the paper, so
   the shadow it throws is wider and softer, which is what actually happens. */
.frame, .minif, .wip, .wip2, .phone__shell {
  transition: transform 0.24s var(--ease), box-shadow 0.24s var(--ease);
}
/* Fractional movement of the large playing video drops decoded frames in
   Chromium. Keep its 3px lift immediate; the shadow and light still animate. */
#board-frame { transition-property: box-shadow; }
.settle--cast::after      { transition: box-shadow 0.24s var(--ease); }
@media (hover: hover) and (pointer: fine) {
.frame:hover              { transform: translateY(-3px); box-shadow: var(--edge), var(--elev-4), var(--amb-4); }
.frame--panel:hover       { transform: translateY(-3px); box-shadow: var(--edge), var(--elev-4); }
.frame--mini:hover        { transform: translateY(-3px); box-shadow: var(--edge-soft), var(--elev-3); }
.frame.settle--cast:hover { box-shadow: var(--edge); }
.frame.settle--cast:hover::after { box-shadow: var(--elev-4), var(--amb-4); }
.wip:hover, .wip2:hover   { transform: translateY(-3px); box-shadow: var(--edge), var(--elev-3); }
.phone:hover .phone__shell { transform: translateY(-3px); box-shadow: var(--elev-4), var(--amb-4), var(--edge-soft), inset 0 0 0 1px rgba(255, 255, 255, 0.10); }
/* round 3 lifted the three callouts' mini-frames 6px, before the frames lifted
   at all. One distance for every surface now: a reader learns the behaviour
   once. The blog card's own 6px comes down in the blog block. */
.cal:hover .minif         { transform: translateY(-3px); box-shadow: var(--edge), var(--elev-3); }
.minif:hover              { transform: translateY(-3px); box-shadow: var(--edge), var(--elev-3); }
}

/* ── 2. one light travels the top lip as the surface rises ──────────────────
   The glass highlight the elevation system already paints along an object's top
   edge (--edge, an inset white hairline) is a still photograph of a light
   catching a lip. On hover it moves: a 26%-wide gradient runs the length of the
   lip once, 600ms, and is gone.

   It is sized and timed so the bright core never leaves the object — at the end
   of the travel the light's right edge is exactly the frame's right edge — so
   no clipping context is needed and the frames' casts stay unclipped.

   Gated on the media query rather than on data-motion: a light that teleports
   across an edge is worse than one that never lights, and this has to work with
   the script off. */
.minif { position: relative; }
.frame::before, .minif::before {
  content: ""; position: absolute; top: 0; left: 0; z-index: 3;
  width: 32%; height: 34%; max-height: 96px;
  background: linear-gradient(101deg,
    rgba(255, 255, 255, 0) 24%, rgba(255, 255, 255, 0.58) 50%, rgba(255, 255, 255, 0) 76%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0) 100%);
          mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0) 100%);
  opacity: 0; transform: translateX(-100%); pointer-events: none;
}
/* The first attempt was a 2px white bar sitting on the lip. It was invisible,
   and measurably so: the shell's top stop is #ffffff, and nothing is brighter
   than white. A specular on a white surface can only read where the light
   crosses something that is not white — so the band is now the height of the
   frame's own chrome and fades out downward through a mask, which puts its
   bright half over the browser bar, the app's navy rail, the panel's header or
   a card's artwork, and leaves the white shell alone. That is also what a light
   crossing a lit object actually does. */
@media (prefers-reduced-motion: no-preference) and (hover: hover) and (pointer: fine) {
  .frame:hover::before, .minif:hover::before,
  .cal:hover .minif::before { animation: sweep 0.6s var(--ease) both; }
}
/* 212.5% is the exact end of the travel: the light is 32% of the object wide,
   and 0.32 x (1 + 212.5/100) = 1, so its right edge finishes flush with the
   object's right edge and never crosses it. That is why no clipping context is
   needed anywhere, and why the frames' casts stay unclipped. */
@keyframes sweep {
  0%   { opacity: 0; transform: translateX(-100%); }
  16%  { opacity: 1; }
  84%  { opacity: 1; }
  100% { opacity: 0; transform: translateX(212.5%); }
}

/* ── 3. the FAQ opens ───────────────────────────────────────────────────────
   The chevron already rotated. The answer did not — it appeared at full height
   the instant the summary was clicked, which on a one-open-at-a-time list is
   the page jumping rather than the question opening.

   THIS IS THE ONE PLACE IN THE SET WHERE SOMETHING ANIMATES A LAYOUT PROPERTY,
   and motion-probe.mjs names it by selector rather than letting it through: an
   answer's own height, and its own bottom padding, for 260ms, on one element at
   a time, on a click. It is declared rather than hidden because a wipe would
   have obeyed the law and still let the page jump, and the page jumping is the
   thing worth fixing. `0fr -> 1fr` on a grid row is the contained form of it:
   the browser resolves one track, not a document reflow. */
.faq__a { display: grid; grid-template-rows: 1fr; }
.faq__a > p { min-height: 0; }
@media (prefers-reduced-motion: no-preference) {
  details[open] > .faq__a { animation: answer 0.26s var(--ease) both; }
}
@keyframes answer {
  from { grid-template-rows: 0fr; opacity: 0; padding-bottom: 0; }
  to   { grid-template-rows: 1fr; opacity: 1; }
}

/* ── 4. a field lights and lifts its rule on focus ──────────────────────────
   The input's own border stays where it is and keeps doing the work with the
   script off and under the preference. On focus a second, lit rule draws across
   it from the left and rises two pixels off it, so the field a reader is typing
   into is the one thing on the docket standing above the paper. Transform and
   opacity: the input, its label and its text never move. */
.onrule { position: relative; }
.onrule::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  border-radius: 2px; background: var(--onrule-lit);
  opacity: 0; transform: scaleX(0); transform-origin: left center;
  transition: transform 0.2s var(--ease), opacity 0.2s var(--ease);
  pointer-events: none;
}
.onrule:focus-within::after { opacity: 1; transform: scaleX(1) translateY(-2px); }

/* ── 5. the footer's role rail lights ───────────────────────────────────────
   The pip is the same stage node the buttons and the nav carry. On the one row
   that is a door today it grows, takes the halo it has everywhere else in the
   set, and the name goes to the brand's text orange. The row itself does not
   move: it is a line of text. */
.ftr__role::before { transition: background-color 0.2s var(--ease), transform 0.2s var(--ease), box-shadow 0.2s var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .ftr__role--open:hover::before {
    background: var(--orange-edge); transform: scaleX(1.34);
    box-shadow: 0 0 0 3px rgba(245, 130, 32, 0.18);
  }
}
.ftr__role--open:hover a { color: var(--orange-text); }
.ftr__role a { transition: color 0.2s var(--ease); }

/* ── 6. the header's filled segment rides on a transform, not on a metric ───
   It animated `width` for 500ms every time the node moved. Absolutely
   positioned, so it could never move anything else — but it put a layout pass on
   the critical path of every frame of the travel, for a bar 1.5px thick. It
   scales now, and app.js hands it a ratio instead of a pixel length. The
   features rail's segment gets the same treatment in the features block.
   Nothing in the set animates a metric any more except the FAQ answer in §3,
   which says so. */
.hdr__seg { width: 100%; transform: scaleX(0); transform-origin: left center; transition: transform 0.5s var(--ease); }

/* ══════════════════ role.html — T-05, the Operations page ═════════════════
   The homepage centres a lockup over a wide frame. This page is read left to
   right, because a role page is one person's working view: who you are, then
   what is in front of you. The queue runs off the right edge of the viewport,
   because a queue does not end at a margin.
   ═════════════════════════════════════════════════════════════════════════ */
.phero--role { padding-bottom: clamp(56px, 7vw, 96px); }
.phero__grid {
  display: grid; grid-template-columns: minmax(0, 0.94fr) minmax(0, 1.06fr);
  gap: clamp(32px, 4.4vw, 60px); align-items: center;
}
/* the role H1 steps down again: it shares its line with a product frame, so it
   sets three lines against the list rather than four against nothing */
.phero--role .phero__h { font-size: clamp(2.25rem, 1.845rem + 1.653vw, 3.75rem); max-width: 19ch; }
.phero__media { position: relative; z-index: 1; }
/* the frame runs past the wrap and off the right edge of the viewport. The
   bleed is clamped at 190px so a very wide window cannot push the board into
   horizontal overflow; .phero clips what is left. */
.frame--list {
  padding: 5px; max-width: none;
  width: calc(100% + clamp(0px, (100vw - var(--wrap)) / 2 + var(--gut), 190px));
}
/* Same discipline as the hero after owner ruling 2, applied to a frame that is
   on the right instead of in the middle: the warm cores sit ON the board's top
   shoulders so the frame's own edge cuts them, the cool sits under and to the
   left of the frame, and everything stays clear of the copy column. Round 1 of
   this page had a warm core floating in open paper above the frame and a second
   one behind the header — a stain rather than a light. */
.bloom--rolehero {
  left: 20%; top: -20px; width: min(1400px, 116vw); height: 1000px;
  background:
    radial-gradient(16% 17% at 30% 71%, rgba(102, 128, 154, 0.52) 0%, rgba(102, 128, 154, 0.40) 26%, rgba(102, 128, 154, 0.12) 56%, rgba(102, 128, 154, 0) 76%),
    radial-gradient(20% 20% at 37% 88%, rgba(102, 128, 154, 0.58) 0%, rgba(102, 128, 154, 0.46) 26%, rgba(102, 128, 154, 0.13) 56%, rgba(102, 128, 154, 0) 78%),
    radial-gradient(14% 15% at 79% 89%, rgba(102, 128, 154, 0.32) 0%, rgba(102, 128, 154, 0.20) 26%, rgba(102, 128, 154, 0) 74%),
    radial-gradient(16% 18% at 35% 25%, rgba(245, 130, 32, 0.88) 0%, rgba(245, 130, 32, 0.70) 27%, rgba(245, 130, 32, 0.18) 57%, rgba(245, 130, 32, 0) 75%),
    radial-gradient(12% 14% at 75% 26%, rgba(245, 130, 32, 0.66) 0%, rgba(245, 130, 32, 0.50) 25%, rgba(245, 130, 32, 0.13) 55%, rgba(245, 130, 32, 0) 73%),
    radial-gradient(40% 36% at 48% 57%, rgba(242, 194, 153, 0.46), rgba(242, 194, 153, 0) 74%);
}

/* ── the day — the week is the widest thing on the page ──────────────────── */
.day { padding-block: clamp(76px, 9vw, 124px) clamp(72px, 8vw, 112px); background: var(--surf-warm); }
.day__frame { margin-top: clamp(36px, 4.4vw, 60px); }

.daycap {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(24px, 3vw, 52px); margin-top: clamp(28px, 3vw, 40px);
  border-top: 1px solid var(--rule); padding-top: clamp(22px, 2.4vw, 30px);
}
.daycap h3 { font-size: 1.0625rem; }
.daycap p { margin-top: 9px; font-size: 0.9375rem; line-height: 1.6; color: var(--ink-soft); }

/* ── the truck — media in the middle, copy in both margins ───────────────── */
.truck { position: relative; overflow-x: clip; padding-block: clamp(84px, 10vw, 136px) clamp(56px, 6.4vw, 88px); }
/* px, not ch: a ch on the container resolves against the container's 17px body
   font, not the 57px display face inside it */
.truck__head { max-width: 640px; margin-bottom: clamp(20px, 2.6vw, 36px); }
.truck__grid {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: clamp(28px, 4vw, 64px); align-items: start;
}
/* the two margins are offset against each other, so the band reads on a slight
   diagonal instead of as a symmetrical sandwich */
.truck__col { display: flex; flex-direction: column; gap: clamp(28px, 3.4vw, 46px); }
.truck__col .btn { align-self: flex-start; }
.truck__col--l { text-align: right; align-items: flex-end; }
.truck__col--l .btn { align-self: flex-end; }
.truck__col--r { margin-top: clamp(44px, 7vw, 128px); }
.tcap { max-width: 30ch; }
.tcap h3 { font-size: 1.0625rem; }
.tcap p { margin-top: 8px; font-size: 0.9375rem; line-height: 1.62; color: var(--ink-soft); }
.truck__note { max-width: 30ch; font-size: 0.9375rem; line-height: 1.62; color: var(--ink-soft); }
.truck__col--r .btn--quiet { margin-top: -14px; }
.truck__phone { display: flex; justify-content: center; }
/* same discipline as the hero after owner ruling 2: a warm key with an edge,
   occluded by the phone's own body, and a cool side for it to read against */
.bloom--truck {
  left: 50%; top: 16%; width: min(1000px, 96vw); height: 700px; transform: translateX(-50%);
  background:
    radial-gradient(20% 22% at 42% 22%, rgba(245, 130, 32, 0.72) 0%, rgba(245, 130, 32, 0.54) 26%, rgba(245, 130, 32, 0.14) 56%, rgba(245, 130, 32, 0) 76%),
    radial-gradient(17% 18% at 62% 40%, rgba(245, 130, 32, 0.46) 0%, rgba(245, 130, 32, 0.30) 24%, rgba(245, 130, 32, 0) 74%),
    radial-gradient(22% 24% at 34% 76%, rgba(102, 128, 154, 0.50) 0%, rgba(102, 128, 154, 0.36) 26%, rgba(102, 128, 154, 0) 76%),
    radial-gradient(18% 18% at 66% 74%, rgba(102, 128, 154, 0.30) 0%, rgba(102, 128, 154, 0) 74%),
    radial-gradient(52% 46% at 50% 52%, rgba(242, 194, 153, 0.44), rgba(242, 194, 153, 0) 76%);
}

/* the message thread inside the phone */
.ph__job { display: flex; align-items: center; gap: 0.5em; padding: 0.55em 0.9em 0.4em; background: #fff; border-bottom: 1px solid #eef1f3; }
.ph__job .pill { margin-top: 0; }
.ph__clock { display: flex; align-items: center; gap: 0.3em; margin-left: auto; font-size: 0.7em; font-weight: 600; color: #1f6b4a; }
.ph__clock .ic { width: 0.9em; height: 0.9em; flex: 0 0 0.9em; }
/* a thread is bottom-anchored: the newest message sits on the compose bar */
.thr { flex: 1 1 auto; min-height: 0; overflow: hidden; padding: 0.7em 0.8em; display: flex; flex-direction: column; justify-content: flex-end; gap: 0.55em; }
.msg { max-width: 84%; border-radius: 0.9em; padding: 0.55em 0.7em; font-size: 0.72em; line-height: 1.4; }
/* explicit, not an opacity: 72% of the bubble's own ink lands at 3.7:1 on the
   incoming bubble, which is under AA */
.msg i { display: block; font-style: normal; margin-top: 0.35em; font-size: 0.86em; color: #5f6970; }
.msg--out i { color: #b9bec3; }
.msg--sys i { color: #1f6b4a; }
.msg--out { align-self: flex-end; background: #333c45; color: #f4f5f6; border-bottom-right-radius: 0.25em; }
.msg--in { align-self: flex-start; background: #fff; color: #2f3a45; border: 1px solid #e6e8ea; border-bottom-left-radius: 0.25em; }
.msg__pic { display: block; margin: -0.2em -0.2em 0.5em; border-radius: 0.6em; overflow: hidden; }
.msg__pic svg { display: block; width: 100%; height: 5em; }
.msg--sys { align-self: center; max-width: 100%; background: rgba(45, 138, 96, 0.13); color: #1f6b4a; font-weight: 600; text-align: center; padding: 0.4em 0.8em; }
.ph__compose {
  display: flex; align-items: center; gap: 0.5em; margin: 0 0.8em 0.5em; padding: 0.5em 0.75em;
  background: #fff; border: 1px solid #e3e5e7; border-radius: 1.2em; font-size: 0.72em; color: #656e77;
}
.ph__compose span { flex: 1; }
.cmp { width: 1.5em; height: 1.5em; border-radius: 50%; background: #e9ecee; flex: 0 0 1.5em; }
.cmp--send { background: var(--orange); }

/* ── the numbers — donuts and bars, the one chart-shaped frame in the set ── */
.nums { padding-block: clamp(76px, 9vw, 124px); background: var(--surf-cool); }
.nums__grid {
  display: grid; grid-template-columns: minmax(0, 1.18fr) minmax(0, 0.82fr);
  gap: clamp(36px, 5vw, 76px); align-items: center;
}
.nums__copy h2 { text-wrap: initial; }
.numlist { margin-top: clamp(26px, 3vw, 38px); border-top: 1px solid var(--rule); }
.numlist > div { padding: 16px 0; border-bottom: 1px solid var(--rule-soft); }
.numlist dt { font-size: 1rem; font-weight: 600; letter-spacing: -0.012em; color: var(--ink); }
.numlist dd { margin: 6px 0 0; font-size: 0.9375rem; line-height: 1.6; color: var(--ink-soft); }

.wip2 { background: #fff; border-radius: 14px; overflow: hidden; border: 1px solid rgba(34, 38, 43, 0.07); }
.wip2__h { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid #eef0f1; }
.wip2__h b { font-size: 13px; color: #2f3a45; }
.wip2__d { display: grid; grid-template-columns: repeat(3, 1fr); }
.don { padding: 18px 16px; text-align: center; border-right: 1px solid #eef0f1; }
.don:last-child { border-right: 0; }
/* the white centre is a background layer, not a pseudo-element: it cannot drift
   off centre, and the label is therefore always on white rather than on the ring */
.don__r {
  display: grid; place-items: center; width: 84px; height: 84px; margin: 0 auto 10px; border-radius: 50%;
  background:
    radial-gradient(closest-side, #fff 71%, transparent 71.5%),
    conic-gradient(var(--ring, #5d9474) calc(var(--p) * 1%), #e7ebed 0);
}
.don__r i { font-style: normal; font-size: 15px; font-weight: 700; color: #2f3a45; font-variant-numeric: tabular-nums; }
.don__r--b { --ring: #4c86c4; }
.don__r--w { --ring: #c1402c; }
.don > b { display: block; font-size: 12px; color: #2f3a45; }
/* :not(.don__r) matters — .don > span would otherwise out-specify .don__r's own
   display:grid and drop the percentage label out of the ring's white centre and
   onto the coloured track, where it measured 2.23:1. */
.don > span:not(.don__r) { display: block; margin-top: 2px; font-size: 10px; color: #6b747d; }
.don > span.is-warn { color: #96331f; font-weight: 600; }
.don dl { display: grid; grid-template-columns: auto auto; gap: 3px 8px; margin-top: 11px; padding-top: 10px; border-top: 1px solid #f1f2f3; }
.don dt { font-size: 9.5px; color: #6b747d; text-align: left; }
.don dd { margin: 0; font-size: 10px; font-weight: 600; color: #2f3a45; text-align: right; font-variant-numeric: tabular-nums; }
.wip2__b { padding: 16px 18px 18px; border-top: 1px solid #eef0f1; background: #fcfcfd; }
.wip2__b > b { display: block; font-size: 12px; color: #2f3a45; margin-bottom: 11px; }
/* .rbar, not .bar: the homepage's cost table already owns .bar as a <td> class,
   and a global `.bar { display: grid }` here turned those cells into grids and
   shortened every cost bar on the homepage. Caught by diffing the homepage
   capture after this sheet changed. */
.bars { display: grid; gap: 7px; }
.rbar { display: grid; grid-template-columns: 150px 1fr 66px; align-items: center; gap: 10px; }
.rbar em { font-style: normal; font-size: 10px; color: #5b656e; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.rbar i { display: block; height: 9px; width: var(--w); border-radius: 3px; background: linear-gradient(90deg, #7fa8cd, #4c86c4); }
.rbar u { text-decoration: none; text-align: right; font-size: 10.5px; font-weight: 600; color: #2f3a45; font-variant-numeric: tabular-nums; }
.rbar--warn i { background: linear-gradient(90deg, #e2a08e, #c1402c); }
.bars__f { display: flex; gap: 1px; margin-top: 13px; background: #eef0f1; border-radius: 7px; overflow: hidden; }
.bars__f span { flex: 1; background: #fff; padding: 8px 10px; font-size: 9.5px; color: #6b747d; }
.bars__f b { display: block; margin-top: 2px; font-size: 12px; color: #2f3a45; font-variant-numeric: tabular-nums; }
.bars__f .is-accent { background: rgba(245, 130, 32, 0.09); color: #5c4426; }
.bars__f .is-accent b { color: #8f4408; }

/* ── decide — the band with no media at all ──────────────────────────────── */
.decide { padding-block: clamp(72px, 8.4vw, 116px); }
.decide__h { max-width: 22ch; text-wrap: initial; }
.decide__grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(26px, 3.4vw, 54px); margin-top: clamp(34px, 4vw, 54px);
}
.decide__grid > div { border-top: 2px solid var(--orange); padding-top: 18px; }
.decide__grid > div:nth-child(2) { border-top-color: rgba(245, 130, 32, 0.5); }
.decide__grid > div:nth-child(3) { border-top-color: rgba(245, 130, 32, 0.25); }
.decide__grid h3 { font-size: 1.0625rem; }
.decide__grid p { margin-top: 9px; font-size: 0.9375rem; line-height: 1.62; color: var(--ink-soft); }

/* ── five teams — the job line at page scale ─────────────────────────────── */
.teams { padding-block: clamp(64px, 7.4vw, 100px) clamp(72px, 8.4vw, 116px); background: var(--surf-warm); }
.teams__head { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(24px, 4vw, 60px); align-items: end; }
.teams__head p { font-size: var(--lead); line-height: 1.58; color: var(--ink-soft); max-width: 44ch; }
.teamline {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  margin-top: clamp(38px, 4.4vw, 60px);
}
.teamline li { position: relative; padding: 30px 22px 0 0; }
.teamline li::before {
  content: ""; position: absolute; left: 0; right: 0; top: 10px; height: 2px;
  background: rgba(34, 38, 43, 0.12);
}
.teamline li.is-on::before { background: var(--orange); }
.teamline li::after {
  content: ""; position: absolute; left: 0; top: 6px; width: 10px; height: 10px; border-radius: 50%;
  background: var(--paper-warm); border: 2px solid rgba(34, 38, 43, 0.2);
}
.teamline li.is-on::after { background: var(--orange); border-color: var(--orange); box-shadow: 0 0 0 4px rgba(245, 130, 32, 0.18); }
.teamline b { display: block; font-size: 1rem; font-weight: 600; letter-spacing: -0.012em; }
.teamline li.is-on b { color: var(--orange-text); }
.teamline span { display: block; margin-top: 8px; font-size: 0.875rem; line-height: 1.58; color: var(--ink-soft); }

/* ── the name-plate — the opener's signature on this page ────────────────
   The other four pages open on a dot, an eyebrow, a serif headline with one
   underlined accent word, and a lead. This one opens on an object: a plate
   hung off the board's left edge, naming the role the way a door plate names
   a room, with The Advance's own stage rail under it set to five segments —
   one per by-role view, the first filled. The headline sits beside it and
   carries no accent word at all, because the plate is the accent. */
.nplate {
  position: relative; z-index: 3; width: 268px; margin-bottom: 20px;
  padding: 17px 20px 19px;
  background: var(--ink-deep); color: #fff;
  border-left: 3px solid var(--orange);
  box-shadow: 0 2px 4px rgba(20, 24, 30, 0.14), 0 18px 38px -14px rgba(20, 24, 30, 0.42);
}
/* the plate is fixed to the board, so the fixing shows: a short orange stem
   dropping from the plate's own edge into the frame's top-left corner. */
.nplate__stem { position: absolute; left: -3px; bottom: -20px; width: 3px; height: 20px; background: var(--orange); }
/* two rivets, on the diagonal — a plate is screwed to something */
.nplate::before, .nplate::after {
  content: ""; position: absolute; width: 3px; height: 3px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.26);
}
.nplate::before { left: 9px; top: 9px; }
.nplate::after { right: 9px; bottom: 9px; }
.nplate__k {
  display: block; font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--orange);
}
.nplate__n {
  display: block; margin-top: 7px; font-family: var(--jobsight-display); font-weight: 400;
  font-size: 1.75rem; line-height: 1.06; letter-spacing: -0.02em; color: #fff;
}
.nplate__rail { display: flex; gap: 4px; margin-top: 15px; }
.nplate__rail i { display: block; height: 3px; flex: 1 1 0; background: rgba(255, 255, 255, 0.2); }
.nplate__rail i:first-child { background: var(--orange); }

/* ── closer — the week, and one thing to do about it ─────────────────────
   Not the shared D-28 shell: no two-line head, no lead, no button pair, no
   glow. One line, one button, and the schedule running out under both of them
   to the edges of the screen. The page ends inside the object it lived in. */
.rclose { position: relative; overflow: clip; color: #fff; background-color: var(--ink-deep);
  background-image: var(--vignette); padding-top: clamp(72px, 8.4vw, 108px);
  box-shadow: var(--edge-ink); }
/* The one dark band in the set with no light of its own. The primary control is
   the only lit object standing on it, so the light belongs behind the control:
   a warm pool at the right, spilling down onto the week that runs out under it.
   Same device, same warm, same alphas as the homepage's own closer. */
.bloom--rclose {
  right: -6%; top: -34%; width: min(880px, 92vw); height: 560px;
  background:
    radial-gradient(44% 48% at 56% 50%, rgba(245, 130, 32, 0.19), rgba(245, 130, 32, 0) 74%),
    radial-gradient(62% 54% at 38% 64%, rgba(184, 90, 12, 0.13), rgba(184, 90, 12, 0) 78%);
}
.rclose__in { position: relative; z-index: 1; }
.rclose__in {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: clamp(28px, 4vw, 64px); flex-wrap: wrap;
  padding-bottom: clamp(38px, 4.4vw, 58px);
}
.rclose__h {
  font-family: var(--jobsight-display); font-weight: 400; color: #fff;
  font-size: clamp(1.75rem, 1.212rem + 2.198vw, 3.125rem); line-height: 1.04;
  letter-spacing: -0.024em; max-width: 19ch;
}
/* the strip. Seven days at the full width of the screen, cropped by the
   viewport rather than stopped by a margin — the same rule the hero board
   follows at the other end of the page. */
.wstrip { position: relative; overflow: hidden; border-top: 1px solid rgba(255, 255, 255, 0.14); }
.wstrip__grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); height: clamp(112px, 12vw, 156px); }
.wstrip__d { position: relative; border-right: 1px solid rgba(255, 255, 255, 0.09); padding-top: 26px; }
.wstrip__d:last-child { border-right: 0; }
.wstrip__d > b {
  position: absolute; left: 12px; top: 9px;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.11em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.56);
}
.wstrip__d.is-now { background: rgba(245, 130, 32, 0.07); box-shadow: inset 0 2px 0 var(--orange); }
.wstrip__d.is-now > b { color: var(--orange); }
.wstrip__d i {
  position: absolute; left: 10px; right: 10px; overflow: hidden;
  top: calc(26px + var(--t)); height: var(--h);
  display: flex; align-items: center; padding: 0 8px;
  font-style: normal; font-size: 10.5px; line-height: 1.3;
  color: rgba(255, 255, 255, 0.92); white-space: nowrap; text-overflow: ellipsis;
}
.wsb--a { background: rgba(115, 139, 164, 0.62); }
.wsb--b { background: rgba(115, 139, 164, 0.38); }
.wsb--c { background: rgba(255, 255, 255, 0.13); }
/* the lit block takes dark ink on brand orange, which is what the product's
   own hot badge does — and it is the only pair on the strip white would fail */
.wstrip__d i.wsb--o { background: rgba(245, 130, 32, 0.92); color: #2b1a08; }

/* ── role page: responsive ───────────────────────────────────────────────── */
@media (max-width: 1179px) { :root { --kl: 0.82; } }
@media (max-width: 1023px) { :root { --kl: 0.7; } }
@media (max-width: 767px)  { :root { --kl: 0.62; } }
@media (max-width: 1100px) {
  /* minmax(0,1fr), not 1fr: the drawn board inside carries a fixed 1180px width,
     and a plain 1fr track sizes to its min-content, which blew the whole hero
     column out to 1192px at a 390 viewport. */
  .phero__grid { grid-template-columns: minmax(0, 1fr); gap: clamp(36px, 5vw, 56px); }
  .phero__h { max-width: 20ch; }
  .frame--list { width: calc(100% + var(--gut)); }
  /* stacked, the plate cannot hang off anything, so it sits on the board's
     shoulder instead — still an object, still not an eyebrow. */
  .nplate { width: 100%; max-width: 340px; }
  /* stacked, the copy is above the frame — so the light goes to the bottom of
     the band, around the frame, and never behind the words (F8) */
  .bloom--rolehero {
    left: 50%; transform: translateX(-50%); top: auto; bottom: -4%;
    width: min(860px, 140vw); height: 620px;
  }
  .truck__grid { grid-template-columns: 1fr; }
  .truck__col--l { text-align: left; align-items: flex-start; padding-top: 0; order: 2; }
  .truck__phone { order: 1; justify-content: flex-start; }
  .truck__col--r { order: 3; padding-bottom: 0; }
  .truck__col--r .btn--quiet { margin-top: 0; }
  .nums__grid { grid-template-columns: 1fr; }
  .teams__head { grid-template-columns: 1fr; gap: 18px; }
  .teamline { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 420px) {
  /* the pair still shares one row at 390 — 143px + 12px + 176px against 350px of
     measure. Full-width stacked CTAs are the most default mobile shape there is. */
  .phero__acts .btn, .fhero__acts .btn { width: auto; }
}
@media (max-width: 720px) {
  .phero__h br, .truck__head h2 br, .nums__copy h2 br, .decide__h br { display: none; }
  .rclose__in { gap: 24px; }
  .wstrip__d > b { left: 7px; font-size: 0.625rem; letter-spacing: 0.06em; }
  .wstrip__d i { left: 6px; right: 6px; }
  .daycap, .decide__grid { grid-template-columns: 1fr; gap: 26px; }
  .teamline { grid-template-columns: 1fr; }
  .teamline li { padding-right: 0; }
  .wip2__d { grid-template-columns: 1fr; }
  .don { border-right: 0; border-bottom: 1px solid #eef0f1; }
  .rbar { grid-template-columns: 118px 1fr 58px; }
  .bars__f { flex-wrap: wrap; }
  .bars__f span { flex: 1 0 44%; }
}

/* ── ROUND 4 — the reporting card lifts with the rest of the surfaces ─────── */
.wip2 { transition: transform 0.24s var(--ease), box-shadow 0.24s var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .wip2:hover { transform: translateY(-3px); box-shadow: var(--edge), var(--elev-3); }
}

/* ══════════════════════ features.html — T-02, All features ════════════════
   A catalogue, and it is allowed to look like one. The homepage argues; this
   page lists. So the composition is a rail and an index rather than a run of
   persuasive bands, the type gets quieter, and the frames do the arguing.

   What it takes from the homepage system: the two faces, The Advance buttons,
   the board menu, the frame construction, the light, the section rhythm.
   What it does differently: it opens on its own contents rather than a product
   screen, it carries a sticky rail with the same node the buttons and the nav
   use, its one dark band is JobSightAI rather than the closer, and it has no
   FAQ — a catalogue that has to explain itself is not a catalogue.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── hero: contents, not a screen ────────────────────────────────────────── */
.fhero { position: relative; overflow-x: clip; padding-top: clamp(40px, 5.4vw, 76px); padding-bottom: clamp(20px, 3vw, 44px); }
/* The index IS the opener. The other four pages open on a dot, an eyebrow and
   a serif headline set as large as the page allows; a catalogue opens on its
   own contents, so the six-family table takes the whole right side at full
   height and the headline steps aside — narrow column, smaller size, no accent
   word, no underline. The reader meets the list before the sentence. */
.fhero__in {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr);
  gap: clamp(28px, 4.6vw, 68px); align-items: start;
}
.fhero__k {
  font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--orange-text);
}
.fhero__h {
  margin-top: 16px; font-size: clamp(1.75rem, 1.413rem + 1.377vw, 3rem);
  max-width: 15ch; text-wrap: initial;
}
.fhero__lead { margin-top: 22px; font-size: var(--lead); line-height: 1.58; color: var(--ink-soft); max-width: 34ch; }
.fhero__note { margin-top: 14px; font-size: 0.9375rem; line-height: 1.6; color: var(--ink-mute); max-width: 32ch; }
.fhero__acts { margin-top: clamp(20px, 2.4vw, 30px); }
/* No light in this hero, on purpose. Every core on this site sits on a product
   frame's shoulder so the frame's own edge cuts it — that occlusion is what
   makes it read as light rather than as a gradient (docs/59 §5.1). This hero
   has no frame to cut anything, and both attempts at one put colour under the
   lead and under the contents index, which is F8. So the page's first light
   arrives with its first product frame, beside the phone. The hero is paper,
   type and rules. */

/* the contents. A real index: number, name, count, and it is the navigation. */
.toc { border-top: 2px solid var(--ink); }
/* ROUND 4: the rule under each family moves onto a pseudo-element so the index
   can draw itself, family by family — this page's orchestrated moment. The li
   keeps its 1px border box and loses only the colour. */
.toc li { position: relative; border-bottom: 1px solid transparent; }
.toc li::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--rule); pointer-events: none;
}
.toc a {
  position: relative; display: grid; grid-template-columns: 44px minmax(0, 1fr) auto;
  align-items: baseline; gap: clamp(10px, 1.4vw, 20px);
  padding: clamp(16px, 1.7vw, 22px) 0;
  text-decoration: none; transition: color 0.3s var(--ease);
}
/* the row lights from its own left edge, the way a job line does */
.toc a::before {
  content: ""; position: absolute; left: -14px; top: 50%; width: 6px; height: 6px;
  border-radius: 50%; background: var(--orange); transform: translateY(-50%) scale(0);
  transition: transform 0.32s var(--ease);
}
.toc a:focus-visible::before { transform: translateY(-50%) scale(1); }
@media (hover: hover) and (pointer: fine) {
  .toc a:hover::before { transform: translateY(-50%) scale(1); }
}
.toc__n {
  font-family: var(--jobsight-display); font-weight: 400; font-size: 1.375rem; line-height: 1;
  color: var(--orange-text); font-variant-numeric: tabular-nums; letter-spacing: 0.01em;
}
.toc__t {
  font-family: var(--jobsight-display); font-weight: 400;
  font-size: clamp(1.25rem, 1.115rem + 0.551vw, 1.75rem); line-height: 1.14;
  letter-spacing: -0.018em; color: var(--ink);
}
.toc__c { font-size: 0.8125rem; color: var(--ink-mute); white-space: nowrap; font-variant-numeric: tabular-nums; }
.toc a:hover .toc__t, .toc a:focus-visible .toc__t { color: var(--orange-text); }

/* ── the catalogue and its rail ──────────────────────────────────────────── */
.cat { padding-top: clamp(24px, 3vw, 40px); }
.cat__grid { display: grid; grid-template-columns: 206px minmax(0, 1fr); gap: clamp(24px, 3.4vw, 62px); align-items: start; }

.rail { position: sticky; top: 92px; padding-block: 8px 0; }
.rail__k { font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase; color: var(--ink-mute); margin-bottom: 16px; padding-left: 18px; }
.rail__list { position: relative; }
.rail__list a {
  display: block; padding: 9px 0 9px 18px; font-size: 0.9375rem; line-height: 1.35;
  color: var(--ink-mute); text-decoration: none; transition: color 0.35s var(--ease);
}
.rail__list a:hover { color: var(--ink); }
.rail__list li.is-on a { color: var(--ink); font-weight: 600; }
/* the line is the job line turned on its side: one hairline, one filled run,
   one orange node — the same three parts the buttons and the header nav use */
/* top and height are set from the measured item centres in app.js, so the line
   runs from the first family to the last and the node lands on a word rather
   than near one. The static values are the no-JavaScript state. */
.rail__line { position: absolute; left: 0; top: 46px; bottom: 8px; width: 2px; background: rgba(34, 38, 43, 0.12); }
.rail__seg { position: absolute; left: 0; top: 46px; width: 2px; height: 0; background: var(--orange); }
.rail__node {
  position: absolute; left: -3px; top: 46px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--orange); box-shadow: 0 0 0 3px rgba(245, 130, 32, 0.2);
  transition: transform 0.5s var(--ease);
}

/* ── a family ────────────────────────────────────────────────────────────── */
.fam { position: relative; overflow-x: clip; padding-block: clamp(56px, 6.4vw, 96px); border-top: 1px solid var(--rule-soft); }
.cat__body > .fam:first-child { border-top: 0; padding-top: clamp(20px, 2.4vw, 34px); }
.fam__h { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: clamp(20px, 3vw, 52px); row-gap: 0; align-items: start; }
/* explicit placement: the number and the name stack in the first column, the
   lead sits beside the name — the asymmetric opener the homepage uses, with a
   catalogue number added */
.fam__n { grid-column: 1; grid-row: 1; }
.fam__h h2 { grid-column: 1; grid-row: 2; }
.fam__lead { grid-column: 2; grid-row: 2; align-self: end; }
.fam__n {
  font-family: var(--jobsight-display); font-size: 2rem; font-weight: 400; line-height: 1;
  letter-spacing: -0.02em; color: var(--orange-display); margin-bottom: 12px;
  font-variant-numeric: tabular-nums;
}
.fam__h h2 { font-size: clamp(1.85rem, 1.5rem + 1.4vw, 2.9rem); text-wrap: initial; }
.fam__lead { font-size: 1.0625rem; line-height: 1.58; color: var(--ink-soft); max-width: 44ch; padding-bottom: 0.3em; }

.fam__split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(30px, 4vw, 64px); align-items: start; margin-top: clamp(30px, 3.6vw, 50px); }
.fam__split--flip { grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr); }
.fam__split--doc { grid-template-columns: minmax(0, 1.16fr) minmax(0, 0.84fr); align-items: start; }
.fam--wide .fam__media { margin-top: clamp(30px, 3.6vw, 50px); }
.fam--wide .index { margin-top: clamp(26px, 3vw, 40px); }
.fam__media { position: relative; }
.fam__media--phone { display: flex; justify-content: center; padding-top: 6px; }
.fam__media--doc { display: flex; justify-content: flex-end; }
/* the phone's own body is the occluder here: both warm cores sit on its
   shoulders and the cool sits under its feet, so the frame cuts the light the
   way the board does in the homepage hero */
/* the phone is narrow, so its cores straddle its two long edges rather than its
   top: the body of the phone is the occluder either way */
.bloom--fam {
  left: 50%; top: 0; width: min(720px, 98vw); height: 620px; transform: translateX(-50%);
  background:
    radial-gradient(13% 15% at 27% 24%, rgba(245, 130, 32, 0.84) 0%, rgba(245, 130, 32, 0.64) 27%, rgba(245, 130, 32, 0.17) 57%, rgba(245, 130, 32, 0) 75%),
    radial-gradient(10% 12% at 74% 29%, rgba(245, 130, 32, 0.60) 0%, rgba(245, 130, 32, 0.44) 25%, rgba(245, 130, 32, 0) 73%),
    radial-gradient(14% 15% at 30% 82%, rgba(102, 128, 154, 0.54) 0%, rgba(102, 128, 154, 0.40) 26%, rgba(102, 128, 154, 0) 76%),
    radial-gradient(12% 13% at 72% 86%, rgba(102, 128, 154, 0.34) 0%, rgba(102, 128, 154, 0) 74%),
    radial-gradient(40% 40% at 50% 54%, rgba(242, 194, 153, 0.38), rgba(242, 194, 153, 0) 76%);
}
/* the sheaf carries its own light, not the column: a bloom sized to the column
   reached 300px into the index and put an orange wash under two entries */

/* ── the index. Ruled rows, never panels, never a card grid. ─────────────── */
.index { display: grid; }
.index > div { padding: 15px 0 17px; border-top: 1px solid var(--rule-soft); }
.index > div:first-child { border-top: 1px solid var(--rule); }
.index dt { font-size: 1rem; font-weight: 600; letter-spacing: -0.012em; color: var(--ink); }
.index dd { margin: 6px 0 0; font-size: 0.9375rem; line-height: 1.6; color: var(--ink-soft); }
.index--1 { grid-template-columns: minmax(0, 1fr); }
.index--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(24px, 3vw, 48px); }
.index--2 > div:nth-child(2) { border-top: 1px solid var(--rule); }
.index--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: clamp(22px, 2.6vw, 44px); }
.index--3 > div:nth-child(2), .index--3 > div:nth-child(3) { border-top: 1px solid var(--rule); }
.index--big { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(28px, 4vw, 68px); }
.index--big > div { padding: 22px 0 26px; }
.index--big > div:nth-child(2) { border-top: 1px solid var(--rule); }
.index--big dt { font-size: 1.375rem; font-family: var(--jobsight-display); font-weight: 400; letter-spacing: -0.026em; }
.index--big dd { margin-top: 10px; font-size: 1rem; max-width: 40ch; }

/* the type-only family sits on the cool paper so the break is felt, not read */
.fam--type { background: var(--surf-cool); box-shadow: var(--edge-soft), var(--elev-1); border-radius: 18px; padding: clamp(34px, 4vw, 56px) clamp(26px, 3vw, 44px); border-top: 0; margin-block: clamp(24px, 3vw, 40px); }
.fam--type .index--big > div { border-top-color: rgba(34, 38, 43, 0.14); }

/* the video line. One sentence and one quiet control, nothing else — the only
   band on the page that is not a family, and it is deliberately the smallest. */
/* Until 5 September this was a line and a link: a sentence on the left and
   "Watch the demo video" on the right, pointing at the booking anchor. There was
   no video behind it. There is one now — the same film the homepage's hero plays
   thirty seconds of — so the slot holds the film itself instead of a promise of
   it. The break in the catalogue's rhythm is still a break; it has a picture in
   it now.

   preload is none and there is no autoplay: the catalogue is a page a reader
   scrolls, and eight megabytes should not leave the server because somebody
   passed by. The browser's own controls start it. The poster is the hero's
   poster, contained over the film's own paper colour, which is where frame one
   of the film sits — so pressing play does not move the picture. */
.watch {
  display: grid; justify-items: center; gap: clamp(20px, 2.4vw, 30px);
  padding-block: clamp(30px, 3.4vw, 46px); border-top: 1px solid var(--rule-soft);
  text-align: center;
}
.watch p { font-family: var(--jobsight-display); font-size: clamp(1.25rem, 1.05rem + 0.8vw, 1.75rem); letter-spacing: -0.026em; max-width: 30ch; }
.watch__frame { width: min(880px, 100%); }
.watch__v {
  display: block; width: 100%; aspect-ratio: 1920 / 1200;
  object-fit: contain; background: #fcfaf8; border-radius: 12px;
}

/* ══ the drawn screens ════════════════════════════════════════════════════ */

/* The phone that stood here was a recreation of the mobile receipt screen. It
   is JobSight's own App Store capture now (docs/59 §34), so its stylesheet —
   the form, the scan dialog, the tick and the contrast note that went with them
   — came out with it on 5 September 2026. */

/* — the business development dashboard, two cards from the real dashboard — */
.appbox--bd { height: calc(660px * var(--k, 1)); }
.app--bd { width: 720px; height: 660px; display: grid; grid-template-rows: auto 1fr; gap: 10px; padding: 10px; background: #eef1f3; align-content: start; }
.dcard { background: #fff; border: 1px solid #e2e7ea; border-radius: 8px; padding: 11px 13px 12px; }
.dcard__h { display: flex; align-items: center; gap: 9px; margin-bottom: 10px; }
.dcard__h b { font-size: 12.5px; color: #2f3a45; }
.dots { margin-left: auto; width: 3px; height: 3px; border-radius: 50%; background: #99a2aa; box-shadow: 0 -5px 0 #99a2aa, 0 5px 0 #99a2aa; }
.vchart { display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: 6px; height: 152px; }
.vchart__y { display: grid; align-content: space-between; }
.vchart__y i { font-style: normal; font-size: 9px; color: #6b747d; text-align: right; }
.vchart__p { display: flex; align-items: flex-end; gap: 12px; border-bottom: 1px solid #dfe4e7; border-left: 1px solid #dfe4e7; padding: 0 6px; position: relative; }
.vchart__p span { position: relative; flex: 1; height: 100%; display: flex; align-items: flex-end; gap: 3px; }
.vchart__p span::before, .vchart__p span::after { content: ""; flex: 1; border-radius: 2px 2px 0 0; }
.vchart__p span::before { height: var(--a); background: #7fa8cd; }
.vchart__p span::after { height: var(--b); background: linear-gradient(180deg, #fbb267, #f58220); }
.vchart__p em {
  position: absolute; left: 50%; bottom: -42px; width: 84px; font-style: normal;
  font-size: 9px; color: #5f6970; transform-origin: left top; transform: rotate(26deg);
  white-space: nowrap;
}
.dcard__f { display: flex; gap: 22px; margin-top: 58px; padding-top: 10px; border-top: 1px solid #eef1f3; }
.dcard__f span { display: flex; align-items: center; gap: 6px; font-size: 10px; color: #2f3a45; font-weight: 600; }
.dcard__f em { font-style: normal; color: #6b747d; font-weight: 400; }
.dcard__f b { margin-left: 2px; font-size: 12px; font-variant-numeric: tabular-nums; }
.key { width: 8px; height: 8px; border-radius: 2px; background: #7fa8cd; flex: 0 0 8px; }
.key--b { background: var(--orange); }
.reftab { font-size: 10px; }
.reftab th { text-align: left; padding: 6px 8px; background: #f4f6f7; border-bottom: 1px solid #e2e7ea; font-size: 9px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: #56626d; }
.reftab td { padding: 7px 8px; border-bottom: 1px solid #f0f2f4; color: #55606a; }
.reftab td:nth-child(2) { color: #2f3a45; font-weight: 600; }
.reftab td:nth-child(4), .reftab td:nth-child(5) { font-variant-numeric: tabular-nums; }
.dcard__note { margin-top: 8px; font-size: 9.5px; color: #6b747d; }

/* — the mitigation report. A printed page, not an app window: the one frame
     in the set with no browser chrome, portrait, with two more pages behind. */
.sheaf { position: relative; width: min(430px, 100%); }
.sheaf .fglow { left: -13%; right: -13%; }
.sheaf__b { position: absolute; inset: 0; border-radius: 4px; background: #fff; border: 1px solid rgba(34, 38, 43, 0.10); }
.sheaf__b--1 { transform: translate(9px, 9px) rotate(0.5deg); box-shadow: var(--lift-1); }
.sheaf__b--2 { transform: translate(18px, 18px) rotate(1.1deg); box-shadow: var(--lift-1); }
.paper {
  position: relative; border-radius: 4px; overflow: hidden;
  background: linear-gradient(180deg, #ffffff 0%, #fcfbfa 100%);
  border: 1px solid rgba(34, 38, 43, 0.10); box-shadow: var(--edge-soft), var(--elev-3);
  transition: transform 0.7s var(--ease), box-shadow 0.7s var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .sheaf:hover .paper { transform: translateY(-6px); box-shadow: var(--edge), var(--elev-4); }
}
.paper__in { font-size: 13px; }
.rep__head { display: flex; align-items: flex-start; gap: 14px; padding: 0 16px 0 0; background: #f1f2f3; }
.rep__mast {
  flex: 0 0 54%; background: var(--orange); color: #1b2027;
  border-bottom-right-radius: 26px; padding: 16px 18px 18px;
}
.rep__mast b { display: block; font-size: 25px; font-weight: 800; letter-spacing: -0.02em; line-height: 1; }
.rep__mast span { display: block; margin-top: 2px; font-size: 19px; font-weight: 300; letter-spacing: 0.01em; }
.rep__meta { padding-top: 15px; display: grid; gap: 5px; }
.rep__meta div { display: grid; grid-template-columns: 66px minmax(0, 1fr); gap: 5px; font-size: 9.5px; }
.rep__meta dt { font-weight: 700; color: #2f3a45; white-space: nowrap; }
.rep__meta dd { margin: 0; color: #4b5a68; }
.rep__body { padding: 16px 18px 8px; }
.rep__h { font-size: 13px; font-weight: 700; color: #2f3a45; margin-bottom: 12px; }
.rep__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 10px; }
.rep__grid dt { font-size: 9px; font-weight: 700; color: #2f3a45; }
.rep__grid dd { margin: 3px 0 0; font-size: 9px; line-height: 1.4; color: #4b5a68; }
.rep__shot { position: relative; margin-top: 14px; border-radius: 2px; overflow: hidden; }
.rep__shot svg { display: block; width: 100%; height: 148px; }
.rep__shot span {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 4px 0; text-align: center;
  background: rgba(24, 30, 36, 0.62); color: #fff; font-size: 9px;
}
.rep__foot {
  display: flex; gap: 10px; justify-content: space-between; align-items: center;
  margin: 12px 0 0; padding: 6px 16px; background: var(--orange); color: #1b2027;
  font-size: 9px; font-weight: 600;
}
.rep__next { padding: 12px 18px 16px; font-size: 11px; font-weight: 700; color: #8f4408; }

/* — JobSightAI: the navy band — */
.aib { position: relative; overflow: hidden; background: var(--navy-deep); color: #eef1f4; padding-block: clamp(72px, 9vw, 124px); }
.aib::before {
  content: ""; position: absolute; right: -14%; top: -28%; width: min(1000px, 92vw); height: 760px;
  background:
    radial-gradient(20% 22% at 46% 60%, rgba(245, 130, 32, 0.42) 0%, rgba(245, 130, 32, 0.26) 28%, rgba(245, 130, 32, 0) 74%),
    radial-gradient(40% 38% at 52% 54%, rgba(120, 152, 186, 0.28), rgba(120, 152, 186, 0) 76%);
  pointer-events: none;
}
.aib .wrap { position: relative; z-index: 1; }
.aib__head { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(22px, 3.4vw, 56px); align-items: end; }
.aib__k { grid-column: 1; display: flex; align-items: center; gap: 12px; font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: #f8ab63; margin-bottom: 16px; }
.aib__k .rule { display: block; width: 34px; height: 2px; background: var(--orange); }
.aib__head h2 { grid-column: 1; color: #fff; text-wrap: initial; }
.aib__lead { grid-column: 2; grid-row: 2; font-size: var(--lead); line-height: 1.58; color: #c3ced9; max-width: 44ch; padding-bottom: 0.35em; }
.aib__frame { margin-top: clamp(34px, 4.4vw, 60px); }
.frame--ink { background: linear-gradient(180deg, #46596c 0%, #33475a 100%); border-color: rgba(255, 255, 255, 0.12); }
.index--ink { margin-top: clamp(30px, 3.6vw, 48px); }
.index--ink > div { border-top-color: rgba(255, 255, 255, 0.14); }
.index--ink > div:first-child, .index--ink > div:nth-child(2) { border-top-color: rgba(255, 255, 255, 0.3); }
.index--ink dt { color: #fff; }
.index--ink dd { color: #bcc8d4; }

/* No bloom on the light closer. On the dark closer a warm core reads as a glow
   in a room because the ground is ink; on paper, with no frame edge to cut it,
   the same core reads as a stain — and here it landed under the quiet link.
   The rule this page settles on: on paper, light needs a frame. */

/* ── closer — the contents rail, folded flat ─────────────────────────────
   The rail spent the whole catalogue running down the left with the node on
   the family being read. Here it folds over into one horizontal line: the
   same hairline, the same filled run, the same node, and the node arrives at
   the button. One line, one control, no lead, no pair, no glow. */
.fclose { background: var(--surf-warm); padding-block: clamp(64px, 7.6vw, 100px); }
.fclose__h {
  font-family: var(--jobsight-display); font-weight: 400;
  font-size: clamp(1.625rem, 1.264rem + 1.478vw, 2.5625rem); line-height: 1.08;
  letter-spacing: -0.024em; max-width: 42ch;
}
.fold {
  position: relative; margin-top: clamp(40px, 5vw, 64px); padding-top: 26px;
  display: flex; align-items: center; justify-content: space-between;
  gap: clamp(20px, 3vw, 44px); flex-wrap: wrap;
}
.fold__line { position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--rule); }
/* the filled run and the node: the same two marks the vertical rail carried */
.fold__line::after {
  content: ""; position: absolute; left: 0; top: 0; height: 1px; width: 100%;
  background: linear-gradient(90deg, rgba(245, 130, 32, 0.22) 0%, rgba(245, 130, 32, 0.62) 58%, var(--orange) 100%);
}
.fold__node {
  position: absolute; right: 0; top: -3.5px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--orange); box-shadow: 0 0 0 4px rgba(245, 130, 32, 0.16);
}
.fold__list { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap; gap: 6px clamp(12px, 1.6vw, 24px); }
.fold__list a {
  display: block; padding: 4px 0; font-size: 0.9375rem; color: var(--ink-soft);
  text-decoration: none; transition: color 0.28s var(--ease);
}
.fold__list a:hover, .fold__list a:focus-visible { color: var(--orange-text); }
.fold__go { flex: 0 0 auto; }

/* ── features: responsive ────────────────────────────────────────────────── */
@media (max-width: 1179px) {
  .cat__grid { grid-template-columns: 178px minmax(0, 1fr); }
}
@media (max-width: 1023px) {
  /* the rail stops following and becomes the band it always was: a list at the
     head of the catalogue. One sticky element per page is enough. */
  .cat__grid { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .rail { position: static; margin-bottom: clamp(28px, 4vw, 44px); }
  .rail__list { display: flex; flex-wrap: wrap; gap: 0 8px; }
  .rail__list a { padding: 7px 0; }
  .rail__list li:not(:last-child) a::after { content: " ·"; color: rgba(34, 38, 43, 0.3); }
  .rail__line, .rail__seg, .rail__node { display: none; }
  .rail__k { padding-left: 0; }
  .rail__list a { padding-left: 0; }
  .fhero__in { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .fhero__h { max-width: 20ch; }
  .fam__h, .fam__split, .fam__split--flip, .fam__split--doc { grid-template-columns: minmax(0, 1fr); }
  .fam__h { row-gap: 16px; }
  .fam__lead { grid-column: 1; grid-row: auto; padding-bottom: 0; }
  .fam__split { gap: clamp(26px, 4vw, 40px); }
  .fam__media--doc, .fam__media--phone { justify-content: flex-start; }
  .fam--flip .fam__split--flip > .fam__media { order: 2; }
  .aib__head { grid-template-columns: minmax(0, 1fr); }
  .aib__k, .aib__head h2, .aib__lead { grid-column: 1; }
  .aib__lead { grid-row: auto; padding-bottom: 0; margin-top: 18px; }
  .toc { margin-top: 8px; }
}
@media (max-width: 720px) {
  .aib__head h2 br { display: none; }
  .index--2, .index--3, .index--big { grid-template-columns: minmax(0, 1fr); }
  .index--2 > div:nth-child(2), .index--3 > div:nth-child(2), .index--3 > div:nth-child(3),
  .index--big > div:nth-child(2), .index--ink > div:nth-child(2) { border-top: 1px solid var(--rule-soft); }
  .index--ink > div:nth-child(2) { border-top-color: rgba(255, 255, 255, 0.14); }
  .toc a { grid-template-columns: 34px minmax(0, 1fr) auto; }
  .fold { flex-direction: column; align-items: flex-start; }
  .watch { flex-direction: column; align-items: flex-start; gap: 6px; }
  .sheaf { width: 100%; }
}

/* ── ROUND 4 — the contents rail's filled segment rides on a transform ──────
   It animated `height` for 500ms every time the node moved. Absolutely
   positioned, so it could never move anything else — but it put a layout pass
   on the critical path of every frame of the travel, for a bar 2px wide.
   app.js hands it a ratio now instead of a pixel length. */
.rail__seg { transform: scaleY(0); transform-origin: top center; transition: transform 0.5s var(--ease); }

/* ══════════════════════ compare.html — T-08, the comparison ═══════════════
   The one page in the set with no product frame. Its picture is the table and
   its object is the gate, so it is set as a document: rules, marks, tabular
   figures, and a source column. Everything the other pages do with depth this
   page does with ruling.

   What it takes from the homepage system: the two faces, The Advance buttons,
   the board menu, the light discipline (a core only where an edge cuts it —
   here, the gate panel and the dark closer), the section rhythm.
   What it does differently: the hero's right column is a form rather than a
   screen; the counter-claim rows are ledger lines, not cards; the comparison
   is a real table, ruled like a ledger, with a SOURCE column; and it is the
   only page with no drawn product screen anywhere on it.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── opener: the ledger itself ───────────────────────────────────────────
   The other four pages open on a dot, an eyebrow, a serif headline with one
   underlined accent word, and a lead. This one opens on its own evidence: a
   single line of display type, the lead held in the right margin beside it,
   the five status marks it uses, and then the sixteen rows. No frame, no form, no
   accent word — the table is the picture. */
.lhero { padding-block: clamp(40px, 5.4vw, 76px) clamp(60px, 7vw, 96px); }
.lhero__top {
  display: grid; grid-template-columns: minmax(0, 1.04fr) minmax(0, 0.96fr);
  gap: clamp(24px, 4vw, 64px); align-items: end;
  padding-bottom: clamp(26px, 3vw, 38px); border-bottom: 2px solid var(--ink);
}
.lhero__h {
  font-size: clamp(2.25rem, 1.845rem + 1.653vw, 3.75rem); max-width: 18ch; text-wrap: initial;
}
.lhero__lead { font-size: var(--lead); line-height: 1.58; color: var(--ink-soft); max-width: 46ch; }

/* ── the gate. A real panel with a real edge, so it is allowed a light.
   Redesigned 3 September on the director's ruling. What changed and why:

   The panel stays, because the band's light has to sit behind something —
   a lobe of glow floating free on the ink is not light, it is a gradient. But
   it stops being a stock card. It is cut as a printed docket now: the page's
   own warm paper, a hairline edge, and the button's 3px radius, against a set
   whose drawn screens are all rounded at 15–20px. Round is a device in this
   set; square is paper. A 14px-radius white card with a soft shadow was
   neither, and it read as the component every SaaS page ships.

   Inside: the head, then the rule under it carrying one lit segment — the
   same "this has started" mark the primary button wears — then four fields
   written on that rule's own hairlines, the consent line, and The Advance at
   the end of it rather than stretched across the panel as an orange bar. ── */
.gate { position: relative; }
.gate__panel {
  position: relative; z-index: 1; color: var(--ink);
  /* ROUND 4, §27 item 2. It carried the deepest cast in the set on a 3px
     corner, and those two facts argued with each other: a hard-edged rectangle
     under --elev-4 reads as a card that forgot to round. 18px is the frame
     family's own panel radius, so the page's hero object now agrees with every
     other lit object in the set. */
  background: var(--surf-raise); border: 1px solid rgba(34, 38, 43, 0.13); border-radius: 18px;
  padding: clamp(24px, 2.6vw, 34px) clamp(22px, 2.4vw, 32px) clamp(22px, 2.4vw, 30px);
  box-shadow: var(--edge), var(--elev-4);
}
.gate__k { font-family: var(--jobsight-display); font-size: 1.5rem; letter-spacing: -0.026em; line-height: 1.1; }
.gate__sub { margin-top: 8px; font-size: 0.9375rem; color: var(--ink-soft); }
.gate__rule { position: relative; display: block; height: 3px; margin: clamp(22px, 2.4vw, 30px) 0 clamp(20px, 2.2vw, 26px); }
.gate__rule::before { content: ""; position: absolute; left: 0; right: 0; top: 1px; height: 1px; background: var(--rule); }
.gate__rule::after { content: ""; position: absolute; left: 0; top: 0; width: 56px; height: 3px; background: var(--orange); }
.gate__panel > .onrule + .onrule,
.gate__panel > .onrule + .gate__pair { margin-top: clamp(20px, 2.2vw, 26px); }
.gate__pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(16px, 2vw, 26px); }

/* the consent box is drawn, not the browser's. A 3px square on the paper,
   the brand orange when it is ticked, and the tick in ink — the same way the
   product's own hot badge carries dark ink on orange rather than white. */
.gate__consent { display: grid; grid-template-columns: 17px minmax(0, 1fr); gap: 12px; margin-top: clamp(22px, 2.4vw, 28px); }
.gate__consent input {
  appearance: none; -webkit-appearance: none;
  width: 17px; height: 17px; margin: 2px 0 0;
  background: var(--white); border: 1px solid rgba(34, 38, 43, 0.34); border-radius: 3px;
  transition: background-color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.gate__consent input:hover { border-color: rgba(34, 38, 43, 0.5); }
.gate__consent input:checked { background: var(--orange); border-color: var(--orange-edge); }
.gate__consent input::after {
  content: ""; display: block; width: 4px; height: 8px; margin: 2px auto 0;
  border: 2px solid var(--ink-deep); border-top: 0; border-left: 0;
  transform: rotate(42deg) scale(0); transform-origin: center;
  transition: transform 0.25s var(--ease);
}
.gate__consent input:checked::after { transform: rotate(42deg) scale(1); }
.gate__consent label { font-size: 0.8125rem; line-height: 1.5; color: var(--ink-mute); }
.gate__consent a { color: var(--orange-text); }
.gate__end {
  display: flex; align-items: baseline; gap: clamp(14px, 1.8vw, 24px); flex-wrap: wrap;
  margin-top: clamp(24px, 2.6vw, 32px); padding-top: clamp(20px, 2.2vw, 26px);
  border-top: 1px solid var(--rule);
}
.gate__note { font-size: 0.8125rem; line-height: 1.5; color: var(--ink-mute); }
.gate .formstate { margin-top: 18px; }

/* ── three claims on one rule ────────────────────────────────────────────── */
.claims { padding-bottom: clamp(56px, 6.4vw, 88px); }
.claims__row {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 48px);
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule-soft);
}
.claims__row > div { padding: 20px 0 22px; }
.claims__row > div + div { border-left: 1px solid var(--rule-soft); padding-left: clamp(20px, 3vw, 48px); }
.claims__row dt { font-family: var(--jobsight-display); font-size: 1.375rem; letter-spacing: -0.026em; }
.claims__row dd { margin: 8px 0 0; font-size: 0.9375rem; line-height: 1.6; color: var(--ink-soft); }

/* ── counter-claim rows ──────────────────────────────────────────────────── */
.stand { background: var(--surf-warm); padding-block: clamp(72px, 8.4vw, 116px); }
.rows { margin-top: clamp(34px, 4vw, 56px); border-top: 1px solid var(--rule); }
.rows li {
  display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  gap: 6px clamp(24px, 3.4vw, 56px); align-items: start;
  padding: clamp(22px, 2.4vw, 32px) 0; border-bottom: 1px solid var(--rule-soft);
}
.rows__k { grid-column: 1; font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase; color: var(--orange-text); margin-bottom: 8px; }
.rows h3 { grid-column: 1; font-family: var(--jobsight-display); font-weight: 400; font-size: clamp(1.3rem, 1.1rem + 0.7vw, 1.75rem); letter-spacing: -0.026em; line-height: 1.14; }
.rows__b { grid-column: 1; margin-top: 10px; max-width: 42ch; font-size: 1rem; line-height: 1.6; color: var(--ink-soft); }
/* the counter-claim is JobSight's own published wording, so it is set as a
   quotation in the margin rather than as an accusation in a coloured box */
.rows__c {
  grid-column: 2; grid-row: 1 / span 4; align-self: center;
  padding-left: clamp(18px, 2vw, 28px); border-left: 2px solid rgba(34, 38, 43, 0.14);
  font-size: 0.9375rem; line-height: 1.6; color: var(--ink-mute); max-width: 34ch;
}
.rows__c span { display: block; font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase; color: var(--ink); margin-bottom: 7px; }

/* ── the ledger ──────────────────────────────────────────────────────────── */
.ledger__key { display: flex; flex-wrap: wrap; gap: 10px clamp(18px, 2.4vw, 34px); margin-top: clamp(28px, 3.2vw, 42px); }
.ledger__key span { display: flex; align-items: center; gap: 9px; font-size: 0.875rem; line-height: 1.4; color: var(--ink-mute); }
.mk {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.35rem; height: 1.35rem; padding-inline: 0.2rem;
  flex: 0 0 1.35rem; border: 1px solid transparent; border-radius: 4px;
  position: relative; font-style: normal; font-size: 1.05rem; font-weight: 700;
  line-height: 1; text-align: center; white-space: nowrap;
}
.mk--yes, .mk--cost {
  color: var(--orange-text); background: rgba(245, 130, 32, 0.12);
  border-color: rgba(201, 102, 15, 0.38);
}
.mk--cost { font-size: 0.95rem; }
.mk--part {
  color: var(--ink-soft); background: rgba(34, 38, 43, 0.06);
  border-color: rgba(34, 38, 43, 0.18); font-size: 1rem;
}
.mk--no { color: var(--ink); background: transparent; border-color: rgba(34, 38, 43, 0.38); }
.mk--none { color: var(--ink-mute); background: transparent; border-color: transparent; font-size: 1.2rem; }

/* position: relative matters. The .sr labels inside the cells are absolutely
   positioned; with no positioned ancestor their containing block is the page,
   so at 320 the ones scrolled off to the right added 26px of document overflow
   even though the table itself was clipped. */
.ledger__scroll { position: relative; margin-top: clamp(22px, 2.6vw, 32px); overflow-x: auto; }
.ltab { min-width: 640px; }
.ltab th, .ltab td { text-align: left; padding: 13px 14px; border-bottom: 1px solid var(--rule-soft); }
.ltab thead th {
  border-bottom: 1.5px solid rgba(34, 38, 43, 0.32); padding-bottom: 10px;
  font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink);
}
.ltab tbody th { font-size: 1rem; font-weight: 500; color: var(--ink); padding-left: 0; }
.ltab thead th:first-child { padding-left: 0; }
.ltab td { width: 172px; }
.ltab .ltab__qualifier {
  display: block; position: static; width: auto; height: auto; max-width: 23ch;
  margin: 7px 0 0; overflow: visible; clip: auto; white-space: normal;
  font-size: 0.8125rem; line-height: 1.35; color: var(--ink-mute);
}
/* the JobSight column is weighted by its ground and its rules, not by a fill */
.ltab .is-us { background: rgba(245, 130, 32, 0.05); box-shadow: inset 1px 0 0 rgba(201, 102, 15, 0.22), inset -1px 0 0 rgba(201, 102, 15, 0.22); }
.ltab thead .is-us { color: var(--orange-text); }
.ltab__src { width: 128px; font-size: 0.8125rem; color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.ltab__grp th {
  padding: 26px 0 8px; border-bottom: 1px solid var(--rule);
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.13em; text-transform: uppercase; color: var(--ink-mute);
}
.ltab tbody tr:last-child th, .ltab tbody tr:last-child td { border-bottom: 1.5px solid rgba(34, 38, 43, 0.32); }
/* ROUND 4. The body rows' hairlines move off the cells and onto the row, so the
   ledger can draw itself line by line as this page's one orchestrated moment.
   The cell keeps its own 1px (or 1.5px) border box and only loses the colour,
   so not a single metric in the table changes; the visible line is now a row
   pseudo-element that can be scaled from its left edge. The head rule is left
   alone — it is the table's frame, not one of its lines. */
.ltab tbody th, .ltab tbody td { border-bottom-color: transparent; }
.ltab tbody tr { position: relative; }
.ltab tbody tr::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--rule-soft); pointer-events: none;
}
.ltab tbody tr:last-child::after { bottom: -1.5px; height: 1.5px; background: rgba(34, 38, 43, 0.32); }
.ledger__foot { margin-top: 18px; max-width: 74ch; font-size: 0.875rem; line-height: 1.6; color: var(--ink-mute); }

/* ── closer: the demo panel, and nothing after it ────────────────────────
   The set's shared ending is a two-line serif head, a lead, a primary and a
   quiet button paired at the right, and a warm glow. This page has spent
   sixteen rows arguing towards one object, so the object IS the ending: what
   the demo covers on the left, the gate on the right, on the page's one ink
   ground. There is no second closer. */
.dclose { position: relative; overflow-x: clip; color: #fff; background-color: var(--ink-deep);
  background-image: var(--vignette); padding-block: clamp(72px, 8.4vw, 112px);
  box-shadow: var(--edge-ink); }
.dclose__in {
  display: grid; grid-template-columns: minmax(0, 1.06fr) minmax(0, 0.94fr);
  gap: clamp(36px, 5vw, 76px); align-items: start;
}
.dclose__h { color: #fff; text-wrap: initial; }
.dclose__h .mute { color: var(--steel); }
.dclose__lead { margin-top: 18px; font-size: var(--lead); line-height: 1.58; color: rgba(255, 255, 255, 0.72); max-width: 44ch; }
.dclose .dlist { grid-template-columns: minmax(0, 1fr); }
.dclose .dlist li { border-top-color: rgba(255, 255, 255, 0.16); }
.dclose .dlist li:nth-child(n+2) { border-top-color: rgba(255, 255, 255, 0.10); }
.dclose .dlist h3 { color: #fff; }
.dclose .dlist h3::before { color: var(--orange); }
.dclose .dlist p { color: rgba(255, 255, 255, 0.66); }
.dlist {
  counter-reset: d; margin-top: clamp(32px, 3.8vw, 52px);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 clamp(28px, 4vw, 64px);
}
.dlist li { counter-increment: d; padding: 22px 0 24px; border-top: 1px solid var(--rule); }
.dlist li:nth-child(n+3) { border-top-color: var(--rule-soft); }
.dlist h3 { position: relative; padding-left: 40px; font-size: 1.0625rem; }
.dlist h3::before {
  content: counter(d, decimal-leading-zero); position: absolute; left: 0; top: 0.1em;
  font-family: var(--jobsight-display); font-size: 1.05rem; color: var(--orange-display); font-variant-numeric: tabular-nums;
}
.dlist p { margin-top: 8px; padding-left: 40px; font-size: 0.9375rem; line-height: 1.6; color: var(--ink-soft); }

/* ── FAQ with an expand-all (D-10) ───────────────────────────────────────── */
.faqx { padding-block: clamp(72px, 8.4vw, 112px); }
.faqx__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
.faqx__head h2 { text-wrap: initial; }
.faqx__all { margin-top: 0; }
.faqx__list { margin-top: clamp(28px, 3.2vw, 44px); border-top: 1px solid var(--rule); }
.faqx details { border-bottom: 1px solid var(--rule-soft); }
.faqx summary {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 20px 0; cursor: pointer; list-style: none;
  font-size: clamp(1.0625rem, 1rem + 0.3vw, 1.25rem); font-weight: 500; letter-spacing: -0.012em;
}
.faqx summary::-webkit-details-marker { display: none; }
.faqx summary i { position: relative; width: 13px; height: 13px; flex: 0 0 13px; }
.faqx summary i::before, .faqx summary i::after { content: ""; position: absolute; background: var(--ink); transition: transform 0.35s var(--ease), opacity 0.35s var(--ease); }
.faqx summary i::before { left: 0; right: 0; top: 6px; height: 1.5px; }
.faqx summary i::after { top: 0; bottom: 0; left: 6px; width: 1.5px; }
.faqx details[open] summary i::after { transform: scaleY(0); opacity: 0; }
.faqx .faq__a { padding-bottom: 22px; max-width: 74ch; }
.faqx .faq__a p { font-size: 1rem; line-height: 1.66; color: var(--ink-soft); }

/* ── compare: responsive ─────────────────────────────────────────────────── */
@media (max-width: 1023px) {
  .lhero__top { grid-template-columns: minmax(0, 1fr); gap: 20px; align-items: start; }
  .lhero__h { max-width: 20ch; }
  .dclose__in { grid-template-columns: minmax(0, 1fr); }
  .rows li { grid-template-columns: minmax(0, 1fr); }
  .rows__c { grid-column: 1; grid-row: auto; margin-top: 16px; align-self: start; }
  .claims__row { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .claims__row > div + div { border-left: 0; padding-left: 0; border-top: 1px solid var(--rule-soft); }
  .dlist { grid-template-columns: minmax(0, 1fr); }
  .dlist li:nth-child(n+2) { border-top-color: var(--rule-soft); }
}
@media (max-width: 720px) {
  .open__h br, .faqx__head h2 br, .dclose__h br { display: none; }
  .gate__pair { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .gate__pair > .onrule + .onrule { margin-top: 20px; }
  .faqx__head { align-items: flex-start; }
  .ledger__foot { font-size: 0.875rem; }
}

/* D-07's own mobile contract: below the tablet break the ledger stops being a
   table and each row becomes a block with the column names generated beside
   the marks (docs/43 — "mobile labels are generated per column"). The label
   text is the same .sr string the mark already carries for a screen reader, so
   nothing is written twice and nothing is invented. A sideways-scrolling
   comparison table on a phone is the default answer, and it is a bad one: the
   source column is the first thing to fall off the edge. */
@media (max-width: 720px) {
  .ledger__scroll { overflow-x: visible; }
  .ltab { min-width: 0; }
  .ltab, .ltab tbody, .ltab tr, .ltab th, .ltab td { display: block; }
  .ltab thead { display: none; }
  .ltab tbody tr { padding: 4px 0 14px; border-bottom: 1px solid var(--rule-soft); }
  .ltab tbody th { padding: 14px 0 8px; border: 0; font-weight: 600; }
  .ltab td { display: flex; align-items: center; gap: 12px; width: auto; padding: 6px 0 6px 12px; border: 0; }
  .ltab td::before { content: attr(data-col); flex: 1; font-size: 0.875rem; color: var(--ink-mute); }
  .ltab td .sr {
    position: static; width: auto; height: auto; margin: 0; overflow: visible;
    clip: auto; white-space: normal; font-size: 0.875rem; color: var(--ink);
  }
  .ltab td .ltab__qualifier { margin: 0; flex: 0 1 135px; }
  .ltab .is-us { background: none; box-shadow: inset 2px 0 0 var(--orange); }
  .ltab .is-us::before { color: var(--orange-text); font-weight: 600; }
  .ltab__src { width: auto; }
  .ltab__grp th { padding: 26px 0 6px; }
  .ltab tbody tr:last-child th, .ltab tbody tr:last-child td { border-bottom: 0; }
  .ltab tbody tr:last-child { border-bottom: 1.5px solid rgba(34, 38, 43, 0.32); }
}

/* ═══════════════════════ blog.html — T-13, the blog index ═════════════════
   The one page in the set where cards are the right answer, because these are
   independent things of equal weight — which is the only case a card grid
   actually describes. Every other band that reached for one was rebuilt.

   What it takes from the homepage system: the two faces, The Advance buttons,
   the board menu, the double-bezel construction (the card is the frame's own
   shell and core at card size), the section rhythm.
   What it does differently: it opens as a masthead with the issue's own facts
   in the margin rather than as a headline over a screen; the newest post runs
   full measure as a lead story instead of being one of four equal boxes; the
   art is drawn and treated in the duotone docs/43 §1d specifies rather than
   photographed; and the page's dark band is the mailing list, so its closer is
   the light D-28.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── masthead — one strip, not a hero ────────────────────────────────────
   A periodical's own name sits above its lead story in a rule, not over it at
   display scale. So the page's h1 is the small type here and the newest post
   is the big type below: title left, the issue's facts run along the same
   rule on the right. */
.mast { padding-block: clamp(26px, 3vw, 40px) clamp(14px, 1.6vw, 20px); border-bottom: 2px solid var(--ink); }
.mast__in { display: flex; align-items: baseline; justify-content: space-between; gap: clamp(18px, 3vw, 48px); flex-wrap: wrap; }
.mast__h {
  font-size: clamp(1.375rem, 1.213rem + 0.661vw, 1.875rem); line-height: 1.1;
  letter-spacing: -0.02em; text-wrap: initial;
}
/* the issue's own facts, run along the masthead rule as a colophon */
.mast__meta { display: flex; flex-wrap: wrap; gap: 6px clamp(16px, 2.4vw, 34px); }
.mast__meta div { display: flex; align-items: baseline; gap: 8px; }
.mast__meta dt { font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.11em; text-transform: uppercase; color: var(--ink-mute); }
.mast__meta dd { margin: 0; font-size: 0.875rem; color: var(--ink); }

/* ── the duotone. Navy ground, orange top-light (docs/43 §1d). ───────────── */
/* isolation matters: without its own stacking context the two blend layers
   composite against the page rather than against the drawing, and the duotone
   comes out as a washed beige instead of navy under an orange key. */
.duo { position: relative; display: block; overflow: hidden; isolation: isolate; background: var(--navy); }
/* z-index: -1 is load-bearing. `filter` makes the svg its own stacking context,
   so without it the drawing paints in tree order AFTER ::before and covers the
   multiply layer — the treatment came out as a warm sepia rather than as navy
   under an orange key. .duo isolates, so the negative index stays inside it. */
.duo svg { position: relative; z-index: -1; display: block; width: 100%; height: 100%; filter: grayscale(1) contrast(1.22) brightness(1.04); }
.duo::before { content: ""; position: absolute; inset: 0; background: var(--navy); mix-blend-mode: multiply; }
.duo::after {
  content: ""; position: absolute; inset: 0; mix-blend-mode: screen;
  background: linear-gradient(var(--duo, 202deg), rgba(245, 130, 32, 0.92) 0%, rgba(245, 130, 32, 0.34) 44%, rgba(245, 130, 32, 0) 78%);
}
/* one light angle per card, so four treated drawings do not read as one picture */
.duo--1 { --duo: 196deg; }
.duo--2 { --duo: 154deg; }
.duo--3 { --duo: 216deg; }
.duo--4 { --duo: 172deg; }

/* ── a post card. The frame's shell and core, at card size. ──────────────── */
.post {
  display: flex; flex-direction: column;
  padding: 6px; border-radius: 16px;
  background: linear-gradient(180deg, #ffffff 0%, #fbf8f4 48%, #f3efe9 100%);
  border: 1px solid rgba(34, 38, 43, 0.10);
  box-shadow: var(--edge), var(--elev-2);
  transition: transform 0.7s var(--ease), box-shadow 0.7s var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .post:hover { transform: translateY(-6px); box-shadow: var(--edge), var(--elev-4); }
}
.post__art { border-radius: 11px; aspect-ratio: 16 / 10; }
.post__art svg { height: 100%; }
.post__b { display: flex; flex-direction: column; padding: clamp(18px, 1.6vw, 24px) clamp(16px, 1.4vw, 22px) clamp(18px, 1.6vw, 22px); flex: 1; }
.post__k { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.tag { font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: #2b1a08; background: var(--orange); border-radius: 3px; padding: 3px 8px; }
.post__cat { font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--orange-text); }
.post h2, .post h3 { font-family: var(--jobsight-display); font-weight: 400; letter-spacing: -0.028em; line-height: 1.1; }
.post h3 { font-size: clamp(1.2rem, 1.06rem + 0.5vw, 1.5rem); }
.post h2 a, .post h3 a { text-decoration: none; }
.post h2 a:hover, .post h3 a:hover { color: var(--orange-text); }
.post__x { margin-top: 12px; font-size: 0.9375rem; line-height: 1.6; color: var(--ink-soft); }
.post__m { display: flex; align-items: center; gap: 10px; margin-top: auto; padding-top: 18px; font-size: 0.875rem; color: var(--ink-mute); }
.post__m span { color: var(--ink); font-weight: 500; }
.post__m i { width: 3px; height: 3px; border-radius: 50%; background: rgba(34, 38, 43, 0.28); }

/* ── the lead story, as the opener ───────────────────────────────────────
   No card shell, no border, no lift: the treated drawing runs off the left
   edge of the screen and the story is set beside it at cover scale. This is
   the page's first object and the only one on it that behaves this way — the
   three below stay cards, because three equal things are what a grid is for. */
.lead { padding-bottom: clamp(30px, 3.4vw, 48px); }
.lead__bleed {
  display: grid; grid-template-columns: minmax(0, 0.96fr) minmax(0, 1.04fr);
  align-items: stretch; gap: clamp(26px, 3.6vw, 60px);
  padding-right: max(var(--gut), calc((100vw - var(--wrap)) / 2 + var(--gut)));
}
.lead__art { min-height: clamp(280px, 34vw, 470px); }
.lead__art svg { height: 100%; }
.lead__b { display: flex; flex-direction: column; justify-content: center; padding-block: clamp(18px, 2.4vw, 34px); }
.lead__h {
  font-family: var(--jobsight-display); font-weight: 400; letter-spacing: -0.03em; line-height: 1.04;
  font-size: clamp(1.875rem, 1.313rem + 2.294vw, 3.5rem); max-width: 17ch;
}
.lead__h a { text-decoration: none; }
.lead__h a:hover { color: var(--orange-text); }
.lead__x { margin-top: 18px; font-size: var(--lead); line-height: 1.58; color: var(--ink-soft); max-width: 44ch; }
.lead .post__m { margin-top: 22px; }
.lead .btn--quiet { align-self: flex-start; margin-top: 14px; }

/* ── the rest of the index ───────────────────────────────────────────────── */
.index-b { padding-bottom: clamp(64px, 7.4vw, 100px); }
.index-b__h { display: flex; align-items: baseline; gap: 18px; flex-wrap: wrap; padding-top: clamp(26px, 3vw, 40px); border-top: 1px solid var(--rule); }
.index-b__h h2 { font-size: clamp(1.4rem, 1.2rem + 0.8vw, 1.9rem); }
.index-b__h p { font-size: 0.9375rem; color: var(--ink-mute); }
.cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(20px, 2.4vw, 32px); margin-top: clamp(26px, 3vw, 40px); }

/* ── pagination (D-22) ───────────────────────────────────────────────────── */
.pager {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  margin-top: clamp(34px, 4vw, 54px); padding-top: 20px; border-top: 1px solid var(--rule);
}
.pager__prev, .pager__next {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 4px;
  font-size: 0.9375rem; font-weight: 600; color: var(--orange-text); text-decoration: none;
}
.pager__prev.is-off { color: var(--ink-mute); opacity: 0.5; }
.pager__n { display: flex; gap: 4px; }
.pager__n a {
  display: grid; place-items: center; min-width: 44px; height: 44px;
  font-size: 0.9375rem; font-variant-numeric: tabular-nums; color: var(--ink); text-decoration: none;
  border-radius: 3px; transition: background-color 0.3s var(--ease), color 0.3s var(--ease);
}
.pager__n a:hover { background: rgba(34, 38, 43, 0.06); }
/* the current page carries the same filled stage segment the primary button
   does at rest — one device, two sizes */
.pager__n a[aria-current] { color: var(--orange-text); font-weight: 700; position: relative; }
.pager__n a[aria-current]::after { content: ""; position: absolute; left: 12px; right: 12px; bottom: 8px; height: 3px; background: var(--orange); }

/* ── closer — the mailing list (D-23) as one row ─────────────────────────
   The boxiest object in the set was three grey input boxes in a row on navy.
   The row survives; the boxes do not. Each field is written on the rule it
   sits on — label above, a hairline under the text, orange when it has focus
   — so the band reads as a form on paper rather than as a widget.

   3 September: the field itself moved into the shared .onrule control, so this
   row and the comparison page's gate are now literally the same object on two
   grounds. What stayed behind here is the row: three fields and the button
   strung along one hairline across the band. */
.news { position: relative; overflow: clip; color: #eef1f4;
  background-color: var(--navy-deep); background-image: var(--vignette-soft);
  padding-block: clamp(56px, 6.4vw, 88px); box-shadow: var(--edge-ink); }
/* the navy band's own light, behind the one control on it. Cooler and quieter
   than the ink closers' — this band is navy, not charcoal, and it is a sign-up
   row rather than the end of the page. */
.bloom--news {
  right: -8%; top: -52%; width: min(760px, 88vw); height: 480px;
  background:
    radial-gradient(46% 50% at 58% 52%, rgba(245, 130, 32, 0.15), rgba(245, 130, 32, 0) 74%),
    radial-gradient(64% 56% at 40% 62%, rgba(96, 140, 190, 0.13), rgba(96, 140, 190, 0) 78%);
}
.news > .wrap { position: relative; z-index: 1; }
.news__head { display: flex; align-items: baseline; gap: clamp(14px, 2.4vw, 30px); flex-wrap: wrap; }
.news__head h2 { color: #fff; font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem); text-wrap: initial; }
.news__head p { font-size: var(--lead); line-height: 1.55; color: #c3ced9; max-width: 40ch; }
.newsrow {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
  gap: clamp(18px, 2.6vw, 40px); align-items: end;
  margin-top: clamp(34px, 4vw, 54px); padding-top: clamp(22px, 2.6vw, 32px);
  border-top: 1px solid rgba(255, 255, 255, 0.18);
}
.news__opt { font-weight: 400; letter-spacing: 0; text-transform: none; color: #9fb0c1; }
.news__go { align-self: end; }
.news .formstate { grid-column: 1 / -1; margin-top: 4px; }

/* ── blog: responsive ────────────────────────────────────────────────────── */
@media (max-width: 1023px) {
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lead__bleed { grid-template-columns: minmax(0, 1fr); gap: clamp(22px, 3vw, 34px); padding-right: 0; }
  .lead__art { min-height: 0; aspect-ratio: 16 / 9; margin-right: var(--gut); }
  .lead__b { padding-inline: var(--gut); padding-block: 0; }
  .newsrow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .news__go { grid-column: 1 / -1; justify-self: start; }
}
@media (max-width: 720px) {
  .cards { grid-template-columns: minmax(0, 1fr); }
  .newsrow { grid-template-columns: minmax(0, 1fr); }
}

/* ── ROUND 4 — the post card answers a pointer like every other surface ─────
   Round 3 lifted it six pixels, before any frame lifted at all. The set now has
   one distance, three pixels, so a reader learns the behaviour once; and the
   card takes the same travelling glass highlight, which on a card lands on its
   own artwork. The shared rules are at the end of the shared block. */
.post { position: relative; }
@media (hover: hover) and (pointer: fine) {
  .post:hover { transform: translateY(-3px); box-shadow: var(--edge), var(--elev-4); }
}
.post::before {
  content: ""; position: absolute; top: 0; left: 0; z-index: 3;
  width: 32%; height: 34%; max-height: 96px;
  background: linear-gradient(101deg,
    rgba(255, 255, 255, 0) 24%, rgba(255, 255, 255, 0.58) 50%, rgba(255, 255, 255, 0) 76%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0) 100%);
          mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0) 100%);
  opacity: 0; transform: translateX(-100%); pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) and (hover: hover) and (pointer: fine) {
  .post:hover::before { animation: sweep 0.6s var(--ease) both; }
}

/* ══════════════════════════════════════════════════════════════════════════
   motion.html — the moments

   A page whose subject is the movement itself. Every moment on it is the set's
   own markup, driven by the same stylesheet and the same script; what is added
   here is the page that holds them, and one small thing the set cannot do for
   itself (below).

   The composition is deliberately quieter than any of the five: a hairline
   between moments, a number held on that hairline, one paragraph, then the
   thing at the size it plays. Nothing competes with what is moving.
   ══════════════════════════════════════════════════════════════════════════ */

.mpage { background: var(--paper); }

.mhead { padding-block: clamp(56px, 7vw, 104px) clamp(30px, 3.4vw, 48px); }
.mhead__k {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-mute);
}
.mhead__h {
  margin-top: 18px; font-family: var(--jobsight-display); font-weight: 400;
  font-size: clamp(2.4rem, 1.6rem + 3.4vw, 4.75rem); line-height: 1.03;
  letter-spacing: -0.032em; color: var(--ink); max-width: 16ch;
}
.mhead__p {
  margin-top: 20px; max-width: 62ch;
  font-size: clamp(1.0625rem, 1rem + 0.28vw, 1.25rem); line-height: 1.58; color: var(--ink-soft);
}

/* the preference note is its own block rather than a line of the masthead, so
   the masthead is the same height whether the preference is set or not — which
   is what lets the motion page and the reduced-motion page be compared at all */
.mnote { padding-bottom: clamp(20px, 2.4vw, 32px); }
.mnote p {
  max-width: 64ch; padding: 14px 18px; border-radius: 12px;
  background: var(--paper-warm); border: 1px solid var(--rule);
  font-size: 0.9375rem; line-height: 1.55; color: var(--ink-soft);
}

.moments { padding-bottom: clamp(64px, 8vw, 128px); }

.moment { padding-block: clamp(44px, 5.2vw, 76px); border-top: 1px solid var(--rule); }
.moment__lede { position: relative; }
.moment__k {
  font-family: var(--jobsight-display); font-weight: 400; font-size: 1.25rem; line-height: 1;
  color: var(--orange-text); letter-spacing: 0.01em;
}
.moment__h {
  margin-top: 10px; font-family: var(--jobsight-display); font-weight: 400;
  font-size: clamp(1.6rem, 1.25rem + 1.5vw, 2.5rem); line-height: 1.12;
  letter-spacing: -0.026em; color: var(--ink);
}
.moment__p { margin-top: 12px; max-width: 64ch; font-size: 1.0625rem; line-height: 1.62; color: var(--ink-soft); }
.moment__ctl { margin-top: 22px; display: flex; flex-wrap: wrap; gap: 12px; }
/* a replay control is not a way onward, so it does not carry the chevron the
   rest of the set uses to say "this goes somewhere" */
.moment__ctl .btn::after { display: none; }
.moment__hint { margin-top: 10px; font-size: 0.875rem; color: var(--ink-mute); }
/* every stage but moment one's sits in the page's own column; moment one is
   the homepage's opening and is given the homepage's full width */
.moment__stage {
  margin-top: clamp(26px, 3vw, 42px);
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
}

/* moment one is the homepage's own opening, so it is given the homepage's own
   room; the rest sit in the page's column */
.moment--wide .moment__stage { margin-top: 0; max-width: none; padding-inline: 0; }
.mhero { position: relative; overflow-x: clip; padding-top: clamp(20px, 2.4vw, 40px); padding-bottom: 8px; }

.mpair { display: flex; flex-wrap: wrap; align-items: flex-start; gap: clamp(18px, 2.4vw, 34px); }
.mpair .frame--mini { max-width: 440px; flex: 1 1 340px; }

.mfaq .faq { padding-block: 0; }
.mfaq .faq__list { margin-top: 0; }
/* the copied band brings its own column with it; the stage is already in one */
.mfaq .wrap { max-width: none; padding-inline: 0; }

/* the header stage: a page you can scroll inside the page, because the one
   thing this set cannot demonstrate in place is a bar that answers a scroll */
.mstage {
  height: min(430px, 62vh); overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  border-radius: 16px; border: 1px solid rgba(34, 38, 43, 0.10);
  background: var(--paper); box-shadow: var(--edge), var(--elev-2);
}
.mstage:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; }
.mstage__in { position: relative; min-height: 200%; }
.mhdr { position: sticky; top: 0; }
.mstage__body { padding: clamp(28px, 3vw, 48px) clamp(20px, 3vw, 44px) 60vh; display: grid; gap: 22px; }
.mstage__body p {
  font-family: var(--jobsight-display); font-weight: 400;
  font-size: clamp(1.15rem, 0.95rem + 0.9vw, 1.75rem); line-height: 1.28;
  letter-spacing: -0.022em; color: var(--ink); max-width: 22ch;
}

/* ── the two pointer behaviours, shown on demand ────────────────────────────
   Every lift in the set is gated on there being a pointer, which is right, and
   which means a reader on a phone would never see one. These two rules let a
   control ask an object to demonstrate itself. They repeat the shared
   distances rather than the shared surfaces — the movement is what this page
   is about, and a colour that changes under a finger would be a colour that
   sticks. The values are the set's own, in the set's own tokens.
   Every one is headed by .mpage, the class on this page's own body, so a rule
   written for a demonstration cannot reach a button or a frame on any of the
   five pages — the thing class-audit.mjs exists to catch. */
/* ── the copied headline keeps an h1's type without being one ───────────────
   Moment one is a picture of the homepage's opening, not a heading of this
   page, so its line is a paragraph — and a paragraph does not inherit the
   display face, the optical size or the balance that h1 and h2 are given. This
   is that same rule, in the same tokens, for the one element that needs it.
   It is headed by .mpage so it cannot reach any of the five pages. */
.mpage .hero__h[role="presentation"] {
  font-family: var(--jobsight-display);
  font-weight: 400;
  font-variation-settings: "opsz" 144, "SOFT" 0, "WONK" 0;
  letter-spacing: -0.032em;
  line-height: 1.02;
  text-wrap: balance;
  font-size: var(--t1);
}

.mpage .btn.is-shown   { transform: translateY(-2px); }
.mpage .btn.is-pressed { transform: translateY(1px); }
.mpage .btn.is-shown::after { transform: rotate(45deg) translate(2px, -2px); }
.mpage .frame.is-shown { transform: translateY(-3px); box-shadow: var(--edge-soft), var(--elev-3); }
[data-motion="on"] .mpage .frame.is-shown::before { animation: sweep 0.6s var(--ease) both; }

@media (max-width: 720px) {
  .mstage { height: min(360px, 66vh); }
  .moment__ctl .btn { width: auto; }
}
