/* =============================================================================
   VB — Growth, Engineered.
   Palette, type and motion tokens are the single source of truth; the hex
   values come straight from build-brief.md section 1 and must not be swapped
   for lookalikes.
   ========================================================================== */

/* --- fonts ---------------------------------------------------------------- */
/* Space Grotesk, self-hosted from the Google Fonts CDN payload. It is a
   variable font, so one file covers 400-700. */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/space-grotesk-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/space-grotesk-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Registered so the hero reveal radius can be transitioned. Browsers without
   @property support just get an instant radius change instead of an eased one. */
@property --reveal-r {
  syntax: '<length>';
  /* must inherit: JS sets it on .hero__media, the mask consumes it on the
     child <img>. With inherits:false the child silently falls back to 0px. */
  inherits: true;
  initial-value: 0px;
}

/* --- tokens --------------------------------------------------------------- */
:root {
  /* brand — exact values from the brief */
  --bg:            #0A0A0A;
  --white:         #FFFFFF;
  --green:         #00FF9C;
  --green-hover:   #4DFFB8;
  --green-pressed: #00CC7D;
  --green-glow:    rgba(0, 255, 156, 0.35);

  /* derived neutrals. --text-2 and --text-3 are contrast-checked against --bg
     (7.6:1 and 5.4:1) so body and tag text clear WCAG AA comfortably. */
  --text-2: #A0A6A3;
  --text-3: #868E8A;

  /* frosted glass. The gradient fill plus the saturate() in the blur is what
     separates "glass" from "grey tile" — saturation pulls the ambient green
     through the pane instead of averaging it to neutral. */
  --glass-bg: linear-gradient(140deg,
                rgba(255, 255, 255, 0.11) 0%,
                rgba(255, 255, 255, 0.055) 42%,
                rgba(255, 255, 255, 0.03) 100%);
  --glass-bg-hover: linear-gradient(140deg,
                rgba(255, 255, 255, 0.15) 0%,
                rgba(0, 255, 156, 0.07) 55%,
                rgba(255, 255, 255, 0.04) 100%);
  --glass-blur: blur(22px) saturate(190%);
  --glass-border: rgba(255, 255, 255, 0.13);
  --glass-edge: linear-gradient(140deg,
                rgba(255, 255, 255, 0.55) 0%,
                rgba(255, 255, 255, 0.10) 34%,
                rgba(255, 255, 255, 0) 62%);
  --glass-edge-hover: linear-gradient(140deg,
                rgba(0, 255, 156, 0.85) 0%,
                rgba(0, 255, 156, 0.22) 38%,
                rgba(255, 255, 255, 0) 68%);
  --glass-shadow: 0 10px 34px rgba(0, 0, 0, 0.42),
                  inset 0 1px 1px rgba(255, 255, 255, 0.07);

  /* surfaces */
  --surface-1: rgba(255, 255, 255, 0.045);
  --surface-2: rgba(255, 255, 255, 0.075);
  --line:      rgba(255, 255, 255, 0.11);
  --line-2:    rgba(255, 255, 255, 0.18);
  --green-8:   rgba(0, 255, 156, 0.08);
  --green-15:  rgba(0, 255, 156, 0.15);
  --green-35:  rgba(0, 255, 156, 0.35);
  --green-55:  rgba(0, 255, 156, 0.55);

  /* type */
  --font: 'Space Grotesk', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* motion — custom curves; the CSS built-ins are too soft to read as intentional */
  --ease-out:    cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

  /* layout */
  --gutter:  clamp(1.25rem, 5vw, 4.5rem);
  --maxw:    1180px;
  --nav-h:   63px;
  --radius:  16px;
  --radius-s: 10px;

  /* set by JS; drives the hero reveal + cursor trail */
  --reveal-x: 50%;
  --reveal-y: 40%;
  --reveal-r: 0px;
}

/* --- reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  /* `clip` (not `hidden`): `hidden` would make this a scroll container, which
     changes how fixed/anchored descendants resolve */
  overflow-x: clip;
  scroll-behavior: smooth;
  /* small breathing room above anchor targets; no fixed header to clear */
  scroll-padding-top: 2rem;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--white);
  font-family: var(--font);
  font-size: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

h1, h2, h3, h4 {
  margin: 0;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.025em;
  text-wrap: balance;
}

p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

a { color: var(--green); text-decoration: none; }

button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* --- a11y helpers --------------------------------------------------------- */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.skip-link {
  position: fixed; top: 0; left: 50%;
  transform: translate(-50%, -120%);
  z-index: 1000;
  padding: 0.75rem 1.25rem;
  background: var(--green);
  color: #04160F;
  font-weight: 600;
  border-radius: 0 0 var(--radius-s) var(--radius-s);
  transition: transform 180ms var(--ease-out);
}
.skip-link:focus-visible { transform: translate(-50%, 0); }

/* One visible focus treatment everywhere, so keyboard users get the same
   affordance the mouse gets from hover. */
:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
  border-radius: 4px;
}

/* =============================================================================
   LOADER
   ========================================================================== */
.loader {
  position: fixed; inset: 0; z-index: 999;
  display: grid; place-items: center;
  background: var(--bg);
  transition: opacity 500ms var(--ease-out), visibility 500ms var(--ease-out);
}
.loader[data-done] { opacity: 0; visibility: hidden; }

.loader__matrix {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px 14px;
  perspective: 800px;
}
.loader__digit {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 18px;
  line-height: 1.4;
  text-align: center;
  color: var(--green);
  text-shadow: 0 0 6px var(--green-glow);
  opacity: 0;
  animation: matrix-fall 1.6s var(--ease-in-out) infinite;
}
.loader__digit:nth-child(1) { animation-delay: 0ms; }
.loader__digit:nth-child(2) { animation-delay: 70ms; }
.loader__digit:nth-child(3) { animation-delay: 140ms; }
.loader__digit:nth-child(4) { animation-delay: 210ms; }
.loader__digit:nth-child(5) { animation-delay: 280ms; }
.loader__digit:nth-child(6) { animation-delay: 350ms; }
.loader__digit:nth-child(7) { animation-delay: 420ms; }
.loader__digit:nth-child(8) { animation-delay: 490ms; }
.loader__digit:nth-child(9) { animation-delay: 560ms; }

.loader__glow {
  position: absolute; inset: -60px;
  background: radial-gradient(circle, rgba(0,255,156,0.12) 0%, transparent 70%);
  animation: matrix-pulse 1.6s ease-in-out infinite;
}

@keyframes matrix-fall {
  0%        { transform: translateY(-22px) rotateX(70deg); opacity: 0; }
  25%, 70%  { transform: translateY(0) rotateX(0);         opacity: 0.9; }
  100%      { transform: translateY(22px) rotateX(-70deg); opacity: 0; }
}
@keyframes matrix-pulse {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 0.75; }
}

/* =============================================================================
   SHARED
   ========================================================================== */
.container {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: clamp(5rem, 11vh, 8.5rem); position: relative; }

/* --- ambient depth --------------------------------------------------------- */
/* Frosted glass only reads as glass if there is something behind it to refract.
   Against flat #0A0A0A a backdrop-filter produces flat #0A0A0A — i.e. a grey
   tile. Three real depth layers sit behind the glass surfaces: a soft far glow,
   a tilted mid-distance circuit grid, and a handful of near "nodes" — plus a
   cursor perspective. Layers move at different rates under the cursor (the
   nearer the layer, the more it shifts), which is the actual depth cue; the
   translate/tilt values themselves are written by the ambient-depth module in
   main.js and default to zero, so the layers sit still and correct if that
   script never runs (reduced motion, touch, or a script failure). */
.ambient {
  position: absolute;
  inset: -8% 0;
  z-index: -1;
  perspective: 1000px;
  pointer-events: none;
  overflow: hidden;
}

/* layer 1 — far: the original soft colour glow. Barely moves. */
.ambient__glow {
  position: absolute; inset: 0;
  transition: translate 500ms var(--ease-out);
  will-change: translate;
}
.ambient__glow::before,
.ambient__glow::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  filter: blur(96px);
}
.ambient__glow::before {
  width: 46%; aspect-ratio: 1.3;
  top: 4%; left: 6%;
  background: radial-gradient(circle, rgba(0, 255, 156, 0.085) 0%, transparent 68%);
}
.ambient__glow::after {
  width: 42%; aspect-ratio: 1.1;
  bottom: 2%; right: 4%;
  background: radial-gradient(circle, rgba(0, 255, 156, 0.13) 0%, transparent 68%);
}
/* second, offset pair so the field is not obviously two blobs */
.ambient--alt .ambient__glow::before {
  top: auto; bottom: 8%; left: 34%;
  width: 38%;
  background: radial-gradient(circle, rgba(77, 255, 184, 0.085) 0%, transparent 70%);
}
.ambient--alt .ambient__glow::after {
  top: 6%; right: 22%; bottom: auto;
  width: 34%;
  background: radial-gradient(circle, rgba(0, 255, 156, 0.075) 0%, transparent 70%);
}

/* layer 2 — mid: a circuit grid on a fixed 3D tilt (so it reads as a plane
   receding into the section, not a flat pattern), plus a small extra tilt on
   top of that from the cursor position. */
.ambient__grid {
  position: absolute; inset: -20% -10%;
  background-image:
    linear-gradient(rgba(0, 255, 156, 0.13) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 255, 156, 0.13) 1px, transparent 1px);
  background-size: 64px 64px;
  transform: rotateX(55deg) rotateZ(-8deg) scale(1.5)
             rotateY(var(--tilt-y, 0deg)) rotateX(var(--tilt-x, 0deg));
  transform-origin: center 30%;
  mask-image: radial-gradient(60% 60% at 50% 40%, #000 0%, transparent 75%);
  -webkit-mask-image: radial-gradient(60% 60% at 50% 40%, #000 0%, transparent 75%);
  opacity: 0.65;
  transition: transform 260ms var(--ease-out);
  will-change: transform;
}

/* layer 3 — near: a few glowing points, the closest layer, so they get the
   largest parallax shift. Whichever one the cursor is nearest brightens, as if
   it noticed. */
.ambient__nodes {
  position: absolute; inset: 0;
  transition: translate 380ms var(--ease-out);
  will-change: translate;
}
.ambient__node {
  position: absolute;
  left: var(--nx); top: var(--ny);
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 15px 4px rgba(0, 255, 156, 0.45);
  transition: scale 220ms var(--ease-out), box-shadow 220ms ease;
}
.ambient__node[data-near] {
  scale: 1.8;
  box-shadow: 0 0 24px 7px rgba(0, 255, 156, 0.7);
}

.section__head { margin-bottom: clamp(2.5rem, 5vw, 4rem); }

.section__title {
  font-size: clamp(1.9rem, 1.2rem + 2.6vw, 3.1rem);
  margin-top: 0.5rem;
  max-width: 18ch;
}

.eyebrow--hero {
  font-size: clamp(0.875rem, 0.8rem + 0.35vw, 1.0625rem);
  letter-spacing: 0.2em;
  font-weight: 600;
  text-shadow: 0 0 18px rgba(0, 255, 156, 0.45);
}

.eyebrow {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--green);
}

/* =============================================================================
   BUTTONS
   Primary = saturated green glass. Secondary = neutral glass with a green
   glow-blob that sweeps in on hover. They must never read as the same weight.
   ========================================================================== */
.btn {
  --blob: 0;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.8rem 1.4rem;
  border-radius: var(--radius-s);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  isolation: isolate;
  transition: transform 160ms var(--ease-out),
              border-color 220ms ease,
              box-shadow 220ms ease,
              background-color 220ms ease,
              color 220ms ease;
}
.btn:active { transform: scale(0.97); }

.btn--lg { padding: 1rem 1.9rem; font-size: 1.0625rem; }

/* the glow blob, shared by both variants at different intensities */
.btn::after {
  content: '';
  position: absolute;
  z-index: -1;
  left: 50%; top: 110%;
  width: 130%; aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(circle, var(--green) 0%, transparent 68%);
  opacity: 0;
  filter: blur(18px);
  transition: opacity 300ms ease, top 420ms var(--ease-out);
}

.btn--primary {
  background: rgba(0, 255, 156, 0.14);
  border: 1px solid var(--green-55);
  color: var(--green);
  box-shadow: 0 0 24px rgba(0, 255, 156, 0.18), inset 0 1px 0 rgba(255,255,255,0.06);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.btn--primary::after { opacity: 0.22; }

.btn--glass {
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  color: var(--white);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.12);
}

@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover {
    background: rgba(0, 255, 156, 0.22);
    border-color: var(--green);
    color: var(--green-hover);
    box-shadow: 0 0 38px var(--green-glow), inset 0 1px 0 rgba(255,255,255,0.1);
  }
  .btn--primary:hover::after { opacity: 0.4; top: 60%; }

  .btn--glass:hover {
    border-color: var(--green-55);
    color: var(--green-hover);
    box-shadow: 0 0 26px rgba(0, 255, 156, 0.16);
  }
  .btn--glass:hover::after { opacity: 0.28; top: 65%; }
}

.btn--primary:active { background: rgba(0, 204, 125, 0.3); border-color: var(--green-pressed); }

/* =============================================================================
   NAV
   ========================================================================== */
/* No bar. The header is a fully transparent layer holding two floating things:
   the wordmark and a single toggle button. Because the nav collapses to a
   button at every width — not just on mobile — there is no strip of link text
   that could end up sitting on light content, so the header can stay pinned and
   keep the CTA reachable the whole way down the page. */
.nav-wrap {
  position: fixed; top: 0; left: 0; right: 0; z-index: 120;
  background: none;
  border: 0;
  pointer-events: none; /* the transparent layer must not eat hero clicks */
}
.nav-wrap > * { pointer-events: auto; }

.nav {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.1rem clamp(1.25rem, 3vw, 2.75rem);
}

.nav__logo { display: block; flex-shrink: 0; }
/* The wordmark's strokes are mid-grey; on near-black they need lifting to
   carry as a brand mark rather than a smudge. */
.nav__logo img,
.footer__brand img { filter: brightness(1.5); }
.nav__logo img { width: auto; height: 34px; }

/* --- toggle button -------------------------------------------------------- */
.nav__toggle {
  display: grid;
  place-items: center;
  width: 46px; height: 46px;
  margin-left: auto;
  border-radius: 14px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  transition: border-color 220ms ease, box-shadow 220ms ease,
              transform 160ms var(--ease-out);
}
.nav__toggle:active { transform: scale(0.94); }

@media (hover: hover) and (pointer: fine) {
  .nav__toggle:hover {
    border-color: var(--green-35);
    box-shadow: 0 10px 34px rgba(0, 0, 0, 0.45), 0 0 26px rgba(0, 255, 156, 0.18);
  }
}

.nav__toggle-bars,
.nav__toggle-bars::before,
.nav__toggle-bars::after {
  display: block;
  width: 18px; height: 1.5px;
  background: var(--white);
  border-radius: 2px;
  transition: transform 240ms var(--ease-out), background-color 160ms ease;
}
.nav__toggle-bars { position: relative; }
.nav__toggle-bars::before,
.nav__toggle-bars::after { content: ''; position: absolute; left: 0; }
.nav__toggle-bars::before { top: -6px; }
.nav__toggle-bars::after  { top: 6px; }

/* open state: the bars collapse into a cross */
.nav__toggle[aria-expanded="true"] .nav__toggle-bars { background: transparent; }
.nav__toggle[aria-expanded="true"] .nav__toggle-bars::before { transform: translateY(6px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] .nav__toggle-bars::after  { transform: translateY(-6px) rotate(-45deg); }

/* --- disclosure panel ----------------------------------------------------- */
/* Scales out of the button it belongs to, not from its own centre. */
.nav__menu {
  position: absolute;
  top: calc(100% + 0.55rem);
  right: clamp(1.25rem, 3vw, 2.75rem);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.25rem;
  width: max-content;
  min-width: 236px;
  padding: 0.7rem;
  border-radius: var(--radius);
  background: rgba(14, 15, 14, 0.92);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  box-shadow: 0 22px 54px rgba(0, 0, 0, 0.62);
  opacity: 0;
  transform: translateY(-8px) scale(0.97);
  transform-origin: top right;
  pointer-events: none;
  transition: opacity 190ms var(--ease-out), transform 190ms var(--ease-out);
}
.nav__menu[data-open] { opacity: 1; transform: none; pointer-events: auto; }

.nav__links { display: flex; flex-direction: column; gap: 0; }
.nav__links a {
  display: block;
  padding: 0.65rem 0.7rem;
  border-radius: 9px;
  color: var(--white);
  font-size: 1rem;
  font-weight: 500;
  transition: color 200ms ease, background-color 200ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .nav__links a:hover {
    color: var(--green-hover);
    background: rgba(255, 255, 255, 0.06);
  }
}

.nav__cta { width: 100%; margin-top: 0.45rem; }

/* =============================================================================
   HERO
   The brief asks for a full-bleed portrait. A true object-fit:cover across a
   16:9 viewport crops the head off a 0.8-ratio portrait, so the image is
   anchored as a full-height right-hand column and feathered into the page
   background — the photo's own vignette is already near-black, so it reads as
   full-bleed without decapitating the subject.
   ========================================================================== */
.hero {
  position: relative;
  /* the nav is fixed and overlays this section, so the hero owns the full
     viewport and just pads its content clear of the bar */
  min-height: min(100svh, 900px);
  display: flex;
  align-items: flex-end;
  padding: calc(var(--nav-h) + 3rem) var(--gutter) clamp(3rem, 7vh, 5rem);
  overflow: hidden;
  isolation: isolate;
}

.hero__media {
  position: absolute;
  z-index: -2;
  top: 0; right: 0; bottom: 0;
  width: min(64vw, 820px);
}

.hero__layers { position: relative; width: 100%; height: 100%; }

.hero__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  /* cover, anchored to the top edge: the portrait fills the frame at its full
     scale and any excess is trimmed off the BOTTOM, so the head and shoulders
     are never cut. */
  object-fit: cover;
  object-position: 50% 0%;
}

/* The cyborg is masked IN under the cursor rather than the portrait being
   punched out. This matters: a zero-radius radial-gradient is an engine-
   dependent edge case (Chrome paints the last stop, Safari paints it
   transparent), so masking the base portrait meant Safari showed the cyborg by
   default. Masking the top layer instead means the worst case is the cyborg
   failing to appear, never the portrait vanishing.

   opacity is a second, independent guard: if mask-image is unsupported or
   invalid entirely, the cyborg still stays hidden until a reveal is active. */
.hero__img--cyborg {
  opacity: 0;
  transition: opacity 260ms var(--ease-out);
  -webkit-mask-image: radial-gradient(
    circle var(--reveal-r) at var(--reveal-x) var(--reveal-y),
    #000 0%, #000 42%, transparent 78%);
  mask-image: radial-gradient(
    circle var(--reveal-r) at var(--reveal-x) var(--reveal-y),
    #000 0%, #000 42%, transparent 78%);
}
.hero__media[data-revealed] .hero__img--cyborg { opacity: 1; }

/* The radius eases on the element whose value actually changes — the child
   just inherits the animating value. */
.hero__media { transition: --reveal-r 420ms var(--ease-out); }

.hero__scrim {
  position: absolute; inset: 0; z-index: -1;
  /* must not intercept the pointer, or it steals the reveal from the portrait */
  pointer-events: none;
  background:
    linear-gradient(to right,
      var(--bg) 0%, rgba(10,10,10,0.94) 26%, rgba(10,10,10,0.55) 48%, transparent 72%),
    linear-gradient(to top,
      var(--bg) 0%, rgba(10,10,10,0.7) 18%, transparent 52%);
}

.hero__content { position: relative; max-width: 42rem; }

.hero__title {
  font-size: clamp(3rem, 1.6rem + 6.6vw, 6.2rem);
  font-weight: 700;
  line-height: 0.94;
  letter-spacing: -0.04em;
  margin: 0.6rem 0 1.25rem;
}

.hero__sub {
  max-width: 34rem;
  color: var(--text-2);
  font-size: clamp(1rem, 0.94rem + 0.35vw, 1.1875rem);
}

.hero__cta { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-top: 2rem; }

/* Touch-only affordance telling people the portrait is interactive. */
.hero__reveal-hint {
  position: absolute;
  /* sits just above the fade at the base of the portrait, clear of the face */
  left: 50%; bottom: 1.25rem;
  translate: -50% 0;
  display: none;
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--green);
  white-space: nowrap;
  pointer-events: none;
}
.hero__reveal-hint span {
  display: inline-block;
  padding: 0.4rem 0.8rem;
  border: 1px solid var(--green-35);
  border-radius: 100px;
  background: rgba(10, 10, 10, 0.6);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  animation: hint-breathe 2.6s ease-in-out infinite;
}
@keyframes hint-breathe {
  0%, 100% { opacity: 0.55; transform: translateY(0); }
  50%      { opacity: 1;    transform: translateY(-3px); }
}
.hero__media[data-revealed] .hero__reveal-hint { opacity: 0; transition: opacity 300ms ease; }

/* =============================================================================
   ABOUT
   ========================================================================== */
/* Portrait beside the copy. The image column is deliberately the narrower one —
   the story is the point of this section, the portrait supports it. */
.about__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.72fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}

.about__portrait {
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
}
.about__portrait img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: 50% 22%;
}

.about__body { margin-top: 1.5rem; }

.about__lead {
  font-size: clamp(1.05rem, 0.92rem + 0.6vw, 1.4rem);
  line-height: 1.65;
  letter-spacing: -0.015em;
}

/* JS wraps each word in .w and each character inside it in .c. The word stays
   inline-block so it never breaks mid-word across lines; the characters inside
   are what actually move.

   `transform` has no CSS transition — the JS manually eases position every
   frame and writes the already-interpolated value directly (same pattern as
   the hero reveal / cursor trail). A CSS transition retargeted on every single
   mousemove never settles into one coherent curve and reads as stutter even at
   a full frame rate; only the glow (color/text-shadow), a discrete on/off
   toggle rather than a continuous chase, gets a short transition. */
.about__lead .w { display: inline-block; white-space: nowrap; }
.about__lead .c {
  display: inline-block;
  transition: color 160ms ease, text-shadow 160ms ease;
}
.about__lead .c[data-near] {
  color: var(--green);
  text-shadow: 0 0 16px var(--green-glow);
}

/* --- experience timeline -------------------------------------------------- */
/* Static by spec. The rule is drawn once across the whole row rather than as a
   segment per item, so the three nodes always land on one continuous line
   regardless of how unevenly the copy below them wraps. */
.timeline {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.25rem, 3vw, 2.5rem);
  margin-top: clamp(3rem, 6vw, 4.5rem);
  padding-top: 1.75rem;
  counter-reset: milestone;
}
.timeline::before {
  content: '';
  position: absolute;
  top: 4px;
  /* full width with faded ends — gap-independent, so the rule always meets the
     node centres regardless of how the columns resolve */
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(to right,
    transparent 0%, var(--line-2) 16%, var(--line-2) 84%, transparent 100%);
}

.timeline__item { position: relative; }
/* the node */
.timeline__item::before {
  content: '';
  position: absolute;
  top: -21px;
  left: 50%;
  translate: -50% 0;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 12px 3px rgba(0, 255, 156, 0.4);
}

.timeline__title {
  font-size: 1rem;
  letter-spacing: -0.015em;
  text-align: center;
}
.timeline__text {
  margin-top: 0.4rem;
  color: var(--text-3);
  font-size: 0.8125rem;
  line-height: 1.55;
  text-align: center;
  text-wrap: pretty;
}

.about__note {
  margin-top: 2rem;
  padding-left: 1rem;
  border-left: 2px solid var(--green-35);
  color: var(--text-3);
  font-size: 0.9375rem;
}

/* =============================================================================
   SERVICES
   7 cards on a 24-column grid: 3 wide cards, then 4 narrower ones. Reading
   order matches the brief exactly; the two-tier sizing gives the row some
   editorial rhythm instead of a flat 7-up wall.
   ========================================================================== */
.services__grid {
  display: grid;
  grid-template-columns: repeat(24, 1fr);
  gap: clamp(0.75rem, 1.4vw, 1.15rem);
  align-items: stretch;
}
/* The slot is the grid item; .card inside it is the visual surface. The slot
   supplies the perspective for the card's rotate-based tilt. */
.card-slot { display: flex; min-width: 0; perspective: 800px; }
.card-slot[data-row="1"] { grid-column: span 8; }
.card-slot[data-row="2"] { grid-column: span 6; }
.card-slot > .card { flex: 1; min-width: 0; }

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  padding: clamp(1.25rem, 2vw, 1.75rem);
  border-radius: var(--radius);
  overflow: hidden;
  /* `rotate`, not `transform`: anime.js owns transform on these during the
     expand-on-hover reflow. */
  transition: border-color 280ms ease, box-shadow 280ms ease,
              rotate 220ms var(--ease-out);
  /* frosted pane — see --glass-* tokens */
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
}

/* Green wash that fades in behind the content on hover. */
.card::before {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(120% 90% at 12% 0%, var(--green-8) 0%, transparent 62%);
  opacity: 0;
  transition: opacity 320ms ease;
  pointer-events: none;
}

/* Specular edge: a bright hairline along the top-left, fading away by the
   bottom-right, the way light catches the lip of a real pane. */
.card::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--glass-edge);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  pointer-events: none;
}

.card__icon {
  display: grid;
  place-items: center;
  width: 52px; height: 52px;
  border-radius: 13px;
  background: rgba(0, 255, 156, 0.07);
  border: 1px solid var(--green-15);
  margin-bottom: 0.35rem;
  overflow: hidden;
}
/* The icon PNGs are neon artwork on an opaque black square. `screen` drops the
   black entirely so only the glow lands on the glass. The artwork carries a
   wide margin inside its own frame, so it is scaled up past the box and
   clipped rather than sitting as a speck in the middle. */
.card__icon img {
  width: 52px; height: 52px;
  scale: 1.35;
  mix-blend-mode: screen;
}

.card__title { font-size: 1.0625rem; letter-spacing: -0.02em; }

.card__desc {
  color: var(--text-2);
  font-size: 0.9rem;
  line-height: 1.6;
  flex: 1;
}

.tags { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.4rem; }
.tags li {
  padding: 0.2rem 0.55rem;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  color: var(--text-3);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.5;
  white-space: nowrap;
  transition: color 260ms ease, border-color 260ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .card:hover {
    background: var(--glass-bg-hover);
    border-color: var(--green-35);
    box-shadow: 0 14px 44px rgba(0, 0, 0, 0.5),
                0 0 44px rgba(0, 255, 156, 0.16),
                inset 0 1px 1px rgba(255, 255, 255, 0.12);
  }
  .card:hover::before { opacity: 1; }
  .card:hover::after { background: var(--glass-edge-hover); }
  .card:hover .tags li { color: var(--text-2); border-color: rgba(0,255,156,0.22); }
}
.card:focus-within {
  border-color: var(--green-35);
  background: var(--glass-bg-hover);
}
.card:focus-within::after { background: var(--glass-edge-hover); }

/* =============================================================================
   HOW I WORK
   Numbered steps on the same glass system as the service cards, but flatter —
   this section is a sequence to read, not a grid to explore, so it gets no
   tilt, no expand, no hover reflow.
   ========================================================================== */
.process__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(0.85rem, 1.5vw, 1.15rem);
  counter-reset: step;
}

.process__step {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: clamp(1.25rem, 2vw, 1.6rem);
  border-radius: var(--radius);
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  counter-increment: step;
  transition: border-color 260ms ease, box-shadow 260ms ease;
}
.process__step::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--glass-edge);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  pointer-events: none;
}
/* step number, sits quietly in the corner */
.process__step::before {
  content: '0' counter(step);
  position: absolute;
  top: clamp(1.25rem, 2vw, 1.6rem);
  right: clamp(1.25rem, 2vw, 1.6rem);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--green);
  opacity: 0.5;
}

.process__icon {
  display: grid;
  place-items: center;
  width: 52px; height: 52px;
  border-radius: 13px;
  background: rgba(0, 255, 156, 0.07);
  border: 1px solid var(--green-15);
  overflow: hidden;
}
/* same neon-on-opaque-black artwork as the service icons */
.process__icon img {
  width: 52px; height: 52px;
  scale: 1.35;
  mix-blend-mode: screen;
}

.process__title { font-size: 1.0625rem; letter-spacing: -0.02em; }
.process__text {
  color: var(--text-2);
  font-size: 0.875rem;
  line-height: 1.6;
}

@media (hover: hover) and (pointer: fine) {
  .process__step:hover { border-color: var(--green-35); box-shadow: 0 12px 38px rgba(0,0,0,0.45), 0 0 30px rgba(0,255,156,0.12); }
  .process__step:hover::after { background: var(--glass-edge-hover); }
}

/* =============================================================================
   TOOLS I USE
   Text-only badges. No third-party brand logos — trademark constraint.
   ========================================================================== */
/* Flex, not an equal-column grid: with five groups of very different widths a
   fixed track size orphaned the last group onto its own row and forced its
   badges to stack one per line, which read as a list rather than a cloud.
   Letting each group take its natural width keeps them packed. */
.tools__groups {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.75rem, 3vw, 2.5rem) clamp(2rem, 4vw, 3.5rem);
}
.tools__group { flex: 0 1 auto; max-width: 100%; }

.tools__label {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: 0.9rem;
}

.tools__list { display: flex; flex-wrap: wrap; gap: 0.45rem; }

.tool-badge {
  padding: 0.5rem 0.85rem;
  border-radius: 100px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--text-2);
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.4;
  white-space: nowrap;
  transition: color 240ms ease, border-color 240ms ease, background 240ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .tool-badge:hover {
    color: var(--white);
    border-color: var(--green-35);
    background: var(--glass-bg-hover);
  }
}

/* =============================================================================
   WORK / PROOF
   ========================================================================== */
.work__group-title {
  margin: clamp(2.5rem, 5vw, 3.5rem) 0 1.1rem;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-3);
}
.work__group-title:first-of-type { margin-top: 0; }

.work__row { display: grid; gap: clamp(0.85rem, 1.5vw, 1.15rem); }
.work__row--3 { grid-template-columns: repeat(3, 1fr); }
.work__row--shots { grid-template-columns: repeat(3, 1fr); }

.proof {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1.35rem;
  min-height: 100%;
  border-radius: var(--radius);
  color: var(--white);
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  transition: border-color 260ms ease, box-shadow 260ms ease,
              background 260ms ease, transform 220ms var(--ease-out);
}
.proof::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--glass-edge);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  pointer-events: none;
}

.proof__tag {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--green);
}

.proof__title {
  font-size: 1rem;
  line-height: 1.35;
  font-weight: 500;
  flex: 1;
}

.proof__link {
  margin-top: 0.35rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--green);
}
.proof__link span { display: inline-block; transition: transform 240ms var(--ease-out); }

/* Reel cards get a 9:16 frame with a play glyph, standing in for the Instagram
   thumbnails, which are not available as local assets. */
.proof__reel-frame {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-s);
  background:
    radial-gradient(70% 70% at 50% 45%, rgba(0,255,156,0.10) 0%, transparent 70%),
    repeating-linear-gradient(115deg, #111 0 12px, #141414 12px 24px);
  border: 1px solid var(--line);
  overflow: hidden;
}
.proof__play {
  width: 0; height: 0;
  border-left: 15px solid var(--green);
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  filter: drop-shadow(0 0 10px var(--green-glow));
  translate: 3px 0;
  transition: scale 260ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .proof:hover {
    border-color: var(--green-35);
    background: var(--glass-bg-hover);
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.48), 0 0 34px rgba(0, 255, 156, 0.14);
  }
  .proof:hover::after { background: var(--glass-edge-hover); }
  .proof:hover .proof__link span { transform: translateX(4px); }
  .proof:hover .proof__play { scale: 1.15; }
}

.shot {
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--glass-border);
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  transition: border-color 260ms ease, box-shadow 260ms ease;
}
.shot img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: top center;
}
.shot figcaption {
  padding: 0.7rem 1rem 0.85rem;
  color: var(--text-3);
  font-size: 0.8125rem;
}
@media (hover: hover) and (pointer: fine) {
  .shot:hover { border-color: var(--green-35); box-shadow: 0 0 32px rgba(0,255,156,0.1); }
}

.self-callout {
  margin-top: clamp(2rem, 4vw, 3rem);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  border-radius: var(--radius);
  border: 1px solid var(--green-15);
  background:
    radial-gradient(110% 160% at 100% 0%, rgba(0,255,156,0.10) 0%, transparent 60%),
    var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-shadow);
  text-align: center;
}
.self-callout__kicker {
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-3);
}
.self-callout__line {
  margin-top: 0.5rem;
  font-size: clamp(1.35rem, 1rem + 1.6vw, 2rem);
  font-weight: 600;
  letter-spacing: -0.03em;
}

/* =============================================================================
   CTA
   ========================================================================== */
.cta {
  position: relative;
  padding-block: clamp(5.5rem, 13vh, 9rem);
  text-align: center;
  overflow: hidden;
  isolation: isolate;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.cta__bg { position: absolute; inset: 0; z-index: -1; }
/* Bright green rays would drop white text to ~1.4:1, so the copy sits on a
   near-black ellipse while the rays outside it stay fully saturated. The
   artwork's own dark core lines up with this ellipse. */
.cta__bg::after {
  content: '';
  position: absolute; inset: 0;
  background:
    radial-gradient(58% 62% at 50% 50%,
      rgba(10,10,10,0.95) 0%, rgba(10,10,10,0.9) 55%, rgba(10,10,10,0.2) 100%),
    linear-gradient(to right,
      var(--bg) 0%, transparent 22%, transparent 78%, var(--bg) 100%);
}
.cta__video {
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0.95;
}

.cta__title { font-size: clamp(1.9rem, 1.2rem + 2.8vw, 3.25rem); }
.cta__sub { margin: 1rem auto 2.25rem; color: var(--text-2); max-width: 34ch; }

/* =============================================================================
   FOOTER
   ========================================================================== */
.footer { padding-block: clamp(3.5rem, 7vw, 5rem) 2rem; }

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: clamp(2rem, 5vw, 3rem);
}

.footer__brand img { width: auto; height: 42px; }
.footer__tagline {
  margin-top: 0.6rem;
  color: var(--text-3);
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
}

.socials { display: flex; gap: clamp(1.25rem, 3vw, 2rem); }

.social { position: relative; display: flex; flex-direction: column; align-items: center; gap: 0.5rem; }

.social__link {
  position: relative;
  display: grid;
  place-items: center;
  width: 48px; height: 48px;
  border-radius: 14px;
  background: rgba(0, 255, 156, 0.07);
  border: 1px solid var(--green-15);
  color: var(--green);
  transition: transform 240ms var(--ease-out), border-color 240ms ease,
              box-shadow 240ms ease, background-color 240ms ease;
}
.social__icon svg { width: 22px; height: 22px; }

/* Glassmorphism profile card that expands out of the icon. It scales from the
   bottom (its trigger) rather than from its own centre. */
.social__card {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  width: 208px;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.85rem 0.95rem;
  border-radius: 14px;
  background: rgba(22, 24, 23, 0.82);
  border: 1px solid var(--green-35);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55), 0 0 28px rgba(0, 255, 156, 0.18);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  text-align: left;
  opacity: 0;
  transform: translate(-50%, 6px) scale(0.96);
  transform-origin: bottom center;
  pointer-events: none;
  transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out);
}
.social__card-name { color: var(--green); font-size: 0.8125rem; font-weight: 600; }
.social__card-sub  { color: var(--text-2); font-size: 0.6875rem; line-height: 1.45; }

@media (hover: hover) and (pointer: fine) {
  .social__link:hover {
    transform: translateY(-3px);
    border-color: var(--green-55);
    background: rgba(0, 255, 156, 0.13);
    box-shadow: 0 0 26px var(--green-glow);
  }
  .social__link:hover .social__card { opacity: 1; transform: translate(-50%, 0) scale(1); }
}
/* Keyboard parity: focus shows the same card hover does. */
.social__link:focus-visible .social__card { opacity: 1; transform: translate(-50%, 0) scale(1); }

/* On touch the always-visible label replaces the card entirely; leaving the
   card in the layout pushed the page horizontally at 390px. */
@media (hover: none), (pointer: coarse) {
  .social__card { display: none; }
}

/* Always-visible text label — the touch equivalent of the hover card. */
.social__label {
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* --- email reveal --------------------------------------------------------- */
/* The address is a fourth icon matching the socials; clicking it discloses the
   address rather than opening a mail client straight away. */
.social--email .social__link { cursor: pointer; }

.email-reveal {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  translate: -50% 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.6rem 0.55rem 0.85rem;
  border-radius: 14px;
  white-space: nowrap;
  background: rgba(22, 24, 23, 0.86);
  border: 1px solid var(--green-35);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55), 0 0 28px rgba(0, 255, 156, 0.18);
  backdrop-filter: blur(18px) saturate(180%);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  transform-origin: bottom center;
  z-index: 5;
}
.email-reveal[hidden] { display: none; }

/* animates in from the icon it belongs to */
.email-reveal[data-open] { animation: email-in 200ms var(--ease-out) both; }
@keyframes email-in {
  from { opacity: 0; transform: translateY(6px) scale(0.96); }
  to   { opacity: 1; transform: none; }
}

.email-reveal__addr {
  font-size: 0.875rem;
  color: var(--green);
  border-bottom: 1px solid transparent;
  transition: border-color 200ms ease, color 200ms ease;
}
.email-reveal__copy {
  padding: 0.3rem 0.6rem;
  border-radius: 8px;
  border: 1px solid var(--line-2);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-2);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  transition: color 200ms ease, border-color 200ms ease, background-color 200ms ease;
}
.email-reveal__copy[data-copied] { color: var(--green); border-color: var(--green-35); }

@media (hover: hover) and (pointer: fine) {
  .email-reveal__addr:hover { color: var(--green-hover); border-bottom-color: var(--green); }
  .email-reveal__copy:hover { color: var(--white); border-color: var(--green-35); }
}

/* Email is the last icon, so on narrow screens the panel opens leftward from
   its right edge instead of centring on the icon and running off-screen. */
@media (max-width: 860px) {
  .email-reveal {
    left: auto; right: 0;
    translate: 0 0;
    transform-origin: bottom right;
    max-width: calc(100vw - 2 * var(--gutter));
    white-space: normal;
  }
  .email-reveal__addr { word-break: break-all; }
}

@media (prefers-reduced-motion: reduce) {
  .email-reveal[data-open] { animation: none; }
}

.footer__copy {
  max-width: var(--maxw);
  margin: clamp(2.5rem, 5vw, 3.5rem) auto 0;
  padding: 1.5rem var(--gutter) 0;
  border-top: 1px solid var(--line);
  color: var(--text-3);
  font-size: 0.75rem;
  text-align: center;
}

/* =============================================================================
   CURSOR LIGHT-BEAM TRAIL
   One continuous stroke through a chain of eased points (quadratic-smoothed
   in JS), not discrete dots. Two paths: a wide blurred glow underneath, and a
   crisp core on top whose stroke is a gradient running tail-to-head, so the
   beam fades away behind the cursor rather than repeating in segments.
   ========================================================================== */
.cursor-trail {
  position: fixed; inset: 0;
  width: 100vw; height: 100vh;
  z-index: 400;
  pointer-events: none;
  overflow: visible;
}
.trail-glow,
.trail-core {
  stroke-linecap: round;
  stroke-linejoin: round;
  will-change: d;
}
.trail-glow {
  stroke: var(--green);
  stroke-width: 6;
  stroke-opacity: 0.14;
  filter: blur(4px);
}
.trail-core {
  stroke-width: 1.4;
}
/* Suppressed over the hero so it never competes with the reveal effect. */
.cursor-trail[data-hidden] { opacity: 0; transition: opacity 200ms ease; }

/* =============================================================================
   SCROLL REVEAL
   ========================================================================== */
[data-inview] {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
}
[data-inview="in"] { opacity: 1; transform: none; }

/* =============================================================================
   RESPONSIVE — mobile and tablet are laid out for their own shape, not scaled
   down from desktop.
   ========================================================================== */

/* ---- tablet -------------------------------------------------------------- */
@media (max-width: 1000px) {
  .services__grid { grid-template-columns: repeat(12, 1fr); }
  .card-slot[data-row="1"] { grid-column: span 6; }
  .card-slot[data-row="2"] { grid-column: span 6; }
  /* 7 cards over 2 columns leaves a widow; the last one runs full width. */
  .card-slot:last-child { grid-column: span 12; }

  .work__row--3, .work__row--shots { grid-template-columns: repeat(2, 1fr); }
  .work__row--3 > :last-child { grid-column: span 2; }

  .process__grid { grid-template-columns: repeat(2, 1fr); }
}

/* ---- mobile -------------------------------------------------------------- */
@media (max-width: 860px) {
  :root { --radius: 14px; }

  /* The nav is the same button-and-panel at every width; on a narrow screen
     the panel just must not exceed the viewport. */
  .nav__menu { min-width: 0; width: min(19rem, calc(100vw - 2 * var(--gutter))); }

  /* --- hero: the portrait moves to the top half and the copy sits under it,
         full width. Bottom-left text over a face does not work at 390px. --- */
  .hero {
    min-height: auto;
    align-items: flex-start;
    flex-direction: column;
    justify-content: flex-end;
    padding-top: var(--nav-h);
    padding-bottom: clamp(2.5rem, 7vh, 4rem);
  }
  .hero__media {
    position: relative;
    width: calc(100% + (2 * var(--gutter)));
    margin-inline: calc(-1 * var(--gutter));
    height: min(62svh, 460px);
    z-index: -2;
  }
  /* the fixed bar overlays the top of the portrait, so nudge the framing down
     to keep the face clear of it */
  .hero__img { object-position: 50% 0%; }
  .hero__img { object-position: 50% 12%; }
  .hero__scrim {
    background:
      linear-gradient(to top, var(--bg) 0%, rgba(10,10,10,0.55) 26%, transparent 62%),
      linear-gradient(to bottom, rgba(10,10,10,0.55) 0%, transparent 30%);
  }
  .hero__content { max-width: none; margin-top: -2.5rem; }
  .hero__title { font-size: clamp(2.75rem, 12vw, 4rem); }
  .hero__cta { margin-top: 1.5rem; }
  .hero__cta .btn { flex: 1 1 auto; }

  /* --- about: stack, portrait above the copy --- */
  .about__grid { grid-template-columns: 1fr; gap: 1.75rem; }
  .about__portrait { max-width: 78%; margin-inline: auto; }

  /* --- timeline: three columns is unreadable at this width, so the rail turns
         vertical and the copy sits beside each node, left-aligned --- */
  .timeline {
    grid-template-columns: 1fr;
    gap: 1.75rem;
    padding-top: 0;
    padding-left: 1.5rem;
  }
  .timeline::before {
    top: 6px; bottom: 6px;
    left: 4px; right: auto;
    width: 1px; height: auto;
    background: linear-gradient(to bottom,
      transparent 0%, var(--line-2) 10%, var(--line-2) 90%, transparent 100%);
  }
  .timeline__item::before { top: 6px; left: -1.5rem; translate: 0 0; }
  .timeline__title,
  .timeline__text { text-align: left; }

  /* --- services: single column, icon inline with the title so cards stay
         short enough to scan by scrolling --- */
  .services__grid { grid-template-columns: 1fr; }
  .card-slot[data-row="1"],
  .card-slot[data-row="2"],
  .card-slot:last-child { grid-column: 1 / -1; }
  .card {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas:
      'icon title'
      'desc desc'
      'tags tags';
    column-gap: 0.9rem;
    row-gap: 0.55rem;
    align-items: center;
  }
  .card__icon  { grid-area: icon; margin-bottom: 0; }
  .card__title { grid-area: title; font-size: 1.125rem; }
  .card__desc  { grid-area: desc; }
  .tags        { grid-area: tags; }

  /* --- how i work: one step at a time --- */
  .process__grid { grid-template-columns: 1fr; }

  /* --- work --- */
  .work__row--3, .work__row--shots { grid-template-columns: 1fr; }
  .work__row--3 > :last-child { grid-column: auto; }

  /* --- footer --- */
  .footer__inner { flex-direction: column; align-items: center; text-align: center; }
  .socials { gap: 1.75rem; }
}

@media (max-width: 420px) {
  .tags li { font-size: 0.65rem; }
  .hero__cta { flex-direction: column; align-items: stretch; }
}

/* =============================================================================
   REDUCED MOTION
   Movement is removed; opacity and colour transitions that aid comprehension
   are kept, per WCAG guidance.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .cursor-trail { display: none; }

  [data-inview] { opacity: 1; transform: none; }

  .about__lead .c { transform: none !important; }

  .hero__reveal-hint span { animation: none; }
}

/* Touch devices have no cursor to trail. */
@media (hover: none), (pointer: coarse) {
  .cursor-trail { display: none; }
  /* clear of the hero copy, which is pulled up over the base of the portrait */
  .hero__reveal-hint { display: block; bottom: 5rem; }
}
