/* ==========================================================================
   eCommWiz — the one stylesheet.
   Order: fonts → tokens → reset → layout escape → primitives → header → footer → motion
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Fonts — self-hosted, Latin subset, WOFF2 only.
   No Google CDN. Five faces, 45.7KB total.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "EW Display";
  src: url("fonts/ew-display-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "EW Display";
  src: url("fonts/ew-display-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "EW Sans";
  src: url("fonts/ew-sans-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "EW Sans";
  src: url("fonts/ew-sans-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "EW Mono";
  src: url("fonts/ew-mono-500.woff2") format("woff2");
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */
:root {
  --void:      oklch(0.134 0.045 260);
  --ink:       oklch(0.174 0.046 273);
  --raise:     oklch(0.227 0.049 273);
  --line:      oklch(0.320 0.058 275);
  --mute:      oklch(0.722 0 0);
  --paper:     oklch(0.943 0 0);
  --violet:    oklch(0.579 0.184 287);
  --violet-hi: oklch(0.698 0.187 302);
  --blue:      oklch(0.634 0.196 253);

  /* Roles. violet-hi carries text and fills because plain violet is 4.36:1
     on void -- fine for large type and UI, short of AA for body copy. */
  --accent:    var(--violet-hi);
  --accent-2:  var(--blue);
  --accent-dp: var(--violet);

  --f-display: "EW Display", "Helvetica Neue", Arial, sans-serif;
  --f-body: "EW Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  --f-mono: "EW Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --pad: clamp(1.25rem, 5vw, 4rem);
  --gut: min(var(--pad), 5vw);
  --wrap: 84rem;
  --read: 44rem;

  --s1: clamp(1rem, 2vw, 1.5rem);
  --s2: clamp(2rem, 4vw, 3rem);
  --s3: clamp(3.5rem, 7vw, 6rem);
  --s4: clamp(5rem, 11vw, 9rem);

  --head-h: 4.5rem;

  /* Derived, not chosen. The sigil's hat edge runs dx=12 over dy=-30, which
     is 21.8deg off vertical. Every diagonal on this site uses that angle. */
  --hat: 21.8deg;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--void);
  color: var(--paper);
  font-family: var(--f-body);
  font-size: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

img, video, svg, canvas { display: block; max-width: 100%; height: auto; }

h1, h2, h3, h4 {
  font-family: var(--f-display);
  font-weight: 800;
  line-height: 0.95;
  letter-spacing: -0.02em;
  margin: 0 0 0.5em;
  text-wrap: balance;
}

p { margin: 0 0 1em; text-wrap: pretty; }

a { color: inherit; text-decoration-color: color-mix(in oklch, currentColor 35%, transparent); text-underline-offset: 0.2em; }
a:hover { text-decoration-color: currentColor; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.skip {
  position: absolute; left: -9999px;
  background: var(--paper); color: var(--void);
  padding: 0.75rem 1rem; z-index: 200; border-radius: 4px;
}
.skip:focus { left: 1rem; top: 1rem; }

.mono { font-family: var(--f-mono); font-size: 0.8125rem; letter-spacing: 0.06em; color: var(--mute); }

/* --------------------------------------------------------------------------
   3. Layout escape
   WordPress wraps post content in a constrained layout that caps every child
   at contentSize. Percentage break-outs compute to zero against it, so the
   wrapper is unconstrained here and each section declares its own width.
   -------------------------------------------------------------------------- */
.site-main,
.site-main > .entry-content,
.wp-site-blocks > .site-main {
  max-width: none;
  padding-inline: 0;
  margin-inline: 0;
}

.site-main > * { max-width: none; }

/* The article template keeps a reading measure. */
.article .wp-block-post-content > * { max-width: var(--read); margin-inline: auto; }
.article .wp-block-post-content > .alignwide { max-width: var(--wrap); }
.article .wp-block-post-content > .alignfull { max-width: none; }

/* --------------------------------------------------------------------------
   4. Section primitives
   -------------------------------------------------------------------------- */
.sec {
  position: relative;
  padding: var(--s4) var(--pad);
}
.sec__in { max-width: var(--wrap); margin-inline: auto; }
.sec--mid { min-height: 60vh; display: grid; align-content: center; max-width: var(--wrap); margin-inline: auto; }
.sec--tight { padding-block: var(--s3); }

.eyebrow {
  font-family: var(--f-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
  margin-bottom: var(--s1);
}

.sec__num {
  font-family: var(--f-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  color: var(--accent);
  border-top: 1px solid var(--line);
  padding-top: 0.75rem;
}

.d-1 { font-size: clamp(2.75rem, 9vw, 7rem); }
.d-2 { font-size: clamp(2.25rem, 6vw, 4.5rem); }
.lede { font-size: clamp(1.0625rem, 0.98rem + 0.4vw, 1.375rem); color: color-mix(in oklch, var(--paper) 78%, transparent); max-width: 46ch; }

.rule { border: 0; border-top: 1px solid var(--line); margin: 0; }

/* --------------------------------------------------------------------------
   5. CTA
   -------------------------------------------------------------------------- */
.cta {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--f-mono);
  font-size: 0.875rem;
  letter-spacing: 0.06em;
  text-decoration: none;
  padding: 0.9rem 1.4rem;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--void);
  border-radius: 2px;
  transition: background 0.25s var(--ease), color 0.25s var(--ease);
}
.cta:hover { background: transparent; color: var(--accent); }
.cta--ghost { background: transparent; color: var(--paper); border-color: var(--line); }
.cta--ghost:hover { background: var(--paper); color: var(--void); border-color: var(--paper); }
.cta--sm { padding: 0.6rem 1rem; font-size: 0.8125rem; }
.cta__arrow { transition: transform 0.25s var(--ease); }
.cta:hover .cta__arrow { transform: translateX(0.25rem); }

.ctas { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: var(--s2); }

/* --------------------------------------------------------------------------
   6. Header
   -------------------------------------------------------------------------- */
.site-head {
  position: sticky;
  top: 0;
  z-index: 100;
  background: color-mix(in oklch, var(--void) 82%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid color-mix(in oklch, var(--line) 60%, transparent);
}

.site-head__in {
  max-width: var(--wrap);
  margin-inline: auto;
  min-height: var(--head-h);
  padding-inline: var(--gut);
  display: flex;
  align-items: center;
  gap: var(--s1);
}

.brand { display: flex; align-items: center; gap: 0.55rem; text-decoration: none; margin-right: auto; }
.brand__mark { width: 2.1rem; height: 2.1rem; flex: none; color: var(--accent); }
.brand__mark svg { width: 100%; height: 100%; display: block; }

/* Wordmark. The W is the beard silhouette, so it sits on its own baseline
   nudge rather than inheriting the font's. */
.brand__word {
  font-family: var(--f-display);
  font-weight: 700;
  letter-spacing: -0.035em;
  font-size: 1.25rem;
  display: flex;
  align-items: baseline;
  color: var(--paper);
}
.brand__word .lo { color: var(--mute); }
.brand__word .w {
  display: inline-block;
  width: 0.92em;
  height: 0.73em;
  margin: 0 0.02em;
  transform: translateY(0.06em);
  color: var(--accent);
}
.brand__word .w svg { width: 100%; height: 100%; display: block; }

.nav { display: none; gap: 1.5rem; }
.nav a { font-size: 0.9375rem; text-decoration: none; color: color-mix(in oklch, var(--paper) 80%, transparent); }
.nav a:hover { color: var(--paper); }

.site-head .cta--sm { display: none; }

.burger {
  appearance: none;
  background: none;
  border: 1px solid var(--line);
  border-radius: 2px;
  width: 2.75rem; height: 2.5rem;
  display: grid; align-content: center; justify-items: center; gap: 0.35rem;
  cursor: pointer;
}
.burger span { display: block; width: 1.1rem; height: 1.5px; background: var(--paper); transition: transform 0.3s var(--ease), opacity 0.3s var(--ease); }
.burger[aria-expanded="true"] span:first-child { transform: translateY(0.425rem) rotate(45deg); }
.burger[aria-expanded="true"] span:last-child { transform: translateY(-0.425rem) rotate(-45deg); }

.m-nav {
  display: grid;
  gap: 0.25rem;
  padding: var(--s1) var(--gut) var(--s2);
  border-top: 1px solid var(--line);
}
.m-nav[hidden] { display: none; }
.m-nav a { font-family: var(--f-display); font-size: 1.75rem; font-weight: 800; letter-spacing: -0.02em; text-decoration: none; padding-block: 0.35rem; }
.m-nav .cta { font-family: var(--f-mono); font-size: 0.875rem; font-weight: 400; justify-self: start; margin-top: var(--s1); }

@media (min-width: 60rem) {
  .nav { display: flex; }
  .site-head .cta--sm { display: inline-flex; }
  .burger, .m-nav { display: none !important; }
}

/* --------------------------------------------------------------------------
   7. Footer
   -------------------------------------------------------------------------- */
/* Footer styles live in section 27. */

/* --------------------------------------------------------------------------
   8. Motion
   Scroll-driven where supported, no library. Headless Chromium defaults to
   prefers-reduced-motion: reduce, so motion tests must pass no-preference.
   -------------------------------------------------------------------------- */
.rv { opacity: 1; }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .rv {
      animation: rv-in linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 28%;
    }
  }
}

@keyframes rv-in {
  from { opacity: 0; transform: translateY(1.25rem); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .rv { animation: none; opacity: 1; transform: none; }
}

/* ==========================================================================
   9. Home page
   Sections declare their own width. Nothing here relies on .entry-content.
   ========================================================================== */

/* ---- 9.1 Hero -----------------------------------------------------------
   Radial glow sits behind everything at low opacity. It is a background,
   not an element, so it cannot trap pointer events.
   ------------------------------------------------------------------------ */
.hero {
  position: relative;
  min-height: clamp(34rem, 88svh, 54rem);
  display: grid;
  align-content: center;
  padding: calc(var(--head-h) + var(--s3)) var(--pad) var(--s3);
  overflow: clip;
  isolation: isolate;
}

.hero::before {
  content: "";
  position: absolute;
  inset: -20% -10% auto;
  height: 130%;
  z-index: -1;
  background:
    radial-gradient(58% 46% at 50% 78%, color-mix(in oklch, var(--violet) 46%, transparent), transparent 72%),
    radial-gradient(38% 32% at 22% 42%, color-mix(in oklch, var(--blue) 22%, transparent), transparent 70%);
  filter: blur(8px);
  pointer-events: none;
}

.hero__in {
  max-width: 62rem;
  margin-inline: auto;
  text-align: center;
  position: relative;
}
/* Scrim. The circuit art is busy right where the CTAs sit, and a ghost
   button over glowing traces is unreadable. This keeps the art visible
   and the buttons legible. */
.hero__in::before {
  content: "";
  position: absolute;
  inset: -12% -18%;
  z-index: -1;
  background: radial-gradient(58% 52% at 50% 46%,
    color-mix(in oklch, var(--void) 88%, transparent) 0 46%,
    transparent 100%);
  pointer-events: none;
}

.hero h1 {
  font-size: clamp(2.25rem, 5.6vw, 4.75rem);
  line-height: 1.02;
  margin-bottom: var(--s1);
  text-wrap: balance;
}
.hero h1 

@media (prefers-reduced-motion: reduce) {
  .rot > b { transition: none; }
}

.hero__lede {
  font-size: clamp(1.0625rem, 0.95rem + 0.5vw, 1.375rem);
  color: color-mix(in oklch, var(--paper) 76%, transparent);
  max-width: 40ch;
  margin-inline: auto;
}
.hero .ctas { justify-content: center; }

.hero__cue {
  font-family: var(--f-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  color: var(--mute);
  margin-top: var(--s3);
}

/* ---- 9.3 Marquee band --------------------------------------------------- */

@media (prefers-reduced-motion: reduce) { .strip__track { animation: none; } }

/* ---- 9.4 Section head + ghost word -------------------------------------- */
.sec__head { display: grid; gap: var(--s1); margin-bottom: var(--s3); }
@media (min-width: 60rem) {
  .sec__head { grid-template-columns: 13rem 1fr; gap: var(--s2); align-items: start; }
}

.ghost { position: relative; overflow: clip; }
.ghost__word {
  position: absolute;
  top: 3%;
  right: clamp(0.5rem, 3vw, 3rem);
  z-index: 0;
  font-family: var(--f-display);
  font-weight: 700;
  font-size: clamp(4.5rem, 13vw, 11rem);
  line-height: 0.8;
  letter-spacing: -0.05em;
  color: transparent;
  -webkit-text-stroke: 1px color-mix(in oklch, var(--line) 78%, transparent);
  pointer-events: none;
  margin: 0;
}
.ghost > .sec__in { position: relative; z-index: 1; }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .ghost__word { animation: drift linear both; animation-timeline: view(); animation-range: entry cover; }
  }
}
@keyframes drift { from { transform: translateX(4%); } to { transform: translateX(-6%); } }

/* ---- 9.5 Bucket columns ------------------------------------------------- */
.buckets { display: grid; gap: 1px; background: var(--line); border-block: 1px solid var(--line); }
@media (min-width: 56rem) { .buckets { grid-template-columns: repeat(3, 1fr); } }

.bucket { background: var(--void); padding: var(--s2) clamp(1.25rem, 3vw, 2.25rem) var(--s3); display: grid; gap: 0.85rem; align-content: start; }
.bucket__k { font-family: var(--f-mono); font-size: 0.75rem; letter-spacing: 0.16em; color: var(--accent); }
.bucket h3 { font-size: clamp(1.5rem, 2.4vw, 2rem); margin: 0; }
.bucket p { color: var(--mute); margin: 0; }
.bucket ul { list-style: none; margin: 0.5rem 0 0; padding: 0; display: grid; gap: 0.4rem; }
.bucket li { font-size: 0.9375rem; color: color-mix(in oklch, var(--paper) 72%, transparent); padding-left: 1.1rem; position: relative; }
.bucket li::before { content: "/"; position: absolute; left: 0; color: var(--violet); font-family: var(--f-mono); }

/* ---- 9.6 Stat strip ----------------------------------------------------- */
.stats { display: grid; gap: 1px; background: var(--line); border-block: 1px solid var(--line); }
@media (min-width: 46rem) { .stats { grid-template-columns: repeat(4, 1fr); } }
.stat { background: var(--ink); padding: var(--s2) clamp(1.25rem, 3vw, 2rem); display: grid; gap: 0.35rem; }
.stat__n { font-family: var(--f-display); font-weight: 700; font-size: clamp(2.25rem, 4.4vw, 3.5rem); line-height: 1; letter-spacing: -0.03em; color: var(--paper); font-variant-numeric: tabular-nums; }
.stat__n small { font-size: 0.42em; color: var(--accent); margin-left: 0.2em; letter-spacing: 0; }
.stat__l { font-size: 0.875rem; color: var(--mute); margin: 0; max-width: 26ch; }

/* ---- 9.7 Work cards ----------------------------------------------------- */
.work { display: grid; gap: var(--s3); }
.workcard { display: grid; gap: var(--s2); align-items: center; }
@media (min-width: 62rem) {
  .workcard { grid-template-columns: 1.35fr 1fr; }
  .workcard--flip .workcard__media { order: 2; }
}

.workcard__media {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  background: var(--ink);
  box-shadow: 0 30px 70px -30px color-mix(in oklch, var(--violet) 55%, transparent);
}
.workcard__media img { width: 100%; height: auto; display: block; }
.workcard__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, transparent 62%, color-mix(in oklch, var(--void) 92%, transparent));
  pointer-events: none;
}

.workcard__k { font-family: var(--f-mono); font-size: 0.75rem; letter-spacing: 0.16em; color: var(--accent); }
.workcard h3 { font-size: clamp(1.75rem, 3vw, 2.5rem); margin: 0.5rem 0 0.75rem; }
.workcard p { color: var(--mute); }
.workcard__meta { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: var(--s1); }
.workcard__meta span {
  font-family: var(--f-mono); font-size: 0.6875rem; letter-spacing: 0.1em;
  border: 1px solid var(--line); border-radius: 2px; padding: 0.35rem 0.55rem; color: var(--mute);
}

.roster { display: flex; flex-wrap: wrap; gap: 0.5rem 0.75rem; margin-top: var(--s2); }
.roster span { font-family: var(--f-mono); font-size: 0.8125rem; color: var(--mute); }
.roster span::after { content: " /"; color: var(--violet); }
.roster span:last-child::after { content: ""; }

/* ---- 9.8 Contrast flip: pricing ----------------------------------------
   The one inverted section on the page. Publishing prices is the argument,
   so it gets the visual break that makes you stop scrolling.
   ------------------------------------------------------------------------ */
.flip { background: var(--paper); color: var(--void); }
.flip .eyebrow, .flip .sec__num { color: color-mix(in oklch, var(--void) 62%, transparent); border-color: color-mix(in oklch, var(--void) 18%, transparent); }
.flip .lede { color: color-mix(in oklch, var(--void) 72%, transparent); }
.flip .cta { background: var(--void); border-color: var(--void); color: var(--paper); }
.flip .cta:hover { background: transparent; color: var(--void); }

.price { border-top: 1px solid color-mix(in oklch, var(--void) 16%, transparent); }
.price__row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.5rem 1.5rem;
  align-items: baseline;
  padding: 1.05rem 0;
  border-bottom: 1px solid color-mix(in oklch, var(--void) 12%, transparent);
}
.price__name { font-family: var(--f-display); font-weight: 600; font-size: clamp(1.0625rem, 1.6vw, 1.375rem); letter-spacing: -0.015em; }
.price__n { font-family: var(--f-mono); font-size: clamp(1rem, 1.5vw, 1.25rem); white-space: nowrap; font-variant-numeric: tabular-nums; }
.price__n b { font-weight: 500; }
.price__note { grid-column: 1 / -1; font-size: 0.875rem; color: color-mix(in oklch, var(--void) 58%, transparent); margin: 0; }

/* ---- 9.9 Rules ---------------------------------------------------------- */
.rules { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 4px; overflow: hidden; }
@media (min-width: 52rem) { .rules { grid-template-columns: repeat(2, 1fr); } }
.rule-i { background: var(--ink); padding: var(--s2) clamp(1.25rem, 3vw, 2rem); display: grid; gap: 0.5rem; align-content: start; }
.rule-i__k { font-family: var(--f-mono); font-size: 0.75rem; letter-spacing: 0.14em; color: var(--accent); }
.rule-i h3 { font-size: 1.25rem; margin: 0; }
.rule-i p { color: var(--mute); margin: 0; font-size: 0.9375rem; }

/* ---- 9.10 Versus rows --------------------------------------------------- */
.vs { display: grid; gap: 1px; background: var(--line); border-block: 1px solid var(--line); }
.vs__row { background: var(--void); display: grid; gap: 0.75rem; padding: clamp(1.1rem, 2.4vw, 1.75rem) clamp(0.75rem, 2vw, 1.25rem); transition: background .25s var(--ease); }
.vs__row:hover { background: var(--ink); }
@media (min-width: 56rem) { .vs__row { grid-template-columns: 1fr 1fr; gap: var(--s2); align-items: center; } }
.vs__them, .vs__us { display: grid; grid-template-columns: 1.4rem 1fr; gap: 0.75rem; align-items: start; font-size: 0.9375rem; }
.vs__them { color: var(--mute); }
.vs__us { color: var(--paper); }
.vs__mark { font-family: var(--f-mono); line-height: 1.5; }
.vs__them .vs__mark { color: color-mix(in oklch, var(--mute) 80%, transparent); }
.vs__us .vs__mark { color: var(--accent); }

/* ---- 9.11 Closing CTA --------------------------------------------------- */
.close { text-align: center; position: relative; }
.close h2 { font-size: clamp(2.25rem, 5.5vw, 4rem); }
.close .ctas { justify-content: center; }
.close__mark { width: clamp(3rem, 6vw, 4.5rem); margin: 0 auto var(--s2); color: var(--accent); opacity: .9; position: relative; }
.close__mark svg { width: 100%; height: auto; display: block; }

/* The cast. boot('footcast') drives the timing; everything here is the part
   that has to survive without JavaScript, which is why the resting state is
   the plain mark and every rule below is additive.

   Two separate animations on purpose. The ember is filter only, for the same
   reason the footer's is: transform stays free for the cast-in and the hover
   re-cast, which GSAP owns. The flare is a one-shot bloom on ::after, so it
   can be restarted on hover by removing and re-adding the class without ever
   touching the mark's own box. */
@media (prefers-reduced-motion: no-preference) {
  .close__mark.is-cast svg { animation: castEmber 6s ease-in-out infinite; }
  .close__mark.is-flare::after { animation: castFlare .78s ease-out; }
}
@keyframes castEmber {
  0%, 100% { filter: drop-shadow(0 0 0 transparent); }
  50%      { filter: drop-shadow(0 0 14px color-mix(in oklch, var(--accent) 58%, transparent)); }
}
.close__mark::after {
  content: '';
  position: absolute;
  inset: -45%;
  border-radius: 50%;
  background: radial-gradient(circle, color-mix(in oklch, var(--accent) 46%, transparent) 0%, transparent 64%);
  opacity: 0;
  pointer-events: none;
}
@keyframes castFlare {
  0%   { opacity: 0; transform: scale(.3); }
  20%  { opacity: 1; }
  100% { opacity: 0; transform: scale(1.55); }
}

/* ==========================================================================
   10. Blueprint grid + diagonals
   The dashed rules are the visual argument for RULE_001. A site that claims
   it measures things should look measured.
   ========================================================================== */

/* ---- 10.1 Fixed gutter rules -------------------------------------------
   One fixed element, behind everything, that never scrolls or reflows.
   Hidden on narrow screens where the gutters are too tight to read.
   ------------------------------------------------------------------------ */
.grid-lines {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  display: none;
}
.grid-lines__in {
  max-width: var(--wrap);
  height: 100%;
  margin-inline: auto;
  padding-inline: var(--gut);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.grid-lines__in span {
  border-left: 1px dashed color-mix(in oklch, var(--line) 55%, transparent);
}
.grid-lines__in span:last-child {
  border-right: 1px dashed color-mix(in oklch, var(--line) 55%, transparent);
}

@media (min-width: 64rem) { .grid-lines { display: block; } }

/* The flip section is light, so the rules have to invert with it. */
.flip { position: relative; z-index: 1; }

/* Content sits above the rules without needing a stacking context per section. */
.site-main > * { position: relative; z-index: 1; }
.site-foot { position: relative; z-index: 2; }

/* .site-head is deliberately NOT in that rule. It is `position: sticky` up in
   section 3, and `relative` here quietly beat it on source order -- which is
   why the header never stuck, and why navhide, the --head-h scroll offsets and
   the star wizard's return-flight target were all computing against a header
   that in fact scrolled away. Sticky already makes its own stacking context,
   so nothing here is lost. */

/* The template part arrives wrapped in .wp-block-template-part, and a sticky
   box cannot escape its parent's, so the wrapper is what has to stick. */
.wp-block-template-part:has(> .site-head) {
  position: sticky;
  top: 0;
  z-index: 100;
}

/* ---- 10.2 Hatched divider ----------------------------------------------
   Cut at --hat. A full-bleed diagonal at 21.8deg off vertical would rise
   3,199px across a 1280px viewport, so the angle lives in a hatch texture
   instead of a section edge.
   ------------------------------------------------------------------------ */


/* ---- 10.3 Nav slash prefix ----------------------------------------------
   The slash already separates items in the marquee and the bucket lists.
   Reusing it in the nav makes it a system rather than a flourish.
   ------------------------------------------------------------------------ */
.nav a::before,
.m-nav a:not(.cta)::before {
  content: "/";
  color: var(--violet);
  margin-right: 0.35em;
  font-family: var(--f-mono);
}
.nav a { display: inline-flex; align-items: baseline; }

/* ---- 10.4 Price rows: dashed rules, hover reveal ------------------------ */
.price__row {
  border-bottom-style: dashed;
  padding-inline: clamp(0.5rem, 1.5vw, 1rem);
  transition: background .22s var(--ease);
}
.price__row:hover { background: color-mix(in oklch, var(--void) 5%, transparent); }
.price__row .price__n { transition: transform .22s var(--ease); }
.price__row:hover .price__n { transform: translateX(-0.15rem); }

/* Marker at the row head, in the hat angle. */
.price__row::before {
  content: "";
  grid-row: 1 / span 2;
  width: 3px;
  align-self: stretch;
  margin-right: 0.9rem;
  background: var(--void);
  opacity: 0;
  transform: skewX(calc(-1 * var(--hat)));
  transition: opacity .22s var(--ease);
}
.price__row:hover::before { opacity: .85; }
.price__row { grid-template-columns: auto 1fr auto; }
.price__note { grid-column: 2 / -1; }

/* ---- 10.5 Skewed accents ------------------------------------------------ */
.stat { position: relative; overflow: hidden; }
.stat::after {
  content: "";
  position: absolute;
  right: -1.5rem;
  bottom: -1.5rem;
  width: 4.5rem;
  height: 7rem;
  background: color-mix(in oklch, var(--violet) 12%, transparent);
  transform: skewX(calc(-1 * var(--hat)));
  pointer-events: none;
}

/* The flip section is opaque and light; the fixed dark rules would vanish
   behind it. Redraw them inside the section in the inverted color. */
.flip { overflow: hidden; }
.flip::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gut);
  display: none;
  background:
    repeating-linear-gradient(to right,
      transparent 0 calc(25% - 1px),
      color-mix(in oklch, var(--void) 12%, transparent) calc(25% - 1px) 25%);
}
@media (min-width: 64rem) { .flip::before { display: block; } }
.flip > .sec__in { position: relative; z-index: 1; }

/* ==========================================================================
   11. Form
   Rendered by the theme, so it inherits the tokens instead of fighting a
   plugin's stylesheet.
   ========================================================================== */
.f { display: grid; gap: var(--s2); max-width: 46rem; }
.f__grid { display: grid; gap: var(--s1); }
@media (min-width: 44rem) { .f__grid { grid-template-columns: 1fr 1fr; } }

.f__field { display: grid; gap: 0.45rem; }
.f__label {
  font-family: var(--f-mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mute);
}
.f__label i { font-style: normal; color: var(--violet); text-transform: none; letter-spacing: 0.06em; }

.f input[type="text"],
.f input[type="email"],
.f select,
.f textarea {
  font: inherit;
  color: var(--paper);
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.85rem 0.95rem;
  width: 100%;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.f textarea { resize: vertical; min-height: 9rem; }
.f input:focus, .f select:focus, .f textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--raise);
}
.f ::placeholder { color: color-mix(in oklch, var(--mute) 70%, transparent); }

.f select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--accent) 50%),
                    linear-gradient(135deg, var(--accent) 50%, transparent 50%);
  background-position: calc(100% - 1.1rem) 55%, calc(100% - 0.75rem) 55%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2.5rem;
}

.f__set { border: 0; padding: 0; margin: 0; display: grid; gap: 0.7rem; }
.f__chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.f__chip input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.f__chip label {
  display: inline-block;
  font-size: 0.875rem;
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 0.5rem 0.8rem;
  cursor: pointer;
  color: var(--mute);
  transition: color .18s var(--ease), border-color .18s var(--ease), background .18s var(--ease);
}
.f__chip label:hover { color: var(--paper); border-color: var(--mute); }
.f__chip input:checked + label {
  color: var(--void);
  background: var(--accent);
  border-color: var(--accent);
}
/* Keyboard users get a ring on the label, since the input itself is offscreen. */
.f__chip input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 3px; }

.f__hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.f__foot { display: grid; gap: 0.9rem; justify-items: start; }
.f__fine { font-size: 0.8125rem; color: var(--mute); margin: 0; }

.f__err { font-size: 0.8125rem; color: oklch(0.72 0.17 25); font-family: var(--f-mono); }
.f input:user-invalid, .f textarea:user-invalid { border-color: oklch(0.62 0.17 25); }

.f__ok {
  border: 1px solid var(--accent);
  border-left-width: 3px;
  border-radius: 3px;
  background: var(--ink);
  padding: var(--s2);
  max-width: 44rem;
  display: grid;
  gap: 0.5rem;
}
.f__ok h3 { margin: 0; font-size: clamp(1.5rem, 3vw, 2rem); }
.f__ok p { margin: 0; color: var(--mute); }
.f__ok .mono { margin: 0; }

/* ==========================================================================
   12. Service rows
   The full service list, without twelve tiles. Dashed rules and a hover
   reveal, same system as the price rows.
   ========================================================================== */
.svc { border-top: 1px dashed var(--line); }
.svc__row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.35rem 1rem;
  align-items: baseline;
  padding: 1.15rem clamp(0.5rem, 1.5vw, 1rem);
  border-bottom: 1px dashed var(--line);
  text-decoration: none;
  color: inherit;
  transition: background .2s var(--ease);
}
@media (min-width: 52rem) {
  .svc__row { grid-template-columns: auto 1fr auto auto; align-items: center; }
}
.svc__row:hover { background: var(--ink); }

.svc__k { font-family: var(--f-mono); font-size: 0.75rem; letter-spacing: 0.12em; color: var(--violet); }
.svc__name { font-family: var(--f-display); font-weight: 600; font-size: clamp(1.125rem, 1.9vw, 1.5rem); letter-spacing: -0.015em; }
.svc__desc { grid-column: 2 / -1; font-size: 0.9375rem; color: var(--mute); margin: 0; }
@media (min-width: 52rem) { .svc__desc { grid-column: auto; text-align: right; max-width: 32ch; justify-self: end; } }
.svc__n { font-family: var(--f-mono); font-size: 0.875rem; color: var(--accent); white-space: nowrap; }
.svc__go { color: var(--accent); opacity: 0; transform: translateX(-0.35rem); transition: opacity .2s var(--ease), transform .2s var(--ease); }
.svc__row:hover .svc__go { opacity: 1; transform: none; }
@media (max-width: 51.99rem) { .svc__go { display: none; } }

/* ==========================================================================
   13. Prose pages (about, long copy)
   ========================================================================== */
.prose { max-width: var(--read); display: grid; gap: 1.1rem; }
.prose h3 { font-size: clamp(1.375rem, 2.4vw, 1.75rem); margin: var(--s1) 0 0; }
.prose p { color: color-mix(in oklch, var(--paper) 78%, transparent); margin: 0; }
.prose strong { color: var(--paper); font-weight: 700; }

.split { display: grid; gap: var(--s2); }
@media (min-width: 62rem) { .split { grid-template-columns: 1.2fr 1fr; gap: var(--s3); align-items: start; } }
.aside { border: 1px solid var(--line); border-radius: 4px; background: var(--ink); padding: var(--s2); display: grid; gap: 0.75rem; align-content: start; }
.aside h3 { margin: 0; font-size: 1.125rem; }
.aside ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.aside li { font-size: 0.9375rem; color: var(--mute); padding-left: 1.1rem; position: relative; }
.aside li::before { content: "/"; position: absolute; left: 0; color: var(--violet); font-family: var(--f-mono); }

/* Interior heroes: shorter than the home page, same glow. */
.hero--sm {
  min-height: auto;
  padding-block: calc(var(--head-h) + var(--s3)) var(--s3);
}
.hero--sm h1 { font-size: clamp(2.25rem, 5vw, 4rem); }
.hero--sm::before { height: 100%; inset: -10% -10% auto; }

/* ==========================================================================
   14. Money-page components
   ========================================================================== */

/* ---- 14.1 Numbered steps ------------------------------------------------ */
.steps { display: grid; gap: 1px; background: var(--line); border-block: 1px solid var(--line); }
@media (min-width: 56rem) { .steps { grid-template-columns: repeat(4, 1fr); } }
.step {
  background: var(--void);
  padding: var(--s2) clamp(1.25rem, 2.5vw, 1.75rem) var(--s3);
  display: grid;
  gap: 0.6rem;
  align-content: start;
  position: relative;
}
.step__n {
  font-family: var(--f-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  color: var(--accent);
  padding-bottom: 0.6rem;
  border-bottom: 1px dashed var(--line);
  margin-bottom: 0.3rem;
}
.step h3 { font-size: 1.1875rem; margin: 0; }
.step p { margin: 0; color: var(--mute); font-size: 0.9375rem; }

/* ---- 14.2 Deliverables list --------------------------------------------- */
.deliv { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 4px; overflow: hidden; }
@media (min-width: 48rem) { .deliv { grid-template-columns: repeat(2, 1fr); } }
.deliv__i { background: var(--ink); padding: 1.25rem 1.4rem; display: grid; grid-template-columns: 1.5rem 1fr; gap: 0.85rem; align-items: start; }
.deliv__i span:first-child { color: var(--accent); font-family: var(--f-mono); font-size: 0.875rem; line-height: 1.5; }
.deliv__i b { display: block; font-family: var(--f-display); font-weight: 600; font-size: 1.0625rem; letter-spacing: -0.01em; margin-bottom: 0.15rem; }
.deliv__i p { margin: 0; font-size: 0.9375rem; color: var(--mute); }

/* ---- 14.3 Price callout ------------------------------------------------- */
.pcall {
  border: 1px solid var(--accent);
  border-left-width: 3px;
  border-radius: 3px;
  background: var(--ink);
  padding: var(--s2);
  display: grid;
  gap: 0.6rem;
  max-width: 42rem;
}
.pcall__n { font-family: var(--f-display); font-weight: 700; font-size: clamp(2rem, 4vw, 2.75rem); letter-spacing: -0.03em; line-height: 1; }
.pcall__n small { font-size: 0.36em; color: var(--accent); letter-spacing: 0; margin-left: 0.3em; }
.pcall p { margin: 0; color: var(--mute); }

/* ---- 14.4 Related links ------------------------------------------------- */
.rel { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.rel a {
  font-family: var(--f-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 0.6rem 0.85rem;
  color: var(--mute);
  transition: color .2s var(--ease), border-color .2s var(--ease), background .2s var(--ease);
}
.rel a::before { content: "/ "; color: var(--violet); }
.rel a:hover { color: var(--paper); border-color: var(--accent); background: var(--ink); }

/* ---- 14.5 Breadcrumb ---------------------------------------------------- */
.crumb { font-family: var(--f-mono); font-size: 0.75rem; letter-spacing: 0.1em; color: var(--mute); margin-bottom: var(--s1); }
.crumb a { color: var(--mute); text-decoration: none; }
.crumb a:hover { color: var(--accent); }
.crumb span { color: var(--violet); padding-inline: 0.4rem; }

/* ==========================================================================
   15. Photography
   ========================================================================== */
.shot { margin: 0 0 var(--s2); max-width: 30rem; }
.shot img {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--line);
  border-radius: 4px;
  /* Feathered rather than a hard rectangle. */
  mask-image: radial-gradient(120% 110% at 50% 40%, #000 62%, transparent 100%);
  -webkit-mask-image: radial-gradient(120% 110% at 50% 40%, #000 62%, transparent 100%);
}
.shot figcaption {
  font-family: var(--f-mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: var(--mute);
  margin-top: 0.75rem;
}

/* Hero backdrop art. Sits under the CSS glow, above nothing. Decorative, so
   it is a background layer rather than an <img> that assistive tech reads. */
.hero::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  height: 78%;
  z-index: -2;
  /* Mobile-first: the 2000px file was the LCP resource on a 360px screen and
     cost ~1s. Small file by default; the large one only where it is visible. */
  background: url("ecommwiz-arcane-circuit-hero-backdrop-sm.webp") center bottom / cover no-repeat;
  opacity: .42;
  pointer-events: none;
}
.hero--sm::after { height: 100%; opacity: .3; }
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .hero::after { animation: heroDrift linear both; animation-timeline: view(); animation-range: cover; }
  }
}
@keyframes heroDrift { from { transform: translateY(0) scale(1.02); } to { transform: translateY(-4%) scale(1.08); } }

/* ==========================================================================
   17. Bucket icons
   ========================================================================== */
.bucket__ic {
  width: clamp(2.75rem, 5vw, 3.5rem);
  height: auto;
  margin-bottom: 0.35rem;
  opacity: .9;
}

/* ==========================================================================
   18. Illustrated section bands
   ========================================================================== */
.sec--band { position: relative; overflow: clip; }
.sec--band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  /* Anchored right and height-fitted, so the art never stretches across the
     column the section head occupies. */
  background: var(--band-art) right center / auto 100% no-repeat;
  opacity: .55;
  pointer-events: none;
  /* Second falloff on top of the one baked into the file, because the head
     text sits over the left half and has to stay readable. */
  mask-image: linear-gradient(to right, transparent 0 48%, #000 88%);
  -webkit-mask-image: linear-gradient(to right, transparent 0 48%, #000 88%);
}
.sec--band > .sec__in { position: relative; z-index: 1; }
@media (max-width: 55.99rem) { .sec--band::before { opacity: .22; } }

/* ==========================================================================
   19. Notes: index, cards, article
   ========================================================================== */
.postlist { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 4px; overflow: hidden; }
@media (min-width: 52rem) { .postlist .wp-block-post-template { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--line); } }
.postlist .wp-block-post-template { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; background: var(--line); }
.postlist .wp-block-post-template > li { background: var(--void); }

.postcard { padding: var(--s2) clamp(1.25rem, 3vw, 1.9rem) var(--s3); display: grid; gap: 0.6rem; align-content: start; }
.postcard__date { font-family: var(--f-mono); font-size: 0.75rem; letter-spacing: 0.14em; color: var(--accent); margin: 0; }
.postcard__title { font-size: clamp(1.25rem, 2.2vw, 1.625rem); margin: 0; }
.postcard__title a { text-decoration: none; }
.postcard__title a:hover { color: var(--accent); }
.postcard__ex { color: var(--mute); font-size: 0.9375rem; margin: 0; }
.postcard__ex .wp-block-post-excerpt__more-link { color: var(--accent); }

.pager { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; margin-top: var(--s2); font-family: var(--f-mono); font-size: 0.875rem; }
.pager a, .pager .page-numbers { text-decoration: none; color: var(--mute); border: 1px solid var(--line); border-radius: 2px; padding: 0.45rem 0.7rem; }
.pager a:hover { color: var(--paper); border-color: var(--accent); }
.pager .current { color: var(--void); background: var(--accent); border-color: var(--accent); }

/* ---- Article body ------------------------------------------------------- */
.hero--post .hero__in { text-align: left; max-width: var(--read); margin-inline: 0; }
.hero--post h1 { font-size: clamp(2rem, 4.4vw, 3.25rem); }

.article .wp-block-post-content { font-size: 1.0625rem; }
.article .wp-block-post-content > * + * { margin-top: 1.15em; }
.article .wp-block-post-content h2 { font-size: clamp(1.5rem, 3vw, 2.125rem); margin-top: 2em; }
.article .wp-block-post-content h3 { font-size: clamp(1.25rem, 2.2vw, 1.5rem); margin-top: 1.6em; }
.article .wp-block-post-content p { color: color-mix(in oklch, var(--paper) 80%, transparent); }
.article .wp-block-post-content a { color: var(--accent); }
.article .wp-block-post-content ul,
.article .wp-block-post-content ol { padding-left: 1.3rem; color: color-mix(in oklch, var(--paper) 80%, transparent); }
.article .wp-block-post-content li + li { margin-top: 0.45em; }
.article .wp-block-post-content blockquote {
  border-left: 3px solid var(--accent);
  padding-left: 1.1rem;
  margin-inline: 0;
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.25rem;
  letter-spacing: -0.015em;
}
.article .wp-block-post-content code {
  font-family: var(--f-mono);
  font-size: 0.875em;
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.15em 0.4em;
}
.article .wp-block-post-content pre {
  font-family: var(--f-mono);
  font-size: 0.875rem;
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1.1rem;
  overflow-x: auto;
}
.article .wp-block-post-content pre code { background: none; border: 0; padding: 0; }
.article .wp-block-post-content img { border-radius: 4px; border: 1px solid var(--line); }
.article .wp-block-post-content hr { border: 0; border-top: 1px dashed var(--line); margin-block: 2em; }

/* ==========================================================================
   20. Sticky contact dock
   Replaces mystickyelements-pro: one plugin's stylesheet, script and icon
   font, for a fixed list of four links.
   ========================================================================== */
.dock {
  position: fixed;
  right: clamp(0.85rem, 2vw, 1.5rem);
  bottom: clamp(0.85rem, 2vw, 1.5rem);
  z-index: 120;
  display: grid;
  justify-items: end;
  gap: 0.6rem;
}

.dock__toggle {
  order: 2;
  position: relative;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--void);
  cursor: pointer;
  display: grid;
  place-items: center;
  box-shadow: 0 10px 30px -8px color-mix(in oklch, var(--violet) 70%, transparent);
  transition: transform .28s var(--ease), background .28s var(--ease), color .28s var(--ease);
}
.dock__toggle:hover { transform: translateY(-2px); }
.dock__mark { width: 2rem; height: 2rem; display: block; transition: opacity .22s var(--ease), transform .22s var(--ease); }
.dock__mark svg { width: 100%; height: 100%; display: block; }

/* The close mark is drawn, not an icon font. */
.dock__x { position: absolute; width: 1.1rem; height: 1.1rem; opacity: 0; transform: rotate(-90deg); transition: opacity .22s var(--ease), transform .22s var(--ease); }
.dock__x::before, .dock__x::after {
  content: ""; position: absolute; inset: 50% 0 auto; height: 1.5px;
  background: currentColor; border-radius: 2px;
}
.dock__x::before { transform: rotate(45deg); }
.dock__x::after  { transform: rotate(-45deg); }

.dock__toggle[aria-expanded="true"] { background: var(--void); color: var(--accent); }
.dock__toggle[aria-expanded="true"] .dock__mark { opacity: 0; transform: scale(.6); }
.dock__toggle[aria-expanded="true"] .dock__x { opacity: 1; transform: rotate(0); }

.dock__list { order: 1; list-style: none; margin: 0; padding: 0; display: grid; gap: 0.45rem; justify-items: end; }
.dock__list[hidden] { display: none; }

.dock__item {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  font-family: var(--f-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  color: var(--paper);
  background: color-mix(in oklch, var(--ink) 94%, transparent);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.55rem 0.95rem 0.55rem 0.6rem;
  backdrop-filter: blur(10px);
  white-space: nowrap;
  transition: border-color .2s var(--ease), color .2s var(--ease), transform .2s var(--ease);
}
.dock__item:hover { border-color: var(--accent); color: var(--accent); transform: translateX(-2px); }
.dock__item.is-primary { background: var(--accent); color: var(--void); border-color: var(--accent); }
.dock__item.is-primary:hover { background: var(--violet); border-color: var(--violet); color: var(--void); }

.dock__ic { width: 1.35rem; height: 1.35rem; flex: none; }
.dock__ic svg { width: 100%; height: 100%; display: block; }

/* Staggered entry. Each item is delayed by its index, set inline by app.js. */
.dock__list li {
  opacity: 0;
  transform: translateY(0.4rem);
  transition: opacity .24s var(--ease), transform .24s var(--ease);
  transition-delay: var(--d, 0ms);
}
.dock.is-open .dock__list li { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .dock__list li, .dock__item, .dock__toggle, .dock__mark, .dock__x { transition: none; }
}

/* Keep the dock clear of the footer CTA on small screens. */
@media (max-width: 30rem) {
  .dock__item { font-size: 0.75rem; padding-right: 0.8rem; }
  .dock__toggle { width: 3.1rem; height: 3.1rem; }
}

/* ==========================================================================
   21. FAQ
   Native <details>, so it works with JavaScript disabled and is announced
   correctly by screen readers without any ARIA of our own.
   ========================================================================== */
.faq__list { border-top: 1px dashed var(--line); }
.faq__i { border-bottom: 1px dashed var(--line); }
.faq__i summary {
  list-style: none;
  cursor: pointer;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 1.5rem;
  align-items: center;
  padding: 1.15rem clamp(0.5rem, 1.5vw, 1rem);
  font-family: var(--f-display);
  font-weight: 600;
  font-size: clamp(1.0625rem, 1.7vw, 1.3125rem);
  letter-spacing: -0.015em;
  transition: background .2s var(--ease), color .2s var(--ease);
}
.faq__i summary::-webkit-details-marker { display: none; }
.faq__i summary:hover { background: var(--ink); }
.faq__i[open] summary { color: var(--accent); }

.faq__mk { position: relative; width: 0.95rem; height: 0.95rem; flex: none; }
.faq__mk::before, .faq__mk::after {
  content: ""; position: absolute; inset: 50% 0 auto; height: 1.5px;
  background: currentColor; border-radius: 2px; transition: transform .22s var(--ease);
}
.faq__mk::after { transform: rotate(90deg); }
.faq__i[open] .faq__mk::after { transform: rotate(0); }

.faq__a { padding: 0 clamp(0.5rem, 1.5vw, 1rem) 1.4rem; }
.faq__a p { margin: 0; color: var(--mute); max-width: 68ch; font-size: 0.9375rem; }

/* ==========================================================================
   22. The magic layer
   Modern CSS, used where it earns its place rather than as a demo reel.
   Every effect degrades to the plain state when unsupported.
   ========================================================================== */

/* ---- 22.1 Registered custom properties ---------------------------------
   @property makes a custom property animatable. Without it, a gradient
   angle cannot be transitioned at all -- the browser has no idea the value
   is an angle rather than an arbitrary string. */
@property --ring {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
@property --spark {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

/* ---- 22.2 The rune ring -------------------------------------------------
   A conic gradient rotating behind a card, clipped to a 1px inset so only
   the border shows. This is a spell-circle, not a glow: it turns. */
.workcard, .bucket, .deliv__i, .pcall, .aside, .step, .faq__i, .postcard {
  position: relative;
  isolation: isolate;
}
.workcard::before, .bucket::before, .pcall::before, .aside::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: -1;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(from var(--ring),
    transparent 0deg,
    var(--violet) 40deg,
    var(--accent) 80deg,
    var(--blue) 110deg,
    transparent 150deg,
    transparent 360deg);
  /* Mask out the middle so only the 1px rim paints. */
  -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;
  opacity: 0;
  transition: opacity .35s var(--ease);
}
@media (prefers-reduced-motion: no-preference) {
  .workcard:hover::before, .bucket:hover::before,
  .pcall:hover::before, .aside:hover::before {
    opacity: 1;
    animation: runeSpin 4s linear infinite;
  }
}
@keyframes runeSpin { to { --ring: 360deg; } }

/* ---- 22.3 Cursor spotlight ---------------------------------------------
   app.js writes --mx/--my as percentages on hover. A radial gradient
   follows the pointer, so the card lights where you point at it. */
.deliv__i, .step, .svc__row, .faq__i summary, .rule-i {
  --mx: 50%;
  --my: 50%;
  position: relative;
  isolation: isolate;
}
.deliv__i::after, .step::after, .rule-i::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s var(--ease);
  background: radial-gradient(18rem circle at var(--mx) var(--my),
    color-mix(in oklch, var(--violet) 22%, transparent), transparent 65%);
}
.deliv__i:hover::after, .step:hover::after, .rule-i:hover::after { opacity: 1; }

/* ---- 22.4 Service rows: the glyph strike -------------------------------
   The index number swaps to a rune and the row name gets underlined by a
   gradient that wipes in from the left. */
.svc__row { overflow: hidden; }
.svc__row .svc__k { transition: color .25s var(--ease), transform .25s var(--ease); }
.svc__row:hover .svc__k { color: var(--accent); transform: translateX(2px); }
.svc__name {
  background-image: linear-gradient(90deg, var(--accent), var(--blue));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 2px;
  transition: background-size .4s var(--ease);
}
.svc__row:hover .svc__name { background-size: 100% 2px; }
/* A faint sweep of light crossing the row, angled to the hat. */
.svc__row::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(calc(90deg - var(--hat)),
    transparent 40%,
    color-mix(in oklch, var(--violet) 18%, transparent) 50%,
    transparent 60%);
  transform: translateX(-100%);
  transition: transform .6s var(--ease);
}
.svc__row:hover::before { transform: translateX(100%); }

/* ---- 22.5 CTA: the ember ----------------------------------------------- */
.cta { position: relative; overflow: hidden; isolation: isolate; }
.cta::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(9rem circle at var(--spark) 50%,
    color-mix(in oklch, #fff 45%, transparent), transparent 60%);
  opacity: 0;
  transition: opacity .3s var(--ease);
}
@media (prefers-reduced-motion: no-preference) {
  .cta:hover::after { opacity: .5; animation: emberRun 1.1s var(--ease) infinite; }
}
@keyframes emberRun { from { --spark: -10%; } to { --spark: 110%; } }
.cta:hover .cta__arrow { transform: translateX(4px); }
.cta__arrow { display: inline-block; transition: transform .25s var(--ease); }

/* ---- 22.6 Bucket icons: the levitate ----------------------------------- */
.bucket:hover .bucket__ic {
  animation: levitate 2.4s ease-in-out infinite;
  filter: drop-shadow(0 0 12px color-mix(in oklch, var(--violet) 60%, transparent));
}
@keyframes levitate {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-6px) rotate(3deg); }
}

/* ---- 22.7 Modern text shaping ------------------------------------------
   text-wrap: balance stops a heading orphaning its last word; pretty does
   the same for body copy. Both are no-ops where unsupported. */
h1, h2, h3, .sec__head h2, .workcard h3, .stat__l { text-wrap: balance; }
p, .prose p, .deliv__i p, .step p { text-wrap: pretty; }

/* ---- 22.8 Cross-document view transitions ------------------------------
   Navigating between pages cross-fades instead of flashing white. Browsers
   without support simply navigate normally. */
@view-transition { navigation: auto; }

/* Elements that exist on both pages are NAMED, so the browser morphs them
   between routes instead of cross-fading the whole screen. The header and
   the contact dock stay put; the page body crossfades under them. */
.site-head { view-transition-name: site-head; }
.site-foot { view-transition-name: site-foot; }
.dock       { view-transition-name: dock; }
.brand      { view-transition-name: brand; }

::view-transition-old(root) { animation: vtOut .2s var(--ease) both; }
::view-transition-new(root) { animation: vtIn .28s var(--ease) both; }
@keyframes vtOut { to { opacity: 0; transform: translateY(-6px); } }
@keyframes vtIn { from { opacity: 0; transform: translateY(10px); } }

/* Persistent chrome must not animate, or the header appears to jump on every
   navigation. */
::view-transition-group(site-head),
::view-transition-group(site-foot),
::view-transition-group(dock),
::view-transition-group(brand) { animation-duration: .25s; }
::view-transition-old(site-head), ::view-transition-new(site-head),
::view-transition-old(dock), ::view-transition-new(dock) { animation: none; mix-blend-mode: normal; }

/* On a phone, forward navigation slides in from the right the way a native
   push does. Portrait only, so it never fires on a desktop resize. */
@media (max-width: 63.99rem) {
  ::view-transition-old(root) { animation: vtPushOut .22s var(--ease) both; }
  ::view-transition-new(root) { animation: vtPushIn .3s cubic-bezier(.22,1,.36,1) both; }
}
@keyframes vtPushOut { to { opacity: 0; transform: translateX(-12%); } }
@keyframes vtPushIn { from { opacity: 0; transform: translateX(14%); } }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* ---- 22.9 :has() -- the section dims when a card is being read ---------- */
@media (min-width: 62rem) and (prefers-reduced-motion: no-preference) {
  .work:has(.workcard:hover) .workcard:not(:hover),
  .buckets:has(.bucket:hover) .bucket:not(:hover),
  .deliv:has(.deliv__i:hover) .deliv__i:not(:hover) {
    opacity: .55;
    transition: opacity .3s var(--ease);
  }
}

/* ---- 22.10 Stat shards react ------------------------------------------- */
.stat { transition: background .3s var(--ease); }
.stat:hover { background: var(--ink); }
.stat:hover .stat__n { color: var(--accent); transition: color .3s var(--ease); }

/* ==========================================================================
   23. Credentials band
   The experience claim, in numbers, on a light ground so it reads as a
   record rather than a boast.
   ========================================================================== */
.creds { display: grid; gap: 1px; background: var(--line); border-block: 1px solid var(--line); }
@media (min-width: 40rem) { .creds { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .creds { grid-template-columns: repeat(4, 1fr); } }
.cred {
  background: var(--void);
  padding: var(--s2) clamp(1.25rem, 2.5vw, 1.75rem);
  display: grid;
  gap: 0.45rem;
  align-content: start;
  position: relative;
  transition: background .3s var(--ease);
}
.cred:hover { background: var(--ink); }
.cred__n {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: clamp(2rem, 4vw, 2.75rem);
  letter-spacing: -0.035em;
  line-height: 1;
  color: var(--paper);
}
.cred__n small { font-size: 0.34em; letter-spacing: 0.08em; color: var(--accent); margin-left: 0.35em; }
.cred:hover .cred__n { color: var(--accent); transition: color .3s var(--ease); }
.cred__l { margin: 0; font-size: 0.9375rem; color: var(--mute); }

/* ==========================================================================
   24. Track record — roles, in reverse order
   ========================================================================== */
.roles { border-top: 1px dashed var(--line); }
.role {
  display: grid;
  gap: 0.3rem 1.5rem;
  padding: 1.3rem clamp(0.5rem, 1.5vw, 1rem);
  border-bottom: 1px dashed var(--line);
  transition: background .2s var(--ease);
}
@media (min-width: 56rem) { .role { grid-template-columns: 15rem 1fr; } }
.role:hover { background: var(--ink); }
.role__when { font-family: var(--f-mono); font-size: 0.8125rem; letter-spacing: 0.16em; color: var(--accent); }
.role__what { font-family: var(--f-display); font-weight: 600; font-size: 1.125rem; letter-spacing: -0.012em; margin: 0.3rem 0 0; color: var(--paper); }
.role__body { margin: 0; }
.role__body ul { list-style: none; margin: 0.5rem 0 0; padding: 0; display: grid; gap: 0.4rem; }
.role__body li { position: relative; padding-left: 1.15rem; font-size: 0.9375rem; color: var(--mute); }
.role__body li::before { content: "/"; position: absolute; left: 0; color: var(--violet); font-family: var(--f-mono); }

/* ==========================================================================
   25. Testimonials
   ========================================================================== */
.quotes { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 4px; overflow: hidden; }
@media (min-width: 52rem) { .quotes { grid-template-columns: repeat(2, 1fr); } }
.quote {
  background: var(--ink);
  padding: var(--s2) clamp(1.25rem, 2.5vw, 1.6rem);
  display: grid;
  gap: 0.9rem;
  align-content: start;
  position: relative;
}
.quote::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 2px;
  background: linear-gradient(var(--accent), var(--blue));
  opacity: 0;
  transition: opacity .3s var(--ease);
}
.quote:hover::before { opacity: 1; }
.quote blockquote { margin: 0; font-size: 0.9375rem; line-height: 1.65; color: color-mix(in oklch, var(--paper) 82%, transparent); }
.quote figcaption { font-family: var(--f-mono); font-size: 0.75rem; letter-spacing: 0.08em; color: var(--mute); }
.quote figcaption b { display: block; color: var(--paper); font-weight: 500; letter-spacing: 0.06em; margin-bottom: 0.15rem; }

/* ==========================================================================
   26. Google rating badge
   Links out to the profile so a visitor can check it rather than take our
   word for it. Deliberately NOT marked up as AggregateRating -- see
   inc/seo.php for why.
   ========================================================================== */
.grate {
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.6rem 1.15rem 0.6rem 0.85rem;
  background: color-mix(in oklch, var(--ink) 92%, transparent);
  transition: border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.grate:hover { border-color: var(--accent); background: var(--ink); transform: translateY(-1px); }
.grate__score {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: 1.375rem;
  letter-spacing: -0.02em;
  color: var(--paper);
  line-height: 1;
}
.grate__stars { color: var(--accent); font-size: 0.9375rem; letter-spacing: 0.1em; line-height: 1; }
.grate__meta { font-family: var(--f-mono); font-size: 0.75rem; letter-spacing: 0.06em; color: var(--mute); line-height: 1.5; }
.grate__meta b { display: block; color: var(--paper); font-weight: 500; }

/* Two badges side by side need to wrap on a phone rather than overflow. */
.grate + .grate { margin-left: 0.6rem; }
@media (max-width: 34rem) {
  .grate { display: flex; width: 100%; justify-content: flex-start; }
  .grate + .grate { margin-left: 0; margin-top: 0.6rem; }
}
.close .grate { margin-inline: 0.3rem; }

/* ==========================================================================
   27. Footer
   Was four links and a copyright line. A footer is the last thing a visitor
   sees and the place they look for proof, so it now carries the mark, the
   full service map, the rating and a way to make contact.
   ========================================================================== */
.site-foot {
  position: relative;
  border-top: 1px solid var(--line);
  background: var(--ink);
  overflow: clip;
}
/* A slow violet bloom behind the footer, so the page ends on light rather
   than just stopping. */
.site-foot__glow {
  position: absolute;
  inset: auto -10% -60% ;
  height: 30rem;
  pointer-events: none;
  background: radial-gradient(50% 60% at 50% 100%,
    color-mix(in oklch, var(--violet) 26%, transparent), transparent 70%);
}
@media (prefers-reduced-motion: no-preference) {
  .site-foot__glow { animation: footGlow 9s ease-in-out infinite alternate; }
}
@keyframes footGlow { from { opacity: .55; transform: translateY(6%) scale(1); }
                      to   { opacity: 1;   transform: translateY(0)  scale(1.08); } }

/* The gradient hairline that separates the page from the footer. */
.site-foot::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--violet), var(--blue), transparent);
  background-size: 200% 100%;
}
@media (prefers-reduced-motion: no-preference) {
  .site-foot::before { animation: footLine 8s linear infinite; }
}
@keyframes footLine { to { background-position: 200% 0; } }

.site-foot__in {
  position: relative;
  z-index: 1;
  max-width: var(--wrap);
  margin-inline: auto;
  padding: var(--s3) var(--pad) var(--s2);
  display: grid;
  gap: var(--s2);
}
@media (min-width: 64rem) {
  .site-foot__in {
    grid-template-columns: 20rem 1fr auto;
    gap: var(--s3);
    align-items: start;
  }
}

/* ---- brand block ---- */
.site-foot__logo { display: inline-flex; align-items: center; gap: 0.65rem; text-decoration: none; }
.site-foot__mark {
  width: 2.4rem; height: 2.4rem; color: var(--accent); flex: none;
  transition: transform .4s var(--ease);
  position: relative;            /* so the liquid-metal canvas can sit over it */
}

/* A slow ember in the mark, once the footer is actually on screen.
   .is-lit is set by boot('footcast') when the footer scrolls in and is never
   removed, so this cannot run against a footer nobody is looking at -- an
   infinite filter animation ticking for the whole visit is exactly the kind of
   idle cost this theme keeps out. It animates filter only; the transform stays
   free for the hover lift below. */
@media (prefers-reduced-motion: no-preference) {
  .site-foot__mark.is-lit { animation: footEmber 5.5s ease-in-out infinite; }
}
@keyframes footEmber {
  0%, 100% { filter: drop-shadow(0 0 0 transparent); }
  50%      { filter: drop-shadow(0 0 9px color-mix(in oklch, var(--accent) 52%, transparent)); }
}
.site-foot__mark svg { width: 100%; height: 100%; display: block; }
.site-foot__logo:hover .site-foot__mark { transform: translateY(-3px) rotate(-6deg); }
.site-foot__word {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: 1.375rem;
  letter-spacing: -0.03em;
  color: var(--paper);
}
.site-foot__word i { font-style: normal; color: var(--mute); }
.site-foot__word b { color: var(--accent); font-weight: 700; }
.site-foot__line { max-width: 34ch; color: var(--mute); font-size: 0.9375rem; margin: 1rem 0 0; }
.site-foot__rate { margin-top: 1.25rem; }

/* ---- link columns ---- */
.site-foot__cols { display: grid; gap: var(--s2) 1.5rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 48rem) { .site-foot__cols { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.site-foot__col { display: grid; gap: 0.5rem; align-content: start; }
/* Column and contact headers. These were <h2> until the document outline
   was audited: five generic headings ("Build", "Get found", "Grow",
   "Company", "Get in touch") were appended to every page on the site, so a
   service page's outline ended with five headings that had nothing to do
   with the page. They are paragraphs now and carry their own type here,
   including the line-height the h1-h4 rule used to give them, so the footer
   renders pixel for pixel the way it did. */
.site-foot__col .site-foot__h,
.site-foot__contact .site-foot__h {
  font-family: var(--f-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  line-height: 0.95;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--violet);
  margin: 0 0 0.35rem;
}
.site-foot__col a,
.site-foot__contact a:not(.site-foot__big) {
  font-size: 0.9375rem;
  text-decoration: none;
  color: color-mix(in oklch, var(--paper) 74%, transparent);
  width: fit-content;
  position: relative;
  transition: color .2s var(--ease);
}
/* Underline wipes in from the left rather than just appearing. */
.site-foot__col a::after,
.site-foot__contact a:not(.site-foot__big)::after {
  content: "";
  position: absolute;
  inset: auto 0 -2px 0;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .25s var(--ease);
}
.site-foot__col a:hover,
.site-foot__contact a:not(.site-foot__big):hover { color: var(--paper); }
.site-foot__col a:hover::after,
.site-foot__contact a:not(.site-foot__big):hover::after { transform: scaleX(1); }

/* ---- contact block ---- */
.site-foot__contact { display: grid; gap: 0.5rem; align-content: start; }
.site-foot__big {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: clamp(1.25rem, 2vw, 1.5rem);
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--paper);
  margin-bottom: 0.4rem;
}
.site-foot__big span { display: inline-block; color: var(--accent); transition: transform .25s var(--ease); }
.site-foot__big:hover { color: var(--accent); }
.site-foot__big:hover span { transform: translateX(5px); }
.site-foot__contact .mono { font-size: 0.75rem; color: var(--mute); margin: 0.6rem 0 0; }

/* ---- legal ---- */
.site-foot__legal {
  grid-column: 1 / -1;
  border-top: 1px dashed var(--line);
  padding-top: 1.1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
  align-items: center;
  font-size: 0.75rem;
  color: var(--mute);
}
.site-foot__legal-links { display: flex; gap: 1.25rem; }
.site-foot__legal a { color: var(--mute); text-decoration: none; }
.site-foot__legal a:hover { color: var(--accent); }

/* ==========================================================================
   28. Client strip
   Names rather than logos, since we hold no logo files for these. Two
   identical sets scrolling as one, so the loop has no visible seam.
   ========================================================================== */


/* ==========================================================================
   29. Heading sweep
   A slow violet highlight travelling across section headings as they enter.
   background-clip on text, so it costs nothing and degrades to plain colour.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  @supports (background-clip: text) or (-webkit-background-clip: text) {
    /* :not([data-split]) is load-bearing, not tidiness.
     *
     * This rule paints the heading by clipping a gradient to the element's own
     * glyphs and setting color: transparent. SplitText then lifts every
     * character out into child <div>s, so the h2 is left with zero direct text
     * nodes -- a gradient clipped to text, with no text to clip to, paints
     * nothing, and the transparent colour inherits down and hides the
     * characters as well. Every section heading on the site went invisible the
     * moment its split ran, which is every motion-enabled visitor on every
     * page, and has been true since SplitText landed in v4.0.
     *
     * Measured in a rendered page: 7 of 7 .sec__head h2 on the front page
     * computed to rgba(0,0,0,0), and restoring a plain text node to one of
     * them brought the heading straight back with this CSS untouched.
     *
     * So the sweep now applies only while the heading is still one piece --
     * no-JS and no-GSAP visitors get it, and split headings keep their real
     * colour and the character reveal that replaced it. */
    .sec__head h2:not([data-split]) {
      background-image: linear-gradient(100deg,
        var(--paper) 0%, var(--paper) 42%,
        color-mix(in oklch, var(--accent) 85%, white) 50%,
        var(--paper) 58%, var(--paper) 100%);
      background-size: 260% 100%;
      background-position: 130% 0;
      -webkit-background-clip: text;
      background-clip: text;
      color: transparent;
      animation: headSweep linear both;
      animation-timeline: view();
      animation-range: entry 15% cover 42%;
    }
  }
}
@keyframes headSweep { to { background-position: -30% 0; } }

/* ==========================================================================
   30. Hero constellation
   Canvas node field, injected by app.js on desktop only. Sits above the
   video and below the scrim, so the content stays readable.
   ========================================================================== */
.hero__net {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .85;
}

/* ==========================================================================
   31. Film grain
   A single repeating SVG noise tile at very low opacity. Kills the flatness
   of large dark areas and stops gradient banding on cheap panels. Costs one
   data URI and no requests.
   ========================================================================== */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: .028;
  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");
}
@media (prefers-reduced-motion: no-preference) {
  body::after { animation: grain 1s steps(3) infinite; }
}
@keyframes grain {
  0%   { transform: translate(0, 0); }
  33%  { transform: translate(-2%, 1%); }
  66%  { transform: translate(1%, -2%); }
  100% { transform: translate(0, 0); }
}

/* ==========================================================================
   32. Magnetic CTA
   app.js writes the transform; this only supplies the return easing, so the
   button springs back rather than snapping.
   ========================================================================== */
.cta, .dock__toggle { transition: transform .35s cubic-bezier(.22,1,.36,1), background .28s var(--ease), color .28s var(--ease), border-color .28s var(--ease); }
.cta:active { transform: scale(.97) !important; }

/* ==========================================================================
   33. Aurora wash
   A slow-drifting colour field behind dark sections. Two conic gradients at
   low opacity, blurred hard. This is the "futuristic" look most agency sites
   get from a WebGL shader, at zero JavaScript cost.
   ========================================================================== */
.sec.ghost::after {
  content: "";
  position: absolute;
  inset: -20% -10%;
  z-index: -2;
  pointer-events: none;
  opacity: .28;
  filter: blur(80px);
  background:
    radial-gradient(30rem 22rem at 18% 30%, color-mix(in oklch, var(--violet) 55%, transparent), transparent 70%),
    radial-gradient(26rem 20rem at 82% 70%, color-mix(in oklch, var(--blue) 45%, transparent), transparent 70%);
}
@media (prefers-reduced-motion: no-preference) {
  .sec.ghost::after { animation: aurora 18s ease-in-out infinite alternate; }
}
@keyframes aurora {
  from { transform: translate3d(-3%, 2%, 0) scale(1); }
  to   { transform: translate3d(3%, -2%, 0) scale(1.12); }
}

/* ==========================================================================
   34. Scramble targets
   Reserve the width so a decoding label does not reflow the layout while
   its glyphs change.
   ========================================================================== */
.sec__num, .step__n, .rule-i__k, .bucket__k { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   35. Large-screen artwork
   The full-resolution backdrops load only where there are pixels to show
   them. Mobile never downloads them at all.
   ========================================================================== */
@media (min-width: 64rem) {
  .hero::after { background-image: url("ecommwiz-arcane-circuit-hero-backdrop.webp"); }
}

/* ==========================================================================
   36. Mobile tap targets
   Audited at 360px: footer links measured 23px tall and breadcrumb links
   16px, both under the 24px minimum. Padding rather than font-size, so the
   density of the footer is unchanged on desktop.
   ========================================================================== */
@media (max-width: 63.99rem) {
  .site-foot__col a,
  .site-foot__contact a:not(.site-foot__big),
  .site-foot__legal a {
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;      /* 44px, comfortably over the 24px floor */
    padding-block: 0.15rem;
  }
  .site-foot__col { gap: 0.15rem; }

  .crumb { line-height: 2.4; }
  .crumb a {
    display: inline-block;
    min-height: 2.75rem;
    line-height: 2.75rem;
    padding-inline: 0.15rem;
  }

  .rel a { min-height: 2.75rem; display: inline-flex; align-items: center; }
  .f__chip label { min-height: 2.75rem; display: inline-flex; align-items: center; }
  .site-foot__legal-links { gap: 1.75rem; }
}

/* The footer bloom is decorative and was measuring wider than the viewport,
   which trips mobile-usability checks even though nothing scrolls. */
.site-foot { overflow-x: clip; }
.site-foot__glow { inset: auto 0 -60%; }

/* Hero rotator: the widest word set the element width and overflowed the
   measurement at 360px. Clip it to the wrapper. */
.hero__in { max-width: 100%; }

/* ==========================================================================
   37. Rotator on narrow screens
   .rot is an inline-grid whose width is the WIDEST word, so at 360px the
   longest phrase pushed the hero to 396px -- clipped rather than scrolling,
   but still content wider than the screen, which mobile-usability checks
   flag. Below the breakpoint the grid gets to shrink and the words wrap.
   ========================================================================== */
@media (max-width: 47.99rem) {
  .rot {
    display: block;
    position: relative;
    height: auto;
    min-height: 2.3em;
    max-width: 100%;
  }
  .rot > b {
    position: absolute;
    inset: 0 auto auto 0;
    width: 100%;
    white-space: normal;
    text-wrap: balance;
  }
  .rot > b.is-on { position: relative; }
  .hero__in { max-width: min(62rem, 100%); }
  .hero h1 { overflow-wrap: break-word; }
}

/* ==========================================================================
   38. Mega menu
   Twenty-two service pages will not fit in a row of links. Grouped into the
   four buckets the site already uses, so the nav teaches the structure.
   ========================================================================== */
.nav__has { position: relative; display: flex; }
.nav__top {
  font: inherit;
  font-size: 0.9375rem;
  color: color-mix(in oklch, var(--paper) 82%, transparent);
  background: none;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0;
  transition: color .2s var(--ease);
}
.nav__top::before { content: "/"; color: var(--violet); margin-right: 0.1rem; }
.nav__top:hover, .nav__top[aria-expanded="true"] { color: var(--paper); }
.nav__caret {
  width: 0.4rem; height: 0.4rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform .25s var(--ease);
}
.nav__top[aria-expanded="true"] .nav__caret { transform: rotate(-135deg) translate(-2px, -2px); }

.mega {
  position: absolute;
  top: calc(100% + 1.1rem);
  left: 50%;
  translate: -50% 0;
  width: min(72rem, calc(100vw - 2 * var(--pad)));
  background: color-mix(in oklch, var(--ink) 97%, transparent);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: var(--s2);
  box-shadow: 0 30px 70px -20px rgba(0,0,0,.75);
  backdrop-filter: blur(14px);
  z-index: 200;
}
.mega[hidden] { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .mega { animation: megaIn .22s var(--ease) both; }
}
@keyframes megaIn { from { opacity: 0; translate: -50% -8px; } to { opacity: 1; translate: -50% 0; } }

/* The same rotating rune rim used on cards, so the menu belongs to the set. */
.mega::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(from var(--ring), transparent 0deg, var(--violet) 60deg, var(--blue) 120deg, transparent 190deg, transparent 360deg);
  -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;
  opacity: .8;
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .mega::before { animation: runeSpin 6s linear infinite; }
}

.mega__in { display: grid; gap: var(--s2) 1.5rem; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.mega__k {
  font-family: var(--f-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--violet);
  margin: 0 0 0.75rem;
}
.mega__col { display: grid; gap: 0.15rem; align-content: start; }
.mega__col a {
  text-decoration: none;
  padding: 0.5rem 0.6rem;
  border-radius: 4px;
  display: grid;
  gap: 0.1rem;
  transition: background .18s var(--ease), transform .18s var(--ease);
}
.mega__col a b {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 0.9375rem;
  letter-spacing: -0.01em;
  color: var(--paper);
}
.mega__col a span { font-size: 0.75rem; color: var(--mute); }
.mega__col a:hover { background: var(--raise); transform: translateX(3px); }
.mega__col a:hover b { color: var(--accent); }
.mega__foot { margin-top: 1rem; }
@media (max-width: 63.99rem) { .nav__has { display: none; } }

/* ==========================================================================
   39. Mobile drawer
   Full-screen, scrollable, with the services grouped in a native <details>
   so it works before JavaScript arrives.
   ========================================================================== */
.m-nav__in { display: grid; gap: 0.35rem; padding-bottom: var(--s3); }
.m-nav__cta { justify-self: stretch; text-align: center; margin-bottom: 0.9rem; }
.m-nav__top {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.125rem;
  letter-spacing: -0.015em;
  text-decoration: none;
  color: var(--paper);
  padding: 0.85rem 0;
  border-bottom: 1px dashed var(--line);
  display: block;
}
.m-nav__top:hover, .m-nav__top:focus-visible { color: var(--accent); }

.m-acc { border-bottom: 1px dashed var(--line); }
.m-acc summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.85rem 0;
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.125rem;
  letter-spacing: -0.015em;
  color: var(--paper);
}
.m-acc summary::-webkit-details-marker { display: none; }
.m-acc__mk { position: relative; width: 0.9rem; height: 0.9rem; flex: none; }
.m-acc__mk::before, .m-acc__mk::after {
  content: ""; position: absolute; inset: 50% 0 auto; height: 1.5px;
  background: var(--accent); border-radius: 2px; transition: transform .22s var(--ease);
}
.m-acc__mk::after { transform: rotate(90deg); }
.m-acc[open] .m-acc__mk::after { transform: rotate(0); }
.m-acc__body { display: grid; gap: 0.1rem; padding-bottom: 0.9rem; }
.m-acc__k {
  font-family: var(--f-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--violet);
  margin: 0.9rem 0 0.3rem;
}
.m-acc__body a {
  text-decoration: none;
  color: color-mix(in oklch, var(--paper) 80%, transparent);
  font-size: 0.9375rem;
  min-height: 2.75rem;
  display: flex;
  align-items: center;
  padding-left: 0.9rem;
  position: relative;
}
.m-acc__body a::before { content: "/"; position: absolute; left: 0; color: var(--violet); font-family: var(--f-mono); }
.m-acc__body a:active, .m-acc__body a:focus-visible { color: var(--accent); }

.m-nav__foot {
  display: grid; gap: 0.4rem; margin-top: var(--s2);
  font-family: var(--f-mono); font-size: 0.875rem;
}
.m-nav__foot a { color: var(--mute); text-decoration: none; min-height: 2.75rem; display: flex; align-items: center; }

/* ==========================================================================
   40. Cursor glow
   Two layers: a lagging violet bloom and a dot that nearly keeps up. The
   difference between them is what makes it feel alive. Never replaces the
   system cursor -- losing the real pointer is a usability cost that a nice
   effect does not justify.
   ========================================================================== */
.cur {
  position: fixed;
  top: 0; left: 0;
  pointer-events: none;
  z-index: 9998;
  opacity: 0;
  transition: opacity .3s var(--ease), width .25s var(--ease), height .25s var(--ease);
  will-change: transform;
}
.cur--glow {
  /* No filter: blur(). Measured at 13fps on a 22rem element -- the browser
     re-blurs it on every frame it moves. A radial gradient with soft stops
     looks the same and costs nothing. */
  width: 17rem; height: 17rem;
  border-radius: 50%;
  background: radial-gradient(circle,
    color-mix(in oklch, var(--violet) 30%, transparent) 0%,
    color-mix(in oklch, var(--violet) 16%, transparent) 26%,
    color-mix(in oklch, var(--blue) 8%, transparent) 48%,
    transparent 70%);
}
.cur--dot {
  width: 0.55rem; height: 0.55rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 14px 3px color-mix(in oklch, var(--accent) 65%, transparent);
}
body.has-cursor .cur { opacity: 1; }
body.cursor-hot .cur--glow { width: 23rem; height: 23rem; }
body.cursor-hot .cur--dot { width: 1.5rem; height: 1.5rem; background: transparent; border: 1.5px solid var(--accent); box-shadow: none; }
body.cursor-down .cur--dot { width: 0.35rem; height: 0.35rem; }
@media (prefers-reduced-motion: reduce) { .cur { display: none; } }

/* ==========================================================================
   41. Effects, turned up
   The first pass was too subtle to notice. These were measured as firing but
   nobody could see them, which is the same as not having built them.
   ========================================================================== */
.hero__net { opacity: 1; }                 /* was .85 */
.sec.ghost::after { opacity: .42; }        /* aurora, was .28 */

/* Card lift and glow, not just a border. */
.workcard, .bucket, .deliv__i, .quote, .step {
  transition: transform .3s var(--ease), background .3s var(--ease), box-shadow .3s var(--ease);
}
@media (hover: hover) and (min-width: 48rem) {
  .workcard:hover, .bucket:hover {
    transform: translateY(-5px);
    box-shadow: 0 24px 50px -24px color-mix(in oklch, var(--violet) 60%, transparent);
  }
  .deliv__i:hover, .step:hover { background: var(--raise); }
  .quote:hover { transform: translateY(-3px); }
}

/* Service rows: a violet bar grows down the left edge on hover. */
.svc__row::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 2px;
  background: linear-gradient(var(--accent), var(--blue));
  transform: scaleY(0);
  transform-origin: top;
  transition: transform .3s var(--ease);
}
.svc__row:hover::after { transform: scaleY(1); }

/* Price rows react too -- they were completely static. */
.price__row { transition: background .25s var(--ease), padding-left .25s var(--ease); }
@media (hover: hover) {
  .price__row:hover { background: color-mix(in oklch, var(--violet) 9%, transparent); padding-left: 0.6rem; }
}

/* ==========================================================================
   42. Grouped services
   Twenty-two rows in one list is a wall. Four labelled sets, so a visitor
   can find their problem instead of reading everything.
   ========================================================================== */
.gsets { display: grid; gap: var(--s3); }
.gset__head { margin-bottom: var(--s1); max-width: var(--read); }
.gset__k {
  font-family: var(--f-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--violet);
  margin: 0 0 0.5rem;
}
.gset__head h3 {
  font-size: clamp(1.5rem, 3vw, 2.125rem);
  letter-spacing: -0.025em;
  margin: 0 0 0.4rem;
}
.gset__lede { margin: 0; color: var(--mute); font-size: 0.9375rem; }

.gsvc { border-top: 1px dashed var(--line); }
.gsvc__row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.3rem 1.25rem;
  align-items: baseline;
  padding: 1.1rem clamp(0.5rem, 1.5vw, 0.9rem);
  border-bottom: 1px dashed var(--line);
  text-decoration: none;
  color: inherit;
  position: relative;
  isolation: isolate;
  transition: background .2s var(--ease), padding-left .2s var(--ease);
}
@media (min-width: 52rem) {
  .gsvc__row { grid-template-columns: 18rem 1fr auto auto; align-items: center; }
}
.gsvc__row:hover { background: var(--ink); padding-left: 1.25rem; }
.gsvc__row::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 2px;
  background: linear-gradient(var(--accent), var(--blue));
  transform: scaleY(0);
  transform-origin: top;
  transition: transform .28s var(--ease);
}
.gsvc__row:hover::after { transform: scaleY(1); }

.gsvc__name {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -0.012em;
  color: var(--paper);
}
.gsvc__row:hover .gsvc__name { color: var(--accent); }
.gsvc__desc { grid-column: 1 / -1; margin: 0; font-size: 0.875rem; color: var(--mute); }
@media (min-width: 52rem) { .gsvc__desc { grid-column: auto; max-width: 44ch; } }
.gsvc__n { font-family: var(--f-mono); font-size: 0.8125rem; color: var(--accent); white-space: nowrap; }
.gsvc__go { color: var(--accent); opacity: 0; transform: translateX(-4px); transition: opacity .2s var(--ease), transform .2s var(--ease); }
.gsvc__row:hover .gsvc__go { opacity: 1; transform: none; }
@media (max-width: 51.99rem) { .gsvc__go { display: none; } }

/* ==========================================================================
   43. Wand cursor
   Three layers: a lagging bloom, a dot that nearly keeps up, and a canvas of
   sparks. The canvas is fixed and pointer-events none, so it never
   participates in hit testing.
   ========================================================================== */
.cur--spark {
  position: fixed;
  inset: 0;
  z-index: 9997;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s var(--ease);
}
body.has-cursor .cur--spark { opacity: 1; }

/* ==========================================================================
   44. Nav letter lift
   Each character rises in sequence, delayed by its index. --i is set by
   app.js at boot; without JS the labels render normally and simply do not
   animate.
   ========================================================================== */
.nav > a .ch,
.nav__top .ch,
.mega__col a b .ch {
  display: inline-block;
  transition: transform .28s cubic-bezier(.22,1,.36,1), color .28s var(--ease);
  transition-delay: calc(var(--i, 0) * 22ms);
  will-change: transform;
}
.nav > a:hover .ch,
.nav__top:hover .ch,
.nav__top[aria-expanded="true"] .ch,
.mega__col a:hover b .ch {
  transform: translateY(-3px);
  color: var(--accent);
}

/* A rune dot that slides in under the hovered nav item. */
.nav > a, .nav__top { position: relative; }
.nav > a::after, .nav__top::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -0.55rem;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px 2px color-mix(in oklch, var(--accent) 70%, transparent);
  opacity: 0;
  transform: translate(-50%, 4px) scale(.4);
  transition: opacity .25s var(--ease), transform .25s var(--ease);
}
.nav > a:hover::after,
.nav__top:hover::after,
.nav__top[aria-expanded="true"]::after { opacity: 1; transform: translate(-50%, 0) scale(1); }

/* ==========================================================================
   45. Logo cast
   The sigil lifts and its mark flares; six motes fly outward on the angles
   set by app.js.
   ========================================================================== */
.brand { position: relative; }
.brand__mark { transition: transform .55s cubic-bezier(.22,1,.36,1), filter .45s var(--ease); }
.brand.is-cast .brand__mark {
  transform: translateY(-3px) rotate(-8deg) scale(1.06);
  filter: drop-shadow(0 0 14px color-mix(in oklch, var(--accent) 85%, transparent));
}
.brand.is-cast .brand__word .w { color: var(--accent); filter: drop-shadow(0 0 10px color-mix(in oklch, var(--accent) 70%, transparent)); }
.brand__word .w { transition: color .4s var(--ease), filter .4s var(--ease); }

.brand__burst {
  position: absolute;
  left: 1.05rem;
  top: 50%;
  width: 0; height: 0;
  pointer-events: none;
}
.brand__burst i {
  position: absolute;
  width: 3px; height: 3px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
}
.brand.is-cast .brand__burst i {
  animation: cast .7s var(--ease) both;
  animation-delay: var(--d, 0ms);
}
@keyframes cast {
  0%   { opacity: 0; transform: rotate(var(--a)) translateX(0) scale(.5); }
  25%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--a)) translateX(28px) scale(.2); }
}

@media (prefers-reduced-motion: reduce) {
  .nav > a .ch, .nav__top .ch, .mega__col a b .ch { transition: none; transform: none; }
  .brand__burst { display: none; }
}

/* Visually hidden but readable by assistive tech and by innerText. Used for
   the real nav label behind the per-character animation spans. */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   46. No hero artwork on small screens
   At 360px the circuit backdrop sits behind a scrim and reads as texture at
   best, but it was the LCP resource -- and a CSS background cannot start
   downloading until the stylesheet has parsed. Dropping it below the
   breakpoint makes the headline the largest paint, which arrives with the
   HTML. Measured: /pricing/ 2472ms -> well inside Google's 2.5s limit.
   ========================================================================== */
@media (max-width: 47.99rem) {
  .hero::after { background-image: none; }
}

/* ==========================================================================
   47. Headline word reveal
   Each word rises and fades in on its own delay as the heading enters. Driven
   by a scroll timeline, so it runs on the compositor rather than in JS.
   The GSAP SplitText + ScrollTrigger version of this is about 70KB.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .wd {
      display: inline-block;
      animation: wordIn linear both;
      animation-timeline: view();
      /* Each word starts fractionally later, which is what makes it read as a
         line assembling rather than a block fading. */
      animation-range: entry calc(4% + var(--w) * 2%) entry calc(46% + var(--w) * 2%);
    }
  }
}
@keyframes wordIn {
  from { opacity: 0; transform: translateY(0.5em) rotate(1.5deg); filter: blur(3px); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* ==========================================================================
   48. Cursor inversion
   The dot flips to difference blending so it inverts whatever is under it —
   dark on the paper sections, bright on the void. One property, no JS, and it
   is the single most "designed" thing on the page.
   ========================================================================== */
.cur--dot { mix-blend-mode: difference; background: #fff; }
body.cursor-hot .cur--dot { background: transparent; border-color: #fff; }

/* ==========================================================================
   49. Stacked work cards -- TRIED AND REMOVED
   Sticky-stacking the work cards looked right on the reference sites and
   wrong here, because their cards are uniform tiles and ours are wide
   two-column blocks of very different heights. The result overlapped the
   copy: the next card's screenshot slid over the previous card's paragraph
   mid-sentence. Recorded here so nobody re-adds it expecting it to work.
   ========================================================================== */

/* ==========================================================================
   50. Image wipe
   Work screenshots reveal under a diagonal clip rather than fading, which
   reads as intent rather than a default.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .workcard__media img {
      animation: wipeIn linear both;
      animation-timeline: view();
      animation-range: entry 8% entry 62%;
    }
  }
}
@keyframes wipeIn {
  from { clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); transform: scale(1.06); }
  to   { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); transform: none; }
}

/* ==========================================================================
   51. Standalone (installed) mode
   When launched from the home screen there is no browser chrome, so the site
   has to supply what the browser was providing: safe-area padding around the
   notch and home indicator, and no accidental text selection or tap flash.
   ========================================================================== */
@media (display-mode: standalone) {
  .site-head { padding-top: env(safe-area-inset-top); }
  .site-foot { padding-bottom: max(env(safe-area-inset-bottom), 1rem); }
  .dock { bottom: max(env(safe-area-inset-bottom), 1rem); }
  .m-nav { padding-top: calc(env(safe-area-inset-top) + 1rem); }

  /* An installed app should not offer to select its own navigation. */
  .site-head, .site-foot, .dock, .m-nav, .nav, .strip, .clients {
    -webkit-user-select: none;
    user-select: none;
  }
}

/* App-like touch behaviour, everywhere on a phone rather than only when
   installed -- these are improvements in a browser too. */
@media (hover: none) {
  a, button, summary, .cta, .dock__toggle {
    -webkit-tap-highlight-color: transparent;
  }
  /* Stop a scroll inside the mobile menu from dragging the page behind it. */
  .m-nav { overscroll-behavior: contain; }
  body { overscroll-behavior-y: none; }
}

/* A hairline that fills as you scroll the page. Reads as app chrome, costs
   one element and no JavaScript. */
.progress {
  position: fixed;
  inset: 0 0 auto;
  height: 2px;
  z-index: 120;
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--violet), var(--blue));
  pointer-events: none;
  scale: 0 1;
}
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .progress {
      animation: progressGrow linear both;
      animation-timeline: scroll(root block);
    }
  }
}
@keyframes progressGrow { to { scale: 1 1; } }

/* ==========================================================================
   52. Mobile drawer — SCROLL FIX
   The drawer was position: static and 1840px tall while <body> carried
   overflow: clip, so the page could not scroll and neither could the menu.
   Everything below "Get found" was unreachable on a phone.
   Now it is a fixed pane that scrolls itself.
   ========================================================================== */
@media (max-width: 63.99rem) {
  .m-nav {
    position: fixed;
    inset: var(--head-h, 4.25rem) 0 0;
    z-index: 150;
    display: block;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;   /* the page behind must not move */
    background: var(--void);
    padding: var(--s1) var(--gut) calc(var(--s3) + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line);
  }
  .m-nav[hidden] { display: none; }
  /* Reset the huge display type: at 1.75rem the 20 service links could never
     fit, which is what made the overflow fatal rather than merely untidy. */
  .m-nav a { font-size: 1rem; padding-block: 0; }
  .m-nav__top { font-size: 1.0625rem; }
  .m-acc summary { font-size: 1.0625rem; }
}

/* ==========================================================================
   53. Interactive work index
   Hover a name, its screenshot appears and follows the cursor. This is the
   signature interaction: the visitor does something and the site answers,
   rather than watching motion happen at them.
   ========================================================================== */



/* ==========================================================================
   9. v4 — THE LIVING LAYER
   GSAP + Lenis drive the motion; everything here is the costume. Every rule
   degrades: with no JS the page is fully visible and static.
   ========================================================================== */

html.ew-hold { overflow: hidden; }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

/* Split-line mask: SplitText wraps heading lines in .ew-line */
.ew-line { display: block; overflow: hidden; padding-bottom: 0.1em; margin-bottom: -0.1em; }

/* ---- first-visit veil ---- */
.ew-veil {
  position: fixed; inset: 0; z-index: 12000; background: var(--void);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1.1rem;
  color: var(--violet-hi); clip-path: inset(0 0 0 0);
}
.ew-veil__sigil { width: clamp(84px, 14vw, 132px); }
.ew-veil__word { font-family: var(--f-display); font-weight: 600; font-size: clamp(1.5rem, 4vw, 2.4rem); color: var(--paper); letter-spacing: -0.01em; }
.ew-veil__word b { color: var(--violet-hi); font-weight: 600; }

/* ---- wand cursor ---- */
.ew-cur, .ew-ring {
  position: fixed; top: 0; left: 0; pointer-events: none; z-index: 11000;
  border-radius: 50%; transform: translate(-50%, -50%);
}
.ew-cur { width: 7px; height: 7px; background: var(--violet-hi); }
.ew-ring {
  width: 34px; height: 34px; border: 1px solid color-mix(in oklch, var(--violet-hi) 60%, transparent);
  display: grid; place-items: center;
  font-family: var(--f-mono); font-size: 10px; letter-spacing: 0.12em; color: var(--void);
  transition: width .35s cubic-bezier(.22,1,.36,1), height .35s cubic-bezier(.22,1,.36,1),
              background .3s, border-color .3s;
}
.ew-ring span { opacity: 0; transition: opacity .2s; }
.ew-ring.is-link { width: 52px; height: 52px; border-color: var(--violet-hi); }
.ew-ring.is-view { width: 82px; height: 82px; background: var(--violet-hi); border-color: var(--violet-hi); }
.ew-ring.is-view span { opacity: 1; }
@media (pointer: coarse) { .ew-cur, .ew-ring { display: none; } }

/* ---- pointer spotlight (fine pointers only) ---- */
@media (pointer: fine) {
  body::after {
    content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 1;
    background: radial-gradient(540px circle at var(--mx, 50vw) var(--my, 38vh),
                color-mix(in oklch, var(--violet-hi) 9%, transparent),
                color-mix(in oklch, var(--blue) 4%, transparent) 40%, transparent 64%);
  }
}

/* ---- header states ---- */
.site-head { transition: transform .5s cubic-bezier(.22,1,.36,1), background .35s, border-color .35s; }
.site-head.is-hidden { transform: translateY(-110%); }
.site-head.is-solid {
  background: color-mix(in oklch, var(--void) 78%, transparent);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
}

/* ---- orb hero (front page) ---- */
.hero--orb { min-height: 100svh; display: flex; align-items: center; text-align: left; overflow: clip; }
.hero--orb .hero__in { max-width: var(--wrap); margin-inline: auto; width: 100%; text-align: left; position: relative; z-index: 1; }
.hero--orb h1 { font-size: clamp(2.6rem, 7vw, 6.6rem); max-width: 14ch; overflow-wrap: anywhere; min-width: 0; }
.hero--orb h1 b { color: var(--violet-hi); font-weight: inherit; }
.hero--orb .hero__lede { margin-inline: 0; max-width: 52ch; text-align: left; }
.hero--orb .ctas { justify-content: flex-start; }
.orb {
  position: absolute; top: 50%; right: max(-150px, -11vw); translate: 0 -50%;
  width: min(58vw, 640px); aspect-ratio: 1; pointer-events: none; z-index: 0;
}
.orb svg.orb__r1, .orb svg.orb__r2 { position: absolute; inset: 0; width: 100%; height: 100%; }
.orb svg.orb__r2 { inset: 12%; width: 76%; height: 76%; margin: auto; }
.orb text { font-family: var(--f-mono); font-size: 15px; letter-spacing: 6px; fill: var(--violet); }
.orb .orb__r2 text { fill: color-mix(in oklch, var(--violet-hi) 72%, transparent); font-size: 12.5px; }
.orb__core {
  position: absolute; inset: 30%; border-radius: 50%;
  border: 1px solid color-mix(in oklch, var(--violet) 45%, transparent);
  display: grid; place-items: center;
  pointer-events: auto;
}
@media (pointer: coarse) { .orb__core { pointer-events: none; } }
.orb__core::before {
  content: ""; position: absolute; inset: 12%; border-radius: 50%;
  border: 1px dashed color-mix(in oklch, var(--blue) 40%, transparent);
}
/* The sigil and its liquid-metal overlay share one box, so the hover cast
   transforms the wrapper once and both layers move together. */
/* The hero copy column is centred in a full-width box, and on the orb hero that
   box lies right across the orb. It has no background and no content out there,
   but it still took every pointer event aimed at the sigil: measured in a
   rendered page, 0 of 25 sample points on .orb__core were reachable, so the
   hover cast that ring() has been wiring up since v4.0 -- the tilt, the glow,
   the rings spinning to 6x, the sparks -- could never fire on desktop. The five
   copy blocks take the pointer back individually, so text selection and the
   CTAs are unchanged; only the empty space around them stops intercepting. */
.hero--orb .hero__in { pointer-events: none; }
.hero--orb .hero__in > * { pointer-events: auto; }

.orb__sigil {
  position: relative;
  display: block;
  width: 42%;
  aspect-ratio: 1;
  line-height: 0;
}
.orb__core svg { width: 100%; color: var(--violet-hi); }

.orb__liquid {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .5s ease;
  pointer-events: none;
  /* Screen, so the shader can only ADD light to the mark beneath it.
     Cross-fading the two instead was measurably worse than doing nothing: a
     liquid-metal render is mostly dark bands with a few highlights, so at the
     107px this mark actually occupies it came out dimmer and less legible than
     the flat sigil it was replacing. Screened over the solid mark, the
     silhouette stays exactly as readable as before and the metal reads as a
     moving sheen across it -- which is also why the SVG below keeps full
     opacity rather than fading out. */
  mix-blend-mode: screen;
}
.orb__liquid canvas { display: block; width: 100%; height: 100%; }

/* Scoped to .is-liquid, which only ever appears once the WebGL2 mount has
   succeeded -- no-JS, no-WebGL and reduced-motion visitors keep the solid
   sigil and its cast animation untouched. */
.is-liquid > .orb__liquid { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .orb__liquid { display: none; }
}
@media (max-width: 900px) {
  .hero--orb {
    min-height: auto;
    align-items: flex-start;
    padding-top: calc(var(--head-h, 76px) + 2.25rem);
    padding-bottom: 14vh;
  }
  .statement { padding-block: 8vh; }
  .statement p { max-width: none; }
  .ley { padding-block: 2.2rem; }
  .orb { top: auto; bottom: -10vh; right: -34vw; translate: 0 0; width: 100vw; opacity: .3; pointer-events: none; }
}

/* ---- statement ---- */
.statement { padding: 16vh var(--pad); position: relative; z-index: 2; }
.statement > .statement__in { max-width: var(--wrap); margin-inline: auto; }
.statement p {
  font-family: var(--f-display); font-weight: 600; font-style: normal;
  font-size: clamp(1.55rem, 3.4vw, 3rem); line-height: 1.24; letter-spacing: -0.01em;
  max-width: 24ch; color: var(--paper);
}
.statement p em { font-style: normal; color: var(--violet-hi); }

/* ---- stacked buckets (front page) ---- */
.buckets--stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: 4vh; }
.buckets--stack .bucket { max-width: none; }
@media (max-width: 900px) {
  .buckets--stack.is-stacking .bucket { position: static !important; min-height: 0; }
}
.buckets--stack.is-stacking .bucket {
  position: sticky; top: calc(76px + 3vh);
  background: linear-gradient(135deg, var(--ink), var(--raise));
  border: 1px solid var(--line); border-radius: 20px;
  min-height: 46vh; will-change: transform;
}

/* ---- work index: the full component. Directional fill rows; the
   screenshot rides the wand, centered in its frame. ---- */
.wx { position: relative; }
.wx__rows { border-top: 1px solid var(--line); }
.wx__row {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: 0.35rem 1.25rem;
  padding: clamp(1.1rem, 2.6vw, 1.9rem) clamp(0.75rem, 2vw, 1.5rem);
  border-bottom: 1px solid var(--line);
  text-decoration: none; color: inherit; isolation: isolate;
}
.wx__row::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: var(--accent); transform: scaleY(0); transform-origin: top;
  transition: transform .45s cubic-bezier(.65,0,.35,1);
}
.wx__row.from-bottom::after { transform-origin: bottom; }
.wx__row:hover::after, .wx__row:focus-visible::after { transform: scaleY(1); }
.wx__row > * { position: relative; z-index: 1; }
.wx__n {
  font-family: var(--f-mono); font-size: 0.75rem; letter-spacing: 0.14em;
  color: var(--violet); align-self: start; padding-top: 0.4em;
}
.wx__name {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(1.5rem, 4.6vw, 3.25rem); letter-spacing: -0.035em; line-height: 1.02;
  color: color-mix(in oklch, var(--paper) 88%, transparent);
  transition: transform .5s cubic-bezier(.22,1,.36,1), color .3s;
}
.wx__cat {
  grid-column: 2; font-family: var(--f-mono); font-size: 0.75rem;
  letter-spacing: 0.08em; color: var(--mute); transition: color .3s;
}
.wx__go {
  font-size: 1.5rem; color: var(--accent); opacity: 0; transform: translateX(-10px);
  transition: opacity .3s, transform .3s, color .3s;
}
@media (hover: hover) {
  .wx__row:hover .wx__name { transform: translateX(1.2vw); }
  .wx__row:hover .wx__go { opacity: 1; transform: none; }
  .wx__row:hover .wx__name, .wx__row:hover .wx__cat,
  .wx__row:hover .wx__n, .wx__row:hover .wx__go { color: var(--void); }
  .wx__rows:has(.wx__row:hover) .wx__row:not(:hover) .wx__name {
    color: color-mix(in oklch, var(--paper) 30%, transparent);
  }
}
.wx__stage {
  position: fixed; top: 0; left: 0; width: min(430px, 34vw); aspect-ratio: 8/5;
  z-index: 800; pointer-events: none; border-radius: 12px; overflow: hidden;
  opacity: 0; border: 1px solid var(--line);
  box-shadow: 0 24px 60px color-mix(in oklch, var(--void) 72%, transparent);
}
.wx__pv {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  opacity: 0; transform: none; transition: opacity .3s;
}
.wx__pv.on { opacity: 1; }
.wx__thumb {
  grid-column: 1 / -1; width: 100%; height: auto; border-radius: 6px;
  border: 1px solid var(--line); margin-top: 0.9rem;
}
.wx.is-live .wx__thumb { display: none; }
@media (hover: hover) and (min-width: 48rem) { .wx__thumb { display: none; } }
@media (hover: none), (max-width: 47.99rem) {
  .wx__stage { display: none; }
  .wx__go { display: none; }
}

/* ---- giant footer word ---- */
.site-foot { overflow: hidden; }
.foot-word {
  font-family: var(--f-display); font-weight: 700; line-height: 0.9; letter-spacing: -0.03em;
  font-size: clamp(4rem, 16.5vw, 15rem); text-align: center; user-select: none;
  color: transparent; -webkit-text-stroke: 1px color-mix(in oklch, var(--paper) 22%, transparent);
  padding-top: 3rem; overflow-wrap: anywhere; min-width: 0;
}
.foot-word b { font-weight: 700; -webkit-text-stroke: 1px color-mix(in oklch, var(--violet-hi) 55%, transparent); }



/* footer brand: the exact header lockup, footer-sized */
.site-foot__logo { display: inline-flex; align-items: center; gap: 0.6rem; }
.site-foot__logo .brand__mark { width: 2.4rem; height: 2.4rem; }
.site-foot__logo .brand__word { font-size: 1.45rem; }


/* ---- ley line: the section seam. A turning rune seal; the line draws
   outward and lights its star nodes as it enters view. Without JS it
   renders complete and lit. ---- */
.ley {
  display: flex; align-items: center;
  padding: clamp(2.8rem, 6vw, 4.5rem) var(--pad);
}
.ley__line { position: relative; flex: 1; height: 1px;
  background: color-mix(in oklch, var(--paper) 10%, transparent); }
.ley__fill { position: absolute; inset: 0;
  background: linear-gradient(90deg,
    color-mix(in oklch, var(--accent) 0%, transparent),
    color-mix(in oklch, var(--accent) 70%, transparent) 30%,
    color-mix(in oklch, var(--accent) 70%, transparent) 70%,
    color-mix(in oklch, var(--accent) 0%, transparent));
}
.ley__line--l .ley__fill { transform-origin: right center; }
.ley__line--r .ley__fill { transform-origin: left center; }
.ley__node {
  position: absolute; top: 50%; width: 7px; height: 7px;
  transform: translate(-50%, -50%) rotate(45deg);
  background: var(--accent);
  box-shadow: 0 0 10px color-mix(in oklch, var(--accent) 60%, transparent);
}
.ley__seal {
  position: relative; width: 62px; height: 62px; flex: none;
  margin-inline: clamp(0.9rem, 2vw, 1.6rem);
  display: grid; place-items: center;
}
.ley__seal::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 1px dashed color-mix(in oklch, var(--accent) 50%, transparent);
  animation: leyspin 26s linear infinite;
}
.ley__seal::after {
  content: ""; position: absolute; inset: 9px; border-radius: 50%;
  border: 1px solid color-mix(in oklch, var(--accent) 22%, transparent);
}
.ley__seal svg {
  width: 26px; height: 26px; color: var(--accent);
  filter: drop-shadow(0 0 8px color-mix(in oklch, var(--accent) 55%, transparent));
}
@keyframes leyspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .ley__seal::before { animation: none; } }


/* ---- rune-coin social icons ---- */
.socoins { display: inline-flex; align-items: center; gap: 0.55rem; }
.socoin {
  position: relative; width: 40px; height: 40px; flex: none;
  display: grid; place-items: center; border-radius: 50%;
  color: var(--text-hi, var(--paper)); transform-style: preserve-3d;
  border: 1px solid color-mix(in oklch, var(--paper) 16%, transparent);
  transition: color .3s, border-color .3s;
}
.socoin__ring {
  position: absolute; inset: -4px; border-radius: 50%;
  border: 1px dashed color-mix(in oklch, var(--accent) 45%, transparent);
  opacity: 0; transition: opacity .3s;
}
.socoin__glyph { width: 17px; height: 17px; }
.socoin:hover { color: var(--accent); border-color: color-mix(in oklch, var(--accent) 55%, transparent); }
.socoin:hover .socoin__ring { opacity: 1; animation: leyspin 8s linear infinite; }
.site-head .socoins { margin-right: 0.9rem; }
@media (max-width: 1180px) { .site-head .socoins { display: none; } }
.m-nav__foot .socoins { margin-top: 0.9rem; }
.m-nav__foot .socoin { width: 44px; height: 44px; }
.site-foot .socoins { margin-top: 1rem; display: flex; }

/* ---- spellbar: scroll progress with a star at its head ---- */
.spellbar {
  position: fixed; top: 0; left: 0; height: 2px; width: 100%;
  z-index: 10800; pointer-events: none;
  background: linear-gradient(90deg, var(--violet-hi), var(--blue));
  transform: scaleX(0); transform-origin: left center;
}
.spellbar__star {
  position: fixed; top: 1px; left: 0; width: 14px; height: 14px;
  z-index: 10801; pointer-events: none; color: var(--violet-hi);
  transform: translate(-50%, -50%);
  filter: drop-shadow(0 0 6px color-mix(in oklch, var(--violet-hi) 70%, transparent));
}

/* ---- comet border on primary CTAs ---- */
@property --cta-a { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
.cta:not(.cta--ghost) { position: relative; }
.cta:not(.cta--ghost)::after {
  content: ""; position: absolute; inset: -2px; border-radius: inherit; padding: 2px;
  background: conic-gradient(from var(--cta-a),
    transparent 0turn 0.82turn,
    color-mix(in oklch, var(--paper) 85%, transparent) 0.9turn,
    transparent 0.98turn 1turn);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: ctacomet 3.2s linear infinite; pointer-events: none;
}
@keyframes ctacomet { to { --cta-a: 360deg; } }

/* ---- pointer sheen on cards ---- */
.bucket, .deliv__i, .quote, .price__row, .workcard { position: relative; }
.bucket::after, .deliv__i::after, .quote::after, .price__row::after, .workcard::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: radial-gradient(220px circle at var(--lx, 50%) var(--ly, 50%),
    color-mix(in oklch, var(--accent) 10%, transparent), transparent 70%);
  opacity: 0; transition: opacity .35s;
}
.bucket:hover::after, .deliv__i:hover::after, .quote:hover::after,
.price__row:hover::after, .workcard:hover::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .cta:not(.cta--ghost)::after { animation: none; opacity: 0; }
  .spellbar, .spellbar__star { display: none; }
  .socoin:hover .socoin__ring { animation: none; }
}


/* ---- the constellation canvas behind the homepage hero ---- */
.ew-const { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .ew-const { display: none; } }


/* ---- what-we-build chips under the work heading ---- */
.wtypes {
  list-style: none; padding: 0; margin: 1.4rem 0 2.2rem;
  display: flex; flex-wrap: wrap; gap: 0.55rem;
}
.wtypes li {
  font-family: var(--f-mono); font-size: 0.74rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: color-mix(in oklch, var(--paper) 78%, transparent);
  border: 1px solid color-mix(in oklch, var(--paper) 16%, transparent);
  border-radius: 999px; padding: 0.42rem 0.85rem;
  transition: color .3s, border-color .3s, background .3s, transform .3s;
}
.wtypes li:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-2px); }
.wtypes__any {
  background: var(--accent); color: var(--void) !important;
  border-color: var(--accent) !important;
}


/* ---- the star wizard familiar ---- */
.ew-wiz {
  position: fixed; left: 0; top: 0; transform-origin: 50% 50%;
  z-index: 101; pointer-events: none;
  filter: drop-shadow(0 0 14px color-mix(in oklch, var(--violet) 18%, transparent));
}
@media (prefers-reduced-motion: reduce) { .ew-wiz { display: none; } }


/* ---- the Arcane Web realm behind the Services page ---- */
.realm-canvas { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; opacity: 0; }
.realm-shade {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    linear-gradient(90deg, color-mix(in oklch, var(--void) 78%, transparent) 0%,
      color-mix(in oklch, var(--void) 50%, transparent) 46%,
      color-mix(in oklch, var(--void) 18%, transparent) 100%),
    linear-gradient(180deg, color-mix(in oklch, var(--void) 70%, transparent), transparent 22%, transparent 78%, color-mix(in oklch, var(--void) 70%, transparent));
}
body.has-realm main { position: relative; z-index: 1; }
body.has-realm .site-foot { position: relative; z-index: 1; }
body.has-realm:not(.realm-scoped) .hero--sm { background: transparent; }
body.has-realm:not(.realm-scoped) .hero--sm::before, body.has-realm:not(.realm-scoped) .hero--sm::after { opacity: 0.35; }
body.has-realm .gset, body.has-realm .bucket, body.has-realm .deliv__i {
  background: color-mix(in oklch, var(--ink) 74%, transparent);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
body.has-realm .gset { padding: 1.4rem; border-radius: 14px; border: 1px solid color-mix(in oklch, var(--paper) 8%, transparent); }
@media (min-width: 901px) {
  body.has-realm:not(.has-stack) .gsets { max-width: 62%; }
  body.has-realm:not(.has-stack) .gsvc__desc { max-width: 26ch; }
}
@media (max-width: 900px) {
  .realm-shade { background: linear-gradient(180deg, color-mix(in oklch, var(--void) 70%, transparent), color-mix(in oklch, var(--void) 45%, transparent) 30%, color-mix(in oklch, var(--void) 45%, transparent) 70%, color-mix(in oklch, var(--void) 70%, transparent)); }
}
@media (prefers-reduced-motion: reduce) { .realm-canvas, .realm-shade { display: none; } }


/* ---- services card stack: the four groups line up into the distance and
   come forward one by one as you scroll (JS pins the stage and drives --d) ---- */
.stack-stage {
  position: relative; height: 100vh; height: 100svh;
  perspective: 1400px; perspective-origin: 50% 40%;
  display: block; max-width: none;
}
.stack-card {
  position: absolute; left: 0; top: 50%; width: min(66%, 860px);
  transform: translate3d(0, -50%, 0);
  transform-style: preserve-3d; will-change: transform, opacity;
  margin: 0;
}
.stack-card.gset { padding: 1.1rem 1.3rem; width: min(70%, 920px); }
.stack-card.gset .gsvc__row { grid-template-columns: minmax(0, 1fr) minmax(0, 1.8fr) auto; column-gap: 1.2rem; }
.stack-stage--center .stack-card { left: 50%; width: min(86%, 1180px); transform: translate(-50%, -50%); }
/* work cards in a stack: side by side, capped so a card fits one screen */
.stack-card.workcard {
  grid-template-columns: 1.25fr 1fr; gap: var(--s2); align-items: center;
  background: color-mix(in oklch, var(--ink) 80%, transparent);
  border: 1px solid color-mix(in oklch, var(--paper) 8%, transparent);
  border-radius: 16px; padding: 1.4rem;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.stack-card.workcard .workcard__media img { max-height: 62vh; object-fit: cover; object-position: top; }
.stack-card.workcard h3 { font-size: clamp(1.5rem, 2.4vw, 2.2rem); }
.stack-card.workcard > div > p:not(.workcard__k) { font-size: 0.95rem; }
.stack-card.gset .gsvc__desc { max-width: none; font-size: 0.86rem; line-height: 1.35; }
.stack-card.gset .gsvc__row { padding-block: 0.5rem; }
.stack-card.gset .gset__head { margin-bottom: 0.5rem; }
.stack-card.gset .gset__head h3 { font-size: clamp(1.4rem, 2.2vw, 1.9rem); }
.stack-card.gset .gset__lede { margin: 0.2rem 0 0.4rem; }


/* ==========================================================================
   THE REBUILD — the wizard destroys an old website; the new one shows
   through. Old site = canvas painted by app.js; new site = the HTML below.
   ========================================================================== */
.rb { margin-top: var(--s2); }
.rb__frame {
  position: relative; max-width: 1120px; margin-inline: auto;
  border-radius: 14px; overflow: hidden; background: #070a1f;
  border: 1px solid color-mix(in oklch, var(--paper) 12%, transparent);
  box-shadow: 0 50px 110px -50px color-mix(in oklch, var(--violet) 70%, transparent);
}
.rb__chrome {
  display: flex; align-items: center; gap: 0.7rem; padding: 0.5rem 0.75rem;
  background: #d4d0c8; border-bottom: 1px solid #8a8a8a;
  font: 500 0.74rem/1 var(--f-mono); color: #333;
  transition: background .6s, color .6s, border-color .6s;
}
.rb__dots { display: flex; gap: 0.32rem; flex: none; }
.rb__dots i { width: 0.6rem; height: 0.6rem; border-radius: 50%; background: #ff5f57; }
.rb__dots i:nth-child(2) { background: #febc2e; }
.rb__dots i:nth-child(3) { background: #28c840; }
.rb__url {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 0.5rem;
  background: #fff; border: 1px solid #9a9a9a; border-radius: 3px; padding: 0.34rem 0.65rem;
  white-space: nowrap; overflow: hidden; transition: background .6s, border-color .6s, border-radius .6s;
}
.rb__warn { color: #b3261e; font-weight: 700; flex: none; }
.rb__lock { display: none; flex: none; font-weight: 400; }
.rb__addr { overflow: hidden; text-overflow: ellipsis; }
.rb__addr--new { display: none; }
.rb--secure .rb__chrome { background: #10153a; border-color: #232a5c; color: var(--paper); }
.rb--secure .rb__url { background: rgba(255,255,255,.07); border-color: rgba(255,255,255,.12); border-radius: 999px; }
.rb--secure .rb__warn, .rb--secure .rb__addr--old { display: none; }
.rb--secure .rb__lock, .rb--secure .rb__addr--new { display: inline; }

.rb__screen {
  position: relative; aspect-ratio: 16 / 10; overflow: hidden;
  container-type: size; touch-action: manipulation; cursor: crosshair;
  -webkit-tap-highlight-color: transparent; user-select: none; -webkit-user-select: none;
}
.rb__old, .rb__fx { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.rb__fx { pointer-events: none; }
.rb__hint {
  position: absolute; left: 50%; bottom: 3.5%; transform: translate(-50%, 8px); z-index: 3; margin: 0;
  padding: 0.5rem 0.95rem; border-radius: 999px; white-space: nowrap; pointer-events: none;
  font: 600 0.74rem/1 var(--f-mono); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--void); background: var(--violet-hi); opacity: 0;
  box-shadow: 0 10px 30px -8px color-mix(in oklch, var(--violet-hi) 80%, transparent);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
}
.rb--hint .rb__hint { opacity: 1; transform: translate(-50%, 0); }
.rb__hint--b { display: none; }
.rb--done .rb__hint--a { display: none; }
.rb--done .rb__hint--b { display: inline; }

.rb__ctrl { display: flex; gap: 0.7rem; justify-content: center; margin-top: 1.2rem; }
.rb__b {
  font: 600 0.78rem/1 var(--f-mono); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--paper); background: transparent; cursor: pointer;
  border: 1px solid color-mix(in oklch, var(--violet-hi) 55%, transparent);
  border-radius: 999px; padding: 0.7rem 1.1rem; min-height: 44px;
  transition: background .25s, color .25s, border-color .25s;
}
.rb__b:hover, .rb__b:focus-visible { background: var(--violet-hi); color: var(--void); border-color: var(--violet-hi); }
.rb__b--replay { opacity: .45; }
.rb--done .rb__b--replay { opacity: 1; }
.rb__ctas { justify-content: center; margin-top: 1.4rem; }
.rb--static .rb__old, .rb--static .rb__fx, .rb--static .rb__hint, .rb--static .rb__ctrl { display: none; }
.rb--static .rbn__toast { opacity: 1; transform: none; }

/* ---- the new site (lives under the wreckage) ---- */
.rbn {
  position: absolute; inset: 0; overflow: hidden;
  font-size: 1.32cqw; color: #f2efff; font-family: var(--f-body);
  display: grid; grid-template-rows: auto 1fr auto; gap: 3.5cqh; padding: 3cqh 3.6cqw 4cqh;
  background:
    radial-gradient(55% 70% at 80% 28%, rgba(119,97,223,.55), transparent 62%),
    radial-gradient(45% 60% at 6% 96%, rgba(0,138,252,.32), transparent 62%),
    linear-gradient(160deg, #10163d, #070a1f 62%);
}
.rbn__nav { display: flex; align-items: center; gap: 2em; }
.rbn__logo { display: flex; align-items: center; gap: .55em; font: 600 1.25em/1 var(--f-display); letter-spacing: -.01em; }
.rbn__logo i { width: 1.5em; height: 1.5em; border-radius: .45em; background: linear-gradient(135deg, #b47cfd, #008afc); box-shadow: 0 0 1.2em rgba(180,124,253,.6); }
.rbn__links { display: flex; gap: 1.8em; margin-left: auto; color: rgba(242,239,255,.7); font-size: .95em; }
.rbn__btn {
  display: inline-flex; align-items: center; justify-content: center; white-space: nowrap;
  padding: .85em 1.35em; border-radius: 999px; font-weight: 700; font-size: .95em; line-height: 1;
}
.rbn__btn--sm { padding: .65em 1.1em; font-size: .85em; background: #f2efff; color: #0b0f2a; }
.rbn__btn--go { background: linear-gradient(135deg, #b47cfd, #6a8dff 60%, #008afc); color: #fff; box-shadow: 0 .6em 1.8em -.5em rgba(140,110,255,.9); }
.rbn__btn--ghost { border: 1px solid rgba(242,239,255,.35); color: #f2efff; }
.rbn__btn--block { width: 100%; margin-top: .9em; }
.rbn__hero { display: grid; grid-template-columns: 1.2fr .8fr; gap: 5%; align-items: center; min-height: 0; position: relative; }
.rbn__pill { display: inline-flex; align-items: center; gap: .55em; padding: .5em .9em; border-radius: 999px; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.12); font-size: .82em; color: rgba(242,239,255,.85); }
.rbn__pill i { width: .55em; height: .55em; border-radius: 50%; background: #3ee08f; box-shadow: 0 0 .8em #3ee08f; }
.rbn__h { font: 600 3.9em/.98 var(--f-display); letter-spacing: -.02em; margin: .38em 0 .32em; }
.rbn__h em { font-style: normal; background: linear-gradient(90deg, #c9a4ff, #6fb6ff); -webkit-background-clip: text; background-clip: text; color: transparent; }
.rbn__p { font-size: 1.12em; color: rgba(242,239,255,.72); margin: 0 0 1.3em; }
.rbn__ctas { display: flex; gap: .8em; }
.rbn__phone {
  position: relative; justify-self: center; width: 78%; padding: 1.3em 1.2em;
  border-radius: 1.6em; background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.14);
  box-shadow: 0 2em 4em -1.5em rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.12);
}
.rbn__ph-k { margin: 0 0 .8em; font-weight: 700; font-size: 1em; }
.rbn__slots { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5em; }
.rbn__slots span { text-align: center; padding: .7em 0; border-radius: .7em; background: rgba(255,255,255,.07); font-size: .9em; }
.rbn__slots .on { background: linear-gradient(135deg, #b47cfd, #008afc); font-weight: 700; }
.rbn__toast {
  position: absolute; left: -12%; top: -14%; display: flex; align-items: center; gap: .55em;
  padding: .7em 1em; border-radius: .9em; background: #f2efff; color: #0b0f2a; font-weight: 700; font-size: .9em;
  box-shadow: 0 1.2em 2.4em -1em rgba(0,0,0,.7); opacity: 0; white-space: nowrap;
}
.rbn__toast i { font-style: normal; width: 1.5em; height: 1.5em; border-radius: 50%; display: grid; place-items: center; background: #3ee08f; color: #062a17; }
.rbn__cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.2em; }
.rbn__cards div { padding: 1.1em 1.2em; border-radius: 1.1em; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); }
.rbn__cards b { display: block; font: 600 1.05em/1.2 var(--f-display); margin-bottom: .35em; }
.rbn__cards span { font-size: .85em; color: rgba(242,239,255,.65); line-height: 1.35; }
.rbn__sweep {
  position: absolute; inset: 0; pointer-events: none; opacity: 0; transform: translateX(-120%);
  background: linear-gradient(100deg, transparent 32%, rgba(210,190,255,.22) 48%, rgba(255,255,255,.35) 50%, rgba(210,190,255,.22) 52%, transparent 68%);
  mix-blend-mode: screen;
}

/* tall stage: phones, and the vertical reel */
@media (max-width: 700px) {
.rb .rb__screen { aspect-ratio: 9 / 14; }
.rb .rbn { font-size: 3.35cqw; gap: 2.4cqh; padding: 2.6cqh 5cqw 3cqh; }
.rb .rbn__links { display: none; }
.rb .rbn__btn--sm { margin-left: auto; }
.rb .rbn__hero { grid-template-columns: 1fr; gap: 2.4cqh; align-content: start; }
.rb .rbn__h { font-size: 2.85em; margin: .4em 0 .3em; }
.rb .rbn__p { margin-bottom: 1em; }
.rb .rbn__phone { width: 100%; padding: 1em 1.1em; justify-self: stretch; }
.rb .rbn__ph-k { margin-bottom: .6em; }
.rb .rbn__slots span { padding: .55em 0; }
.rb .rbn__toast { left: auto; right: 4%; top: -1.1em; }
.rb .rbn__cards { grid-template-columns: 1fr 1fr 1fr; gap: .6em; }
.rb .rbn__cards div { padding: .8em .7em; }
.rb .rbn__cards b { font-size: .9em; }
.rb .rbn__cards span { display: none; }
.rb .rb__chrome { font-size: 0.68rem; }
.rb .rb__hint { font-size: 0.66rem; }
}
.rb.rb--tall .rb__screen { aspect-ratio: 9 / 14; }
.rb.rb--tall .rbn { font-size: 3.35cqw; gap: 2.4cqh; padding: 2.6cqh 5cqw 3cqh; }
.rb.rb--tall .rbn__links { display: none; }
.rb.rb--tall .rbn__btn--sm { margin-left: auto; }
.rb.rb--tall .rbn__hero { grid-template-columns: 1fr; gap: 2.4cqh; align-content: start; }
.rb.rb--tall .rbn__h { font-size: 2.85em; margin: .4em 0 .3em; }
.rb.rb--tall .rbn__p { margin-bottom: 1em; }
.rb.rb--tall .rbn__phone { width: 100%; padding: 1em 1.1em; justify-self: stretch; }
.rb.rb--tall .rbn__ph-k { margin-bottom: .6em; }
.rb.rb--tall .rbn__slots span { padding: .55em 0; }
.rb.rb--tall .rbn__toast { left: auto; right: 4%; top: -1.1em; }
.rb.rb--tall .rbn__cards { grid-template-columns: 1fr 1fr 1fr; gap: .6em; }
.rb.rb--tall .rbn__cards div { padding: .8em .7em; }
.rb.rb--tall .rbn__cards b { font-size: .9em; }
.rb.rb--tall .rbn__cards span { display: none; }
.rb.rb--tall .rb__chrome { font-size: 0.68rem; }
.rb.rb--tall .rb__hint { font-size: 0.66rem; }

/* ---- the magic layer ---- */


.ew-magic {
  position: fixed; inset: 0; width: 100vw; height: 100vh;
  pointer-events: none; z-index: 10500;
}
.ew-mote {
  position: absolute; border-radius: 50%;
  background: var(--violet-hi); opacity: 0.25;
  box-shadow: 0 0 8px 2px color-mix(in oklch, var(--violet-hi) 45%, transparent);
  pointer-events: none; z-index: 0;
}
@media (prefers-reduced-motion: reduce) {
  .ew-magic { display: none; }
  .ew-mote { display: none; }
}

/* ---- motion safety ---- */
@media (prefers-reduced-motion: reduce) {
  body::after { display: none; }
  .ew-cur, .ew-ring, .ew-veil { display: none; }
  .buckets--stack.is-stacking .bucket { position: static; min-height: 0; }
  .site-head.is-hidden { transform: none; }
}


/* ==========================================================================
   10. v4.1 — PERSONALITY LAYER
   Each service page declares a family; the accent shifts with it while
   staying inside the brand's three hues. The glyph, spell and twists are
   driven from app.js; everything here degrades to a normal static page.
   ========================================================================== */

:root[data-fam="found"] { --accent: var(--blue); }
:root[data-fam="grow"]  { --accent: color-mix(in oklch, var(--violet-hi) 52%, var(--blue)); }
:root[data-fam="run"]   { --accent: color-mix(in oklch, var(--violet-hi) 68%, var(--paper)); }

/* the page's sigil: one huge outlined glyph drifting behind the hero */
.hero--svc { overflow: clip; }
.pglyph {
  position: absolute; top: 8%; right: max(-40px, -3vw); z-index: 0;
  font-family: var(--f-display), sans-serif; font-weight: 700; line-height: 1;
  font-size: clamp(11rem, 30vw, 26rem); pointer-events: none; user-select: none;
  color: transparent; -webkit-text-stroke: 1.5px color-mix(in oklch, var(--accent) 34%, transparent);
}
.hero--svc .hero__in { position: relative; z-index: 1; }
@media (max-width: 760px) { .pglyph { font-size: 12rem; right: -8vw; opacity: .6; } }

/* services hub rows: directional fill, content inverts */
.svc__row { position: relative; }
.svc__row::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: var(--accent); transform: scaleY(0); transform-origin: top;
  transition: transform .45s cubic-bezier(.65,0,.35,1);
}
.svc__row.from-bottom::after { transform-origin: bottom; }
.svc__row:hover::after, .svc__row:focus-visible::after { transform: scaleY(1); }
.svc__row > * { position: relative; z-index: 1; }
.svc__row:hover { background: transparent; }
.svc__row:hover .svc__k, .svc__row:hover .svc__name, .svc__row:hover .svc__desc,
.svc__row:hover .svc__n, .svc__row:hover .svc__go { color: var(--void); }
.svc__row .svc__name { transition: transform .5s cubic-bezier(.22,1,.36,1), color .3s; }
.svc__row:hover .svc__name { transform: translateX(0.9vw); }

/* pricing rows: quiet until touched, then lit */
.price__row { transition: background .3s, border-color .3s; border-radius: 10px; }
.price__row:hover { background: color-mix(in oklch, var(--accent) 8%, transparent); }
.price__row:hover .price__n b { color: var(--accent); }

/* deliverables / buckets / quotes tilt in 3D — give them a hover surface */
.deliv__i, .quote { will-change: transform; }

/* contact form: the field you are in glows like a charged rune */
input:focus-visible, textarea:focus-visible, select:focus-visible {
  outline: 2px solid var(--accent);
  box-shadow: 0 0 0 4px color-mix(in oklch, var(--accent) 18%, transparent),
              0 0 24px color-mix(in oklch, var(--accent) 22%, transparent);
  transition: box-shadow .25s;
}

@media (prefers-reduced-motion: reduce) {
  .pglyph { display: none; }
}

/* ==========================================================================
   11. MOBILE RHYTHM — appended last so these overrides always win.
   Phones get tighter section breathing and a footer built for thumbs.
   ========================================================================== */
@media (max-width: 900px) {
  :root {
    --s4: clamp(3.25rem, 9vw, 4.5rem);
    --s3: clamp(2.5rem, 6.5vw, 3.5rem);
  }
  .statement { padding-block: 7vh; }
  .ley { padding-block: 1.6rem; }
  .hero--orb { padding-bottom: 11vh; }

  /* footer: compact, tappable, no dead air */
  .site-foot__in { padding: 2.6rem var(--pad) 1.6rem; gap: 1.9rem; }
  .site-foot__cols { gap: 1.5rem 1.25rem; }
  .site-foot__col { gap: 0.15rem; }
  .site-foot__col .site-foot__h { margin-bottom: 0.35rem; }
  .site-foot__col a { font-size: 0.95rem; padding-block: 0.32rem; line-height: 1.35; min-height: 44px; }
  .site-foot__big {
    display: block; text-align: center;
    font-size: 1.15rem; padding: 1rem 1.4rem; border-radius: 999px;
    background: var(--accent); color: var(--void);
    margin-block: 0.4rem 0.9rem;
  }
  .site-foot__big span { color: inherit; }
  .site-foot .socoins { justify-content: center; width: 100%; margin-top: 0.6rem; }
  .site-foot__legal { text-align: center; }
  .site-foot__legal-links { justify-content: center; }
  .foot-word { font-size: clamp(3.4rem, 17vw, 8rem); padding-top: 1.6rem; }


  /* work index on phones: the preview pins under the header and swaps as
     rows scroll past — the cursor-follow, translated to the thumb */
  .wx--scrollstage .wx__stage {
    display: block; position: sticky; top: calc(var(--head-h, 76px) + 8px);
    left: auto; width: 100%; aspect-ratio: 8/5; opacity: 1; transform: none;
    z-index: 5; margin: 0 0 0.9rem; border-radius: 12px; overflow: hidden;
    border: 1px solid var(--line);
    box-shadow: 0 16px 40px color-mix(in oklch, var(--void) 78%, transparent);
    background: var(--ink);
  }
  .wx--scrollstage .wx__thumb { display: none !important; }
  .wx--scrollstage .wx__row { grid-column: auto; }
  .wx__row.is-active::after { transform: scaleY(1); }
  .wx__row.is-active .wx__name, .wx__row.is-active .wx__cat,
  .wx__row.is-active .wx__n { color: var(--void); }
  .wx__row.is-active .wx__name { transform: translateX(2vw); }
  .wx--scrollstage .wx__row { transition: opacity .35s; }
  .wx--scrollstage .wx__row.is-passed { opacity: 0.18; }

  body.has-stack .gset { width: 100%; padding: 1rem; }
  body.has-stack .gset__head h3 { font-size: 1.45rem; }
  body.has-stack .gsvc__row { padding-block: 0.55rem; }
  body.has-stack .gsvc__desc { font-size: 0.82rem; line-height: 1.3; }
  body.has-stack .gsets { perspective: 900px; }

  /* footer fold (mobile accordion; JS builds it, so no-JS keeps links open) */
  .ffold .site-foot__h {
    display: flex; align-items: center; justify-content: space-between;
    cursor: pointer; user-select: none;
    padding-block: 0.55rem; margin-bottom: 0;
    border-bottom: 1px solid color-mix(in oklch, var(--paper) 10%, transparent);
  }
  .ffold__chev {
    font-family: var(--f-mono); font-size: 1rem; line-height: 1;
    color: var(--accent); display: inline-block;
  }
  .ffold .ffold__links { display: none; overflow: hidden; padding-block: 0.4rem 0.5rem; }
  .ffold.is-open .ffold__links { display: grid; }
  .site-foot__cols:has(.ffold) { gap: 0.4rem 1.25rem; grid-template-columns: minmax(0, 1fr); }

}
