/* ==========================================================================
   Eaon Labs — labs.eaon.dev

   Design system lifted from openclaw.ai: its palette, spacing and type scales,
   and its component patterns. The two things that actually make that site read
   as "techy" and that a normal card layout does not:

     1. Zero border-radius, and hairline grids instead of floating cards. The
        grid container's background IS the border colour and the gap is 1px, so
        the gaps between cells render as hairlines. Cells sit on --bg and
        change to --surface on hover. No shadows, no rounding, no gaps.
     2. Monospace, uppercase, letter-spaced, coral for every piece of UI
        chrome — nav, section titles, card titles, buttons, labels. Sans is
        reserved for headlines and body prose only.

   Content and copy are Eaon's own.
   ========================================================================== */

/* ------------------------------- tokens ---------------------------------- */
:root {
  /* Ink / paper ramp */
  --ink-950: #101012;
  --ink-900: #19191c;
  --ink-850: #202024;
  --ink-500: #9a9aa2;
  --ink-300: #bcbcc4;
  --ink-50:  #ededed;

  --bg:        var(--ink-950);
  --surface:   var(--ink-900);
  --elevated:  var(--ink-850);
  --recessed:  color-mix(in oklch, var(--bg) 86%, oklch(0% 0 0));

  --text:        var(--ink-50);
  --text-2:      var(--ink-300);
  --text-muted:  var(--ink-500);
  --text-on-accent: #17171a;

  --border:        #9a9aa22e;
  --border-strong: color-mix(in oklch, var(--text) 28%, transparent);
  --border-accent: #f5654a66;

  /* Two accents. Coral leads; sea green marks anything live or verified. */
  --coral:      #f5654a;
  --coral-mid:  #e05540;
  --coral-deep: #b23a28;
  --sea:        #4fc8ae;
  --sea-mid:    #2fa48d;

  --surface-interactive: #ededed14;
  --surface-accent-soft: #f5654a24;

  --font-body: "Switzer", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-display: var(--font-body);
  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  --font-serif: "Instrument Serif", Georgia, "Times New Roman", serif;

  --fs-xs:   0.75rem;
  --fs-sm:   0.8125rem;
  --fs-base: 0.875rem;
  --fs-md:   0.9375rem;
  --fs-lg:   1.0625rem;
  --fs-xl:   1.25rem;
  --fs-2xl:  1.5rem;
  --fs-3xl:  2rem;

  --s1: 0.25rem; --s2: 0.5rem;  --s3: 0.75rem; --s4: 1rem;
  --s5: 1.5rem;  --s6: 2rem;    --s7: 3rem;    --s8: 4rem;

  --content-max: 75rem;
  --content-narrow: 48rem;
  --rail-pad: 32px;

  /* Blueprint layer. Deliberately quiet — it sits under the existing palette
     rather than competing with it. */
  --sketch-line:   rgba(237, 237, 237, 0.42);
  --sketch-line-2: rgba(237, 237, 237, 0.11);

  /* Three pressures, because a hand does not bear down evenly. */
  --sk-c:  rgba(237, 237, 237, 0.44);
  --sk-c2: rgba(237, 237, 237, 0.29);
  --sk-c3: rgba(237, 237, 237, 0.57);

  /* Tapered strokes. A pencil touches down, bears weight, then lifts; a stroke
     with hard-cut ends reads as a printed rule no matter how much it is
     wobbled. Each variant fades over a different distance, and the long-rule
     versions also lighten toward both ends so the pressure varies along the
     length rather than only at the tips. */
  --sk-h1: linear-gradient(90deg, transparent 0%, var(--sk-c)  14%, var(--sk-c)  88%, transparent 100%);
  --sk-h2: linear-gradient(90deg, transparent 0%, var(--sk-c2) 21%, var(--sk-c2) 79%, transparent 100%);
  --sk-h3: linear-gradient(90deg, transparent 0%, var(--sk-c3) 10%, var(--sk-c3) 93%, transparent 100%);
  --sk-v1: linear-gradient(180deg, transparent 0%, var(--sk-c)  16%, var(--sk-c)  86%, transparent 100%);
  --sk-v2: linear-gradient(180deg, transparent 0%, var(--sk-c2) 23%, var(--sk-c2) 75%, transparent 100%);
  --sk-v3: linear-gradient(180deg, transparent 0%, var(--sk-c3) 11%, var(--sk-c3) 91%, transparent 100%);
  --sk-hr: linear-gradient(90deg, transparent 0%, var(--sk-c2) 5%, var(--sk-c) 23%,
                           var(--sk-c) 71%, var(--sk-c2) 93%, transparent 100%);
  --sk-vr: linear-gradient(180deg, transparent 0%, var(--sk-c2) 7%, var(--sk-c) 26%,
                           var(--sk-c) 74%, var(--sk-c2) 92%, transparent 100%);
  --sketch-faint: rgba(237, 237, 237, 0.05);

  --r-sm: 4px;
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --dur-fast: 0.16s;
  --dur-ui: 0.2s;
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-md);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.02em; }
p { margin: 0; }
a { color: inherit; }

:where(a, button):focus-visible {
  outline: 2px solid var(--coral);
  outline-offset: 2px;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* Every horizontally-constrained block uses the same rail. */
.rail {
  max-width: var(--content-max);
  margin: 0 auto;
  padding-inline: var(--rail-pad);
}

/* -------------------------- shared chrome bits --------------------------- */

/* Mono / uppercase / coral. Used for section titles, card titles and labels —
   this single treatment does most of the work of the aesthetic. */
.label {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--coral);
}

.section-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s4);
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.section-note {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  letter-spacing: 0.04em;
}

/* Sections are divided by hairlines, not by whitespace alone. */
.band { padding-block: var(--s7); border-top: 1px solid var(--border); }

/* =========================== blueprint layer ============================= */
/* A CAD-drawing overlay: construction strokes that overshoot each corner and
   cross at the joint, plus an optional survey grid behind a band. Entirely
   additive — every piece is an absolutely-positioned, pointer-events: none
   pseudo-element, so nothing here can move or resize real content. */

.sketch-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Two passes per element, the way a line actually gets drawn twice by hand:
     ::after  - the main construction strokes at all four corners
     ::before - an overdrawn second stroke plus loose mid-edge ticks
   The passes carry different seeds, different offsets and opposite sub-degree
   rotations, so they never sit parallel to each other or to the box. That
   near-miss is what stops the whole thing looking plotted. */
.sketch { position: relative; }

.sketch::before,
.sketch::after {
  content: "";
  position: absolute;
  pointer-events: none;
  background-repeat: no-repeat;
  --sk-w: 1px;
}

/* ---- pass 1: corner construction strokes ---- */
.sketch::after {
  /* Negative inset is what makes the strokes overshoot the box. 14px fits
     inside the rail's 20px minimum padding, so they never cause overflow. */
  inset: calc(var(--sk-out) * -1);
  --sk-out: 14px;
  /* Eight strokes, eight lengths. A drafted overshoot is never twice the same
     distance, and equal lengths were the second-biggest mechanical tell after
     the shared filter seed. */
  --l1: 40px; --l2: 27px; --l3: 30px; --l4: 43px;
  --l5: 46px; --l6: 25px; --l7: 33px; --l8: 37px;
  /* No two of the eight are the same stroke: taper distance, pressure and
     weight all rotate through. */
  background-image:
    var(--sk-h1), var(--sk-v3),
    var(--sk-h2), var(--sk-v1),
    var(--sk-h3), var(--sk-v2),
    var(--sk-h1), var(--sk-v3);
  background-size:
    var(--l1) 1px,   1px var(--l2),
    var(--l3) 1.5px, 1.5px var(--l4),
    var(--l5) 1px,   1px var(--l6),
    var(--l7) 1.5px, 1px var(--l8);
  /* Each corner gets one horizontal and one vertical stroke, sitting on the real
     border line and running past it in both directions.
     Explicit 4-value syntax throughout: the 3-value shorthand parses, but which
     axis the lone offset binds to is easy to get wrong when it is a variable. */
  background-position:
    left 0px top var(--sk-out),      left var(--sk-out) top 0px,
    right 0px top var(--sk-out),     right var(--sk-out) top 0px,
    left 0px bottom var(--sk-out),   left var(--sk-out) bottom 0px,
    right 0px bottom var(--sk-out),  right var(--sk-out) bottom 0px;
  transform: rotate(0.11deg);
  /* Displaces the strokes along a noise field so they read as drawn rather than
     plotted. Only ever applied to these decorative layers - running it over text
     would wreck legibility. */
  filter: url(#sk-1);
}

/* ---- pass 2: overdrawn stroke + loose mid-edge ticks ---- */
.sketch::before {
  inset: calc(var(--sk-out2) * -1);
  --sk-out2: 11px;
  /* A tick sized to exactly twice the inset and pinned to the pseudo's own edge
     straddles the real border line, centred on it, with no extra maths. */
  --tick: calc(var(--sk-out2) * 2);
  opacity: 0.6;
  background-image:
    var(--sk-h3), var(--sk-v1),
    var(--sk-v2), var(--sk-v1),
    var(--sk-h2), var(--sk-h1);
  background-size:
    34px 1px,          /* overdrawn top-left horizontal */
    1px 29px,          /* overdrawn bottom-right vertical */
    1px var(--tick),   /* tick, top edge */
    1px var(--tick),   /* tick, bottom edge */
    var(--tick) 1px,   /* tick, left edge */
    var(--tick) 1px;   /* tick, right edge */
  /* Off-centre percentages on purpose: ticks at 50% read as measured, not drawn. */
  background-position:
    left 0px top var(--sk-out2),
    right var(--sk-out2) bottom 0px,
    left 34% top 0px,
    left 61% bottom 0px,
    left 0px top 43%,
    right 0px top 68%;
  transform: rotate(-0.24deg) translate(1px, -1px);
  filter: url(#sk-2);
}

/* Longer, heavier marks where the element is large enough to carry them. */
.sketch--bold::after {
  --sk-out: 18px;
  --l1: 54px; --l2: 34px; --l3: 39px; --l4: 58px;
  --l5: 61px; --l6: 31px; --l7: 44px; --l8: 49px;
}
.sketch--bold::before { --sk-out2: 14px; }

/* Swapped seeds and mirrored rotations, so two adjacent blocks never waver
   alike. Goes on the CTA card and on alternating project cells. */
.sketch--alt::after {
  filter: url(#sk-3);
  transform: rotate(-0.15deg);
  --l1: 31px; --l2: 41px; --l3: 45px; --l4: 26px;
  --l5: 29px; --l6: 43px; --l7: 25px; --l8: 39px;
}
.sketch--alt::before {
  filter: url(#sk-4);
  transform: rotate(0.21deg) translate(-1px, 1px);
}

/* A third seed pair. Three project cells sit next to each other now, and two of
   them sharing a wobble is exactly the repetition this system exists to avoid. */
.sketch--alt2::after {
  filter: url(#sk-5);
  transform: rotate(0.19deg);
  --l1: 36px; --l2: 24px; --l3: 27px; --l4: 47px;
  --l5: 33px; --l6: 39px; --l7: 29px; --l8: 44px;
}
.sketch--alt2::before {
  filter: url(#sk-6);
  transform: rotate(-0.17deg) translate(-1px, -1px);
}

/* Quieter and tighter, for the project cells sitting inside an already-marked
   grid. Their outer corners coincide with the grid's, and two sets of strokes
   landing on the same corner at different seeds is exactly the overdrawn look. */
.sketch--light::after {
  --sk-out: 10px;
  --l1: 26px; --l2: 19px; --l3: 22px; --l4: 28px;
  --l5: 30px; --l6: 18px; --l7: 24px; --l8: 21px;
  opacity: 0.72;
}
.sketch--light::before { --sk-out2: 8px; opacity: 0.42; }

/* Pulled in on phones: the rail's padding drops to 20px there, and a full-size
   overshoot would leave the marks all but touching the viewport edge. */
@media (max-width: 720px) {
  .sketch::after {
    --sk-out: 10px;
    --l1: 26px; --l2: 18px; --l3: 21px; --l4: 28px;
    --l5: 30px; --l6: 17px; --l7: 23px; --l8: 25px;
  }
  .sketch::before { --sk-out2: 8px; }
  .sketch--bold::after {
    --sk-out: 12px;
    --l1: 32px; --l2: 21px; --l3: 25px; --l4: 34px;
    --l5: 36px; --l6: 20px; --l7: 28px; --l8: 30px;
  }
  .sketch--bold::before { --sk-out2: 9px; }
  .sketch--light::after {
    --sk-out: 7px;
    --l1: 18px; --l2: 13px; --l3: 15px; --l4: 20px;
    --l5: 21px; --l6: 12px; --l7: 17px; --l8: 15px;
  }
  .sketch--light::before { --sk-out2: 6px; }
}

/* Survey grid behind a band, fading out downward like the reference's floor. */
.blueprint { position: relative; }
.blueprint::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Two spacings rather than one flat lattice: a real survey grid has heavier
     primary lines with lighter subdivisions between them. */
  background-image:
    repeating-linear-gradient(90deg, var(--sketch-line-2) 0 1px, transparent 1px 132px),
    repeating-linear-gradient(0deg,  var(--sketch-line-2) 0 1px, transparent 1px 132px),
    repeating-linear-gradient(90deg, var(--sketch-faint) 0 1px, transparent 1px 33px),
    repeating-linear-gradient(0deg,  var(--sketch-faint) 0 1px, transparent 1px 33px);
  mask-image: linear-gradient(#000 0%, rgba(0, 0, 0, 0.5) 55%, transparent 100%);
  -webkit-mask-image: linear-gradient(#000 0%, rgba(0, 0, 0, 0.5) 55%, transparent 100%);
  /* Sags the whole lattice, so the grid is drawn by the same hand as the marks
     instead of being the one perfectly straight thing on the page. */
  filter: url(#sk-grid);
}

/* ---- drawn strokes (freehand replacements for 1px hairlines) ----
   The box is deliberately far taller than the stroke it carries. An
   feDisplacementMap can only move pixels around *within the filter region*, and
   on a 1px-tall element there is nowhere vertical to move to — the wobble gets
   clipped away and the line comes out ruler-straight. So the stroke is painted
   as a background gradient inside an 11px box, leaving room to displace into.
   (The first version of the section rule was a 1px-tall element and was barely
   wavering at all for exactly this reason.) */
/* Freehand rule under each section title. The filter only ever touches this
   pseudo-element, never the heading text itself. */
.label { position: relative; }
.label::after {
  content: "";
  position: absolute;
  left: -3px;
  right: -9px;
  bottom: -12px;
  height: 11px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image: var(--sk-hr);
  background-size: 100% 1px;
  background-position: left 0px top 5px;
  opacity: 0.5;
  filter: url(#sk-3);
}

/* --------------------------------- topbar -------------------------------- */
.site-topbar {
  position: sticky;
  top: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  min-height: 54px;
  padding: 0 var(--rail-pad);
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: saturate(140%) blur(16px);
}
@supports not (backdrop-filter: blur(1px)) {
  .site-topbar { background: color-mix(in srgb, var(--bg) 94%, transparent); }
}

.site-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-decoration: none;
}
.site-brand svg { width: 22px; height: 22px; display: block; }
.site-brand em {
  font-style: normal;
  font-weight: 500;
  color: var(--text-muted);
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 26px;
  white-space: nowrap;
}
.site-nav a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.15s;
}
.site-nav a:hover { color: var(--text); }
.site-nav svg { width: 13px; height: 13px; flex: none; }

/* ------------------------------ burger menu ------------------------------ */
.site-menu { display: none; }
.site-menu-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--s2) var(--s1);
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  list-style: none;
  transition: color 0.15s;
}
.site-menu-toggle::-webkit-details-marker { display: none; }
.site-menu-toggle:hover,
.site-menu[open] .site-menu-toggle { color: var(--text); }

.site-burger { display: flex; flex-direction: column; gap: var(--s1); }
.site-burger span {
  display: block;
  width: 18px;
  height: 1.6px;
  background: currentColor;
  transition: transform 0.25s var(--ease-out), opacity var(--dur-ui) var(--ease-out);
}
.site-menu[open] .site-burger span:first-child { transform: translateY(5.6px) rotate(45deg); }
.site-menu[open] .site-burger span:nth-child(2) { opacity: 0; }
.site-menu[open] .site-burger span:nth-child(3) { transform: translateY(-5.6px) rotate(-45deg); }

.site-menu-panel {
  position: fixed;
  top: 52px;
  left: 12px;
  right: 12px;
  z-index: 92;
  display: grid;
  gap: 2px;
  padding: 8px;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg) 96%, transparent);
  backdrop-filter: saturate(140%) blur(18px);
}
.site-menu-panel a {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 42px;
  padding: 0 var(--s3);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--text-2);
  text-decoration: none;
  transition: background var(--dur-ui) var(--ease-out), color 0.15s;
}
.site-menu-panel a:hover { background: var(--surface-interactive); color: var(--text); }

@media (max-width: 720px) {
  :root { --rail-pad: 20px; }
  .site-topbar { min-height: 52px; }
  .site-nav-desktop { display: none; }
  .site-menu { display: block; margin-left: auto; }
}

/* ---------------------------------- hero -------------------------------- */
.hero { position: relative; overflow: hidden; }

/* The ASCII field lives in its own band across the top, and the copy is pulled
   up to overlap its faded tail. Keeping the field out of the copy's box —
   rather than behind it — is what stops the two ever fighting. */
.hero-art {
  height: min(36vh, 340px);
  pointer-events: none;
  user-select: none;
  /* Fades out well before the band ends, so the copy overlaps only the tail.
     The horizontal pass dissolves the outer "wings" — left un-faded they run
     to the viewport edge at constant density and stop reading as arcs. */
  --art-mask:
    linear-gradient(transparent 0%, #000 30% 58%, transparent 86%),
    linear-gradient(90deg, transparent 0%, #000 16%, #000 84%, transparent 100%);
  mask-image: var(--art-mask);
  mask-composite: intersect;
  -webkit-mask-image: var(--art-mask);
  -webkit-mask-composite: source-in;
}
.hero-art canvas { display: block; width: 100%; height: 100%; }

.hero-content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-top: -76px;
  padding-bottom: 68px;
}

.hero-eyebrow {
  position: relative;
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--coral);
  margin-bottom: 18px;
}
/* The eyebrow is small, coral, and sits closest to the densest part of the
   field — the one place the art genuinely wins against the text. This gives it
   its own patch of background regardless of viewport height. */
.hero-eyebrow::before {
  content: "";
  position: absolute;
  inset: -10px -26px;
  z-index: -1;
  background: radial-gradient(56% 130% at 50% 50%, color-mix(in srgb, var(--bg) 94%, transparent) 42%, transparent 76%);
}

.hero-title {
  max-width: 20ch;
  font-size: clamp(2rem, 5.4vw, 3.5rem);
  line-height: 1.05;
  font-weight: 600;
  letter-spacing: -0.025em;
  text-wrap: balance;
}
.hero-title em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: 1.07em;
  letter-spacing: -0.01em;
  color: var(--coral);
}

.hero-lede {
  max-width: 56ch;
  margin-top: 18px;
  font-size: var(--fs-lg);
  line-height: 1.6;
  color: var(--text-muted);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--s3);
  margin-top: 36px;
}

/* Buttons: mono, square, 44px tall. No radius, no lift, no shadow. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: 44px;
  padding: 0 var(--s5);
  border: 1px solid var(--coral);
  background: var(--coral);
  color: var(--text-on-accent);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.btn:hover {
  background: color-mix(in srgb, var(--coral) 82%, #fff);
  border-color: color-mix(in srgb, var(--coral) 82%, #fff);
}
.btn--ghost {
  background: var(--bg);
  border-color: var(--border);
  color: var(--text);
}
.btn--ghost:hover {
  background: var(--surface);
  border-color: color-mix(in srgb, var(--text-muted) 55%, transparent);
}
.btn svg { width: 14px; height: 14px; flex: none; }

.hero-meta {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  margin-top: 26px;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.hero-meta b { color: var(--text-2); font-weight: 600; }

.dot {
  flex: none;
  width: 6px; height: 6px;
  background: var(--sea);
  border-radius: 50%;
  box-shadow: 0 0 8px color-mix(in srgb, var(--sea) 60%, transparent);
}

@media (max-width: 720px) {
  .hero-art { height: min(26vh, 220px); }
  .hero-content { margin-top: -74px; padding-bottom: 52px; }
  .hero-actions { flex-direction: column; align-items: stretch; width: 100%; max-width: 320px; }
}

/* ------------------------------- quickstart ------------------------------ */
/* A real terminal frame — window chrome, mono content, coral prompts. */
/* No overflow: hidden here — .code-content clips its own horizontal scroll, and
   hiding overflow on the block would also clip the blueprint corner marks. */
.code-block {
  border: 1px solid var(--border);
  background: var(--surface);
}
.code-header {
  display: flex;
  align-items: stretch;
  gap: 20px;
  min-height: 46px;
  padding: 0 20px;
  border-bottom: 1px solid var(--border);
}
.code-dots { display: flex; align-items: center; gap: 8px; }
.code-dot { width: 11px; height: 11px; border-radius: 50%; background: var(--text-muted); }
.code-dot:nth-child(1) { background: #ff5f57; }
.code-dot:nth-child(2) { background: #febc2e; }
.code-dot:nth-child(3) { background: #28c840; }
.code-title {
  margin-left: auto;
  align-self: center;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.code-content {
  padding: 18px 20px;
  font-family: var(--font-mono);
  font-size: var(--fs-base);
  line-height: 1.75;
  overflow-x: auto;
}
.code-line { display: flex; align-items: baseline; gap: var(--s2); padding: 2px 0; white-space: pre; }
.code-line.comment { color: var(--text-muted); font-style: italic; }
.code-prompt { color: var(--coral); user-select: none; flex: none; }
.code-out { color: var(--text-muted); }
.code-ok { color: var(--sea); }

.copy-btn {
  margin-left: auto;
  flex: none;
  padding: 2px 8px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-ui) var(--ease-out), color 0.15s, border-color 0.15s;
}
.code-line:hover .copy-btn,
.copy-btn:focus-visible { opacity: 1; }
.copy-btn:hover { color: var(--text); border-color: var(--border-strong); }
.copy-btn[data-copied="1"] { opacity: 1; color: var(--sea); border-color: color-mix(in srgb, var(--sea) 50%, transparent); }

.quickstart-note {
  margin-top: var(--s4);
  text-align: center;
  font-size: var(--fs-base);
  color: var(--text-muted);
}
.quickstart-note a {
  color: var(--text-2);
  text-decoration: none;
  border-bottom: 1px solid var(--border-strong);
  transition: color 0.15s, border-color 0.15s;
}
.quickstart-note a:hover { color: var(--text); border-color: var(--coral); }
/* Lead-in paragraph above a terminal, rather than a caption below one. */
.quickstart-note--lead {
  margin-top: 0;
  margin-bottom: var(--s4);
  max-width: 72ch;
  text-align: left;
}
.quickstart-note em { font-style: italic; color: var(--text-2); }
.quickstart-note strong { color: var(--text-2); font-weight: 600; }

/* A 64-character hash cannot be nowrap: it sets the paragraph's minimum width
   and pushed the document 179px past a 320px viewport. */
.quickstart-note code.hash {
  white-space: normal;
  overflow-wrap: anywhere;
}

.quickstart-note code {
  padding: 0.08rem 0.32rem;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface) 62%, transparent);
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: 0.86em;
  white-space: nowrap;
}

/* ------------------------------ hairline grid ---------------------------- */
/* The container's background is the border colour and the gap is 1px, so the
   gaps themselves render as hairlines. Cells paint --bg over the top. */
.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  border: 1px solid var(--border);
  background: var(--border);
}
@media (min-width: 700px)  { .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Lead project spans the row. With three cards in a two-column hairline grid,
   the alternative is one empty cell inside the border, which reads as a card
   that failed to load. */
@media (min-width: 700px)  { .cell--wide { grid-column: 1 / -1; } }

.cell {
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* A grid item's automatic minimum size is its content-based minimum, which the
     nowrap <code> in .cell-foot pushes past the track width on narrow screens —
     that pushed the whole document 29px wider than a 320px viewport. */
  min-width: 0;
  padding: 28px var(--s5);
  background: var(--bg);
  color: inherit;
  text-decoration: none;
  transition: background var(--dur-ui) var(--ease-out);
}
/* `display: flex` above outranks the UA stylesheet's `[hidden]` rule. */
.cell[hidden] { display: none; }
.cell:hover { background: var(--surface); }

.cell-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
}
.cell-idx {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}
.cell-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.tag {
  padding: 1px 6px;
  border: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.tag--live { color: var(--sea); border-color: color-mix(in srgb, var(--sea) 40%, transparent); }

.cell-title {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--coral);
}
.cell-desc {
  flex: 1;
  font-size: var(--fs-base);
  line-height: 1.7;
  color: var(--text-muted);
}
.cell-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  margin-top: 4px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  min-width: 0;
}
.cell-foot code {
  /* overflow: hidden lets it shrink as a flex item, but the flex *container's*
     min-content still sums its children's content minimums — so this needs to be
     explicit for .cell-foot to be shrinkable at all. Truncates instead. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-2);
}
.cell-link { flex: none; text-decoration: none; transition: color 0.15s; }

/* On a phone the metadata line has nowhere to go, and ellipsising it to
   "apple silicon · unsig…" reads as broken rather than deliberate. Let it wrap
   onto a second line and keep the whole string. */
@media (max-width: 520px) {
  .cell-foot { flex-wrap: wrap; gap: 4px 12px; }
  .cell-foot code {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }
}
.cell:hover .cell-link { color: var(--coral); }

.grid-note {
  margin-top: 16px;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* --------------------------------- filters ------------------------------- */
.filters { display: flex; border: 1px solid var(--border); }
.filter {
  appearance: none;
  min-height: 32px;
  padding: 0 14px;
  border: 0;
  border-left: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background var(--dur-ui) var(--ease-out), color 0.15s;
}
.filter:first-child { border-left: 0; }
.filter:hover { color: var(--text-2); background: var(--surface-interactive); }
.filter.is-active { color: var(--coral); background: var(--surface-accent-soft); }

/* --------------------------------- prose -------------------------------- */
.prose { max-width: var(--content-narrow); display: flex; flex-direction: column; gap: 14px; }
.prose p { font-size: var(--fs-md); color: var(--text-muted); line-height: 1.75; }
.prose a {
  color: var(--text-2);
  text-decoration: none;
  border-bottom: 1px solid var(--border-strong);
  transition: color 0.15s, border-color 0.15s;
}
.prose a:hover { color: var(--text); border-color: var(--coral); }

/* -------------------------------- CTA band ------------------------------- */
.cta-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  padding: var(--s6);
  border: 1px solid var(--border);
  /* Drafting hatch, at the same weight as the survey grid. */
  background-color: var(--surface);
  background-image: repeating-linear-gradient(45deg, var(--sketch-faint) 0 1px, transparent 1px 9px);
}
.cta-card h2 { font-size: var(--fs-2xl); letter-spacing: -0.02em; }
.cta-card p { margin-top: 6px; font-size: var(--fs-base); color: var(--text-muted); }

/* --------------------------------- footer -------------------------------- */
/* The footer is treated as a drawing sheet's title block: every hairline in it
   is replaced by a drawn stroke, and the whole content area gets construction
   marks. `border-top-color: transparent` keeps the 1px of layout space the
   border was occupying while handing the visible line to the pseudo-element. */
.footer {
  position: relative;
  margin-top: var(--s7);
  border-top: 1px solid var(--border);
  border-top-color: transparent;
  padding-block: var(--s6) var(--s7);
}
/* Full-bleed, so this one gets no overshoot — extending past the viewport edge
   would widen the document. */
.footer::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -6px;
  height: 11px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image: var(--sk-hr);
  background-size: 100% 1px;
  background-position: left 0px top 5px;
  opacity: 0.45;
  filter: url(#sk-5);
}
.footer-inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s6);
  justify-content: space-between;
}
.footer-brand { display: flex; flex-direction: column; gap: 8px; max-width: 34ch; }
.footer-brand p { font-size: var(--fs-sm); color: var(--text-muted); }
.footer-groups { display: flex; flex-wrap: wrap; gap: var(--s7); }
.footer-group { position: relative; display: flex; flex-direction: column; gap: 8px; }

/* Drawn column rule in the gutter between link groups. */
.footer-group + .footer-group::before {
  content: "";
  position: absolute;
  left: calc(var(--s7) / -2 - 5px);
  top: -2px;
  bottom: 10px;
  width: 11px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image: var(--sk-vr);
  background-size: 1px 100%;
  background-position: left 5px top 0px;
  opacity: 0.28;
  filter: url(#sk-2);
}

.footer-group h3 {
  position: relative;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 2px;
}
/* Freehand underline per column heading. */
.footer-group h3::after {
  content: "";
  position: absolute;
  left: -2px;
  right: -7px;
  bottom: -9px;
  height: 11px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image: var(--sk-hr);
  background-size: 100% 1px;
  background-position: left 0px top 5px;
  opacity: 0.4;
}
/* A different seed per column, so the three underlines never match. */
.footer-group:nth-child(1) h3::after { filter: url(#sk-1); }
.footer-group:nth-child(2) h3::after { filter: url(#sk-4); }
.footer-group:nth-child(3) h3::after { filter: url(#sk-6); }
.footer-group a {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-2);
  text-decoration: none;
  transition: color 0.15s;
}
.footer-group a:hover { color: var(--coral); }
.footer-legal {
  position: relative;
  margin-top: var(--s6);
  padding-top: var(--s4);
  border-top: 1px solid var(--border);
  border-top-color: transparent;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s4);
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

/* Inside the rail, so this rule can overshoot. */
.footer-legal::before {
  content: "";
  position: absolute;
  left: -11px;
  right: -14px;
  top: -6px;
  height: 11px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image: var(--sk-hr);
  background-size: 100% 1px;
  background-position: left 0px top 5px;
  opacity: 0.42;
  filter: url(#sk-6);
}

@media (max-width: 720px) {
  /* The link groups wrap on phones, which would leave column rules floating in
     the middle of nothing. */
  .footer-group + .footer-group::before { display: none; }
  /* And the brand block's text fills its full 34ch at this width, so marks on
     its edges landed on top of the copy instead of in open space. The footer's
     two drawn rules and the column underlines carry the look here on their own. */
  .footer-brand.sketch::before,
  .footer-brand.sketch::after { display: none; }
}

/* ------------------------------- entrances ------------------------------- */
/* Keyframes, not JS-toggled transitions: a transition needs a frame between
   states and rAF does not fire in a background tab, which would leave the
   hero blank. */
.js .hero-content > * { animation: rise 0.7s var(--ease-out) both; }
.js .hero-eyebrow { animation-delay: 0.04s; }
.js .hero-title   { animation-delay: 0.1s; }
.js .hero-lede    { animation-delay: 0.17s; }
.js .hero-actions { animation-delay: 0.24s; }
.js .hero-meta    { animation-delay: 0.31s; }
@keyframes rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

.js .cell { opacity: 0; }
.js .cell.is-in {
  opacity: 1;
  transition: opacity 0.5s var(--ease-out) calc(var(--i, 0) * 55ms);
}

@media (prefers-reduced-motion: reduce) {
  .js .hero-content > * { animation: none; }
  .js .cell { opacity: 1; }
}
