/* ============================================================
   Wing Digital — design system v2
   Editorial authority. Pentagram restraint x COLLINS warmth.
   Rules live in design-dna.md. Do not add a value that is not
   a token, and do not add a fourth radius.
   ============================================================ */

:root{
  /* --- type families (all SIL OFL 1.1, free for commercial use) --- */
  --display:"Fraunces",Georgia,"Times New Roman",serif;
  --sans:"Instrument Sans",-apple-system,"Segoe UI",Helvetica,Arial,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  /* --- palette: warm. 60 paper / 30 ink / 10 accent --- */
  --paper:#FAF9F6;
  --sand:#EFEBE4;
  --ink:#141110;
  --ink-2:#1F1B19;
  --stone:#6B6560;
  --faint:#6E6862;
  --hair:rgba(20,17,16,.13);
  --hair-2:rgba(20,17,16,.07);
  --accent:#2757E6;
  --accent-ink:#1B3FAE;
  --on-ink:rgba(250,249,246,.94);
  --on-ink-mute:rgba(250,249,246,.62);
  --on-ink-faint:rgba(250,249,246,.40);
  --on-hair:rgba(250,249,246,.16);

  /* --- spacing: 1.6 ratio, seven steps. Never invent an eighth. --- */
  --s1:8px; --s2:16px; --s3:24px; --s4:40px; --s5:64px; --s6:104px; --s7:168px;

  /* --- radius scale: four values, four jobs. --- */
  --r-chip:2px;    /* buttons, chips, inputs */
  --r-photo:4px;   /* every photograph, always */
  --r-card:12px;   /* panels that hold other things */
  --r-full:999px;  /* only the mascot and avatars */

  --ease:cubic-bezier(.19,1,.22,1);
  --maxw:1320px;
  --gutter:40px;
}

*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;overflow-x:clip}
body{
  font-family:var(--sans);font-size:17px;line-height:1.55;letter-spacing:-.005em;
  background:var(--paper);color:var(--ink);
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  overflow-x:clip;
}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%;height:auto}
/* height:auto is load-bearing: every <img> carries width/height attributes
   to stop layout shift, and without this the attribute height is used
   literally and every aspect-ratio below is ignored. */
::selection{background:var(--accent);color:#fff}

.wrap{max-width:var(--maxw);margin:0 auto;padding:0 var(--gutter)}
@media(max-width:720px){:root{--gutter:22px}}

/* ============================================================
   TYPE SCALE — six sizes. Adding a seventh is a defect.
   Display line-height is 1.0 flat. Tracking -3.5% at display,
   -2.5% at h2, -1.5% at h3, -0.5% at body.
   ============================================================ */
.t-display,.wordmark{
  font-family:var(--display);font-weight:400;
  font-size:clamp(52px,8.4vw,128px);line-height:1.0;letter-spacing:-.035em;
  font-variation-settings:"SOFT" 0,"WONK" 1,"opsz" 144;
  text-wrap:balance;
}
h1,.t-h1{
  font-family:var(--display);font-weight:400;
  font-size:clamp(38px,5.6vw,82px);line-height:1.0;letter-spacing:-.033em;
  font-variation-settings:"SOFT" 0,"WONK" 1,"opsz" 96;
  text-wrap:balance;
}
h2,.t-h2{
  font-family:var(--display);font-weight:400;
  font-size:clamp(30px,3.9vw,54px);line-height:1.04;letter-spacing:-.028em;
  font-variation-settings:"SOFT" 0,"WONK" 1,"opsz" 60;
  text-wrap:balance;
}
h3,.t-h3{
  font-family:var(--display);font-weight:400;
  font-size:clamp(21px,1.8vw,26px);line-height:1.14;letter-spacing:-.02em;
  font-variation-settings:"opsz" 30;
}
h4{font-family:var(--sans);font-size:17px;font-weight:600;letter-spacing:-.012em;line-height:1.3}
.lead{font-size:clamp(18px,1.5vw,21px);line-height:1.45;letter-spacing:-.012em;color:var(--stone);max-width:52ch}
p{max-width:68ch}
.muted{color:var(--stone)}

/* the one label style on the whole site */
.lbl{
  font-family:var(--mono);font-size:12px;font-weight:400;
  letter-spacing:.12em;text-transform:uppercase;line-height:1;
  color:var(--stone);
}
.lbl--ink{color:var(--ink)}

/* ============================================================
   NAMED LAYOUT — "band rule"
   Every section opens the same way: one hairline, mono label
   left, mono index right. This is the site's spine.
   ============================================================ */
.band-rule{
  display:flex;justify-content:space-between;align-items:baseline;gap:var(--s3);
  border-top:1px solid var(--ink);padding-top:14px;margin-bottom:var(--s4);
}
.band-rule .r{color:var(--stone);white-space:nowrap}
section{padding:var(--s7) 0}
@media(max-width:860px){section{padding:var(--s6) 0}}
section.tight{padding:var(--s6) 0}
.sand{background:var(--sand)}

/* ============================================================
   BUTTONS — 2px, flat, fill accent on hover. No pills anywhere.
   Refusal: a button never has both a border and a background.
   ============================================================ */
.btn{
  display:inline-flex;align-items:center;gap:10px;
  height:52px;padding:0 26px;border:0;border-radius:var(--r-chip);
  font-family:var(--sans);font-size:15px;font-weight:500;letter-spacing:-.008em;
  cursor:pointer;background:var(--ink);color:var(--paper);
  transition:background .45s var(--ease),color .45s var(--ease),transform .45s var(--ease);
}
.btn:hover{background:var(--accent);color:#fff}
.btn svg{width:15px;height:15px;flex:none}
.btn--ghost{background:none;color:var(--ink);box-shadow:inset 0 0 0 1px var(--hair)}
.btn--ghost:hover{background:none;color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
.btn--onink{background:var(--paper);color:var(--ink)}
.btn--onink:hover{background:var(--accent);color:#fff}

/* text link with a wiping underline */
.tlink{position:relative;display:inline-flex;align-items:center;gap:9px;font-weight:500}
.tlink::after{content:"";position:absolute;left:0;right:0;bottom:-3px;height:1px;
  background:currentColor;transform-origin:left;transition:transform .5s var(--ease)}
.tlink:hover::after{transform:scaleX(0);transform-origin:right}
.tlink .ar{transition:transform .5s var(--ease)}
.tlink:hover .ar{transform:translateX(6px)}

/* ============================================================
   NAV — transparent over the hero, solid after.
   Mobile: NOT sticky (chrome budget). One persistent element max.
   ============================================================ */
nav{position:fixed;top:0;left:0;right:0;z-index:60;transition:background .5s,border-color .5s}
nav .wrap{display:flex;align-items:center;justify-content:space-between;height:78px;gap:var(--s3)}
nav.solid{background:var(--paper);border-bottom:1px solid var(--hair)}
.logo{display:flex;align-items:center;gap:10px;font-family:var(--display);
  font-size:21px;font-weight:400;letter-spacing:-.025em;color:var(--paper);transition:color .4s}
nav.solid .logo{color:var(--ink)}
.logo img{height:30px;width:auto}
.navlinks{display:flex;gap:var(--s3);margin-left:auto;margin-right:var(--s3)}
.navlinks a{font-size:15px;color:var(--on-ink);position:relative;padding:4px 0;transition:color .4s}
nav.solid .navlinks a{color:var(--ink)}
.navlinks a::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:currentColor;transform:scaleX(0);transform-origin:left;
  transition:transform .45s var(--ease)}
.navlinks a:hover::after{transform:scaleX(1)}
.navlinks a[aria-current="page"]::after{transform:scaleX(1);background:var(--accent)}
.burger{display:none;background:none;border:0;cursor:pointer;padding:10px;margin-left:auto}
.burger span{display:block;width:22px;height:1.5px;background:var(--paper);margin:5px 0;
  transition:transform .35s var(--ease),opacity .2s}
nav.solid .burger span{background:var(--ink)}
nav.menu-open .burger span:nth-child(1){transform:translateY(6.5px) rotate(45deg)}
nav.menu-open .burger span:nth-child(2){opacity:0}
nav.menu-open .burger span:nth-child(3){transform:translateY(-6.5px) rotate(-45deg)}

@media(max-width:900px){
  nav{position:absolute}                 /* not sticky on phones */
  nav .wrap{height:68px}
  .logo{white-space:nowrap;font-size:21px}
  .navlinks{display:none;position:absolute;top:68px;left:0;right:0;
    flex-direction:column;gap:0;background:var(--ink);padding:var(--s1) 0 var(--s3);
    margin:0;border-bottom:1px solid var(--on-hair)}
  nav.menu-open .navlinks{display:flex}
  .navlinks a{padding:16px var(--gutter);font-size:18px;color:var(--on-ink)}
  .navlinks a::after{display:none}
  nav.menu-open{background:var(--ink)}
  nav.menu-open .logo{color:var(--paper)}
  /* the CTA moves into the open panel instead of fighting the header row */
  .nav-cta{display:none}
  /* script.js moves the CTA inside the panel on phones, so it can simply sit
     in normal flow instead of being positioned with a guessed offset */
  nav.menu-open .navlinks .nav-cta{display:inline-flex;
    margin:var(--s2) var(--gutter) 0;background:var(--paper);color:var(--ink)}
  /* the burger is the ONLY way into the nav on a phone. Without this rule the
     whole site loses its navigation (regression caught 2026-09-07). */
  .burger{display:flex;flex-direction:column;justify-content:center;align-items:center;
    gap:5px;width:44px;height:44px;padding:0;margin-left:auto;flex:none}
  .burger span{width:22px;height:1.5px;margin:0;background:var(--paper)}
  nav.solid .burger span,nav.menu-open .burger span{background:var(--paper)}
  nav.solid:not(.menu-open) .burger span{background:var(--ink)}
  nav.menu-open .burger span:nth-child(1){transform:translateY(6.5px) rotate(45deg)}
  nav.menu-open .burger span:nth-child(2){opacity:0}
  nav.menu-open .burger span:nth-child(3){transform:translateY(-6.5px) rotate(-45deg)}
}

.nav-cta{height:42px;padding:0 20px;font-size:15px}

/* ============================================================
   INTRO — F3 wordmark wipe. ~1.9s total, reduced-motion safe.
   The name holds, then wipes left-to-right off the page,
   which is the direction everything below it reads.
   ============================================================ */

/* ============================================================
   HERO — full first viewport, photo behind a transparent header.
   ============================================================ */
.hero{
  position:relative;min-height:100svh;display:flex;flex-direction:column;
  justify-content:flex-end;padding:150px 0 var(--s5);
  background:var(--ink);color:var(--on-ink);overflow:hidden;
}
.hero-bg{position:absolute;inset:0;z-index:0}
.hero-bg img{width:100%;height:100%;object-fit:cover;opacity:.30;
  transform:scale(1.06);animation:heroDrift 26s ease-in-out infinite alternate}
@keyframes heroDrift{to{transform:scale(1.12) translate3d(-1.5%,-1%,0)}}
.hero-bg::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(20,17,16,.72) 0%,rgba(20,17,16,.42) 42%,rgba(20,17,16,.92) 100%)}
.hero>.wrap{position:relative;z-index:1;width:100%}
.hero-grid{display:grid;grid-template-columns:1.42fr 1fr;gap:var(--s5);align-items:end}
@media(max-width:900px){.hero-grid{grid-template-columns:1fr;gap:var(--s4)}}
.hero h1{color:var(--paper)}
.hero-right{display:flex;flex-direction:column;gap:var(--s3);align-items:flex-start;
  justify-self:end;max-width:400px}
.hero-right p{color:var(--on-ink-mute);font-size:18px;line-height:1.5;max-width:38ch}
.hero-meta,.hero-meta.lbl{color:var(--on-ink-mute)}
.hero-meta{display:flex;gap:var(--s3);flex-wrap:wrap;margin-top:var(--s4);
  padding-top:var(--s2);border-top:1px solid var(--on-hair)}
.hero-meta span{color:var(--on-ink-mute)}

/* ============================================================
   PAGE HEAD — subpages. Same grammar, no photo, less height.
   ============================================================ */
.page-head{background:var(--ink);color:var(--on-ink);
  padding:170px 0 var(--s6);position:relative;overflow:hidden}
.page-head .band-rule{border-color:var(--on-hair)}
.page-head .band-rule .l{color:var(--on-ink-mute)}
.page-head .band-rule .r{color:var(--on-ink-mute)}
.page-head h1{color:var(--paper);max-width:17ch}
.page-head .lead{color:var(--on-ink-mute);margin-top:var(--s3)}
@media(max-width:900px){.page-head{padding:calc(var(--s6) + var(--s3)) 0 var(--s5)}}

/* ============================================================
   NAMED LAYOUT — "ledger row"
   Numbered full-width rows. Replaces every card grid.
   Refusal: no section on this site has three equal cards.
   ============================================================ */
.ledger{border-top:1px solid var(--hair)}
.lrow{
  display:grid;grid-template-columns:76px minmax(0,1fr) minmax(0,1.05fr) 44px;
  gap:var(--s4);align-items:start;
  padding:var(--s4) 0;border-bottom:1px solid var(--hair);
  transition:padding-left .55s var(--ease),background .55s var(--ease);
}
.lrow:hover{padding-left:18px;background:var(--hair-2)}
.lrow .n{font-family:var(--mono);font-size:12px;letter-spacing:.1em;color:var(--faint);padding-top:8px}
.lrow h3{transition:color .45s var(--ease)}
.lrow:hover h3{color:var(--accent)}
.lrow .body{color:var(--stone);font-size:17px;line-height:1.55}
.lrow ul{list-style:none;margin-top:var(--s2);display:grid;gap:7px}
.lrow ul li{position:relative;padding-left:18px;font-size:14px;color:var(--stone)}
.lrow ul li::before{content:"";position:absolute;left:0;top:9px;width:7px;height:1px;background:var(--faint)}
.lrow .ar{justify-self:end;padding-top:8px;color:var(--faint);
  transition:transform .55s var(--ease),color .45s var(--ease)}
.lrow:hover .ar{transform:translateX(7px);color:var(--accent)}
@media(max-width:900px){
  .lrow{grid-template-columns:44px 1fr;gap:var(--s2) var(--s3);padding:var(--s3) 0}
  .lrow .body{grid-column:2}
  .lrow .ar{display:none}
}

/* ============================================================
   NAMED LAYOUT — "asymmetric proof split"
   58/42. The photo bleeds the outer gutter. Never 50/50,
   never centred. Alternate .flip down the page.
   ============================================================ */
.split{display:grid;grid-template-columns:58fr 42fr;gap:var(--s5);align-items:center;
  margin-bottom:var(--s6)}
.split:last-child{margin-bottom:0}
.split .shot{position:relative;margin-left:calc(var(--gutter) * -1)}
.split .shot img{width:100%;border-radius:var(--r-photo);aspect-ratio:16/11;object-fit:cover}
/* a screenshot of a real site is evidence: never crop it */
.split .shot img.shot--full{aspect-ratio:auto;object-fit:contain;background:var(--paper)}
.split.flip{grid-template-columns:42fr 58fr}
.split.flip .shot{order:2;margin-left:0;margin-right:calc(var(--gutter) * -1)}
.split.flip .txt{order:1}
.split .txt h3{margin:var(--s2) 0 var(--s2)}
.split .txt p{color:var(--stone);margin-bottom:var(--s3)}
@media(max-width:900px){
  .split,.split.flip{grid-template-columns:1fr;gap:var(--s3);margin-bottom:var(--s5)}
  .split .shot,.split.flip .shot{order:1;margin:0 calc(var(--gutter) * -1)}
  .split .txt,.split.flip .txt{order:2}
  .split .shot:not(.shot--phone) img{border-radius:0}
}

/* a photo on its own, always 4px, always captioned */
.frame img{width:100%;border-radius:var(--r-photo);object-fit:cover}
.cap{font-size:14px;color:var(--stone);margin-top:14px;line-height:1.5;max-width:46ch}
.cap b{color:var(--ink);font-weight:600}
.band-photo{margin:0;padding:0}
.band-photo img{width:100%;height:clamp(240px,38vw,460px);object-fit:cover}

/* the 2 + 1 evidence arrangement (deliberately not three equal tiles) */
.evidence{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4) var(--s4)}
.evidence > *:first-child{grid-column:1 / -1}
.evidence > *:first-child img{aspect-ratio:auto}
/* dashboard captures are evidence: shown whole, never cropped */
.evidence img{aspect-ratio:auto;object-fit:contain;background:#fff;
  border:1px solid var(--hair)}
@media(max-width:860px){.evidence{grid-template-columns:1fr}
  .evidence > *:first-child{grid-column:auto}}

/* ============================================================
   FACT LEDGER — replaces the three counting stat tiles.
   Four rows, mono figures, no animation.
   ============================================================ */
.facts{border-top:1px solid var(--hair)}
.facts div{display:grid;grid-template-columns:180px 1fr;gap:var(--s4);
  padding:var(--s3) 0;border-bottom:1px solid var(--hair);align-items:baseline}
.facts .fig{font-family:var(--display);font-size:clamp(30px,3.4vw,44px);
  line-height:1;letter-spacing:-.03em;font-variation-settings:"opsz" 60}
.facts .txt{color:var(--stone);font-size:17px}
.facts .txt b{color:var(--ink);font-weight:600;display:block;margin-bottom:3px;
  font-size:17px}
@media(max-width:720px){.facts div{grid-template-columns:1fr;gap:var(--s1)}}

/* ============================================================
   PROSE — for blog posts, case studies, service essays.
   This replaces 176 inline style attributes.
   ============================================================ */
.post{max-width:70ch}
.post h2{font-size:clamp(21px,1.8vw,26px);margin:var(--s5) 0 var(--s2)}
.post h2:first-child{margin-top:0}
.post h3{margin:var(--s4) 0 var(--s1)}
.post p{font-size:17px;line-height:1.68;color:var(--ink-2);margin-bottom:var(--s2)}
.post ul,.post ol{margin:0 0 var(--s3) 20px;color:var(--ink-2)}
.post li{margin-bottom:9px;line-height:1.6}
.post a:not(.btn):not(.tlink){color:var(--accent-ink);text-decoration:underline;
  text-underline-offset:3px;text-decoration-thickness:1px}
.post blockquote{border-left:2px solid var(--accent);padding-left:var(--s3);
  margin:var(--s4) 0;font-family:var(--display);font-size:clamp(18px,1.5vw,21px);line-height:1.35;
  letter-spacing:-.02em}
.byline{display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap;
  padding-bottom:var(--s3);border-bottom:1px solid var(--hair);margin-bottom:var(--s4)}

/* ============================================================
   CARDS — used only where a row genuinely will not work.
   Refusal: a card never has both a shadow and a border.
   ============================================================ */
.panel{border:1px solid var(--hair);border-radius:var(--r-card);padding:var(--s4);
  background:var(--paper)}
.panel.on-sand{background:#fff;border-color:transparent;box-shadow:0 1px 2px rgba(20,17,16,.05)}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3)}
.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s3)}
@media(max-width:900px){.grid-4{grid-template-columns:1fr 1fr}}
@media(max-width:720px){.grid-2,.grid-4{grid-template-columns:1fr}}

/* post list + work grid share the ledger grammar */
.postlist{border-top:1px solid var(--hair)}
.plink{display:grid;grid-template-columns:170px 1fr 44px;gap:var(--s4);align-items:center;
  padding:var(--s3) 0;border-bottom:1px solid var(--hair);
  transition:padding-left .55s var(--ease),background .55s var(--ease)}
.plink:hover{padding-left:18px;background:var(--hair-2)}
.plink img{width:170px;height:104px;object-fit:cover;border-radius:var(--r-photo)}
.plink h3{transition:color .45s var(--ease)}
.plink:hover h3{color:var(--accent)}
.plink p{color:var(--stone);font-size:15px;margin-top:6px;max-width:60ch}
.plink .ar{justify-self:end;color:var(--faint);transition:transform .55s var(--ease),color .45s}
.plink:hover .ar{transform:translateX(7px);color:var(--accent)}
@media(max-width:720px){
  .plink{grid-template-columns:1fr;gap:var(--s2)}
  .plink img{width:100%;height:200px}
  .plink .ar{display:none}
}

/* ============================================================
   FAQ — real disclosure semantics, driven by [hidden].
   ============================================================ */
.faq{border-top:1px solid var(--hair);max-width:78ch}
.qa{border-bottom:1px solid var(--hair)}
.qa button{width:100%;display:flex;justify-content:space-between;align-items:center;
  gap:var(--s3);background:none;border:0;cursor:pointer;text-align:left;
  font-family:var(--display);font-weight:400;font-size:clamp(21px,1.8vw,26px);
  letter-spacing:-.02em;line-height:1.25;color:var(--ink);
  padding:var(--s3) 0;transition:color .4s var(--ease)}
.qa button:hover{color:var(--accent)}
.qa .ic{position:relative;width:13px;height:13px;flex:none}
.qa .ic::before,.qa .ic::after{content:"";position:absolute;background:currentColor;
  transition:transform .45s var(--ease)}
.qa .ic::before{left:0;right:0;top:6px;height:1px}
.qa .ic::after{top:0;bottom:0;left:6px;width:1px}
.qa[open] .ic::after,.qa button[aria-expanded="true"] .ic::after{transform:scaleY(0)}
.qa .ans{padding:0 0 var(--s3);max-width:66ch}
.qa .ans p{color:var(--stone);font-size:17px;line-height:1.6}

/* ============================================================
   CONTACT — the only dark band below the hero.
   ============================================================ */
.contact{background:var(--ink);color:var(--on-ink)}
.contact .band-rule{border-color:var(--on-hair)}
.contact .band-rule .l{color:var(--on-ink-mute)}
.contact .band-rule .r{color:var(--on-ink-mute)}
.contact h1,.contact h2{color:var(--paper)}
/* when the dark contact band is the first thing on a page it has to clear
   the fixed nav itself, because there is no page-head above it */
.contact#main{padding-top:calc(var(--s7) + var(--s1))}
@media(max-width:900px){.contact#main{padding-top:calc(var(--s6) + var(--s2))}}
.contact .lead{color:var(--on-ink-mute)}
.contact-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--s5)}
@media(max-width:900px){.contact-grid{grid-template-columns:1fr;gap:var(--s4)}}
.contact .meta{margin-top:var(--s4);display:grid;gap:12px;
  padding-top:var(--s3);border-top:1px solid var(--on-hair)}
.contact .meta div{display:flex;gap:12px;align-items:center;color:var(--on-ink-mute);font-size:15px}
.contact .meta svg{width:16px;height:16px;flex:none;color:var(--accent)}
form{display:grid;gap:12px}
input,textarea{
  font-family:var(--sans);font-size:17px;color:var(--paper);
  background:rgba(250,249,246,.05);border:1px solid var(--on-hair);
  border-radius:var(--r-chip);padding:15px 16px;width:100%;
  transition:border-color .4s,background .4s}
input::placeholder,textarea::placeholder{color:var(--on-ink-mute)}
input:focus,textarea:focus{outline:none;border-color:var(--accent);background:rgba(250,249,246,.08)}
textarea{min-height:132px;resize:vertical}
form .btn{justify-content:center;margin-top:var(--s1)}

/* CTA strip that closes every subpage */
.cta-strip{background:var(--sand);text-align:left}
.cta-strip .inner{display:grid;grid-template-columns:1.5fr auto;gap:var(--s4);align-items:end}
@media(max-width:860px){.cta-strip .inner{grid-template-columns:1fr;align-items:start}}

/* ============================================================
   FOOTER — under 700px tall on phones (chrome budget).
   ============================================================ */
footer{background:var(--ink);color:var(--on-ink-mute);padding:var(--s5) 0 var(--s4)}
footer .top{display:grid;grid-template-columns:1.2fr 2fr;gap:var(--s5);
  padding-bottom:var(--s4);border-bottom:1px solid var(--on-hair)}
footer .brand{font-family:var(--display);font-size:26px;letter-spacing:-.03em;
  color:var(--paper);display:flex;align-items:center;gap:10px}
footer .brand img{height:28px;width:auto}
footer .blurb{margin-top:var(--s2);font-size:15px;line-height:1.55;max-width:34ch}
footer .cols{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s3)}
footer .cols h5{font-family:var(--mono);font-size:12px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--on-ink-mute);font-weight:400;margin-bottom:14px}
footer .cols a{display:block;font-size:15px;padding:5px 0;transition:color .35s}
footer .cols a:hover{color:var(--paper)}
footer .base{display:flex;justify-content:space-between;gap:var(--s3);
  flex-wrap:wrap;padding-top:var(--s3);font-size:14px;color:var(--on-ink-mute)}
@media(max-width:860px){
  footer{padding:var(--s4) 0 var(--s3)}
  footer .top{grid-template-columns:1fr;gap:var(--s3);padding-bottom:var(--s3)}
  footer .cols{grid-template-columns:1fr 1fr;gap:var(--s2)}
  footer .blurb{display:none}
  /* 44px minimum tap target on phones */
  footer .cols a{padding:11px 0}
  .navlinks a{padding:16px var(--gutter)}
}

/* ============================================================
   MOTION — one reveal moment, near the top only (hard rule 2).
   Everything further down is static.
   ============================================================ */
.rv{opacity:0;transform:translateY(26px);
  transition:opacity 1.1s var(--ease),transform 1.1s var(--ease)}
.rv.in{opacity:1;transform:none}

@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important}
  .rv{opacity:1;transform:none}
  .intro{display:none}
  .hero-bg img{animation:none;transform:none}
}

/* ============================================================
   NIMBUS — the one persistent element on the page.
   Was duplicated as an inline <style> block on 16 pages.
   ============================================================ */
#wing-helper{position:fixed;right:18px;bottom:16px;z-index:55;
  opacity:0;pointer-events:none;transition:opacity .8s var(--ease)}
#wing-helper.on{opacity:1;pointer-events:auto;cursor:pointer}
@media(max-width:900px){#wing-helper{right:12px;bottom:12px}}

/* ============================================================
   UTILITIES — the only ones. Anything else belongs in a
   named layout, not an inline style attribute.
   ============================================================ */
.measure-xs{max-width:13ch}
.measure-sm{max-width:15ch}
.measure{max-width:17ch}
.measure-lg{max-width:20ch}
.stack-3{margin-top:var(--s3)}
.stack-4{margin-top:var(--s4)}
.stack-5{margin-top:var(--s5)}
.gap-5{margin-bottom:var(--s5)}
.gap-6{margin-bottom:var(--s6)}

/* fact ledger with a word instead of a figure */
.facts--plain .fig{font-family:var(--sans);font-size:18px;font-weight:600;letter-spacing:-.01em}

/* ============================================================
   FOCUS — keyboard users get a visible ring everywhere.
   Found missing by the design-dna rebuild test, 2026-09-06.
   ============================================================ */
:focus{outline:none}
:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:3px;
  border-radius:var(--r-chip);
}
.contact :focus-visible,.hero :focus-visible,nav.menu-open :focus-visible,
footer :focus-visible,.page-head :focus-visible,
nav:not(.solid) :focus-visible{outline-color:var(--paper)}
input:focus-visible,textarea:focus-visible{outline-offset:0}
/* ledger and post-list rows mirror their hover state on keyboard focus */
.lrow:focus-visible,.plink:focus-visible{padding-left:18px;background:var(--hair-2)}
.lrow:focus-visible h3,.plink:focus-visible h3{color:var(--accent)}
.lrow:focus-visible .ar,.plink:focus-visible .ar{transform:translateX(7px);color:var(--accent)}

/* skip link: first tab stop on every page */
.skip{position:absolute;left:-9999px;top:0;z-index:300;
  background:var(--ink);color:var(--paper);padding:14px 20px;
  border-radius:var(--r-chip);font-size:15px;font-weight:500}
.skip:focus{left:12px;top:12px}


/* a portrait phone does not need the major column; give the words the space */
.split:has(.shot--phone){grid-template-columns:38fr 62fr;align-items:center}
.split.flip:has(.shot--phone){grid-template-columns:62fr 38fr}
@media(max-width:900px){
  .split:has(.shot--phone),.split.flip:has(.shot--phone){grid-template-columns:1fr}
}

/* a client site shown on a phone: portrait, never cropped, sat on the band */
.shot--phone{display:flex;flex-direction:column;align-items:center;margin-left:0!important;margin-right:0!important}
.shot--phone img{width:auto!important;max-height:620px;aspect-ratio:auto!important;
  object-fit:cover!important;object-position:top center;
  border-radius:14px;box-shadow:0 30px 60px -30px rgba(20,17,16,.45);
  /* a phone capture ends wherever the viewport ended; fade the cut so it reads
     as a page continuing rather than an image clipped by accident */
  -webkit-mask-image:linear-gradient(180deg,#000 78%,rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(180deg,#000 78%,rgba(0,0,0,0) 100%)}
.shot--phone .cap{text-align:center;margin-top:var(--s3)}
@media(max-width:900px){.shot--phone img{max-height:460px}}


/* ============================================================
   TOUCH — standalone navigation links need a 44px target.
   Inline .tlink inside prose is exempt (it is running text).
   ============================================================ */
@media(max-width:900px){
  .tlink,.page-head .band-rule a{display:inline-flex;align-items:center;min-height:44px}
  .post .tlink,p .tlink{min-height:0}   /* inline prose links stay inline */
  .logo{min-height:44px}
  .contact .meta div{min-height:44px}
}
/* short pages: keep the mascot clear of right-aligned band-rule indices */
body{padding-bottom:0}
.contact#main .wrap>.band-rule .r,.page-head .band-rule .r{padding-right:0}
@media(max-width:900px){
  section:last-of-type,.contact:last-of-type{padding-bottom:calc(var(--s6) + var(--s5))}
}

/* form fields carry a persistent label; a placeholder disappears the moment
   someone starts typing, which is when they most need to know what a box is */
.field{display:grid;gap:7px}
.field label{font-family:var(--mono);font-size:12px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--on-ink-mute)}
.field input:focus+label,.field:focus-within label{color:var(--on-ink-mute)}

/* ============================================================
   ANIMATED BAND BACKGROUNDS  (assets/bg.js)
   One variant per page so no two pages open the same way.
   Painted ONLY on the dark bands; light editorial bands stay clean.
   ============================================================ */
.bg{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.bg canvas{display:block;width:100%;height:100%}
/* a hairline horizon under every dark band, so the canvas has an edge to sit on */
.hero,.page-head,.contact{position:relative;isolation:isolate}
.hero>.wrap,.page-head>.wrap,.contact>.wrap{position:relative;z-index:2}
.hero-bg{z-index:0}
.bg{z-index:1}
/* the photo hero needs the canvas to read over the scrim, the others do not */
.hero .bg{mix-blend-mode:screen;opacity:1}
.page-head .bg{opacity:.9}
/* on the contact band the right half is a form. Fade the canvas out
   before it reaches the fields so nothing moves behind an input. */
.contact .bg{opacity:.55;
  -webkit-mask-image:linear-gradient(90deg,#000 0%,#000 38%,rgba(0,0,0,0) 62%);
  mask-image:linear-gradient(90deg,#000 0%,#000 38%,rgba(0,0,0,0) 62%)}
@media(max-width:900px){
  .contact .bg{-webkit-mask-image:linear-gradient(180deg,#000 0%,rgba(0,0,0,0) 46%);
    mask-image:linear-gradient(180deg,#000 0%,rgba(0,0,0,0) 46%)}
}

/* a faint vignette keeps the canvas from touching the type */
.page-head::before,.contact::before{content:"";position:absolute;inset:0;z-index:1;
  pointer-events:none;
  background:radial-gradient(105% 85% at 14% 50%,rgba(20,17,16,.90) 0%,rgba(20,17,16,.30) 42%,rgba(20,17,16,0) 70%)}

/* ============================================================
   INTRO v2 — "signal lock"
   A grid draws, a scan sweeps, the wordmark resolves out of it,
   then the whole panel irises away. Under 2.2s, reduced-motion safe.
   ============================================================ */


/* two dashboards read better stacked full width than as a 2+1 with a gap */
.evidence--stack > *{grid-column:1 / -1}
.evidence--stack img{aspect-ratio:auto}

/* ============================================================
   THE OPENING  (assets/intro.js)
   Points fly in, assemble into the wordmark, hold, then scatter
   outward and hand over the hero, where the same network is
   still drifting. The letterforms are sampled off an offscreen
   canvas, so the type is literally built out of the network.

   There is exactly ONE intro block in this file. A second one
   used to exist here and its higher specificity silently beat
   the newer rules, which shipped the wordmark permanently
   blurred. If you add another, delete this one.
   ============================================================ */
.intro{position:fixed;inset:0;z-index:200;pointer-events:none;
  background:var(--ink);opacity:1;transition:opacity .55s cubic-bezier(.7,0,.2,1)}
.intro .introcv{position:absolute;inset:0;display:block}
.intro.lift{opacity:0}
.intro.done{display:none}
@media(prefers-reduced-motion:reduce){.intro{display:none}}

/* ============================================================
   LEAD FORM: honeypot, inline errors
   The honeypot is a real, submittable field pushed off screen.
   It is NOT display:none on the input, because a bot that reads
   the computed style skips those; off screen it still gets filled.
   ============================================================ */
.leadform .hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}

.ferr{
  font-family:var(--mono);font-size:12px;letter-spacing:.04em;line-height:1.35;
  color:var(--paper);margin:0;max-width:none;
}
.leadform .field.bad input,.leadform .field.bad textarea{
  box-shadow:inset 0 0 0 1px var(--paper);
}

/* ============================================================
   CONSENT BANNER  (assets/consent.js)
   Only rendered when an analytics ID is configured. No ID, no
   banner, because there is nothing to consent to.
   ============================================================ */
.cc{
  position:fixed;left:var(--gutter);right:var(--gutter);bottom:var(--gutter);z-index:180;
  display:flex;flex-wrap:wrap;align-items:center;gap:14px var(--s2);
  max-width:760px;margin:0 auto;padding:18px 20px;
  background:var(--ink);color:var(--on-ink);
  border-radius:var(--r-chip);box-shadow:0 10px 40px rgba(20,17,16,.28);
}
.cc p{font-size:14px;line-height:1.45;margin:0;flex:1 1 320px;max-width:52ch;color:var(--on-ink)}
.cc a{color:var(--paper);text-decoration:underline;text-underline-offset:3px}
.cc .cc-acts{display:flex;gap:10px;flex:0 0 auto}
.cc .btn{height:40px;padding:0 18px;font-size:12px}
.cc :focus-visible{outline:2px solid var(--paper);outline-offset:3px}
@media(max-width:720px){
  .cc{padding:16px;gap:12px}
  .cc .cc-acts{width:100%}
  .cc .cc-acts .btn{flex:1 1 0}
}
