/* ════════════════════════════════════════════════════════════════════════
   McPherson Coatings — UI layer
   Everything the v5 handover pack does NOT provide. The handover CSS
   (mcc.css) ships 0 media queries, 0 keyframes and 0 transitions, so this
   file owns:

     1. motion tokens + the scroll-reveal system
     2. signature brand animations (trowel sweep, hairline draw, mask
        reveal, watermark drift, count-up, marquee)
     3. header chrome — sticky/shrink, scroll progress, mobile menu
     4. hover/focus states
     5. the entire responsive cascade
     6. prefers-reduced-motion + print

   Animation vocabulary matches the main McPherson Painting module
   (data-anim / data-delay) so the two codebases stay legible to the same
   developer, then extends it.
   ════════════════════════════════════════════════════════════════════════ */

:root{
  --ease:      cubic-bezier(.16,1,.3,1);      /* same curve as layouttwo/common.css */
  --ease-out:  cubic-bezier(.22,.61,.36,1);
  --ease-in:   cubic-bezier(.4,0,1,1);
  --ease-back: cubic-bezier(.34,1.56,.64,1);
  --t-fast:.28s; --t-med:.55s; --t-slow:.9s; --t-xslow:1.4s;
  --hdr-h:92px;
}

/* ─────────────────────────────────────────── 1. SCROLL REVEAL
   TWO RULES THIS SECTION MUST NEVER BREAK:

   1. Nothing is hidden unless mcc.js is confirmed running. The hidden state
      is scoped to `html.mcc-anim`, a class the script adds only once it has
      successfully installed the observer, and removes again if it throws.
      Without it every [data-anim] element renders normally. A CSS-only or
      broken-JS visit therefore shows the full page, never a blank one.

   2. No element gets `filter` and `clip-path` at the same time. That
      combination blanked every .fig-box and .devpanel in Chrome: the old
      `[data-anim].is-in` rule applied `filter:blur(0)` to everything,
      including the clip variant, so images occupied their space but painted
      nothing. `filter` is now scoped to the blur variant alone, and the
      image reveal uses opacity + scale instead of clip-path. */

[data-anim]{transition:opacity var(--t-slow) var(--ease),transform var(--t-slow) var(--ease)}

.mcc-anim [data-anim]:not(.is-in){opacity:0}
/* 34px, not the 52px the painting module uses. `transform` does not affect
   layout, so a large offset paints an element well outside its own box —
   at 52px a mono eyebrow label landed on top of the <h1> directly beneath it
   for the length of the transition. Small labels travel less again. */
.mcc-anim [data-anim="up"]:not(.is-in)    {transform:translate3d(0,34px,0)}
.mcc-anim .lbl[data-anim="up"]:not(.is-in),
.mcc-anim .credit[data-anim="up"]:not(.is-in),
.mcc-anim .meta[data-anim="up"]:not(.is-in){transform:translate3d(0,14px,0)}
.mcc-anim [data-anim="down"]:not(.is-in)  {transform:translate3d(0,-40px,0)}
.mcc-anim [data-anim="left"]:not(.is-in)  {transform:translate3d(-64px,0,0)}
.mcc-anim [data-anim="right"]:not(.is-in) {transform:translate3d(64px,0,0)}
.mcc-anim [data-anim="scale"]:not(.is-in) {transform:translate3d(0,28px,0) scale3d(.94,.94,1)}
.mcc-anim [data-anim="fade"]:not(.is-in)  {transform:translate3d(0,12px,0)}
/* blur is the ONLY variant allowed to use filter */
.mcc-anim [data-anim="blur"]:not(.is-in)  {transform:translate3d(0,22px,0);filter:blur(9px)}
[data-anim="blur"]{transition:opacity var(--t-slow) var(--ease),transform var(--t-slow) var(--ease),filter var(--t-slow) var(--ease)}
[data-anim="blur"].is-in{filter:blur(0)}
/* clip: the finish "goes on" — reads as laid-on depth, done with scale so it
   never needs clip-path. Paired with the .fig-box img scale-down and the
   trowel sweep below. */
.mcc-anim [data-anim="clip"]:not(.is-in),
.mcc-anim [data-anim="clip-l"]:not(.is-in){transform:translate3d(0,26px,0) scale3d(1.015,1.015,1)}
[data-anim="clip"],[data-anim="clip-l"]{transition:opacity 1.15s var(--ease),transform 1.15s var(--ease)}

[data-anim].is-in{opacity:1;transform:translate3d(0,0,0) scale3d(1,1,1)}

[data-delay="1"]{transition-delay:.09s}[data-delay="2"]{transition-delay:.18s}
[data-delay="3"]{transition-delay:.27s}[data-delay="4"]{transition-delay:.36s}
[data-delay="5"]{transition-delay:.45s}[data-delay="6"]{transition-delay:.54s}
[data-delay="7"]{transition-delay:.63s}[data-delay="8"]{transition-delay:.72s}
/* JS-assigned stagger (data-stagger on the parent) */
[style*="--sd"]{transition-delay:calc(var(--sd) * 70ms)}

/* ─────────────────────────────────────────── 2. SIGNATURE ANIMATIONS */

/* 2.1 hairline in .seclab draws out to the gutter.
   Every "hidden until revealed" state below is scoped to .mcc-anim for the
   same reason as section 1: without the script, it must render finished. */
.seclab .ln{transform-origin:left center;transition:transform 1.1s var(--ease) .1s}
.mcc-anim .seclab:not(.is-in) .ln{transform:scaleX(0)}
.seclab.is-in .ln,.is-in .seclab .ln{transform:scaleX(1)}

/* 2.2 rules / steps / kv / faq rows — hairline sweep then content */
.rules>div,.step,.kv>div,.faq>div{position:relative}
.rules>div::before,.step::before,.faq>div::before{content:"";position:absolute;left:0;right:0;height:1px;
  background:var(--orange);transform:scaleX(0);transform-origin:left center;
  transition:transform .85s var(--ease)}
.rules>div::before,.faq>div::before{bottom:-1px}
.step::before{top:-1px}
.rules>div.is-in::before,.step.is-in::before,.faq>div.is-in::before{transform:scaleX(1)}

/* 2.3 trowel sweep — a burnish highlight crosses the image once on reveal */
/* NB: .swatch::after and .ph::after already carry the handover's caption text,
   so the sweep uses .fig-box::after and .hero-img::before only - never those two. */
.fig-box,.mask-box,.hero-img,.swatch,.ph{position:relative;overflow:hidden}
.fig-box::after,.hero-img::before{content:"";position:absolute;inset:0;z-index:3;
  background:linear-gradient(104deg,transparent 38%,rgba(255,255,255,.30) 50%,transparent 62%);
  transform:translate3d(-120%,0,0);pointer-events:none}
.fig-box.is-in::after,.hero-img.is-in::before{animation:mccSweep 1.5s var(--ease) .25s 1 both}
@keyframes mccSweep{0%{transform:translate3d(-120%,0,0)}100%{transform:translate3d(120%,0,0)}}

/* 2.4 images settle from a slow scale — reads as depth, not motion */
.fig-box img,.proj .fig-box img{transition:transform 1.6s var(--ease)}
.mcc-anim .fig-box:not(.is-in) img{transform:scale(1.07)}
.fig-box.is-in img{transform:scale(1)}

/* 2.5 hero — headline rises word by word, photo pulls back */
/* Hero headline words.
   Driven by a CSS animation with fill-mode `both`, NOT by a JS-toggled
   class: mcc.js only splits the text and sets each word's animation-delay.
   That matters because a transition needs something to flip it, and if that
   callback never fires (throttled frame loop, headless capture, background
   tab) the heading would stay invisible. An animation runs and settles on
   its own, so the words always end up on screen. */
@keyframes mccWordIn{from{opacity:0;transform:translate3d(0,.7em,0)}to{opacity:1;transform:none}}
.hero-copy .w{display:inline-block}
.mcc-anim .hero-copy .w{animation:mccWordIn .8s var(--ease) both}
.hero-img img{animation:mccHeroZoom 14s var(--ease-out) both}
@keyframes mccHeroZoom{from{transform:scale(1.14)}to{transform:scale(1)}}
.hero-credit{opacity:0;animation:mccFadeIn .9s var(--ease) 1.1s both}

/* 2.6 brandmark watermark drifts on scroll (JS sets --p 0→1) */
.wmark{transform:translateY(calc(-50% + (var(--p,0) * 42px))) rotate(calc(var(--p,0) * 2.5deg));
  transition:transform .2s linear}

/* 2.7 count-up numerals */
.cred b,.datum .v,.stat-row .v{font-variant-numeric:tabular-nums}
.cred b{transition:opacity .7s var(--ease),transform .7s var(--ease)}
.cred span{transition:opacity .7s var(--ease) .18s}
.mcc-anim .cred:not(.is-in) b{opacity:0;transform:translate3d(0,18px,0)}
.mcc-anim .cred:not(.is-in) span{opacity:0}
.cred.is-in b{opacity:1;transform:none}
.cred.is-in span{opacity:1}
/* the vertical divider grows */
.cred{position:relative}
.cred::after{content:"";position:absolute;right:-1px;top:50%;width:1px;height:0;background:var(--orange);
  transform:translateY(-50%);transition:height .9s var(--ease) .2s}
.cred.is-in:not(:last-child)::after{height:56%}

/* 2.8 brandmark mask box — the mark scales in over the photo.
   Default is the FINISHED state (84%), so a no-JS visit shows the masked
   image rather than nothing. Only .mcc-anim starts it at 0%. */
.mask-box img{transition:-webkit-mask-size 1.5s var(--ease),mask-size 1.5s var(--ease),transform 1.5s var(--ease);
  -webkit-mask-size:84% auto;mask-size:84% auto}
.mcc-anim .mask-box:not(.is-in) img{-webkit-mask-size:0% auto;mask-size:0% auto;transform:scale(1.08)}
.mask-box.is-in img{-webkit-mask-size:84% auto;mask-size:84% auto;transform:scale(1)}

/* 2.9 logo strip marquee (only when it overflows; JS adds .run) */
.logos.run{flex-wrap:nowrap;overflow:hidden;mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent)}
.logos.run .track{display:flex;gap:56px;align-items:center;animation:mccMarquee 26s linear infinite}
.logos.run:hover .track{animation-play-state:paused}
@keyframes mccMarquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* 2.10 tiles — stagger in, then lift */
.tile{transition:transform var(--t-med) var(--ease),box-shadow var(--t-med) var(--ease),
  border-color var(--t-med) var(--ease),background var(--t-med) var(--ease)}
.tile:hover{transform:translateY(-6px);box-shadow:0 18px 46px -18px rgba(35,30,19,.30);border-color:#B4AEA2}
.dark .tile:hover{border-color:var(--on-dark-2);box-shadow:0 18px 46px -18px rgba(0,0,0,.6)}
.tile.on:hover{box-shadow:0 20px 50px -18px rgba(240,75,35,.55)}
.tile .arrow{transition:transform var(--t-fast) var(--ease),letter-spacing var(--t-fast) var(--ease)}
.tile:hover .arrow{transform:translateX(6px)}
/* the featured (orange) tile breathes once in view */
.tile.on.is-in{animation:mccBreathe 3.6s var(--ease-out) .5s 1 both}
@keyframes mccBreathe{0%,100%{box-shadow:0 0 0 0 rgba(240,75,35,0)}
  45%{box-shadow:0 0 0 14px rgba(240,75,35,.10)}}

/* 2.11 buttons */
.btn{position:relative;overflow:hidden;transition:transform var(--t-fast) var(--ease),
  background var(--t-fast) var(--ease),color var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease)}
.btn::before{content:"";position:absolute;inset:0;background:currentColor;opacity:0;
  transform:translate3d(0,101%,0);transition:transform var(--t-med) var(--ease),opacity var(--t-med) var(--ease)}
.btn:hover{transform:translateY(-2px)}
.btn-s:hover{border-color:var(--orange);color:var(--orange)}
.btn-d:hover{border-color:var(--on-dark);color:#fff}
.btn-w:hover{border-color:var(--on-warm);color:#fff}
.cta .btn:active{transform:translateY(0)}
.arrow{transition:transform var(--t-fast) var(--ease)}
a:hover>.arrow,.arrow:hover{transform:translateX(5px)}

/* 2.12 links + nav underline */
.nav a{position:relative;transition:color var(--t-fast) var(--ease)}
.nav a::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:1px;background:var(--orange-dark);
  transform:scaleX(0);transform-origin:left center;transition:transform var(--t-med) var(--ease)}
.nav a:hover{color:var(--on-dark)}
.nav a:hover::after,.nav a.on::after{transform:scaleX(1)}
.ftr li{transition:color var(--t-fast),transform var(--t-fast) var(--ease)}
.ftr li:hover{color:var(--on-dark)}
.ftr a:hover li{transform:translateX(3px)}
.dl a{transition:padding-left var(--t-med) var(--ease),border-color var(--t-fast)}
.dl a:hover{padding-left:10px;border-bottom-color:var(--orange)}
.svc{transition:background var(--t-med) var(--ease)}
.svc:hover{background:rgba(240,75,35,.03)}
.svc:hover .arrow{transform:translateX(6px)}
/* chips are <label> + checkbox so they work with JS off. :has() gives the
   selected state natively; mcc.js also toggles .on for older engines. */
/* No indicator dot: the handover's chip is a plain pill and the orange fill
   already reads unambiguously as selected. A ::before circle was added here
   and has been removed — it read as a radio affordance even on the
   multi-select capability chips, and it is not the designer's chip. */
.chip{transition:all var(--t-fast) var(--ease);cursor:pointer;user-select:none;
  display:inline-flex;align-items:center}
.chip input[type=checkbox],.chip input[type=radio]{position:absolute;opacity:0;width:0;height:0;margin:0}
.chip:hover{border-color:var(--orange);color:var(--orange)}
.chip:has(input:checked){border-color:var(--orange);color:#fff;background:var(--orange)}
.chip.on:hover,.chip:has(input:checked):hover{color:#fff}
.chip:has(input:focus-visible){outline:2px solid var(--orange);outline-offset:3px}
.badge{transition:transform var(--t-med) var(--ease),border-color var(--t-fast)}
.badge:hover{transform:translateY(-3px);border-color:var(--orange)}
.proj{transition:transform var(--t-med) var(--ease)}
.proj:hover{transform:translateY(-5px)}
.swatch{transition:transform var(--t-med) var(--ease),border-radius var(--t-med) var(--ease)}
.swatch:hover{transform:scale(1.03)}

@keyframes mccFadeIn{from{opacity:0}to{opacity:1}}

/* ─────────────────────────────────────────── 2.13 BRANDED DEVICE PANEL
   Used by parts/device.phtml wherever McPherson's own photography is still to
   be shot. Brand guide p.13 sanctions the brandmark as a graphic device, so a
   pending shot reads as a designed panel instead of a broken grey image. */
.devpanel{position:relative;overflow:hidden;border-radius:var(--r-lg);display:grid;place-items:center;
  isolation:isolate}
.devpanel.dev-flat{border-radius:0}
.dev-brown {background:linear-gradient(152deg,#2C2619 0%,var(--brown) 52%,var(--brown-deep) 100%)}
.dev-warm  {background:linear-gradient(152deg,var(--warm-lift) 0%,var(--warm) 60%,#4F4A40 100%)}
.dev-orange{background:linear-gradient(152deg,#F46A47 0%,var(--orange) 55%,#C93C18 100%)}
.dev-light {background:linear-gradient(152deg,#E6E4E1 0%,var(--light) 55%,#C3C0BB 100%)}
/* a faint trowel grain so the panel has surface, like the finishes do */
.devpanel::before{content:"";position:absolute;inset:0;z-index:0;opacity:.5;
  background:repeating-linear-gradient(112deg,rgba(255,255,255,.05) 0 3px,transparent 3px 9px),
             radial-gradient(120% 90% at 26% 8%,rgba(255,255,255,.14),rgba(255,255,255,0) 62%)}
.dev-light::before{background:repeating-linear-gradient(112deg,rgba(35,30,19,.04) 0 3px,transparent 3px 9px)}
.dev-mark{position:relative;z-index:1;display:block;width:100%;text-align:center;opacity:.42;
  transition:transform 1.5s var(--ease),opacity 1.5s var(--ease)}
.dev-mark svg{display:inline-block;height:auto}
.mcc-anim .devpanel:not(.is-in) .dev-mark{opacity:.30;transform:scale(.94)}
.devpanel.is-in .dev-mark{transform:scale(1);opacity:.42}
.dev-cap{position:absolute;left:18px;right:18px;bottom:14px;z-index:2;font-family:var(--mono);
  font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;line-height:1.6;color:rgba(237,234,228,.72)}
.dev-light .dev-cap{color:rgba(35,30,19,.6)}
.dev-cap i{display:block;font-style:normal;color:var(--orange-dark);margin-top:3px}
.dev-light .dev-cap i,.dev-orange .dev-cap i{color:rgba(255,255,255,.85)}

/* the handover's own .ph placeholder, made to read as deliberate too */
.ph{position:relative}
.ph::before{content:"";position:absolute;inset:0;
  background:radial-gradient(120% 90% at 26% 8%,rgba(255,255,255,.16),rgba(255,255,255,0) 62%)}
.ph[data-cap]::after{padding-right:82px}

/* ─────────────────────────────────────────── 2.14 HANDOVER LAYOUT FIXES
   The v5 pack is desktop-only, so a few of its rules only misbehave below
   ~900px. Fixed here rather than in mcc.css, which stays a verbatim copy. */

/* No element may scroll the page sideways. `clip` rather than `hidden`
   because clip does not create a scroll container, so the sticky header
   still sticks to the viewport. This is the backstop; the actual causes are
   fixed below. */
html{overflow-x:clip}
body{overflow-x:clip;max-width:100%}

/* The decorative brandmark sits at right:-6% by design, which pushed the
   document 30px wider than the viewport on every page carrying it (measured
   with _deploy/scripts/find-overflow.php). .closing already clipped it; the
   dark feature band and the 404/error heroes did not. */
.rel:has(> .wmark){overflow:hidden}
.closing,.band{overflow:hidden}

/* The closing CTA band lives in the layout now, so it is a sibling of <main>,
   not of the page's last section. That silently broke the handover's
   `.dark + .closing` / `.warm + .closing` / `.light + .closing` rules — they
   can never match through <main>, so EVERY page was getting var(--sec) of
   paper above the dark band, including the pages that end on a coloured
   full-bleed ground (/acoustics ends warm, /projects ends on a bleed band).
   The spacing decision moves to the last element inside <main>: paper
   sections keep the breathing room the design intends, coloured grounds sit
   flush against the band. */
.closing{margin-top:0}
main > *:last-child:not(.dark):not(.warm):not(.light):not(.band):not(.closing)
  {padding-bottom:var(--sec)}

/* .ftr-in is `class="shell ftr-in"`, and mcc.css declares
   `.ftr-in{padding:72px 0 32px}` AFTER .shell — a shorthand, so it reset
   .shell's left/right gutters to 0. Invisible on desktop (the shell is
   centred inside a wider viewport) but on mobile the whole footer ran edge
   to edge with no margin. Restore the gutters without touching the
   vertical rhythm. */
.ftr-in{padding-left:var(--pad);padding-right:var(--pad)}

/* Long unbreakable strings — email addresses, ABNs, mono runs — must break
   rather than push the layout wider. */
.ftr .mn,.ftr .contact,.rules p,.rules b,.body,.lede,.credit,.dl .t,.kv,.cap,
.step p,.faq p,.proj p,.tile p{overflow-wrap:anywhere}

/* Desktop column counts that the handover set with inline style attributes.
   Inline styles beat media queries, so those pages never collapsed to one
   column on mobile. Declared here as classes (BEFORE the responsive section
   below, so the breakpoints still win) and the inline styles removed from
   the views. */
.projs.cols2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid2.form-split{grid-template-columns:minmax(0,.85fr) minmax(0,1.15fr)}

/* Full-bleed editorial band. mcc.css defines .bleed and .bleed .cap-box but
   the handover never used the component, so it has no img rule — the base
   `img{max-width:100%}` capped the photograph at its natural 1600px and
   letterboxed it, leaving bare paper to the right on wider screens. Give the
   band a ratio and let the image cover it, so it is full width at any size
   and crops taller on narrow screens instead of becoming a slit. */
.bleed{aspect-ratio:1600/685}
.bleed img{width:100%;height:100%;object-fit:cover;display:block}

/* On narrow screens the caption box is taller than a 21:9 band, and because
   .bleed clips, its heading was being cut off. Take the box out of absolute
   positioning below 900px and let it sit under the photograph — then it can
   never be clipped no matter how long the copy is. */
@media (max-width:900px){
  .bleed{aspect-ratio:auto;overflow:visible}
  .bleed img{height:auto;aspect-ratio:16/9;border-radius:0}
  .bleed .cap-box{position:static;left:auto;right:auto;bottom:auto;max-width:none;
    border-radius:0;margin:0;padding:26px var(--pad) 30px}
}
@media (max-width:620px){ .bleed img{aspect-ratio:4/3} }

/* ── WCAG 2.1 AA CONTRAST ────────────────────────────────────────────────
   Measured by _deploy/scripts/audit-typography.php, corrections computed by
   solve-contrast.php (smallest lightness shift that reaches the target, hue
   and saturation kept). Six pairs failed; all pass now.

   The approach follows the brand guide's own precedent: it already ships
   --orange-dark because "the brand orange is 4.1:1 on brown - fails AA below
   24px". Same idea, generalised — the IDENTITY orange #F04B23 is untouched
   for the brandmark and fills, and small orange TEXT resolves per ground. */

:root{
  /* was #8E8779 = 3.3:1 on paper, 2.9:1 on the Light Grey ground. Used by
     .muted, .credit, .meta, .cap, .tile .n, .role, .sub, .kv .lbl — all of it
     small text, some at 9.5px. One value that clears 4.5:1 on all three
     grounds it appears over: paper 5.88, surface 5.43, light grey 4.51. */
  --ink-3:#645F54;
  /* was #D3CEC4 = 3.85:1 on the warm ground */
  --on-warm-2:#E2DFD8;            /* 4.53:1 on warm */
  /* small orange text on light grounds (NOT the brandmark/fill colour) */
  --orange-text:#D4350E;          /* 4.51:1 on paper */
}
/* per-ground resolution, mirroring how mcc.css keys --seal-* by ground */
.dark{--orange-text:var(--orange-dark)}      /* 5.59:1 on brown */
.warm{--orange-text:#FFD9CF}                 /* 4.62:1 on warm  */
/* the Light Grey ground is darker than paper, so body copy needs a nudge:
   was #696256 = 4.28:1 there. Scoped, so paper copy is unchanged. */
.light{--ink-2:#655E53}                      /* 4.55:1 on light grey */

/* small orange text picks up the per-ground token. mcc.css's own
   `.dark .arrow` / `.warm .eyebrow` rules have higher specificity and still
   win where they exist — they resolve to the same values. */
.eyebrow,.arrow,.go,.headrow a,.step .n,.flag,.hi,.faq .pm{color:var(--orange-text)}
.flag{border-color:var(--orange-text)}

/* .btn-p is white on orange at 10.5px = 3.66:1, and it is the primary CTA on
   every page. The label stays white (brand), the button ground darkens by
   7.5% lightness — still unmistakably the brand orange, now AA.
   NB: a deviation from the guide's flat #F04B23 for this one component;
   flagged in CHANGELOG-mcc-phase2e.md for sign-off. */
.btn-p{background:#DE370F;border-color:#DE370F}   /* 4.51:1 with #fff */
.btn-p:hover{background:#C42F0B;border-color:#C42F0B}

/* WCAG 1.4.11 Non-text Contrast — 3:1 for the boundary of a UI COMPONENT.
   The handover's --rule (#D3D1CC) is 1.4:1 on paper, which is fine for a
   decorative hairline but not for the edge of a control you are meant to
   find and click. Applied to inputs, chips and outlined buttons ONLY.
   Deliberately NOT applied to .rules / .faq / .kv / .tw / .seclab dividers:
   those separate content, they are not components, and 1.4.11 does not
   cover them — darkening them would coarsen the design for no benefit. */
:root{--control-border:#938E82}   /* 3.26:1 on white, 3.02:1 on paper */
.inp,input.inp,textarea.inp,select.inp,.chip{border-color:var(--control-border)}
.btn-s{border-color:var(--control-border)}
.dark .btn-d,.btn-d{border-color:#75674E}          /* 3.01:1 on brown */
.warm .btn-w,.btn-w{border-color:#C9C3B6}          /* 3.03:1 on warm  */
/* selected/hover states keep the brand orange, which already clears 3:1 */
.chip:has(input:checked),.chip.on{border-color:var(--orange)}

/* ── Heading levels vs heading LOOK ──────────────────────────────────────
   Several headings were promoted so the document outline has no skipped
   levels (h1 -> h3 was reported on /projects, /acoustics/products and the
   404; every page skipped h2 -> h4 at the footer). The element changed, the
   appearance must not: these rules restate the h3 / .ftr h4 typography from
   mcc.css for the promoted elements.
   `h1,h2,h3,h4` in mcc.css already sets opsz 32, margin 0 and text-wrap. */
.tile-t,.proj-t{font-size:19px;font-weight:500;letter-spacing:-.012em;line-height:1.3}
.tile .tile-t{margin-top:16px}
.proj .proj-t{margin-top:7px}
/* the bleed band caption is a figure caption, not a section heading */
.bleed .cap-box .cap-t{font-size:19px;font-weight:500;letter-spacing:-.012em;
  line-height:1.3;color:var(--on-dark);font-variation-settings:"opsz" 32;margin:0}
/* footer column labels (were .ftr h4) */
.ftr .ftr-h{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--on-dark);font-weight:500;margin-bottom:16px;font-variation-settings:normal}
.ftr .ftr-h.gap{margin-top:28px}

/* /projects filter chips. They are real <button>s now (they actually filter),
   so the UA button styling is reset back to the handover's chip look. A chip
   whose value would return no projects is dimmed and unclickable rather than
   letting someone select an empty result. */
button.chip{font-family:inherit;font-size:13.5px;background:none;cursor:pointer;
  -webkit-appearance:none;appearance:none;line-height:normal}
button.chip.on{background:var(--orange);border-color:var(--orange);color:#fff}
.chip-off{opacity:.34;cursor:not-allowed}
.chip-off:hover{border-color:var(--rule);color:var(--ink-2)}
.proj[hidden]{display:none}
#projcount{color:var(--ink-3)}

/* ─────────────────────────────────────────── 2.15 COMING-SOON PAGE
   Shown while mcc.settings.comingsoon = 1. Same tokens and type as the rest
   of the site so it reads as the brand, not as a placeholder. */
.cs{padding-top:0;min-height:100svh;display:flex;align-items:center;overflow:hidden}
.cs-in{padding-top:72px;padding-bottom:72px;width:100%}
.cs-wmark{right:-14%;opacity:.05}
.cs-brand{display:flex;align-items:center;gap:14px;margin-bottom:56px}
.cs-brand .wm{font-size:19px}
.cs-eyebrow{margin-bottom:18px}
.cs-h1{font-size:clamp(34px,5.6vw,64px);max-width:15ch}
.cs-lede{max-width:54ch;margin-top:22px}
.cs-cta{margin-top:34px}
.cs-what{margin-top:64px;padding-top:26px;border-top:1px solid var(--dark-rule);max-width:62ch}
.cs-what-lbl{color:var(--on-dark-2);margin-bottom:14px}
.cs-list{display:grid;gap:7px}
.cs-list li{font-size:15.5px;color:var(--on-dark-2);line-height:1.5;position:relative;padding-left:18px}
.cs-list li::before{content:"";position:absolute;left:0;top:.62em;width:7px;height:1px;background:var(--orange-dark)}
.cs-badges{margin-top:44px}
.cs-badges .accred{gap:14px}
.cs-foot{margin-top:52px;font-size:11.5px;line-height:1.9;color:var(--on-dark-2);
  letter-spacing:.05em;text-transform:uppercase}
.cs-foot a{color:var(--on-dark);border-bottom:1px solid var(--dark-rule)}
.cs-foot a:hover{border-bottom-color:var(--orange-dark)}
@media (max-width:900px){
  .cs{min-height:0}
  .cs-in{padding-top:52px;padding-bottom:56px}
  .cs-brand{margin-bottom:38px}
  .cs-what{margin-top:44px}
  .cs-wmark{width:420px!important;right:-28%;opacity:.04}
}
@media (max-width:620px){
  .cs-cta .btn{flex:1 1 100%;text-align:center}
  .cs-badges .accred{flex-direction:column;align-items:stretch}
}

/* ─────────────────────────────────────────── 3. HEADER CHROME */
.hdr{position:sticky;top:0;z-index:60;
  transition:height var(--t-med) var(--ease),background var(--t-med) var(--ease),box-shadow var(--t-med) var(--ease)}
.hdr-in{transition:height var(--t-med) var(--ease)}
.hdr.scrolled{background:rgba(26,22,13,.92);backdrop-filter:saturate(1.4) blur(14px);
  box-shadow:0 1px 0 0 var(--dark-rule),0 18px 40px -28px rgba(0,0,0,.8)}
.hdr.scrolled .hdr-in{height:70px}
.hdr .wordmark{transition:transform var(--t-med) var(--ease)}
.hdr.scrolled .wordmark{transform:scale(.92)}
.wordmark svg path{transition:fill var(--t-med) var(--ease)}
.wordmark:hover svg path{fill:#FF6242}

/* scroll progress */
#mccpb{position:fixed;top:0;left:0;height:2px;width:0;z-index:80;
  background:linear-gradient(90deg,var(--orange),#FF8A6B);box-shadow:0 0 12px rgba(240,75,35,.7)}

/* Skip link. Clipped rather than pushed to left:-9999px — a far-offscreen
   absolute element shows up as a false positive in overflow audits and, in
   some engines, can extend the scrollable area. */
.skip{position:absolute;top:0;left:0;z-index:100;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;background:var(--orange);color:#fff;
  border-radius:0 0 var(--r-sm) 0;font-family:var(--mono);font-size:11px;
  letter-spacing:.14em;text-transform:uppercase}
.skip:focus{width:auto;height:auto;overflow:visible;clip-path:none;padding:12px 18px}

/* hamburger */
.ham{display:none;width:44px;height:44px;border:1px solid var(--dark-rule);border-radius:var(--r-sm);
  background:transparent;cursor:pointer;padding:0;position:relative;flex:none}
.ham i{position:absolute;left:12px;right:12px;height:1.5px;background:var(--on-dark);border-radius:2px;
  transition:transform var(--t-med) var(--ease),opacity var(--t-fast) var(--ease)}
.ham i:nth-child(1){top:15px}.ham i:nth-child(2){top:21px}.ham i:nth-child(3){top:27px}
.ham.open i:nth-child(1){transform:translateY(6px) rotate(45deg)}
.ham.open i:nth-child(2){opacity:0}
.ham.open i:nth-child(3){transform:translateY(-6px) rotate(-45deg)}

/* mobile drawer */
.mob{position:fixed;inset:var(--hdr-h) 0 0 0;z-index:55;background:var(--brown-deep);
  padding:28px var(--pad) 40px;overflow-y:auto;
  opacity:0;visibility:hidden;transform:translate3d(0,-12px,0);
  transition:opacity var(--t-med) var(--ease),transform var(--t-med) var(--ease),visibility var(--t-med)}
.mob.open{opacity:1;visibility:visible;transform:none}
.mob a{display:block;padding:17px 0;border-bottom:1px solid var(--dark-rule);color:var(--on-dark);
  font-size:21px;font-weight:500;letter-spacing:-.02em;font-variation-settings:"opsz" 32;
  opacity:0;transform:translate3d(0,14px,0)}
.mob.open a{animation:mccMobIn .5s var(--ease) both}
.mob.open a:nth-child(1){animation-delay:.05s}.mob.open a:nth-child(2){animation-delay:.10s}
.mob.open a:nth-child(3){animation-delay:.15s}.mob.open a:nth-child(4){animation-delay:.20s}
.mob.open a:nth-child(5){animation-delay:.25s}.mob.open a:nth-child(6){animation-delay:.30s}
.mob.open a:nth-child(7){animation-delay:.35s}
.mob a.on{color:var(--orange-dark)}
.mob .mob-foot{margin-top:32px;display:flex;flex-direction:column;gap:14px}
.mob .mob-foot .btn{text-align:center}
@keyframes mccMobIn{to{opacity:1;transform:none}}

/* back to top */
.totop{position:fixed;right:26px;bottom:26px;z-index:50;width:48px;height:48px;border-radius:50%;
  background:var(--brown);color:var(--on-dark);border:1px solid var(--dark-rule);cursor:pointer;
  display:grid;place-items:center;opacity:0;visibility:hidden;transform:translate3d(0,14px,0) scale(.9);
  transition:all var(--t-med) var(--ease)}
.totop.on{opacity:1;visibility:visible;transform:none}
.totop:hover{background:var(--orange);border-color:var(--orange);color:#fff;transform:translateY(-3px)}
.totop svg{width:16px;height:16px}

/* focus visibility — the handover pack has none */
a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,
.chip:focus-visible,.tile:focus-visible{outline:2px solid var(--orange);outline-offset:3px;border-radius:4px}
.dark a:focus-visible,.dark button:focus-visible{outline-color:var(--orange-dark)}

/* real form controls (handover uses static divs) */
input.inp,textarea.inp,select.inp{width:100%;font-family:var(--sans);color:var(--ink);
  transition:border-color var(--t-fast),box-shadow var(--t-fast),background var(--t-fast)}
input.inp:focus,textarea.inp:focus,select.inp:focus{outline:0;border-color:var(--orange);
  background:#fff;box-shadow:0 0 0 3px rgba(240,75,35,.13)}
textarea.inp{min-height:132px;resize:vertical}
.err{margin-top:7px;font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:#C0341A}
.note{padding:16px 18px;border-radius:var(--r-sm);font-size:14.5px;margin-bottom:22px}
.note.ok{background:#EAF4EC;color:#1E5B2C;border:1px solid #BFDDC6}
.note.bad{background:#FBEAE6;color:#8E2A12;border:1px solid #F0C6BA}

/* ─────────────────────────────────────────── 5. RESPONSIVE */
@media (max-width:1360px){
  :root{--pad:48px}
  .hero-copy{padding-left:48px;padding-right:48px}
}
@media (max-width:1180px){
  :root{--sec:112px;--pad:40px}
  h1{font-size:52px}h1.sm{font-size:42px}h2{font-size:34px}h2.mid{font-size:28px}
  .grid2,.split{gap:56px}
  .tiles.t4,.projs.cols4,.spec-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .creds-in{grid-template-columns:repeat(2,minmax(0,1fr))}
  .cred{border-bottom:1px solid var(--rule)}
  .cred:nth-child(2n){border-right:0}
  .cred:not(:first-child){padding-left:0}
  .cred:nth-child(2n){padding-left:32px}
  .ftr-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:40px}
  .swatch-list{grid-template-columns:repeat(3,minmax(0,1fr))}
  .hero{min-height:0}
  .hero-copy{padding-top:72px;padding-bottom:72px}
  .hero-img{min-height:420px}
}
@media (max-width:900px){
  :root{--sec:88px;--pad:28px;--hdr-h:74px}
  .hdr-in{height:74px}
  .nav,.hdr-r .tel,.hdr-r .btn{display:none}
  .ham{display:block}
  h1{font-size:41px;max-width:none}h1.sm{font-size:34px}
  h2{font-size:28px;max-width:none}h2.mid{font-size:24px}
  .lede{font-size:17px;max-width:none}
  /* minmax(0,1fr), never plain 1fr: `1fr` is minmax(auto,1fr), so its
     min-content floor lets a wide child (a nowrap table cell, a long mono
     string) push the column past the viewport. */
  .grid2,.grid2.form-split,.split,.hero,.steps,.svc,.svc.rev,.kv>div,.row2
    {grid-template-columns:minmax(0,1fr);gap:36px}
  .svc.rev>div:first-child{order:0}
  .hero-copy{padding:56px var(--pad)}
  .hero-img{min-height:320px;order:-1}
  .steps{gap:0}
  .faq>div{flex-direction:column;gap:10px}
  .bleed .cap-box{left:var(--pad);right:var(--pad);bottom:24px;max-width:none;padding:24px 22px}
  .accred{gap:16px}
  .logos{gap:32px}
  .mask-box{height:340px}
  .quote{font-size:24px;max-width:none}
  .ftr-legal{flex-direction:column;gap:10px}
  section{padding-top:var(--sec)}
  .pad{padding-top:80px;padding-bottom:80px}
  .band{margin-top:88px}
  .wmark{width:380px!important}
}
@media (max-width:620px){
  :root{--pad:20px;--sec:72px;--r-lg:16px}
  h1{font-size:33px}h2{font-size:25px}h2.mid{font-size:22px}
  body{font-size:16px}
  .tiles.t2,.tiles.t3,.tiles.t4,.projs,.projs.cols2,.projs.cols3,.projs.cols4,
  .spec-grid,.tiles.cols3,.tiles.cols4,.tiles.cols5{grid-template-columns:minmax(0,1fr)}
  .creds-in{grid-template-columns:minmax(0,1fr)}
  .cred{border-right:0;padding:28px 0!important}
  .cred b{font-size:42px}
  .cred::after{display:none}
  .ftr-grid{grid-template-columns:minmax(0,1fr);gap:32px}
  .swatch-list{grid-template-columns:repeat(2,minmax(0,1fr))}
  .form{padding:26px 22px 30px}
  .cta{gap:10px}.cta .btn{flex:1 1 100%;text-align:center}
  .tw{overflow-x:auto}
  .hero-img{min-height:250px}
  .mask-box{height:250px}
  .totop{right:14px;bottom:14px;width:42px;height:42px}
  .logos span{font-size:16px}
  .wmark{width:280px!important;opacity:.05}
}

/* tables never break the page */
.tw{max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}

/* ─────────────────────────────────────────── 6. REDUCED MOTION + PRINT */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
  [data-anim]{opacity:1!important;transform:none!important;filter:none!important;clip-path:none!important}
  .hero-copy .w{opacity:1!important;transform:none!important}
  .seclab .ln{transform:scaleX(1)!important}
  .mask-box img{-webkit-mask-size:84% auto!important;mask-size:84% auto!important;transform:none!important}
  .fig-box img{transform:none!important}
  .logos.run .track{animation:none!important}
  .wmark{transform:translateY(-50%)!important}
  .cred b,.cred span{opacity:1!important;transform:none!important}
}
@media print{
  .hdr,.mob,.ham,#mccpb,.totop,.closing .cta{display:none!important}
  [data-anim],.hero-copy .w,.cred b,.cred span{opacity:1!important;transform:none!important;clip-path:none!important}
  body{background:#fff}
  .dark,.warm,.light,.closing,.ftr{background:#fff!important;color:#000!important}
}

/* no-JS: never leave content invisible */
.no-js [data-anim],.no-js .hero-copy .w,.no-js .cred b,.no-js .cred span{opacity:1!important;transform:none!important;clip-path:none!important}
.no-js .seclab .ln{transform:scaleX(1)!important}
.no-js .mask-box img{-webkit-mask-size:84% auto!important;mask-size:84% auto!important}
