/* ==========================================================================
   LUMINOVA CONSTRUCTION
   Black ground, gold accent, hairline structure. Every colour is a token.
   ========================================================================== */

/* ==========================================================================
   THEME
   Dark is the brand default and lives on :root, so the site looks the way it
   always has with no attribute set. Light is an explicit opt-in via the header
   toggle, which writes data-theme onto <html> and remembers the choice.

   Nothing below this block hard-codes a colour, a shadow or a photo filter -
   a light page cannot just invert a dark one. Photography crushed to 26%
   brightness looks right under white text and looks like mud under black
   text, so the image treatments are tokens too.
   ========================================================================== */
:root {
  color-scheme: dark;

  --bg:       #000000;
  --panel:    #070709;
  --panel-2:  #0C0D11;
  --line:     rgba(255, 255, 255, 0.07);
  --line-2:   rgba(255, 255, 255, 0.16);
  /* Audience: 30–60. Every step on this ramp clears WCAG AA (4.5:1) on black,
     because the tier below --dim carries most of the site's small labels and
     the old --faint (#555C68) measured 2.4:1 - decorative, not readable. */
  --text:     #F4F6F8;   /* 18.4:1 */
  --dim:      #A9B1BE;   /*  9.4:1 */
  --faint:    #828C9B;   /*  5.4:1 */
  --accent:   #C9A24A;
  --accent-2: #EAD29A;
  --glow:     rgba(201, 162, 74, 0.35);

  /* Surfaces and ink that used to be hard-coded */
  --on-accent: #000;                      /* text on a gold fill */
  --chip:      #E9EBEE;                   /* light plate under cert logos */
  --opt-bg:    #0C0D11;                   /* native <select> dropdown */
  --sweep:     rgba(255, 255, 255, .08);  /* ghost-button sheen */
  --shadow:    rgba(0, 0, 0, .9);
  --shadow-2:  rgba(0, 0, 0, .9);
  --err:       #F2A192;
  --err-line:  #E0654A;
  --accent-deep: color-mix(in srgb, var(--accent) 55%, #000);  /* gradient tail */
  --aura-op:   .75;
  --grain-op:  .018;

  /* Image treatments - see the note above */
  --photo:       grayscale(.7) contrast(1.08) brightness(.8);
  --photo-hover: grayscale(0) contrast(1.02) brightness(.95);
  --photo-hero:  grayscale(.55) contrast(1.12) brightness(.5);
  --photo-page:  grayscale(.7) brightness(.34);
  --photo-cta:   grayscale(.8) brightness(.26);
  --photo-svc:   grayscale(.65) brightness(.8) contrast(1.08);
  --photo-svc-h: grayscale(0) brightness(.95);
  /* For photos where colour is INFORMATION, not decoration - hazard yellow,
     white PPE, pink containment. The house desaturation turns those into
     anonymous sacks, and touch devices have no hover to restore them. */
  --photo-keep:  grayscale(.14) brightness(.94) contrast(1.04);

  --font-display: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --font-body:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --wrap: 1240px;
  --header-h: 88px;
  --rail: 92px;

  /* One spacing scale. Everything vertical resolves to a step on this ladder -
     consistency is most of what reads as "considered" rather than "assembled". */
  --s1: 8px;
  --s2: 13px;
  --s3: 20px;
  --s4: 30px;
  --s5: 42px;
  --s6: 58px;
  --s7: clamp(48px, 5vw, 72px);
  --section: clamp(64px, 6.5vw, 104px);
  --gutter: clamp(22px, 3.4vw, 46px);
  --cell: clamp(22px, 2.4vw, 36px);   /* inner padding for divided grid cells */

  /* Corner radii. The brand is drafting-precise, not bubbly, so these stay
     generous-but-restrained: enough to stop the page reading as a stack of
     rectangles, not enough to turn it into a toy. Change the scale here and
     the whole site softens or hardens together. */
  --r-sm:   10px;   /* chips, small plates */
  --r-md:   16px;   /* segmented controls, status panels */
  --r-lg:   24px;   /* cards, imagery, glass panels, the header capsule */
  --r-pill: 999px;  /* buttons and anything that should read as a lozenge */

  /* ------------------------------------------------------------- glass ---
     A pane only reads as glass when four things agree: a translucent FILL,
     a REFRACTING backdrop behind it, a SPECULAR RIM that is bright where the
     light falls and nearly gone where it doesn't, and a soft DROP SHADOW so
     the pane floats above the page instead of being painted onto it.
     Miss the rim and you get a grey box; miss the shadow and it looks flat. */
  --glass-blur: 26px;
  --glass-sat:  1.7;
  --glass-fill:  linear-gradient(158deg, rgba(255,255,255,.085), rgba(255,255,255,.022) 44%, rgba(255,255,255,.055));
  --glass-rim:   linear-gradient(148deg, rgba(255,255,255,.60), rgba(255,255,255,.07) 36%, rgba(255,255,255,.02) 64%, rgba(255,255,255,.26));
  --glass-gloss: linear-gradient(180deg, rgba(255,255,255,.13), rgba(255,255,255,0) 56%);
  --glass-spec:  rgba(255,255,255,.20);          /* the travelling highlight */
  --glass-inset: inset 0 1px 0 rgba(255,255,255,.20), inset 0 -1px 0 rgba(255,255,255,.05);
  --glass-shadow: 0 30px 70px -28px rgba(0,0,0,.85), 0 8px 22px -14px rgba(0,0,0,.6);
  --lift-shadow:  0 44px 90px -32px rgba(0,0,0,.9), 0 12px 30px -16px rgba(0,0,0,.65);

  --ease:     cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* Logo silhouette, used to mask the light sweeps. Declared here, not inline:
     a relative URL in a custom property resolves against the STYLESHEET's
     location, so an HTML-side "assets/img/…" would 404 at "assets/css/…". */
  --logo: url('../img/logo-sm.webp');
}

/* ------------------------------------------------------------------ light */
/* Architectural paper, not white. The brand gold (#C9A24A) only manages
   2.2:1 on a light ground, so it FAILS as text - light mode uses a deeper
   bronze that clears AA at 4.6:1 and still reads as Luminova gold. Gold
   fills therefore carry white text here, not black. */
:root[data-theme='light'] {
  color-scheme: light;

  --bg:       #F4F5F7;
  --panel:    #EAECF0;
  --panel-2:  #FFFFFF;
  --line:     rgba(16, 19, 26, 0.11);
  --line-2:   rgba(16, 19, 26, 0.24);
  --text:     #0E1116;   /* 17.2:1 */
  --dim:      #45505F;   /*  7.5:1 */
  --faint:    #5E6978;   /*  5.1:1 */
  --accent:   #8A6A18;   /*  4.6:1 - deep bronze, readable as text */
  --accent-2: #5E4708;   /*  8.0:1 - hover/emphasis goes darker, not lighter */
  --glow:     rgba(138, 106, 24, 0.22);

  --on-accent: #FFFFFF;
  --chip:      #FFFFFF;
  --opt-bg:    #FFFFFF;
  --sweep:     rgba(16, 19, 26, .07);
  --shadow:    rgba(16, 19, 26, .16);
  --shadow-2:  rgba(16, 19, 26, .10);
  --err:       #A8341A;
  --err-line:  #C24A2E;
  /* Lighten rather than darken here, or the headline's gradient tail turns
     to soot on paper. */
  --accent-deep: color-mix(in srgb, var(--accent) 70%, #C9A24A);
  --aura-op:   .5;
  --grain-op:  .022;

  /* Lifted, not crushed. Dark text needs a light photograph under it. */
  --photo:       grayscale(.5) contrast(1.02) brightness(1);
  --photo-hover: grayscale(0) contrast(1) brightness(1);
  --photo-hero:  grayscale(.5) contrast(.95) brightness(1.12);
  --photo-page:  grayscale(.6) contrast(.95) brightness(1.15);
  --photo-cta:   grayscale(.7) contrast(.95) brightness(1.15);
  --photo-svc:   grayscale(.45) brightness(1.02) contrast(1);
  --photo-svc-h: grayscale(0) brightness(1);
  --photo-keep:  grayscale(.06) brightness(1.02) contrast(1.02);

  /* On paper the glass is a frosted white pane, and the rim inverts: bright
     white where light hits, a soft dark edge where it falls away. Reusing the
     dark values here would produce grey smears. */
  --glass-fill:  linear-gradient(158deg, rgba(255,255,255,.82), rgba(255,255,255,.46) 44%, rgba(255,255,255,.68));
  --glass-rim:   linear-gradient(148deg, rgba(255,255,255,.98), rgba(16,19,26,.11) 36%, rgba(16,19,26,.05) 64%, rgba(255,255,255,.75));
  --glass-gloss: linear-gradient(180deg, rgba(255,255,255,.55), rgba(255,255,255,0) 56%);
  --glass-spec:  rgba(255,255,255,.80);
  --glass-inset: inset 0 1px 0 rgba(255,255,255,.95), inset 0 -1px 0 rgba(16,19,26,.05);
  --glass-shadow: 0 26px 60px -30px rgba(16,19,26,.28), 0 6px 18px -12px rgba(16,19,26,.16);
  --lift-shadow:  0 40px 80px -34px rgba(16,19,26,.34), 0 10px 26px -14px rgba(16,19,26,.20);
}

/* Veils carry more of the page colour in light mode. White text stays legible
   over a fairly open photograph; near-black text does not, so the washes over
   the hero, page headers and CTA band are pulled up rather than letting the
   headline fight the image. */
:root[data-theme='light'] .hero__veil {
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--bg) 94%, transparent) 0%, color-mix(in srgb, var(--bg) 64%, transparent) 38%, color-mix(in srgb, var(--bg) 92%, transparent) 82%, var(--bg) 100%),
    linear-gradient(96deg, color-mix(in srgb, var(--bg) 96%, transparent) 0%, color-mix(in srgb, var(--bg) 54%, transparent) 62%);
}
:root[data-theme='light'] .page-hero__veil {
  background: linear-gradient(180deg, color-mix(in srgb, var(--bg) 92%, transparent), color-mix(in srgb, var(--bg) 82%, transparent) 45%, var(--bg));
}
:root[data-theme='light'] .cta__veil {
  background: linear-gradient(180deg, var(--bg), color-mix(in srgb, var(--bg) 90%, transparent) 45%, var(--bg));
}
/* Cert logos are dark marks on a white plate; on paper they need an edge. */
:root[data-theme='light'] .chip { border: 1px solid var(--line); }

/* ------------------------------------------------------------------ reset */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 32px);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.68;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
body.is-locked { overflow: hidden; }

img, svg, iframe, canvas { display: block; max-width: 100%; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -0.03em;
  margin: 0 0 0.45em;
  text-wrap: balance;
}
/* Deliberately restrained. Oversized display type reads as a billboard; the
   precision of a small, tightly-tracked scale is what reads as engineered. */
h1 { font-size: clamp(2.5rem, 5.2vw, 4.5rem); font-weight: 800; letter-spacing: -0.04em; }
h2 { font-size: clamp(1.75rem, 3.1vw, 2.75rem); letter-spacing: -0.032em; }
h3 { font-size: clamp(1.12rem, 1.35vw, 1.28rem); letter-spacing: -0.01em; }

p { margin: 0 0 1.1em; color: var(--dim); text-wrap: pretty; }
.lead { font-size: clamp(1.06rem, 1.2vw, 1.2rem); line-height: 1.66; max-width: 56ch; }

a { color: inherit; text-decoration: none; }
ul { margin: 0; padding: 0; list-style: none; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
::selection { background: var(--accent); color: var(--on-accent); }

/* --------------------------------------------------------------- scaffold */
.wrap { width: min(100% - (var(--gutter) * 2), var(--wrap)); margin-inline: auto; }
.narrow { width: min(100% - (var(--gutter) * 2), 860px); margin-inline: auto; }

.section { padding: var(--section) 0; position: relative; }
.section--panel { background: var(--panel); }

.skip-link {
  position: absolute; left: 20px; top: -80px; z-index: 300;
  background: var(--accent); color: var(--on-accent); padding: 12px 22px;
  border-radius: var(--r-pill);
  font-family: var(--font-mono); font-size: 0.78rem;
  transition: top 0.25s;
}
.skip-link:focus { top: 20px; }

main, .site-header, .site-footer { position: relative; z-index: 2; }

/* ------------------------------------------------------------ backdrops -- */
/* Atmosphere. Slow-drifting light in a deep volume - this is what stops the
   page reading as a flat black rectangle. Costs one composited layer. */
.aura {
  position: fixed; inset: -25%; z-index: 0; pointer-events: none;
  opacity: var(--aura-op);
  background:
    radial-gradient(34% 28% at 20% 16%, color-mix(in srgb, var(--accent) 15%, transparent), transparent 70%),
    radial-gradient(30% 24% at 80% 34%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 72%),
    radial-gradient(44% 34% at 54% 86%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 74%);
  will-change: transform;
  animation: drift 52s ease-in-out infinite alternate;
}
@keyframes drift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(2.5%, -2%, 0) scale(1.08); }
  100% { transform: translate3d(-2%, 2.5%, 0) scale(1.03); }
}

.grain {
  position: fixed; inset: 0; z-index: 1; pointer-events: none; opacity: var(--grain-op);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

.scroll-bar {
  position: fixed; inset: 0 auto auto 0; z-index: 300;
  height: 2px; width: 100%;
  transform: scaleX(0); transform-origin: left;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
}

/* -------------------------------------------------------------- cursor --- */
/* A CAD-style reticle that ACCOMPANIES the pointer. It used to replace it
   (cursor: none), which is the kind of flourish that reads as broken to
   anyone who isn't a designer - people lose the pointer, text selection
   feels dead, and the I-beam over a form field disappears. The system
   cursor now stays visible and this is decoration layered behind it. */
.cur { position: fixed; left: 0; top: 0; z-index: 350; pointer-events: none; opacity: 0; transition: opacity .3s; }
.cur.on { opacity: 1; }
.cur__ring {
  position: absolute; left: 0; top: 0;
  width: 38px; height: 38px; margin: -19px 0 0 -19px;
  border: 1px solid var(--line-2); border-radius: 50%;
  transition: width .3s var(--ease), height .3s var(--ease), margin .3s var(--ease), border-color .3s, border-radius .3s, background-color .3s;
}
/* The centre dot is gone - the real cursor occupies that spot now. */
.cur__dot { display: none; }
.cur.hot .cur__ring {
  width: 64px; height: 64px; margin: -32px 0 0 -32px;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* --------------------------------------------------------- sheet index --- */
/* Fixed drawing-set rail: where you are in the document. */
/* A spine of ticks. The label only appears for the section you're in (or the
   one you're pointing at), so it stays near-invisible until it's useful. */
.rail {
  position: fixed; left: 30px; top: 50%; z-index: 40;
  transform: translateY(-50%);
  display: grid; gap: 20px;
  opacity: 0; transition: opacity .6s var(--ease);
}
.rail.on { opacity: 1; }
.rail a {
  position: relative; display: block;
  width: 22px; height: 1px;
  background: var(--line-2);
  transition: width .5s var(--ease), background-color .4s;
}
.rail a span {
  position: absolute; left: 34px; top: 50%; transform: translateY(-50%) translateX(-6px);
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent); white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity .4s var(--ease), transform .5s var(--ease);
}
.rail a:hover { width: 34px; background: var(--text); }
.rail a:hover span { opacity: .7; transform: translateY(-50%) translateX(0); }
.rail a[aria-current='true'] { width: 40px; background: var(--accent); }
.rail a[aria-current='true'] span { opacity: 1; transform: translateY(-50%) translateX(0); }

/* ------------------------------------------------------------------ bits */
/* Mono uppercase labels sit one tier up from where a pure style exercise
   would put them. Wide tracking costs legibility at small sizes, so the
   tracking comes down as the size goes up - it still reads as drafting
   annotation, but at arm's length and without reading glasses. */
.tag {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-mono);
  font-size: 0.8rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--s3);
}
.tag::before { content: ''; width: 30px; height: 1px; background: currentColor; opacity: .6; }
.tag--center::before { display: none; }

.mono {
  font-family: var(--font-mono);
  font-size: 0.8rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--faint);
}

.head { max-width: 760px; margin-bottom: var(--s7); }
.head.center { margin-inline: auto; text-align: center; }
.head h2 { margin-bottom: var(--s3); }
.head h2:last-child { margin-bottom: 0; }
.head .lead { margin-bottom: 0; }

/* ================================================================ GLASS ===
   One material, applied to every floating surface. The rim and the gloss are
   drawn as pseudo-elements at z-index -1, so they sit above the element's own
   fill but below its content - which is why these selectors are only ever put
   on elements whose ::before / ::after are free.

   --gx / --gy are written by main.js from the pointer, so the specular
   highlight tracks the cursor across the pane. They default to a fixed
   top-left light, so the material is complete with JS off. */
/* ---- the material: fill + rim + gloss + shadow. No blur here. ---------- */
.site-header__in,
.form__status,
.theme-btn,
.burger,
.to-top,
.btn--line {
  position: relative;
  isolation: isolate;
  background: var(--glass-fill);
  border-color: transparent;
  box-shadow: var(--glass-shadow), var(--glass-inset);
}
/* The chip keeps its opaque light plate under the certification logos, so it
   takes the rim and gloss only. */
.chip { position: relative; isolation: isolate; box-shadow: var(--glass-shadow), var(--glass-inset); }

/* ---- refraction: the expensive half, spent on one pane only ------------
   backdrop-filter re-samples everything behind it EVERY frame, and the .aura
   behind this page never stops moving - so each refracting surface is a
   permanent per-frame cost, not a one-off. Measured on this page: 15 of them
   ran at 4fps, the same page with the blur off ran at 169fps. The blur is
   therefore spent only where a large pane has genuinely varied content behind
   it. Small controls get the fill, rim and gloss above, which is what reads as
   glass at that size anyway - at 46px nobody can see what the blur did.
   If you add another, measure before and after. */
.site-header__in {
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
}

/* The refracting edge. A masked ring so it follows border-radius exactly -
   a plain border can't carry a gradient that fades around the curve. */
.site-header__in::before,
.chip::before,
.form__status::before,
.theme-btn::before,
.burger::before,
.to-top::before,
.btn::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; padding: 1px;
  background: var(--glass-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

/* Top gloss plus the travelling highlight. */
.site-header__in::after,
.chip::after,
.form__status::after,
.theme-btn::after,
.burger::after,
.to-top::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; pointer-events: none;
  background:
    radial-gradient(220px circle at var(--gx, 22%) var(--gy, 0%), var(--glass-spec), transparent 68%),
    var(--glass-gloss);
}

/* Imagery sits UNDER glass: a lit rim on top of the photo, and a shadow deep
   enough that the card reads as a physical tile above the page. */
.work__fig .shot::before,
.svc__media .shot::before {
  content: ''; position: absolute; inset: 0; z-index: 2;
  border-radius: inherit; padding: 1px;
  background: var(--glass-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
.work__fig .shot, .svc__media .shot { box-shadow: var(--glass-shadow); }

/* The tilt lives on the WRAPPER, not on .shot - .shot already carries the
   scroll-driven depth animation, and an animation beats a declared transform,
   so a tilt written there would simply never appear. */
.work__fig, .svc__media {
  transform: perspective(1100px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform .5s var(--ease), filter .5s var(--ease);
}
.work__fig:hover, .svc__media:hover { filter: drop-shadow(0 26px 40px rgba(0,0,0,.45)); }

/* --------------------------------------------------------------- button - */
.btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 52px;               /* comfortable target, not a 40px hairline */
  padding: 15px 30px;
  font-family: var(--font-mono);
  font-size: 0.8rem; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--on-accent);
  /* Lit metal, not a flat fill: light along the top edge, the brand gold
     through the middle, a darker roll-off at the bottom. */
  background: linear-gradient(168deg,
    color-mix(in srgb, var(--accent-2) 82%, transparent),
    var(--accent) 46%,
    color-mix(in srgb, var(--accent) 80%, #000));
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  /* isolation is load-bearing: without a stacking context the z-index:-1 rim
     paints BEHIND the button's own background and vanishes. */
  isolation: isolate;
  cursor: pointer;
  box-shadow: var(--glass-shadow), inset 0 1px 0 rgba(255,255,255,.45), inset 0 -1px 0 rgba(0,0,0,.18);
  transition: color .4s var(--ease), box-shadow .5s var(--ease), transform .35s var(--ease);
}
.btn > * { position: relative; z-index: 1; }
.btn::after {
  content: ''; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(110deg, transparent 20%, rgba(255,255,255,.55) 50%, transparent 80%);
  transform: translateX(-120%);
  transition: transform .7s var(--ease);
}
/* Rises toward the pointer and throws more light. */
.btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--lift-shadow), 0 0 44px -8px var(--glow),
              inset 0 1px 0 rgba(255,255,255,.55), inset 0 -1px 0 rgba(0,0,0,.18);
}
.btn:hover::after { transform: translateX(120%); }
.btn svg { transition: transform .4s var(--ease); }
.btn:hover svg { transform: translateX(5px); }

/* The ghost button re-asserts the glass fill HERE, after .btn. Both selectors
   are (0,1,0), so .btn's gold gradient above wins on source order - relying on
   the glass group alone silently painted every ghost button gold, with white
   label text on it. */
.btn--line {
  color: var(--text);
  background: var(--glass-fill);
  box-shadow: var(--glass-shadow), var(--glass-inset);
}
.btn--line::after { background: linear-gradient(110deg, transparent 20%, var(--sweep) 50%, transparent 80%); }
.btn--line:hover {
  color: var(--accent-2);
  box-shadow: var(--lift-shadow), var(--glass-inset);
}
.btn--block { width: 100%; justify-content: center; }

.arrow-link {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-mono);
  font-size: 0.82rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text);
  padding: 12px 0 8px;            /* clears a 40px target with the rule intact */
  border-bottom: 1px solid var(--line-2);
  transition: color .35s, border-color .35s;
}
.arrow-link svg { transition: transform .4s var(--ease); }
.arrow-link:hover { color: var(--accent-2); border-color: var(--accent); }
.arrow-link:hover svg { transform: translateX(5px); }

/* --------------------------------------------------------------- header - */
/* A floating glass instrument, not a bar welded to the top of the page. */
.site-header {
  position: fixed; inset: 14px 0 auto 0; z-index: 100;
  display: flex; align-items: center;
  transition: inset .5s var(--ease);
}
.site-header__in {
  width: min(100% - 40px, 1340px); margin-inline: auto;
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding: 11px 11px 11px 20px;
  border-radius: var(--r-lg);
  /* Blur, rim and shadow all come from the glass group above - they used to be
     restated here, which silently overrode it.

     The gloss moves OUT of ::after and into the element's own background,
     stacked over the fill. That frees ::after to be nothing but the travelling
     highlight, which is what lets it fade in and out on its own. */
  background: var(--glass-gloss), var(--glass-fill);
  transition: padding .5s var(--ease), box-shadow .5s var(--ease), backdrop-filter .5s var(--ease);
}
/* The highlight only lights up under the pointer. Elsewhere the header keeps
   its gloss and rim, so it still reads as glass - it just isn't glowing at
   nobody. Touch devices never hover, so it simply stays off. */
.site-header__in::after {
  background: radial-gradient(220px circle at var(--gx, 50%) var(--gy, 50%), var(--glass-spec), transparent 68%);
  opacity: 0;
  transition: opacity .4s var(--ease);
}
.site-header__in:hover::after { opacity: 1; }
.site-header.is-stuck { inset: 10px 0 auto 0; }
/* Scrolled: the pane thickens rather than swapping to a flat fill. */
.site-header.is-stuck .site-header__in {
  padding: 7px 7px 7px 16px;
  backdrop-filter: blur(calc(var(--glass-blur) * 1.4)) saturate(calc(var(--glass-sat) * 1.1));
  -webkit-backdrop-filter: blur(calc(var(--glass-blur) * 1.4)) saturate(calc(var(--glass-sat) * 1.1));
  box-shadow: var(--lift-shadow), var(--glass-inset);
}

.brand { display: flex; align-items: center; gap: 13px; }
.brand__mark { position: relative; display: block; }
.brand__mark img { height: 38px; width: auto; transition: height .5s var(--ease); }
.site-header.is-stuck .brand__mark img { height: 30px; }
.brand__mark .sheen {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: linear-gradient(108deg, transparent 38%, rgba(255,255,255,.85) 50%, transparent 62%);
  -webkit-mask-image: var(--logo); mask-image: var(--logo);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.brand:hover .brand__mark .sheen { animation: sheen .9s var(--ease-out); }
.brand__txt { display: flex; flex-direction: column; line-height: 1.12; font-family: var(--font-display); }
.brand__name { font-size: 0.98rem; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase; }
.brand__sub { font-family: var(--font-mono); font-size: 0.64rem; letter-spacing: 0.28em; text-transform: uppercase; color: var(--accent); }

.nav { display: flex; gap: 2px; }
.nav a {
  position: relative; padding: 13px 18px;
  font-family: var(--font-mono);
  font-size: 0.8rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--dim);                /* was --faint: nav is primary, not chrome */
  transition: color .35s;
}
.nav a::after {
  content: ''; position: absolute; left: 18px; right: 18px; bottom: 4px;
  height: 1px; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform .45s var(--ease);
}
.nav a:hover { color: var(--text); }
.nav a:hover::after, .nav a[aria-current='page']::after { transform: scaleX(1); }
.nav a[aria-current='page'] { color: var(--text); }

.header__act { display: flex; align-items: center; gap: 14px; }
/* This audience calls. The number is a headline element, not a footnote -
   full size, always legible, and it survives every breakpoint below. */
.header__tel {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 46px; padding: 0 4px;
  font-family: var(--font-mono); font-size: 0.92rem; font-weight: 500; letter-spacing: .02em;
  color: var(--text); transition: color .3s;
}
.header__tel svg { color: var(--accent); }
.header__tel:hover { color: var(--accent-2); }

/* ---------------------------------------------------------- theme toggle */
/* Shows the mode you'd GET, not the one you're in - a sun on the dark site,
   a moon on the light one - and says so in its label, because an icon alone
   is ambiguous to anyone who hasn't memorised the convention. */
.theme-btn {
  flex-shrink: 0;
  width: 46px; height: 46px; padding: 0;
  display: grid; place-items: center;
  background: transparent; border: 1px solid var(--line-2); color: var(--text);
  border-radius: 50%;
  cursor: pointer;
  transition: border-color .3s, color .3s, background-color .3s;
}
.theme-btn:hover { border-color: var(--accent); color: var(--accent); }
.theme-btn__ico { position: relative; display: block; width: 20px; height: 20px; }
.theme-btn__ico svg {
  position: absolute; inset: 0; width: 20px; height: 20px;
  transition: opacity .35s var(--ease), transform .5s var(--ease);
}
.theme-btn .ico-sun  { opacity: 1; transform: none; }
.theme-btn .ico-moon { opacity: 0; transform: rotate(-50deg) scale(.55); }
:root[data-theme='light'] .theme-btn .ico-sun  { opacity: 0; transform: rotate(50deg) scale(.55); }
:root[data-theme='light'] .theme-btn .ico-moon { opacity: 1; transform: none; }
.theme-btn__txt { display: none; }

/* Drawer variant: labelled, full width, because a bare icon in a menu sheet
   gives the visitor nothing to read. */
.theme-btn--wide {
  width: 100%; height: auto; min-height: 52px;
  border-radius: var(--r-pill);
  display: flex; align-items: center; justify-content: center; gap: 12px;
  font-family: var(--font-mono); font-size: .8rem;
  letter-spacing: .12em; text-transform: uppercase;
}
.theme-btn--wide .theme-btn__txt { display: inline; }

.burger {
  display: none; position: relative;
  width: 46px; height: 46px; padding: 0;
  background: transparent; border: 1px solid var(--line-2); cursor: pointer;
  border-radius: 50%;
}
.burger i {
  position: absolute; left: 13px; width: 20px; height: 1px; background: var(--text);
  transition: transform .45s var(--ease), opacity .2s;
}
.burger i:nth-child(1) { top: 18px; }
.burger i:nth-child(2) { top: 23px; }
.burger i:nth-child(3) { top: 28px; }
.burger[aria-expanded='true'] i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.burger[aria-expanded='true'] i:nth-child(2) { opacity: 0; }
.burger[aria-expanded='true'] i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

/* --------------------------------------------------------------- drawer - */
.drawer {
  position: fixed; inset: 0; z-index: 99;
  background: var(--bg);
  padding: calc(var(--header-h) + 40px) 0 44px;
  display: flex; flex-direction: column;
  clip-path: inset(0 0 100% 0);
  visibility: hidden;
  transition: clip-path .7s var(--ease-out), visibility .7s;
}
.drawer.is-open { clip-path: inset(0 0 0 0); visibility: visible; }
.drawer__nav { border-top: 1px solid var(--line); }
.drawer__nav a {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 24px 0; border-bottom: 1px solid var(--line);
  font-family: var(--font-display); font-size: clamp(1.7rem, 8vw, 2.4rem); font-weight: 700;
  letter-spacing: -.03em;
  opacity: 0; transform: translateY(18px);
  transition: opacity .6s var(--ease), transform .6s var(--ease), color .3s;
}
.drawer.is-open .drawer__nav a { opacity: 1; transform: none; }
.drawer.is-open .drawer__nav a:nth-child(1) { transition-delay: .16s; }
.drawer.is-open .drawer__nav a:nth-child(2) { transition-delay: .22s; }
.drawer.is-open .drawer__nav a:nth-child(3) { transition-delay: .28s; }
.drawer.is-open .drawer__nav a:nth-child(4) { transition-delay: .34s; }
.drawer__nav a:hover { color: var(--accent-2); }
.drawer__nav a span { font-family: var(--font-mono); font-size: .78rem; letter-spacing: .14em; color: var(--faint); }
.drawer__foot { margin-top: auto; padding-top: 36px; display: grid; gap: 12px; }

/* ------------------------------------------------------------------ shot */
.shot { position: relative; overflow: hidden; background: var(--bg); border-radius: var(--r-lg); }
/* Full-bleed backdrops stay square - a rounded corner against the viewport
   edge just exposes the page behind it. */
.cta .shot, .page-hero .shot { border-radius: 0; }
.shot > img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 1.1s var(--ease), filter .8s;
}

/* ------------------------------------------------------------------ hero */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  /* Just under a full screen, so the next section peeks and the page reads as
     scrollable rather than as a wall. */
  min-height: 86svh;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding-top: calc(var(--header-h) + 40px);
}
.hero__stage { position: absolute; inset: -8% 0; z-index: -3; will-change: transform; }
.hero__stage img {
  width: 100%; height: 100%; object-fit: cover; object-position: center 62%;
  filter: var(--photo-hero);
  transform: scale(1.12);
  animation: heroIn 2.6s var(--ease-out) forwards;
}
@keyframes heroIn { to { transform: scale(1); } }

.hero__veil {
  position: absolute; inset: 0; z-index: -2;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--bg) 92%, transparent) 0%, color-mix(in srgb, var(--bg) 42%, transparent) 38%, color-mix(in srgb, var(--bg) 88%, transparent) 82%, var(--bg) 100%),
    linear-gradient(96deg, color-mix(in srgb, var(--bg) 95%, transparent) 0%, color-mix(in srgb, var(--bg) 28%, transparent) 62%);
}
.hero__spot {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  opacity: 0; transition: opacity .8s;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%), var(--glow), transparent 70%);
}
.hero:hover .hero__spot { opacity: .5; }

.hero__in { padding-bottom: var(--s6); }
.hero h1 { margin-bottom: var(--s4); max-width: 15ch; }
.hero h1 em {
  font-style: normal;
  background: linear-gradient(100deg, var(--accent-2), var(--accent) 60%, var(--accent-deep));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero .lead { color: color-mix(in srgb, var(--text) 78%, transparent); }
.hero__cta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3) var(--s4); margin-top: var(--s5); }

.hero__stats {
  position: relative;
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--s4) var(--s6);
  margin-top: var(--s6); padding-top: var(--s4);
}
.hero__stats div { padding: 0; }
.hero__stats dt {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(1.5rem, 2.2vw, 2rem); letter-spacing: -.03em; line-height: 1;
}
.hero__stats dt i { font-style: normal; color: var(--accent); }
.hero__stats dd { margin: var(--s2) 0 0; font-family: var(--font-mono); font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; color: var(--faint); }

.scroll-hint {
  position: absolute; right: 0; bottom: clamp(40px, 5vw, 64px); z-index: 1;
  display: flex; align-items: center; gap: 12px;
  font-family: var(--font-mono); font-size: .76rem; letter-spacing: .16em; text-transform: uppercase; color: var(--faint);
}
.scroll-hint i { display: block; width: 44px; height: 1px; background: var(--line-2); overflow: hidden; position: relative; }
.scroll-hint i::after { content: ''; position: absolute; inset: 0; background: var(--accent); animation: sweep 2.4s var(--ease) infinite; }
@keyframes sweep { 0% { transform: translateX(-100%); } 60%, 100% { transform: translateX(100%); } }

/* ------------------------------------------------------------- page hero */
.page-hero {
  position: relative; isolation: isolate; overflow: hidden;
  padding: calc(var(--header-h) + 110px) 0 clamp(64px, 8vw, 110px);
  border-bottom: 1px solid var(--line);
}
.page-hero .shot { position: absolute; inset: 0; z-index: -2; }
.page-hero .shot > img { filter: var(--photo-page); }
.page-hero__veil {
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, color-mix(in srgb, var(--bg) 86%, transparent), color-mix(in srgb, var(--bg) 70%, transparent) 45%, var(--bg));
}
.page-hero h1 { font-size: clamp(2.6rem, 6vw, 4.8rem); }
.crumbs { display: flex; align-items: center; gap: 10px; margin-bottom: 26px; font-family: var(--font-mono); font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; color: var(--faint); }
.crumbs a { display: inline-block; padding: 9px 0; }   /* was a 21px target */
.crumbs a:hover { color: var(--accent-2); }

/* ------------------------------------------------------------ cert strip */
.certs { border-block: 1px solid var(--line); background: var(--panel); padding: 22px 0; }
.certs__in { display: flex; align-items: center; gap: 32px; }
.certs__label { flex-shrink: 0; margin: 0; padding-right: 32px; border-right: 1px solid var(--line); }
.marquee {
  flex: 1; min-width: 0; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 92%, transparent);
}
/* Credentials are the single thing this audience actually scrutinises before
   calling a contractor, so the strip runs slowly and sits at full opacity
   rather than waiting for a hover that may never come.
   --shift and --spin are written by main.js from the measured width of one
   chip set: the shift is exactly one set, and the duration is that width at a
   fixed px/sec, so the speed does not change with the viewport. The fallbacks
   keep it moving if the script never runs. */
.marquee__track {
  display: flex; align-items: center; gap: 14px; width: max-content;
  animation: scrollX var(--spin, 64s) linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
@keyframes scrollX { to { transform: translateX(var(--shift, calc(-50% - 7px))); } }
.chip {
  flex-shrink: 0; display: grid; place-items: center;
  height: 64px; padding: 0 20px;
  background: var(--chip); opacity: .88; border-radius: var(--r-sm);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
}
.chip img { height: 42px; width: auto; }
.chip:hover { opacity: 1; transform: translateY(-2px); }

/* ----------------------------------------------------------------- intro */
.intro__stmt { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.4rem, 2.4vw, 2.1rem); line-height: 1.2; letter-spacing: -.028em; color: var(--text); max-width: 26ch; margin: 0; }

/* Dividers come from borders, not grid gaps, so the first cell can stay flush
   with the page margin while the inner cells get padding on BOTH sides.
   With gap-dividers, columns 2+ printed straight onto the rule. */
/* Columns are separated by space, not by rules. Boxing each cell in hairlines
   made these read as a spreadsheet; the only line left is a single top edge
   that fades out rather than closing the shape. */
.intro__grid {
  position: relative;
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--s5) var(--s6);
  margin-top: var(--s7); padding-top: var(--s5);
}
.intro__grid::before, .steps::before, .hero__stats::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 1px;
  background: linear-gradient(90deg, var(--line-2), transparent 88%);
}
.intro__cell { padding: 0; }
.intro__cell h3 { margin: var(--s3) 0 var(--s2); }
.intro__cell p { font-size: .95rem; margin: 0; max-width: 34ch; }

/* --------------------------------------------------------- service rows - */
.rows { border-top: 1px solid var(--line); }
.rows__item { border-bottom: 1px solid var(--line); }
.rows__link {
  display: grid; grid-template-columns: 60px 1fr auto 32px; align-items: center; gap: var(--s3);
  padding: var(--s4) 0;
  transition: opacity .5s var(--ease), padding .5s var(--ease);
}
/* Dimming the unhovered rows to .3 made the list feel like it was breaking
   under the pointer. .62 still directs the eye without hiding anything. */
.rows:hover .rows__item:not(:hover) .rows__link { opacity: .62; }
.rows__link:hover { padding-left: 14px; }
.rows__no { font-family: var(--font-mono); font-size: .76rem; letter-spacing: .12em; color: var(--faint); }
.rows__title { font-family: var(--font-display); font-size: clamp(1.15rem, 2vw, 1.7rem); font-weight: 700; letter-spacing: -.026em; transition: color .4s; }
.rows__link:hover .rows__title { color: var(--accent-2); }
.rows__meta { font-family: var(--font-mono); font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; color: var(--faint); text-align: right; }
.rows__arrow { justify-self: end; color: var(--faint); transition: transform .45s var(--ease), color .4s; }
.rows__link:hover .rows__arrow { transform: translateX(6px); color: var(--accent); }

/* ------------------------------------------- service detail (services) -- */
.svc {
  display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr);
  gap: clamp(40px, 6vw, 88px); align-items: center;
  padding: clamp(64px, 9vw, 124px) 0;
  border-bottom: 1px solid var(--line);
}
.svc:last-child { border-bottom: 0; }
.svc:nth-child(even) .svc__media { order: -1; }
.svc__media { position: relative; }
.svc__media .shot { aspect-ratio: 5 / 4; }
.svc__media .shot > img { filter: var(--photo-svc); transform: scale(1.04); }
.svc__media .shot:hover > img { transform: scale(1); filter: var(--photo-svc-h); }
/* Must come after the two rules above - same specificity, so order decides. */
.svc__media--colour .shot > img { filter: var(--photo-keep); }
.svc__media--colour .shot:hover > img { filter: none; }

/* --------------------------------------------------------- client logos --
   The supplied artwork is transparent, but nearly all of it is DARK ink -
   navy, black, charcoal - so it would vanish against this page. Each logo
   therefore keeps the same light plate the certification chips use. That is
   also the only way to show five other companies' marks without altering
   their colours, which a brand guideline would not thank you for.

   The row mixes wide wordmarks (Weidner, ~4:1) with stacked lockups (Avenue,
   Equium) and a round badge (Rimrock). Constraining height alone would shrink
   the wordmarks to a thread; constraining width alone would tower the stacked
   ones. So each plate is a fixed box and `object-fit: contain` lets every
   lockup fill whichever axis it runs out of first. */
/* Spans the whole service row rather than sitting in the copy column, so the
   logos run right across under the photograph and get room to be read. */
.clients {
  grid-column: 1 / -1;
  margin-top: clamp(40px, 5vw, 64px);
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
}
.clients__label { margin: 0 0 var(--s4); color: var(--faint); }
/* A grid, not a flex row: fixed-width plates overflowed and dropped one logo
   onto a line of its own, which reads as a mistake. auto-fit shares the width
   evenly - five across on desktop, fewer as it narrows. */
.clients__row {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(10px, 1.4vw, 18px);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.clients__row li {
  display: grid; place-items: center;
  height: clamp(92px, 9vw, 116px); padding: 16px 22px;
  background: var(--chip); opacity: .92; border-radius: var(--r-md);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
}
.clients__row li:hover { opacity: 1; transform: translateY(-3px); }
/* The height cap is in pixels on purpose. A percentage resolves against the
   grid track, which the image is itself sizing, so it is treated as none -
   the round Rimrock badge and the stacked Equium lockup both burst out of
   their plates. This is the plate height less its padding. */
.clients__row img {
  max-width: 100%; max-height: clamp(60px, 6vw, 84px);
  width: auto; height: auto;
}
.svc__no { font-family: var(--font-mono); font-size: .82rem; letter-spacing: .16em; color: var(--accent); margin-bottom: var(--s3); }
.svc h2 { margin-bottom: var(--s3); }
.svc p { max-width: 48ch; }

.bullets { display: grid; gap: var(--s2); margin-top: var(--s4); padding-top: var(--s4); border-top: 1px solid var(--line); }
.bullets li { display: flex; gap: var(--s2); font-size: .94rem; color: var(--dim); }
.bullets li::before { content: ''; flex-shrink: 0; width: 14px; height: 1px; background: var(--accent); margin-top: 12px; }

.pills { display: flex; flex-wrap: wrap; gap: var(--s1); margin-top: var(--s4); }
.pill {
  font-family: var(--font-mono); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--faint); border: 1px solid var(--line-2); padding: 7px 14px; border-radius: 100px;
}

/* -------------------------------------------------------------- founders --
   Closes the "Why Luminova" argument with the people making it. The photo
   column is the narrower one: the shot is 4:5 portrait, and at half width it
   would tower over the copy beside it. */
.founders {
  display: grid; grid-template-columns: minmax(0, .72fr) minmax(0, 1.28fr);
  gap: clamp(28px, 4vw, 56px); align-items: center;
  margin-top: clamp(48px, 6vw, 80px);
  padding-top: clamp(40px, 5vw, 64px);
  border-top: 1px solid var(--line);
}
.founders__fig { margin: 0; }
.founders__fig .shot { aspect-ratio: 4 / 5; box-shadow: var(--glass-shadow); }
/* Faces carry the lighter grading. The house treatment is built for buildings;
   at grayscale(.7) people read as severe, which is the opposite of the point
   of putting them here. */
.founders__fig .shot > img { filter: var(--photo-keep); }
.founders__copy h3 { margin: var(--s2) 0 var(--s3); font-size: clamp(1.3rem, 2.2vw, 1.75rem); }
.founders__copy p:last-child { color: var(--dim); max-width: 46ch; margin: 0; }

/* ------------------------------------------------------------------ work */
/* A contact sheet, not an editorial spread. The old asymmetric 12-column
   layout stacked to 1795px on its own; this reads faster and costs a third. */
.work { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s4); }
.work__item { grid-column: auto; }
.work__fig { position: relative; margin: 0; }
.work__fig .shot { aspect-ratio: 4 / 3; }
/* Wipe-in reveal driven by the scroll observer. */
.work__fig .shot::after {
  content: ''; position: absolute; inset: 0; z-index: 4; background: var(--bg);
  transform-origin: right;
  transition: transform 1.05s var(--ease-out);
}
[data-reveal].is-in .work__fig .shot::after { transform: scaleX(0); }
.work__fig .shot > img {
  filter: var(--photo);
  transform: scale(1.04);
}
.work__fig .shot:hover > img { transform: scale(1); filter: var(--photo-hover); }
/* Same specificity as the rule above, so this has to stay after it.
   The grading unifies a GRID of photos; with one real project between two held
   slots there is no grid to unify, and the only proof of work on the page was
   being buried. Touch devices never get the hover that would lift it, either. */
.work__fig--colour .shot > img { filter: var(--photo-keep); }
.work__fig--colour .shot:hover > img { filter: none; }
.work__cap { display: flex; justify-content: space-between; gap: var(--s2); align-items: baseline; margin-top: var(--s2); padding-top: var(--s2); border-top: 1px solid var(--line); }
.work__cap h3 { margin: 0; font-size: 1.02rem; }
.work__cap .mono { font-size: .74rem; }

/* --------------------------------------------------------------- process */
.steps {
  position: relative;
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: var(--s5) var(--s5);
  padding-top: var(--s5);
}
.steps__item { padding: 0; }
.steps__item h3 { margin: var(--s3) 0 var(--s2); }
.steps__item p { font-size: 1rem; margin: 0; max-width: 32ch; }
.steps__no { font-family: var(--font-mono); font-size: .82rem; letter-spacing: .16em; color: var(--accent); }

/* -------------------------------------------------------------- cta band */
.cta { position: relative; isolation: isolate; overflow: hidden; padding: clamp(96px, 12vw, 170px) 0; text-align: center; }
.cta .shot { position: absolute; inset: 0; z-index: -2; }
.cta .shot > img { filter: var(--photo-cta); }
.cta__veil { position: absolute; inset: 0; z-index: -1; background: linear-gradient(180deg, var(--bg), color-mix(in srgb, var(--bg) 82%, transparent) 45%, var(--bg)); }
.cta h2 { max-width: 16ch; margin-inline: auto; }
.cta .hero__cta { justify-content: center; }

/* ----------------------------------------------------------------- forms */
.contact-grid { display: grid; grid-template-columns: minmax(0,1.15fr) minmax(0,.85fr); gap: var(--s7); align-items: start; }
.form { display: grid; gap: var(--s4); }
.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
.field { display: grid; gap: var(--s1); }
/* Labels are always visible and sized to be read, never placeholder-only -
   a placeholder that vanishes on focus is the classic way a form loses
   someone who looks away mid-answer. */
.field label { font-family: var(--font-mono); font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; color: var(--dim); }
.field label i { font-style: normal; color: var(--accent); }
.field input, .field select, .field textarea {
  width: 100%; min-height: 52px; padding: 15px 0;
  background: transparent; color: var(--text);
  border: 0; border-bottom: 1px solid var(--line-2);
  font-family: var(--font-body); font-size: 1.06rem;
  transition: border-color .4s var(--ease), box-shadow .4s var(--ease);
}
/* A 1px hairline is not enough feedback. The focused field gets a visible
   accent underline at 2px so it's obvious where typing will land. */
.field input:focus, .field select:focus, .field textarea:focus {
  box-shadow: 0 1px 0 0 var(--accent);
}
.field textarea { resize: vertical; min-height: 140px; }
/* The chevron is a data URI, so its stroke colour can't read a custom
   property. Two rules is the robust answer - anything cleverer (a masked
   pseudo-element) has to be positioned by hand and drifts the moment a
   validation message expands the field. */
.field select {
  appearance: none; padding-right: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23A9B1BE' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right center;
}
:root[data-theme='light'] .field select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2345505F' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
.field select option { background: var(--opt-bg); color: var(--text); }
.field input::placeholder, .field textarea::placeholder { color: var(--faint); }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-bottom-color: var(--accent); }
.field .err { font-size: .9rem; color: var(--err); display: none; }
.field.bad input, .field.bad select, .field.bad textarea { border-bottom-color: var(--err-line); box-shadow: 0 1px 0 0 var(--err-line); }
.field.bad .err { display: block; }
.form__note { font-family: var(--font-mono); font-size: .78rem; letter-spacing: .06em; color: var(--faint); margin: 0; }
.form__status { display: none; padding: 18px 20px; border: 1px solid var(--accent); border-radius: var(--r-md); color: var(--accent-2); font-size: 1.02rem; }
.form__status.on { display: block; }
/* Failure has to look different from success at a glance - a lost lead is the
   most expensive thing this page can do. */
.form__status--bad { border-color: var(--err-line); color: var(--err); }

.info { border-top: 1px solid var(--line); }
.info li { display: flex; justify-content: space-between; gap: 24px; align-items: baseline; padding: 22px 0; border-bottom: 1px solid var(--line); }
/* Phone and email are the payoff of this page - sized to be read and tapped,
   not to match the surrounding body copy. */
.info a, .info p { margin: 0; color: var(--text); font-size: 1.08rem; }
.info a { display: inline-block; padding: 7px 0; }
.info a:hover { color: var(--accent-2); }
.hours li.today { color: var(--accent-2); }
.hours li.today .mono { color: var(--accent); }


/* ------------------------------------------------------------- accordion */
.acc { border-top: 1px solid var(--line); }
.acc__item { border-bottom: 1px solid var(--line); }
.acc__btn {
  width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 26px;
  padding: 28px 0; background: transparent; border: 0; cursor: pointer;
  color: var(--text); text-align: left;
  font-family: var(--font-display); font-size: clamp(1.1rem, 1.7vw, 1.32rem); font-weight: 600; letter-spacing: -.015em;
  transition: color .35s;
}
.acc__btn:hover { color: var(--accent-2); }
.acc__ico { flex-shrink: 0; width: 36px; height: 36px; display: grid; place-items: center; border: 1px solid var(--line-2); border-radius: 50%; transition: transform .5s var(--ease), background-color .35s, color .35s, border-color .35s; }
.acc__btn[aria-expanded='true'] .acc__ico { transform: rotate(135deg); background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.acc__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .55s var(--ease); }
.acc__panel > div { overflow: hidden; }
.acc__panel.on { grid-template-rows: 1fr; }
.acc__panel p { padding-bottom: 28px; margin: 0; max-width: 68ch; }

/* ---------------------------------------------------------------- footer */
.site-footer { background: var(--panel); border-top: 1px solid var(--line); padding: var(--s7) 0 0; }
.site-footer__grid { display: grid; grid-template-columns: 1.8fr 1fr 1fr; gap: var(--s6); padding-bottom: var(--s6); }
.site-footer h3 { font-family: var(--font-mono); font-size: .78rem; font-weight: 500; letter-spacing: .14em; text-transform: uppercase; color: var(--faint); margin-bottom: var(--s4); }
.flinks { display: grid; gap: var(--s2); }
.flinks a, .flinks li { color: var(--dim); font-size: 1rem; transition: color .3s; }
.flinks a { display: inline-block; padding: 7px 0; }   /* 42px tall targets */
.flinks a:hover { color: var(--accent-2); }
.site-footer__brand p { font-size: 1rem; max-width: 36ch; margin-top: var(--s3); }
.socials { display: flex; gap: var(--s1); margin-top: var(--s4); }
.socials a { width: 42px; height: 42px; display: grid; place-items: center; border: 1px solid var(--line-2); border-radius: 50%; color: var(--dim); transition: background-color .35s var(--ease), border-color .35s var(--ease), color .35s var(--ease), transform .35s var(--ease); }
.socials a:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); transform: translateY(-3px); }
.site-footer__bar { border-top: 1px solid var(--line); padding: var(--s4) 0 var(--s5); display: flex; flex-wrap: wrap; gap: var(--s2); justify-content: space-between; }
/* 404 carries the copyright bar alone, with no link columns above it - so it
   drops the footer's top padding and the bar's rule, which would otherwise
   float with nothing on the other side of it. */
.site-footer--bare { padding-top: 0; }
.site-footer--bare .site-footer__bar { border-top: 0; }

/* ---------------------------------------------------------------- cookie */
.cookie {
  position: fixed; inset: auto 0 0 0; z-index: 150;
  background: color-mix(in srgb, var(--panel) 94%, transparent);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--line);
  padding: 18px 0;
  transform: translateY(101%);
  transition: transform .7s var(--ease-out);
}
.cookie.on { transform: none; }
/* The notice sits above the drawer (z-index 150 vs 99), so on a first visit it
   covered the drawer's own phone and theme controls. It steps aside while the
   menu is open and comes back when it closes. */
body.is-locked .cookie { transform: translateY(101%); }
.cookie__in { display: flex; align-items: center; justify-content: space-between; gap: 26px; }
.cookie p { margin: 0; font-size: .96rem; }
.cookie__act { display: flex; gap: 10px; flex-shrink: 0; }
.cookie .btn { min-height: 46px; padding: 12px 24px; font-size: .76rem; }

.to-top {
  position: fixed; right: 28px; bottom: 90px; z-index: 90;
  width: 46px; height: 46px; display: grid; place-items: center;
  background: transparent; border: 1px solid var(--line-2); color: var(--text);
  border-radius: 50%;
  cursor: pointer; opacity: 0; visibility: hidden; transform: translateY(10px);
  /* Explicit, not `all`. A theme flip swaps ~20 custom properties at once, and
     `transition: all` makes every element watching it animate things nobody
     asked to animate. */
  transition: opacity .45s var(--ease), transform .45s var(--ease), visibility .45s,
              background-color .45s var(--ease), border-color .45s var(--ease), color .45s var(--ease);
}
.to-top.on { opacity: 1; visibility: visible; transform: none; }
.to-top:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

/* ---------------------------------------------------------------- motion */
.word { display: inline-block; overflow: hidden; vertical-align: top; }
.word > i {
  display: inline-block; font-style: inherit;
  transform: translateY(105%);
  transition: transform .95s var(--ease-out);
  transition-delay: var(--wd, 0ms);
}
.is-in .word > i { transform: none; }

[data-reveal] {
  opacity: 0; transform: translateY(30px);
  transition: opacity 1s var(--ease-out), transform 1s var(--ease-out);
  transition-delay: var(--d, 0ms);
}
[data-reveal].is-in { opacity: 1; transform: none; }
[data-reveal].work__item { transform: none; opacity: 1; }

/* Native scroll-driven depth: imagery drifts toward the viewer as it passes.
   Progressive enhancement - browsers without scroll timelines just skip it. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .work__fig .shot, .svc__media .shot {
      animation: depth linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 55%;
    }
    @keyframes depth {
      from { transform: perspective(1400px) translate3d(0, 26px, -70px); }
      to   { transform: perspective(1400px) translate3d(0, 0, 0); }
    }
  }
}

/* Cross-document page transitions, where supported. */
@view-transition { navigation: auto; }
.site-header { view-transition-name: site-header; }
::view-transition-old(root) { animation: vt-out .32s var(--ease) both; }
::view-transition-new(root) { animation: vt-in .42s var(--ease-out) both; }
@keyframes vt-out { to { opacity: 0; transform: translateY(-10px); } }
@keyframes vt-in { from { opacity: 0; transform: translateY(14px); } }

/* ================================================================= INTRO ==
   One-time layered logo build. Hidden unless the inline head script adds
   .intro-on to <html>, so the site is never blocked when JS is off. */
.intro { display: none; }
html.intro-on .intro {
  position: fixed; inset: 0; z-index: 400;
  display: grid; place-items: center;
  background: transparent;
  overflow: hidden;
}
html.intro-on body { overflow: hidden; }

.intro__panel {
  position: absolute; left: 0; right: 0; height: 50.5%;
  background: #000;
  transition: transform 1.15s var(--ease-out);
}
.intro__panel--t { top: 0; }
.intro__panel--b { bottom: 0; }
.intro.is-out .intro__panel--t { transform: translateY(-100%); }
.intro.is-out .intro__panel--b { transform: translateY(100%); }

.intro__in { position: relative; z-index: 1; display: grid; justify-items: center; gap: 22px; }

.intro__rule {
  width: 190px; height: 1px; background: var(--accent);
  transform: scaleX(0);
  transition: transform .6s var(--ease-out), width 1.1s var(--ease-out), opacity .8s ease;
}
.intro.is-ready .intro__rule { transform: scaleX(1); }
.intro.is-out .intro__rule { width: 100vw; opacity: 0; }

.intro__logo { position: relative; width: min(62vw, 360px); transform-origin: center center; will-change: transform; }
.lum { position: relative; display: block; width: 100%; aspect-ratio: 900 / 695; }
.lum__layer {
  position: absolute; inset: 0; width: 100%; height: 100%;
  filter: saturate(1.12) contrast(1.06);
  backface-visibility: hidden; will-change: opacity, transform; opacity: 0;
}
.intro.is-ready .lum__text   { animation: lum-text-in   1.05s cubic-bezier(.26,.62,.2,1) .10s both; }
.intro.is-ready .lum__base   { animation: lum-base-in   1.10s cubic-bezier(.26,.62,.2,1) .65s both; }
.intro.is-ready .lum__towers { animation: lum-towers-in 1.15s cubic-bezier(.26,.62,.2,1) 1.05s both; }
.intro.is-ready .lum { animation: lum-settle 2.9s cubic-bezier(.18,.62,.18,1) .05s both; }

@keyframes lum-text-in {
  from { opacity: 0; transform: perspective(1800px) translate3d(0,1.4%,-190px); filter: saturate(.55) contrast(1.02) brightness(.45); }
  45%  { opacity: .86; filter: saturate(.85) contrast(1.04) brightness(.7); }
  to   { opacity: 1; transform: perspective(1800px) translate3d(0,0,0); filter: saturate(1.12) contrast(1.06) brightness(1); }
}
@keyframes lum-base-in {
  from { opacity: 0; filter: saturate(.55) contrast(1.02) brightness(.45); }
  18%  { opacity: .55; filter: saturate(.65) contrast(1.03) brightness(.55); }
  55%  { opacity: 1; filter: saturate(.85) contrast(1.04) brightness(.72); }
  to   { opacity: 1; filter: saturate(1.12) contrast(1.06) brightness(1); }
}
@keyframes lum-towers-in {
  from { opacity: 0; transform: perspective(1800px) translate3d(0,0,-280px); filter: saturate(.55) contrast(1.02) brightness(.45); }
  45%  { opacity: .8; filter: saturate(.85) contrast(1.04) brightness(.68); }
  to   { opacity: 1; transform: perspective(1800px) translate3d(0,0,0); filter: saturate(1.12) contrast(1.06) brightness(1); }
}
@keyframes lum-settle { from { transform: perspective(2200px) scale(1.028); } to { transform: perspective(2200px) scale(1); } }

.lum__sweep {
  position: absolute; inset: 0; overflow: hidden; pointer-events: none;
  -webkit-mask-image: var(--logo); mask-image: var(--logo);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.lum__spec, .lum__glint { position: absolute; inset: -25% -14%; will-change: transform; opacity: 0; }
.lum__spec { mix-blend-mode: overlay; background: linear-gradient(102deg, rgba(255,255,255,0) 30%, rgba(255,250,230,.42) 43%, rgba(255,255,255,.92) 50%, rgba(255,250,230,.42) 57%, rgba(255,255,255,0) 70%); }
.lum__glint { mix-blend-mode: screen; background: linear-gradient(102deg, rgba(255,255,255,0) 45%, rgba(255,246,214,.5) 49.2%, rgba(255,255,255,.95) 50%, rgba(255,246,214,.5) 50.8%, rgba(255,255,255,0) 55%); }
.intro.is-ready .lum__spec  { animation: lum-specular 1.05s cubic-bezier(.42,.06,.5,1) 1.50s both; }
.intro.is-ready .lum__glint { animation: lum-glint    .80s cubic-bezier(.38,.04,.5,1) 1.65s both; }
@keyframes lum-specular { 0% { transform: translate3d(-120%,0,0); opacity: 0; } 12%,88% { opacity: 1; } 100% { transform: translate3d(120%,0,0); opacity: 0; } }
@keyframes lum-glint    { 0% { transform: translate3d(-135%,0,0); opacity: 0; } 12%,88% { opacity: 1; } 100% { transform: translate3d(135%,0,0); opacity: 0; } }

.lum__star { position: absolute; left: 86.5%; top: 27%; width: 50%; aspect-ratio: 1/1; pointer-events: none; }
.lum__star > span { position: absolute; left: 0; top: 0; transform-origin: 50% 50%; opacity: 0; }
.lum__star .s-h { width: 88%; height: 3px; background: linear-gradient(90deg, rgba(198,160,60,0) 0%, rgba(214,180,96,.85) 34%, #fff8e2 50%, rgba(214,180,96,.85) 66%, rgba(198,160,60,0) 100%); filter: drop-shadow(0 0 8px rgba(214,180,96,.7)); }
.lum__star .s-v { width: 3px; height: 40%; background: linear-gradient(rgba(198,160,60,0) 0%, rgba(214,180,96,.8) 34%, #fff8e2 50%, rgba(214,180,96,.8) 66%, rgba(198,160,60,0) 100%); filter: drop-shadow(0 0 8px rgba(214,180,96,.7)); }
.lum__star .s-d1, .lum__star .s-d2 { width: 24%; height: 2px; background: linear-gradient(90deg, rgba(198,160,60,0) 0%, rgba(226,196,124,.85) 50%, rgba(198,160,60,0) 100%); }
.lum__star .s-core { width: 16%; aspect-ratio: 1/1; border-radius: 50%; background: radial-gradient(closest-side, #fffaeb 0%, rgba(233,201,124,.9) 26%, rgba(205,170,80,.35) 52%, rgba(205,170,80,0) 76%); filter: drop-shadow(0 0 14px rgba(214,180,96,.65)); }
.intro.is-ready .lum__star .s-h    { animation: lum-star-h    .95s cubic-bezier(.2,.7,.25,1) 2.05s both; }
.intro.is-ready .lum__star .s-v    { animation: lum-star-v    .95s cubic-bezier(.2,.7,.25,1) 2.05s both; }
.intro.is-ready .lum__star .s-d1   { animation: lum-star-d1   .80s cubic-bezier(.2,.7,.25,1) 2.10s both; }
.intro.is-ready .lum__star .s-d2   { animation: lum-star-d2   .80s cubic-bezier(.2,.7,.25,1) 2.10s both; }
.intro.is-ready .lum__star .s-core { animation: lum-star-core .95s cubic-bezier(.2,.7,.25,1) 2.05s both; }
@keyframes lum-star-core { from { opacity:0; transform: translate(-50%,-50%) scale(.2);} 22%{opacity:1;transform:translate(-50%,-50%) scale(1.1);} 55%{opacity:.75;transform:translate(-50%,-50%) scale(.9);} to{opacity:0;transform:translate(-50%,-50%) scale(1.35);} }
@keyframes lum-star-h { from{opacity:0;transform:translate(-50%,-50%) scaleX(.06);} 20%{opacity:1;transform:translate(-50%,-50%) scaleX(1);} 60%{opacity:.5;transform:translate(-50%,-50%) scaleX(.72);} to{opacity:0;transform:translate(-50%,-50%) scaleX(.2);} }
@keyframes lum-star-v { from{opacity:0;transform:translate(-50%,-50%) scaleY(.06);} 24%{opacity:.95;transform:translate(-50%,-50%) scaleY(1);} 60%{opacity:.45;transform:translate(-50%,-50%) scaleY(.66);} to{opacity:0;transform:translate(-50%,-50%) scaleY(.18);} }
@keyframes lum-star-d1 { from{opacity:0;transform:translate(-50%,-50%) rotate(45deg) scaleX(.06);} 26%{opacity:.7;transform:translate(-50%,-50%) rotate(45deg) scaleX(.8);} to{opacity:0;transform:translate(-50%,-50%) rotate(45deg) scaleX(.16);} }
@keyframes lum-star-d2 { from{opacity:0;transform:translate(-50%,-50%) rotate(-45deg) scaleX(.06);} 26%{opacity:.7;transform:translate(-50%,-50%) rotate(-45deg) scaleX(.8);} to{opacity:0;transform:translate(-50%,-50%) rotate(-45deg) scaleX(.16);} }

.intro__glow {
  position: absolute; inset: -45% -30%; z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side, rgba(201,162,74,.22), transparent 72%);
  opacity: 0; transition: opacity 1s ease .35s;
}
.intro.is-ready .intro__glow { opacity: 1; }
.intro.is-out .intro__glow { opacity: 0; transition-delay: 0s; }

.intro__logo.is-flying { transition: transform 1.15s var(--ease-out); }
html.intro-on .site-header .brand__mark { opacity: 0; }

@keyframes sheen { 0% { transform: translateX(-140%); opacity: 0; } 12% { opacity: 1; } 88% { opacity: 1; } 100% { transform: translateX(140%); opacity: 0; } }

.intro__count {
  margin: 0;
  font-family: var(--font-mono); font-size: .8rem; letter-spacing: .2em; color: var(--faint);
  opacity: 0; transition: opacity .6s ease;
}
.intro.is-ready .intro__count { opacity: 1; transition-delay: .5s; }
.intro.is-out .intro__count { opacity: 0; transition-duration: .3s; transition-delay: 0s; }

/* Always a visible way out of the splash. Injected by main.js so it exists on
   every page without duplicating markup three times. */
.intro__skip {
  position: fixed; right: clamp(18px, 3vw, 34px); bottom: clamp(18px, 3vw, 34px);
  z-index: 2;
  min-height: 46px; padding: 12px 22px;
  background: transparent; border: 1px solid var(--line-2); color: var(--dim);
  border-radius: var(--r-pill);
  font-family: var(--font-mono); font-size: .78rem; letter-spacing: .12em; text-transform: uppercase;
  cursor: pointer;
  opacity: 0; transition: opacity .5s ease, color .3s, border-color .3s;
}
.intro__skip.on { opacity: 1; }
.intro__skip:hover { color: var(--accent-2); border-color: var(--accent); }
.intro.is-out .intro__skip { opacity: 0; }

/* ------------------------------------------------------------ responsive */
@media (max-width: 1400px) { .rail { display: none; } }

@media (max-width: 1180px) {
  .nav { display: none; }
  .burger { display: block; }
  :root { --header-h: 74px; }
  /* The phone number stays. It used to vanish here and not come back until
     720px, which blanked it across every tablet and small laptop. */
}

@media (max-width: 1000px) {
  /* Nothing to re-point any more: the grids separate with gap, so they just
     reflow. This block used to carry three rules shuffling dividers around. */
  .intro__grid, .steps { grid-template-columns: repeat(2, 1fr); }
  .contact-grid { grid-template-columns: 1fr; }
  .work { grid-template-columns: repeat(2, 1fr); }
  .site-footer__grid { grid-template-columns: 1fr 1fr; gap: 40px; }
  .svc { grid-template-columns: 1fr; }
  .svc:nth-child(even) .svc__media { order: 0; }
  /* Held as two columns this squeezed the portrait to 109px beside a 194px
     ragged text column. Stack, and cap the photo so it does not dominate. */
  .founders { grid-template-columns: 1fr; }
  .founders__fig { max-width: 340px; }
}

@media (max-width: 720px) {
  .work { grid-template-columns: 1fr; }
  /* The label and the strip cannot share a 375px row: the label took ~250px of
     it and left the marquee 82px, about one and a half logos. Stack them and
     the credentials get the full width. */
  .certs__in { display: block; }
  .certs__label { padding: 0 0 14px; border-right: 0; }
  /* Stacked: gap alone, no rules between rows. */
  .intro__grid, .steps { grid-template-columns: 1fr; }
  .intro__cell p, .steps__item p { max-width: none; }
  /* Stats become single-line readouts rather than three stacked blocks. */
  .hero__stats { grid-template-columns: 1fr; gap: var(--s3); }
  .hero__stats div {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
  }
  .hero__stats dd { margin: 0; text-align: right; }
  .scroll-hint { display: none; }
  .rows__link { grid-template-columns: 50px 1fr 28px; gap: 16px; padding: 26px 0; }
  .rows__meta { display: none; }
  .form__row { grid-template-columns: 1fr; }
  .site-footer__grid { grid-template-columns: 1fr; }
  .cookie__in { flex-direction: column; align-items: flex-start; gap: 16px; }
  .hero__cta .btn { width: 100%; justify-content: center; }
  /* Hidden on phones. It floats over the content column, and at this width
     that column IS the page - so the circle parks on top of whichever
     full-width link happens to be under it and steals the tap. The header is
     sticky and flick-scrolling is free on touch, so it earns very little here. */
  .to-top { display: none; }
  .header__act > .btn { display: none; }
  /* Three 46px buttons plus the wordmark overflow a 375px header, so the
     toggle hands off to its labelled twin in the drawer here. */
  .header__act > .theme-btn { display: none; }
  /* Tap-to-call: the number collapses to a labelled icon target, never away. */
  .header__tel { display: grid; place-items: center; width: 48px; height: 48px; padding: 0; border: 1px solid var(--accent); border-radius: 50%; color: var(--accent); }
  .header__tel span { display: none; }
}


@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
  .word > i { transform: none; }
  .intro { display: none !important; }
  .cur { display: none !important; }
}

@media (hover: none), (pointer: coarse) {
  .cur { display: none !important; }
}

@media print {
  .site-header, .drawer, .cookie, .to-top, .certs, .cta, .grain, .scroll-bar, .rail, .cur { display: none !important; }
  body { background: #fff; color: #000; }
}