/* TRIFORGE — shared stylesheet. Single external file: multi-page site, so it
   caches once and is reused. NO web fonts (see CLAUDE.md — a font CDN was the
   original cause of slow first paint). Rationale for the palette, the reveal
   fallback and the theme system lives in CLAUDE.md, not in bytes shipped here. */

:root {
  /* ONE brand hue (indigo) + ONE support (teal) + ONE semantic state.
     Each is used as TEXT somewhere, so each clears AA 4.5:1 on --bg AND
     --surface. Ratios and the gradient sweep are tabulated in CLAUDE.md. */
  --accent:       #4338ca;
  --accent-hover: #3730a3;
  --accent-2:     #0f766e;
  --danger:       #be123c;   /* errors only — NOT a third brand colour */

  /* Surfaces — cool neutral, no longer tinted toward the accent. */
  --bg:        #f7f9fc;
  --surface:   #ffffff;
  --surface-2: #eef2f8;

  /* --border is a decorative hairline. --border-strong (3.66:1) is for anything
     whose edge IS the control — form fields, focus rings. WCAG 1.4.11. */
  --border:        #dde4ee;
  --border-strong: #78838f;

  /* Text */
  --text:  #131820;   /* 16.88:1 on --bg */
  --muted: #545f6d;   /*  6.16:1 on --bg */
  --on-accent: #ffffff;

  /* Two stops, one cool family. Worst interpolated point 5.19:1 on --bg. */
  --grad: linear-gradient(100deg, var(--accent), var(--accent-2));

  /* Ink-tinted, not neutral black: black shadow on a light ground reads grey. */
  --shadow-sm: 0 1px 2px rgba(19, 24, 32, .06);
  --shadow-md: 0 6px 16px -4px rgba(67, 56, 202, .16), 0 2px 6px rgba(19, 24, 32, .06);
  --shadow-lg: 0 18px 38px -12px rgba(67, 56, 202, .24), 0 4px 12px rgba(19, 24, 32, .07);
  --ring: 0 0 0 3px rgba(67, 56, 202, .30);

  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: var(--font-body);
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", monospace;

  /* Hero blob strength. Dark ink on a light ground stains, so it stays low. */
  --hero-blob: .28;

  --wrap: 1180px;
  --radius: 14px;
  --section-y: 96px;
}

/* DARK THEME — applied by [data-theme="dark"], set pre-paint by the inline
   <head> script. A counterpart, not an inversion: accents get brighter and
   text on an accent flips to near-black. All ratios verified in CLAUDE.md. */
[data-theme="dark"] {
  --accent:       #a5b4fc;
  --accent-hover: #c7d2fe;
  --accent-2:     #5eead4;
  --danger:       #fda4af;

  --bg:        #0d1017;
  --surface:   #151a22;
  --surface-2: #1e242e;

  --border:        #262d38;
  --border-strong: #646f7e;   /* 3.42:1 on --surface — form-field edges */

  --text:  #f1f4f8;
  --muted: #a0aab7;
  /* Bright accents need dark text on top. */
  --on-accent: #0d1017;

  /* Subtler on dark: depth comes from surface lift, not glow. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .45);
  --shadow-md: 0 6px 16px -4px rgba(0, 0, 0, .55), 0 2px 6px rgba(0, 0, 0, .35);
  --shadow-lg: 0 18px 38px -12px rgba(0, 0, 0, .65), 0 4px 12px rgba(0, 0, 0, .4);
  --ring: 0 0 0 3px rgba(165, 180, 252, .40);

  /* Light ink on a dark ground glows rather than stains, so it can go up. */
  --hero-blob: .42;
}

/* Header tint follows the ground. */
[data-theme="dark"] .site-header { background: rgba(13, 16, 23, .96); }


/* SITE-WIDE WATERMARK (trefoil: three interlocking rings on a triangle).
   One CSS rule, no markup, so there is no per-page copy and
   no new sync point between index.html and chrome.php. A MASK, not a background
   image: a data-URI SVG is a separate document and cannot read var(), but a mask
   is pure geometry and the colour comes from background-color — so it follows the
   theme like everything else. Static: no animation, no JS, no extra request.
   @supports guard: without mask support this would paint a flat accent wash, so
   the rule is skipped entirely instead. Suppressed on the admin panel, which is
   deliberately plain. */
@supports ((-webkit-mask-image: none) or (mask-image: none)) {
  body::before {
    content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
    background-color: var(--accent);
    opacity: .05;
    /* URI written twice on purpose: a --var is smaller raw, LARGER gzipped. */
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 620 580' fill='none' stroke='%2523000' stroke-width='5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='310' cy='188' r='150'/%3E%3Ccircle cx='180' cy='394' r='150'/%3E%3Ccircle cx='440' cy='394' r='150'/%3E%3Cpath d='M310 188L180 394M180 394h260M440 394L310 188'/%3E%3Ccircle cx='310' cy='188' r='17'/%3E%3Ccircle cx='180' cy='394' r='17'/%3E%3Ccircle cx='440' cy='394' r='17'/%3E%3Ccircle cx='310' cy='325' r='9'/%3E%3Cpath d='M310 205v105M296 334l-99 52M324 334l99 52' stroke-dasharray='14 13'/%3E%3Cpath d='M310 8v34M96 512l24-24M524 512l-24-24'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 620 580' fill='none' stroke='%2523000' stroke-width='5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='310' cy='188' r='150'/%3E%3Ccircle cx='180' cy='394' r='150'/%3E%3Ccircle cx='440' cy='394' r='150'/%3E%3Cpath d='M310 188L180 394M180 394h260M440 394L310 188'/%3E%3Ccircle cx='310' cy='188' r='17'/%3E%3Ccircle cx='180' cy='394' r='17'/%3E%3Ccircle cx='440' cy='394' r='17'/%3E%3Ccircle cx='310' cy='325' r='9'/%3E%3Cpath d='M310 205v105M296 334l-99 52M324 334l99 52' stroke-dasharray='14 13'/%3E%3Cpath d='M310 8v34M96 512l24-24M524 512l-24-24'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
    -webkit-mask-position: center 42%; mask-position: center 42%;
    /* PERCENTAGE, not min()/vw: both compute to 0px here — mask-size silently
       renders nothing, and CSS.supports() still reports true. Measured. */
    -webkit-mask-size: auto 108%; mask-size: auto 108%;
  }
  /* Cap it on wide screens so it stays a watermark, not wallpaper. */
  @media (min-width: 1100px) {
    body::before { -webkit-mask-size: auto 900px; mask-size: auto 900px; }
  }
  body.admin::before { content: none; }
}

/* reset */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
html { background: var(--bg); }
body {
  margin: 0;
  background: transparent;
  color: var(--text);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; }
a { color: inherit; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0 0 .5em;
  font-weight: 700;
}
h1 { font-size: clamp(2.1rem, 5.2vw, 3.6rem); }
h2 { font-size: clamp(1.6rem, 3.4vw, 2.4rem); }
h3 { font-size: 1.18rem; }
p { margin: 0 0 1.1em; }

/* Keyboard focus — never remove without a replacement. */
:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--on-accent); padding: 10px 16px; border-radius: 0 0 8px 0;
}
.skip-link:focus { left: 0; }

/* layout */
.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 22px; }
.wrap.narrow { max-width: 760px; }
.section { padding: var(--section-y) 0; }
.section + .section { border-top: 1px solid var(--border); }

.eyebrow {
  font-size: .78rem; text-transform: uppercase; letter-spacing: .16em;
  color: var(--accent-2); font-weight: 600; margin: 0 0 .8em;
}
.lead { font-size: 1.12rem; color: var(--muted); max-width: 62ch; }
.muted { color: var(--muted); }
.small { font-size: .88rem; }
.grad-text {
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}

/* header */
/* No backdrop-filter: a blur on a STICKY element re-blurs the strip passing
   under it every scrolled frame — GPU cost invisible to a JS profile. See CLAUDE.md. */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(247, 249, 252, .96);
  border-bottom: 1px solid var(--border);
}
/* Three children: logo | nav | theme toggle. The toggle stays OUTSIDE the
   collapsible list so it is reachable while the menu is shut. */
.header-inner { display: flex; align-items: center; gap: 14px; min-height: 66px; position: relative; }

.logo { font-family: var(--font-display); font-weight: 800; letter-spacing: -.03em; font-size: 1.2rem; text-decoration: none; }
.nav-links { display: flex; flex-wrap: wrap; gap: 4px 20px; list-style: none; margin: 0; padding: 0; align-items: center; }
.nav-links a {
  text-decoration: none; color: var(--muted); font-size: .94rem; font-weight: 500;
  padding: 6px 2px; position: relative; transition: color .16s ease;
}
/* scaleX underline — composites, no layout. */
.nav-links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--grad); border-radius: 2px;
  transform: scaleX(0); transform-origin: left;
  transition: transform .2s cubic-bezier(.22,.7,.3,1);
}
.nav-links a:hover { color: var(--accent); }
.nav-links a:hover::after { transform: scaleX(1); }
.nav-links a:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; border-radius: 3px; }
.nav-links .nav-cta::after { display: none; }
/* .nav-links .nav-cta (0,2,0) beats .nav-links a on its own — no !important. */
.nav-links .nav-cta {
  color: var(--text);
  border: 1px solid var(--border); border-radius: 999px; padding: 7px 16px;
  transition: border-color .15s ease, background .15s ease;
}
.nav-links .nav-cta:hover { border-color: var(--accent); background: var(--surface); }

/* buttons */
/* Interactive states are pure CSS so they survive GSAP/JS being unavailable.
   Only transform / box-shadow / colour animate — nothing layout-triggering. */
.btn {
  display: inline-block; padding: 12px 24px; border-radius: 999px;
  font-weight: 600; font-size: .96rem; text-decoration: none; cursor: pointer;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text);
  box-shadow: var(--shadow-sm);
  transition: border-color .16s ease, background .16s ease,
              transform .16s cubic-bezier(.22,.7,.3,1), box-shadow .16s ease, color .16s ease;
}
.btn:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-2px); box-shadow: var(--shadow-md); }
/* Pressed: travels back down and drops the shadow. */
.btn:active { transform: translateY(0); box-shadow: var(--shadow-sm); transition-duration: .05s; }
.btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.btn-block { display: block; width: 100%; text-align: center; }

.btn-primary {
  background: var(--grad); color: var(--on-accent); border-color: transparent;
  box-shadow: var(--shadow-md);
}
/* Lift, not opacity — fading the gradient on a light page costs contrast. */
.btn-primary:hover { color: var(--on-accent); transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.btn-primary:active { transform: translateY(0); box-shadow: var(--shadow-sm); }

.text-link {
  color: var(--accent); text-decoration: none; font-weight: 600;
  border-bottom: 2px solid transparent; transition: border-color .16s ease, color .16s ease;
}
.text-link:hover { color: var(--accent-hover); border-bottom-color: var(--accent-hover); }
.text-link:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* hero — full-bleed cinematic photo backdrop (SpaceX-style: one big image tells
   the story, minimal overlaid copy), replacing the old SVG-blob treatment. */
.hero { position: relative; padding: clamp(84px, 13vw, 168px) 0 clamp(76px, 11vw, 140px); overflow: hidden; }
.hero-inner { position: relative; z-index: 2; text-align: center; }
.hero h1 { max-width: 19ch; margin-left: auto; margin-right: auto; font-size: clamp(2.4rem, 6.2vw, 4.4rem); }
.hero .lead { margin: 1.1em auto 0; font-size: clamp(1.1rem, 1.6vw, 1.32rem); max-width: 58ch; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 2.4em; justify-content: center; }

.hero-cinematic {
  padding: clamp(140px, 22vw, 260px) 0 clamp(96px, 14vw, 176px);
  min-height: 78vh; display: flex; align-items: center;
}
.hero-bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
}
/* Bottom-weighted scrim: keeps the photo readable behind the copy without
   flattening it to a solid colour. Fixed dark values on purpose — the photo
   itself doesn't follow the light/dark toggle, so neither does its overlay. */
.hero-scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(6,9,16,.5) 0%, rgba(6,9,16,.62) 45%, rgba(6,9,16,.86) 100%);
}
/* Text sits on a dark photo regardless of site theme, so its colours are fixed
   here rather than inherited from --text/--accent. */
.hero-cinematic .eyebrow { color: #b9c3ff; }
.hero-cinematic h1 { color: #ffffff; }
.hero-cinematic h1 .grad-text {
  background-image: linear-gradient(100deg, #a5b4fc, #5eead4);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero-cinematic .lead { color: #dfe4ee; }

/* keyword strip (static, replaces the old JS marquee) */
/* TICKER — pure CSS. Track holds the list twice and slides -50pc, so the loop is
   invisible. transform only: composites, no layout. */
.keyword-strip {
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
  background: var(--surface); overflow: hidden;
}
.kw-track {
  display: flex; width: max-content;
  animation: kw-scroll 46s linear infinite;
}
/* Pause to read. :focus-within covers the keyboard path — terms are real links. */
.keyword-strip:hover .kw-track,
.keyword-strip:focus-within .kw-track { animation-play-state: paused; }
@keyframes kw-scroll { from { transform: translate3d(0,0,0); } to { transform: translate3d(-50%,0,0); } }

.kw-list { display: flex; gap: 0 34px; list-style: none; margin: 0; padding: 22px 16px; }
/* The sprite is a definitions block: it must occupy no space and paint nothing.
   Kept even though .ic is no longer used by the ticker — service detail pages
   still reference the same sprite for their own icon lists. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.ic {
  width: 1.15em; height: 1.15em; flex: none; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
/* Plain <div>, not a link: these are illustrative keywords, not navigation —
   stacked icon-over-label, non-interactive on purpose. */
.kw {
  color: var(--muted); font-size: .87rem; text-transform: uppercase; letter-spacing: .1em;
  white-space: nowrap;
  display: inline-flex; flex-direction: column; align-items: center; gap: 9px;
}
.kw-icon {
  display: block; width: 60px; height: 60px;
  object-fit: cover; border-radius: 14px;
  box-shadow: var(--shadow-sm);
}

/* Reduced motion: no autoplay. Clone dropped, real list wraps — every term kept. */
@media (prefers-reduced-motion: reduce) {
  .kw-track { animation: none; width: auto; flex-wrap: wrap; }
  .kw-list[aria-hidden="true"] { display: none; }
  .kw-list { flex-wrap: wrap; gap: 10px 26px; justify-content: center; width: 100%; padding: 18px 22px; }
}

/* Four short phrases, read at a glance. Counter-numbered, so it stays a plain <ol>. */
.roadmap {
  list-style: none; counter-reset: rm; padding: 0; margin: 1.4em 0 1.6em;
  display: flex; flex-wrap: wrap; gap: 10px 14px;
}
.roadmap li {
  counter-increment: rm; display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-display); font-weight: 700; font-size: 1.06rem;
  letter-spacing: -.01em;
  border: 1px solid var(--border); border-radius: 999px;
  padding: 8px 16px 8px 10px; background: var(--surface);
}
.roadmap li::before {
  content: counter(rm); display: grid; place-items: center;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--grad); color: var(--on-accent);
  font-family: var(--font-mono); font-size: .72rem; font-weight: 700;
}

/* cards */
.card-grid { display: grid; gap: 22px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); margin-top: 2.6em; }
.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 28px 26px; position: relative;   /* containing block for .card-link */
  box-shadow: var(--shadow-sm);
  transition: border-color .18s ease, box-shadow .18s ease,
              transform .18s cubic-bezier(.22,.7,.3,1);
}
/* Tilt writes its own transform here and supersedes this; both composite. */
.card:hover { border-color: var(--accent); box-shadow: var(--shadow-lg); transform: translateY(-4px); }
/* Stretched link: the whole card is clickable through the link already there, so
   the a11y tree keeps exactly one link and one tab stop. */
.card-link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.card:focus-within { border-color: var(--accent); box-shadow: var(--shadow-md); }
.card h3 { margin-bottom: .4em; }
.card p:last-child { margin-bottom: 0; }
.card-index { font-family: var(--font-mono); font-size: .8rem; color: var(--accent-2); }

/* aspect-ratio matches the SVG viewBox: reserves the box so lazy loading
   cannot shift layout. Same rule for every image on the site. */
.card-art {
  display: block; width: calc(100% + 52px); margin: -28px -26px 20px; max-width: none;
  aspect-ratio: 1376 / 768; object-fit: cover;
  border-radius: var(--radius) var(--radius) 0 0;
  border-bottom: 1px solid var(--border);
}

/* Cinematic full-bleed PRODUCT SECTIONS — SpaceX vehicle-page style: each
   product/service is its own full-viewport-width section stacked one after
   another. Scroll, and the next photo fills the screen with its title over
   it. This is deliberately NOT a grid of cards — one section per product. */
.section-flush { padding-bottom: 0; }
/* flex column with no gap and no border between children — the photos run
   straight into one another as you scroll. Each section's own scrim darkens
   both its top and bottom edge (see .cine-section-scrim below), so the seam
   where one photo meets the next is a soft blend, not a hard cut. */
.cine-stack { display: flex; flex-direction: column; margin-top: 48px; }
.cine-section {
  position: relative; overflow: hidden;
  min-height: clamp(360px, 46vw, 560px); display: flex; align-items: flex-end;
}
.cine-section-bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
}
/* Two dark bands — one hugging the top edge, one at the bottom — with the
   photo showing clearly in between. The top band matches the bottom band's
   darkness so two stacked sections blend into each other at the seam instead
   of jumping from "very dark" to "fully bright" in a single pixel. */
.cine-section-scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg,
    rgba(6,9,16,.85) 0%,
    rgba(6,9,16,0) 18%,
    rgba(6,9,16,0) 58%,
    rgba(6,9,16,.55) 78%,
    rgba(6,9,16,.94) 100%);
}
.cine-section-content {
  position: relative; z-index: 2;
  padding-bottom: clamp(28px, 5vw, 56px);
}
.cine-section-content h3 {
  color: #fff; margin: 0 0 .2em;
  font-size: clamp(1.3rem, 2.4vw, 1.9rem);
}
.cine-tagline { color: #dfe4ee; margin: 0 0 .55em; font-size: .95rem; }
.cine-section-content .text-link.card-link {
  color: #fff; border-bottom-color: rgba(255,255,255,.5); font-size: .95rem;
}
.cine-section-content .text-link.card-link:hover { color: var(--accent-2); border-bottom-color: var(--accent-2); }

/* Icon-badge images used where a small square badge (not full-bleed art) is
   still wanted — kept for any spot that still uses it. */
.icon-badge {
  display: block; width: 76px; height: 76px;
  object-fit: cover; border-radius: 16px;
  margin: 0 0 18px; box-shadow: var(--shadow-md);
}

/* Full-picture mini cards — same photo + scrim + overlay-heading idea as the
   services stack, scaled down to sit in a grid. Used where a section should
   explain itself mostly through the image, with one short heading and no
   body copy: the "Why TRIFORGE" reasons. */
.mini-cine-grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); margin-top: 2.6em; }
.mini-cine-card {
  position: relative; overflow: hidden; border-radius: var(--radius);
  min-height: 230px; display: flex; align-items: flex-end;
  box-shadow: var(--shadow-sm);
  transition: transform .18s cubic-bezier(.22,.7,.3,1), box-shadow .18s ease;
}
.mini-cine-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.mini-cine-bg { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; object-fit: cover; }
.mini-cine-scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(6,9,16,0) 42%, rgba(6,9,16,.88) 100%);
}
.mini-cine-content { position: relative; z-index: 2; padding: 16px 18px; }
.mini-cine-content h3 { color: #fff; margin: 0; font-size: 1.05rem; }
.js .mini-cine-grid > .reveal:nth-child(2) { transition-delay: .09s; }
.js .mini-cine-grid > .reveal:nth-child(3) { transition-delay: .18s; }

/* Same idea again, smaller still, for the "after you reach out" steps — a
   numbered picture tile instead of an icon-plus-paragraph row. */
.step-tile-grid { list-style: none; padding: 0; display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); margin: 2.4em 0 0; }
.step-tile {
  position: relative; overflow: hidden; border-radius: var(--radius);
  min-height: 175px; display: flex; align-items: flex-end;
  box-shadow: var(--shadow-sm);
}
.step-tile-bg { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; object-fit: cover; }
.step-tile-scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(6,9,16,0) 48%, rgba(6,9,16,.84) 100%);
}
.step-tile-content { position: relative; z-index: 2; padding: 13px 15px; }
.step-tile-content h3 { color: #fff; margin: 0; font-size: .92rem; }
.step-tile-num {
  position: absolute; z-index: 2; top: 12px; left: 14px;
  font-family: var(--font-mono); font-size: .72rem; color: #dfe4ee;
  background: rgba(6,9,16,.55); border-radius: 999px; padding: 3px 9px;
}

/* service deep-dives */
.detail-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 14px; }
.detail-badge {
  font-family: var(--font-mono); font-size: .76rem; letter-spacing: .1em; text-transform: uppercase;
  border: 1px solid var(--border); border-radius: 999px; padding: 5px 13px; color: var(--muted);
}
.service-list { list-style: none; padding: 0; margin: 2.2em 0 0; display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }
.service-list h3 { font-size: 1.02rem; margin-bottom: .3em; }
.service-list p { margin: 0; color: var(--muted); font-size: .94rem; }
.service-list li { border-left: 2px solid var(--border); padding-left: 16px; }
.service-list li:hover { border-left-color: var(--accent-2); }
/* Icon sits beside the heading, not above the whole item, so the text block keeps
   its own left edge and the list still scans as a list. */
.service-list h3 { display: flex; align-items: center; gap: 9px; }
.service-list .ic { color: var(--accent); width: 1.25em; height: 1.25em; }

.stack-row { margin-top: 2.4em; }
.stack-row h4 { font-size: .8rem; text-transform: uppercase; letter-spacing: .13em; color: var(--muted); margin: 0 0 .9em; font-weight: 600; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; padding: 0; margin: 0; }
.chips li {
  font-family: var(--font-mono); font-size: .8rem; color: var(--muted);
  border: 1px solid var(--border); border-radius: 6px; padding: 5px 11px; background: var(--surface);
}

/* data tables */
.table-scroll { overflow-x: auto; margin-top: 2.2em; -webkit-overflow-scrolling: touch; }
table { width: 100%; border-collapse: collapse; font-size: .95rem; min-width: 540px; }
caption { text-align: left; color: var(--muted); font-size: .9rem; padding-bottom: .9em; }
th, td { text-align: left; padding: 13px 16px; border-bottom: 1px solid var(--border); vertical-align: top; }
thead th { font-size: .78rem; text-transform: uppercase; letter-spacing: .1em; color: var(--accent-2); font-weight: 600; white-space: nowrap; }
tbody th { font-weight: 600; }
tbody tr:hover { background: var(--surface); }
td.num, th.num { font-family: var(--font-mono); white-space: nowrap; }
.table-note { color: var(--muted); font-size: .88rem; margin-top: 1em; }

/* work / case studies
   The track is an ordinary scrollable flex row. GSAP only upgrades it to a
   pinned sideways scroll on wide screens; it never creates the layout. */
.work-pin { margin-top: 2.6em; }
.work-track {
  display: flex; gap: 22px;
  padding: 4px 22px 18px;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
}
.work-card {
  flex: 0 0 min(78vw, 380px);
  scroll-snap-align: start;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden;
  transition: border-color .2s ease, transform .2s ease;
  /* No permanent will-change: it held 5 compositor layers alive for the page
   lifetime. anim.js adds it on mouseenter and removes it on mouseleave. */
}
.work-card:hover { border-color: var(--accent); box-shadow: var(--shadow-lg); }
.work-art {
  display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-bottom: 1px solid var(--border);
}
.work-body { padding: 22px 24px 26px; }
.work-body h3 { margin: .25em 0 .45em; }
.work-body p { color: var(--muted); font-size: .95rem; }
.work-body .chips { margin-top: 14px; }

/* Pinned: GSAP drives the track, so native scroll is off. The container MUST
   keep overflow:hidden or the unclipped row scrolls the whole page sideways. */
.work-pin.work-pinned { overflow: hidden; }
.work-pinned .work-track { overflow: visible; scroll-snap-type: none; }

/* stats */
.stat-grid { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); margin-top: 2.4em; list-style: none; padding: 0; }
.stat-grid li { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 24px; }
.stat-value { font-family: var(--font-display); font-size: 2.3rem; font-weight: 800; line-height: 1; letter-spacing: -.03em; display: block; }
.stat-label { color: var(--muted); font-size: .9rem; margin-top: .55em; display: block; }

/* Separates #why's three sub-blocks without a second <section> (= a second nav target). */
.why-sub { margin-top: 3.4em; padding-top: 2.4em; border-top: 1px solid var(--border); }
.why-sub-title { font-size: clamp(1.4rem, 2.6vw, 1.9rem); letter-spacing: -.02em; margin-bottom: .5em; }

/* Step cards carry the map's discipline colours. Nothing hides behind the hover,
   so this degrades to "no lift". */
.step-cards {
  list-style: none; counter-reset: none; padding: 0; margin: 2.6em 0 0;
  display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
}
.step-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  border-top: 3px solid var(--accent);
  padding: 22px 20px 24px; box-shadow: var(--shadow-sm);
  transition: border-color .18s ease, box-shadow .18s ease,
              transform .18s cubic-bezier(.22,.7,.3,1);
}
.step-card:hover { box-shadow: var(--shadow-lg); transform: translateY(-4px); }
.step-icon {
  display: block; width: 52px; height: 52px;
  object-fit: cover; border-radius: 12px;
  box-shadow: var(--shadow-sm); margin-bottom: 14px;
}
.step-num { font-family: var(--font-mono); font-size: .78rem; color: var(--muted); margin: 0 0 .5em; letter-spacing: .1em; }
.step-card h3 { font-size: 1.04rem; margin-bottom: .45em; }
.step-card p:last-child { margin: 0; color: var(--muted); font-size: .93rem; }
/* Pure CSS, so a blocked GSAP loses nothing. */
.step-map .pnode { transition: opacity .16s ease; }
.step-map .pnode:hover { opacity: .72; }

/* process
   Each step now leads with a real icon rather than a numbered counter — the
   <ol> still gives the step order to assistive tech, so nothing is lost. */
.process-list { list-style: none; padding: 0; margin: 2.4em 0 0; display: grid; gap: 4px; }
.process-list li { display: grid; grid-template-columns: 64px 1fr; gap: 20px; padding: 22px 0; border-top: 1px solid var(--border); align-items: start; }
/* Service detail pages ("how an engagement runs") reuse .process-list but have
   no icon — just one <div> per <li>. Without this it lands in the 64px icon
   column and wraps one word per line. */
.process-list li > div:only-child { grid-column: 1 / -1; }
.process-icon {
  display: block; width: 54px; height: 54px;
  object-fit: cover; border-radius: 12px;
  box-shadow: var(--shadow-sm); margin-top: 2px;
}
.process-list h3 { margin-bottom: .3em; }
.process-list p { margin: 0; color: var(--muted); }

/* testimonials (static, no carousel) */
.quote-grid { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); margin-top: 2.4em; }
blockquote { margin: 0; background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: var(--radius); padding: 26px; }
blockquote p { font-size: 1.02rem; }
blockquote footer { color: var(--muted); font-size: .89rem; }
blockquote cite { font-style: normal; font-weight: 600; color: var(--text); display: block; }

/* FAQ (native <details>, zero JS) */
.faq-list { margin-top: 2.2em; border-top: 1px solid var(--border); }
.faq-list details { border-bottom: 1px solid var(--border); }
.faq-list summary {
  cursor: pointer; padding: 20px 40px 20px 0; position: relative;
  font-weight: 600; font-size: 1.03rem; list-style: none;
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary::after {
  content: "+"; position: absolute; right: 6px; top: 17px;
  font-size: 1.5rem; color: var(--accent-2); font-weight: 400; line-height: 1;
}
.faq-list details[open] summary::after { content: "\2212"; }
.faq-list summary:hover { color: var(--accent-2); }
.faq-answer { padding: 0 40px 20px 0; color: var(--muted); }
.faq-answer p:last-child { margin-bottom: 0; }

/* contact */
.contact-grid { display: grid; gap: 44px; grid-template-columns: 1.15fr .85fr; margin-top: 2.4em; }
.field { margin-bottom: 18px; }
.field label { display: block; font-size: .86rem; font-weight: 600; margin-bottom: 7px; }
/* By EXCLUSION, never a list of types — a new input type must not fall out and go
   unstyled (this happened to tel + password). 1rem: under 16px iOS zooms on focus. */
input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]),
textarea, select {
  width: 100%; padding: 12px 14px; font: inherit; font-size: 1rem; min-height: 46px;
  color: var(--text); background: var(--surface);
  /* --border-strong: the border IS the control boundary (WCAG 1.4.11, 3:1). */
  border: 1px solid var(--border-strong);
  border-radius: 9px;
  transition: border-color .16s ease, box-shadow .16s ease, background .16s ease;
}
input:hover, textarea:hover { border-color: var(--accent); background: var(--surface-2); }
input:focus, textarea:focus { border-color: var(--accent); outline: none; box-shadow: var(--ring); }
textarea { min-height: 140px; resize: vertical; }
.field-row { display: grid; gap: 18px; grid-template-columns: 1fr 1fr; margin-bottom: 18px; }
/* Row owns the spacing: gap + child margin was 36px when stacked, 18px elsewhere. */
.field-row .field { margin-bottom: 0; }
/* One visual unit — does NOT stack at any width; 8.5rem still leaves the number
   ~200px at 360px. Measured. */
.field-row.field-row-phone { grid-template-columns: 8.5rem 1fr; }
/* select and input compute different heights from identical padding (49 vs 52),
   so the grid equalises the boxes and each control fills its own. */
.field-row-phone .field { display: flex; flex-direction: column; }
.field-row-phone .field > select,
.field-row-phone .field > input { flex: 1 1 auto; }
/* Closed select is narrower than its longest option — hence code-first labels. */
select { cursor: pointer; text-overflow: ellipsis; }

fieldset { border: 0; padding: 0; margin: 0 0 18px; }
legend { font-size: .86rem; font-weight: 600; margin-bottom: 7px; padding: 0; }
.check-row { display: flex; flex-wrap: wrap; gap: 9px; }
.check-row label {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 999px; padding: 8px 15px;
  font-size: .9rem; background: var(--surface); transition: border-color .15s ease;
  margin: 0;
}
.check-row label:hover { border-color: var(--accent); }
.check-row input { accent-color: var(--accent); margin: 0; }

/* Honeypot: off-screen and out of the a11y tree. */
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-note { font-size: .87rem; color: var(--muted); margin-top: 14px; }
.form-note[data-state="error"] { color: var(--danger); }
.form-success {
  background: var(--surface); border: 1px solid var(--accent-2); border-radius: var(--radius);
  padding: 30px; text-align: center;
}
.form-success h3 { color: var(--accent-2); }

.contact-facts {
  list-style: none; margin: 0; padding: 2px 22px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-sm);
}
.contact-facts li { padding: 15px 0; border-bottom: 1px solid var(--border); }
/* Nothing under the last rule to divide. */
.contact-facts li:last-child { border-bottom: 0; }
.contact-facts dt, .contact-facts .k { font-size: .78rem; text-transform: uppercase; letter-spacing: .11em; color: var(--muted); display: block; margin-bottom: 3px; }

/* BLOG
   Card listing that matches .card elsewhere on the site (same hover lift, shadow
   and border-accent), plus real typographic rhythm on the post page. All theme
   tokens — nothing hardcoded, so it follows light/dark like everything else. */
.post-list { list-style: none; padding: 0; margin: 2.4em 0 0; display: grid; gap: 20px; }
.post-item {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); overflow: hidden;
  transition: border-color .18s ease, box-shadow .18s ease,
              transform .18s cubic-bezier(.22,.7,.3,1);
}
.post-item:hover { border-color: var(--accent); box-shadow: var(--shadow-lg); transform: translateY(-3px); }
.post-item:focus-within { border-color: var(--accent); box-shadow: var(--shadow-md); }
.post-body { padding: 22px 26px 24px; }
.post-title { font-size: 1.34rem; margin: 0 0 .3em; line-height: 1.25; }
.post-title a { text-decoration: none; }
.post-item:hover .post-title a { color: var(--accent); }
.post-meta { font-size: .82rem; color: var(--muted); margin: 0 0 .5em; letter-spacing: .02em; }
.post-excerpt { color: var(--muted); margin: 0 0 .9em; max-width: 62ch; }
.post-more { font-size: .9rem; }

/* Per-post accent strip. Inline SVG in the template, theme-agnostic ink, ~180 B —
   cheaper and sharper than any raster and it needs no extra request. */
.post-accent { display: block; width: 100%; height: 6px; }
.post-hero { display: block; width: 100%; height: 64px; margin-bottom: 1.6em; border-radius: var(--radius); }

.post-back { margin-top: 3em; padding-top: 1.6em; border-top: 1px solid var(--border); }
.empty-state { background: var(--surface); border: 1px dashed var(--border-strong); border-radius: var(--radius); padding: 40px 30px; margin-top: 2.6em; text-align: center; }
.badge-example {
  display: inline-block; font-size: .78rem; font-weight: 600;
  color: var(--on-accent); background: var(--accent-2); border-radius: 5px; padding: 3px 9px; margin-bottom: 1em;
}

/* Reading rhythm: a measure near 68ch, generous space above headings and much
   less below, so each heading binds to the text it introduces. */
.prose { margin-top: 2em; font-size: 1.05rem; line-height: 1.75; }
.prose > p { max-width: 68ch; margin: 0 0 1.35em; }
.prose h2 {
  font-size: 1.42rem; line-height: 1.3; max-width: 40ch;
  margin: 2.1em 0 .55em; padding-top: .9em; border-top: 1px solid var(--border);
}
.prose h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.prose ul { padding-left: 1.15em; margin: 0 0 1.35em; max-width: 66ch; }
.prose li { margin-bottom: .55em; padding-left: .15em; }
.prose li::marker { color: var(--accent); }
/* Lead-in: the first paragraph carries the post, so give it slightly more weight. */
.prose > p:first-child { font-size: 1.14rem; color: var(--text); }

code { font-family: var(--font-mono); font-size: .88em; background: var(--surface-2); border: 1px solid var(--border); border-radius: 5px; padding: 1px 6px; }

/* footer */
.site-footer { border-top: 1px solid var(--border); padding: 54px 0 34px; margin-top: var(--section-y); }
.footer-inner { display: flex; flex-wrap: wrap; gap: 30px; justify-content: space-between; margin-bottom: 30px; }
.site-footer ul { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 8px 22px; }
.site-footer a { color: var(--muted); text-decoration: none; font-size: .93rem; }
.site-footer a:hover { color: var(--text); }
.site-footer .logo { margin-bottom: .3em; }

/* scroll reveal (progressive enhancement)
   Hidden state applies only once <html> has .js, so JS-off hides nothing.
   Three fallbacks live in index.html — see CLAUDE.md before touching. */
.js .reveal { opacity: 0; transform: translateY(22px); transition: opacity .62s cubic-bezier(.22,.7,.3,1), transform .62s cubic-bezier(.22,.7,.3,1); }
.js .reveal.in { opacity: 1; transform: none; }

/* Direction/stagger variants: still one CSS transition, same observer. */
.js .reveal-l { transform: translate3d(-30px, 0, 0); }
.js .reveal-r { transform: translate3d(30px, 0, 0); }
.js .reveal-s { transform: scale(.965); }
.js .reveal-l.in, .js .reveal-r.in, .js .reveal-s.in { transform: none; }

/* Grid stagger is CSS, not JS. */
.js .card-grid > .reveal:nth-child(2)  { transition-delay: .09s; }
.js .card-grid > .reveal:nth-child(3)  { transition-delay: .18s; }

/* Reduced motion kills the whole enhancement layer; anim.js checks the same query. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .reveal,
  .js .reveal-l, .js .reveal-r, .js .reveal-s { opacity: 1; transform: none; transition: none; }
  .js .card-grid > .reveal { transition-delay: 0s !important; }
  /* Reduced motion: no entrance at all, straight to final state. */
  .enter, .site-header .logo {
    animation: none !important; opacity: 1 !important; transform: none !important;
  }
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* responsive */
@media (max-width: 980px) {
  .hero h1 { max-width: 15ch; }
}
@media (max-width: 880px) {
  :root { --section-y: 68px; }
  .contact-grid { grid-template-columns: 1fr; gap: 34px; }
  /* No horizontal offset when the block spans the column — it would push the
     page wider than the viewport mid-transition. */
  .js .reveal-l, .js .reveal-r { transform: translate3d(0, 22px, 0); }
}
@media (max-width: 620px) {
  body { font-size: 16px; }
  .field-row { grid-template-columns: 1fr; }
  /* The number needs the room more than the code label does. */
  .field-row.field-row-phone { grid-template-columns: 7rem 1fr; }
  .contact-facts { padding: 2px 18px; }
  .contact-facts li { padding: 13px 0; }
  .contact-grid { gap: 26px; }
  .process-list li { grid-template-columns: 44px 1fr; gap: 12px; }
  .process-icon { width: 40px; height: 40px; border-radius: 10px; }
  .icon-badge { width: 60px; height: 60px; }
  .step-icon { width: 44px; height: 44px; }
  .wrap { padding: 0 16px; }
  .card { padding: 24px 20px; }
  .card-art { width: calc(100% + 40px); margin: -24px -20px 18px; }
  .cine-stack { margin-top: 36px; }
  .cine-section { min-height: 320px; }
  .cine-section-content { padding-bottom: 24px; }
  .mini-cine-card { min-height: 190px; }
  .step-tile { min-height: 150px; }
  .post-body { padding: 20px 18px 22px; }
  .hero-actions .btn { flex: 1 1 100%; text-align: center; }
  .footer-inner { gap: 20px; }
}

/* MOBILE NAV — native <details> for state, <ul> as its SIBLING (never its child:
   a closed <details> hides its own content and CSS cannot reliably re-show it).
   No JavaScript. See CLAUDE.md. */
.site-nav { display: flex; align-items: center; margin-left: auto; }
.nav-toggle { display: none; }

@media (max-width: 720px) {
  .nav-toggle {
    display: inline-flex; align-items: center; gap: 9px;
    min-height: 44px; padding: 0 15px;
    border: 1px solid var(--border-strong); border-radius: 999px;
    background: var(--surface); color: var(--text);
    font-size: .92rem; font-weight: 600; cursor: pointer;
    list-style: none;                       /* hides the disclosure triangle */
  }
  .nav-toggle::-webkit-details-marker { display: none; }
  .nav-toggle:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

  /* Three bars, an X when open. transform only. */
  .nav-burger { position: relative; display: block; width: 17px; height: 2px; background: currentColor; border-radius: 2px; }
  .nav-burger::before, .nav-burger::after {
    content: ""; position: absolute; left: 0; width: 17px; height: 2px;
    background: currentColor; border-radius: 2px; transition: transform .18s ease, top .18s ease;
  }
  .nav-burger::before { top: -6px; }
  .nav-burger::after  { top: 6px; }
  .nav-menu[open] .nav-burger { background: transparent; }
  .nav-menu[open] .nav-burger::before { top: 0; transform: rotate(45deg); }
  .nav-menu[open] .nav-burger::after  { top: 0; transform: rotate(-45deg); }

  .nav-menu:not([open]) ~ .nav-links { display: none; }
  .nav-menu[open] ~ .nav-links {
    position: absolute; top: calc(100% + 12px); right: 0; left: 0;
    flex-direction: column; align-items: stretch; gap: 2px;
    background: var(--surface); border: 1px solid var(--border-strong);
    border-radius: var(--radius); box-shadow: var(--shadow-lg);
    padding: 7px; max-height: 72vh; overflow-y: auto;
  }
  .nav-menu[open] ~ .nav-links a {
    display: flex; align-items: center; min-height: 48px; padding: 0 14px;
    border-radius: 9px; font-size: 1rem; color: var(--text);
  }
  .nav-menu[open] ~ .nav-links a::after { display: none; } /* no scaleX underline in a stacked list */
  .nav-menu[open] ~ .nav-links a:hover { background: var(--surface-2); color: var(--accent); }
  .nav-menu[open] ~ .nav-links .nav-cta { border: 0; color: var(--accent); font-weight: 700; }
}

/* TAP TARGETS — (pointer: coarse) is the real condition, not viewport width. */
@media (pointer: coarse) {
  .nav-links a, .site-footer a, .admin-bar a,
  .admin-actions a, .admin-actions button, .admin-pager a, .admin-pager .current {
    min-height: 44px; display: inline-flex; align-items: center;
  }
  .text-link { display: inline-flex; align-items: center; min-height: 44px; }
  /* Breadcrumb: inline in a sentence, keeps the text line box. */
  .post-meta .text-link { display: inline; min-height: 0; }
  .theme-toggle { min-height: 44px; padding: 6px 14px; }
  .btn { padding: 14px 26px; }
  .check-row label { min-height: 44px; }
  .check-row input { width: 20px; height: 20px; }
  .faq-list summary { padding-top: 22px; padding-bottom: 22px; }
  /* No hover on touch: spend the underline's space on a finger-sized target. */
  .kw { min-height: 44px; min-width: 44px; justify-content: center; padding-bottom: 0; border-bottom: 0; }
  /* Short labels ("SEO", "Blog") miss 44px on WIDTH even when tall enough. */
  .site-footer a { min-width: 44px; justify-content: center; }
  .site-header .logo { min-height: 44px; display: inline-flex; align-items: center; }
  .kw-list { padding: 5px 13px; }
}

@media print {
  .site-header, .site-footer, .hero-actions, .skip-link, .wa-fab { display: none; }
  body { background: #fff; color: #000; }
}
}


/* ENTRANCE — pure CSS on purpose: it must start at first paint, and the motion
   libraries are deferred so they always arrive later. ~660-780ms total, never
   blocks input. GSAP must not take this over. See CLAUDE.md. */
@keyframes enter-rise {
  from { opacity: 0; transform: translate3d(0, 18px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes enter-mark {
  from { opacity: 0; transform: translate3d(0, 8px, 0) scale(.96); }
  to   { opacity: 1; transform: none; }
}

/* Above-the-fold only; the rest use the scroll-reveal. */
/* Gated on .theme-ready (set by the inline <head> script the moment theme
   resolution finishes): the reveal cannot start before the theme settles, and
   with JS off no animation is declared at all. See CLAUDE.md. */
.theme-ready .enter { animation: enter-rise .42s cubic-bezier(.22, .7, .3, 1) both; }
.theme-ready .enter-1 { animation-delay: .04s; }
.theme-ready .enter-2 { animation-delay: .12s; }
.theme-ready .enter-3 { animation-delay: .20s; }
.theme-ready .enter-4 { animation-delay: .28s; }
.theme-ready .site-header .logo { animation: enter-mark .38s cubic-bezier(.22, .7, .3, 1) both; }

/* Interruptible: first input snaps everything to its final state. */
.entry-done .enter,
.entry-done .site-header .logo { animation: none !important; opacity: 1 !important; transform: none !important; }

/* THEME TOGGLE */
.theme-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: 999px;
  padding: 6px 12px; font: inherit; font-size: .82rem; font-weight: 600;
  cursor: pointer; line-height: 1;
  transition: border-color .16s ease, background .16s ease, transform .16s ease;
}
.theme-toggle:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-1px); }
.theme-toggle:active { transform: translateY(0); }
.theme-toggle:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.theme-toggle .tt-icon { font-size: .95rem; line-height: 1; }
/* Cycles auto -> light -> dark; the label states the current mode. */
.theme-toggle .tt-label { white-space: nowrap; }

/* STEP / ENGAGEMENT MAPS — inline SVG so they inherit the theme via var() and
   GSAP can address each node. Nodes are VISIBLE BY DEFAULT; anim.js only adds
   the scroll stagger, so a blocked library leaves a complete diagram. */
.step-map { margin: 2.4em 0 0; }
.step-map svg { width: 100%; height: auto; display: block; overflow: visible; }
/* Nodes carry the discipline colour on the <g> and the children inherit it, so
   text must opt out of the stroke or it renders outlined. */
.step-map text { stroke: none; }
.step-map .pnum, .step-map .wnum {
  font-family: var(--font-mono); font-size: 13px; font-weight: 600; fill: var(--muted);
}
.step-map .wnum { fill: var(--text); font-size: 12px; }
.step-map .plabel, .step-map .wlabel {
  font-family: var(--font-body); font-size: 15px; font-weight: 600; fill: var(--text);
}
.step-map .wlabel { font-size: 13.5px; }
.step-map .wdisc {
  font-family: var(--font-body); font-size: 11px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;   /* fill inherited from <g> */
}
.work-map { margin-bottom: .5em; }

/* Contained scroller, not display:none and not a shrink — a 15px label would
   render at ~5px here. Same pattern as .table-scroll. See CLAUDE.md. */
@media (max-width: 860px) {
  .step-map {
    overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
    padding: 4px 2px 10px; scroll-snap-type: x proximity;
  }
  .step-map svg { min-width: 620px; }
  .work-map svg { min-width: 660px; }
}

/* WhatsApp FAB. Bottom-LEFT deliberately: the theme toggle is top-right in the
   sticky header, so the left corner is the free one. z-index 40 keeps it BELOW
   the header (50) — a floating button must never cover the nav. */
.wa-fab {
  position: fixed; left: 20px; bottom: 20px; z-index: 40;
  display: inline-flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; border-radius: 50%;
  background: #25d366; color: #05231a;   /* WhatsApp brand green; icon is near-black for 8.6:1 */
  border: 1px solid rgba(0,0,0,.12);
  box-shadow: var(--shadow-md);
  transition: transform .16s cubic-bezier(.22,.7,.3,1), box-shadow .16s ease;
}
.wa-fab:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.wa-fab:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
.wa-fab:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
/* 48px stays over the 44px tap floor while giving back some of the 360px gutter. */
@media (max-width: 620px) { .wa-fab { width: 48px; height: 48px; left: 12px; bottom: 12px; } }
@media (prefers-reduced-motion: reduce) { .wa-fab { transition: none; } }
