/* ─────────────────────────────────────────────────────────────────────
   Alish Karki — "SIGNAL"
   A directed, animated instrument. Two channels, one instrument: a canvas
   lane-field that answers to the pointer, kinetic type, a pinned horizontal
   collection, and a scroll that behaves like a system running.

   Colour as two signals, nothing else:
     --a  vermillion  (the hot path / the input)
     --b  mint        (the cool path / the output)
   Type   Outfit (display) · IBM Plex Mono (data) · Arial (body)

   Motion: transform and opacity only; strong custom curves; hover gated to
   real pointers; every effect has a reduced-motion alternative.
   ───────────────────────────────────────────────────────────────────── */

@font-face {
  font-family: 'Outfit';
  src: url('fonts/outfit-latin-wght-normal.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('fonts/ibm-plex-mono-latin-400-normal.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('fonts/ibm-plex-mono-latin-500-normal.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: dark;
  --bg: #08080a;
  --panel: #0f0f13;
  --ink: #f3f0e9;
  --dim: #8c8c94;
  --faint: #82828a; /* micro-labels: 5.1:1 on --bg, was 2.6:1 */
  --line: #1d1d23;
  --line-2: #2a2a32;
  --a: #ff5230;
  --b: #4de3c0;

  --display: 'Outfit', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --sans: Arial, 'Helvetica Neue', Helvetica, sans-serif;

  --gutter: clamp(1.25rem, 5vw, 5.5rem);
  --pad: clamp(1.25rem, 5vw, 5.5rem);
  --rule: 1px solid var(--line);

  /* strong custom curves; the built-in CSS easings are too weak */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease: cubic-bezier(0.23, 1, 0.32, 1);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}
* {
  margin: 0;
}
html {
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}
body {
  font-family: var(--sans);
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.08rem);
  line-height: 1.55;
  color: var(--ink);
  background: var(--bg);
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
  overflow-x: hidden;
}
a {
  color: inherit;
  text-decoration: none;
}
button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}
/* controls: no tap delay, no long-press selection or callout */
a,
button,
[role='button'] {
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
canvas {
  display: block;
}
::selection {
  background: var(--a);
  color: #0a0a0c;
}
:focus-visible {
  outline: 2px solid var(--b);
  outline-offset: 3px;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* the skip link is visually hidden until it takes focus */
.visually-hidden:focus {
  position: fixed;
  top: calc(0.75rem + env(safe-area-inset-top, 0px));
  left: 0.75rem;
  z-index: 110;
  width: auto;
  height: auto;
  clip: auto;
  padding: 0.6rem 0.9rem;
  background: var(--b);
  color: #08080a;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
}
.mono {
  font-family: var(--mono);
}
.eyebrow {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
}
.eyebrow .dot {
  color: var(--a);
}

/* film grain — a static texture so flat areas have material. It never moves:
   a full-screen animated layer is decoration that spends frame budget. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ── cursor ──────────────────────────────────────────────────────────────
   The outer element is positioned by a transform; the ring is a child that
   scales. Nothing animates width, height or margin. */
.cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 100;
  pointer-events: none;
  will-change: transform;
}
.cursor i {
  display: block;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border: 1px solid var(--ink);
  border-radius: 50%;
  mix-blend-mode: difference;
  transition: transform 0.25s var(--ease-out), background-color 0.25s var(--ease-out);
}
.cursor[data-hot] i {
  transform: scale(1.95);
  /* accent, not --ink: difference-blending a cream fill over the dark cards
     turned the ring into a solid white disc */
  background: var(--a);
  border-color: var(--a);
  mix-blend-mode: normal;
}
@media (hover: none), (prefers-reduced-motion: reduce) {
  .cursor {
    display: none;
  }
}

/* ── progress ────────────────────────────────────────────────────────── */
.progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 80;
  height: 2px;
  background: transparent;
}
.progress span {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--b), var(--a));
}

/* ── header ──────────────────────────────────────────────────────────── */
.head {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: 1.1rem var(--pad);
  padding-top: calc(1.1rem + env(safe-area-inset-top, 0px));
  background: linear-gradient(180deg, rgba(8, 8, 10, 0.92) 0%, rgba(8, 8, 10, 0) 100%);
  transition: transform 0.45s var(--ease-out), opacity 0.3s var(--ease-out);
}
.head[data-hidden] {
  transform: translateY(-110%);
  opacity: 0;
}
.head-mark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  min-width: 0;
}
/* The mark: a cream squircle with an A and K sharing one stroke — the A's
   right leg is the K's stem, so the monogram interlocks the way the reference
   does. Inline SVG so it takes the palette and costs no request. */
.head-logo {
  flex: none;
  width: 28px;
  height: 28px;
  transition: transform 0.3s var(--ease-out);
}
.head-logo .tile {
  fill: #f4ede1;
  /* a slow glow so the mark is never completely still between redraws */
  animation: ak-glow 4s ease-in-out infinite;
}
.head-logo .ink-wrap {
  stroke: var(--a);
}
/* The mark retracts and redraws itself on a slow loop — the one piece of
   motion that never stops. Each stroke waits its turn (the A, its crossbar,
   then the two K arms) by delaying its own cycle, so the redraw re-traces the
   order it was first drawn. --len is each path's real length; the dash math
   needs no measurement at runtime and works with JavaScript off. */
.head-logo .ink {
  --len: 137;
  stroke-dasharray: var(--len);
  stroke-dashoffset: 0;
  animation: ak-cycle 6s linear infinite;
}
.head-logo .a2 {
  --len: 23;
  animation-delay: 0.14s;
}
.head-logo .k1,
.head-logo .k2 {
  --len: 40;
  animation-delay: 0.28s;
}
@keyframes ak-cycle {
  0%,
  84% {
    stroke-dashoffset: 0;
  }
  90% {
    stroke-dashoffset: var(--len);
  }
  100% {
    stroke-dashoffset: 0;
  }
}
@keyframes ak-glow {
  0%,
  100% {
    filter: drop-shadow(0 0 0 rgba(255, 82, 48, 0));
  }
  50% {
    filter: drop-shadow(0 0 8px rgba(255, 82, 48, 0.5));
  }
}
/* Hover: the tile lifts and the K opens its arms. */
.head-logo .arms {
  transform-box: fill-box;
  transform-origin: 0% 50%;
  transition: transform 0.35s var(--ease-out);
}
.head-mark:hover .head-logo,
.head-mark:focus-visible .head-logo {
  transform: translateY(-1px) scale(1.04);
}
.head-mark:hover .head-logo .arms,
.head-mark:focus-visible .head-logo .arms {
  transform: scaleX(1.14);
}
.head-nav {
  margin-left: auto;
  display: flex;
  gap: 1.6rem;
}
.head-nav a {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dim);
  transition: color 0.2s var(--ease-out);
}

/* ── intro ───────────────────────────────────────────────────────────── */
.intro {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: grid;
  grid-template-rows: 1fr auto;
  padding: var(--pad);
  background: var(--bg);
}
.intro[data-done] {
  pointer-events: none;
}
.intro-count {
  align-self: center;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(4rem, 18vw, 16rem);
  line-height: 0.82;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.intro-foot {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}
.intro-skip {
  color: var(--dim);
  transition: color 0.2s var(--ease-out);
}

/* ── hero ────────────────────────────────────────────────────────────── */
.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  grid-template-rows: 1fr auto;
  padding: var(--pad);
  overflow: clip;
}
#field {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}
/* scrim: the field is bright mid-frame, so the copy gets its own contrast */
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(8, 8, 10, 0.55) 0%,
    rgba(8, 8, 10, 0.28) 20%,
    rgba(8, 8, 10, 0.44) 55%,
    rgba(8, 8, 10, 0.95) 100%
  );
}
.hero-inner {
  position: relative;
  z-index: 1;
  align-self: end;
  display: grid;
  gap: clamp(1.5rem, 4vw, 3rem);
  padding-bottom: clamp(1.5rem, 6vw, 4rem);
}
.hero-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.6rem, 8.4vw, 9rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
  max-width: 15ch;
  text-wrap: balance;
}
.hero-title em {
  font-style: normal;
  color: var(--a);
}
.hero-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2rem;
  align-items: end;
  border-top: var(--rule);
  padding-top: 1.25rem;
}
.hero-sub {
  max-width: 46ch;
  color: var(--dim);
}
.hero-sub strong {
  color: var(--ink);
  font-weight: 700;
}
.hero-cta {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.85rem 1.25rem;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  transition: border-color 0.2s var(--ease-out), color 0.2s var(--ease-out),
    background-color 0.2s var(--ease-out), transform 0.16s var(--ease-out);
}
/* press feedback: the button answers on pointer-down */
.btn:active {
  transform: scale(0.97);
}
.btn-fill {
  background: var(--ink);
  color: #0a0a0c;
  border-color: var(--ink);
}
.hero-rail {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  padding-top: 1.1rem;
  border-top: var(--rule);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}
.hero-rail b {
  color: var(--dim);
  font-weight: 400;
}
.scroll-cue {
  position: absolute;
  right: var(--pad);
  bottom: var(--pad);
  z-index: 1;
  writing-mode: vertical-rl;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  display: flex;
  align-items: center;
  gap: 0.8rem;
}
.scroll-cue i {
  width: 1px;
  height: 48px;
  background: linear-gradient(var(--a), transparent);
  animation: cue 2s var(--ease-out) infinite;
}
@keyframes cue {
  0%, 100% { transform: scaleY(0.4); opacity: 0.4; }
  50% { transform: scaleY(1); opacity: 1; }
}

/* split-text reveal */
.line {
  display: block;
  overflow: hidden;
}
.line > span {
  display: block;
  transform: translateY(105%);
}

/* ── marquee ─────────────────────────────────────────────────────────── */
.marquee {
  border-top: var(--rule);
  border-bottom: var(--rule);
  padding: 1.6rem 0;
  overflow: hidden;
  background: var(--panel);
}
.marquee-track {
  display: flex;
  gap: 3rem;
  width: max-content;
  animation: slide 34s linear infinite;
}
.marquee-track span {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.5rem, 4vw, 3rem);
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--dim);
  display: inline-flex;
  align-items: center;
  gap: 3rem;
  white-space: nowrap;
}
.marquee-track span::after {
  content: '';
  width: 12px;
  height: 12px;
  background: var(--a);
  border-radius: 50%;
}
@keyframes slide {
  to { transform: translateX(-50%); }
}

/* ── section chrome ──────────────────────────────────────────────────── */
.section {
  padding: clamp(4rem, 12vw, 10rem) var(--pad);
}
.sec-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  border-top: var(--rule);
  padding-top: 1.25rem;
  margin-bottom: clamp(2.5rem, 6vw, 5rem);
}
.sec-index {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--a);
}
.sec-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.8rem, 5vw, 4.2rem);
  line-height: 0.95;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}
.sec-note {
  max-width: 34ch;
  color: var(--dim);
  text-align: right;
}

/* ── collection (pinned horizontal) ──────────────────────────────────── */
.collection {
  position: relative;
  padding: 0;
  overflow: hidden;
}
.collhead {
  padding: clamp(3rem, 6vw, 5rem) var(--pad) 0;
}
.collection .sec-head {
  margin-bottom: 0;
}
.collection .sec-title {
  font-size: clamp(1.6rem, 4vw, 3.4rem);
}
.track {
  display: flex;
  gap: clamp(1rem, 3vw, 2.5rem);
  padding: clamp(1.5rem, 3vw, 2.5rem) var(--pad);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  will-change: transform;
}
/* when the pin is driving the track, it must not scroll itself */
html.pin .track {
  overflow: visible;
}
/* A pinned section taller than the window has its bottom cut off for the whole
   length of the scroll — and that bottom strip is exactly where each project's
   link sits, so the cards read as dead ends. So while the pin is on, the
   vertical space is driven by viewport height: identical on a tall window,
   squeezed on a short one. main.js additionally refuses to pin below a floor
   height, so this never has to squeeze past the point where copy stops
   fitting. */
html.pin .collhead {
  padding-top: clamp(1.25rem, min(6vw, 7vh), 5rem);
}
html.pin .track {
  padding-block: clamp(0.9rem, min(3vw, 3vh), 2.5rem);
}
html.pin .panel canvas {
  height: clamp(96px, min(19vw, 21vh), 200px);
}
.panel {
  position: relative;
  flex: 0 0 auto;
  width: min(78vw, 480px);
  scroll-snap-align: start;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 1rem;
  padding: 1.4rem;
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: 4px;
}
.panel:nth-child(3n) {
  width: min(78vw, 560px);
}
.panel canvas {
  width: 100%;
  height: clamp(140px, 19vw, 200px);
}
.panel-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-width: 0;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}
.panel-top > * {
  min-width: 0;
  overflow-wrap: anywhere;
}
.panel-no {
  color: var(--a);
}
.panel-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.6rem, 3.4vw, 2.6rem);
  line-height: 1;
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}
.panel-name em {
  font-style: normal;
  color: var(--dim);
  font-weight: 400;
}
.panel-desc {
  color: var(--dim);
  font-size: 0.95rem;
  max-width: 44ch;
  overflow-wrap: anywhere;
}
.panel-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border-top: var(--rule);
  padding-top: 1rem;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}
.panel-foot .go {
  color: var(--b);
  transition: color 0.2s var(--ease-out);
}
/* the whole card opens the card's link, not just the small footer one:
   a stretched ::after keeps one <a> per card instead of nesting links or
   adding a click handler (no JS, keyboard and screen readers unchanged) */
.panel-foot .go::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.coll-progress {
  position: absolute;
  left: var(--pad);
  right: var(--pad);
  bottom: clamp(1rem, 3vw, 2rem);
  height: 1px;
  background: var(--line);
}
.coll-progress i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--a);
}

/* ── run ───────────────────────────────────────────────────────────────
   The one section that does something rather than describing something.
   Bars grow with scaleX, never width: the runner measures this page's
   frame budget at the same time as it measures the kernel. */
.run .sec-title em {
  font-style: normal;
  color: var(--a);
}
.run-gate {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem 1.75rem;
  padding-bottom: clamp(1.5rem, 3vw, 2.5rem);
}
.run-status {
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}
.run-gate .btn[disabled] {
  opacity: 0.5;
  pointer-events: none;
}
.run-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.75rem;
  border-top: var(--rule);
  padding-top: 1rem;
  margin-bottom: clamp(1.25rem, 3vw, 2rem);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}
.run-legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}
/* .run-legend span is more specific than [hidden], so hiding the SIMD128
   entry when the engine has none needs saying explicitly — otherwise the
   panel reports "no SIMD128" while still listing it */
.run-legend span[hidden] {
  display: none;
}
.swatch {
  width: 10px;
  height: 10px;
  background: var(--line-2);
}
.swatch-a {
  background: var(--a);
}
.swatch-b {
  background: var(--b);
}
/* pushes itself right on desktop, wraps under the legend on narrow screens */
.run-machine {
  margin-left: auto;
  color: var(--dim);
}
.run-rows {
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.run-row {
  display: grid;
  grid-template-columns: minmax(7rem, 11rem) minmax(0, 1fr) 4rem;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.5rem);
  background: var(--bg);
  padding: clamp(1rem, 2.5vw, 1.6rem) clamp(1rem, 2.5vw, 2rem);
}
.run-size {
  display: block;
  font-size: 0.95rem;
  color: var(--ink);
}
.run-where {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.78rem;
  color: var(--faint);
}
.run-graph {
  display: grid;
  gap: 0.5rem;
}
.run-g {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 6rem;
  align-items: center;
  gap: 1rem;
}
.bar {
  display: block;
  height: 8px;
  background: var(--line-2);
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform 0.7s var(--ease-out);
}
.bar-a {
  background: var(--a);
}
.bar-b {
  background: var(--b);
}
.run-val {
  font-size: 0.72rem;
  color: var(--dim);
  text-align: right;
}
.run-val em {
  font-style: normal;
  margin-left: 0.3rem;
  color: var(--faint);
}
.run-x {
  font-size: 0.82rem;
  color: var(--ink);
  text-align: right;
}
.run-method {
  max-width: 64ch;
  margin-top: clamp(1.25rem, 3vw, 2rem);
  font-size: 0.68rem;
  line-height: 1.75;
  letter-spacing: 0.04em;
  color: var(--faint);
}

/* ── bench ───────────────────────────────────────────────────────────── */
.bench {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.bench-cell {
  position: relative;
  background: var(--bg);
  padding: clamp(1.5rem, 3vw, 2.5rem);
  min-height: 220px;
  display: grid;
  align-content: space-between;
  gap: 2rem;
  overflow: hidden;
  transition: background-color 0.4s var(--ease-out);
}
.bench-cell h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.1rem, 2vw, 1.5rem);
  letter-spacing: -0.01em;
}
.bench-cell .cap {
  font-family: var(--mono);
  font-size: 0.76rem;
  line-height: 1.7;
  color: var(--dim);
}
.bench-cell .num {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  color: var(--faint);
}
.bench-cell::after {
  content: '';
  position: absolute;
  right: -30%;
  bottom: -30%;
  width: 160px;
  height: 160px;
  border: 1px solid var(--line-2);
  border-radius: 50%;
  transition: transform 0.6s var(--ease-out), border-color 0.4s var(--ease-out);
}

/* ── case study ────────────────────────────────────────────────────────
   Project pages: one channel of the same instrument. A document, set in
   the site's type and colour, using the shared reveals and no motion of
   its own. Generated by tools/make-case.py. */
.doc {
  padding: clamp(6rem, 14vw, 11rem) var(--pad) clamp(3rem, 8vw, 6rem);
  /* the prose is capped at a reading measure; without this the cap sat against
     the left pad and left every spare pixel as dead space on the right */
  max-width: calc(74ch + 2 * var(--pad));
  margin-inline: auto;
}
.doc > * {
  max-width: 74ch;
}
.doc-back {
  display: inline-block;
  margin-bottom: clamp(2rem, 6vw, 4rem);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  transition: color 0.2s var(--ease-out);
}
.doc-back span {
  color: var(--a);
}
.doc-title {
  margin-top: 0.6rem;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2rem, 6vw, 4.6rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
}
.doc-lede {
  max-width: 44ch;
  margin-top: 1.5rem;
  font-size: clamp(1.05rem, 2.2vw, 1.4rem);
  line-height: 1.5;
  color: var(--ink);
}
.doc-meta {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.5rem 1.5rem;
  margin-top: clamp(2rem, 5vw, 3rem);
  padding-top: 1.25rem;
  border-top: var(--rule);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dim);
}
.doc-meta dt {
  color: var(--faint);
}
.doc-meta a {
  color: var(--b);
}
.doc-meta a:active {
  color: var(--a);
}
.doc-status-note {
  margin-top: 1rem;
  font-size: 0.9rem;
  color: var(--faint);
}

/* measured facts */
.doc-facts {
  margin-top: clamp(3rem, 8vw, 5rem);
}
.doc-facts-h {
  padding-bottom: 1rem;
  border-bottom: var(--rule);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--a);
}
.doc-facts dl {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-top: 0;
}
.doc-facts dl > div {
  background: var(--bg);
  padding: clamp(1rem, 2.5vw, 1.6rem);
}
.doc-facts dt {
  font-size: clamp(1.4rem, 3.4vw, 2.2rem);
  letter-spacing: -0.03em;
  color: var(--ink);
}
.doc-facts dd {
  margin-top: 0.6rem;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--dim);
}
.doc-facts dd span {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.72rem;
  color: var(--faint);
}

/* body */
.doc-sec {
  margin-top: clamp(3rem, 8vw, 5.5rem);
}
.doc-sec h2 {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  margin-bottom: 1.25rem;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.25rem, 2.8vw, 1.9rem);
  letter-spacing: -0.02em;
}
.doc-sec h2 .mono {
  flex: none;
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  color: var(--a);
}
.doc-sec p {
  margin-bottom: 1.1rem;
  line-height: 1.7;
  color: var(--dim);
}
.doc-sec p b {
  font-weight: 500;
  color: var(--ink);
}
.doc-list {
  margin-top: 1.5rem;
  list-style: none;
  border-top: var(--rule);
}
.doc-list li {
  position: relative;
  padding: 0.9rem 0 0.9rem 1.5rem;
  border-bottom: var(--rule);
  font-size: 0.92rem;
  line-height: 1.65;
  color: var(--dim);
}
.doc-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 1.5rem;
  width: 6px;
  height: 6px;
  background: var(--b);
}
.doc-list li b {
  font-weight: 500;
  color: var(--ink);
}
.doc-limits .doc-list li::before {
  background: var(--a);
}

/* authoring notes — deliberately outside the published voice */
.doc-notes {
  margin-top: clamp(3rem, 8vw, 5rem);
  padding: clamp(1.25rem, 3vw, 2rem);
  border: 1px dashed var(--a);
}
.doc-notes-h {
  margin-bottom: 1rem;
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--a);
}
.doc-notes ul {
  list-style: none;
}
.doc-notes li {
  position: relative;
  margin-bottom: 0.5rem;
  padding-left: 1.1rem;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--dim);
}
.doc-notes li::before {
  content: '→';
  position: absolute;
  left: 0;
  color: var(--faint);
}

/* prev / next */
.doc-next {
  display: flex;
  justify-content: space-between;
  gap: 1.5rem;
  margin-top: clamp(3.5rem, 9vw, 6rem);
  padding-top: 1.5rem;
  border-top: var(--rule);
}
.doc-next-a {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-family: var(--display);
  font-size: 1.05rem;
  color: var(--ink);
  transition: color 0.2s var(--ease-out);
}
.doc-next-a .mono {
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}
.doc-next-b {
  align-items: flex-end;
  text-align: right;
}

@media (hover: hover) and (pointer: fine) {
  .doc-back:hover,
  .doc-next-a:hover,
  .doc-meta a:hover {
    color: var(--a);
  }
  .doc-next-a:hover .mono {
    color: var(--a);
  }
}

/* ── contact ─────────────────────────────────────────────────────────── */
.contact {
  position: relative;
  padding: clamp(4rem, 14vw, 12rem) var(--pad);
  border-top: var(--rule);
  overflow: clip;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: end;
}
.contact-copy {
  min-width: 0;
}
/* The portrait is cut out of its studio background, so there is no white
   rectangle fighting the page — he stands on the same ground as the type.
   The glow sits behind the image as a separate layer rather than on it. */
.contact-portrait {
  position: relative;
  margin: 0;
  align-self: end;
  justify-self: end;
}
.contact-portrait::before {
  content: "";
  position: absolute;
  inset: 16% -12% 8% -12%;
  background: radial-gradient(52% 50% at 50% 55%, rgba(77, 227, 192, 0.2), transparent 72%);
  filter: blur(26px);
}
.contact-portrait img {
  position: relative;
  display: block;
  width: clamp(240px, 26vw, 380px);
  height: auto;
}
.contact h2 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.2rem, 7.5vw, 7rem);
  line-height: 0.94;
  letter-spacing: -0.04em;
  max-width: 18ch;
  text-wrap: balance;
}
.contact h2 em {
  font-style: normal;
  color: var(--a);
}
.contact-mail {
  display: inline-block;
  margin-top: clamp(2rem, 5vw, 3.5rem);
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.3rem, 3.5vw, 2.6rem);
  letter-spacing: -0.02em;
  border-bottom: 2px solid var(--a);
  padding-bottom: 0.15em;
  transition: color 0.25s var(--ease-out), border-color 0.25s var(--ease-out);
}
.contact-mail:active {
  color: var(--a);
}
.contact-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 2rem;
  margin-top: clamp(2.5rem, 6vw, 4rem);
  padding-top: 1.5rem;
  border-top: var(--rule);
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}
.contact-links a {
  color: var(--dim);
  transition: color 0.2s var(--ease-out);
}
.contact-links a:active {
  color: var(--b);
}

.foot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  justify-content: space-between;
  padding: 1.5rem var(--pad);
  padding-bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px));
  border-top: var(--rule);
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

/* ── reveal primitives ───────────────────────────────────────────────── */
[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.55s var(--ease-out), transform 0.55s var(--ease-out);
  transition-delay: var(--d, 0ms);
}
[data-reveal].is-in {
  opacity: 1;
  transform: none;
}
/* without JS nothing may stay hidden */
html.no-js [data-reveal],
html.plain [data-reveal] {
  opacity: 1;
  transform: none;
  transition: none;
}
html.no-js .line > span,
html.plain .line > span {
  transform: none;
}

/* ── hover: real pointers only ───────────────────────────────────────────
   Touch fires a hover on tap and leaves it stuck, so every hover state
   lives behind a capability query. Touch still gets :active feedback. */
@media (hover: hover) and (pointer: fine) {
  .head-nav a:hover {
    color: var(--ink);
  }
  .btn:hover {
    border-color: var(--ink);
  }
  .btn-fill:hover {
    background-color: var(--a);
    border-color: var(--a);
  }
  .intro-skip:hover {
    color: var(--ink);
  }
  .panel-foot a.go:hover {
    color: var(--a);
  }
  .bench-cell:hover {
    background-color: var(--panel);
  }
  .bench-cell:hover::after {
    transform: scale(1.5);
    border-color: var(--a);
  }
  .contact-mail:hover {
    color: var(--a);
  }
  .contact-links a:hover {
    color: var(--b);
  }
}

/* ── command palette ───────────────────────────────────────────────────
   Built by main.js rather than markup: it does nothing without JS, so it
   does not exist without JS — no dead control, and nothing to keep in
   sync across the generated project pages. */
.pal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: clamp(3rem, 12vh, 9rem) 1rem 1rem;
}
.pal[hidden] {
  display: none;
}
.pal-scrim {
  position: absolute;
  inset: 0;
  background: rgba(8, 8, 10, 0.72);
  opacity: 0;
  transition: opacity 0.25s var(--ease-out);
}
.pal-panel {
  position: relative;
  width: min(38rem, 100%);
  background: var(--panel);
  border: 1px solid var(--line-2);
  overflow: hidden;
  opacity: 0;
  transform: translateY(-10px) scale(0.985);
  transition: transform 0.3s var(--ease-out), opacity 0.2s var(--ease-out);
}
.pal[data-open] .pal-scrim {
  opacity: 1;
}
.pal[data-open] .pal-panel {
  opacity: 1;
  transform: none;
}
.pal-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem 1.15rem;
  border-bottom: 1px solid var(--line);
}
.pal-prefix {
  color: var(--a);
  font-size: 0.9rem;
}
.pal-input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: none;
  border: 0;
  outline: none;
}
.pal-input::placeholder {
  color: var(--faint);
}
.pal-kbd {
  flex: none;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--line-2);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}
.pal-list {
  max-height: min(52vh, 24rem);
  overflow-y: auto;
  list-style: none;
  overscroll-behavior: contain;
}
.pal-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1.15rem;
  cursor: pointer;
}
.pal-item[data-active] {
  background: rgba(255, 82, 48, 0.1);
}
.pal-item[data-active] .pal-label {
  color: var(--a);
}
.pal-label {
  font-size: 0.92rem;
  color: var(--ink);
}
.pal-hint {
  flex: none;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--faint);
}
.pal-empty {
  padding: 1.2rem 1.15rem;
  font-size: 0.72rem;
  color: var(--faint);
}
.pal-foot {
  padding: 0.7rem 1.15rem;
  border-top: 1px solid var(--line);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}
/* the header trigger; margin-left:auto so it still sits right when the nav
   is hidden on narrow screens */
.pal-key {
  flex: none;
  margin-left: auto;
  padding: 0.45rem 0.75rem;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  color: var(--dim);
  transition: color 0.2s var(--ease-out), border-color 0.2s var(--ease-out);
}
/* the ring cursor is a pointer affordance; over a text field it is only noise */
html.pal-open .cursor {
  opacity: 0;
}

.toast {
  position: fixed;
  left: 50%;
  bottom: clamp(1rem, 4vw, 2rem);
  z-index: 110;
  padding: 0.6rem 1rem;
  border-radius: 999px;
  background: var(--ink);
  color: #0a0a0c;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 1rem);
  transition: opacity 0.25s var(--ease-out), transform 0.25s var(--ease-out);
}
.toast[data-on] {
  opacity: 1;
  transform: translate(-50%, 0);
}

@media (hover: hover) and (pointer: fine) {
  .pal-key:hover {
    color: var(--ink);
    border-color: var(--ink);
  }
}
@media (prefers-reduced-motion: reduce) {
  .head-logo,
  .head-logo .arms {
    transition: none;
  }
  .head-logo .ink {
    animation: none;
    stroke-dashoffset: 0;
  }
  .head-logo .tile {
    animation: none;
    filter: none;
  }
  .pal-scrim,
  .pal-panel,
  .pal-key,
  .toast {
    transition: none;
  }
}

/* ── responsive ──────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .head-nav {
    display: none;
  }
  .contact {
    grid-template-columns: minmax(0, 1fr);
  }
  .contact-portrait {
    order: -1;
    justify-self: start;
  }
  .contact-portrait img {
    width: clamp(170px, 44vw, 230px);
  }
  .bench {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .hero-row {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }
  .sec-note {
    text-align: left;
  }
  .sec-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
  }
  /* collection: the rail is scrollable by default (see .track); mobile just
     sets a wider panel so one fills the view nicely */
  .panel {
    width: 86vw;
  }
  .panel:nth-child(3n) {
    width: 86vw;
  }
  .coll-progress {
    display: none;
  }
  /* run: the label, the bars and the speed-up stack into one column; the
     graph keeps its value column so the numbers stay aligned with the bars */
  .run-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem;
  }
  .run-x {
    text-align: left;
  }
}
@media (max-width: 560px) {
  .bench {
    grid-template-columns: 1fr;
  }
  .scroll-cue {
    display: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .marquee-track,
  .scroll-cue i {
    animation: none !important;
  }
  [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .cursor i,
  .btn,
  .bar,
  .head {
    transition: none;
  }
}
