/* Figma: Eblek Workspace — node 6422:46214 (1920 wide frame) */
:root {
  --paper: #efeeec;
  --ink: #222;
  --ink-strong: #111;
  --ink-soft: #545454;
  --ink-title: #222725;
  --fg: #fff;
  --hero-bg: #04080f; /* the background light's darkest navy, so its edges never show grey */
  --muted: #b0b0b0;
  --glass: rgba(255, 255, 255, 0.1);
  --glass-hover: rgba(255, 255, 255, 0.18);
  --gutter: clamp(16px, 3.125vw, 60px);
  --font: "Manrope", "Helvetica Neue", Arial, sans-serif;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  /* Unfixed Studio's appear curve: slow start, fast glide, soft landing */
  --ease-appear: cubic-bezier(0.8, 0.2, 0, 1);
  /* Letter reels settle with a long exponential tail */
  --ease-roll: cubic-bezier(0.16, 1, 0.3, 1);
}

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

html {
  /* (the browser's own scroll anchoring moves the page when something above the view changes size,
     and fights the smooth scroll's position: smooth-scroll.js) */
  overflow-anchor: none;
}

html,
body {
  margin: 0;
  /* Shows around the hero as it shrinks, like the reference */
  background: var(--paper);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

body {
  font-family: var(--font);
  overflow-x: hidden;
}

a {
  color: inherit;
  text-decoration: none;
}

p,
h1,
h2,
h3,
ul {
  margin: 0;
}

/* ---------- Nav (6422:46229) ---------- */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 20;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24px var(--gutter);
  color: var(--fg);
  text-transform: uppercase;
  /* Stays legible over the dark hero and the light sections alike */
  mix-blend-mode: difference;
  pointer-events: none;
}

.nav > * {
  pointer-events: auto;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  font-size: 18px;
  letter-spacing: -0.05em;
}

.brand__dot {
  position: relative;
  width: 14px;
  height: 14px;
  flex: none;
}

/* The SVG carries its own drop shadows, so it overflows the 14px slot */
.brand__dot img {
  position: absolute;
  top: -2px;
  left: -12px;
  animation: pulse 2.4s ease-in-out infinite;
}

.nav__right {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: min(592px, 56vw);
}

.nav__links {
  display: flex;
  gap: 16px;
  font-weight: 600;
  font-size: 14px;
  line-height: 1.4;
  letter-spacing: -0.03em;
}

.nav__links a {
  color: var(--muted);
  transition: color 0.25s ease;
}

.nav__links a[aria-current],
.nav__links a:hover {
  color: var(--fg);
}

.nav__cta {
  font-weight: 600;
  font-size: 16px;
  line-height: 1.4;
  letter-spacing: -0.03em;
}

/* ---------- Hero (6422:46215) ---------- */
/* The hero is a scroll track: its frame sticks for the length of the dive into the device's
   screen (hero.js drives --dive 0 → 1), then the next section simply follows */
/* (--dive changes every frame the hero scrolls: registered as not inherited, so each change
   restyles just the few layers that read it — hero.js sets it on each of them — and not the
   thousand-odd letters inside them) */
@property --dive {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
.hero {
  /* (its screen-high layers are never shorter than this — but a phone on its side, a few hundred
     pixels high, has them the screen's height, or the title would sit below it) */
  --frame-min: 600px;
  --dive: 0;
  --hero-bottom: clamp(24px, 5vh, 56px); /* the title's and the bio's shared bottom line */
  position: relative;
  height: 260vh;
  height: 260svh;
  color: var(--fg);
}

.hero__frame {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  min-height: var(--frame-min);
  overflow: hidden;
  background: var(--hero-bg);
}

/* BG LIQUID 3D&VID — runs right to the edges */
.hero__bg {
  position: absolute;
  inset: 0;
}

/* hero-fluid.js renders the liquid here; until then (or without WebGL) the frame's dark fill shows */
.hero__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 0.6s ease;
}

.hero.has-gl .hero__canvas {
  opacity: 1;
}

/* Front layer for the hero's text: sticks over the frame (pulled up by its own height) and
   sits above the device. The whole layer blends by difference — white text that turns dark
   over bright light — since a blend inside it would only see its own transparent backdrop. */
.hero__ui {
  position: sticky;
  top: 0;
  z-index: 6;
  height: 100vh;
  height: 100svh;
  min-height: var(--frame-min);
  margin-top: calc(-1 * max(100vh, var(--frame-min)));
  margin-top: calc(-1 * max(100svh, var(--frame-min)));
  pointer-events: none;
  mix-blend-mode: difference;
}

/* Interactive 3D device (hero-device.js), fixed over the page like the robot on juncastudio.com:
   above the hero and the next section (so its screen can open onto it), below the text and nav.
   hero-device.js hides it once the dive has finished and only takes pointer input at rest. */
.hero__device {
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  touch-action: pan-y;
}

.hero__device canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* 6422:46224 */
.hero__content {
  position: absolute;
  z-index: 2;
  pointer-events: none;
  /* fades out once its letters have scrambled away */
  opacity: clamp(0, 1 - (var(--dive) - 0.06) * 12, 1);
  left: var(--gutter);
  right: var(--gutter);
  bottom: var(--hero-bottom);
}

.hero__content > * {
  pointer-events: auto;
}

.hero__title {
  transition: opacity 2s var(--ease-appear);
  max-width: 8.84em;
  font-weight: 600;
  font-size: clamp(44px, 6.25vw, 120px);
  line-height: 1;
  letter-spacing: -0.04em;
  text-transform: uppercase;
}

/* 6435:47022 */
/* Its last line sits on the title's baseline: lifted by the title's gap below its baseline
   (~0.122em of the title size) less its own (~0.267em) */
.hero__intro {
  position: absolute;
  z-index: 2;
  left: 66.1%;
  bottom: calc(var(--hero-bottom) + 0.122 * clamp(44px, 6.25vw, 120px) - 0.267em);
  width: min(469px, 24.4vw);
  font-size: clamp(14px, 1.042vw, 20px);
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  text-indent: 3.75em;
}

/* Title reels, as on the reference: reveal.js turns every word of a title into a clipped reel
   holding three copies of its letters. Each letter's reel starts shifted up (third copy showing)
   and drops to rest, so the letters roll down through the copies one after another. */
.word {
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  white-space: nowrap;
}

.reel {
  display: block;
}

.reel--2,
.reel--3 {
  position: absolute;
  left: 0;
}

.reel--2 {
  top: 1.8em;
}

.reel--3 {
  top: 2.5em;
}

.ch {
  display: inline-block;
  transition: transform 1.8s var(--ease-roll) var(--d);
  will-change: transform;
}

/* Leaving the viewport resets a reel instantly (reveal.js), ready to roll again */
.reel-reset .ch {
  transition: none;
}

/* Start offsets until reveal.js marks the title .is-in
   (scaled from the reference's -170 / -300 / -239px at 148px type) */
[data-reel]:not(.is-in) .reel--1 .ch {
  transform: translateY(-1.15em);
}

[data-reel]:not(.is-in) .reel--2 .ch {
  transform: translateY(-2.9em);
}

[data-reel]:not(.is-in) .reel--3 .ch {
  transform: translateY(-2.5em);
}

a:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
}

/* ---------- Intro states ---------- */
/* (the hero's text decodes in letter by letter once the font is in: hero.js) */
.is-loading .hero__title,
.is-loading .hero__intro {
  opacity: 0.001;
}

/* The next section, as seen through the device's screen (hero-device.js): a layer stuck over
   the frame, below the device. During the dive it is clipped to the glass and its section is
   scaled to fit inside it; as the camera goes through the glass it settles flat and full-size,
   then scrolls away with the hero like any section. */
.hero__page {
  --look: 0;
  --rgb: 0;
  position: sticky;
  top: 0;
  z-index: 2;
  height: 100vh;
  height: 100svh;
  min-height: var(--frame-min);
  margin-top: calc(-1 * max(100vh, var(--frame-min)));
  margin-top: calc(-1 * max(100svh, var(--frame-min)));
  overflow: hidden;
  background: var(--paper);
  color: var(--ink);
  clip-path: inset(50%);
}

/* Without the device (no WebGL, or before it loads) the section simply fades in at the end */
.hero:not(.has-device) .hero__page {
  clip-path: none;
  opacity: clamp(0, (var(--dive) - 0.75) * 6, 1);
}

.hero__page .problem {
  height: 100%;
  flex-wrap: wrap;
  align-content: center;
  transform-origin: 0 0;
  /* a touch of colour split while the screen glitches over (hero-device.js also blurs it a
     little while the dive rushes) */
  text-shadow: calc(var(--rgb) * 5px) 0 rgba(255, 40, 90, calc(var(--rgb) * 0.7)),
    calc(var(--rgb) * -5px) 0 rgba(0, 170, 255, calc(var(--rgb) * 0.7));
}

/* CRT look while the section is still "on the screen": scanlines and a cool glow towards the
   bezel, over the glass only (hero-device.js passes its box), fading as the camera crosses */
.hero__page::after {
  content: "";
  position: absolute;
  left: var(--hx, 0);
  top: var(--hy, 0);
  width: var(--hw, 100%);
  height: var(--hh, 100%);
  pointer-events: none;
  opacity: var(--look);
  background: repeating-linear-gradient(0deg, rgba(10, 20, 40, 0.07) 0 1px, transparent 1px 3px),
    radial-gradient(ellipse at 50% 45%, rgba(255, 255, 255, 0.35) 0%, transparent 55%),
    radial-gradient(ellipse at center, transparent 50%, rgba(6, 27, 70, 0.35) 100%);
  mix-blend-mode: multiply;
}

/* The hill hero's sky: a still dusk, slate blue overhead fading to warm cream at the horizon */
.hero--hill .hero__frame {
  background: linear-gradient(180deg, #8ea3b6 0%, #a7b8c6 30%, #c4cfd6 58%, #e3e1d4 80%, #efe4cd 100%);
}

/* ---------- The vortex hero (hero-vortex.js) ----------
   The star, the vortex round it and the void behind are all drawn on the canvas. Scrolling the
   hero's track (--dive 0 → 1), the camera rushes into the star; its white light swells from the
   middle until it fills the screen, and the next section comes up out of it. */
/* (held down to shatter the vortex, hero-vortex.js: no text selection or long-press menu) */
.hero--vortex {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

.hero--vortex .hero__frame {
  background: #000;
}

.hero__vortex {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  touch-action: pan-y;
}

/* The light: a white glow from the middle, growing until it covers everything */
.hero__flash {
  --f: clamp(0, (var(--dive) - 0.3) * 2.5, 1);
  position: sticky;
  top: 0;
  z-index: 1;
  height: 100vh;
  height: 100svh;
  min-height: var(--frame-min);
  margin-top: calc(-1 * max(100vh, var(--frame-min)));
  margin-top: calc(-1 * max(100svh, var(--frame-min)));
  pointer-events: none;
  background: radial-gradient(circle at 50% 50%, #fff 0%, #fff 18%, rgba(240, 246, 255, 0.85) 30%, rgba(200, 222, 255, 0.35) 48%, rgba(200, 222, 255, 0) 70%);
  opacity: clamp(0, var(--f) * 2, 1);
  transform: scale(calc(0.15 + var(--f) * var(--f) * 4));
}

/* The next section, out of the light: from soft focus and a touch too close, into place */
.hero--vortex .hero__page {
  --appear: clamp(0, (var(--dive) - 0.7) * 4, 1);
  clip-path: none;
  opacity: var(--appear);
  filter: blur(calc((1 - var(--appear)) * 16px));
  transform: scale(calc(1.08 - var(--appear) * 0.08));
}

/* ---------- The sorting hero (hero-sort.js) ----------
   The tiles, the cards and the little status bar are all drawn on the canvas, over near-black.
   Scrolling the hero's track (--dive 0 → 1) plays the sort, then pushes the camera through the
   tiles, and the next section comes up out of focus into place. */
.hero--sort {
  -webkit-user-select: none;
  user-select: none;
}

.hero--sort .hero__frame {
  background: radial-gradient(120% 90% at 50% 25%, #0f1a22 0%, #060b10 55%, #020406 100%);
  /* (the canvas runs on past its foot: see below) */
  overflow: visible;
}

.hero__sort {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 100%;
  /* the whole screen, past the frame's foot, once a phone's toolbar has slid away (the large
     viewport): no strip of the page shows under it. The frame and the layers stuck over it keep
     to the small viewport, so the text and cards stay clear of the toolbar (hero-sort.js → VH) */
  height: max(100%, 100vh);
  height: max(100%, 100lvh);
}

/* The sorting hero's text (Figma 6594:44222): the title bottom left, the bio bottom right, their
   bottoms level, 60px in from the edges of a 1918 × 967 frame. Both are gone as soon as the page
   is scrolled — hero.js scrambles their letters over the first sliver of the track and the layer
   fades — before the scan begins. */
.hero--sort {
  --hero-bottom: clamp(24px, 6.2svh, 60px);
}

.hero--sort .hero__ui {
  opacity: clamp(0, 1 - (var(--dive) - 0.012) * 40, 1);
  /* solid white text over the dark sky, not blended into it */
  mix-blend-mode: normal;
}

.hero--sort .hero__content {
  opacity: 1;
}

.hero--sort .hero__title {
  max-width: 11.75em;
  font-size: clamp(40px, 4.17vw, 80px);
}

@media (min-width: 1024px) {
  .hero--sort .hero__intro {
    left: auto;
    right: var(--gutter);
    bottom: var(--hero-bottom);
    width: min(587px, 30.6vw);
    text-indent: 4em;
  }
}

/* The problem, over the cubes adrift in space once the scan has scattered them (Figma 6594:44222,
   a 1918 × 967 frame): the label and title top left, a short paragraph bottom right. Its own layer,
   without the intro's blend. In as the swarm forms, out before the cubes gather into groups. */
.hero__hall {
  position: sticky;
  top: 0;
  z-index: 6;
  height: 100vh;
  height: 100svh;
  min-height: var(--frame-min);
  margin-top: calc(-1 * max(100vh, var(--frame-min)));
  margin-top: calc(-1 * max(100svh, var(--frame-min)));
  pointer-events: none;
  color: #fff;
  text-transform: uppercase;
  /* (just there for its stretch of the dive: its letters come and go by themselves, text-fx.js) */
  opacity: clamp(0, min((var(--dive) - 0.285) * 400, 1 - (var(--dive) - 0.452) * 400), 1);
}

.hero__hall-head {
  position: absolute;
  top: clamp(96px, 14.48%, 140px);
  left: var(--gutter);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
  width: min(647px, calc(100% - 2 * var(--gutter)));
  transform: translateY(calc((1 - clamp(0, (var(--dive) - 0.29) * 25, 1)) * 16px));
}

.hero__hall-label {
  margin: 0;
  font-size: 16px;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.hero__hall-title {
  margin: 0;
  max-width: 8.1em;
  font-weight: 600;
  font-size: clamp(36px, 4.17vw, 80px);
  line-height: 1;
  letter-spacing: -0.04em;
}

/* lined up under the nav's first link, "About": the nav's right block (.nav__right) starts at
   min(592px, 56vw) in from the right gutter */
.hero__hall-text {
  position: absolute;
  left: calc(100% - var(--gutter) - min(592px, 56vw));
  bottom: clamp(32px, 6.2%, 60px);
  width: min(351px, calc(100% - 2 * var(--gutter)));
  margin: 0;
  font-size: clamp(14px, 1.042vw, 20px);
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-align: left;
}

/* no nav links on smaller screens: bottom right, still ranged left */
@media (max-width: 1023px) {
  .hero__hall-text {
    left: auto;
    right: var(--gutter);
  }
}

/* The Daily Toolkit, once the cubes have sorted into its four cards (Figma 6609:1262, a 1920 × 967
   frame): "Stack" and the title top left, the line opposite, and four cards 24px apart, 60px in
   from either side, each a glass panel over its discipline's cubes. hero-sort.js packs the cubes
   into .hero__kit-card and drives --kit (0 → 1) as the cards form and burst. */
.hero__kit {
  position: sticky;
  top: 0;
  z-index: 6;
  height: 100vh;
  height: 100svh;
  min-height: var(--frame-min);
  margin-top: calc(-1 * max(100vh, var(--frame-min)));
  margin-top: calc(-1 * max(100svh, var(--frame-min)));
  pointer-events: none;
  color: #fff;
  /* (the glass comes quickly; the letters on it decode over the whole of --kit: text-fx.js) */
  opacity: clamp(0, var(--kit, 0) * 3, 1);
  visibility: hidden;
  --kit-edge: 60px;
}

.hero.is-kit .hero__kit {
  visibility: visible;
}

.hero__kit-head {
  position: absolute;
  top: clamp(96px, 14.48%, 140px);
  left: var(--kit-edge);
  right: var(--kit-edge);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 40px;
  text-transform: uppercase;
  transform: translateY(calc((1 - var(--kit, 0)) * 16px));
}

.hero__kit-heading {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.hero__kit-label {
  margin: 0;
  font-size: 16px;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.hero__kit-title {
  margin: 0;
  font-weight: 600;
  font-size: clamp(36px, 4.17vw, 80px);
  line-height: 1;
  letter-spacing: -0.04em;
}

.hero__kit-lead {
  width: min(370px, 100%);
  margin: 0 0 0.25em;
  font-size: clamp(14px, 1.042vw, 20px);
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-align: right;
}

/* the cards: 345 → 879 of the frame's 967 */
.hero__kit-cards {
  position: absolute;
  top: 35.68%;
  left: var(--kit-edge);
  right: var(--kit-edge);
  height: 55.22%;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
}

.hero__kit-card {
  position: relative;
}

/* dead centre on the card's block of cubes (hero-sort.js → --bx, --by, --bw, --bh: where the
   block sits in the card), with the same rim of cubes showing all round — 60px on a 485px card, as
   designed */
.hero__kit-card {
  --rim: calc(var(--bw, 100%) * 0.124);
}

.hero__kit-glass {
  position: absolute;
  top: calc(var(--by, 0px) + var(--rim));
  left: calc(var(--bx, 0px) + var(--rim));
  width: calc(var(--bw, 100%) - 2 * var(--rim));
  height: calc(var(--bh, 100%) - 2 * var(--rim));
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 16px;
  padding: clamp(16px, 1.25vw, 24px);
  overflow: hidden;
  /* of a piece with the cubes: only a light blur, so their pattern (and the cursor's light on them)
     still shows through; a faint sheen at the top settling into a dark tint for the text, and a
     hairline rim catching the light along its top edge */
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.025) 45%, rgba(6, 10, 24, 0.32));
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16);
  -webkit-backdrop-filter: blur(3px) saturate(1.3);
  backdrop-filter: blur(3px) saturate(1.3);
  transform: translateY(calc((1 - var(--kit, 0)) * 24px));
  /* (its size is set from outside, so it can be asked about both ways: the name scales with its
     width, and a short panel — a phone's — packs its list tighter so the whole of it fits) */
  container-type: size;
}

.hero__kit-top {
  display: flex;
  flex-direction: column;
  gap: 16px;
  font-weight: 500;
}

/* 40px as designed, smaller where the panel is narrower, so the longest ("Product design", about
   seven of its own heights wide) stays on one line */
.hero__kit-name {
  margin: 0;
  font-weight: 500;
  font-size: clamp(14px, 100cqi / 7, 40px);
  line-height: 1;
  letter-spacing: -0.02em;
}

.hero__kit-desc {
  margin: 0;
  font-size: clamp(13px, 0.833vw, 16px);
  line-height: 1.4;
}

.hero__kit-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.hero__kit-list li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  font-weight: 500;
  font-size: clamp(12px, 0.833vw, 16px);
  line-height: 1.6;
  text-transform: uppercase;
}

.hero__kit-list li:last-child {
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

/* small screens: the title and its line stacked, the cards two by two, without their descriptions */
@media (max-width: 699px) {
  .hero__kit-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
  }

  .hero__kit-heading {
    gap: 12px;
  }

  .hero__kit-label {
    font-size: 13px;
  }

  .hero__kit-lead {
    text-align: left;
  }

  .hero__kit {
    --kit-edge: var(--gutter);
  }

  /* (just under the header — it ends about 210px down — so the cards get all the height there is) */
  .hero__kit-cards {
    top: max(240px, 33%);
    height: auto;
    bottom: 3%;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
  }

  .hero__kit-card {
    --rim: calc(var(--bw, 100%) * 0.08);
  }

  .hero__kit-glass {
    gap: 8px;
    padding: 12px;
  }

  .hero__kit-desc {
    display: none;
  }

  .hero__kit-list li {
    gap: 8px;
    padding: 2px 0;
    font-size: 11px;
  }

  .hero__kit-list img {
    width: 12px;
    height: 12px;
  }
}

/* A short card panel — a phone's, upright or on its side — packs its text tighter, so the whole
   list always fits */
@container (max-height: 165px) {
  .hero__kit-desc {
    display: none;
  }

  .hero__kit-list li {
    padding: 1px 0;
    line-height: 1.25;
  }
}

@container (max-height: 138px) {
  /* (the panel is the container, and a query can't restyle its own container: its 12px padding
     and 8px gap are drawn in from the inside, to 9px and 4px) */
  .hero__kit-top {
    margin: -3px -3px -4px;
  }

  .hero__kit-list {
    margin: 0 -3px -3px;
  }

  /* (a touch smaller, so the longest name stays on one line) */
  .hero__kit-name {
    font-size: clamp(12px, 100cqi / 7.8, 40px);
  }

  .hero__kit-list li {
    gap: 6px;
    font-size: 10px;
    line-height: 1.2;
  }
}

/* The projects (Figma 6609:1675): rising from below the screen one at a time over the word
   PROJECTS — half the width each, right, left, right, left — the image, then its name and line.
   hero.js moves the list as the page scrolls while the dive holds on the word. */
.hero__projects {
  position: sticky;
  top: 0;
  z-index: 6;
  height: 100vh;
  height: 100svh;
  min-height: var(--frame-min);
  margin-top: calc(-1 * max(100vh, var(--frame-min)));
  margin-top: calc(-1 * max(100svh, var(--frame-min)));
  overflow: hidden;
  /* (clip: unlike hidden, focus can't scroll it to bring a project into view — hero.js does that) */
  overflow: clip;
  pointer-events: none;
  color: #fff;
  /* (unseen outside its hold, but still there for the keyboard and screen readers: tabbing onto
     a project brings the page to it, hero.js) */
  opacity: 0;
}

.hero__projects.is-on {
  opacity: 1;
}

.hero__projects-list {
  position: absolute;
  top: 0;
  left: var(--gutter);
  right: var(--gutter);
  display: flex;
  flex-direction: column;
  transform: translate3d(0, 100vh, 0);
  will-change: transform;
}

.hero__proj {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 0 28px;
}

.hero__proj--right {
  align-items: flex-end;
}

.hero__proj--left {
  align-items: flex-start;
}

/* 872 × 700; the picture is taller than its frame, so hero.js can drift it (data-parallax). A
   click opens it in the viewer (viewer.js: a black curtain drops from the top with it centred). */
.hero__proj-media {
  position: relative;
  display: block;
  width: 50%;
  aspect-ratio: 872 / 700;
  padding: 0;
  border: 0;
  overflow: hidden;
  background: #111;
  cursor: zoom-in;
}

/* (the layer lets the pointer through to the hero, except onto the pictures while they're up) */
.hero__projects.is-on .hero__proj-media {
  pointer-events: auto;
}

.hero__proj-media:focus-visible {
  outline: 1px solid #fff;
  outline-offset: 4px;
}

.hero__proj-media::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.hero__proj-media img {
  position: absolute;
  left: 0;
  top: -80%;
  width: 100%;
  height: 180%;
  object-fit: cover;
  will-change: transform;
}

.hero__proj-media img {
  transition: scale 0.9s var(--ease-out);
}

.hero__proj-media:hover img {
  scale: 1.04;
}

.hero__proj-text {
  display: flex;
  flex-direction: column;
  gap: 13.6px;
  width: 50%;
  padding-bottom: 45px;
  text-transform: uppercase;
}

.hero__proj-title {
  margin: 0;
  font-family: "Archivo", var(--font);
  font-weight: 500;
  font-size: clamp(22px, 1.458vw, 28px);
  line-height: 1.1;
}

.hero__proj-desc {
  max-width: 514px;
  margin: 0;
  font-size: clamp(14px, 0.833vw, 16px);
  line-height: 1.4;
  letter-spacing: -0.02em;
}

@media (max-width: 699px) {
  .hero__proj {
    padding: 0;
  }

  .hero__proj-media,
  .hero__proj-text {
    width: 100%;
  }
}

/* a point in the hero's track a little into the projects (hero.js: the hold starts 1.435 dives in,
   the slow stretches before it included; 0.18 of the way through it is 1.5025 dives, × 1200
   screens) */
.hero__anchor {
  position: absolute;
  top: 1803vh;
  top: 1803svh;
  left: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}

/* a long track: the whole sort plays out over it as you scroll (hero-sort.js) */
.hero--sort {
  /* (1200 screens for the dive — long, so each stage has room to be watched — plus, played four
     times slower (data-slow), 324 more for the wall breaking up into the swarm (0.26 → 0.35:
     0.09 × 3 × 1200), 288 more for the swarm flying into the Daily Toolkit cards
     (0.46 → 0.54: 0.08 × 3 × 1200), 270 more for the cards bursting and gathering into PROJECTS
     (0.6 → 0.675: 0.075 × 3 × 1200) and 288 more for PROJECTS turning into the hall (0.73 → 0.81:
     0.08 × 3 × 1200), and 450 more while it holds on PROJECTS for the projects list
     (data-projects="0.7,0.375": 0.375 × 1200)) */
  height: 2920vh;
  height: 2920svh;
}

/* (.hero twice: to beat the no-device fade above) */
/* ...the next section comes up out of the white light (hero-sort.js), from soft focus and a touch
   too close into place, as on the vortex hero */
.hero.hero--sort .hero__page {
  --appear: clamp(0, (var(--dive) - 0.97) * 34, 1);
  clip-path: none;
  opacity: var(--appear);
  filter: blur(calc((1 - var(--appear)) * 16px));
  transform: scale(calc(1.08 - var(--appear) * 0.08));
  pointer-events: none;
}

/* Read out, never seen */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- Content after the hero ---------- */
.after {
  position: relative;
  z-index: 1;
  /* Overlap the hero by a pixel so no sub-pixel seam shows at the hand-off */
  margin-top: -1px;
  background: var(--paper);
  color: var(--ink);
}

.display {
  font-weight: 600;
  font-size: clamp(56px, 8.333vw, 160px);
  line-height: 1;
  letter-spacing: -0.04em;
  text-transform: uppercase;
}

.label {
  color: var(--ink-strong);
  font-size: 16px;
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

/* Small uppercase paragraph that sits opposite the big titles */
.lead {
  flex: none;
  width: min(441px, 100%);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.6;
  text-transform: uppercase;
}

/* 6422:46234 */
.problem {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 40px;
  padding: clamp(96px, 10.417vw, 200px) var(--gutter);
  text-transform: uppercase;
}

.problem__head {
  display: flex;
  flex-direction: column;
  gap: 40px;
}

.problem__title {
  max-width: 6.82em;
}

/* Shared header: big title, then a hairline row */
.section-head {
  display: flex;
  flex-direction: column;
  gap: 40px;
}

.section-head .display {
  max-width: 5.3em;
}

.section-head__top {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 40px;
}

.section-head__rule {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 24px;
  border-top: 1px solid #000;
}

.section {
  display: flex;
  flex-direction: column;
  gap: 64px;
  padding: clamp(80px, 7.8125vw, 150px) var(--gutter);
}

/* 6435:47410 — label with an underline that slides through on hover */
.more {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 0;
  overflow: hidden;
  color: #151513;
  font-weight: 500;
  font-size: 16px;
  line-height: 1.4;
  text-transform: uppercase;
}

.more::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: currentColor;
  transform-origin: right;
  transition: transform 0.5s var(--ease-out);
}

.more:hover::after {
  transform: scaleX(0);
}

.more img {
  transition: transform 0.3s ease;
}

.more:hover img {
  transform: translateX(3px);
}

/* ---------- Daily Toolkit (6435:47267) ---------- */
.toolkit .display {
  max-width: 5.75em;
}

.tools {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
}

.tool {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 48px;
  min-height: clamp(400px, 26.875vw, 516px);
  padding: 24px;
  background: #e9e8e5;
}

.tool__head {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.tool__title {
  color: var(--ink);
  font-weight: 500;
  font-size: clamp(28px, 2.083vw, 40px);
  line-height: 1;
  letter-spacing: -0.02em;
}

.tool__desc {
  color: var(--ink-soft);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.4;
}

.tool__list,
.job__meta {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tool__list li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 0;
  border-top: 1px solid rgba(0, 0, 0, 0.1);
  color: var(--ink);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.6;
  text-transform: uppercase;
}

.tool__list li:last-child {
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

/* 6435:47406 — alternating checkerboard of text / image */
.project {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: end;
  padding: 0 28px;
}

.project__text {
  display: flex;
  flex-direction: column;
  gap: 13.6px;
  padding-bottom: 45px;
}

.project--flip .project__text {
  order: 2;
  padding-left: 28px;
}

.project__title {
  color: var(--ink-title);
  font-weight: 500;
  font-size: clamp(22px, 1.458vw, 28px);
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.project__desc {
  max-width: 514px;
  color: var(--ink-soft);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.4;
}

.project--flip .project__desc {
  max-width: 480px;
}

.project__media {
  position: relative;
  display: block;
  width: 100%;
  height: clamp(360px, 36.46vw, 700px);
  padding: 0;
  border: 0;
  overflow: hidden;
  background: #dcdbd8;
  cursor: zoom-in;
}

.project__media:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: 4px;
}

.project__media::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid rgba(0, 0, 0, 0.06);
  pointer-events: none;
}

/* Much taller than its frame so hero.js can drift it at the reference's parallax rate */
.project__media img {
  position: absolute;
  left: 0;
  top: -80%;
  width: 100%;
  height: 180%;
  object-fit: cover;
  transition: scale 0.9s var(--ease-out);
  will-change: transform;
}

.project__media:hover img {
  scale: 1.04;
}

/* ---------- Image viewer (after grids03.obys.agency's trailer) ---------- */
/* The label replaces the system cursor wherever it shows, on mouse devices only */
@media (hover: hover) and (pointer: fine) {
  .project__media {
    cursor: none;
  }
}

.cursor-label {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 90;
  color: var(--ink);
  font-size: 12px;
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  will-change: transform;
}

.cursor-label--light {
  color: var(--fg);
}

.cursor-label[hidden],
.viewer[hidden] {
  display: none;
}

.viewer {
  position: fixed;
  inset: 0;
  z-index: 80;
  /* frosted black: the page blurs behind it */
  background: rgba(0, 0, 0, 0.72);
  -webkit-backdrop-filter: blur(24px);
  backdrop-filter: blur(24px);
  color: var(--fg);
  overscroll-behavior: contain;
  transform: translateY(-100%);
  transition: transform 0.7s cubic-bezier(0.65, 0, 0.35, 1);
  will-change: transform;
}

.viewer.is-open {
  transform: none;
}

/* The project, filling the screen: its pictures down the right, 4:3 and as big as fits (the one
   on top whole, the next peeking in below), and its story in whatever width is left */
.viewer {
  --v-top: 88px; /* under (Close) */
  --v-bottom: var(--gutter);
  /* a picture's width: as wide as 4:3 allows at the screen's height, up to 62% of its width */
  --v-pic: min(62vw, (100vh - var(--v-top) - var(--v-bottom)) * 4 / 3);
  --v-pic: min(62vw, (100svh - var(--v-top) - var(--v-bottom)) * 4 / 3);
}

.viewer__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--v-pic);
  gap: clamp(32px, 4vw, 80px);
  height: 100%;
  padding: 0 var(--gutter);
}

.viewer__info {
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-width: 0;
  padding: var(--v-top) 0 var(--v-bottom);
  text-transform: uppercase;
}

.viewer__count {
  font-size: 12px;
  line-height: 1.2;
  letter-spacing: -0.02em;
  opacity: 0.6;
}

.viewer__title {
  margin: 0;
  font-family: "Archivo", var(--font);
  font-weight: 500;
  font-size: clamp(32px, 3.333vw, 64px);
  line-height: 1;
  letter-spacing: -0.02em;
}

.viewer__desc {
  margin: 0;
  font-size: clamp(14px, 1.042vw, 20px);
  line-height: 1.4;
  letter-spacing: -0.02em;
  opacity: 0.8;
}

/* the details sit at the foot of the column, level with the picture's bottom edge */
.viewer__details {
  margin: auto 0 0;
  padding-top: 24px;
  font-size: 12px;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.viewer__details[hidden] {
  display: none;
}

.viewer__details > div {
  display: grid;
  grid-template-columns: minmax(80px, 30%) 1fr;
  gap: 16px;
  padding: 12px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
}

.viewer__details > div:last-child {
  border-bottom: 1px solid rgba(255, 255, 255, 0.18);
}

.viewer__details dt {
  opacity: 0.55;
}

.viewer__details dd {
  margin: 0;
}

/* the column's text settles in just after the curtain lands */
.viewer__info > * {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s ease, transform 0.7s var(--ease-out);
}

.viewer.is-open .viewer__info > * {
  opacity: 1;
  transform: none;
}

.viewer.is-open .viewer__info > :nth-child(1) { transition-delay: 0.45s; }
.viewer.is-open .viewer__info > :nth-child(2) { transition-delay: 0.52s; }
.viewer.is-open .viewer__info > :nth-child(3) { transition-delay: 0.59s; }
.viewer.is-open .viewer__info > :nth-child(4) { transition-delay: 0.66s; }

/* the pictures: one under another, the column running from under (Close) to the foot of the
   screen, each settling into place as it scrolls */
.viewer__gallery {
  display: flex;
  flex-direction: column;
  gap: 16px;
  height: calc(100% - var(--v-top));
  margin: var(--v-top) 0 0;
  padding: 0 0 var(--v-bottom);
  list-style: none;
  overflow-y: auto;
  overscroll-behavior: contain;
  scroll-snap-type: y proximity;
  scrollbar-width: none;
  outline: none;
}

.viewer__gallery::-webkit-scrollbar {
  display: none;
}

.viewer__gallery li {
  flex: none;
  scroll-snap-align: start;
}

.viewer__gallery img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: rgba(255, 255, 255, 0.04);
}

/* narrow screens: the pictures side by side to swipe through, the story under them; the sheet
   itself scrolls */
@media (max-width: 899px) {
  .viewer {
    --v-top: 64px;
    overflow-y: auto;
  }

  .viewer__layout {
    display: flex;
    flex-direction: column;
    gap: 24px;
    height: auto;
    padding: var(--v-top) 0 var(--v-bottom);
  }

  .viewer__gallery {
    order: -1;
    flex-direction: row;
    gap: 12px;
    height: auto;
    padding: 0 var(--gutter);
    overflow-x: auto;
    margin: 0;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-padding: 0 var(--gutter);
  }

  .viewer__gallery li {
    width: calc(100% - var(--gutter) * 2 - 24px);
  }

  .viewer__gallery li:only-child {
    width: 100%;
  }

  .viewer__info {
    gap: 12px;
    padding: 0 var(--gutter);
  }

  .viewer__details {
    padding-top: 8px;
  }
}

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

/* "(Close)" stands exactly where the contact sheet's does (where the nav's "Contact me" is) */
.viewer__close {
  position: absolute;
  top: 24px;
  right: var(--gutter);
  z-index: 1;
  padding: 0;
  border: 0;
  background: none;
  color: var(--paper);
  font: inherit;
  font-weight: 600;
  font-size: 16px;
  line-height: 1.4;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  cursor: pointer;
  transition: opacity 0.2s ease;
}

.viewer__close:hover {
  opacity: 0.6;
}

.viewer__close:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
}

/* ---------- Career Journey (6435:47448) ---------- */
/* Without career.js (or with reduced motion) this is the static Figma staircase */
.career {
  position: relative;
  /* clip, not hidden: hidden would make this a scroll box and break the sticky stage */
  overflow: clip;
  background: #000;
  color: var(--fg);
}

.career__stage {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 100px;
  padding: clamp(64px, 5.208vw, 100px) var(--gutter) clamp(64px, 7.5vw, 145px);
  overflow: clip;
}

/* The photo, shaded: dark down the left and fading to black at its foot (the shadow belongs to
   the picture, so it travels with it) */
/* deep space behind it all (career-space.js); the photo layer above it now only carries the
   shading, still drifting up with the scroll (career.js) */
.career__space {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: #02040a;
}

.career__photo {
  position: absolute;
  inset: 0;
}

.career__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* (the sky is the hero's own, unshaded, so the hero's end runs straight into it: only the foot
   goes dark, at the end of the section, into the black one after it) */
.career__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0.88) 16%, rgba(0, 0, 0, 0) 52%);
}

/* …and fading to black at the foot, into the black section that follows (579px at 1920) */
.career__stage::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  height: clamp(240px, 30.16vw, 579px);
  background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
  pointer-events: none;
}

.career__stage > :not(.career__photo):not(.career__space) {
  position: relative;
  z-index: 2;
}

/* (white at 40%) */
.section-head--light .section-head__rule {
  border-color: rgba(255, 255, 255, 0.4);
}

.section-head--light .label {
  color: var(--fg);
  font-weight: 600;
}

.career .display {
  max-width: 5.2em;
}

/* Four cards stepping down and to the right, each overlapping the last by 40px */
.jobs {
  --card-h: 480px;
  --step: calc(var(--card-h) - 40px);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin: 0;
  padding: 0 0 calc(3 * var(--step));
  list-style: none;
}

.job {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 40px;
  min-height: var(--card-h);
  padding: 24px;
  /* glass over the sky: a frosted pane, a faint sheen at the top settling into a dark tint, and a hairline
     rim catching the light along its top edge — as the Daily Toolkit's cards */
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.04) 45%, rgba(6, 10, 24, 0.38));
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.2),
    0 20px 48px rgba(0, 0, 0, 0.25);
  /* (a lighter blur: the sky behind changes every frame, so the blur is redone every frame too) */
  -webkit-backdrop-filter: blur(10px) saturate(1.3);
  backdrop-filter: blur(10px) saturate(1.3);
  color: var(--fg);
  transform: translateY(calc(var(--i) * var(--step)));
}

/* Pinned version (career.js adds .is-pinned): the stage sticks for a few screens while
   the cards rise into place one by one and the photo keeps drifting with the scroll */
.career.is-pinned {
  /* (held long enough for the cards to rise unhurried) */
  height: 520vh;
  height: 520svh;
}

.is-pinned .career__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  gap: clamp(24px, 6vh, 100px);
  padding: clamp(80px, 10.3vh, 100px) var(--gutter) clamp(20px, 4vh, 40px);
}

/* The photo at its own proportions (and never shorter than 130% of the stage), so there's room
   to travel; career.js moves it, shadow and all */
.is-pinned .career__photo {
  inset: 0 0 auto;
  aspect-ratio: 904 / 1200;
  min-height: 130%;
  will-change: transform;
}

/* (no fade at the stage's foot while it's pinned: the photo's own shadow takes it to black) */
.is-pinned .career__stage::after {
  display: none;
}

/* Keep the header from eating the screen on short viewports */
.is-pinned .display {
  font-size: clamp(44px, min(8.333vw, 13vh), 160px);
}

.is-pinned .jobs {
  flex: 1;
  min-height: 0;
  padding: 0;
  align-items: start;
}

.is-pinned .job {
  height: 100%;
  max-height: var(--card-h);
  min-height: 0;
  gap: clamp(16px, 3vh, 40px);
  /* Parked below the stage until career.js lifts it */
  transform: translateY(110vh);
  will-change: transform;
}

.job__head {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.job__n {
  color: rgba(255, 255, 255, 0.7);
  font-weight: 500;
  font-size: 16px;
}

.job__title {
  font-weight: 500;
  font-size: clamp(28px, 2.083vw, 40px);
  line-height: 1;
  letter-spacing: -0.02em;
}

.job__desc {
  color: rgba(255, 255, 255, 0.72);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.4;
}

.job__meta li {
  padding: 4px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.22);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.6;
  text-transform: uppercase;
}

.job__meta li:last-child {
  border-bottom: 1px solid rgba(255, 255, 255, 0.22);
}

/* ---------- Recognition (6594:42980) ---------- */
/* White on black, carrying on from the foot of Career Journey */
.recognition {
  background: #000;
  color: #fff;
}

.recognition .display,
.recognition .label {
  color: #fff;
}

/* (its header rule is black on black in the design: there, but unseen) */
.recognition .section-head__rule,
.recognition [data-reveal="rule"]::before {
  border-top-color: #000;
  background: #000;
}
.section-head .recognition__title {
  max-width: 5.86em;
  font-weight: 500;
}

/* 1347 of the 1800px content width, right-aligned */
.wins {
  align-self: flex-end;
  width: 74.83%;
  margin: 0;
  padding: 0;
  list-style: none;
}

.wins li {
  display: grid;
  grid-template-columns: 11.21% 33.63% 11.21% 11.21% 1fr auto;
  padding: 24px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.4);
  color: #fff;
  font-weight: 500;
  font-size: 16px;
  line-height: 1.4;
}

.wins__name {
  text-transform: uppercase;
}

.wins__year {
  text-align: right;
}

/* Below ~1600px the Figma proportions squeeze the description onto two lines */
@media (min-width: 1024px) and (max-width: 1599px) {
  .wins li {
    grid-template-columns: 8% 30% 10% 10% 1fr auto;
  }
}

/* ---------- Footer (layout after crosswalk.cl) ---------- */
/* A screen-high black panel with a fine grain. Hairlines frame the top row — title on the left
   (7 of 12 columns), intro on the right — and a vertical rule runs down from it, splitting the
   form (bottom-left) from the links (bottom-right). */
.footer {
  --line: rgba(239, 238, 236, 0.5);
  display: grid;
  grid-template-rows: auto 1fr;
  min-height: max(100vh, 640px);
  min-height: max(100svh, 640px);
  padding: clamp(72px, 7vw, 134px) var(--gutter) 0;
  color: var(--paper);
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.09 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"),
    #121211;
}

.footer__head {
  display: grid;
  grid-template-columns: 7fr 5fr;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.footer__title {
  align-self: center;
  padding: clamp(36px, 3.4vw, 64px) 0;
  color: var(--paper);
  font-weight: 400;
  font-size: clamp(52px, 6.6vw, 127px);
  line-height: 1.1;
  letter-spacing: -0.035em;
}

.footer__intro {
  padding: clamp(40px, 5vw, 96px) 0 clamp(56px, 7vw, 134px) 2em;
  border-left: 1px solid var(--line);
  font-size: clamp(17px, 1.45vw, 28px);
  line-height: 1.3;
  letter-spacing: -0.01em;
}

.footer__intro p {
  max-width: 26em;
}

.footer__body {
  display: grid;
  grid-template-columns: 6fr 1fr 5fr;
}

.footer__form {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 10px;
  padding: 40px 0 clamp(40px, 4vw, 77px);
}

.footer__form input,
.footer__form textarea {
  display: block;
  width: 100%;
  padding: 6px 10px;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  color: var(--paper);
  font: 400 14px/20px var(--font);
  outline: none;
  transition: border-color 0.25s ease;
}

.footer__form input {
  height: 38px;
}

.footer__form textarea {
  height: 140px;
  resize: vertical;
}

.footer__form ::placeholder {
  color: var(--paper);
  opacity: 0.9;
}

.footer__form input:focus,
.footer__form textarea:focus {
  border-bottom-color: var(--paper);
}

/* 48px high, in capitals, the label centred (as is "Schedule a call" beside it) */
.footer__form button {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  padding: 0 28px;
  border: 0;
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
  font: 500 14px/1 var(--font);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 0.25s ease;
}

.footer__form button:hover {
  background: #fff;
}

.footer__contact {
  grid-column: 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 8px;
  padding: 40px 0 clamp(40px, 4vw, 77px) 2em;
  border-left: 1px solid var(--line);
}

.footer__small {
  font-size: clamp(14px, 1.06vw, 20px);
  line-height: 1.3;
}

.footer__small a {
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  transition: opacity 0.2s ease;
}

.footer__small a:hover {
  opacity: 0.6;
}

.footer__legal {
  opacity: 0.6;
}

/* An outline pill beside the form's solid Submit; it fills on hover and the arrow nudges along */
.footer__call {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  height: 48px;
  margin-top: 16px;
  padding: 0 28px;
  border: 1px solid var(--paper);
  border-radius: 999px;
  color: var(--paper);
  font: 500 14px/1 var(--font);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  transition:
    background-color 0.25s ease,
    color 0.25s ease;
}

.footer__call svg {
  flex: none;
  transition: transform 0.25s var(--ease-out);
}

.footer__call:hover {
  background: var(--paper);
  color: var(--ink);
}

.footer__call:hover svg {
  transform: translateX(3px);
}

.footer__call:focus-visible {
  outline: 1px solid var(--paper);
  outline-offset: 3px;
}

/* ---------- Site footer (6594:43052) ---------- */
/* Black. The invitation and its white pill on the left, the links in a row
   bottom-right; then five rows of dots (the design's own SVG row, scaled with the page). Set in
   Archivo. (The .footer rules above dress the contact sheet.) */
.site-footer {
  --pad: clamp(16px, 2.083vw, 40px);
  display: flex;
  flex-direction: column;
  padding: 0 var(--pad) clamp(48px, 5vw, 96px);
  background: #000;
  color: #fff;
  font-family: "Archivo", var(--font);
}

.site-footer__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 40px;
  padding: clamp(28px, 2.19vw, 42px) 0 0;
}

.site-footer__lead {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(24px, 2.083vw, 40px);
}

.site-footer__title {
  font-weight: 600;
  font-size: clamp(40px, 4.167vw, 80px);
  line-height: 1;
  letter-spacing: -0.04em;
  /* in capitals, like the other section titles */
  text-transform: uppercase;
}

.site-footer__title span {
  display: block;
}

/* The same outline pill as "Schedule a call" in the contact sheet: 48px high, in capitals, an
   arrow after the label; it fills white on hover and the arrow nudges along */
.site-footer__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  height: 48px;
  padding: 0 28px;
  border: 1px solid #fff;
  border-radius: 999px;
  /* (black, so the dots' links pass behind it rather than through it) */
  background: #000;
  color: #fff;
  font: 500 14px/1 var(--font);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  transition:
    background-color 0.25s ease,
    color 0.25s ease;
}

.site-footer__button svg {
  flex: none;
  transition: transform 0.25s var(--ease-out);
}

.site-footer__button:hover {
  background: #fff;
  color: #070707;
}

.site-footer__button:hover svg {
  transform: translateX(3px);
}

.site-footer__button:focus-visible {
  outline: 1px solid #fff;
  outline-offset: 3px;
}

.site-footer__social {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-weight: 500;
  font-size: 14px;
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

.site-footer__social a {
  background: linear-gradient(currentColor, currentColor) 0 100% / 0 1px no-repeat;
  transition: background-size 0.35s var(--ease-out);
}

.site-footer__social a:hover {
  background-size: 100% 1px;
}

/* Five rows, 105px apart at 1920 */
.site-footer__dots {
  display: flex;
  flex-direction: column;
  gap: clamp(40px, 5.47vw, 105px);
  margin-top: clamp(24px, 2.5vw, 48px);
}

.site-footer__dots img {
  display: block;
  width: 100%;
  height: auto;
}

/* footer-dots.js draws the node, its links and the dots it has passed on a canvas over the whole
   footer, behind its text (so the node still shows off the field); the rows themselves step back
   (still holding the layout) so unvisited dots stay dark */
.site-footer {
  position: relative;
}

.site-footer > :not(.site-footer__links) {
  position: relative;
  z-index: 1;
}

.site-footer__dots.has-links img {
  visibility: hidden;
}

.site-footer__links {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

@media (max-width: 767px) {
  .site-footer__head {
    flex-direction: column;
    align-items: flex-start;
  }

  .site-footer__social {
    gap: 16px;
  }
}

/* ---------- Contact sheet (contact-sheet.js) ---------- */
/* "Contact me" drops a copy of the footer over the page, the same curtain as the project viewer:
   the same frosted black, the page blurred behind it */
.contact-sheet {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: rgba(0, 0, 0, 0.72);
  -webkit-backdrop-filter: blur(24px);
  backdrop-filter: blur(24px);
  overflow-y: auto;
  overscroll-behavior: contain;
  transform: translateY(-100%);
  transition: transform 0.7s cubic-bezier(0.65, 0, 0.35, 1);
  will-change: transform;
}

.contact-sheet.is-open {
  transform: none;
}

.contact-sheet[hidden] {
  display: none;
}

/* (the nav's "Contact me" would show through under its (Close)) */
.nav__cta[aria-expanded="true"],
body:has(.viewer.is-open) .nav__cta {
  visibility: hidden;
}

/* Where "Contact me" sits in the nav, so the button seems to turn into it */
.contact-sheet__close {
  position: absolute;
  top: 24px;
  right: var(--gutter);
  padding: 0;
  border: 0;
  background: none;
  color: var(--paper);
  font: inherit;
  font-weight: 600;
  font-size: 16px;
  line-height: 1.4;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  cursor: pointer;
  transition: opacity 0.2s ease;
}

.contact-sheet__close:hover {
  opacity: 0.6;
}

.contact-sheet__close:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
}

/* ---------- Leaving (text-fx.js) ---------- */
/* The letter spans text-fx.js wraps text in are plain inline text, whatever rules the spans of
   their block have (some titles set their own spans to block, for a line each) */
.sc-run,
.sc-run .sc {
  display: inline !important;
  margin: 0 !important;
  padding: 0 !important;
}

/* A letter mid-scramble keeps its own glyph, unseen, holding its place, and the noise is drawn
   over it, centred: a wider or narrower character can't rewrap the line, so the page never shifts
   under the scroll */
.sc.is-noise {
  position: relative;
  -webkit-text-fill-color: transparent;
}

.sc.is-noise::after {
  content: attr(data-g);
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  -webkit-text-fill-color: currentColor;
}

/* ---------- Enter motion (reveal.js) ---------- */
/* Small text: rises and fades in like the hero's bio — 45px, over 2.5s on the appear curve —
   every time it comes up from below (reveal.js resets it, without animating, once it's gone) */
[data-reveal="text"] {
  transition:
    opacity 2.5s var(--ease-appear) var(--rd, 0s),
    transform 2.5s var(--ease-appear) var(--rd, 0s),
    background-color 0.25s ease;
}

[data-reveal="text"]:not(.is-in) {
  opacity: 0.001;
  transform: translateY(45px);
}

[data-reveal="text"].reveal-reset {
  transition: none;
}

/* Cards: further and slower, one after another */
[data-reveal="card"] {
  transition:
    opacity 1.4s var(--ease-out) var(--rd, 0s),
    transform 1.4s var(--ease-out) var(--rd, 0s);
}

[data-reveal="card"]:not(.is-in) {
  opacity: 0;
  transform: translateY(48px);
}

/* Section hairlines draw in from the left */
/* The ::before line replaces the border (the light variant's border rule is more specific) */
.section-head__rule[data-reveal="rule"],
.section-head--light .section-head__rule[data-reveal="rule"] {
  position: relative;
  border-top-color: transparent;
}

[data-reveal="rule"]::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  right: 0;
  height: 1px;
  background: #000;
  transform-origin: left;
  transition: transform 1.6s var(--ease-appear);
}

.section-head--light [data-reveal="rule"]::before {
  background: rgba(255, 255, 255, 0.4);
}

[data-reveal="rule"]:not(.is-in)::before {
  transform: scaleX(0);
}

/* ---------- Tablet / mobile ---------- */
@media (max-width: 1023px) {
  .nav__links {
    display: none;
  }

  .nav__right {
    width: auto;
  }

  .hero__intro {
    left: var(--gutter);
    top: 104px;
    bottom: auto;
    width: min(340px, 72vw);
    font-size: 14px;
  }

  .problem,
  .section-head__top {
    flex-direction: column;
    align-items: flex-start;
  }

  /* Footer: everything in one column, the rules turn horizontal */
  .footer__head,
  .footer__body {
    grid-template-columns: 1fr;
  }

  .footer__intro {
    padding: 24px 0 40px;
    border-left: 0;
  }

  .footer__contact {
    grid-column: 1;
    padding: 24px 0 40px;
    border-top: 1px solid var(--line);
    border-left: 0;
  }

  .hero__page .problem {
    flex-wrap: nowrap;
    justify-content: center;
  }

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

  .project,
  .project--flip {
    grid-template-columns: 1fr;
    gap: 20px;
    padding: 0 0 56px;
  }

  .project__text,
  .project--flip .project__text {
    order: 2;
    padding: 0;
  }

  .project__media {
    height: auto;
    aspect-ratio: 4 / 3;
  }

  /* Cards stack; the stagger only works with four columns */
  .jobs {
    grid-template-columns: 1fr 1fr;
    padding: 0;
  }

  .job {
    min-height: 400px;
    transform: none;
  }

  /* One card on top of the next, like a deck */
  .is-pinned .career__stage {
    gap: 24px;
  }

  .is-pinned .jobs {
    grid-template-columns: 1fr;
  }

  .is-pinned .job {
    grid-area: 1 / 1;
    height: calc(100% - 42px);
    min-height: 0;
    max-height: 420px;
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.2),
      0 -12px 32px rgba(0, 0, 0, 0.35);
  }

  .wins {
    width: 100%;
  }
}

@media (max-width: 599px) {
  .nav__cta {
    font-size: 14px;
  }

  .tools,
  .jobs {
    grid-template-columns: 1fr;
  }

  .tool {
    min-height: 0;
  }

  .job {
    min-height: 0;
  }

  /* Row 1: number, name, year. Row 2: placing and awarding body */
  .wins li {
    grid-template-columns: 40px auto 1fr auto;
    column-gap: 12px;
    row-gap: 4px;
  }

  .wins__name {
    grid-column: 2 / 4;
  }

  /* Every row says "Winner", so it only matters to screen readers here */
  .wins li > :nth-child(3) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .wins li > :nth-child(4),
  .wins__by {
    grid-row: 2;
    color: rgba(255, 255, 255, 0.6);
  }

  .wins li > :nth-child(4) {
    grid-column: 2;
  }

  .wins__by {
    grid-column: 3 / -1;
  }

  .wins__year {
    grid-column: 4;
    grid-row: 1;
  }

}

@media (max-height: 500px) and (orientation: landscape) {
  .hero {
    --frame-min: 0px;
  }

  /* The Daily Toolkit on a phone on its side: a compact header, and the four cards in a row
     just under it, taking the rest of the screen */
  .hero__kit {
    --kit-edge: var(--gutter);
  }

  .hero__kit-head {
    top: 72px;
    flex-direction: row;
    align-items: flex-end;
    gap: 24px;
  }

  .hero__kit-heading {
    gap: 8px;
  }

  .hero__kit-label {
    font-size: 12px;
  }

  .hero__kit-title {
    font-size: 28px;
  }

  .hero__kit-lead {
    width: min(300px, 45%);
    font-size: 12px;
    text-align: right;
  }

  .hero__kit-cards {
    top: 140px;
    height: auto;
    bottom: 4%;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
  }

  .hero__kit-card {
    --rim: calc(var(--bw, 100%) * 0.08);
  }

  .hero__kit-glass {
    gap: 8px;
    padding: 10px;
  }

  .hero__kit-desc {
    display: none;
  }

  .hero__kit-list li {
    gap: 6px;
    padding: 1px 0;
    font-size: 10px;
  }

  .hero__kit-list img {
    width: 11px;
    height: 11px;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* No dive: the hero is a plain screen-high section, the next section simply follows it */
  .hero {
    display: flex;
    flex-direction: column;
    height: auto;
  }

  /* (the sort is drawn still, at its cards — hero-sort.js — with the hero's text over it; the
     projects, which otherwise rise over it as the page scrolls, follow it as a plain list, and
     the nav's "Projects," lands on them) */
  .hero--sort .hero__ui {
    opacity: 1;
  }

  .hero__anchor {
    order: 1;
    position: relative;
    top: auto;
  }

  .hero__projects {
    order: 1;
    position: relative;
    height: auto;
    min-height: 0;
    margin-top: 0;
    padding: clamp(64px, 10vh, 120px) 0;
    overflow: visible;
    opacity: 1;
    background: var(--hero-bg);
  }

  .hero__projects-list {
    position: relative;
    left: auto;
    right: auto;
    padding: 0 var(--gutter);
    transform: none;
  }

  .hero__projects .hero__proj-media {
    pointer-events: auto;
  }

  .hero__proj-media img {
    top: -40%;
  }

  .hero__frame {
    position: relative;
  }

  .hero__device {
    position: absolute;
  }

  .hero__flash {
    display: none;
  }

  .hero__page,
  .hero:not(.has-device) .hero__page,
  .hero--vortex .hero__page,
  .hero.hero--sort .hero__page {
    order: 1;
    position: relative;
    height: auto;
    min-height: 0;
    margin-top: 0;
    clip-path: none;
    opacity: 1;
    filter: none;
    transform: none;
  }

  .hero__page .problem {
    height: auto;
  }

  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
    animation: none !important;
  }
}
