/* ============================================================================
   OW.L website — shared stylesheet
   Tokens and primitives from owl-design-system/owl-tokens.css v1.0.

   Type uses the system's READER scale (its @media clamps), not the fixed
   1920x1080 presentation scale — these are documents, not slides. Everything
   else (palette, surfaces, accent channel, motion curve, focus floor) is the
   design system unchanged.
   ========================================================================= */

/* --- 0. Fonts ------------------------------------------------------------
   Self-hosted Poppins, replacing the Google Fonts stylesheet. That link was
   render-blocking across two third-party origins: the browser had to resolve and
   handshake fonts.googleapis.com, parse the CSS it returned, then resolve and
   handshake fonts.gstatic.com before any text could paint. Served from here the
   files arrive on the connection already open for this stylesheet, and they pick
   up the immutable, max-age=31536000 header that /assets/* already carries.
   unicode-range is copied verbatim from what Google served, so latin-ext is still
   gated behind characters that need it - an English page fetches only the latin
   faces (38.4 KB of the 65.3 KB on disk). display:swap matches the &display=swap
   the old URL requested, so first paint is never held for a font.
   Poppins is licensed OFL, which permits self-hosting and redistribution. */
@font-face{font-family:'Poppins';font-style:normal;font-weight:400;
  font-display:swap;src:url('/assets/fonts/poppins-400-latin.7d93459d.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Poppins';font-style:normal;font-weight:400;
  font-display:swap;src:url('/assets/fonts/poppins-400-latin-ext.0b1fcab4.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Poppins';font-style:normal;font-weight:500;
  font-display:swap;src:url('/assets/fonts/poppins-500-latin.cd36de20.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Poppins';font-style:normal;font-weight:500;
  font-display:swap;src:url('/assets/fonts/poppins-500-latin-ext.af5fda16.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Poppins';font-style:normal;font-weight:600;
  font-display:swap;src:url('/assets/fonts/poppins-600-latin.f4e80d9d.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Poppins';font-style:normal;font-weight:600;
  font-display:swap;src:url('/assets/fonts/poppins-600-latin-ext.bb1f2d58.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Poppins';font-style:normal;font-weight:700;
  font-display:swap;src:url('/assets/fonts/poppins-700-latin.9338e65f.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Poppins';font-style:normal;font-weight:700;
  font-display:swap;src:url('/assets/fonts/poppins-700-latin-ext.ccfd87f6.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Poppins';font-style:normal;font-weight:800;
  font-display:swap;src:url('/assets/fonts/poppins-800-latin.60bf0aba.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Poppins';font-style:normal;font-weight:800;
  font-display:swap;src:url('/assets/fonts/poppins-800-latin-ext.a72eccfa.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}

/* --- 1. Tokens ----------------------------------------------------------- */
:root{
  --sand:#f8f4e3; --mahogany:#6a3735; --sienna:#ae5b36;
  --olive:#b2bc63; --teal:#98d3d2;
  --ink:#2b1a16;
  --ink-2:rgba(43,26,22,.86); --ink-3:rgba(43,26,22,.66);
  --sand-2:rgba(248,244,227,.82); --sand-3:rgba(248,244,227,.64);
  --sienna-surface:#8c3d1c; --deep:#241413; --tile:#fffdf6; --shell:#edebe3;
  /* Darkened teal for use as a surface, exactly as --sienna-surface is a darkened
     --sienna. Raw --teal (#98d3d2) cannot be a surface here: the Adinkra pillar
     artwork only exists in cream, which would sit at 1.51:1 on it. --teal-ink
     (#266a68) is too light for body copy - .muted lands at 4.41, under the 4.5
     floor. #1d5856 beats the sienna it replaces on all three: sand 7.37 (was
     6.80), .muted 5.56 (5.14), .faint 4.06 (3.77). */
  --teal-surface:#1d5856;
  --olive-ink:#55611f; --teal-ink:#266a68; --sienna-ink:#8f4420;

  /* Radius + elevation, merged from the Harvest reference. Two ideas transfer cleanly
     because OWL already shares Harvest's premise of a warm cream canvas rather than white:
     a disciplined radius scale (20 cards / 16 images and controls / pill tags), and
     surfaces that FLOAT rather than sit flat. The site had seven arbitrary radii
     (14/12/11/10/8/7) and no card elevation at all.

     Harvest's rule that the shadow hue must match the base rather than be neutral gray is
     already native here — every existing shadow is built on rgba(43,26,22), the mahogany
     ink — so these just extend it to card elevation. What is NOT taken: Harvest's
     single-orange discipline, which is the exact opposite of OWL's four-accent system. */
  --r-card:20px; --r-img:16px; --r-pill:999px;
  --shadow-card:0 6px 22px rgba(43,26,22,.09), 0 1px 3px rgba(43,26,22,.05);
  --shadow-card-hi:0 14px 34px rgba(43,26,22,.15), 0 2px 6px rgba(43,26,22,.07);

  /* Hover shades. The pale accents LIGHTEN on hover and the dark ones darken,
     so contrast against their paired ink rises in both directions rather than
     collapsing. Measured against their ink: olive 4.64 -> 5.55, teal 5.69 ->
     6.61, sienna-surface 6.80 -> 8.58, mahogany 8.61 -> 10.58. */
  --olive-hi:#c3cc7e; --teal-hi:#b2e0df;
  --sienna-deep:#75320f; --mahogany-hi:#572c2a;

  /* Accent washes for card hover on cream — all keep body ink above 14:1. */
  --wash-olive:#f2f3e2; --wash-teal:#eaf5f5; --wash-sienna:#f7ece5;
  --font:'Poppins',system-ui,sans-serif;
  --r:18px;
  --ease:cubic-bezier(.22,1,.36,1);
  --wrap:1180px;
  --gut:clamp(20px,5vw,72px);
  --sec:clamp(60px,8.5vw,116px);
  /* Header height must track the logo: the wrap is 62px of logo + 8px .brand
     padding + 20px wrap padding = 90px. This variable also positions the mobile
     dropdown (.nav inset) and the anchor scroll offset, so leaving it at 66 while
     the bar grew would put the menu 24px under the header. */
  --hdr-h:90px;
}
*{margin:0;padding:0;box-sizing:border-box}
/* overflow-x lives on html, not body, and prefers clip over hidden. On body,
   overflow-x:hidden makes the OTHER axis compute to auto, so body became its own
   scroll container while html kept scrolling too — the documented cause of a page
   that scrolls far past its content on iOS Safari. clip does the same clipping
   without creating a scroll container; hidden is the fallback for Safari < 16 and
   is safe on html because it propagates to the viewport instead of nesting a
   second scroller. Measured first: no page overflows horizontally at 320 or 375,
   so this is a guard, not a fix for live overflow. */
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;
  scroll-padding-top:calc(var(--hdr-h) + 16px);
  overflow-x:hidden;overflow-x:clip}
body{font-family:var(--font);background:var(--sand);color:var(--ink);
  font-weight:400;line-height:1.6}
img,svg{max-width:100%;display:block}
a{color:inherit}
::selection{background:var(--olive);color:var(--mahogany)}

/* --- 2. Surfaces --------------------------------------------------------- */
.s-cream{background:var(--sand);color:var(--ink)}
.s-mahogany{background:var(--mahogany);color:var(--sand)}
.s-sienna{background:var(--sienna-surface);color:var(--sand)}
.s-teal{background:var(--teal-surface);color:var(--sand)}
/* MLG's own terracotta, used for the Impact block of their case study so the roadmap
   artwork's white phase labels are readable — they are baked into the image at pure
   white and sat at 1.10:1 on cream. On #B45A32 they reach 4.72:1 and the road still
   separates at 3.84:1.

   This surface is unusual and the numbers are why: at luminance 0.1725 **only pure
   white clears 4.5:1** here. --sand lands at 4.28 (large text only) and --sand-2 at
   3.42, so this section has no dimmed tier — .muted and .faint resolve to white and
   hierarchy is carried by size and weight instead. Darkening the terracotta to buy a
   muted tier does not work either: --sand-2 needs luminance <= 0.114 while the road
   needs >= 0.125, and those windows do not overlap. Non-text boundaries need white at
   >= 0.67 alpha for 3:1. */
.s-terracotta{background:#b45a32;color:#fff}
.s-shell{background:var(--shell);color:var(--ink)}
.muted{color:var(--ink-2)} .faint{color:var(--ink-3)}
.s-mahogany .muted,.s-sienna .muted,.s-teal .muted{color:var(--sand-2)}
.s-terracotta .muted,.s-terracotta .faint{color:#fff}
.s-mahogany .faint,.s-sienna .faint,.s-teal .faint{color:var(--sand-3)}
/* sand-3 (alpha .64) puts .faint at 4.07 on --teal-surface, short of the 4.5 that
   13px/600 needs. .72 lifts it to 4.62. Placed after the grouped rule so it wins at
   equal specificity. The same shortfall exists on .s-sienna (3.78) and on that
   surface it is pre-existing, so it is left alone rather than changed silently. */
.s-teal .faint{color:rgba(248,244,227,.72)}

/* --- 3. Accent channel ---------------------------------------------------
   Two separate channels, because a colour that works as a RULE is not the
   same colour that works as a FILL:

     --accent   line / marker / eyebrow. Remapped to the -ink variant on light
                surfaces, where the raw brand accents fail contrast as text.
     --fill     solid backgrounds, plus the ink and hover shade that go with
                them. Never remapped — a fill keeps the vivid brand colour and
                carries dark ink instead.

   Raw --sienna is deliberately absent from the fill channel: at its best
   pairing it reaches only 4.38:1, so --sienna-surface stands in for it. */
.a-olive {--accent:var(--olive);  --fill:var(--olive);          --fill-ink:var(--mahogany); --fill-hi:var(--olive-hi);    --wash:var(--wash-olive)}
.a-teal  {--accent:var(--teal);   --fill:var(--teal);           --fill-ink:var(--mahogany); --fill-hi:var(--teal-hi);     --wash:var(--wash-teal)}
.a-sienna{--accent:var(--sienna); --fill:var(--sienna-surface); --fill-ink:var(--sand);     --fill-hi:var(--sienna-deep); --wash:var(--wash-sienna)}
.a-sand  {--accent:var(--sand);   --fill:var(--sand);           --fill-ink:var(--mahogany); --fill-hi:#fff;               --wash:rgba(248,244,227,.1)}
.a-mahogany{--accent:var(--mahogany);--fill:var(--mahogany);    --fill-ink:var(--sand);     --fill-hi:var(--mahogany-hi); --wash:rgba(106,55,53,.08)}
.s-cream .a-olive,.s-shell .a-olive{--accent:var(--olive-ink)}
.s-cream .a-teal,.s-shell .a-teal{--accent:var(--teal-ink)}
.s-cream .a-sienna,.s-shell .a-sienna{--accent:var(--sienna-ink)}
.acc{color:var(--accent)}

/* --- 4. Type ------------------------------------------------------------- */
h1,h2,h3{font-weight:600;letter-spacing:-.025em;line-height:1.06}
h1{font-size:clamp(38px,7.6vw,86px);font-weight:700;letter-spacing:-.038em;line-height:1.02}
h2{font-size:clamp(26px,4.4vw,46px);letter-spacing:-.03em;line-height:1.1}
h3,.h3{font-size:clamp(19px,2.2vw,26px);letter-spacing:-.015em;line-height:1.2}
/* .h3 carries h3's scale onto a heading whose LEVEL must be higher for the
   document outline — a card title that is the page's primary content. */
.h3{font-weight:600}
/* Capped below h3 (26px) on purpose. At 25px the two were a pixel apart and
   only weight separated a section's opening line from a card title. */
.lead{font-size:clamp(17px,1.95vw,22px);line-height:1.52;font-weight:400}
.body{font-size:clamp(15px,1.5vw,18px);line-height:1.62}
.cap{font-size:clamp(13px,1.2vw,15px);line-height:1.5}
.label{font-weight:600;font-size:clamp(11px,1.1vw,13px);letter-spacing:.14em;text-transform:uppercase}
.mt-s{margin-top:14px} .mt-m{margin-top:20px} .mt-l{margin-top:clamp(26px,3vw,38px)}

/* --- 5. Layout ----------------------------------------------------------- */
.wrap{max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gut)}
/* Opt-in wider measure for layouts that pair an image with text. */
.wrap.wide{--wrap:1480px}
section{padding-block:var(--sec)}
.chrome{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  flex-wrap:wrap;padding-bottom:18px;margin-bottom:clamp(28px,3.6vw,48px);
  border-bottom:1px solid rgba(58,39,35,.16)}
.s-mahogany .chrome,.s-sienna .chrome,.s-teal .chrome{border-color:rgba(248,244,227,.22)}
.s-terracotta .chrome{border-color:rgba(255,255,255,.7)}
/* Below 700px, stack unconditionally rather than relying on flex-wrap's
   content-length coincidence. Two headings with the same markup ("What guides
   us" / "Adinkra pillars" and "Work with us" / "Next step") were short enough to
   fit on one line at mobile widths while every longer pair wrapped, so those two
   sections looked cramped and inconsistent next to the rest, which read as a
   clean heading-then-label stack. Forcing the stack removes the coincidence:
   every .chrome now behaves identically regardless of its text length. Left-
   aligned to match every other stacked instance - nothing in this stylesheet
   centers a heading, so introducing that here would be the actual inconsistency. */
@media (max-width:700px){
  .chrome{flex-direction:column;align-items:flex-start;justify-content:flex-start}
}
.g2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(26px,3.6vw,56px)}
.g3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(24px,3vw,44px)}
.g4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:clamp(22px,2.6vw,36px)}
@media (max-width:900px){.g3,.g4{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:620px){.g2,.g3,.g4{grid-template-columns:minmax(0,1fr)}}

/* --- 6. Header ----------------------------------------------------------- */
/* The header takes a different brand colour on each page, and the type adapts
   to it rather than the colour being bent to fit the type. Every value below is
   a token from owl-tokens.css — no derived shades.

   Raw --sienna is absent as a ground on purpose: it carries sand at 4.38, ink
   at 3.45 and mahogany at 1.97, so nothing readable sits on it. The system's
   own --sienna-surface stands in, exactly as it does elsewhere.

     page      ground             text        text ratio  accent
     home      --mahogany         --sand            6.87  olive
     about     --sienna-surface   --sand            5.47  teal
     founders  --olive            --ink             6.16  mahogany
     services  --teal             --ink             7.34  mahogany
     contact   --deep             --sand           12.08  olive

   Olive and teal are close to the page in LUMINANCE (1.85 and 1.51) though
   plainly different in hue, so on those two the bottom edge is drawn in ink to
   make the band unambiguous rather than relying on tone alone. */
.hdr{position:sticky;top:0;z-index:60;
  background:var(--hdr-bg,var(--mahogany));color:var(--hdr-fg,var(--sand));
  border-bottom:1px solid var(--hdr-edge,rgba(248,244,227,.18))}
/* These blocks were .hdr.h-* until the back-to-top button needed the same
   per-page colorway. Custom properties inherit, but the button sits at the end of
   <body>, outside the header — so the tokens are declared unscoped and the page's
   variant class goes on both the header and the button. Nothing else in the
   stylesheet targeted .hdr.h-*, so dropping the qualifier changes no cascade. */
.h-mahogany{--hdr-bg:var(--mahogany);       --hdr-fg:var(--sand); --hdr-fg-2:var(--sand-2);
                --hdr-accent:var(--olive);      --hdr-btn-ink:var(--mahogany);
                --hdr-btn-hi:var(--teal);
                --hdr-edge:rgba(248,244,227,.18)}
.h-sienna  {--hdr-bg:var(--sienna-surface); --hdr-fg:var(--sand); --hdr-fg-2:var(--sand-2);
                --hdr-accent:var(--teal);       --hdr-btn-ink:var(--mahogany);
                --hdr-btn-hi:var(--olive);
                --hdr-edge:rgba(248,244,227,.18)}
.h-olive   {--hdr-bg:var(--olive);          --hdr-fg:var(--ink);  --hdr-fg-2:var(--ink-2);
                --hdr-accent:var(--mahogany);   --hdr-btn-ink:var(--sand);
                --hdr-btn-hi:var(--teal-surface);
                --hdr-edge:rgba(43,26,22,.30)}
.h-teal    {--hdr-bg:var(--teal);           --hdr-fg:var(--ink);  --hdr-fg-2:var(--ink-2);
                --hdr-accent:var(--mahogany);   --hdr-btn-ink:var(--sand);
                --hdr-btn-hi:var(--olive-ink);
                --hdr-edge:rgba(43,26,22,.30)}
.h-deep    {--hdr-bg:var(--deep);           --hdr-fg:var(--sand); --hdr-fg-2:var(--sand-2);
                --hdr-accent:var(--olive);      --hdr-btn-ink:var(--mahogany);
                --hdr-btn-hi:var(--sand);
                --hdr-edge:rgba(248,244,227,.18)}
/* Sixth colorway, for the case studies page: all five original grounds were
   already spoken for and raw --sienna is deliberately excluded from this channel
   (it tops out at 4.38:1 with any ink). --teal-surface was built earlier for the
   About "What guides us" band and measures sand 7.37 / sand-2 5.56, so it carries
   header text safely. Olive CTA on it is 3.98 for the boundary and 4.64 for the
   label; hover to sand gives 7.37 / 8.61. */
.h-teal-deep{--hdr-bg:var(--teal-surface);  --hdr-fg:var(--sand); --hdr-fg-2:var(--sand-2);
                --hdr-accent:var(--olive);     --hdr-btn-ink:var(--mahogany);
                --hdr-btn-hi:var(--sand);
                --hdr-edge:rgba(248,244,227,.18)}
.hdr .wrap{display:flex;align-items:center;justify-content:space-between;gap:18px;
  min-height:var(--hdr-h);padding-block:10px}
.hdr .brand{display:block;padding-block:4px}
/* x2 of the previous 31px. The SVG aspect is 445.33/194 = 2.296:1, so this
   renders 142x62 - it fits every viewport (320px leaves 280px of room and the
   logo plus gap plus burger needs 208px). --hdr-h is raised in step below. */
.hdr .brand img{height:62px;width:auto}
.nav{display:flex;align-items:center;gap:clamp(12px,1.8vw,26px)}
.nav a.nl{position:relative;font-size:14.5px;font-weight:500;text-decoration:none;
  color:var(--hdr-fg-2,var(--sand-2));padding-block:7px;min-height:26px;
  transition:color .2s var(--ease)}
/* Underline and CTA follow the page's header accent, chosen per ground above. */
.nav a.nl::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;
  background:var(--hdr-accent);transform:scaleX(0);transform-origin:left;
  transition:transform .28s var(--ease)}
.nav a.nl:hover{color:var(--hdr-fg)}
.nav a.nl:hover::after{transform:scaleX(1)}
.nav a.nl[aria-current="page"]{color:var(--hdr-fg);font-weight:600}
.nav a.nl[aria-current="page"]::after{transform:scaleX(1)}
/* The default button is mahogany on mahogany — it would vanish here. */
.hdr .btn{background:var(--hdr-accent);color:var(--hdr-btn-ink)}
/* Header CTA hover. Two bugs lived here.
   1. The generic .btn:hover:not(:disabled) rule is (0,3,0) and outranked
      .hdr .btn (0,2,0), so hovering forced background:var(--mahogany-hi) on EVERY
      page. On the three headers whose button ink is mahogany, that put dark text
      on a dark ground: measured 1.24:1. Hence the wash-out.
   2. filter:brightness() lifts the text along with the background, so it can never
      raise contrast - it moves both terms the same direction.
   Each hover moves to a DIFFERENT palette colour rather than a tint of its own rest
   colour, chosen to complement that page's header band. Selector is (0,4,0) so it
   outranks the generic rule regardless of source order.
   Measured, rest -> hover (text vs button ink | hover vs the header band):
     index    (sienna band) teal 5.69 -> olive        4.64 | 3.67
     about    (mahogany)    olive 4.64 -> teal         5.69 | 5.69
     founders (olive)       mahogany 8.61 -> teal-surface 7.37 | 3.98
     services (teal)        mahogany 8.61 -> olive-ink    6.11 | 4.04
     contact  (deep)        olive 4.64 -> sand         8.61 | 16.08
   All clear 4.5 for the label and 3.0 for the button against its band. */
.hdr .btn:hover:not(:disabled){
  background:var(--hdr-btn-hi,var(--hdr-accent));
  color:var(--hdr-btn-ink)}

/* Hamburger — real button, 48px target, animates to an X. */
.burger{display:none;width:48px;height:48px;border:0;background:transparent;
  cursor:pointer;color:var(--hdr-fg);align-items:center;justify-content:center}
.burger span{display:block;position:relative;width:23px;height:2px;border-radius:2px;
  background:currentColor;transition:background .2s var(--ease)}
.burger span::before,.burger span::after{content:"";position:absolute;left:0;
  width:23px;height:2px;border-radius:2px;background:currentColor;
  transition:transform .3s var(--ease),top .3s var(--ease)}
.burger span::before{top:-7px} .burger span::after{top:7px}
.burger[aria-expanded="true"] span{background:transparent}
.burger[aria-expanded="true"] span::before{top:0;transform:rotate(45deg)}
.burger[aria-expanded="true"] span::after{top:0;transform:rotate(-45deg)}

/* Six nav items in a narrow desktop header: between 881px (where the burger
   disappears) and about 1000px, "Case studies" wrapped onto a second line. Measured
   with nowrap at 881px the row still fits with an 18px gap to the logo and no
   horizontal scroll, so suppressing the wrap is safe. Scoped above the burger
   breakpoint — the drawer's items are full-width rows and must stay free to wrap. */
@media (min-width:881px){.nav a{white-space:nowrap}}
@media (max-width:880px){
  .burger{display:flex}
  .nav{position:fixed;inset:var(--hdr-h) 0 auto 0;flex-direction:column;
    align-items:stretch;gap:0;background:var(--hdr-bg,var(--mahogany));
    border-bottom:1px solid var(--hdr-edge);
    padding:8px var(--gut) 22px;
    transform:translateY(-8px);opacity:0;visibility:hidden;
    transition:opacity .26s var(--ease),transform .26s var(--ease),visibility .26s}
  .nav.open{transform:none;opacity:1;visibility:visible}
  .nav a.nl{font-size:17px;padding-block:15px;min-height:48px;
    border-bottom:1px solid var(--hdr-edge)}
  .nav a.nl::after{display:none}
  .nav .btn{margin-top:16px;width:100%}
}

/* --- 7. Buttons ---------------------------------------------------------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;
  font:600 15px/1 var(--font);letter-spacing:.02em;text-decoration:none;
  color:var(--sand);background:var(--mahogany);border:0;border-radius:100px;
  padding:15px 28px;min-height:48px;cursor:pointer;
  transition:background .25s var(--ease),transform .25s var(--ease),opacity .25s}
.btn:hover:not(:disabled){background:var(--mahogany-hi);transform:translateY(-2px)}
.btn:disabled{opacity:.55;cursor:default;transform:none}

/* Coloured solid buttons. Pair a button with an accent class and it takes that
   accent's fill and its matching ink: .btn.fill.a-olive, .btn.fill.a-teal,
   .btn.fill.a-sienna. All four pairings clear AA for 15px semibold. */
.btn.fill{background:var(--fill,var(--mahogany));color:var(--fill-ink,var(--sand))}
.btn.fill:hover:not(:disabled){background:var(--fill-hi,var(--mahogany-hi))}

/* Ghost buttons fill with their accent on hover rather than plain currentColor,
   so the outline state resolves into brand colour instead of a flat invert. */
.btn.ghost{background:transparent;color:currentColor;
  box-shadow:inset 0 0 0 1.5px currentColor}
.btn.ghost:hover:not(:disabled){background:currentColor;color:var(--sand);
  transform:translateY(-2px)}
.s-mahogany .btn.ghost:hover,.s-sienna .btn.ghost:hover,.s-teal .btn.ghost:hover{color:var(--mahogany)}
.btn.ghost.a-olive:hover:not(:disabled),
.btn.ghost.a-teal:hover:not(:disabled),
.btn.ghost.a-sienna:hover:not(:disabled),
.btn.ghost.a-sand:hover:not(:disabled){
  background:var(--fill);color:var(--fill-ink);
  box-shadow:inset 0 0 0 1.5px var(--fill)}

.btn.sand{background:var(--sand);color:var(--mahogany)}
.btn.sand:hover:not(:disabled){background:#fff}
.btns{display:flex;flex-wrap:wrap;gap:14px}
.ext{width:13px;height:13px;flex:none;opacity:.75}

/* Inline text link with a WCAG-sized hit area. */
.tl{display:inline-block;font-weight:500;text-decoration:none;
  border-bottom:1px solid currentColor;padding-block:5px;min-height:26px;
  transition:color .2s var(--ease),border-color .2s var(--ease)}
/* Opt-in modifier for text links that carry the external-link arrow. Plain .tl is
   inline-block, which lets an inline <svg> wrap onto its own line — the arrow was
   dropping below the text with the underline stretched under both. inline-flex
   keeps glyph and arrow on one line. Applied only where an arrow is present so the
   16 existing text-only .tl links are untouched. */
.tl.tl-ext{display:inline-flex;align-items:center;gap:7px}
/* Text links resolve to their container's accent on hover. On cream the accent
   channel has already swapped to the -ink variants, so this stays >=5.6:1;
   on the dark surfaces the pale accents are the readable ones. */
.tl:hover{color:var(--accent,var(--sienna-ink))}
.s-mahogany .tl:hover,.s-sienna .tl:hover,.s-teal .tl:hover{color:var(--accent,var(--olive))}
/* No accent hover here: every palette accent fails on terracotta (olive 2.31,
   teal 2.83), so the link keeps white and shows the hover on its underline. */
.s-terracotta .tl{color:#fff} .s-terracotta .tl:hover{color:#fff}

/* --- 8. Page hero -------------------------------------------------------- */
.phero{padding-block:clamp(46px,7vw,84px) clamp(40px,6vw,72px)}
.phero h1{margin-block:14px 18px;max-width:18ch}
.phero .lead{max-width:56ch}

/* --- 8c. Statement band --------------------------------------------------
   Breaks the run of cream sections and gives the positioning line its own
   surface instead of burying it at the foot of the hero.

   The operators are --teal on --sienna-surface: 3.76:1, which clears the 3.0
   large-text threshold because they are set at 22px+ and bold. The words
   around them are --sand at 6.80:1. */
.stmt{max-width:26ch}
.stmt-eq{font-size:clamp(22px,3.4vw,38px);font-weight:500;line-height:1.24;
  letter-spacing:-.015em;margin:0}
.stmt-eq b{font-weight:700;color:var(--teal);padding-inline:.12em}

/* --- 8d. Photographic heroes ---------------------------------------------
   Full-bleed photograph with the copy in the empty part of the frame. Contrast
   is not left to the picture: the darkest pixel in the copy zone gives --ink as
   little as 1.0:1, so a cream scrim runs across the frame. Measured, 0.55 alpha
   over the worst pixel yields 5.43:1, and the gradient never falls below ~0.6
   anywhere the copy can reach.

   Each hero declares its own --hero-aspect (image height as a % of width) so
   the mobile rule can reserve exactly enough room to show the whole picture. */
.hero-photo{position:relative;isolation:isolate;
  background-size:cover;background-repeat:no-repeat;
  background-color:var(--sand);
  background-image:var(--hero-img-lg);
  background-position:var(--hero-focus,64% center);
  padding-block:clamp(44px,5vw,76px);
  min-height:clamp(400px,46vh,540px);
  display:grid;align-items:center;
  overflow:hidden}
/* The watermark is a mobile-only device here: on desktop the photograph fills
   the frame and the two would fight, but on mobile the copy sits on bare cream
   above the picture and that space wants something in it. */
@media (max-width:1000px){.hero-photo{background-image:var(--hero-img-sm)}}

.hero-photo::before{content:"";position:absolute;inset:0;z-index:-1;
  background:var(--hero-scrim,linear-gradient(96deg,
    rgba(248,244,227,.96) 0%,
    rgba(248,244,227,.88) 30%,
    rgba(248,244,227,.52) 54%,
    rgba(248,244,227,0)   76%))}
.hero-photo > .wrap{position:relative;width:100%;max-width:1480px}
.hero-copy{max-width:44ch}
@media (min-width:1280px){.hero-copy{max-width:50ch}}
.hero-photo h1{line-height:.98}
@media (min-width:761px) and (max-width:1279px){
  .hero-photo{--hero-focus:40% center}
  .hero-copy{max-width:32ch}
}

/* ---- Oversized type-led home headline ------------------------------------
   Sized from measured Poppins 900 metrics rather than estimated: at -.05em
   tracking the widest word, "Systems.", measures 4.507em, so the ceiling at
   any width is (available measure / 4.507). 19vw keeps every word inside the
   gutter from 320px up; 288px is the cap, set by where .hero-photo > .wrap
   stops growing (1480 - 2*72 gutter = 1336 available, and 288 * 4.507 = 1298).
   Each word is its own block so the lines break where the copy intends rather
   than wherever the measure happens to run out.
   Selector is qualified as .hero-photo h1.hero-h1 (0,2,1) because the plain
   .hero-photo h1 rule above is (0,1,1) and would otherwise win on line-height
   no matter which order these appear in. */
.hero-photo h1.hero-h1{
  /* Caps via text-transform rather than typing them into the markup, so the DOM
     keeps sentence case: screen readers announce words normally instead of
     risking letter-by-letter spelling, and the source stays editable. */
  text-transform:uppercase;
  /* Re-measured for caps: at -.05em tracking the widest word is "SYSTEMS.", 4.549em
     at weight 800. 18vw clears the 320px case by 18px; 280px is the cap where the
     hero wrap stops growing (1336 available, 280 * 4.549 = 1274). Floor is 44px and
     only governs below a 244px viewport. Sizes are held at the weight-900 values
     even though 800 is 2.1% narrower — dropping the weight was meant to change the
     weight, not to quietly grow the type. */
  /* -10px on mobile (two -5px passes), as a calc subtraction rather than a smaller
     vw rate: a rate change would shed 10px at 375 but nearly 20px by 760, whereas
     this holds the reduction flat across the whole range. Note the 44px floor now
     binds below a 300px viewport, where 18vw - 10px drops under it. Only viewports below 1001px see this —
     the desktop rule further down replaces the whole declaration.
     Upper bound is 180px to match that desktop cap. It was 280px, which had been
     unreachable since 18vw peaks at 180px by 1000px, but left a trap: raising the
     1001px breakpoint would have jumped the headline to 280px and broken the hero
     contrast that the 180px cap exists to protect. */
  font-size:clamp(44px,calc(18vw - 10px),180px);
  font-weight:800;
  letter-spacing:-.05em;
  /* 1.0 is safe here only because the type is caps. In mixed case this measured a
     1px gap — the g of "Design." all but touching the S below it. Caps have no
     descenders, so the clear space is now line-box minus cap-height (~0.30em)
     and the gaps measure 20px at 320 up to 84px at 1920. Verified by pixel-row
     analysis of the render; if this ever reverts to mixed case, 1.0 collides. */
  line-height:1;
  margin-block:clamp(8px,1.2vw,18px) clamp(18px,2.4vw,34px)}
.hero-h1 > span{display:block}
/* Desktop: hold header + hero inside a single viewport. Three lines at
   line-height 1 cost 3x the font-size, and everything else in the hero — the 91px
   header, section padding, eyebrow, lead, buttons — measures 541px at worst, so
   566px is the safe constant. Solving 500 + 3*fs <= 100vh gives the
   height-driven cap; min() takes whichever of the width- and height-driven limits
   binds first, and the 64px max() floor stops very short windows collapsing the
   type to nothing (below ~690px tall the hero will exceed the viewport rather
   than shrink further — a deliberate trade). Scoped to >=1001px because below
   that the hero stacks the photograph underneath, a different vertical budget. */
@media (min-width:761px){
  /* 761px, not 1001px: the mobile stacked hero ends at 760px, so the copy overlays
     the photograph from 761px up. Every protection in this block — the viewport
     cap, the 180px width cap, the lead's measure — was scoped to 1001px, which left
     761-1000px overlaying with none of them. Measured there: body text 2.08-2.26
     against a 4.5 floor, headline 1.88 at 800px, and text running to 93% of the
     width when the scrim fades out at 76%. There was also a 2x font-size cliff at
     exactly 1001px (170px -> 84px).

     Cap is 180px, not 280px, and the photograph is why. The scrim fades to zero at
     76% of the width, so anything reaching past ~61% sits on bare photo. Text ends
     at (wrap offset + gutter + font-size * 4.549), which puts the safe ceiling at
     177px on a 1440-wide window rising to 240px at 3840 — 180px is the value that
     holds at every width. At 280px the headline ran to 82% and measured 1.93:1
     against a 3.0 floor. The earlier fix for that was a much heavier scrim, which
     is what washed the photograph out; capping the type keeps both legible. */
  /* Desktop hero, all three of these paid for out of one budget.
       Measured at 1920x1080 (viewport 993): header 91, hero padding 152, eyebrow
       21, gap 44, headline 399, h1 margin-bottom 34, lead 100, buttons 86 = 927,
       leaving 66px slack against a 594 constant that was 66px conservative.

       Reclaiming half the hero's own padding (76 -> 52 per side, below) funds a
       larger eyebrow gap, looser leading and bigger type without the hero
       outgrowing one screen:
         eyebrow gap  44 -> clamp(30px,4vw,64px)
         line-height  1  -> 1.06
         font-size    141px at 993 (+6%), 84px at 813 (+15%), 180px cap unchanged

       The divisor is 3.18, not 3: three lines at line-height 1.06 cost 3 x 1.06 x
       font-size, so leaving it at 3 would have under-reserved by ~24px and pushed
       the hero past the fold. The constant is 545, sized for the 1440 case where
       the lead wraps to four lines and non-headline content reaches 534.
       Caps have no descenders, so looser leading only widens the 16-78px ink gaps
       measured at line-height 1 — no collision risk. */
  .hero-photo h1.hero-h1{
    font-size:max(50px,min(clamp(44px,18vw,180px),calc((100vh - 545px)/3.18)));
    line-height:1.06;
    margin-top:clamp(30px,4vw,64px)}
  /* Scoped to .is-home (0,2,0) so the contact hero keeps its own deeper padding,
     which is set by an equally specific rule elsewhere. */
  .hero-photo.is-home{padding-block:clamp(32px,3.6vw,52px)}
}
/* The headline needs the whole measure, so the home hero stops capping its copy
   block and the cap moves to the paragraph instead. At (0,2,0) this outranks
   both .hero-copy rules above, including the 32ch one inside the media query,
   so cascade order cannot silently undo it. */
.is-home .hero-copy{max-width:none}
/* The scrim is a percentage gradient, so what matters for the lead is how far it
   reaches as a FRACTION of the viewport — and a fixed ch cap grows as a fraction
   when the window narrows. At a flat 46ch the paragraph hit 53% of the width at
   1280 and measured 4.33:1, under the 4.5 body floor, while passing at 1440+. Any
   single breakpoint leaks, so the cap is whichever of the two limits binds first:
   46ch keeps the measure readable on wide screens, 36vw keeps the right edge
   inside the light zone on narrow ones. Narrowing the paragraph beats thickening
   the scrim — it keeps the photograph visible rather than veiling more of it. The
   headline still gets the full measure; only the paragraph is capped. */
/* Desktop only. Below 1001px the hero stacks the photograph underneath and the
   scrim is switched off entirely, so the copy sits on plain cream at ~15:1 and
   needs no width limit — applying 40vw there squeezed the paragraph to 150px on a
   375px phone and broke it into six stub lines. */
@media (min-width:761px){
  .is-home .hero-copy .lead{max-width:min(46ch,40vw)}
  /* Slightly smaller than the global .lead on desktop only. At the narrow end of
     desktop the 40vw cap forced this paragraph to five lines (167px at 1280), and
     that height came straight out of the headline's budget — the hero overflowed by
     30-40px at 1280x800 and 1366x768. 1.45vw/20px brings it back to four lines
     without touching mobile, where the lead is the main copy and needs its size. */
  .is-home .hero-copy .lead{font-size:clamp(16px,1.45vw,20px)}
}

/* Mobile: the whole photograph, uncropped. background-size:100% auto renders it
   at its natural ratio, pinned to the bottom, and the padding reserves exactly
   its height — so the copy sits above it on plain cream (15:1) and no scrim is
   needed at all. */
@media (max-width:760px){
  /* Both heroes: the photograph drops below the copy at its natural ratio, so
     the whole frame including the subject is visible and the copy sits on plain
     cream at 15:1. Overlaying on a portrait phone frame does not work for
     Contact — holding the subject in frame needs focus ~70%, which leaves only
     23-31% of the width as wall, so the copy crosses the subject and the scrim
     required to keep it legible (0.55-0.85) washes her out. */
  .hero-photo{background-size:100% auto;background-position:center bottom;
    min-height:0;
    padding-block:clamp(30px,7vw,44px);
    padding-bottom:calc(var(--hero-aspect,58%) + clamp(18px,4vw,30px))}
  .hero-photo::before{background:none}
  .hero-copy{max-width:none}
  /* Mobile-only watermark, hung off the lead paragraph rather than the section.
     Anchoring it to .lead means it centres on the paragraph whatever the
     headline does — positioning it from the top of the hero put it behind the
     h1 and drifted as the h1 rewrapped. */
  .is-home .hero-copy .lead{position:relative}
  .is-home .hero-copy .lead::before{content:"";position:absolute;z-index:-1;
    pointer-events:none;
    /* The deck's own mobile treatment: 135% of the measure, bleeding off the
       right edge, clipped by the hero. The crop is intentional — this is a
       large mark, not a small badge. Anchored to .lead so its mass sits on the
       paragraph rather than up at the headline. */
    top:50%;right:-38%;transform:translateY(-50%);
    width:135%;height:auto;aspect-ratio:1;
    background:url('/assets/brand/owl-symbol-sienna.706d0dfa.svg')
      center/contain no-repeat;
    opacity:.06}
}

/* per-page pictures */
.hero-photo.is-home{
  --hero-img-lg:url('/assets/photo/hero20-wide.5846116e.webp');
  --hero-img-sm:url('/assets/photo/hero20-sm.27810cdc.webp');
  /* No --hero-scrim override: the shared default is used. A heavier one existed
     while the headline was capped at 280px and ran to ~90% of the measure, but the
     viewport-height cap brought that back to 193px, so text now stops at 61% of
     the width. Measured requirement over the actual glyph pixels is alpha 0.33 at
     worst (body copy at 35-45%) and <=0.20 for the headline; the default supplies
     0.66-0.73 there and 0.26 at 60-65%, clearing every band with headroom while
     still reaching full transparency at 76% — so the right quarter of the frame is
     unveiled. The old override never dropped below 0.52 and washed out the whole
     photograph. */
  --hero-aspect:64%;                 /* 1472x941 -> 63.9% */
  /* 40% rather than centre: at centre the standing figure's head met the top
     edge exactly. Simulated the cover crop to pick this. */
  --hero-focus:64% 40%}
/* MUST stay after .hero-photo.is-home above: both selectors are (0,2,0), so the
   later one wins. Placed before it, this silently lost and retina screens were
   served the 1x file while the preload still pulled the 2x - both downloaded. */
/* Retina desktop gets a 1:1 asset instead of the browser stretching the 1x one.
   Honest about the ceiling: the source holds 1472 real pixels, so this 3840 file
   is a 2.6x upscale and measured only +9% edge energy over the browser doing it.
   Kept because it costs one file that only 2x screens fetch; the 1x rebuild
   (+17%) and the mobile asset (+70%) are where the real gains are. */
@media (min-width:1001px) and (min-resolution:1.5dppx){
  .hero-photo.is-home{--hero-img-lg:url('/assets/photo/hero20-wide2x.95b84092.webp')}
}
.hero-photo.is-contact{
  --hero-img-lg:url('/assets/photo/contact-wide.c15c2fb4.webp');
  --hero-img-sm:url('/assets/photo/contact-sm.45c485fc.webp');
  --hero-scrim:linear-gradient(96deg,
    rgba(248,244,227,.96) 0%,
    rgba(248,244,227,.88) 30%,
    rgba(248,244,227,.52) 54%,
    rgba(248,244,227,0)   76%);
  --hero-aspect:56.3%;               /* 1672x941 */
  /* The wall is the left half, so favour it horizontally. Vertically 25%: this
     hero's copy is short, so min-height drives its height, and a centred crop
     at 414px cut the top of the subject's head off. */
  --hero-focus:38% 25%;
  min-height:clamp(430px,56vh,560px)}

/* Contact hero, desktop only.
   1. The wrap matched the home hero at 1480px while the form below uses the 1180px
      --wrap, so the heading started 150px left of the first form field at 1600px+.
      Tying it to var(--wrap) puts the eyebrow, h1 and lead on the same left edge as
      NAME, and keeps tracking the token if that ever changes.
   2. Deeper padding: 76px -> 124px at 1920. Scoped to >=1001px because below that
      the hero stacks the photograph underneath and sets its own padding-bottom to
      reserve the image height - overriding it there would break that reservation. */
@media (min-width:761px){
  .hero-photo.is-contact > .wrap{max-width:var(--wrap)}
  .hero-photo.is-contact{padding-block:clamp(72px,6.5vw,124px)}
}

/* --- 8e. Full-bleed band -------------------------------------------------
   A photograph running the full width of the viewport, no container and no
   radius. Overrides the section padding default, which would otherwise inset it
   by 116px top and bottom and stop it bleeding. The ratio flattens on wide
   screens and squares up on phones, where a 21:9 strip would be ~160px tall. */
.bleed{padding:0;width:100%;line-height:0;background:var(--deep);
  position:relative;overflow:hidden}
.bleed picture,.bleed img{display:block;width:100%;height:auto}
/* Centre suits this frame: it is an overhead shot with the subject matter — the
   table, the devices, the hands — in the middle band. The 22% offset the previous
   image needed would have thrown that off. Verified by simulating the crop. */
.bleed img{aspect-ratio:21/9;object-fit:cover;object-position:center}
@media (max-width:999px){.bleed img{aspect-ratio:16/9}}
@media (max-width:599px){.bleed img{aspect-ratio:4/3}}

/* --- 9. Cards ------------------------------------------------------------ */
.card{position:relative;isolation:isolate;
  border-top:3px solid var(--accent,var(--olive));padding:22px 18px 0 0;
  transition:transform .3s var(--ease),border-top-width .3s var(--ease)}
.card:hover{transform:translateY(-5px);border-top-width:5px}
/* Accent wash on hover. It bleeds past the text box so the card reads as a
   tinted plate rather than a boxed paragraph; isolation keeps the z-index:-1
   layer inside the card instead of dropping behind the section background. */
.card::before{content:"";position:absolute;inset:-4px -16px -12px -16px;
  border-radius:14px;background:var(--wash,transparent);opacity:0;z-index:-1;
  transition:opacity .3s var(--ease)}
.card:hover::before{opacity:1}
.card .num{font-size:13px;font-weight:600;letter-spacing:.12em;color:var(--accent,var(--olive))}
.card h3{margin-block:9px 10px}
.csb{border-top:2px solid var(--accent,var(--olive));padding-top:11px}
.csb .csk{font-weight:600;font-size:11.5px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--accent,var(--olive))}
.csb .csv{font-size:15.5px;line-height:1.44;margin-top:7px}

/* --- 9c. Problems we solve ------------------------------------------------
   Ported from the deck, not devised here. The figure fit is its .landfig recipe:
   max-width AND max-height at 100% with object-fit:contain, inside a flex cell
   that carries min-width:0 / min-height:0 so a wide image cannot force the grid
   track wider than its share. Below 900px the grid stacks and the figure releases
   max-height, which is what the deck does at its own narrow breakpoint - without
   that release the image collapses to nothing once the row height is unconstrained.
   The artwork has its labels baked in at 2122px, so they become illegible on a
   phone; that is why the alt text carries all five and the rows below carry the
   substance. */
.landgrid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.94fr);
  gap:clamp(26px,3.6vw,52px);align-items:center}
.landcopy{display:flex;flex-direction:column;justify-content:center;min-width:0}
.landfig{margin:0;min-width:0;min-height:0;overflow:hidden;
  display:flex;align-items:center;justify-content:center}
.landfig img{max-width:100%;max-height:100%;width:auto;height:auto;
  object-fit:contain;display:block}
@media (max-width:900px){
  .landgrid{grid-template-columns:minmax(0,1fr)}
  .landfig{margin-top:4px;overflow:visible}
  .landfig img{max-width:100%;max-height:none}
}
/* Symptom -> service rows, also the deck's: statement left, the segment that
   answers it right, colour-matched to that segment's accent in the accordion
   below. On cream the a-* classes remap to the -ink variants, so these stay
   legible at small sizes. */
.prow{display:grid;grid-template-columns:minmax(0,1fr) auto;
  gap:clamp(14px,2vw,26px);align-items:baseline;
  padding:17px 0;border-bottom:1px solid rgba(58,39,35,.14)}
.prow:last-child{border-bottom:0}
.prow .pq{font-weight:600;font-size:clamp(16.5px,2.1vw,25px);
  letter-spacing:-.015em;line-height:1.25}
.prow .pa{font-weight:500;font-size:clamp(13px,1.4vw,16px);letter-spacing:.02em;
  text-align:right;white-space:nowrap;text-decoration:none;min-height:24px;
  color:var(--accent,var(--olive-ink));
  border-bottom:1px solid transparent;transition:border-color .2s var(--ease)}
.prow .pa:hover{border-bottom-color:currentColor}
@media (max-width:700px){
  .prow{grid-template-columns:minmax(0,1fr);gap:5px;padding:13px 0}
  .prow .pa{text-align:left;white-space:normal}
}

/* --- 10. Lists ----------------------------------------------------------- */
.list{list-style:none;display:flex;flex-direction:column;gap:20px}
.list li{position:relative;padding-left:38px;font-size:clamp(16px,1.7vw,19px);
  font-weight:500;line-height:1.3}
.list li::before{content:"";position:absolute;left:0;top:.52em;width:22px;height:3px;
  border-radius:2px;background:var(--accent,var(--olive-ink))}
.list li span{display:block;font-weight:400;font-size:clamp(14px,1.4vw,16px);
  line-height:1.5;margin-top:6px;color:var(--ink-2)}
.s-mahogany .list li span,.s-sienna .list li span,.s-teal .list li span{color:var(--sand-2)}
.s-terracotta .list li span{color:#fff}

/* --- 11. Chips ----------------------------------------------------------- */
.chips{display:flex;flex-wrap:wrap;gap:9px}
.chip{border-radius:100px;padding:8px 17px;font-size:13.5px;font-weight:500;
  border:1.5px solid currentColor;opacity:.9;
  transition:color .25s var(--ease),border-color .25s var(--ease),opacity .25s}
.chip:hover{color:var(--accent);border-color:var(--accent);opacity:1}
.chip.fill{border-color:transparent;background:var(--accent,var(--olive));
  color:var(--mahogany);opacity:1}

/* --- 12. Accordion -------------------------------------------------------
   The Services segments. A <button> per row drives an aria-controls panel;
   the panel animates on grid-template-rows so it works with unknown content
   height and still collapses to exactly zero. */
.acc{border-top:1px solid rgba(58,39,35,.18)}
.acc-item{border-bottom:1px solid rgba(58,39,35,.18)}
.acc-btn{display:flex;align-items:center;gap:clamp(14px,2vw,26px);width:100%;
  background:transparent;border:0;cursor:pointer;text-align:left;color:inherit;
  font-family:var(--font);padding:clamp(20px,2.4vw,30px) 0;min-height:64px;
  transition:padding-left .3s var(--ease)}
.acc-btn:hover{padding-left:10px}
.acc-num{font-size:13px;font-weight:600;letter-spacing:.12em;
  color:var(--accent,var(--olive-ink));flex:none;width:26px}
.acc-ttl{flex:1;font-size:clamp(18px,2.4vw,29px);font-weight:600;
  letter-spacing:-.02em;line-height:1.16}
.acc-sub{display:block;font-size:clamp(13.5px,1.4vw,16px);font-weight:400;
  line-height:1.5;color:var(--ink-2);margin-top:7px;max-width:62ch}
.acc-ico{flex:none;width:34px;height:34px;border-radius:50%;display:grid;
  place-items:center;border:1.5px solid var(--accent,var(--olive-ink));
  color:var(--accent,var(--olive-ink));
  transition:background .28s var(--ease),color .28s var(--ease),transform .28s var(--ease)}
.acc-ico svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2.4;
  stroke-linecap:round}
.acc-ico .v{transition:transform .3s var(--ease)}
.acc-btn[aria-expanded="true"] .acc-ico{background:var(--accent,var(--olive-ink));
  color:var(--sand)}
.acc-btn[aria-expanded="true"] .acc-ico .v{transform:rotate(90deg) scaleX(0)}
.acc-panel{display:grid;grid-template-rows:0fr;
  transition:grid-template-rows .42s var(--ease)}
.acc-panel[data-open="true"]{grid-template-rows:1fr}
.acc-inner{overflow:hidden}
.acc-body{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,300px);
  gap:clamp(24px,3.4vw,52px);align-items:start;
  padding:4px 0 clamp(30px,3.6vw,44px)}
@media (max-width:760px){
  .acc-body{grid-template-columns:minmax(0,1fr)}
  .acc-figure{order:-1;max-width:260px}
}
/* Case-study panels carry no illustration, so they opt out of the 300px figure
   track rather than leaving it empty and pulling the prose narrow. */
.acc-body.solo{grid-template-columns:minmax(0,1fr)}
/* No plate behind the artwork — the illustrations carry their own ground, and
   a tinted card under them read as a double background. */
.acc-figure{position:relative;aspect-ratio:1}
.acc-figure img{width:100%;height:100%;object-fit:contain}

/* ---- Case studies: the Design / Systems / Impact sections as accordions ----
   These reuse .acc-item / .acc-btn / .acc-panel so the existing accordion() drives
   them unchanged — including "first item open" and the #hash deep link. What differs
   from /services is that each item IS a full-bleed coloured section rather than a row
   in a list, so the button spans the section and only its contents align to the wrap. */
.cs-accsec{padding-block:0}
.cs-acc{border-bottom:0}
.cs-acc .acc-btn{display:block;padding:clamp(26px,3.4vw,44px) 0;min-height:0}
.cs-acc .acc-btn:hover{padding-left:0}          /* the base nudge would shift the wrap */
.cs-acc .acc-line{display:flex;align-items:center;gap:clamp(14px,2vw,26px);
  width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gut);
  transition:transform .3s var(--ease)}
.cs-acc .acc-btn:hover .acc-line{transform:translateX(6px)}
.cs-acc h2{margin:0}
.cs-acc .acc-num{width:auto;min-width:26px}

/* Vertical rhythm lives on .cs-accbody, never on .acc-inner: .acc-inner is the grid
   item that collapses to 0fr, and padding on it would stay visible when closed. */
.cs-accbody{padding-bottom:var(--sec)}

/* Sienna at full strength is 4.37:1 on sand — under AA for the 13px number. The -ink
   variant is 6.2:1. Same reason .s-cream .a-olive already remaps to --olive-ink. */
.s-cream .cs-acc.a-sienna{--accent:var(--sienna-ink)}

/* On teal the panel chrome inverts. The expanded icon is the trap: the shared rule
   paints --accent as the fill and --sand as the glyph, which on this section is light
   teal under near-white — the same 1.08:1 shape that hit .tchip three times. The glyph
   takes the section's own ground instead: 5.0:1. */
.s-teal .cs-acc .acc-sub{color:var(--sand-2)}
.s-teal .cs-acc .acc-btn[aria-expanded="true"] .acc-ico{color:var(--teal-surface)}

@media (prefers-reduced-motion:reduce){
  .cs-acc .acc-btn:hover .acc-line{transform:none}
}

/* --- 13. Founders -------------------------------------------------------- */
.fcard{background:rgba(248,244,227,.07);border-radius:var(--r);
  padding:clamp(24px,3vw,38px);border-top:3px solid var(--accent,var(--olive))}
.s-cream .fcard,.s-shell .fcard{background:rgba(43,26,22,.035)}
.fcard .role{font-weight:600;font-size:13px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--accent,var(--olive));margin-top:8px}
.fcard .creds{margin-top:18px;padding-top:16px;border-top:1px solid rgba(248,244,227,.2)}
.s-cream .fcard .creds,.s-shell .fcard .creds{border-color:rgba(43,26,22,.14)}

/* --- 13b. Team members ---------------------------------------------------
   The deck's founders slide, carried onto the site: a circular portrait beside
   the text with an emblem badge, not a card with a full-bleed photo on top.
   The deck runs the photo at 230px on its 1920 stage and steps it to 190 / 150
   / 132; those are display sizes, so they transfer directly to a document. */
.team{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(30px,4vw,64px)}
@media (max-width:1199px){.team{grid-template-columns:minmax(0,1fr)}}
.team.solo{grid-template-columns:minmax(0,1fr)}
@media (min-width:1200px){.team.solo > .member{max-width:calc(50% - clamp(15px,2vw,32px))}}

.member{display:flex;gap:clamp(18px,2.2vw,30px);align-items:flex-start}
@media (max-width:520px){.member{flex-direction:column;gap:16px}}

.photo{position:relative;flex:0 0 auto;
  width:clamp(132px,11.5vw,168px);height:clamp(132px,11.5vw,168px)}
.photo > img{width:100%;height:100%;object-fit:cover;display:block;
  border-radius:50%;box-shadow:0 8px 26px rgba(43,26,22,.16);
  transition:transform .3s var(--ease)}
.member:hover .photo > img{transform:translateY(-4px) scale(1.02)}
/* Oden is a drawing, not a photograph — he sits inside a tinted disc instead of
   being cropped to one. */
.photo.is-mascot > img{object-fit:contain;background:var(--wash,rgba(43,26,22,.05));
  box-shadow:none}
.emblem{position:absolute;right:2px;bottom:2px;border-radius:50%;
  width:clamp(46px,4.2vw,58px);height:clamp(46px,4.2vw,58px);
  box-shadow:0 4px 12px rgba(43,26,22,.24)}
.emblem img{width:100%;height:100%;object-fit:cover;border-radius:50%;display:block}

.who{display:flex;flex-direction:column;min-width:0;padding-top:2px;flex:1 1 auto}
.mname{font-weight:600;font-size:clamp(21px,2.6vw,30px);letter-spacing:-.02em;
  line-height:1.08}
.mrole{font-weight:600;font-size:clamp(11.5px,1.2vw,13.5px);letter-spacing:.09em;
  text-transform:uppercase;margin-top:11px;color:var(--accent)}
/* A role whose initials spell something. The uppercase transform would flatten
   the acronym out of existence, so this variant keeps sentence case and marks
   the letters that carry it. */
.mrole.spells{text-transform:none;letter-spacing:.01em;
  font-size:clamp(13px,1.4vw,15.5px)}
.mrole.spells .ini{text-decoration:underline;text-decoration-thickness:2px;
  text-underline-offset:3px}
/* Caps the measure when the row is a single full-width column; in the
   two-column layout the track is already narrower than this. */
.mbio{font-weight:400;font-size:clamp(14.5px,1.5vw,17px);line-height:1.5;
  margin-top:14px;max-width:62ch}
.mhandle{font-weight:500;font-size:clamp(12.5px,1.2vw,14px);letter-spacing:.04em;
  margin-top:13px}
.mhandle a{text-decoration:none;border-bottom:1px solid currentColor}

/* The deck's "+ More" affordance: a text button whose gap opens on hover. */
.mmore{margin-top:16px;align-self:flex-start;background:none;border:0;cursor:pointer;
  padding-block:5px;min-height:26px;box-sizing:content-box;
  font:600 clamp(14px,1.4vw,15px)/1 var(--font);letter-spacing:.02em;
  color:var(--accent);display:inline-flex;align-items:center;gap:8px;
  transition:gap .2s var(--ease),color .2s var(--ease)}
.mmore:hover{gap:12px}
.mmore .plus{font-weight:400;font-size:19px;line-height:1;
  transition:transform .3s var(--ease)}
.mmore[aria-expanded="true"] .plus{transform:rotate(45deg)}

.pdetail{display:grid;grid-template-rows:0fr;
  transition:grid-template-rows .42s var(--ease)}
.pdetail[data-open="true"]{grid-template-rows:1fr}
.pdetail > div{overflow:hidden}
.pdetail .inner{padding-top:16px;margin-top:16px;
  border-top:1px solid rgba(43,26,22,.14)}
.s-mahogany .pdetail .inner,.s-sienna .pdetail .inner,.s-teal .pdetail .inner{
  border-top-color:rgba(248,244,227,.2)}

/* --- 14. Pillars --------------------------------------------------------- */
.pill{text-align:center}
.pill img{height:clamp(52px,6vw,72px);width:auto;margin:0 auto 16px;opacity:.95;
  transition:transform .35s var(--ease)}
.pill:hover img{transform:translateY(-5px) rotate(-4deg)}
.pill h3{font-size:clamp(17px,1.9vw,21px)}

/* --- 14b. Designation block ----------------------------------------------
   A credential: the seal beside the explanation. Seal first on narrow screens
   so the badge is what you see, with the text underneath. */
.desig{display:grid;grid-template-columns:minmax(0,240px) minmax(0,1fr);
  gap:clamp(20px,3vw,44px);align-items:center;
  padding:clamp(22px,2.6vw,34px);border-radius:var(--r);
  background:rgba(43,26,22,.04);
  border-left:3px solid var(--accent,var(--olive))}
.s-mahogany .desig,.s-sienna .desig,.s-teal .desig{background:rgba(248,244,227,.07)}
@media (max-width:720px){
  .desig{grid-template-columns:minmax(0,1fr);justify-items:center;text-align:left}
  /* An explicit width, not just a max: justify-items:center sizes the track to
     content, and an image at width:100% of a zero-width parent resolves to 0. */
  .desig-seal{width:min(210px,62%)}
}
/* Entrance and hover are on different elements so they never fight over
   transform: the wrapper handles the reveal, the image handles the hover. */
.desig-seal img{width:100%;height:auto;display:block;
  transition:transform .4s var(--ease),filter .4s var(--ease);
  filter:drop-shadow(0 6px 14px rgba(43,26,22,.14))}
.desig-seal:hover img{transform:translateY(-7px) rotate(2.5deg) scale(1.035);
  filter:drop-shadow(0 16px 28px rgba(43,26,22,.24))}

/* Reveal: the seal settles in like a stamp being pressed. Gated on .js, so
   without JavaScript it is simply visible. */
.js .desig[data-anim] .desig-seal{opacity:0;transform:scale(.82) rotate(-8deg);
  transition:opacity .6s var(--ease),transform .75s cubic-bezier(.34,1.56,.64,1)}
.js .desig[data-anim].is-in .desig-seal{opacity:1;transform:none}

@media (prefers-reduced-motion:reduce){
  .js .desig[data-anim] .desig-seal{opacity:1;transform:none}
  .desig-seal:hover img{transform:none}
}
.desig h3{margin-bottom:10px}

/* --- 15. Corporate grid -------------------------------------------------- */
.corp{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(20px,2.6vw,34px)}
@media (max-width:860px){.corp{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:520px){.corp{grid-template-columns:minmax(0,1fr)}}

/* --- 16. Form ------------------------------------------------------------ */
.form{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}
@media (max-width:640px){.form{grid-template-columns:minmax(0,1fr)}}
.f-full{grid-column:1/-1}
.f-lab{display:block;font-size:13px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;margin-bottom:7px;color:var(--ink-2)}
.f-lab .req{color:var(--sienna-ink)}
/* Border alpha is .5, not .26. WCAG 1.4.11 wants 3:1 for the boundary that
   identifies a control, and this field's fill (--tile #fffdf6) sits only 1.08:1
   against the cream page — so the fill identifies nothing and the border is doing
   the whole job. At .26 it measured 1.72 against the page and 1.87 against its own
   fill; .5 gives 3.19 and 3.45, clearing both adjacent surfaces. */
.field{font:400 16px/1.4 var(--font);color:var(--ink);background:var(--tile);
  border:1px solid rgba(43,26,22,.5);border-radius:10px;padding:12px 13px;
  min-height:48px;width:100%;font-family:var(--font);
  transition:border-color .2s var(--ease),box-shadow .2s var(--ease)}
.field:hover{border-color:rgba(43,26,22,.66)}
.field:focus{outline:0;border-color:var(--sienna-ink);box-shadow:0 0 0 3px rgba(143,68,32,.18)}
.field.bad{border-color:#a3271b;box-shadow:0 0 0 3px rgba(163,39,27,.14)}
textarea.field{min-height:132px;resize:vertical}
/* The radio itself is 16px and fails WCAG 2.5.8, so it is stretched invisibly
   across the whole chip and the chip becomes the measured target. */
.radios{display:flex;flex-wrap:wrap;gap:10px}

/* Consent checkbox. Built on .rchip's mechanics — a visually hidden native input covering
   the label, so the whole row is the hit area — but square rather than a pill, and it wraps
   to multiple lines because the wording has to carry the purpose, not just "I agree". */
.ckchip{position:relative;display:flex;align-items:flex-start;gap:11px;
  border:1.5px solid rgba(43,26,22,.5);border-radius:12px;
  padding:14px 16px;cursor:pointer;
  background:var(--tile);transition:border-color .2s,background .2s}
.ckchip input{position:absolute;inset:0;width:100%;height:100%;opacity:0;margin:0;cursor:pointer}
.ckchip .tick{flex:none;width:20px;height:20px;margin-top:1px;border-radius:5px;
  border:1.5px solid rgba(43,26,22,.55);background:var(--tile);
  display:grid;place-items:center;transition:border-color .2s,background .2s}
.ckchip .tick svg{width:13px;height:13px;fill:none;stroke:var(--tile);stroke-width:3.4;
  stroke-linecap:round;stroke-linejoin:round;opacity:0;transition:opacity .15s}
.ckchip .ckt{font-size:14.5px;line-height:1.5;color:var(--ink-2)}
.ckchip:hover{border-color:rgba(43,26,22,.68)}
/* The tick uses an ADJACENT-SIBLING selector, not :has(). The input is opacity:0, so if
   :has() were ever unsupported a consent box would show no checked state at all — worse
   here than on a radio chip, because the whole point is that the person can see what they
   agreed to. `input:checked + .tick` needs nothing beyond CSS2, and the DOM order puts the
   tick immediately after the input to make that possible. :has() is kept only for the
   chip's own border and wash, which are decoration. */
.ckchip input:checked + .tick{border-color:var(--sienna-ink);background:var(--sienna-ink)}
.ckchip input:checked + .tick svg{opacity:1}
.ckchip:has(input:checked){border-color:var(--sienna-ink);background:rgba(143,68,32,.07)}
.ckchip:has(input:focus-visible){outline:3px solid currentColor;outline-offset:3px}
.ckchip.bad{border-color:#a3271b;box-shadow:0 0 0 3px rgba(163,39,27,.14)}
/* The link inside must be clickable, so it sits above the input overlay. */
.ckchip .ckt a{position:relative;z-index:1}
/* Same 1.4.11 reasoning as .field: the unchecked chip has no fill of its own, so
   its border is the only boundary. .3 measured 1.89; .5 gives 3.19. The checked
   state was already fine at 6.31 via --sienna-ink. */
.rchip{position:relative;display:inline-flex;align-items:center;gap:9px;
  border:1.5px solid rgba(43,26,22,.5);border-radius:100px;padding:11px 19px;
  min-height:44px;cursor:pointer;font-size:14.5px;font-weight:500;
  transition:border-color .2s var(--ease),background .2s var(--ease)}
.rchip input{position:absolute;inset:0;width:100%;height:100%;opacity:0;
  margin:0;cursor:pointer}
.rchip .dot{width:15px;height:15px;border-radius:50%;flex:none;
  border:1.5px solid rgba(43,26,22,.55);transition:border-color .2s,box-shadow .2s}
.rchip:hover{border-color:rgba(43,26,22,.68)}
.rchip:has(input:checked){border-color:var(--sienna-ink);background:rgba(143,68,32,.07)}
.rchip:has(input:checked) .dot{border-color:var(--sienna-ink);
  box-shadow:inset 0 0 0 3.5px var(--sienna-ink)}
.rchip:has(input:focus-visible){outline:3px solid currentColor;outline-offset:3px}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.f-msg{grid-column:1/-1;font-size:15px;line-height:1.5;min-height:24px;
  padding:12px 15px;border-radius:10px;display:none}
.f-msg.show{display:block}
.f-msg.ok{background:rgba(85,97,31,.12);color:var(--olive-ink);
  border:1px solid rgba(85,97,31,.3)}
.f-msg.err{background:rgba(163,39,27,.09);color:#8e2418;
  border:1px solid rgba(163,39,27,.28)}
.f-note{font-size:13px;line-height:1.5;color:var(--ink-3)}

/* --- 17. Footer ---------------------------------------------------------- */
/* Bottom padding covers the fixed back-to-top button's own footprint (48px
   button + 32px viewport margin = 80px) so it can never sit over the legal
   row's tagline in the bottom-right corner when a viewport happens to bottom
   out exactly at the page end. */
.ftr{padding-block:clamp(44px,6vw,70px) 80px}
.ftr img{height:29px;width:auto}
.frow{display:flex;flex-wrap:wrap;justify-content:space-between;gap:26px;align-items:flex-start}
.flinks{display:flex;flex-wrap:wrap;gap:6px 22px;max-width:560px}
.flinks a{font-size:14.5px;text-decoration:none;color:var(--sand-2);
  padding-block:5px;min-height:26px;transition:color .2s}
/* Footer links cycle through the three accents that stay readable on mahogany:
   olive 4.64:1, teal 5.69:1, sand 8.61:1. Raw sienna is excluded — it lands at
   1.97:1 against this ground. */
.flinks a:hover{color:var(--sand)}
.flinks a:nth-child(3n+1):hover{color:var(--olive)}
.flinks a:nth-child(3n+2):hover{color:var(--teal)}
.flinks a:nth-child(3n):hover{color:var(--sand)}

.social{display:flex;gap:10px;margin-top:14px}
.social a{display:grid;place-items:center;width:42px;height:42px;border-radius:50%;
  border:1px solid rgba(248,244,227,.28);color:var(--sand-2);
  transition:background .25s var(--ease),color .25s var(--ease),
             border-color .25s var(--ease),transform .25s var(--ease)}
.social a:hover{background:var(--sand);color:var(--mahogany);transform:translateY(-2px)}
/* Each icon fills with a different accent; all four carry mahogany ink. */
.social a:nth-child(1):hover{background:var(--olive);border-color:var(--olive)}
.social a:nth-child(2):hover{background:var(--teal);border-color:var(--teal)}
.social a:nth-child(3):hover{background:var(--sand);border-color:var(--sand)}
.social a:nth-child(4):hover{background:var(--olive-hi);border-color:var(--olive-hi)}
.social svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}
.legal{margin-top:34px;padding-top:22px;border-top:1px solid rgba(248,244,227,.2);
  display:flex;flex-wrap:wrap;justify-content:space-between;gap:12px}
/* The policy link rides in the .legal strip rather than .flinks: that row is the site's
   nav equivalent and capped at 560px, where a policy link would compete with the pages
   people actually came for. Underlined because it is the one link in this strip. */
.flegal{color:var(--sand-2);text-decoration:underline;text-underline-offset:2px;
  text-decoration-thickness:1px;padding-block:4px;transition:color .2s var(--ease)}
.flegal:hover{color:var(--sand)}

/* Footer referral band. .legal already carries a rule line, so this band takes the top
   border and .legal's own margin drops when the two are adjacent — two 34px gaps and two
   rules read as a mistake. */
.frefs{margin-top:34px;padding-top:22px;border-top:1px solid rgba(248,244,227,.2)}
.frefs+.legal{margin-top:20px}
.frefs-h{display:block;margin-bottom:11px}
.frefs-list{display:flex;flex-wrap:wrap;gap:9px 14px;margin-bottom:12px}
.frefs-list a{display:inline-flex;align-items:center;
  font-size:14.5px;font-weight:600;text-decoration:none;color:var(--sand);
  /* Border alpha is set by WCAG 1.4.11, not by taste: it is the visual boundary that
     identifies these as controls, so it needs 3:1 against the mahogany footer. Measured
     over rgb(106,55,53) — .30 gives 2.16:1 (fails), .45 gives 3.07:1, .50 gives 3.43:1. */
  background:rgba(248,244,227,.10);border:1px solid rgba(248,244,227,.50);
  border-radius:var(--r-pill);padding:8px 16px;min-height:34px;
  transition:background .25s var(--ease),border-color .25s var(--ease)}
.frefs-list a:hover{background:rgba(248,244,227,.20);border-color:rgba(248,244,227,.72)}

/* The Google Workspace and Carrd marks carry the same referral links. The anchor fills the
   wrapper's content box so the whole chip is the target, not just the artwork inside it.
   The ring is INSET: .tball is overflow:hidden, which clips an outward outline entirely. */
.toolink{display:grid;place-items:center;width:100%;height:100%;
  line-height:0;border-radius:inherit}
/* The wrapper's padding moves onto the anchor, so the anchor is the WHOLE chip rather than
   just the artwork inside it. Without this the anchor is only the content box — 20px in
   .wf-tools, under the 24px WCAG 2.5.8 (AA) minimum — and the white ring around the logo
   is dead space that looks clickable and is not. Everything is border-box, so the artwork
   lands in exactly the same place either way. .linked is set in the markup rather than
   inferred with :has() so the padding hand-off cannot silently half-apply. */
.tdot.linked,.tball.linked,.tool-logo.linked{padding:0}
/* .wf-tools .tdot (line ~1415) and .cs-logos.dots-only .tdot (~1391) re-declare padding at
   the SAME specificity as .tdot.linked and sit LATER in the sheet, so they win on source
   order and the hand-off silently half-applies: the wrapper keeps its padding, the anchor
   adds its own, and the artwork renders at half size in a still-too-small target. Measured:
   anchor 20px, img 10px where both should be 30px / 20px. Zero them at (0,3,0). */
.wf-tools .tdot.linked{padding:0}
.cs-logos.dots-only .tdot.linked{padding:0}
.tdot.linked>.toolink{padding:6px}
.wf-tools .tdot.linked>.toolink{padding:5px}
.cs-logos.dots-only .tdot.linked>.toolink{padding:9px}
.tball.linked>.toolink{padding:clamp(15px,1.7vw,22px)}
.tool-logo.linked>.toolink{padding:8px}
.toolink:focus-visible{outline:3px solid var(--ink);outline-offset:-3px;border-radius:inherit}

/* --- 18. Accessibility floor --------------------------------------------- */
.skip{position:absolute;left:-9999px;top:0;background:var(--mahogany);color:var(--sand);
  padding:12px 20px;border-radius:0 0 10px 0;z-index:99;text-decoration:none}
.skip:focus{left:0}
:focus-visible{outline:3px solid currentColor;outline-offset:3px;border-radius:8px}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
  white-space:nowrap}

/* --- 17b. Case-study pull quote --------------------------------------------
   Its own spacing rather than the shared .mt-l utility (26-38px), which is tuned
   for ordinary block flow and left this reading cramped between the role list
   above and the link below. A pull quote needs to sit apart to read as a quote:
   roughly double the utility above and below, and 20px to the attribution so the
   speaker doesn't crowd the last line. */
.cs-quote{margin-block:clamp(48px,5.6vw,80px) 0;max-width:60ch;
  border-left:3px solid var(--sienna-ink);
  padding-left:clamp(20px,2.4vw,32px)}
.cs-quote .cap{margin-top:20px}
.cs-after{margin-top:clamp(40px,4.8vw,68px)}

/* --- 18a2. Case study exhibits -------------------------------------------
   Client marks sit on white tiles rather than straight on the section. The
   monograms were drawn for white; their forest-green and terracotta colourway was
   never tested against this site's #f8f4e3, and a tile is honest about that
   instead of quietly restaging someone else's identity on our ground. */
.vtiles{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(9px,1.2vw,16px)}
.vtile{background:#fff;border-radius:var(--r-card);padding:clamp(12px,1.5vw,18px);
  display:grid;place-items:center;box-shadow:var(--shadow-card);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.vtile:hover{transform:translateY(-3px);box-shadow:var(--shadow-card-hi)}
/* Two things cap how big a mark renders: this height, and the tile's inner width once
   max-width:100% and object-fit kick in. Raising the height alone did nothing in the
   761-1000px band, where the two-column grid leaves each tile ~77-104px of inner width
   and the width was already binding — hence the trimmed padding above. */
.vtile img{display:block;max-width:100%;height:clamp(76px,9.6vw,124px);object-fit:contain}
.vtile.wide{margin-top:clamp(9px,1.2vw,16px)}
.vtile.wide img{height:clamp(140px,17vw,224px)}

/* Palette — ported from the deck, including its behaviour. Three things carry over:
   the swatches form one joined bar (no gaps, clipped by the row's radius), the label
   is a translucent dark pill so a single ink is readable on all four fills rather
   than four hand-picked foregrounds, and hovering one swatch expands and lifts it
   while its siblings desaturate. Click or press Enter/Space to copy the hex. */
.swrow{display:flex;border-radius:var(--r-card);overflow:hidden;margin-top:clamp(14px,1.8vw,24px)}
.sw{flex:1;min-width:0;height:clamp(76px,9vw,104px);display:flex;align-items:flex-end;
  padding:clamp(9px,1.1vw,13px);cursor:pointer;
  transition:flex-grow .3s var(--ease),transform .25s var(--ease),filter .25s var(--ease)}
.swrow:hover .sw{filter:saturate(.82)}
.swrow .sw:hover{filter:none;flex-grow:1.6;transform:translateY(-4px)}
.sw span{background:rgba(20,12,10,.66);color:#fdf7ec;padding:4px 9px;border-radius:7px;
  font-weight:600;font-size:clamp(11px,1.15vw,13.5px);letter-spacing:.02em;white-space:nowrap}
.sw:focus-visible{outline:3px solid var(--ink);outline-offset:-4px;border-radius:8px}

/* Tool cards for the systems chain: a logo plate, the step, the product, and what it
   actually does. The plate is white because these are third-party marks with their
   own backgrounds baked in, and several vanish on the teal section without it. */
.tools{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(22px,3vw,44px)}
.tool{display:flex;flex-direction:column;gap:12px}
.tool-h{display:flex;align-items:center;gap:14px}
.tool-logo{flex:0 0 auto;width:clamp(44px,4.6vw,56px);height:clamp(44px,4.6vw,56px);
  background:#fff;border-radius:var(--r-img);display:flex;align-items:center;
  justify-content:center;padding:8px;box-shadow:var(--shadow-card)}
/* width/height:auto is load-bearing. These imgs carry width/height attributes, so
   max-width alone constrains the width while the attribute keeps the intrinsic
   height — a 192x153 mark rendered 40 wide by 153 tall, spilling out of the plate
   and over the list below it. The plate has a definite height, so max-height:100%
   has something to resolve against. */
.tool-logo img{display:block;width:auto;height:auto;max-width:100%;max-height:100%}
.tool-n{min-width:0}
.tool-n .num{font-size:clamp(11px,1.1vw,13px);font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--accent,var(--olive))}
.tool-n h3{margin:2px 0 0;font-size:clamp(19px,2.1vw,26px)}
.tool ul{list-style:none;display:flex;flex-direction:column;gap:7px;margin:2px 0 0}
.tool li{position:relative;padding-left:20px;font-size:clamp(14px,1.45vw,16.5px);
  line-height:1.45;color:var(--sand-2)}
.tool li::before{content:"";position:absolute;left:0;top:.62em;width:11px;height:2px;
  border-radius:2px;background:var(--accent,var(--olive))}
.s-cream .tool li{color:var(--ink-2,rgba(43,26,22,.72))}

/* MLG's own five-phase roadmap. It stays on the cream section rather than getting a
   dark plate, and that is a measured decision: on cream the road reads at 16.4:1, the
   phase circles at 8.7:1 and their rings at 5.3:1, while every dark surface in this
   palette collapses at least one of them (the road is #161615, so it needs a ground of
   luminance >= 0.121 to separate, and --mahogany, --teal-surface and --deep all sit at
   0.06-0.09). The artwork's own phase labels are pure white — built for a dark ground —
   so they simply do not show here; the names are set as real text in .phases below,
   which a screen reader can also actually read. */
/* The roadmap and the keys handover are **mobile only** — on desktop they never fitted the
   column widths without either dominating the page or reading as a bad crop, so they are
   gone above 760px rather than compromised.

   They are painted as background-images inside the max-width:759px query, not <img>
   elements hidden with display:none. That is deliberate: Chrome fetches a display:none
   image even with loading="lazy" (measured on this project), so the hide-one approach
   would have cost every desktop visitor 189KB for artwork they never see. A background
   declared only inside an unmatched media query is never requested at all.

   Both use aspect-ratio taken from the real pixel dimensions, so the box reserves its space
   before the image decodes AND nothing is cropped. Do not put a made-up ratio here: the
   2.35/1 cover-crop that briefly sat on the hands was a desktop-only measure, invented when
   the band was 666px tall at 1280px wide. Carried onto mobile it trimmed 18% off the
   forearms for no reason — at 375px the natural band is only 195px tall, so there is nothing
   to trim. Both graphics stay edge to edge because the artwork's own left and right edges cut
   the subject; inset, those cuts read as severed limbs. */
.road,.handover{display:none}
@media (max-width:759px){
  .road{display:block;margin:clamp(30px,4.4vw,44px) 0 0;line-height:0;
    aspect-ratio:1000/2091;
    background-position:center;background-size:contain;background-repeat:no-repeat}
  /* The image URL is scoped to an OPEN panel. These live inside the Impact panel,
     which ships collapsed, and a background in a matched rule is fetched whether or
     not its box is visible — 194KB downloaded on mobile for content nobody opened.
     A background in an unmatched selector is never requested. */
  .acc-panel[data-open="true"] .road{background-image:url('/assets/case/mlg-road-tall.dfe7e076.webp')}
  .handover{display:block;margin:clamp(30px,4.4vw,44px) 0 clamp(6px,1vw,14px);line-height:0;
    aspect-ratio:2000/1041;
    background-position:center;background-size:cover;background-repeat:no-repeat}
  /* The image URL is scoped to an OPEN panel. These live inside the Impact panel,
     which ships collapsed, and a background in a matched rule is fetched whether or
     not its box is visible — 194KB downloaded on mobile for content nobody opened.
     A background in an unmatched selector is never requested. */
  .acc-panel[data-open="true"] .handover{background-image:url('/assets/case/mlg-handover.09cfb954.webp')}
}

/* The phase legend — on desktop this is now the ONLY place the five phases appear, since
   the roadmap artwork is mobile-only, so it carries the content rather than decorating it.
   The number is generated content because it ties back to the numbered circles in the
   artwork on mobile; a plain list marker would not match them. */
.phases{list-style:none;display:grid;grid-template-columns:repeat(5,minmax(0,1fr));
  gap:clamp(14px,1.8vw,26px);margin:clamp(22px,2.6vw,38px) 0 0;counter-reset:phase}
.phases li{counter-increment:phase;border-top:2px solid var(--accent,var(--olive-ink));
  padding-top:11px}
.phases li::before{content:counter(phase);display:block;font-size:clamp(11px,1.1vw,13px);
  font-weight:600;letter-spacing:.12em;color:var(--accent,var(--olive-ink))}
.phases b{display:block;margin-top:3px;font-size:clamp(15px,1.6vw,19px);font-weight:600;
  letter-spacing:-.015em}
.phases span{display:block;margin-top:5px;font-size:clamp(12.5px,1.3vw,14.5px);
  line-height:1.45;color:var(--ink-2,rgba(43,26,22,.72))}
/* The phase OWL handed off at: a heavier rule, so the hand-off point is findable on
   desktop where the artwork's "we are here" pin is not shown. */
.phases li.is-here{border-top-width:4px}

/* Impact block on terracotta. Every accent-coloured detail in here is remapped to white:
   the phase rules, the card top-borders and numbers and the quote's rule all need >= 3:1
   as boundaries and the small accent text needs 4.5, and no palette accent reaches either
   on this ground (olive measures 2.31, teal 2.83). */

/* --- Harvest merge, scoped to the case studies page -----------------------------
   These are on .cs only. .card and .prow are shared with four other pages, and giving
   the whole site floating surfaces is a bigger decision than this page's brief — the
   same treatment can be lifted site-wide later by dropping the .cs prefix.

   The Harvest ideas being borrowed: surfaces that float on the warm canvas rather than
   sitting flat, depth from a warm-tinted shadow rather than from a border, a disciplined
   radius scale, and generous card padding. The accent-coloured eyebrow is Harvest's
   section-heading pattern (theirs is orange on cream); OWL keeps its own four accents
   instead of collapsing to one, which is the whole point of not taking Harvest's
   single-chromatic rule. Measured on sand: sienna-ink 6.31, olive-ink 6.11, teal-ink
   5.69, mahogany 8.61 — all clear 4.5 at the 11-13px eyebrow size. */
.cs .s-cream .label.faint{color:var(--accent,var(--sienna-ink))}

/* Feature-card treatment for the tool chain. Harvest's card is the same colour as its
   canvas and gets its depth from shadow, not contrast — on a dark section the equivalent
   is a 6% light tint, which keeps the band intact. At that tint body copy still measures
   4.85:1 and headings 6.31:1; at 10% body would drop to 4.43 and fail. */
.cs .tool{background:rgba(255,255,255,.06);border-radius:var(--r-card);
  padding:clamp(22px,2.6vw,32px);box-shadow:var(--shadow-card);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.cs .tool:hover{transform:translateY(-3px);box-shadow:var(--shadow-card-hi)}

/* Further case studies as floating cards rather than ruled rows. */
.cs .csrows{display:grid;gap:clamp(12px,1.6vw,18px)}
.cs .csrows .prow{background:var(--tile,#fffdf6);border-bottom:0;border-radius:var(--r-card);
  padding:clamp(20px,2.4vw,30px) clamp(20px,2.6vw,34px);box-shadow:var(--shadow-card);
  align-items:center;
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.cs .csrows .prow:hover{transform:translateY(-3px);box-shadow:var(--shadow-card-hi)}

/* --- Case-studies overhaul: Harvest flow + media ---------------------------------
   All .cs-scoped. The template's flow is followed (centred hero with floating product
   preview cards over a warm wash, trust logo row, eyebrow+title section headings,
   feature-card grid, 2-column text+screenshot blocks, centred closing stack) and its
   visual language (product UI as the dominant image, integration logos in circles,
   grayscale trust marks). Two of its rules are deliberately NOT followed, by decision:
   OWL keeps its four accents rather than collapsing to one orange, and keeps the teal
   and terracotta bands rather than running everything on cream.

   MOTION IS AUTHORED HERE, NOT EXTRACTED. The template set — markdown, tokens.json,
   variables.css, theme.css — defines no animation, transition, duration or easing
   anywhere; only hover states and a gradient that "suggests movement". What follows is
   written in that idiom and layered on the site's existing reveal()/data-anim system,
   so prefers-reduced-motion is already honoured. */

/* Hero: a warm wash behind floating proof. The template's wash is orange/marigold/peach
   at low opacity; this is the same gesture in OWL's palette, kept under 0.5 alpha so it
   never competes with the copy sitting on it. */
/* The monarch closes the hero .wrap and the next section already runs padding-top:0, so
   zeroing the hero's bottom padding leaves .legacy's own margin-block as the only spacing
   on either side of it — equal above and below across the section seam. .phero's shared
   bottom padding is untouched for the other pages' heroes. */
.cs-hero{position:relative;overflow:hidden;padding-bottom:0}
.cs-hero .wrap{position:relative;z-index:2}
/* Kept deliberately faint. The first pass ran these at .24-.30 alpha and the cream read
   as discoloured rather than warmed — the template's own rule is that the wash suggests
   warmth and "never competes with content". At these values it is felt, not seen. */
.cs-wash{position:absolute;inset:-20% -15% auto -15%;height:145%;z-index:0;pointer-events:none;
  background:
    radial-gradient(48% 42% at 14% 22%, rgba(174,91,54,.13), transparent 66%),
    radial-gradient(44% 40% at 82% 14%, rgba(178,188,99,.11), transparent 68%),
    radial-gradient(52% 46% at 66% 82%, rgba(152,211,210,.10), transparent 70%);
  filter:blur(14px);
  animation:cs-drift 30s var(--ease) infinite alternate}
@keyframes cs-drift{
  from{transform:translate3d(-1.5%,-1%,0) scale(1.02)}
  to  {transform:translate3d(2%,1.5%,0) scale(1.08)}
}

/* Floating product preview cards — the template's "dashboard preview card": white
   surface, 16px radius, soft warm shadow, offset at a slight angle for depth. */
/* Stacked, not side by side: the SOP tracker is a dense spreadsheet and in a narrow side
   column it was a texture rather than a readable artefact. Full width it renders about
   1180x549 on a desktop, where the pillar, system, owner and status columns are legible. */

/* Full-bleed phone band, every width. Height is capped and the image covers: the source is
   1.128:1, so an uncropped full-bleed band would stand 1134px tall at 1280 and 1702px at
   1920. object-position biases up and left, which is where the phone screen sits, so the
   crop takes the empty ground rather than the device. */
.phoneband{margin:clamp(34px,4.4vw,64px) 0 0;line-height:0;overflow:hidden}
/* Transparent cutout: phone and hand in the left ~45%, forearm running out through the
   bottom-right corner. Mobile shows it whole at full width, touching both edges. From
   760px it is anchored to the right edge at a much smaller size — at full height it
   stood 561px tall and dominated the hero. Never object-fit:cover: the ratio is kept
   exactly, so nothing is cropped and the section colour shows through the 57% of the
   frame that is transparent. */
.phoneband img{display:block;width:105.263%;max-width:none;height:auto;margin-inline:auto -5.263%}
@media (min-width:760px){
  .phoneband img{width:45%;height:auto;margin-inline:auto -2.25%}
}
.shotcard{margin:0;background:#fff;border-radius:var(--r-img);overflow:hidden;
  box-shadow:var(--shadow-card-hi);line-height:0;
  transition:transform .5s var(--ease),box-shadow .5s var(--ease)}
.shotcard img{display:block;width:100%;height:auto}
.shotcard.tilt-l{transform:rotate(-1.4deg)}
.shotcard.tilt-r{transform:rotate(1.8deg)}
.shotcard:hover{transform:rotate(0deg) translateY(-6px);box-shadow:var(--shadow-card-hi)}
.shotcap{margin-top:12px;line-height:1.5}

/* .shotcard declares margin:0 and sits AFTER the .mt-* utilities in this sheet, so at
   equal specificity source order wins and every .mt-* on a shotcard silently resolved
   to 0. That is why the SOP tracker sat flush against the tool chips above it — the
   white Zoom chip and the white card merged into one shape. Restate at class+class. */
.shotcard.mt-s{margin-top:14px}
.shotcard.mt-m{margin-top:20px}
.shotcard.mt-l{margin-top:clamp(26px,3vw,38px)}


/* ---------- Policy pages ----------
   Named .policy, NOT .legal: .legal is already the footer's copyright strip
   (display:flex, space-between, a sand border and 34px of top margin). Putting that
   class on <main> silently handed the whole page a flex row and an invisible rule —
   it looked almost right, which is what made it worth catching. ---------
   The site's base `li` is built for marketing lists — 38px indent, 16-19px, weight 500 —
   which reads as a set of claims rather than a document. A policy needs ordinary prose
   lists, so .policy resets them inside its own scope and nowhere else. */
.policy h2{font-size:clamp(21px,2.3vw,27px);letter-spacing:-.015em;line-height:1.18;
  max-width:34ch;scroll-margin-top:calc(var(--hdr-h) + 20px)}
.policy-body{max-width:68ch}
.policy-body > p{margin-top:12px}
.policy-body > h2{margin-top:clamp(30px,3.6vw,46px);padding-top:clamp(18px,2.2vw,26px);
  border-top:1px solid rgba(43,26,22,.13)}
.policy-body > h2:first-child{margin-top:0;padding-top:0;border-top:0}
.policy-body > h2 + p{margin-top:11px}
.policy strong{font-weight:600;color:var(--ink)}
.policy a.tl{padding-block:0;min-height:0}

/* Prose lists: a marker we draw ourselves, since base li clears list-style. */
.policy ul{list-style:none;display:flex;flex-direction:column;gap:9px;margin:13px 0 0}
.policy ul li{position:relative;padding-left:20px;
  font-size:clamp(15px,1.5vw,18px);font-weight:400;line-height:1.62;color:var(--ink-2)}
.policy ul li::before{content:"";position:absolute;left:2px;top:.72em;
  width:5px;height:5px;border-radius:50%;background:var(--ink-3)}

/* Oden beside "Your choices". .policy-body is capped at 68ch, which on a 1180px wrap
   leaves roughly 400px of empty margin to its right — this puts him there instead of
   indenting the prose, so every section keeps the same left edge. The artwork is NOT
   mirrored (the thought bubble contains real "?" glyphs), so he faces slightly outward;
   at 1000px+ he is far enough from the text for that to read as looking up and thinking
   rather than away. */
.policy-aside{display:grid;grid-template-columns:minmax(0,1fr);gap:clamp(18px,2.4vw,30px)}
.policy-oden{margin:0;display:flex;justify-content:center;align-items:flex-start}

/* Two placements, ONE download. Oden sits under the summary on mobile and in the right
   margin beside "Your choices" from 1000px. Both are backgrounds declared inside their own
   media query, which is the only way to get that: a duplicated <img> hidden with
   display:none is still fetched, and the two positions live in different sections so no
   amount of `order` can move a single element between them. Same technique as
   .heroband.hb-phone / .hb-legacy on the case study. */
.oden-fig{margin:0;display:none;background-repeat:no-repeat;background-position:center top;
  background-size:contain;aspect-ratio:560/744}
@media (max-width:999px){
  .oden-fig.oden-m{display:block;width:clamp(150px,44vw,210px);margin:clamp(20px,4vw,28px) auto 0;
    background-image:url('/assets/brand/mascot/oden-thinking.a3f07aa3.webp')}
}
@media (min-width:1000px){
  .oden-fig.oden-d{display:block;width:100%;
    background-image:url('/assets/brand/mascot/oden-thinking.a3f07aa3.webp')}
}

@media (min-width:1000px){
  /* Break out of the 68ch prose column into the margin beside it. The text column keeps
     its own measure; only the figure lives outside. */
  .policy-aside{grid-template-columns:minmax(0,1fr) clamp(190px,19vw,240px);
    gap:clamp(24px,3vw,44px);align-items:start;
    width:calc(100% + clamp(190px,19vw,240px) + clamp(24px,3vw,44px));
    max-width:calc(var(--wrap) - var(--gut) * 2)}
}

/* The summary card. Sits on .s-shell so it reads as a distinct plate without needing a
   fourth accent; the left rule is the only ornament. */
.policy-sum{max-width:68ch;border-left:3px solid var(--mahogany);
  padding:clamp(16px,2vw,22px) 0 clamp(16px,2vw,22px) clamp(16px,2vw,24px)}
.policy-sum ul li{font-weight:400}

@media (max-width:759px){
  .policy-sum{padding-right:0}
}

/* ---------- SOP tracker: a live table where a screenshot used to be ----------
   Replaces shot-sop-desktop.webp (101 KB, one image request) with real markup: sortable,
   filterable, searchable, and readable with JS off because every row is in the HTML.
   The --st-* locals map the site tokens once at the top so the component can be lifted out
   whole without hunting for colour references. */
.soptable{
  --st-line:rgba(43,26,22,.13);
  /* Pillars use OWL's own accents rather than the spreadsheet's arbitrary chip colours.
     Each pair is the AA-safe ink on its matching wash: measured 5.65 / 6.62 / 5.90. */
  --st-customer:var(--teal-ink);   --st-customer-wash:var(--wash-teal);
  --st-product:var(--sienna-ink);  --st-product-wash:var(--wash-sienna);
  --st-business:var(--olive-ink);  --st-business-wash:var(--wash-olive);
}

/* ---- controls ---- */
.st-controls{display:flex;flex-wrap:wrap;gap:14px 20px;align-items:flex-end;
  margin-bottom:clamp(16px,2vw,22px)}
.st-field{flex:1 1 240px;min-width:0;max-width:340px}
.st-field-wide{flex-basis:100%;max-width:none}
.st-label{display:block;font-size:11px;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink-3);margin-bottom:7px}
.st-search{position:relative}
.st-search > svg{position:absolute;left:13px;top:50%;translate:0 -50%;width:16px;height:16px;
  stroke:var(--ink-3);stroke-width:2;fill:none;pointer-events:none}
.st-search input{width:100%;font:inherit;font-size:14.5px;color:var(--ink);
  background:var(--tile);border:1px solid var(--st-line);border-radius:var(--r-pill);
  padding:11px 44px 11px 38px;min-height:44px;
  transition:border-color .2s var(--ease),box-shadow .2s var(--ease)}
.st-search input::placeholder{color:var(--ink-3)}
.st-search input::-webkit-search-cancel-button{display:none}
.st-search input:focus{outline:0;border-color:var(--sienna-ink);
  box-shadow:0 0 0 3px rgba(143,68,32,.18)}
.st-clear{position:absolute;right:5px;top:50%;translate:0 -50%;
  width:34px;height:34px;display:none;place-items:center;
  background:none;border:0;border-radius:50%;cursor:pointer;color:var(--ink-3)}
.st-clear svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none}
.st-clear:hover{background:var(--shell);color:var(--ink)}
.st-search.has-value .st-clear{display:grid}

.st-chips{display:flex;flex-wrap:wrap;gap:8px}
.st-chip{display:inline-flex;align-items:center;gap:8px;font:inherit;font-size:13.5px;
  font-weight:600;color:var(--ink-2);background:var(--tile);
  border:1px solid var(--st-line);border-radius:var(--r-pill);
  padding:9px 15px;min-height:40px;cursor:pointer;
  transition:background .2s var(--ease),border-color .2s var(--ease),color .2s var(--ease)}
.st-chip:hover{border-color:rgba(43,26,22,.34)}
.st-chip .n{font-size:12px;font-weight:600;color:var(--ink-3);
  background:var(--shell);border-radius:var(--r-pill);padding:1px 8px;min-width:24px;text-align:center}
.st-chip[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:var(--tile)}
.st-chip[aria-pressed="true"] .n{background:rgba(255,255,255,.22);color:var(--tile)}

/* ---- card + scroller ---- */
.st-card{position:relative;background:var(--tile);border-radius:var(--r-card);
  box-shadow:var(--shadow-card);overflow:hidden}
/* touch-action:pan-x is load-bearing. Without it a vertical page swipe that begins inside
   the scroller is swallowed and the page stops moving under the finger — the exact bug the
   tool band shipped with once. */
.st-scroll{overflow-x:auto;overscroll-behavior-x:contain;touch-action:pan-x;scrollbar-width:thin}
.st-card::after{content:"";position:absolute;right:0;top:0;bottom:0;width:44px;
  pointer-events:none;z-index:3;opacity:0;transition:opacity .3s var(--ease);
  background:linear-gradient(to right,rgba(255,253,246,0),var(--tile))}
.st-card.can-scroll:not(.at-end)::after{opacity:1}

/* ---- table ---- */
/* min-width stops seven columns from crushing the longest procedure name into three lines
   on a 1180px wrap. Narrower than this the .st-scroll takes over; under 760px the whole
   thing becomes cards and the min-width is dropped. */
.soptable table{width:100%;min-width:1010px;border-collapse:collapse;font-size:14.5px;line-height:1.45}
.soptable caption{text-align:left;padding:18px clamp(16px,2vw,22px) 0;
  font-size:13px;color:var(--ink-3)}
.soptable th,.soptable td{text-align:left;vertical-align:top;
  padding:13px clamp(10px,1.2vw,16px);border-bottom:1px solid var(--st-line)}
/* Deliberately NOT position:sticky. overflow-x:auto makes .st-scroll the containing block
   for sticky, and with no height constraint the header could never actually stick — dead
   code that reads as a feature. Thirteen rows do not need a frozen header. */
.soptable thead th{background:var(--shell);border-bottom:1px solid rgba(43,26,22,.2);
  padding:0;white-space:nowrap}
.soptable tbody tr:last-child td{border-bottom:0}
.soptable tbody tr{transition:background .18s var(--ease)}
.soptable tbody tr:hover{background:rgba(43,26,22,.035)}
.soptable tbody tr[hidden]{display:none}

/* The sort control is a real button filling the th, not a click handler on the th. */
.st-sort{display:flex;align-items:center;gap:7px;width:100%;
  font:inherit;font-size:11.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-2);background:none;border:0;cursor:pointer;
  padding:14px clamp(10px,1.2vw,16px);min-height:46px;text-align:left}
.st-sort:hover{color:var(--ink);background:rgba(43,26,22,.05)}
.st-sort .arrow{flex:0 0 auto;width:12px;height:12px;stroke:currentColor;stroke-width:2.4;
  fill:none;opacity:.25;transition:opacity .2s var(--ease),transform .2s var(--ease)}
th[aria-sort] .st-sort{color:var(--ink)}
th[aria-sort] .arrow{opacity:1}
th[aria-sort="descending"] .arrow{transform:rotate(180deg)}

/* ---- cells ---- */
.st-pillar{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;font-weight:600;
  border-radius:var(--r-pill);padding:4px 11px;white-space:nowrap}
.st-pillar::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor;flex:0 0 auto}
tr[data-pillar="Customer"] .st-pillar{color:var(--st-customer);background:var(--st-customer-wash)}
tr[data-pillar="Product"]  .st-pillar{color:var(--st-product);background:var(--st-product-wash)}
tr[data-pillar="Business"] .st-pillar{color:var(--st-business);background:var(--st-business-wash)}
.st-system{display:inline-block;font-size:12.5px;font-weight:500;color:var(--ink-2);
  background:var(--shell);border-radius:8px;padding:4px 10px}
.st-proc{font-weight:500;color:var(--ink)}
.st-owner{color:var(--ink-2);white-space:nowrap}
.st-date{color:var(--ink-3);font-variant-numeric:tabular-nums;white-space:nowrap}
/* Completion carries the WORD as well as the tick — never icon or colour alone. */
.st-done{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;
  color:var(--st-business);white-space:nowrap}
.st-done svg{flex:0 0 auto;width:14px;height:14px;stroke:currentColor;stroke-width:3;fill:none}
/* The source cells hyperlink into MLG's own Drive. Those URLs are not public, so these are
   reference chips and NOT anchors — a link that goes nowhere is worse than no link. */
.st-ref{display:inline-flex;align-items:baseline;gap:8px;font-size:13px;color:var(--ink-2)}
.st-ref .num{flex:0 0 auto;font-weight:700;font-variant-numeric:tabular-nums;
  color:var(--ink);background:var(--shell);border-radius:6px;padding:2px 7px;font-size:12px}

/* ---- empty + footer ---- */
.st-empty{padding:40px clamp(16px,2vw,22px);text-align:center;color:var(--ink-3);font-size:14.5px}
.st-empty[hidden]{display:none}
.st-foot{display:flex;flex-wrap:wrap;gap:10px 18px;justify-content:space-between;
  align-items:baseline;margin-top:14px;font-size:12.5px;color:var(--ink-3)}
.st-count{font-weight:600;color:var(--ink-2)}
.st-note{max-width:70ch;line-height:1.6}
.st-sortfield{display:none;margin-bottom:14px}
.st-sortbar{display:flex;flex-wrap:wrap;gap:8px}
:where(.soptable) :focus-visible{outline:3px solid var(--ink);outline-offset:-3px;border-radius:6px}

/* ---- on .s-teal, where this component actually lives ----
   The card is its own light surface so everything inside it keeps the ink palette. But the
   controls, the count and the note sit directly on --teal-surface, where ink text measures
   1.7-1.9:1. This is the two-channel accent rule: on a dark section, text on the section
   ground takes the sand channel. Measured after: label/count/note 5.56, chips 5.56, active
   chip 8.86. Miss this and the footnote is invisible while every automated check that only
   samples the card passes. */
.s-teal .st-label,.s-teal .st-count,.s-teal .st-note{color:var(--sand-2)}
/* Full --sand, not --sand-2: over the chip's own .10 wash the .82 alpha lands on exactly
   4.50 against the teal, which is the threshold rather than clear of it. */
.s-teal .st-chip{background:rgba(248,244,227,.10);border-color:rgba(248,244,227,.52);
  color:var(--sand)}
/* The count pill goes DARKER on teal, not lighter. A white wash inside the chip's own
   white wash lifted the ground enough to drop sand text to 3.79:1; recessing it instead
   keeps the badge shape and takes the text well clear. */
.s-teal .st-chip .n{background:rgba(20,42,41,.42);color:var(--sand)}
.s-teal .st-chip:hover{border-color:rgba(248,244,227,.74)}
.s-teal .st-chip[aria-pressed="true"]{background:var(--sand);border-color:var(--sand);color:var(--ink)}
.s-teal .st-chip[aria-pressed="true"] .n{background:rgba(43,26,22,.12);color:var(--ink-2)}
/* Only the controls OUTSIDE the card get the sand ring; the sort buttons inside it are on
   white and keep the ink one. */
.s-teal .st-chip:focus-visible{outline-color:var(--sand)}

/* ---- under 760px the table becomes cards ---- */
@media (max-width:759px){
  .soptable thead{position:absolute;width:1px;height:1px;overflow:hidden;
    clip-path:inset(50%);white-space:nowrap}
  .soptable table,.soptable tbody,.soptable tr,.soptable td{display:block;width:100%}
  .soptable table{min-width:0}
  /* A <caption> inside a display:block table is sized to MIN-CONTENT by the engine, which
     wrapped this sentence at about ten characters. And <colgroup> percentages mean nothing
     once the table is not a table. Both must be switched off explicitly. */
  .soptable caption{display:block;padding:16px clamp(14px,4vw,18px) 0}
  .soptable colgroup,.soptable col{display:none}
  .st-scroll{overflow-x:visible;touch-action:auto}
  .st-card::after{display:none}
  /* Six short fields sit 2-up rather than stacking, taking a card from ~430px to ~350px.
     The procedure name spans the row and is pulled up as the card heading. auto-fit lets a
     very narrow column collapse to one rather than overflow. */
  .soptable tbody tr{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));
    gap:12px 14px;border-bottom:1px solid var(--st-line);padding:16px clamp(14px,4vw,18px)}
  .soptable tbody tr:last-child{border-bottom:0}
  .soptable td{border-bottom:0;padding:0;margin-bottom:0;min-width:0;overflow-wrap:anywhere}
  .st-cell-proc{grid-column:1/-1;order:-1}
  .soptable td[data-l]::before{content:attr(data-l);display:block;
    font-size:10.5px;font-weight:600;letter-spacing:.11em;text-transform:uppercase;
    color:var(--ink-3);margin-bottom:4px}
  .st-cell-proc::before{display:none!important}
  .st-proc{font-size:15.5px;font-weight:600}
  /* nowrap is right in a table cell and wrong in a 112px card column. */
  .soptable td .st-owner,.soptable td .st-date,.soptable td .st-done,
  .soptable td .st-pillar,.soptable td .st-ref{white-space:normal}
  .soptable td .st-ref{align-items:center;flex-wrap:wrap;gap:6px}
  .st-sortfield{display:block}
}
@media (prefers-reduced-motion:reduce){
  .soptable *{transition:none!important}
}

/* Trust row — grayscale marks, per the template, so no partner colour competes with
   OWL's accents. Colour returns on hover as a small reward. */
.cs-trust{margin-top:clamp(28px,3.6vw,46px)}
.cs-trust .label{display:block;margin-bottom:clamp(14px,1.6vw,20px)}
/* The template draws a distinction the first pass missed: PARTNER logos go grayscale so
   they cannot compete with the accent, but INTEGRATION logos sit in white circles and keep
   their literal brand colour — "the only place external color enters the system". These
   are integrations, so they get circles. It also solves a real problem: the marks are
   wildly different shapes and weights, and a circle normalises all of them. */
.cs-logos{display:flex;flex-wrap:wrap;align-items:center;gap:clamp(12px,1.6vw,20px)}
.tchip{display:inline-flex;align-items:center;gap:11px;
  background:var(--tile,#fffdf6);border-radius:var(--r-pill);
  padding:7px 16px 7px 7px;box-shadow:var(--shadow-card);
  font-size:clamp(12.5px,1.3vw,14.5px);font-weight:500;letter-spacing:.01em;
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.tchip:hover{transform:translateY(-2px);box-shadow:var(--shadow-card-hi)}
.tdot{flex:0 0 auto;width:34px;height:34px;border-radius:50%;background:#fff;
  display:grid;place-items:center;padding:6px;box-shadow:0 1px 3px rgba(43,26,22,.10)}
.tdot img{display:block;width:auto;height:auto;max-width:100%;max-height:100%}
/* Chips for tools OWL holds no mark for. Same pill, no logo well — mixing a pill with a
   circle would read as two components; a pill with and without an icon reads as one. */
.tchip.nodot{padding:11px 17px}
/* A chip is a near-white tile. On a dark section its label was inheriting the section's
   light ink and measured 1.08:1 — light text on a light tile. Any light surface inside a
   dark band has to reset its own ink, the same trap the Further case studies cards hit. */
.s-teal .tchip,.s-mahogany .tchip,.s-sienna .tchip,.s-terracotta .tchip{color:var(--ink)}
/* Compact variant for the broad ecosystem row — circles only, no labels. */
.cs-logos.dots-only{gap:clamp(10px,1.4vw,16px)}
.cs-logos.dots-only .tdot{width:clamp(40px,4.4vw,52px);height:clamp(40px,4.4vw,52px);padding:9px;
  box-shadow:var(--shadow-card);transition:transform .3s var(--ease)}
.cs-logos.dots-only .tdot:hover{transform:translateY(-3px)}

/* --- Workflow diagram: how the tools produce design, systems and impact -------------
   AUTHORED, not from the template — the template set defines no motion at all. The
   connectors march a dashed stroke so the diagram reads as flow rather than a static
   org chart, and every node fades up on the site's existing reveal timing. */
.wf{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,.86fr);
  gap:clamp(18px,3vw,44px);align-items:center;position:relative;
  margin-top:clamp(26px,3.4vw,44px)}
.wf-col{display:flex;flex-direction:column;gap:clamp(12px,1.6vw,18px);min-width:0}
.wf-node{background:var(--tile,#fffdf6);border-radius:var(--r-card);
  padding:clamp(15px,1.8vw,22px);box-shadow:var(--shadow-card);
  border-top:3px solid var(--accent,var(--olive-ink));
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.wf-node:hover{transform:translateY(-3px);box-shadow:var(--shadow-card-hi)}
.wf-node .num{display:block;font-size:clamp(10.5px,1.05vw,12.5px);font-weight:600;
  letter-spacing:.13em;text-transform:uppercase;color:var(--accent,var(--olive-ink))}
.wf-node b{display:block;margin-top:4px;font-size:clamp(16px,1.8vw,21px);
  font-weight:600;letter-spacing:-.018em}
.wf-node span{display:block;margin-top:5px;font-size:clamp(12.5px,1.3vw,14.5px);
  line-height:1.45;color:var(--ink-2,rgba(43,26,22,.72))}
.wf-tools{display:flex;flex-wrap:wrap;gap:8px;margin-top:11px}
.wf-tools .tdot{width:30px;height:30px;padding:5px;box-shadow:0 1px 3px rgba(43,26,22,.12)}
/* Placeholder well for the two tools OWL holds no mark for. A lettered token keeps the row
   a uniform set of circles rather than leaving two holes in it. */
.tdot.nomark{font-size:10px;font-weight:700;letter-spacing:.01em;color:var(--ink-2,rgba(43,26,22,.72));
  background:var(--shell,#edebe3);text-transform:none;padding:0}
.wf-out{align-self:center}
.wf-line{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0}
.wf-line path{fill:none;stroke:var(--sienna-ink);stroke-width:2;opacity:.5;
  stroke-linecap:round;stroke-dasharray:2 7;animation:wf-march 2.6s linear infinite}
@keyframes wf-march{to{stroke-dashoffset:-32}}
.wf-col,.wf-out{position:relative;z-index:1}

@media (max-width:900px){
  .wf{grid-template-columns:minmax(0,1fr);gap:clamp(14px,2.4vw,20px)}
  .wf-line{display:none}
  .wf-out{align-self:stretch}
}
@media (prefers-reduced-motion:reduce){
  .wf-line path{animation:none}
  .tchip,.wf-node,.cs-logos.dots-only .tdot{transition:none}
}



/* Device mockups are transparent cut-outs, so they get no card chrome — a white plate
   behind a laptop that already has its own bezel reads as a screenshot of a screenshot. */
.device-shot{margin:0;line-height:0}
.device-shot img{display:block;width:100%;height:auto}

/* The phone mockup takes the hands treatment: full-bleed, mobile only. Same reasoning —
   it is a cut-out whose subject runs to the artwork edge, so it belongs edge to edge, and
   it is painted as a background inside the media query so desktop never fetches it. */
@media (max-width:759px){
}

/* MLG's own site background, as a full-bleed band. Uses the site's existing .bleed idiom
   rather than a new component. */
/* The band is a full-bleed sibling between two sections, so it owns the gap beneath it —
   the following section carries padding-top:0 and the eyebrow was sitting hard against the
   photo edge. */
.cs-band{padding:0;width:100%;line-height:0;background:var(--deep);
  margin-bottom:var(--sec)}
.cs-band img{display:block;width:100%;height:auto;aspect-ratio:21/9;object-fit:cover;
  object-position:center 62%}

/* The wider stack. These are name pills, not logos: the list runs to ~18 tools and OWL
   does not hold marks for most of them, so a uniform pill is honest where a half-logo,
   half-text row would look broken. The four MLG actually ran on keep their logo circles
   in the hero, where the distinction matters. */
.stackpills{display:flex;flex-wrap:wrap;gap:clamp(7px,.9vw,10px)}
.stackpills span{display:inline-block;background:var(--tile,#fffdf6);border-radius:var(--r-pill);
  padding:8px 15px;font-size:clamp(12.5px,1.25vw,14px);font-weight:500;letter-spacing:.01em;
  box-shadow:var(--shadow-card);color:var(--ink-2,rgba(43,26,22,.72));
  transition:transform .25s var(--ease),box-shadow .25s var(--ease),color .25s var(--ease)}
.stackpills span:hover{transform:translateY(-2px);box-shadow:var(--shadow-card-hi);color:var(--ink)}

/* Embedded flipbook (the MLG pitch deck). Given a real aspect rather than the vendor's
   fixed 264px, which is unreadable on a wide screen. */
.flipbook{margin:0;background:none;box-shadow:none;border-radius:0}
.fp-link{display:inline-block;line-height:0;border-radius:4px}
.fp-link:focus-visible{outline:2px solid var(--accent,var(--olive-ink));outline-offset:4px}
.fp-thumb{display:block;width:250px;max-width:100%;height:auto;
  border:1px solid lightgray;box-shadow:lightgray 0 0 4px 1px}
.fp-link:hover .fp-thumb{box-shadow:lightgray 0 0 8px 2px}

/* 2-column text + screenshot block — the template's alternating feature grid. */
.cs-split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(26px,4vw,56px);align-items:center}
/* Screenshot-weighted variant. An even split renders the SOP tracker too small to read
   as anything but a texture; the template treats product UI as proof, so it gets the room. */
.cs-split.wide-shot{grid-template-columns:minmax(0,.72fr) minmax(0,1.28fr)}
.cs-split.flip > *:first-child{order:2}

@media (max-width:820px){
  /* .cs-proof deliberately absent: its own collapse lives at 760px with the phone cap.
     Listing it here too left 761-820px in one column with no cap, rendering the phone
     mockup 685px wide. */
  .cs-split{grid-template-columns:minmax(0,1fr)}
  .cs-split.flip > *:first-child{order:0}
  .shotcard.tilt-l,.shotcard.tilt-r{transform:none}
}
@media (prefers-reduced-motion:reduce){
  .cs-wash{animation:none}
  .shotcard,.cs-logos img{transition:none}
  .shotcard.tilt-l,.shotcard.tilt-r{transform:none}
}

/* Resident-journey medallions. The artwork is already circular with a palette ring baked
   in, so the figure adds no frame of its own. */
/* The roadmap artwork carries its phase labels baked in at pure white. With the Impact
   block now on cream those labels measure 1.10:1 — invisible — so the figure keeps its own
   terracotta plate, where they reach 4.72:1. The plate is only painted where the artwork is
   (below 760px); above that the figure is not rendered at all. */
/* The roadmap's phase labels are baked in at pure white, so they need a terracotta ground
   (4.72:1; on cream they are 1.10:1 — invisible). Rather than a rounded plate hugging the
   artwork, the terracotta now runs as a square-cornered band from the very top of the
   Impact section down past the roadmap, so the eyebrow, heading and lead sit on it too.
   Mobile only, because that is the only place the artwork renders. */
.impact-top{background:transparent}
.impact.s-cream{padding-top:0}
.impact-top > .wrap{padding-top:var(--sec)}
@media (max-width:759px){
  .impact-top{background:#b45a32}
  /* The tail goes on the BAND, not on the road. The road sets aspect-ratio, and with
     border-box sizing a padding-bottom there shrinks the background area instead of adding
     space below it — the artwork just re-centres and the gap barely moves. */
  .impact-top{padding-bottom:clamp(88px,20vw,132px)}
  .impact-top .label,.impact-top h2,.impact-top .lead{color:#fff}
}
@media (min-width:760px){
  /* No band above 760px — the artwork is not rendered there, so nothing needs the ground. */
  .impact-top > .wrap{padding-bottom:0}
}

/* Slidable trust band. Native horizontal scrolling with snap, plus pointer-drag, so it works
   by swipe, trackpad, drag, keyboard and scrollbar. overflow-x lives on this element only —
   the page itself must never gain a horizontal scrollbar. */
.tband{position:relative;margin-top:clamp(14px,1.8vw,22px)}
/* Edge fade so the strip reads as continuing rather than ending. Sits above the circles
   but must never eat pointer events, or the drag handler loses its target. */
.tband::after{content:"";position:absolute;right:0;top:0;bottom:18px;width:clamp(28px,4vw,56px);
  pointer-events:none;z-index:2;opacity:1;transition:opacity .3s var(--ease);
  background:linear-gradient(to right,rgba(248,244,227,0),var(--sand))}
/* The fade means "there is more to the right". At the end of the scroll there is not, and
   leaving it up washed out 26-54px of the final circle — the last mark dissolving into the
   background, which reads as empty space rather than as the end of the row. */
.tband.at-end::after{opacity:0}
.s-terracotta .tband::after{background:linear-gradient(to right,rgba(180,90,50,0),#b45a32)}
/* Sized so the strip genuinely overflows at every width — at the previous 76px the ten
   circles fitted exactly on a 1280 screen and the band silently stopped being slidable. */
.tband-scroll{display:flex;gap:clamp(26px,3.4vw,54px);align-items:flex-end;
  overflow-x:auto;overscroll-behavior-x:contain;touch-action:pan-x;
  padding:6px 2px 16px;scrollbar-width:thin;
  scrollbar-color:rgba(43,26,22,.28) transparent;cursor:grab}
.tband-scroll.is-drag{cursor:grabbing;scroll-behavior:auto}
.tband-scroll::-webkit-scrollbar{height:6px}
.tband-scroll::-webkit-scrollbar-thumb{background:rgba(43,26,22,.24);border-radius:99px}
.tband-scroll::-webkit-scrollbar-track{background:transparent}
/* Every mark sits in the same inner box with generous padding, so a wide logo and a square
   one read at the same optical weight and none of them touches the circle edge. The source
   crops differ (each was trimmed to its own alpha bounds), so without a shared box Zoom and
   Airtable rendered noticeably larger than Gemini. overflow:hidden is belt-and-braces
   against a mark that scales past the circle. */
.tball{flex:0 0 auto;display:grid;place-items:center;overflow:hidden;
  width:clamp(60px,6.6vw,84px);height:clamp(60px,6.6vw,84px);border-radius:50%;
  background:#fff;padding:clamp(15px,1.7vw,22px);box-shadow:var(--shadow-card);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
/* Explicit caps, not percentages. max-height:100% on a grid item did not resolve against
   the padding box here — logos rendered up to 48px tall inside a 40px inner box — which is
   the same percentage-resolution trap the tool plates hit. A hard clamp on both axes makes
   every mark share one bounding box regardless of its aspect. */
.tball img{display:block;width:auto;height:auto;object-fit:contain;
  max-width:clamp(28px,3.1vw,40px);max-height:clamp(28px,3.1vw,40px)}
.tball:hover{transform:translateY(-4px);box-shadow:var(--shadow-card-hi)}
.tmore{flex:0 0 auto;display:grid;place-items:center;text-align:center;
  width:clamp(60px,6.6vw,84px);height:clamp(60px,6.6vw,84px);border-radius:50%;
  background:transparent;border:1.5px dashed var(--accent,var(--sienna-ink));
  font-size:clamp(10.5px,1.05vw,12px);font-weight:600;line-height:1.2;letter-spacing:.02em;
  color:var(--accent,var(--sienna-ink));padding:8px}
.tband-hint{margin-top:2px;line-height:1.5}
/* Functional grouping. Each group is one flex child: a label above its own row of marks,
   so the eye reads "what these do" before "which ones". */
.tgroup{flex:0 0 auto;display:flex;flex-direction:column;gap:clamp(8px,1vw,12px)}
.tglabel{font-size:clamp(10px,1vw,11.5px);font-weight:600;letter-spacing:.13em;
  text-transform:uppercase;color:var(--ink-3);white-space:nowrap}
.tgrow{display:flex;gap:clamp(12px,1.5vw,20px);align-items:center}
.s-terracotta .tglabel,.s-teal .tglabel,.s-mahogany .tglabel{color:var(--sand-2)}
.tmore{align-self:flex-end}
.s-terracotta .tball{box-shadow:0 2px 10px rgba(0,0,0,.18)}
.s-terracotta .tmore{border-color:rgba(255,255,255,.8);color:#fff}
@media (prefers-reduced-motion:reduce){
  .tband-scroll{scroll-behavior:auto}
  .tball{transition:none}
}

/* Further case studies now sits on terracotta, but its rows are near-white tiles — a light
   surface inside a dark section. The a-* classes resolve to their LIGHT variants outside
   .s-cream, which is right on a dark ground and wrong on a light card sitting on one: the
   "Open case study" link measured 1.64:1 in light teal on the tile. Inside these cards the
   accents are put back to their -ink variants. */
/* The rows are light tiles, so their text must not inherit the section's white. Without
   this the card titles were white on a near-white tile — invisible. */
.s-terracotta .csrows .prow{color:var(--ink)}
.s-terracotta .csrows .prow .pq span{color:var(--ink-2,rgba(43,26,22,.72))}
/* The Impact outcome cards dropped their 01/02/03 numerals — they collided with the
   section's own "03 · Impact" and with the roadmap's 1-5 and the journey's Stage 01-04.
   Three numbering systems in one section is not a hierarchy, it is noise. The heading now
   carries the identity and the accent rule carries the grouping. */
.s-cream .g3 .card h3{margin-top:0}
.s-terracotta .csrows .prow.a-olive{--accent:var(--olive-ink)}
.s-terracotta .csrows .prow.a-teal{--accent:var(--teal-ink)}
.s-terracotta .csrows .prow.a-sienna{--accent:var(--sienna-ink)}

/* One line of description under a row's title, for the reduced case-study list.
   Mirrors .list li span so the two read as the same kind of thing. */
.prow .pq span{display:block;margin-top:5px;font-weight:400;
  font-size:clamp(13.5px,1.35vw,15.5px);line-height:1.5;color:var(--ink-2,rgba(43,26,22,.72))}

@media (max-width:900px){
  .phases{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:520px){
  .phases{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:620px){
  .tools{grid-template-columns:minmax(0,1fr)}
  /* The joined bar cannot hold four legible hex labels in a phone column, so it
     becomes a 2x2 of separate plates and gives up the expand-on-hover, which needs
     the flex row to grow into. */
  .swrow{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;
    border-radius:0;overflow:visible}
  .sw{border-radius:var(--r-img);height:clamp(68px,17vw,84px)}
  .swrow .sw:hover{flex-grow:0;transform:none}
}
@media (prefers-reduced-motion:reduce){
  .sw{transition:none}
  .swrow .sw:hover{flex-grow:1;transform:none}
}

/* --- 18b. Back to top ------------------------------------------------------
   Mirrors the header's Book a Consultation CTA: same --hdr-accent fill, same
   --hdr-btn-ink label, same --hdr-btn-hi on hover, so the control reads as part of
   that page's colorway. The variant class (h-sienna, h-mahogany, …) is repeated on
   this button because it lives at the end of <body>, outside the header that would
   otherwise have supplied the tokens by inheritance.

   The ring is not decoration. This button floats over whatever section has
   scrolled beneath it, and on some pages the accent matches one of those grounds
   exactly — teal on the index sienna band, mahogany on the founders footer — which
   left the boundary at 1.00:1 and failed WCAG 1.4.11. A ring in --hdr-btn-ink
   fixes that by construction: the ink is guaranteed >=4.5:1 against its own fill,
   so wherever the fill disappears into the ground, the ring does not. Measured
   worst case across every page/ground pair is 3.98:1.

   Focus is deliberately two-tone for the same reason. A single-colour outline
   cannot clear all five grounds (sand fails on cream, ink fails on deep), so the
   ink outline sits inside an accent halo and at least one of the two always
   contrasts. */
.totop{position:fixed;right:clamp(16px,4vw,32px);bottom:clamp(16px,4vw,32px);
  z-index:55;width:48px;height:48px;border-radius:50%;
  background:var(--hdr-accent,var(--mahogany));
  color:var(--hdr-btn-ink,var(--sand));
  border:2px solid var(--hdr-btn-ink,var(--sand));
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  box-shadow:0 6px 20px rgba(43,26,22,.28);
  opacity:0;visibility:hidden;transform:translateY(10px);
  transition:opacity .25s var(--ease),transform .25s var(--ease),
    visibility .25s,background .2s var(--ease)}
.totop.is-visible{opacity:1;visibility:visible;transform:none}
.totop.is-visible:hover{background:var(--hdr-btn-hi,var(--mahogany-hi));transform:translateY(-3px)}
.totop:focus-visible{outline:3px solid var(--hdr-btn-ink,var(--sand));outline-offset:3px;
  box-shadow:0 0 0 9px var(--hdr-accent,var(--mahogany)),0 6px 20px rgba(43,26,22,.28)}
.totop svg{width:20px;height:20px;stroke:currentColor;fill:none;
  stroke-width:2.3;stroke-linecap:round;stroke-linejoin:round}
@media (max-width:520px){
  .totop{width:44px;height:44px;right:14px;bottom:14px}
  .totop svg{width:18px;height:18px}
}

/* --- 19. Motion ----------------------------------------------------------
   The reveal is gated on .js, which a one-line inline script in <head> sets.
   Without JavaScript the rule never applies and every element is simply
   visible — a broken or blocked script can never leave a page blank. */
.js [data-anim]{opacity:0;transform:translateY(24px);
  transition:opacity .6s var(--ease),transform .7s var(--ease)}
.js [data-anim].is-in{opacity:1;transform:none}
[data-d="1"]{transition-delay:.08s} [data-d="2"]{transition-delay:.16s}
[data-d="3"]{transition-delay:.24s} [data-d="4"]{transition-delay:.32s}
[data-d="5"]{transition-delay:.40s}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{transition-duration:.01ms!important;animation-duration:.01ms!important}
  .js [data-anim]{opacity:1;transform:none}
  .card:hover,.btn:hover,.pill:hover img,.social a:hover,.acc-btn:hover,.totop.is-visible:hover{transform:none}
  .acc-btn:hover{padding-left:0}
}


/* ---------- Case study hero band ---------- */
/* One column at every width: the mission lockup first, the hero copy under it. The
   two-column pairing is gone — the copy now reads as a caption to the artwork rather than
   sitting beside it, which is what the stacked order asks for. */
.hero-cols{display:grid;grid-template-columns:minmax(0,1fr);
  gap:clamp(20px,2.6vw,32px);
  margin-top:clamp(16px,2vw,26px)}
.hero-cols .lead{max-width:56ch}
/* The name lockup moved up under the h1. .phero h1 leaves only 18px below itself, which is
   too tight against a block that carries its own rule line, so it gets its own clearance. */
.cs-hero-chrome{margin-top:clamp(22px,2.8vw,36px)}
/* The lockup renders at every width now: stacked under the lead on mobile, the right column
   from 760px. `contain` throughout — line art on transparency, so a crop would cut the
   roofline or the type. */
.heroband.hb-legacy{display:block;margin:0;height:clamp(170px,52vw,240px);
  background:url('/assets/case/mlg-legacy-line-s.db930241.webp') center center/contain no-repeat}

/* The phone now sits at the end of the Design panel, mobile only, at 90% width instead of
   full bleed. margin-left:auto keeps it running off the RIGHT edge; the background inside
   is still 105.263% and left-anchored, so the element box clips exactly the same 5% off the
   right as before — bleed and crop preserved, size reduced. */
@media (max-width:759px){
  .heroband.hb-phone{display:block;width:90%;margin-left:auto;margin-right:0;
    aspect-ratio:1.0714;
    background:url('/assets/case/shot-phone-hand.c0c8fa4d.webp') left center/105.263% auto no-repeat}
}
/* Backgrounds, not <img>: an image inside an unmatched media query is never requested,
   whereas a display:none <img> is still fetched. Only one of these two ever downloads. */
.heroband{display:none;margin:clamp(34px,4.4vw,64px) 0 0}
@media (min-width:760px){
  /* The mission lockup is the desktop hero. Line art on transparency, so `contain` and
     never `cover` — a crop would cut the roofline or the type. background-origin:content-box
     makes it start at the wrap gutter, so it lines up with the h1 and the lead above it. */
  .heroband.hb-legacy{height:clamp(230px,25vw,360px)}
}

/* ---------- Roadmap: wide on desktop, tall on mobile ---------- */
.roadwrap{position:relative}
.road-tip{display:none}
@media (min-width:760px){
  /* The artwork's five phase names are baked in at pure white, built for a dark ground.
     On cream they measure 1.10:1 — invisible. The mobile version solves this by sitting on
     the terracotta .impact-top band; the desktop one carries its own plate for the same
     reason, where the same labels reach 4.72:1. Contained to the wrap rather than bled,
     so it reads as a figure and not as a second section. */
  .roadwrap{background:#b45a32;border-radius:var(--r-img);
    max-width:var(--wrap);margin:clamp(30px,4vw,48px) auto 0;
    /* Right padding is not decorative: the arrowhead reaches 105.31% of the road box, so
       without room for that 5.31% it would be drawn outside the plate, onto the cream. */
    padding:clamp(16px,2.2vw,30px) clamp(72px,7vw,100px) clamp(16px,2.2vw,30px) clamp(16px,2.4vw,34px)}
  .road{display:block;margin:0;line-height:0;
    aspect-ratio:1900/869;
    background-position:center;background-size:contain;background-repeat:no-repeat}
  /* The image URL is scoped to an OPEN panel. These live inside the Impact panel,
     which ships collapsed, and a background in a matched rule is fetched whether or
     not its box is visible — 194KB downloaded on mobile for content nobody opened.
     A background in an unmatched selector is never requested. */
  .acc-panel[data-open="true"] .road{background-image:url('/assets/case/mlg-road-wide.29430091.webp')}
  /* Measured off the artwork: the road is 138px thick, centred at y 37.63%, and ends at
     x 98.26%. The arrowhead copies the mobile one's proportions exactly — base 1.993x the
     road thickness (31.65% of the box height), length 0.970x (7.05% of the box width) — and
     its colour is sampled from that same head, #151513. Centring uses `translate` so the
     animation can own `transform` outright. */
  .road{position:relative}
  .road-tip{display:block;position:absolute;left:98.26%;top:37.63%;width:7.05%;height:31.65%;
    translate:0 -50%;background:#151513;
    clip-path:polygon(0 0,100% 50%,0 100%);
    animation:road-go 2.6s var(--ease) infinite}
}
@keyframes road-go{
  0%,100%{transform:translateX(0);opacity:.9}
  50%    {transform:translateX(24%);opacity:1}
}

/* ---------- MLG mission line + the monarch ---------- */
/* The monarch alone, centred between the two intro paragraphs — so the space above and
   below is equal, not the old top-heavy pair. It is a link, so it carries a focus ring and
   a 44px minimum hit area even at the small end of its clamp. */
.legacy{display:flex;justify-content:center;
  margin-block:clamp(26px,3.4vw,44px)}
.legacy-fly{display:block;line-height:0;border-radius:12px;
  min-width:44px;min-height:44px;
  width:clamp(74px,8vw,116px)}
.legacy-fly img{display:block;width:100%;height:auto;
  transform-origin:60% 90%;
  animation:fly-drift 7s var(--ease) infinite alternate}
.legacy-fly:focus-visible{outline:2px solid var(--accent,var(--olive-ink));outline-offset:4px}
@keyframes fly-drift{
  0%   {transform:translate3d(0,0,0) rotate(0deg)}
  50%  {transform:translate3d(-10px,-16px,0) rotate(-5deg)}
  100% {transform:translate3d(6px,-6px,0) rotate(3deg)}
}
@media (prefers-reduced-motion:reduce){
  .road-tip{animation:none;opacity:1;transform:none}
  .legacy-fly img{animation:none}
  .legacy-fly{transform:none}
}