/* Torspan — "Shop Drawing" design system
   Direction A from build/04-design-benchmarks.md. Warm paper, hard ink,
   one orange that only ever means "act here", numbers in mono. */

/* ---------- fonts ---------- */
@font-face{font-family:Archivo;src:url(/fonts/archivo.woff2)format("woff2");font-weight:100 900;font-stretch:62% 125%;font-display:swap}
@font-face{font-family:InterVar;src:url(/fonts/inter.woff2)format("woff2");font-weight:100 900;font-display:swap}
@font-face{font-family:MonoVar;src:url(/fonts/mono.woff2)format("woff2");font-weight:100 800;font-display:swap}

/* ---------- tokens ---------- */
:root{
  --paper:#FBFAF7; --paper-2:#F2F0EA; --paper-3:#E7E4DC;
  --rule:#DFDBD2; --rule-hard:#B9B4A8;
  --ink:#14130F; --ink-2:#45423B; --ink-3:#6B675E;
  --signal:#B4430F; --signal-ink:#8A330B; --blueprint:#1B3A5C;
  /* --signal is 3.27:1 on --chrome. This amber is the same "act here" on a dark
     band; it was hardcoded in six places, which is how a token drifts. */
  --signal-on-dark:#E8A33D;
  --chrome:#16150F; --chrome-2:#221F18; --on-chrome:#FBFAF7; --on-chrome-2:#CFC9BC;
  --rule-a2:#14130F14; --rule-a3:#14130F26; --rule-on-dark:#ffffff26;

  --w-regular:400; --w-medium:520; --w-semibold:600; --w-bold:700;

  /* The preferred term, not the min, is what stair-stepped the home H1 into
     four ragged lines at 390 ("The paperwork's" alone is 349px at 41px type).
     Measured: 36px at 390 breaks it into three near-full lines and the clamp
     still reaches 72px, so nothing above 875px moves. */
  --display:clamp(2.25rem,.44rem + 7.42vi,4.5rem);
  --display-tracking:clamp(-1.6px,-0.6px - 0.12vw,-0.5px);
  --t-1:clamp(2rem,1.3rem + 2.6vi,3.5rem);
  --t-2:clamp(1.75rem,1.2rem + 1.9vi,2.75rem);
  --t-3:clamp(1.5rem,1.15rem + 1.1vi,2rem);
  /* --t-4 and --t-5 were fixed while every step above them was clamped, so on a
     phone the scale collapsed: h2:h3 measured 1.167 at 390 against 1.81 at 1280,
     i.e. a section heading and a card heading ranked the same on the device that
     carries the traffic. Both clamps top out at their old value, so desktop is
     untouched. */
  --t-4:clamp(1.3125rem,1.19rem + .49vi,1.5rem);
  --t-5:clamp(1.125rem,1.05rem + .3vi,1.25rem);
  --t-6:1.0625rem;
  --body:1.0625rem; --body-lg:1.1875rem; --label:.8125rem;

  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px; --s-5:24px; --s-6:32px;
  --s-7:48px; --s-8:64px; --s-9:96px; --s-10:128px; --s-11:160px;

  --page:1120px; --gutter:20px;
  --dur-instant:100ms; --dur-quick:180ms; --dur-calm:320ms;
  --ease:cubic-bezier(.2,0,0,1);
}
@media(min-width:744px){:root{--gutter:32px}}
@media(min-width:1100px){:root{--gutter:48px}}

/* ---------- base ---------- */
*,*::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:InterVar,-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;
  font-size:var(--body);line-height:1.55;font-weight:var(--w-regular);
  font-feature-settings:"cv01","ss03";font-variation-settings:"opsz" auto;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
h1,h2,h3,h4{font-family:Archivo,InterVar,system-ui,sans-serif;font-variation-settings:"wdth" 112;margin:0;text-wrap:balance}
h1{font-size:var(--display);line-height:1.02;letter-spacing:var(--display-tracking);font-weight:var(--w-bold)}
h2{font-size:var(--t-2);line-height:1.06;letter-spacing:-.022em;font-weight:var(--w-bold)}
h3{font-size:var(--t-4);line-height:1.25;letter-spacing:-.012em;font-weight:var(--w-semibold)}
h4{font-size:var(--t-5);line-height:1.3;letter-spacing:-.012em;font-weight:var(--w-semibold)}
p{margin:0 0 var(--s-5);max-width:66ch}
a{color:var(--signal-ink);text-decoration:none;border-bottom:1px solid var(--rule-hard);transition:border-color var(--dur-instant) var(--ease),color var(--dur-instant) var(--ease)}
a:hover{border-bottom-color:var(--signal)}
strong{font-weight:var(--w-semibold)}
hr{border:0;border-top:1px solid var(--rule);margin:var(--s-8) 0}
img{max-width:100%;height:auto;display:block}
::selection{background:#B4430F26}
:focus-visible{outline:2px solid var(--signal);outline-offset:3px;border-radius:2px}

.wrap{width:100%;max-width:var(--page);margin-inline:auto;padding-inline:var(--gutter)}
.narrow{max-width:42rem}
.mono{font-family:MonoVar,ui-monospace,SFMono-Regular,Menlo,monospace;font-variant-numeric:tabular-nums}
.label{font-family:MonoVar,ui-monospace,monospace;font-size:var(--label);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2);font-weight:var(--w-medium)}
.lead{font-size:var(--body-lg);color:var(--ink-2);max-width:60ch}
.muted{color:var(--ink-2)}
.center{text-align:center;margin-inline:auto}

/* ---------- header ---------- */
.site-head{position:sticky;top:0;z-index:60;background:var(--chrome);color:var(--on-chrome)}
.site-head > .wrap{display:flex;align-items:center;gap:var(--s-5);min-height:60px}
.mobile-nav .wrap{display:block}
.brand{display:flex;align-items:center;min-height:44px;gap:10px;color:var(--on-chrome);border:0;font-family:Archivo,sans-serif;font-weight:var(--w-bold);font-size:1.15rem;letter-spacing:-.01em;font-variation-settings:"wdth" 112}
.brand svg,.brand img{display:block;flex:none}
.brand img{width:26px;height:26px;object-fit:contain}
.foot-brand .brand img{width:24px;height:24px}
.nav{display:none;margin-left:auto;align-items:center;gap:var(--s-2);align-self:stretch}
@media(min-width:1000px){.nav{display:flex}}
/* The trigger has to fill the bar's height or the panel below it can only start
   inside the dark band: measured .nav-item bottom 49px against a 60px header,
   so the paper panel painted over the last 5px of chrome and left a 6px dead
   strip between button and panel that dropped :hover on the way down. */
.nav-item{position:relative;display:flex;align-items:center;align-self:stretch}
.nav-link{display:inline-flex;align-items:center;gap:6px;padding:9px 12px;color:var(--on-chrome-2);border:0;font-size:.95rem;font-weight:var(--w-medium);border-radius:3px;background:none;cursor:pointer;font-family:inherit}
.nav-link:hover,.nav-item:hover .nav-link{color:var(--on-chrome);background:var(--chrome-2)}
.nav-link[aria-current]{color:var(--signal-on-dark)}  /* --signal is only 3.27:1 on --chrome */
.nav-link .chev{width:9px;height:9px;opacity:.65}
.menu{position:absolute;top:100%;left:0;min-width:250px;max-height:calc(100dvh - 72px);overflow-y:auto;background:var(--paper);border:1px solid var(--rule-hard);border-radius:4px;padding:var(--s-2);box-shadow:0 14px 40px #14130f1f;opacity:0;visibility:hidden;transform:translateY(-4px);transition:all var(--dur-quick) var(--ease)}
/* aria-expanded on those buttons was a standing lie: the panels opened on hover
   and focus only, so at 1000-1279px — an iPad in landscape, where .burger is
   display:none — the primary navigation needed a pointer that a touch screen
   does not have. The attribute the markup already carried now drives the panel. */
.nav-item:hover .menu,.nav-item:focus-within .menu,
.nav-link[aria-expanded="true"] + .menu{opacity:1;visibility:visible;transform:none}
.menu a{display:block;padding:9px 12px;color:var(--ink);border:0;border-radius:3px;font-size:.95rem}
.menu a:hover{background:var(--paper-2);color:var(--signal-ink)}
.menu a[aria-current]{color:var(--signal-ink)}  /* current page; was 18 inline styles */
.menu a span{display:block;color:var(--ink-2);font-size:.8125rem;line-height:1.35;margin-top:2px}
.head-cta{margin-left:auto;display:flex;align-items:center;gap:var(--s-3)}
@media(min-width:1000px){.head-cta{margin-left:var(--s-4)}}
/* white-space: a label that breaks mid-phrase ("See it / work") is never the
   right answer, and in the header it grew the sticky bar to 79px at 320px wide,
   which the drawer's own max-height:calc(100dvh - 60px) then measured wrong. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:11px 18px;min-height:44px;border-radius:3px;font-weight:var(--w-semibold);font-size:.95rem;border:1px solid transparent;cursor:pointer;transition:all var(--dur-instant) var(--ease);font-family:inherit;line-height:1.2;white-space:nowrap}
.btn-primary{background:var(--signal);color:#fff;border-color:var(--signal)}
.btn-primary:hover{background:var(--signal-ink);border-color:var(--signal-ink)}
.btn-ghost{color:var(--ink);border-color:var(--rule-hard);background:none}
.btn-ghost:hover{border-color:var(--ink);background:var(--paper-2)}
.btn-on-dark{color:var(--on-chrome);border-color:#ffffff33;background:none}
.btn-on-dark:hover{border-color:var(--on-chrome);background:#ffffff14}
/* .head-cta already carries margin-left:auto. A second one here split the
   leftover space between the two of them, so the button landed wherever the
   arithmetic put it — 36px/36px at 390, x=386-505 in an 820px bar. One auto,
   one pair, flush right. */
.burger{display:inline-flex;align-items:center;justify-content:center;background:none;border:1px solid #ffffff2e;border-radius:3px;padding:9px 11px;min-width:44px;min-height:44px;color:var(--on-chrome);cursor:pointer}
@media(min-width:1000px){.burger{display:none}}
/* Under 375px the bar cannot hold wordmark + button + burger without the button
   breaking in half. The burger is the one that has to survive; the CTA is a
   duplicate of the hero's primary and of a drawer row. */
@media(max-width:374px){.head-cta{display:none}.burger{margin-left:auto}}
.burger svg{display:block}
.mobile-nav{display:none;background:var(--chrome);border-top:1px solid #ffffff1a;padding:var(--s-4) 0 var(--s-6);
  max-height:calc(100vh - 60px);max-height:calc(100dvh - 60px);overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
.mobile-nav.open{display:block}
@media(min-width:1000px){.mobile-nav.open{display:none}}
.mobile-nav .group{padding:var(--s-3) 0;border-bottom:1px solid #ffffff14}
.mobile-nav .group:last-child{border-bottom:0}
/* one declaration; this was two rules two lines apart, which is the residue the
   overlapping-columns bug came out of */
.mobile-nav .label{color:#8c8779;margin-bottom:var(--s-2);display:block;
  white-space:normal;overflow-wrap:anywhere;max-width:100%}
.mobile-nav a{display:block;padding:11px 0;color:var(--on-chrome);border:0;font-size:1.05rem}
/* On the iPad the drawer was one 756px column, 1315px tall inside 1119px of
   screen — a menu you have to scroll while 60% of the width is empty. Measured
   with columns:2 at 820: 723px, scrollHeight === clientHeight, whole menu on
   one screen. Groups stay intact, so no cluster splits across the gutter. */
@media(min-width:744px) and (max-width:999px){
  .mobile-nav .wrap{columns:2;column-gap:var(--s-8)}
  .mobile-nav .group{break-inside:avoid}
}

/* ---------- sections ---------- */
/* 96px top AND bottom meant every boundary spent 192px of a 844px phone, on
   pages already running 20-39 screens. The step now scales with the screen it
   is pacing; 1000px up is exactly what it was. */
.section{padding-block:var(--s-8)}
@media(min-width:744px){.section{padding-block:var(--s-9)}}
@media(min-width:1000px){.section{padding-block:var(--s-11)}}
.section-tight{padding-block:var(--s-8)}
.section-paper2{background:var(--paper-2)}
.section-chrome{background:var(--chrome);color:var(--on-chrome)}
.section-chrome h2,.section-chrome h3{color:var(--on-chrome)}
.section-chrome p{color:var(--on-chrome-2)}
.section-chrome .label{color:#8c8779}
.section-chrome a{color:var(--signal-on-dark);border-bottom-color:#ffffff2e}
.rule-top{border-top:1px solid var(--rule)}
.section-head{max-width:52rem;margin-bottom:var(--s-7)}
.section-head .label{display:block;margin-bottom:var(--s-3)}
.section-head p{margin-top:var(--s-4);color:var(--ink-2);font-size:var(--body-lg)}

/* ---------- hero ---------- */
.hero{padding-block:var(--s-8) var(--s-9);border-bottom:1px solid var(--rule)}
@media(min-width:1000px){.hero{padding-block:var(--s-9) var(--s-10)}}
/* 999px was the only pill in a sheet whose every other corner is 2, 3 or 4px —
   a SaaS badge in a shop-drawing system, used 18 times. */
.hero .kicker{display:inline-flex;align-items:center;gap:9px;border:1px solid var(--rule-hard);border-radius:3px;padding:6px 13px;margin-bottom:var(--s-6)}
.hero h1{max-width:17ch}
.hero .lead{margin-top:var(--s-5);font-size:clamp(1.125rem,1rem + .6vi,1.375rem);line-height:1.45}
.hero-actions{display:flex;flex-wrap:wrap;gap:var(--s-3);margin-top:var(--s-7)}
.hero-note{margin-top:var(--s-5);color:var(--ink-3);font-size:.9375rem}

/* ---------- grids & cards ---------- */
.grid{display:grid;gap:var(--s-5)}
/* A grid item's automatic minimum is min-content, so one wide child (a table
   carrying min-width:520px) blows the whole track past the viewport and the
   PAGE scrolls sideways instead of the table scrolling inside its wrap. */
.grid>*{min-width:0}
.g2{grid-template-columns:1fr}
.g3{grid-template-columns:1fr}
@media(min-width:744px){.g2{grid-template-columns:repeat(2,1fr)}.g3{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1000px){.g3{grid-template-columns:repeat(3,1fr)}}
/* .g2 split at 744, so an 820 iPad — the width this site is designed for first —
   gave a table 330-366px against the 348px the same table gets on a 390 phone.
   Measured on /hours at 820: the crew week showed Mon-Wed of a five-day week
   beside a paragraph about Wednesday, and the payroll CSV showed 4 of 7 columns
   beside a paragraph about columns 5, 6 and 7. Eight clipped blocks became zero
   with this one rule. Browsers without :has() get today's behaviour. */
@media(max-width:999px){
  .g2:has(.table-wrap),.g2:has(.scroll-x){grid-template-columns:1fr}
}
.card{background:var(--paper);border:1px solid var(--rule);border-radius:4px;padding:var(--s-5)}
.card h3{margin-bottom:var(--s-3)}
.card p:last-child{margin-bottom:0}
/* .card-flat is a .card variant and /company's numbered blocks are built from
   it; without it here the page hand-rolled the same recipe inline ten times and
   drifted 4px of margin off the component. */
.card .num,.card-flat .num{font-family:MonoVar,monospace;font-size:var(--label);color:var(--signal-ink);letter-spacing:.06em;display:block;margin-bottom:var(--s-3)}
.card-flat{background:none;border:0;border-top:1px solid var(--rule-hard);border-radius:0;padding:var(--s-5) 0 0}

/* ---------- doctrine / quote ---------- */
.doctrine{border-left:2px solid var(--blueprint);padding:var(--s-2) 0 var(--s-2) var(--s-5);max-width:38rem}
.doctrine p{font-family:Archivo,sans-serif;font-variation-settings:"wdth" 112;font-size:var(--t-4);line-height:1.28;letter-spacing:-.012em;color:var(--blueprint);font-weight:var(--w-medium);margin:0}
.wall-line{font-family:Archivo,sans-serif;font-variation-settings:"wdth" 112;font-size:var(--t-3);line-height:1.15;letter-spacing:-.022em;font-weight:var(--w-bold);max-width:22ch;margin:0}

/* ---------- artifact: DOM-built product UI ---------- */
.artifact{background:var(--paper-2);border:1px solid var(--rule-hard);border-radius:4px;overflow:hidden}
/* flex-wrap:wrap places an item by its HYPOTHETICAL width, so min-width:0 on the
   title does not save it: the moment the title exceeded the bar minus the dot,
   the whole title jumped to line two and left an 8px dot alone on a 20px line.
   11 of 46 bars at 390. nowrap + flex:1 1 auto lets the title wrap inside the
   bar, beside its dot, where it belongs. */
.artifact-bar{display:flex;flex-wrap:nowrap;align-items:center;gap:8px;padding:10px 14px;background:var(--paper-3);border-bottom:1px solid var(--rule-hard)}
.artifact-bar .dot{width:8px;height:8px;border-radius:50%;background:var(--rule-hard);flex:none}
.artifact-bar .t{font-family:MonoVar,monospace;font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-2);
  flex:1 1 auto;min-width:0;overflow-wrap:anywhere}
/* .record-row is also used directly in an artifact body, without a .record
   wrapper, so the container below has to exist at both levels or the same row
   stacks on one page and sets ragged-left on another. Nearest container wins,
   so a .record inside an .artifact-body still measures itself. */
.artifact-body{padding:var(--s-5);container-type:inline-size}
/* 80% is a messaging-app convention for bubbles that ALTERNATE sides. Every
   incoming bubble here is left-aligned, so the 20% was permanently dead: 240px
   of bubble inside a 348px phone, and the long reply on the home page ran 13
   lines. .msg.out stays inset because that offset is the only thing saying "a
   person said this"; it is also dark-on-paper, so the grammar survives. */
.msg{max-width:min(100%,46rem);background:var(--paper);border:1px solid var(--rule);border-radius:4px;padding:12px 14px;margin-bottom:var(--s-3);font-size:1rem}
.msg.out{margin-left:auto;max-width:min(88%,42rem);background:var(--chrome);color:var(--on-chrome);border-color:var(--chrome)}
.msg .who{display:block;font-family:MonoVar,monospace;font-size:.6875rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);margin-bottom:5px}
.msg.out .who{color:#9a9483}
.record{background:var(--paper);border:1px solid var(--rule-hard);border-radius:4px;padding:var(--s-4);margin-top:var(--s-4);container-type:inline-size}
.record-row{display:flex;justify-content:space-between;gap:var(--s-4);padding:9px 0;border-bottom:1px solid var(--rule);font-size:.9375rem}
.record-row:last-child{border-bottom:0}
.record-row .k{font-family:MonoVar,monospace;font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3);white-space:nowrap}
.record-row .v{text-align:right;font-weight:var(--w-medium)}
.record-row .v.blank{color:var(--signal-ink);font-family:MonoVar,monospace;font-size:.875rem}
/* The two-column spec-sheet read is right when the row is wide. When it is not,
   the value wraps and sets as ragged-LEFT prose with no fixed edge to scan
   down — 62 of 142 rows at 390, 50 at 820, and "AM" alone on a right-aligned
   second line. A viewport query is the wrong instrument: rows measure 266-332px
   at 820 against 316px at 390, because the row's width comes from its card, not
   from the screen. Fires at 390 and 820, does not fire at 1280. */
@container (max-width:380px){
  .record-row{display:block}
  .record-row .k{display:block;white-space:normal;margin-bottom:2px}
  .record-row .v{display:block;text-align:left}
}
/* .cite is named for citations and carries the editorial asides: 163 blocks and
   ~4,100 words of running prose set in mono site-wide. The system says numbers
   in mono — and it is making prose sans that lets the CSV, the email headers and
   the record values read as machine output instead of as more commentary.
   Strings that genuinely are machine output carry .mono inside the block. */
.cite{font-family:InterVar,-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;
  font-size:.9375rem;color:var(--ink-2);border-top:1px dashed var(--rule-hard);margin-top:var(--s-4);padding-top:var(--s-3)}

/* ---------- blocks that scroll sideways ----------
   Ten <pre> blocks on eight pages carried the same nine inline declarations,
   which is how the two that differ (the CSV sample, the day-rate maths) drifted
   a size and a leading away from the other eight. One class; the variants say
   what they are with one property each.

   Measured 2026-08-22 at 390px and at 820px: nine of the ten clip their longest
   line — the CSV sample by 279px, the export tree by 262px — at BOTH widths,
   because both put them in a half-width grid column. Eleven .table-wraps clip
   the same way. A row of CSV is wider than a phone at any type size, so the
   content has to scroll; what was missing was any sign that it could. iOS and
   iPadOS draw an overlay scrollbar that appears only once you have already
   scrolled, so on the surface this site is built for first, the sixth column of
   the payroll file — the column the paragraph beside it is entirely about —
   just looked like it wasn't there.

   Two gradients per edge. The pair painted in the block's own --cb-bg are
   attached `local`, so they ride the content: while there is nothing to scroll
   to they sit over the shadow pair and hide it, and they slide away to uncover
   a shadow the moment there is. No JS, nothing to keep in sync, and it costs
   nothing when the block already fits. */
.scroll-x,.table-wrap{
  --cb-bg:var(--paper);
  overflow-x:auto;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;
  background-image:
    linear-gradient(to right,var(--cb-bg),rgba(251,250,247,0)),
    linear-gradient(to left,var(--cb-bg),rgba(251,250,247,0)),
    linear-gradient(to right,rgba(20,19,15,.17),rgba(20,19,15,0)),
    linear-gradient(to left,rgba(20,19,15,.17),rgba(20,19,15,0));
  background-position:left center,right center,left center,right center;
  background-repeat:no-repeat;
  background-size:26px 100%,26px 100%,11px 100%,11px 100%;
  background-attachment:local,local,scroll,scroll;
}
/* The cover has to be the colour that is actually showing at the block's edge
   or it reads as a smear. A .table-wrap paints nothing of its own, so that
   colour is whatever it is sitting on. Measured: every one of them resolves to
   --paper or --paper-2 and nothing else. .record/.card come last on purpose —
   a record inside a paper-2 section is still paper, and at equal specificity
   source order is what settles it. */
.section-paper2 :is(.scroll-x,.table-wrap),.artifact :is(.scroll-x,.table-wrap){--cb-bg:var(--paper-2)}
.record :is(.scroll-x,.table-wrap),.card :is(.scroll-x,.table-wrap){--cb-bg:var(--paper)}

/* A code block is the opposite case, and reading it as "match the surroundings"
   is a real bug I shipped for one pass: these declare a background ON PURPOSE,
   a shade off the panel they sit in, so that the sample reads as a thing quoted
   rather than as more panel. Both blocks on the home page contradict their
   container — the paper one sits in a paper-2 artifact, the paper-2 one sits in
   a paper record — so taking the colour from the surroundings swapped the two
   of them. A code block's cover comes from its own declaration. Specificity is
   the pair, so it has to be a two-class selector and it has to come after. */
.codeblock.scroll-x{--cb-bg:var(--paper);background-color:var(--cb-bg);
  margin:0;font-size:.8125rem;line-height:1.7;padding:var(--s-3);
  border:1px solid var(--rule);border-radius:3px}

/* ---------- tables ---------- */
.table-wrap{max-width:100%;border:1px solid var(--rule-hard);border-radius:4px}
/* min-width:520px was a blanket floor aimed at the wide comparison tables and
   applied to all 46. Twenty-one of them have two columns and fit a phone: the
   reference table on /drawings rendered "drawing · cut sheet · submitta",
   "As printed, plus the issue dat" — cut mid-word — under a paragraph that says
   "Both are readable on a phone." A table cannot shrink below its own
   min-content, so the genuinely wide ones (the CSV export, the spreadsheet mock)
   still scroll, from their content instead of from a hand-set number.
   Measured at 390 with the floor gone: 19 of 21 tables fit whole. */
table{border-collapse:collapse;width:100%;font-size:.9375rem}
th,td{text-align:left;padding:13px 16px;border-bottom:1px solid var(--rule);vertical-align:top}
@media(max-width:539px){th,td{padding:11px 10px}}
th{font-family:MonoVar,monospace;font-size:.75rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2);background:var(--paper-2);font-weight:var(--w-medium)}
tbody tr:last-child td{border-bottom:0}
/* Tabular figures buy nothing left-aligned — the glyphs are equal width, the
   decimals still land in different places. On /hours the ACTUAL column ended
   96.0 then 1,026.0 and the total did not line up with the column it totals, on
   a page whose whole argument is that the columns add up. */
td.num,th.num{font-family:MonoVar,monospace;font-variant-numeric:tabular-nums;text-align:right}

/* ---------- lists ---------- */
.list{list-style:none;padding:0;margin:0 0 var(--s-5);max-width:60ch}
.list li{padding:11px 0 11px 26px;border-bottom:1px solid var(--rule);position:relative}
.list li:last-child{border-bottom:0}
/* a bullet is not an action: 207 of these site-wide, 26 on /pricing alone,
   all wearing the one colour that is supposed to mean "act here" */
.list li::before{content:"";position:absolute;left:4px;top:19px;width:7px;height:1px;background:var(--ink-3)}
.list-check li::before{content:"✓";font-family:MonoVar,monospace;background:none;color:var(--signal-ink);width:auto;height:auto;top:11px;left:0;font-size:.875rem}
.list-no li::before{content:"×";font-family:MonoVar,monospace;background:none;color:var(--ink-3);width:auto;height:auto;top:11px;left:0;font-size:1rem}

/* ---------- on-page nav ----------
   The <ul> carries an inline font-size:.8125rem on every page. Styling the
   anchor (which has no inline style) lifts both the type and the tap target
   without needing !important or 16 hand edits.
   Measured: 7px of padding landed these at 43.1px and 38.3px — a pass that
   missed by under a pixel is still a miss, so the padding is set from the
   target, not by eye. */
nav[aria-label="On this page"] a{display:inline-block;padding-block:11px;font-size:.9375rem}
/* The <ul> variant on 17 pages inherited the global a{border-bottom} and then
   had it pushed 11px clear of the text by that padding, so each row drew a
   detached grey stub of a different length: four broken rules, not a list of
   links. This is the same failure the .eyebrow-line comment below diagnosed —
   a border draws across the whole box — fixed in one place and left standing in
   seventeen. The rule moves to the <li>, where a full-width row is what it is.
   /for-foremen/ and /for-owners/ build the same nav out of .toc-row anchors and
   carried the type and padding inline, so neither the size nor the target above
   could reach them. One component, one appearance, and one colour: a jump link
   is "act here", so both variants say so. */
nav[aria-label="On this page"] li{border-bottom:1px solid var(--rule)}
nav[aria-label="On this page"] li:last-child{border-bottom:0}
nav[aria-label="On this page"] li a{display:block;border-bottom:0;padding-block:12px;font-size:.9375rem}
nav[aria-label="On this page"] .toc-row{display:block;font-size:.9375rem;padding-block:12px;
  border-bottom:1px solid var(--rule)}
nav[aria-label="On this page"] .toc-row:last-child{border-bottom:0}
nav[aria-label="On this page"] .toc-row,
nav[aria-label="On this page"] li a{color:var(--signal-ink)}
/* A citation sitting alone in its own flex column is a target, not a word in a
   sentence; 21px of statute link is not something you hit from a lift. */
.cite-row a{display:inline-block;padding-block:12px;margin-block:-12px;border-bottom:0;
  text-decoration:underline;text-decoration-color:var(--rule-hard);
  text-decoration-thickness:1px;text-underline-offset:3px}
.cite-row a:hover{text-decoration-color:var(--signal)}

/* ---------- faq ---------- */
.qa{border-top:1px solid var(--rule-hard)}
.qa details{border-bottom:1px solid var(--rule);padding:var(--s-4) 0}
.qa summary{cursor:pointer;font-weight:var(--w-semibold);font-size:var(--t-5);list-style:none;display:flex;justify-content:space-between;gap:var(--s-4);align-items:flex-start;min-height:44px}
.qa summary::-webkit-details-marker{display:none}
.qa summary::after{content:"+";font-family:MonoVar,monospace;color:var(--signal-ink);font-size:1.25rem;line-height:1;flex:none}
.qa details[open] summary::after{content:"–"}
.qa details p{margin-top:var(--s-4);margin-bottom:0;color:var(--ink-2)}

/* ---------- cta band ---------- */
.cta-band{background:var(--chrome);color:var(--on-chrome);padding-block:var(--s-9)}
.cta-band h2{color:var(--on-chrome);max-width:20ch}
.cta-band p{color:var(--on-chrome-2);max-width:52ch}
.cta-band .btn-primary:hover{background:#C94D12;border-color:#C94D12}

/* ---------- footer ---------- */
.site-foot{background:var(--chrome);color:var(--on-chrome-2);padding-block:var(--s-8) var(--s-6);border-top:1px solid #ffffff1a}
/* Four tracks at 744 gave an 820 iPad 210/150/150/150: "Answers off the prints"
   and "Reports and the office" wrapped to 55px rows against 44px neighbours and
   column 4 ended 400px above column 2. Same two breakpoints .g3 already uses. */
.foot-grid{display:grid;gap:var(--s-6);grid-template-columns:1fr}
@media(min-width:744px){.foot-grid{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1000px){.foot-grid{grid-template-columns:1.4fr 1fr 1fr 1fr}}
.site-foot h4{font-family:MonoVar,monospace;font-size:var(--label);letter-spacing:.06em;text-transform:uppercase;color:#8c8779;font-weight:var(--w-medium);margin-bottom:var(--s-3)}
.site-foot a{display:flex;align-items:center;min-height:44px;color:var(--on-chrome-2);border:0;padding:4px 0;font-size:.9375rem}
.site-foot a:hover{color:var(--signal)}
.foot-brand p{color:#8c8779;font-size:.9375rem;max-width:34ch;margin-top:var(--s-4)}
.foot-legal{display:flex;flex-wrap:wrap;gap:var(--s-3) var(--s-6);justify-content:space-between;align-items:center;margin-top:var(--s-7);padding-top:var(--s-5);border-top:1px solid #ffffff14;color:#85806f;font-size:.875rem}  /* #7d7869 was 4.15:1 */
/* inline-block + padding takes the legal row from a 27px target to 45px without
   moving the baseline; these are standalone links, not links inside a sentence,
   so the WCAG 2.5.8 inline exception does not cover them. min-width carries the
   short labels ("Terms" measured 43.5px wide) over the same 44px floor. */
.foot-legal a{display:inline-block;min-height:0;min-width:44px;text-align:center;padding-block:12px;color:#85806f}
/* The email is the tail of a sentence, not a standalone label, so the WCAG 2.5.8
   inline exception covers it and the rule above does not: at 15px inside a 13px
   mono address it read as a different size, and its 12px of block padding
   inflated the line box until the two wrapped address lines sat 33px apart
   instead of 20px — two paragraphs where there is one. */
.foot-legal .addr a{min-width:0;text-align:left;font-size:inherit;min-height:0;
  padding-block:12px;margin-block:-12px}
.foot-legal .addr{font-family:MonoVar,monospace;font-size:.8125rem;letter-spacing:.02em}

/* ---------- eyebrow ----------
   The two-line company line above the H1 on / and the Company cluster. It was
   hand-styled inline on each of the four pages that carry it and drifted into
   three different treatments — 12px vs 13px, ink vs signal-ink, with and
   without the padding. One component, so it can only look like one thing.
   The underline is text-decoration, not border-bottom: a border draws across
   the whole box, so on a 390px screen the wrapped two-line link rendered a
   full-width rule that read as a section divider and cut the eyebrow's second
   line away from its first. */
.eyebrow{margin:0 0 var(--s-6)}
/* padding takes the single-line case from 34px to 44px; the negative margin
   gives back exactly what the padding added, so the eyebrow sits where it sat */
.eyebrow-line{display:inline-block;max-width:100%;padding-block:13px;margin-block:-3px -7px;
  font-size:.75rem;line-height:1.5;color:var(--ink);border-bottom:0}
a.eyebrow-line{text-decoration:underline;text-decoration-color:var(--rule-hard);
  text-decoration-thickness:1px;text-underline-offset:3px}
a.eyebrow-line:hover{text-decoration-color:var(--signal);color:var(--signal-ink)}
.eyebrow-sub{display:block;margin-top:3px;font-size:.75rem;line-height:1.5;color:var(--ink-3)}
.eyebrow-sub.sentence{letter-spacing:.02em;text-transform:none}

/* ---------- dark bands ----------
   .section-chrome and .cta-band are the same near-black surface, but only the
   first one ever got text rules, so every closing CTA on all 22 pages drew its
   links in --signal-ink on --chrome: 2.23:1. Separately, .section-head p is
   declared later in this sheet at equal specificity, which quietly repainted
   chrome-band leads --ink-2 on black: 1.82:1. Both are one bug — a light-surface
   rule reaching into a dark band — so both are settled here, at the end, where
   source order decides. Anything added below this point has to think about it. */
.cta-band{color:var(--on-chrome)}
.cta-band h3,.cta-band h4{color:var(--on-chrome)}
.cta-band p,.cta-band li{color:var(--on-chrome-2)}
.cta-band .label,.cta-band .muted{color:#8c8779}
.section-chrome .section-head p,.cta-band .section-head p{color:var(--on-chrome-2)}
/* :not(.btn) matters — a button on a dark band paints its own background and
   already carries its own text colour; letting the band's link colour reach it
   put amber type on the orange primary button at 2.6:1. */
.section-chrome a:not(.btn),.cta-band a:not(.btn){color:var(--signal-on-dark);border-bottom-color:#ffffff2e}
.section-chrome a:not(.btn):hover,.cta-band a:not(.btn):hover{border-bottom-color:var(--signal-on-dark)}
/* --signal is 3.27:1 on --chrome. It is the right colour on paper and the wrong
   one on the chrome, and it was reaching the chrome three ways: the current-page
   link in the drawer, the current-page link in the footer, and every footer
   hover. Same amber as the desktop nav uses. */
.mobile-nav a[aria-current],.site-foot a[aria-current]{color:var(--signal-on-dark)}
.mobile-nav a:hover,.site-foot a:hover{color:var(--signal-on-dark)}

/* A component that paints its own light background has to declare its own ink.
   Inheriting it is correct right up until the component is dropped into a dark
   band — which is what put the weekly-hours table's rows at 1.09:1, white type
   on paper, unreadable on /how-it-works/ and /safety/. */
.artifact,.record,.card,.menu{color:var(--ink)}
.section-chrome :is(.artifact,.record,.card) :is(h2,h3,h4,p),
.cta-band :is(.artifact,.record,.card) :is(h2,h3,h4,p){color:var(--ink)}
.section-chrome :is(.artifact,.record,.card) :is(.label,.muted,.cite),
.cta-band :is(.artifact,.record,.card) :is(.label,.muted,.cite){color:var(--ink-2)}
.section-chrome :is(.artifact,.record,.card) a,
.cta-band :is(.artifact,.record,.card) a{color:var(--signal-ink);border-bottom-color:var(--rule-hard)}
.section-chrome .msg.out,.cta-band .msg.out{color:var(--on-chrome)}
.section-chrome .msg.out .who,.cta-band .msg.out .who{color:#9a9483}

/* Standalone links sitting alone in a record row or a citation line are not
   links inside a sentence, so they get a real target. Padding grows the hit
   box, the equal negative margin keeps the line box exactly where it was, and
   the underline moves to text-decoration so it stays glued to the glyphs
   instead of dropping to the bottom of the new padding. */
/* .hero-note carries the audience router — "Foremen · Crews · Owners", the most
   important routing decision a first-time visitor makes — at 20px tall, and the
   card CTAs ("How hours get made →") at 21px. Same pattern, same two lines, the
   two places it was missed. Measured: 20 -> 47px and 21 -> 50px, baselines
   unmoved. */
.record-row a,.cite a,.hero-note a,.card p:last-child a{display:inline-block;padding-block:12px;margin-block:-12px;
  border-bottom:0;text-decoration:underline;text-decoration-color:var(--rule-hard);
  text-decoration-thickness:1px;text-underline-offset:3px}
.record-row a:hover,.cite a:hover,.hero-note a:hover,.card p:last-child a:hover{text-decoration-color:var(--signal)}

/* ---------- motion ---------- */
/* content is visible by default; only a JS-capable page arms the reveal */
.rise{transition:opacity var(--dur-calm) var(--ease),transform var(--dur-calm) var(--ease)}
.js .rise{opacity:0;transform:translateY(8px)}
.rise.in{opacity:1;transform:none}
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
  .rise{opacity:1;transform:none}
}
