/* ============================================================================
   intrnls.com — marketing site stylesheet
   ----------------------------------------------------------------------------
   PALETTE: none declared here. Every colour in this file resolves to a variable
   from css/tokens.css (a verbatim sync of brand/tokens.css). There is no raw hex
   below this line — check it with:  grep -nE "#[0-9a-fA-F]{3,8}" web/css/site.css
   Derived values use color-mix() over token variables ONLY, never a new hue.

   GROUND: set with data-ground="lab" | "cashmere" on <html> (page ground) or on
   any <section> (a band that flips ground). One flip per page, maximum.
   ============================================================================ */

/* ---------------------------------------------------------------------------
   1. TYPE — self-hosted, no CDN, works offline.
   Cera Pro is the owned display face and is NOT redistributed here; when the
   licensed files land, drop them in web/fonts/ and add the @font-face blocks
   ABOVE these — the stack already prefers 'Cera Pro'.

   ⚠ DEPLOY: Poppins is licensed under the SIL Open Font License 1.1, which
   requires the licence to travel WITH the font files. The copyright notice is
   inside each .woff2, but the licence text is NOT — it lives only in
   web/fonts/OFL.txt. Copying the web/ tree satisfies this automatically; an
   asset-graph build that emits only referenced files would silently drop it.
   Ship web/fonts/OFL.txt with the fonts. It costs 4 KB.
   Inventory + reasoning: THIRD-PARTY-NOTICES.md.
   --------------------------------------------------------------------------- */
@font-face{font-family:'Poppins';font-style:normal;font-weight:400;font-display:swap;
  src:url('../fonts/poppins-latin-400.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Poppins';font-style:normal;font-weight:500;font-display:swap;
  src:url('../fonts/poppins-latin-500.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Poppins';font-style:normal;font-weight:600;font-display:swap;
  src:url('../fonts/poppins-latin-600.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Poppins';font-style:normal;font-weight:700;font-display:swap;
  src:url('../fonts/poppins-latin-700.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Poppins';font-style:normal;font-weight:800;font-display:swap;
  src:url('../fonts/poppins-latin-800.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}

/* ---------------------------------------------------------------------------
   2. SITE SCALE — spacing, type, measure. No colour here.
   Type is in rem with NO px font-size on <html>, so browser zoom and the user's
   own default font size both work. That is the accessibility text-size control
   on a marketing site; a bespoke A-/A/A+ widget belongs on tool surfaces, not here.
   --------------------------------------------------------------------------- */
:root{
  --container: 1200px;
  --gutter: clamp(1.25rem, 5vw, 3.5rem);

  --s1:.25rem; --s2:.5rem;  --s3:.75rem; --s4:1rem;   --s5:1.5rem;
  --s6:2rem;   --s7:3rem;   --s8:4rem;   --s9:6rem;   --s10:8rem;

  --section-y: clamp(4rem, 9vw, 8rem);

  --t-mono:   .6875rem;                       /* 11px, tracked +.18em, uppercase */
  --t-sm:     .8125rem;
  --t-body:   1.0625rem;
  --t-lede:   clamp(1.125rem, 1.55vw, 1.3125rem);
  --t-h3:     clamp(1.25rem, 1.6vw, 1.4375rem);
  --t-h2:     clamp(2rem, 4.2vw, 3.25rem);
  --t-h1:     clamp(2.625rem, 6.6vw, 5.25rem);

  --r-sm:.5rem; --r-md:.75rem; --r-lg:1.125rem;
  --measure: 62ch;

  --ease:.16s cubic-bezier(.4,0,.2,1);
}

/* ---------------------------------------------------------------------------
   3. GROUND-DERIVED SURFACES — all color-mix over tokens, no new hues.
   --label  : the smallest text colour. Deliberately different per ground:
              --fg-muted passes on black but NOT at 11px on white, so Cashmere
              steps up to --fg-quiet. Never demote it back.
   --go-ink : acid green pushed to a readable ink for TEXT on Cashmere. Derived
              from --intrnls-green + --intrnls-black; it is not a new palette entry.
   --------------------------------------------------------------------------- */
:root,[data-ground="cashmere"]{
  color-scheme: light;                        /* native controls match the ground */
  --panel:   color-mix(in srgb, var(--intrnls-ink) 5%, var(--bg));
  --panel-2: color-mix(in srgb, var(--intrnls-ink) 9%, var(--bg));
  --label:   var(--fg-quiet);
  --go-ink:  color-mix(in srgb, var(--intrnls-green) 48%, var(--intrnls-black));
  --on-accent: var(--intrnls-white);
  /* STATE INKS — for form errors and confirmations. Same doctrine as --go-ink:
     derived from the state tokens, never a new hue, and never used colour-alone
     (every state below is glyph + word + colour). Crimson is 5.05:1 on white, so
     on Cashmere it is used neat. */
  --err:     var(--intrnls-state-critical);
  --err-bg:  color-mix(in srgb, var(--intrnls-state-critical) 6%, var(--bg));
  --ok:      var(--go-ink);
  --field-bg: var(--bg);
  /* The filled "go" button, as VARIABLES rather than a ground-descendant rule —
     see the note on .btn-go in §5. On white, neat acid green cannot carry white
     text or black text legibly at 13px, so the fill steps down to --go-ink. */
  --go-fill:       var(--go-ink);
  --go-fill-ink:   var(--intrnls-white);
  --go-fill-hover: color-mix(in srgb, var(--go-ink) 85%, var(--intrnls-black));
}
[data-ground="lab"]{
  color-scheme: dark;
  --panel:   color-mix(in srgb, var(--intrnls-white) 4%, var(--bg));
  --panel-2: color-mix(in srgb, var(--intrnls-white) 8%, var(--bg));
  --label:   var(--fg-muted);
  --go-ink:  var(--intrnls-green);
  --on-accent: var(--intrnls-black);
  /* Neat crimson is only 4.16:1 on black — it FAILS AA for a 13px error message.
     Lifted with white until it passes (measured 5.4:1). Do not demote it back;
     this is the same call --label makes on Cashmere, in the other direction. */
  --err:     color-mix(in srgb, var(--intrnls-state-critical) 74%, var(--intrnls-white));
  --err-bg:  color-mix(in srgb, var(--intrnls-state-critical) 16%, var(--bg));
  --ok:      var(--intrnls-state-go);
  --field-bg: var(--panel);
  --go-fill:       var(--intrnls-green);
  --go-fill-ink:   var(--intrnls-black);
  --go-fill-hover: color-mix(in srgb, var(--intrnls-green) 82%, var(--intrnls-white));
}

/* ---------------------------------------------------------------------------
   4. RESET / BASE
   --------------------------------------------------------------------------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; scroll-behavior:smooth}
body{
  margin:0;
  background:var(--bg);
  color:var(--fg);
  font-family:var(--intrnls-font-body);
  font-size:var(--t-body);
  line-height:1.65;
  font-weight:400;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}
img,svg{display:block;max-width:100%}
h1,h2,h3,h4,p,ul{margin:0}
ul{padding:0;list-style:none}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0}
::selection{background:var(--accent); color:var(--on-accent)}

/* focus is brand law — visible, ground-aware, never removed */
:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:3px;
  border-radius:var(--r-sm);
}

@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important;
  }
}

/* ---------------------------------------------------------------------------
   5. PRIMITIVES
   --------------------------------------------------------------------------- */
.container{width:100%; max-width:var(--container); margin-inline:auto; padding-inline:var(--gutter)}

.band{background:var(--bg); color:var(--fg); padding-block:var(--section-y)}
.band + .band{border-top:1px solid var(--hairline)}

.kicker{
  font-family:var(--intrnls-font-mono);
  font-size:var(--t-mono);
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--label);
  font-weight:500;
}
.lede{font-size:var(--t-lede); line-height:1.55; color:var(--fg-quiet); max-width:var(--measure)}
.prose{color:var(--fg-quiet); max-width:var(--measure)}

/* Safety net: a display line must never be the thing that makes the page scroll
   sideways. Any authored <br> that is hidden responsively MUST have a space after it. */
h1,h2,h3{font-family:var(--intrnls-font-display); font-weight:800; overflow-wrap:break-word}
.h1{font-size:var(--t-h1); line-height:.96; letter-spacing:-.035em}
.h2{font-size:var(--t-h2); line-height:1.04; letter-spacing:-.028em}
.h3{font-size:var(--t-h3); line-height:1.25; letter-spacing:-.01em; font-weight:700}

/* THE GRADIENT MOMENT. Display sizes only (>=2rem / >=700). Never at body size:
   the crimson tail loses contrast below large-text thresholds. */
.grad{
  background:var(--gradient);
  -webkit-background-clip:text; background-clip:text;
  color:transparent; -webkit-text-fill-color:transparent;
  padding-block:.06em; margin-block:-.06em;   /* stop the clip cropping descenders */
}
@supports not ((background-clip:text) or (-webkit-background-clip:text)){
  .grad{color:var(--intrnls-orange); -webkit-text-fill-color:currentColor}
}

/* ---- buttons ---------------------------------------------------------- */
.btn{
  display:inline-flex; align-items:center; gap:var(--s2);
  padding:.85rem 1.4rem; border-radius:var(--r-sm);
  font-size:var(--t-sm); font-weight:700; letter-spacing:.02em;
  border:1px solid transparent; cursor:pointer;
  transition:background var(--ease), color var(--ease), border-color var(--ease), transform var(--ease);
}
.btn .arw{transition:transform var(--ease)}
.btn:hover .arw{transform:translateX(3px)}

/* primary = the acid-green "go". The only filled control on the page.
   FIXED 2026-08-15 (ux-engineer): this used to be a plain rule plus a
   [data-ground="cashmere"] .btn-go override. That override matched by ANCESTRY,
   so a green button inside a Lab band on a Cashmere page (every interior page's
   CTA) took the Cashmere fill while --go-ink resolved to neat acid green from
   the nearer Lab scope: white text on acid green, measured 1.36:1. Ground-aware
   values belong in ground-scoped VARIABLES, which resolve from the nearest
   ancestor that sets a ground. Do not reintroduce a descendant override here. */
.btn-go{background:var(--go-fill); color:var(--go-fill-ink); border-color:var(--go-fill)}
/* an accent-coloured ring on an accent-coloured fill is a ring you can't see —
   the one control that overrides the global ring colour, and the only one. */
.btn-go:focus-visible,.skip:focus-visible{outline-color:var(--fg)}
.btn-go:hover{background:var(--go-fill-hover); border-color:var(--go-fill-hover)}

/* secondary = outline, quiet */
.btn-line{border-color:var(--hairline); color:var(--fg)}
.btn-line:hover{border-color:var(--fg); background:var(--panel)}

/* tertiary = a text link that behaves like a control */
.btn-text{padding-inline:0; color:var(--fg-quiet); font-weight:600}
.btn-text:hover{color:var(--fg)}

/* ---- section header --------------------------------------------------- */
.sec-head{
  display:grid; grid-template-columns:1fr auto; align-items:start;
  gap:var(--s5) var(--s7); margin-bottom:var(--s7);
  padding-top:var(--s5); border-top:1px solid var(--hairline);
}
.sec-head .kicker{display:block; margin-bottom:var(--s4)}
.sec-head .h2{max-width:18ch}
.sec-head .deck{margin-top:var(--s4); color:var(--fg-quiet); max-width:48ch; font-size:var(--t-body)}
.sec-more{
  white-space:nowrap; margin-top:-.15rem;
  font-size:var(--t-sm); font-weight:600; color:var(--fg-quiet);
  border-bottom:1px solid var(--hairline); padding-bottom:.25rem;
  transition:color var(--ease), border-color var(--ease);
}
.sec-more:hover{color:var(--fg); border-color:var(--accent)}

/* ---------------------------------------------------------------------------
   6. HEADER / NAV  — the only place the logo appears on the page.
   The 1px gradient rule under the bar is CHROME, not the gradient moment; it is
   the same signature the tool surfaces use, so site and cockpit read as one company.
   --------------------------------------------------------------------------- */
.skip{
  position:absolute; left:var(--s4); top:var(--s4); z-index:100;
  transform:translateY(-200%);
  background:var(--go); color:var(--intrnls-black);
  padding:.7rem 1.1rem; border-radius:var(--r-sm); font-weight:700; font-size:var(--t-sm);
}
.skip:focus{transform:none}

.site-head{
  position:sticky; top:0; z-index:50;
  background:color-mix(in srgb, var(--bg) 84%, transparent);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  border-bottom:1px solid var(--hairline);
}
.site-head::after{
  content:""; position:absolute; left:0; right:0; bottom:-1px; height:1px;
  background:var(--gradient);
}
.head-bar{display:flex; align-items:center; gap:var(--s5); min-height:4.5rem}

/* LOGO SLOT — real lockup, operator-supplied 2026-08-20 (superseded the plan
   below: two separate stroke-color artboards). The delivered SVG's keyline
   stroke was retargeted to currentColor (web/_include/header.html), so ONE
   inlined file reads correctly on both Cashmere and Lab — color here is the
   only thing that needs to flip per ground, and --fg already does that. */
.brand{
  display:flex; align-items:center; margin-right:auto;
}
/* 2.25rem, not the original 1.75rem — operator flagged the mark as hard to
   read on Cashmere (2026-08-20). Diagnosed as a size problem, not a stroke
   problem: this SVG's keyline strokes are 7px/4px in a 540x180 viewBox, so
   at 1.75rem (28px tall) they render sub-pixel thin regardless of colour —
   confirmed on a reliable full-page screenshot, not the flaky small-viewport
   captures this session kept hitting. currentColor stays; a heavier/darker
   stroke would fight the mark's actual (thin, geometric) design intent. */
.brand svg{
  display:block; height:2.25rem; width:auto; color:var(--fg);
}

.nav{display:flex; align-items:center; gap:var(--s2)}
.nav a{
  position:relative; padding:.5rem .85rem; border-radius:var(--r-sm);
  font-size:var(--t-sm); font-weight:600; color:var(--fg-quiet);
  transition:color var(--ease);
}
.nav a:hover{color:var(--fg)}
/* active = colour + underline + aria-current. Never colour alone. */
.nav a[aria-current="page"]{color:var(--fg)}
.nav a[aria-current="page"]::after{
  content:""; position:absolute; left:.85rem; right:.85rem; bottom:.15rem;
  height:2px; background:var(--accent);
}
.nav .btn{margin-left:var(--s3); padding:.6rem 1.1rem}

.nav-toggle{
  display:none; align-items:center; gap:var(--s2); margin-left:auto;
  padding:.55rem .9rem; border:1px solid var(--hairline); border-radius:var(--r-sm);
  font-size:var(--t-sm); font-weight:600; cursor:pointer;
}
.nav-toggle .bars{display:grid; gap:3px}
.nav-toggle .bars i{display:block; width:15px; height:1.5px; background:currentColor}

/* ---------------------------------------------------------------------------
   7. HERO — one focal point: the headline. No competing artwork inside it.
   --------------------------------------------------------------------------- */
.hero{padding-block:clamp(4rem, 10vw, 8.5rem) clamp(3rem, 6vw, 4.5rem)}
.hero .kicker{display:block; margin-bottom:var(--s5)}
.hero .h1{margin-bottom:var(--s8)}

/* the foot row is what stops the hero being a left column against dead space:
   standfirst holds the left, the actions hold the right, a hairline binds them. */
.hero-foot{
  display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:start;
  gap:var(--s5) var(--s8);
  padding-top:var(--s5); border-top:1px solid var(--hairline);
}
.hero-foot .lede{max-width:48ch; margin:0}
.hero-actions{display:flex; flex-wrap:wrap; align-items:center; gap:var(--s4); justify-content:flex-end}

/* ---------------------------------------------------------------------------
   8. SLOTS — the honesty rule.
   PROSE placeholders read finished (they are marked in HTML comments + README).
   ASSET and FACT placeholders are VISIBLY empty. We never render a fake client
   mark, a fake number or a fake quote, not even for one screenshot.
   --------------------------------------------------------------------------- */
.slot{
  display:grid; place-content:center; gap:var(--s3); text-align:center;
  background:var(--panel);
  border:1px dashed color-mix(in srgb, var(--fg) 34%, transparent);
  border-radius:var(--r-md);
  padding:var(--s5);
}
/* .slot-tag also stands alone — a FACT slot inline in running content
   (an inbox, a phone number, a location). Dashed underline = "not filled yet". */
.slot-tag{
  display:inline-block;
  font-family:var(--intrnls-font-mono); font-size:var(--t-mono);
  letter-spacing:.18em; text-transform:uppercase; color:var(--label);
  border-bottom:1px dashed color-mix(in srgb, var(--fg) 34%, transparent);
  padding-bottom:.15rem;
}
.slot .slot-tag{color:var(--fg-quiet); border-bottom:0; padding-bottom:0}
.slot .slot-note{font-size:var(--t-sm); color:var(--label); max-width:46ch; line-height:1.55}

/* An empty asset plate has to read as scaffolding, not as a design element that
   failed to load. The hatch is the standard "nothing here yet" convention — it is
   load-bearing information, and it disappears the moment real art is dropped in. */
.slot[class*="shot"], .slot.hero-media{
  background-image:repeating-linear-gradient(135deg,
    transparent 0 11px,
    color-mix(in srgb, var(--fg) 6%, transparent) 11px 12px);
}

.hero-media{aspect-ratio:16/6; border-radius:var(--r-lg)}
@media (max-width:640px){ .hero-media{aspect-ratio:4/3} }

/* ---------------------------------------------------------------------------
   9. CAPABILITY CARDS
   --------------------------------------------------------------------------- */
.cards{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5)}
.card{
  position:relative; display:flex; flex-direction:column; gap:var(--s4);
  padding:var(--s6) var(--s5) var(--s6);
  background:var(--panel); border:1px solid var(--hairline); border-radius:var(--r-lg);
  transition:border-color var(--ease), background var(--ease);
}
.card:hover{border-color:color-mix(in srgb, var(--accent) 55%, var(--hairline))}
.card:focus-within{outline:2px solid var(--accent); outline-offset:3px}
.card .idx{
  font-family:var(--intrnls-font-mono); font-size:var(--t-mono); letter-spacing:.18em;
  color:var(--label);
}
.card .h3 a::after{content:""; position:absolute; inset:0}   /* whole card is the target */
.card .h3 a:focus-visible{outline:none}                      /* ring shown by :focus-within */
.card p{font-size:var(--t-sm); line-height:1.6; color:var(--fg-quiet); margin:0}
/* .tags and .meta are generic — a card is just their most common home. Unscoping
   them is what lets What We Do / a case study use the same chips without a
   second, near-identical component. */
.tags{display:flex; flex-wrap:wrap; gap:var(--s2)}
.tags li{
  font-family:var(--intrnls-font-mono); font-size:var(--t-mono); letter-spacing:.1em;
  color:var(--label); border:1px solid var(--hairline); border-radius:var(--r-sm);
  padding:.25rem .5rem;
}
.card .tags{margin-top:auto; padding-top:var(--s4)}

/* Real copy landed titles of very different lengths in the same row ("Materials"
   next to "Pilot → commercialization", "Big problems only" next to "Physics first,
   then opinion"). One title wrapping to two lines dropped that card's body a line
   below its siblings and the row read ragged. Above the 1-up breakpoint the title
   reserves two lines, so every body in a row starts on the same baseline; it is a
   floor, not a cap, so a three-line title still grows. At 1-up there is no row to
   align to, so the rule is not applied and no card carries dead space. */
@media (min-width:721px){
  .cards .card .h3{min-height:2.5em}   /* 2 lines at --t-h3's 1.25 line-height */
}

/* ---------------------------------------------------------------------------
   10. WORK — the one ground flip on this page (Cashmere). Photography needs it.
   --------------------------------------------------------------------------- */
.work-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s5)}
/* BUG FIX, 2026-08-20: work.html has carried data-cols="2" since the fourth
   project cleared, and its comment describes a 2x2 block — but data-cols was only
   ever implemented for .cards, so the four cards were rendering 3-up with the
   fourth orphaned in a row of its own, which is precisely what that attribute was
   added to avoid. Same pattern as .cards, including both responsive steps below:
   the attribute selector out-specifies the plain .work-grid rules inside the media
   queries, so it has to be repeated there or a phone gets a frozen 2-up. */
.work-grid[data-cols="2"]{grid-template-columns:repeat(2,1fr)}
.work-item{display:flex; flex-direction:column; gap:var(--s4)}
/* .shot is the 4:3 plate wherever it appears — it used to be scoped to
   .work-item, which meant the same markup in a case-study gallery collapsed to a
   content-height strip. Ratio belongs to the plate, not to its container. */
.slot.shot{aspect-ratio:4/3; border-radius:var(--r-md)}
.work-item .h3{margin-top:var(--s1)}
.meta{
  font-family:var(--intrnls-font-mono); font-size:var(--t-mono); letter-spacing:.14em;
  text-transform:uppercase; color:var(--label);
}
.work-item p{font-size:var(--t-sm); color:var(--fg-quiet); margin:0}

/* ---------------------------------------------------------------------------
   11. PROOF — the reserved evidence row. Home's copy of it was CUT by the operator
   on 2026-08-19; the only surviving user is the case study's Results row, which is
   still deliberately empty so nobody fills it with an unattributable claim later.
   --------------------------------------------------------------------------- */
/* Outer frame solid, inner tiles dashed — so the hierarchy reads "a real row that
   is deliberately empty", not "everything here is broken". */
.proof{
  border:1px solid var(--hairline);
  border-radius:var(--r-lg); padding:var(--s6);
  display:grid; gap:var(--s6);
}
.proof-head{display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--s3) var(--s5)}
.proof-head p{font-size:var(--t-sm); color:var(--fg-quiet); margin:0; max-width:60ch}
.proof-row{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s4)}
.proof-row .slot{min-height:5.5rem; border-style:dashed}

/* ---------------------------------------------------------------------------
   12. CTA — the loudest thing after the hero, and it uses GREEN, not gradient.
   One gradient moment per page; the hero already spent it.
   --------------------------------------------------------------------------- */
.cta{text-align:center}
.cta .h2{margin-inline:auto; max-width:16ch}
.cta .lede{margin:var(--s5) auto var(--s7); text-align:center}
.cta-actions{display:flex; flex-wrap:wrap; gap:var(--s4); justify-content:center}
.cta .fineprint{
  margin-top:var(--s6); font-family:var(--intrnls-font-mono);
  font-size:var(--t-mono); letter-spacing:.14em; text-transform:uppercase; color:var(--label);
}

/* ---------------------------------------------------------------------------
   13. FOOTER — no wordmark. The header already carries the one logo on this
   surface; repeating it is the duplicate-lockup mistake, not a convention.
   --------------------------------------------------------------------------- */
.site-foot{border-top:1px solid var(--hairline); padding-block:var(--s8) var(--s6)}
/* THREE columns, not four: the "Elsewhere" column went with the socials the
   operator does not have. The count lives here so the footer closes flush
   instead of ending in an empty cell. */
.foot-grid{display:grid; grid-template-columns:1.4fr repeat(2,1fr); gap:var(--s7) var(--s5)}
.foot-grid h2{font-size:var(--t-mono); font-family:var(--intrnls-font-mono);
  letter-spacing:.18em; text-transform:uppercase; color:var(--label); font-weight:500}
.foot-grid ul{display:grid; gap:var(--s2); margin-top:var(--s4)}
/* on li, not li a — the Contact column mixes links (email, phone) with a plain
   line (location), and the two must set the same. */
.foot-grid li{font-size:var(--t-sm); color:var(--fg-quiet)}
.foot-grid li a{color:inherit}
.foot-grid li a:hover{color:var(--fg)}
.foot-lede{font-size:var(--t-sm); color:var(--fg-quiet); max-width:34ch}
.foot-rule{margin-top:var(--s8); padding-top:var(--s5); border-top:1px solid var(--hairline);
  display:flex; flex-wrap:wrap; gap:var(--s3) var(--s5); align-items:center;
  font-family:var(--intrnls-font-mono); font-size:var(--t-mono);
  letter-spacing:.14em; text-transform:uppercase; color:var(--label)}
.foot-rule .right{margin-left:auto}

/* ===========================================================================
   14. LONG-FORM + PAGE FURNITURE   (added building What We Do / Work / About /
   Contact — web/README.md §11 hands these pages and the form to the ux-engineer.)
   Everything here is layout and type only; the sole colours are the state inks
   declared in §3. FOUR genuinely new pieces are flagged for the art director in
   web/README.md §12: .split, .factsheet, .notice and the form.
   =========================================================================== */

/* ---- .split — the long-copy workhorse: a mono label column against a prose
   column. What We Do's capabilities, About's stance, the case study's sections.
   It is a layout, not a look: it adds no colour and no decoration. ---------- */
.split{
  display:grid; grid-template-columns:minmax(0,16rem) minmax(0,1fr);
  gap:var(--s5) var(--s8); align-items:start;
}
.split + .split{margin-top:var(--s8); padding-top:var(--s8); border-top:1px solid var(--hairline)}
.split-label{display:grid; gap:var(--s3); align-content:start}
.split-label .idx{
  font-family:var(--intrnls-font-mono); font-size:var(--t-mono);
  letter-spacing:.18em; color:var(--label);
}

/* ---- running copy ------------------------------------------------------ */
/* the flow rule. Every child gets its space from the sibling above it, so no
   element carries a margin it has to fight with. (Do NOT add `.prose p{margin:0}`
   back — it is more specific than this and silently kills paragraph spacing.) */
.prose > * + *{margin-top:var(--s5)}
.prose strong{color:var(--fg); font-weight:600}
.prose h3{color:var(--fg); margin-top:var(--s6)}
.prose h3 + p{margin-top:var(--s3)}
/* list markers are a GLYPH, not a colour — the em dash carries the meaning */
.prose ul{display:grid; gap:var(--s3)}
.prose li{position:relative; padding-left:var(--s5)}
.prose li::before{content:"\2014"; position:absolute; left:0; color:var(--label)}

/* ---- card grid widths -------------------------------------------------- */
.cards[data-cols="4"]{grid-template-columns:repeat(4,1fr)}
.cards[data-cols="2"]{grid-template-columns:repeat(2,1fr)}

/* ---- breadcrumb -------------------------------------------------------- */
.crumbs{
  display:flex; flex-wrap:wrap; align-items:center; gap:var(--s3);
  font-family:var(--intrnls-font-mono); font-size:var(--t-mono);
  letter-spacing:.18em; text-transform:uppercase; color:var(--label);
  margin-bottom:var(--s5);
}
.crumbs a{border-bottom:1px solid var(--hairline); padding-bottom:.15rem}
.crumbs a:hover{color:var(--fg); border-color:var(--accent)}

/* ---- factsheet — a case study's role/duration/discipline row. dt is the mono
   label; dd is either real text or a visibly-empty FACT slot. ------------- */
/* 3 columns: the only unscoped consumer is the case-study template, which
   dropped its 4th item (Disciplines, a tags row) 2026-08-20 -- see
   web/README.md §16. Contact's factsheet always gets the 2-col `aside`
   override below regardless of this value. */
.factsheet{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--s5);
  border-top:1px solid var(--hairline); padding-top:var(--s5); margin:0;
}
.factsheet dt{
  font-family:var(--intrnls-font-mono); font-size:var(--t-mono);
  letter-spacing:.18em; text-transform:uppercase; color:var(--label);
  margin-bottom:var(--s3);
}
.factsheet dd{margin:0; font-size:var(--t-sm); color:var(--fg-quiet)}
/* a real, actionable fact (mailto:, tel:). Solid underline against .slot-tag's
   dashed one, and the same idiom as .crumbs a — not a new affordance. */
.factsheet dd a{border-bottom:1px solid var(--hairline); padding-bottom:.15rem}
.factsheet dd a:hover{color:var(--fg); border-color:var(--accent)}
/* one fact wider than a column. With Hours removed the street address would sit in
   a half column, wrap three lines and leave an empty cell beside it; full width
   closes the row instead. */
.factsheet .fact-wide{grid-column:1 / -1}
/* in a half-width column (Contact's aside) four across is too tight */
aside .factsheet{grid-template-columns:repeat(2,minmax(0,1fr))}

/* ---- galleries + extra asset ratios. The "shot" in the class name is what
   makes the hatch selector in §8 pick these up — keep it. ----------------- */
/* .duo — two equal content columns (form + aside, portrait pair). Stacks at 720. */
.duo{display:grid; grid-template-columns:1fr 1fr; gap:var(--s7) var(--s8); align-items:start}

.gallery{display:grid; grid-template-columns:1fr 1fr; gap:var(--s5)}
.gallery figure{margin:0}
.gallery figcaption{margin-top:var(--s3)}
.slot.shot-wide{aspect-ratio:16/9; border-radius:var(--r-md)}
/* a portrait plate is capped: at half a 1200px band an uncapped 3:4 plate is
   nearly 700px tall, which makes an empty slot the loudest thing on the page */
.slot.shot-portrait{aspect-ratio:3/4; border-radius:var(--r-md); max-width:22rem}
.slot.shot-square{aspect-ratio:1; border-radius:var(--r-md)}

/* ---- REAL PHOTOGRAPHY. A filled plate and an empty one must hold the SAME box,
   or a page half-filled with photographs reflows as the slots are replaced one by
   one. So the ratio and the radius are repeated here for <img>, and nothing else
   is: an <img> takes none of .slot's dashed border, hatch or padding, because it
   is not reserved — it is filled.
   height:auto is load-bearing. The width/height ATTRIBUTES are the file's real
   pixels (so the browser reserves the box before the bytes land and nothing jumps),
   and without height:auto that attribute height stays literal while max-width
   shrinks the width — the photograph squashes. With it, aspect-ratio governs.
   object-fit:cover then matters in exactly one place: .hero-media re-crops to 4:3
   under 640px, and cover takes the middle rather than distorting.
   The panel behind is what shows during decode — a token surface, not white. ---- */
img.hero-media, img.shot, img.shot-wide, img.shot-portrait, img.shot-square{
  width:100%; height:auto; object-fit:cover; background:var(--panel);
}
img.shot{aspect-ratio:4/3; border-radius:var(--r-md)}
img.shot-wide{aspect-ratio:16/9; border-radius:var(--r-md)}
img.shot-portrait{aspect-ratio:3/4; border-radius:var(--r-md); max-width:22rem}
img.shot-square{aspect-ratio:1; border-radius:var(--r-md)}

/* ---- the only two spacing utilities, both on the scale. They exist so a page
   never carries an inline style; if you need a third, you probably need a
   component. --------------------------------------------------------------- */
.gap-top{margin-top:var(--s8)}
.gap-bottom{margin-bottom:var(--s7)}

code{font-family:var(--intrnls-font-mono); font-size:.9em; color:var(--fg)}

/* ---- pager (prev / next case study) ------------------------------------ */
.pager{
  display:flex; flex-wrap:wrap; gap:var(--s4); justify-content:space-between;
  border-top:1px solid var(--hairline); padding-top:var(--s5); margin-top:var(--s8);
}

/* ---- .notice — an editorial aside that is TRUE about the artefact itself
   (e.g. "this file is a template"). Never a place to park a claim about a
   client. Status is border + glyph + word; the glyph is aria-hidden and the
   word is in the sentence, so it survives greyscale and a screen reader. -- */
.notice{
  display:flex; gap:var(--s4); align-items:flex-start;
  border:1px solid var(--hairline); border-left-width:3px;
  border-radius:var(--r-sm); background:var(--panel);
  padding:var(--s4) var(--s5); font-size:var(--t-sm); color:var(--fg-quiet);
}
.notice .ic{font-weight:700; color:var(--fg); line-height:1.4}
.notice strong{color:var(--fg)}
.notice[data-state="note"]{border-left-color:var(--accent)}
.notice[data-state="ok"]{border-left-color:var(--ok); background:var(--panel)}
.notice[data-state="err"]{border-left-color:var(--err); background:var(--err-bg)}
.notice[data-state="busy"]{border-left-color:var(--fg-muted)}
.notice[data-state="ok"] .ic{color:var(--ok)}
.notice[data-state="err"] .ic{color:var(--err)}

/* ===========================================================================
   15. THE CONTACT FORM
   Every state is colour + GLYPH + WORD. Errors are wired to their field with
   aria-describedby + aria-invalid, and the summary is a real focus target, so
   "there is a problem" is announced, not just painted red.
   =========================================================================== */
.form{display:grid; gap:var(--s5); max-width:34rem}
.form-grid{display:grid; grid-template-columns:1fr 1fr; gap:var(--s5)}

/* align-content:start matters: side-by-side fields are equal-height grid items,
   and without it the field WITHOUT a hint stretches its input to match the one
   that has one — two inputs of different heights on the same row. */
.field{display:grid; gap:var(--s2); align-content:start}
.field > label{
  font-family:var(--intrnls-font-mono); font-size:var(--t-mono);
  letter-spacing:.18em; text-transform:uppercase; color:var(--label); font-weight:500;
}
/* "(required)" is a WORD, not a red asterisk — an asterisk is colour-and-symbol
   trivia the reader has to decode, and it is invisible to some voice users. */
.field > label .req{color:var(--fg-quiet)}
.field .hint{font-size:var(--t-sm); color:var(--label); margin:0}

.input{
  width:100%; font:inherit; font-size:var(--t-sm); line-height:1.5;
  padding:.8rem .9rem; color:var(--fg); background:var(--field-bg);
  border:1px solid color-mix(in srgb, var(--fg) 26%, transparent);
  border-radius:var(--r-sm);
  transition:border-color var(--ease), background var(--ease);
}
.input:hover{border-color:color-mix(in srgb, var(--fg) 48%, transparent)}
.input:focus-visible{border-color:var(--accent)}   /* ring comes from the global rule */
textarea.input{min-height:9rem; resize:vertical}
select.input{appearance:none; -webkit-appearance:none; padding-right:var(--s7)}
/* the select chevron is drawn from currentColor, so it inherits the ground and
   needs no icon file and no hex */
.select-wrap{position:relative; display:grid}
.select-wrap::after{
  content:""; position:absolute; right:var(--s5); top:50%; width:.5rem; height:.5rem;
  border-right:2px solid currentColor; border-bottom:2px solid currentColor;
  transform:translateY(-70%) rotate(45deg); pointer-events:none; color:var(--fg-quiet);
}

/* INVALID: border + tinted field + the message below. Three signals, one of
   which (the message) is pure text. */
.input[aria-invalid="true"]{border-color:var(--err); background:var(--err-bg)}
.field-err{
  display:flex; gap:var(--s2); align-items:flex-start; margin:0;
  font-size:var(--t-sm); font-weight:600; color:var(--err);
}
.field-err .ic{font-weight:800; line-height:1.5}

.form-actions{display:flex; flex-wrap:wrap; align-items:center; gap:var(--s4)}
.form .btn[aria-disabled="true"], .form .btn:disabled{opacity:.6; cursor:not-allowed}
.form-foot{font-size:var(--t-sm); color:var(--label); margin:0}

/* the error summary is focusable (tabindex=-1) so submit can move the reader
   straight to it; it lists every failed field as a link to that field */
.err-summary{display:grid; gap:var(--s3)}
.err-summary ul{display:grid; gap:var(--s2); margin:0}
.err-summary a{color:var(--err); font-weight:600; border-bottom:1px solid currentColor}

/* ---------------------------------------------------------------------------
   16. RESPONSIVE — three steps. Nothing reflows below 320px.
   --------------------------------------------------------------------------- */
@media (max-width:940px){
  .cards,.work-grid,.proof-row{grid-template-columns:1fr 1fr}
  .cards[data-cols="4"],.cards[data-cols="2"],.work-grid[data-cols="2"]{grid-template-columns:1fr 1fr}
  .factsheet{grid-template-columns:1fr 1fr}
  .split{grid-template-columns:minmax(0,12rem) minmax(0,1fr); gap:var(--s5)}
  .foot-grid{grid-template-columns:1fr 1fr}
  .sec-head{grid-template-columns:1fr}
  .sec-more{justify-self:start}
}
@media (max-width:720px){
  .cards,.work-grid,.proof-row{grid-template-columns:1fr}
  .cards[data-cols="4"],.cards[data-cols="2"],.work-grid[data-cols="2"]{grid-template-columns:1fr}
  .split{grid-template-columns:1fr; gap:var(--s4)}
  .split + .split{margin-top:var(--s7); padding-top:var(--s7)}
  .gallery,.form-grid,.duo{grid-template-columns:1fr}
  .factsheet{grid-template-columns:1fr; gap:var(--s4)}
  .pager{flex-direction:column}
  .hero-foot{grid-template-columns:1fr; gap:var(--s6)}
  .hero-actions{justify-content:flex-start}
  br.brk{display:none}
  .nav{
    display:none; position:absolute; left:0; right:0; top:100%;
    flex-direction:column; align-items:stretch; gap:var(--s1);
    padding:var(--s4) var(--gutter) var(--s6);
    background:var(--bg); border-bottom:1px solid var(--hairline);
  }
  .nav[data-open="true"]{display:flex}
  .nav a{padding:.75rem .25rem; font-size:var(--t-body)}
  .nav a[aria-current="page"]::after{left:.25rem; right:auto; width:1.5rem; bottom:.4rem}
  .nav .btn{margin:var(--s3) 0 0; justify-content:center}
  .nav-toggle{display:inline-flex}
  .site-head{position:sticky}
  .head-bar{position:relative}
  .hero .h1{max-width:none}
  .foot-grid{grid-template-columns:1fr}
  .foot-rule .right{margin-left:0}
}
@media (max-width:400px){
  .hero-actions{flex-direction:column; align-items:stretch}
  .hero-actions .btn{justify-content:center}
}

/* ---------------------------------------------------------------------------
   17. NAV STATE + NO-JS. Kept out of §6 so the reference page's chrome block
   stays readable, but these are header rules.
   --------------------------------------------------------------------------- */
/* The Contact item is a button, so the generic active underline would hang
   outside it. Tuck it inside — the current page still reads as colour + a
   drawn mark + aria-current, never colour alone. */
.nav .btn[aria-current="page"]{border-color:var(--accent); color:var(--fg)}
.nav .btn[aria-current="page"]::after{
  content:""; position:absolute; left:1.1rem; right:1.1rem; bottom:.35rem;
  height:2px; background:var(--accent);
}
.nav .btn{position:relative}

/* NO SCRIPT: the Menu button can only work with JS, so without it the button is
   hidden and the nav stacks open under the bar. A phone with scripting off gets
   the whole nav instead of a control that does nothing. The `js` class is set by
   a one-line inline script in <head>, before first paint. */
@media (max-width:720px){
  /* the bar wraps so the nav gets a row of its own — dropped into the flex row
     as-is it becomes a cramped column squeezed against the right edge */
  html:not(.js) .head-bar{flex-wrap:wrap; padding-bottom:var(--s3)}
  html:not(.js) .nav{
    display:flex; position:static; width:100%; padding:0;
    border-bottom:0; background:transparent;
  }
  html:not(.js) .nav .btn{align-self:flex-start; margin-top:var(--s2)}
  html:not(.js) .nav-toggle{display:none}
}

/* the header is sticky, so an in-page anchor (what-we-do.html#materials) would land
   under it. Every id offsets itself by the bar height plus a breath. */
[id]{scroll-margin-top:calc(4.5rem + var(--s5))}

/* utility: used by the form's error/status blocks, which are in the DOM from the
   start (so aria-describedby always resolves) and revealed as they apply. */
[hidden]{display:none !important}

/* ---------------------------------------------------------------------------
   18. PRINT — Cashmere always; drop the chrome. (Decks and PDFs are Cashmere.)
   --------------------------------------------------------------------------- */
@media print{
  .site-head,.nav-toggle,.skip,.cta-actions,.pager,.form-actions{display:none}
  html{color-scheme:light}
  body{background:var(--intrnls-white); color:var(--intrnls-ink)}
}
