/* ==================================================================
   RXDSEC — BONE / INK
   ------------------------------------------------------------------
   A deliberate break from the product's own dark chrome. This is
   editorial, not terminal: warm bone paper, ink black, hairline rules,
   square corners, oversized display type set tight, and monospace only
   where a machine is speaking. Colour is used twice — ultramarine for
   anything you can act on, acid for anything that wants your eye — and
   nowhere else.

   Both themes are first-class. Dark is not a filter over light; it is
   the same system with the paper and the ink swapped, and the shader
   re-reads its palette from --gl-* when the theme changes.
   ================================================================== */

/* ================= tokens ================= */

:root {
  /* paper */
  --paper:    #e9e7e1;
  --paper-2:  #f2f1ec;
  --paper-3:  #dcd9d1;
  --panel:    #f6f5f1;

  /* ink */
  --ink:      #0b0b0c;
  --ink-2:    #5a5751;
  --ink-3:    #8d8981;

  /* rules */
  --rule:      rgba(11, 11, 12, 0.16);
  --rule-soft: rgba(11, 11, 12, 0.09);
  --rule-hard: rgba(11, 11, 12, 0.42);

  /* Two colours only. Retuned from ultramarine to crimson so the interface
     agrees with the hero artwork instead of fighting it — a violet CTA on a
     red-black photograph reads as a mistake. Acid stays as the single
     non-red signal, used sparingly. */
  --blue:  #d81f2b;
  --blue-2:#f0343f;
  --acid:  #ccff2f;

  --sel: rgba(216, 31, 43, 0.18);

  /* the shader reads these */
  --gl-bg: #e9e7e1;
  --gl-a:  #d81f2b;
  --gl-b:  #ffd9c0;
  /* aura: crimson wisps around a hot white core */
  --gl-edge: #e02230;
  --gl-core: #ffd2c2;

  /* type */
  --sans: "Space Grotesk", "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
  --mono: "JetBrains Mono", "SF Mono", ui-monospace, Consolas, monospace;

  /* geometry — square by default, this is not a rounded design */
  --r: 2px;
  --nav-h: 58px;
  --gutter: clamp(18px, 4.4vw, 56px);
  --maxw: 1440px;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-io: cubic-bezier(0.65, 0, 0.35, 1);
}

:root[data-theme="dark"] {
  /* Graded toward the artwork: the neutrals carry a little of its red so the
     page reads as one scene instead of a warm photograph pasted onto cold
     grey. Barely a nudge — enough to feel, not enough to look tinted. */
  --paper:    #0a0708;
  --paper-2:  #140f11;
  --paper-3:  #1e1719;
  --panel:    #110c0e;

  --ink:      #f0eae8;
  --ink-2:    #9a908d;
  --ink-3:    #6d6461;

  --rule:      rgba(240, 234, 232, 0.16);
  --rule-soft: rgba(240, 234, 232, 0.08);
  --rule-hard: rgba(240, 234, 232, 0.40);

  --blue:  #ff3b47;
  --blue-2:#ff6f78;
  --acid:  #ccff2f;

  --sel: rgba(255, 59, 71, 0.26);

  --gl-bg: #0a0708;
  --gl-a:  #ff2f3d;
  --gl-b:  #ffd2c2;
  --gl-edge: #ff2f3d;
  --gl-core: #ffd8cc;
}

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 24px);
  background: var(--paper);
}

body {
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  transition: background 0.5s var(--ease), color 0.5s var(--ease);
}

html.nav-open, html.nav-open body { overflow: hidden; }

::selection { background: var(--sel); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--paper); }
::-webkit-scrollbar-thumb { background: var(--rule-hard); }
::-webkit-scrollbar-thumb:hover { background: var(--blue); }

a { color: inherit; text-decoration: none; }
img, svg, canvas { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
.icon { flex-shrink: 0; display: block; }

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

.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ================= shell ================= */

.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }

/* the raymarched field sits behind everything, always */
#gl {
  position: fixed; inset: 0; width: 100%; height: 100%;
  z-index: 0; pointer-events: none;
  opacity: 0; transition: opacity 1.4s var(--ease) 0.2s;
}
html.ready #gl { opacity: var(--gl-opacity, 1); }

/* a paper grain so the flat bone never looks like a plain fill */
.grain {
  position: fixed; inset: -50%; z-index: 1; pointer-events: none;
  opacity: 0.32; mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  animation: grainShift 0.9s steps(3) infinite;
}
:root[data-theme="dark"] .grain { mix-blend-mode: screen; opacity: 0.14; }
@keyframes grainShift {
  0%   { transform: translate(0, 0); }
  33%  { transform: translate(-2%, 1%); }
  66%  { transform: translate(1%, -2%); }
  100% { transform: translate(0, 0); }
}

main, .site-nav, .site-foot { position: relative; z-index: 2; }

/* ================= preloader ================= */

.preloader {
  position: fixed; inset: 0; z-index: 999;
  background: var(--paper);
  display: grid; place-content: center; gap: 22px;
  padding: var(--gutter);
  transition: opacity 0.7s var(--ease), transform 0.9s var(--ease);
}
.preloader.gone { opacity: 0; transform: translateY(-14px); pointer-events: none; }
.pre-mark {
  font-family: var(--mono); font-size: clamp(30px, 8vw, 62px);
  font-weight: 700; letter-spacing: -0.03em; line-height: 1;
}
.pre-mark span { color: var(--blue); }
.pre-row { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3); }
.pre-bar { width: min(58vw, 340px); height: 2px; background: var(--rule); overflow: hidden; }
.pre-bar i { display: block; height: 100%; width: 0; background: var(--ink); transition: width 0.18s linear; }

/* ================= cursor ================= */

.cursor { position: fixed; inset: 0; z-index: 900; pointer-events: none; transition: opacity 0.25s; }
.cursor-dot, .cursor-ring { position: fixed; top: 0; left: 0; will-change: transform; }
.cursor-dot { width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; background: var(--blue); border-radius: 50%; }
.cursor-ring {
  width: 38px; height: 38px; border: 1px solid var(--rule-hard); border-radius: 50%;
  transition: width 0.28s var(--ease), height 0.28s var(--ease), border-radius 0.28s var(--ease),
              border-color 0.2s, background 0.2s;
}
.cursor.snapped .cursor-ring { border-color: var(--blue); background: rgba(216, 31, 43, 0.10); }
.cursor.down .cursor-ring { transform-origin: center; border-color: var(--blue); background: rgba(216, 31, 43, 0.20); }
html.has-cursor, html.has-cursor a, html.has-cursor button { cursor: none; }

/* ================= scroll progress + curtain ================= */

.scroll-progress { position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 80; pointer-events: none; }
.scroll-progress i { display: block; height: 100%; background: var(--blue); transform: scaleX(0); transform-origin: 0 50%; }

.curtain {
  position: fixed; inset: 0; z-index: 950; pointer-events: none;
  background: var(--ink);
  transform: scaleY(0); transform-origin: 50% 100%;
  transition: transform 0.42s var(--ease-io);
}
.curtain.on { transform: scaleY(1); transform-origin: 50% 0%; }

/* ================= nav ================= */

.site-nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 70;
  height: var(--nav-h);
  display: flex; align-items: center;
  border-bottom: 1px solid transparent;
  transition: transform 0.4s var(--ease), background 0.3s, border-color 0.3s, backdrop-filter 0.3s;
}
.site-nav.stuck {
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  border-bottom-color: var(--rule);
}
.site-nav.hidden { transform: translateY(-100%); }
.site-nav .wrap { display: flex; align-items: center; gap: 16px; }

.brand { display: inline-flex; align-items: baseline; gap: 2px; font-family: var(--mono); font-weight: 700; font-size: 15px; letter-spacing: -0.02em; }
.brand b { font-weight: 700; }
.brand i { font-style: normal; color: var(--blue); }
.brand-sub { font-family: var(--mono); font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-3); margin-left: 10px; padding-left: 10px; border-left: 1px solid var(--rule); }

.nav-links { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.nav-links a {
  position: relative; padding: 7px 11px;
  font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-2); transition: color 0.18s;
}
.nav-links a::after {
  content: ""; position: absolute; left: 11px; right: 11px; bottom: 3px; height: 1px;
  background: var(--blue); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 0.32s var(--ease);
}
.nav-links a:hover { color: var(--ink); }
.nav-links a:hover::after, .nav-links a.current::after { transform: scaleX(1); }
.nav-links a.current { color: var(--ink); }

.nav-tools { display: flex; align-items: center; gap: 6px; margin-left: 8px; }
.icon-btn {
  width: 34px; height: 34px; display: grid; place-items: center;
  border: 1px solid var(--rule); color: var(--ink-2);
  transition: border-color 0.18s, color 0.18s, background 0.18s;
}
.icon-btn:hover { border-color: var(--ink); color: var(--ink); }
.theme-btn .moon { display: none; }
:root[data-theme="dark"] .theme-btn .sun { display: none; }
:root[data-theme="dark"] .theme-btn .moon { display: block; }

.nav-toggle { display: none; }

/* ================= buttons ================= */

.btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  padding: 13px 22px;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; font-weight: 500;
  border: 1px solid var(--ink); color: var(--ink); background: transparent;
  overflow: hidden; white-space: nowrap;
  transition: color 0.3s var(--ease), border-color 0.3s;
}
/* the fill wipes up from the baseline rather than fading */
.btn::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--ink); transform: scaleY(0); transform-origin: 50% 100%;
  transition: transform 0.42s var(--ease);
}
.btn > * { position: relative; }
.btn:hover { color: var(--paper); }
.btn:hover::before { transform: scaleY(1); transform-origin: 50% 0%; }

.btn-primary { background: var(--blue); border-color: var(--blue); color: #fff; }
.btn-primary::before { background: var(--ink); }
.btn-primary:hover { color: #fff; border-color: var(--ink); }

.btn-acid { background: var(--acid); border-color: var(--acid); color: #0b0b0c; }
.btn-acid::before { background: var(--ink); }
.btn-acid:hover { color: var(--acid); border-color: var(--ink); }

.btn-sm { padding: 9px 15px; font-size: 11px; }
.btn-lg { padding: 17px 30px; font-size: 13px; }
.btn[aria-disabled="true"] { opacity: 0.4; pointer-events: none; }

/* ================= type ================= */

.display {
  font-size: clamp(48px, 12.5vw, 190px);
  line-height: 0.86;
  letter-spacing: -0.055em;
  font-weight: 700;
  text-transform: uppercase;
}
.h1 { font-size: clamp(38px, 7.6vw, 96px); line-height: 0.94; letter-spacing: -0.045em; font-weight: 700; }
.h2 { font-size: clamp(28px, 4.4vw, 58px); line-height: 1.0; letter-spacing: -0.035em; font-weight: 700; }
.h3 { font-size: clamp(20px, 2.2vw, 27px); line-height: 1.15; letter-spacing: -0.02em; font-weight: 600; }
.h4 { font-size: 17px; letter-spacing: -0.012em; font-weight: 600; }

.lede { font-size: clamp(16px, 1.55vw, 21px); line-height: 1.5; color: var(--ink-2); max-width: 40ch; }
.body { font-size: 15.5px; line-height: 1.62; color: var(--ink-2); }
.small { font-size: 13.5px; line-height: 1.55; color: var(--ink-2); }

.mono { font-family: var(--mono); }
.tabular { font-variant-numeric: tabular-nums; }

.label {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--ink-3); font-weight: 500;
}
.mark { background: var(--acid); color: #0b0b0c; padding: 0 0.16em; }
.blue { color: var(--blue); }
.outline-text {
  -webkit-text-stroke: 1.2px var(--ink);
  color: transparent;
}

/* ================= section furniture ================= */

.section { padding: clamp(70px, 11vh, 150px) 0; position: relative; }
.section-line { border-top: 1px solid var(--rule); }

/* the numbered header: index, rule, title — a running spine down the page */
.shead { display: grid; grid-template-columns: auto 1fr; gap: 18px 26px; align-items: start; margin-bottom: clamp(36px, 6vh, 72px); }
.shead-idx {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.18em; color: var(--blue);
  padding-top: 10px; white-space: nowrap;
}
.shead-main { min-width: 0; }
.shead-main .h2 { margin-bottom: 16px; max-width: 18ch; }
.shead-rule { grid-column: 1 / -1; height: 1px; background: var(--rule); margin-bottom: 22px; order: -1; }

/* ================= hero ================= */

.hero { min-height: 100svh; display: flex; flex-direction: column; justify-content: flex-end; padding: calc(var(--nav-h) + 40px) 0 34px; position: relative; }
/* The aura runs bright behind the hero. On desktop the copy owns the left, so
   the scrim is weighted there; on one column it has to cover the whole
   measure instead. Without this the lede is genuinely unreadable wherever a
   wisp passes behind it. */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(95deg,
    var(--paper) 0%,
    color-mix(in srgb, var(--paper) 88%, transparent) 26%,
    color-mix(in srgb, var(--paper) 45%, transparent) 46%,
    transparent 68%);
}
.hero-inner { position: relative; z-index: 3; }
.hero-top { flex: 1; display: flex; align-items: center; }
.hero-inner { width: 100%; }

.hero-kicker { display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px 22px; margin-bottom: clamp(18px, 3vh, 34px); }

.hero-name { position: relative; }
.hero-name .display { max-width: 14ch; }
.hero-name .display .l2 { display: block; padding-left: 0.09em; }

.hero-sub { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(20px, 4vw, 60px); align-items: end; margin-top: clamp(26px, 4vh, 46px); }
.hero-actions { display: flex; flex-wrap: wrap; gap: 10px; }

.hero-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 28px;
  border-top: 1px solid var(--rule); padding-top: 16px; margin-top: clamp(28px, 5vh, 56px);
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-3);
}
.hero-foot .spacer { flex: 1; }
.hero-foot .live { display: inline-flex; align-items: center; gap: 8px; color: var(--ink-2); }
.hero-foot .live i { width: 6px; height: 6px; border-radius: 50%; background: var(--acid); box-shadow: 0 0 0 3px rgba(204,255,47,.25); animation: blip 2.4s var(--ease) infinite; }
@keyframes blip { 0%,100% { box-shadow: 0 0 0 3px rgba(204,255,47,.25); } 50% { box-shadow: 0 0 0 7px rgba(204,255,47,0); } }

/* scroll hint */
.scroll-hint { display: inline-flex; align-items: center; gap: 10px; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-3); }
.scroll-hint i { display: block; width: 1px; height: 26px; background: var(--rule-hard); position: relative; overflow: hidden; }
.scroll-hint i::after { content: ""; position: absolute; inset: 0; background: var(--blue); animation: drip 1.9s var(--ease-io) infinite; }
@keyframes drip { 0% { transform: translateY(-100%); } 60%,100% { transform: translateY(100%); } }

/* ================= full-bleed art layer =================
   Wallpaper-shaped artwork behind everything. Each slide gets a slow Ken
   Burns push and crossfades to the next; the whole stack parallaxes against
   the aura, which is composited over it in screen blend so the energy reads
   as light in the scene rather than a sticker on top of it.

   Mounted by fx.js only from data/art.json, which ships empty — no art, no
   layer, no 404s. */

.art-layer {
  position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden;
  opacity: 0; transition: opacity 1.6s var(--ease) 0.2s;
}
html.ready.has-art .art-layer { opacity: 1; }

.art-slide {
  /* Sharpness. A 1600x900 source cannot gain detail, but it can stop losing
     it: every percent of inset and every bit of Ken Burns scale is extra
     upscaling on top of `cover`. Both are now near zero, so the image is
     drawn as close to 1:1 as the viewport allows. The grade is a light
     contrast/saturation lift, which reads as crispness without haloing the
     way a sharpen filter would. */
  position: absolute; inset: -2%;
  background-size: cover;
  background-repeat: no-repeat;
  opacity: 0;
  transition: opacity var(--art-fade, 1800ms) var(--ease);
  will-change: transform, opacity;
  transform: translate3d(var(--ax, 0px), var(--ay, 0px), 0) scale(1.005);
  filter: contrast(1.08) saturate(1.10) brightness(1.02);
}
.art-slide.on {
  opacity: 1;
  animation: kenburns 22s ease-in-out infinite alternate;
}
@keyframes kenburnsPortrait {
  from { transform: translate3d(var(--ax, 0px), var(--ay, 0px), 0) scale(1.0); }
  to   { transform: translate3d(calc(var(--ax, 0px) - 4px), calc(var(--ay, 0px) + 3px), 0) scale(1.03); }
}
@keyframes kenburns {
  from { transform: translate3d(var(--ax, 0px), var(--ay, 0px), 0) scale(1.005); }
  to   { transform: translate3d(calc(var(--ax, 0px) - 8px), calc(var(--ay, 0px) + 5px), 0) scale(1.045); }
}

/* Legibility. Artwork this busy will eat body copy alive without it: a
   left-weighted wash where the column sits, plus a floor gradient. --art-dim
   is written from data/art.json. */
.art-layer::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(95deg,
      var(--paper) 0%,
      color-mix(in srgb, var(--paper) 88%, transparent) 26%,
      color-mix(in srgb, var(--paper) 42%, transparent) 52%,
      transparent 78%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 60%, transparent) 0%,
      transparent 32%,
      color-mix(in srgb, var(--paper) 62%, transparent) 76%,
      var(--paper) 100%);
  opacity: var(--art-dim, 0.55);
}

/* With art behind it the aura stops being the background and becomes light
   in the scene. */
html.has-art #gl { z-index: 1; mix-blend-mode: screen; }
/* With artwork behind it the hero scrim has to work HARDER, not less: body
   copy on the bright part of a photograph is unreadable. Keeping --art-dim
   low and this high means the image stays vivid everywhere except directly
   behind the words. */
html.has-art .hero::before { opacity: 0.92; }
/* With a centred figure the copy sits ON the artwork, so the scrim becomes a
   vertical one: dark at the top and bottom where the type is, clear through
   the middle where the subject is. */
html.has-figure:not(.has-art) .hero::before {
  opacity: 1;
  /* Shaped to the layout, not evenly: open through the upper middle where the
     ring and the head are, heavier from 70% down where the lede sits. The
     headline survives the clear band on its text-shadow alone. */
  background: linear-gradient(180deg,
    var(--paper) 0%,
    color-mix(in srgb, var(--paper) 70%, transparent) 13%,
    color-mix(in srgb, var(--paper) 26%, transparent) 30%,
    color-mix(in srgb, var(--paper) 34%, transparent) 54%,
    color-mix(in srgb, var(--paper) 74%, transparent) 70%,
    color-mix(in srgb, var(--paper) 94%, transparent) 86%,
    var(--paper) 100%);
}

/* Centre-composed hero. The headline sits over the figure's upper half and the
   supporting copy under it, which is how the artwork itself is arranged. */
html.has-figure:not(.has-art) .hero { text-align: center; }
html.has-figure:not(.has-art) .hero-kicker,
html.has-figure:not(.has-art) .hero-actions { justify-content: center; }
html.has-figure:not(.has-art) .hero-name .display { max-width: none; }
html.has-figure:not(.has-art) .hero-name .display .l2 { padding-left: 0; }
html.has-figure:not(.has-art) .hero-sub { grid-template-columns: 1fr; justify-items: center; gap: 22px; }
html.has-figure:not(.has-art) .lede { max-width: 46ch; margin-inline: auto; }
/* the type has to survive sitting on the artwork */
html.has-figure:not(.has-art) .hero-copy .display,
html.has-figure:not(.has-art) .hero-copy .h1 { text-shadow: 0 6px 54px rgba(0,0,0,.92), 0 2px 10px rgba(0,0,0,.8), 0 1px 3px rgba(0,0,0,.7); }
html.has-figure:not(.has-art) .hero-copy .lede { text-shadow: 0 2px 16px rgba(0,0,0,.85); }
/* The kicker lands on the brightest part of the artwork — the ring — so it
   needs its own backing rather than relying on the gradient. */
html.has-figure:not(.has-art) .hero-kicker .label {
  text-shadow: 0 2px 14px rgba(0,0,0,.95), 0 0 4px rgba(0,0,0,.9);
  background: color-mix(in srgb, var(--paper) 55%, transparent);
  padding: 3px 9px;
}

/* The nav sits over the top of the artwork before it goes sticky, so it needs
   its own wash or the strapline disappears into the image. */
html.has-art .site-nav::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--paper) 88%, transparent),
    color-mix(in srgb, var(--paper) 45%, transparent) 60%,
    transparent);
  transition: opacity .25s;
}
html.has-art .site-nav.stuck::before { opacity: 0; }

@media (max-width: 820px) {
  /* A 16:9 image on a portrait phone is the hard case: `cover` scales it to
     fill the height and then only about a quarter of its width survives the
     crop. Three things keep it readable — a shallower inset, a much gentler
     Ken Burns so the crop does not tighten further, and a focus point set
     above centre so the subject stays in frame rather than the floor. */
  .art-slide { inset: -1%; background-position: 50% 30% !important; }
  .art-slide.on { animation-name: kenburnsPortrait; }

  .art-layer::after {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 62%, transparent) 0%,
      color-mix(in srgb, var(--paper) 78%, transparent) 45%,
      color-mix(in srgb, var(--paper) 92%, transparent) 75%,
      var(--paper) 100%);
    opacity: calc(var(--art-dim, 0.55) + 0.22);
  }
  .art-slide { inset: -10%; }
}

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

/* ================= figure layer =================
   An optional silhouette that sits between the aura and the copy. It is
   mounted by fx.js only if assets/img/figure.png actually loads, so the
   page never shows a broken image — drop a PNG with a transparent
   background in and it appears. Artwork you own; see the README. */

.figure-layer {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  display: grid;
  /* Centred. The artwork is already composed as a poster — subject dead
     centre, glow radiating out — so anything other than the middle of the
     frame fights the composition that is baked into it. */
  place-items: center;
  overflow: hidden;
  opacity: 0;
  transition: opacity 1.6s var(--ease) 0.4s;
}
html.ready .figure-layer { opacity: 1; }

.figure-holder {
  position: relative;
  transition: filter 0.35s var(--ease);
  transform:
    translate3d(var(--fx, 0), var(--fy, 0), 0)
    rotateY(var(--rx, 0deg)) rotateX(var(--ry, 0deg))
    scale(var(--fs, 1));
  will-change: transform;
}

/* A scan sweep, masked by the artwork's own alpha so it runs across the figure
   and not across the empty space around it. Screen blend keeps it additive. */
.figure-scan {
  position: absolute; inset: 0; pointer-events: none;
  -webkit-mask-image: var(--fig); mask-image: var(--fig);
  -webkit-mask-size: 100% 100%;  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  mix-blend-mode: screen;
  background: linear-gradient(180deg,
    transparent 0%,
    transparent 42%,
    color-mix(in srgb, var(--blue) 55%, transparent) 49%,
    rgba(255,255,255,.55) 50%,
    color-mix(in srgb, var(--blue) 55%, transparent) 51%,
    transparent 58%,
    transparent 100%);
  background-size: 100% 240%;
  animation: figureScan 6.5s cubic-bezier(.5,0,.5,1) infinite;
  opacity: .75;
}
@keyframes figureScan {
  0%   { background-position: 0 130%; opacity: 0; }
  8%   { opacity: .8; }
  60%  { opacity: .8; }
  70%  { background-position: 0 -40%; opacity: 0; }
  100% { background-position: 0 -40%; opacity: 0; }
}

.figure-layer img {
  /* Sized to the viewport rather than a fixed cap: the artwork is 16:9, so
     width is the binding constraint on wide screens and height on tall ones. */
  width: min(96vw, 1500px);
  height: auto;
  max-height: 92vh;
  object-fit: contain;
  object-position: bottom center;
  /* backlight: the silhouette glows out of the aura rather than sitting on it */
  filter:
    drop-shadow(0 0 34px color-mix(in srgb, var(--blue) 55%, transparent))
    drop-shadow(0 0 90px color-mix(in srgb, var(--blue) 32%, transparent))
    contrast(1.06);
  animation: figureBreathe 7s var(--ease) infinite;
}
@keyframes figureBreathe {
  0%, 100% { filter: drop-shadow(0 0 30px color-mix(in srgb, var(--blue) 50%, transparent)) drop-shadow(0 0 80px color-mix(in srgb, var(--blue) 28%, transparent)) contrast(1.06); }
  50%      { filter: drop-shadow(0 0 46px color-mix(in srgb, var(--blue) 72%, transparent)) drop-shadow(0 0 120px color-mix(in srgb, var(--blue) 42%, transparent)) contrast(1.08); }
}

/* Foreground haze. In the reference compositions the energy passes BOTH
   behind and in front of the figure — that overlap is most of what sells the
   depth. The aura canvas is behind, so this is a cheap screen-blended layer
   in front: two slow drifting radial gradients, masked to the lower body so
   the head never washes out. */
.figure-haze {
  position: fixed; inset: 0; z-index: 1; pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 1.8s var(--ease) 0.8s;
  -webkit-mask-image: linear-gradient(180deg, transparent 26%, #000 62%, #000 88%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 26%, #000 62%, #000 88%, transparent 100%);
}
html.ready.has-figure .figure-haze { opacity: 0.55; }
.figure-haze::before,
.figure-haze::after {
  content: ""; position: absolute; inset: -20%;
  background: radial-gradient(38% 30% at 50% 62%, color-mix(in srgb, var(--blue) 55%, transparent), transparent 70%);
  filter: blur(28px);
  animation: hazeDrift 17s var(--ease) infinite alternate;
}
.figure-haze::after {
  background: radial-gradient(30% 26% at 44% 74%, color-mix(in srgb, var(--gl-core, #bfe4ff) 42%, transparent), transparent 72%);
  animation-duration: 23s;
  animation-direction: alternate-reverse;
}
@keyframes hazeDrift {
  0%   { transform: translate3d(-3%, 2%, 0) scale(1.00); }
  100% { transform: translate3d( 4%, -3%, 0) scale(1.12); }
}

/* the figure itself gets a real perspective tilt, not just a translate */
.figure-layer { perspective: 1400px; }


@media (max-width: 820px) {
  /* A 16:9 poster on a portrait screen goes tiny if fitted by width, so let it
     overflow the sides instead and keep the subject readable.

     Centring it cannot be left to the grid. When a grid item is wider than its
     track the browser clamps it to the start edge rather than letting it
     overflow both ways — data-loss avoidance — so place-items:center was being
     ignored and the figure sat against the left edge, 169px off centre on a
     375px screen. Absolute positioning with a -50% translate is not clamped,
     so the subject stays in the middle whatever the screen ratio.

     The parallax transform follows the centring translate and so still
     applies; order matters, because transforms compose right to left. */
  .figure-layer { display: block; }
  .figure-holder {
    position: absolute;
    top: 50%;
    left: 50%;
    transform:
      translate(-50%, -50%)
      translate3d(var(--fx, 0), var(--fy, 0), 0)
      scale(var(--fs, 1));
  }
  .figure-layer img { width: 190vw; max-width: none; height: auto; max-height: 62vh; opacity: 0.85; }
  html.ready.has-figure .figure-haze { opacity: 0.4; }
}
@media (prefers-reduced-motion: reduce) {
  .figure-layer img { animation: none; }
  .figure-scan { display: none; }
  .figure-haze::before, .figure-haze::after { animation: none; }
}

/* ================= REX interface mockup =================
   A depiction of the actual app: three panes, streaming transcript, inline
   tool cards, an editable diff waiting on approval, detail pane, status bar.
   Rendered in the product's own dark chrome with its purple accent — one of
   the accents the app really ships — so it reads as a screenshot of REX
   rather than as more website. */

.gui {
  --g-bg:      #0b0b0c;
  --g-raised:  #101012;
  --g-overlay: #17171a;
  --g-sunken:  #08080a;
  --g-text:    #f1f1f2;
  --g-text2:   #9a9aa1;
  --g-text3:   #66666d;
  --g-line:    rgba(255,255,255,.085);
  --g-line2:   rgba(255,255,255,.16);
  --g-accent:  #9d6dd6;
  --g-ok:      #4ea672;
  --g-warn:    #d9a441;

  border: 1px solid var(--g-line2);
  background: var(--g-bg);
  color: var(--g-text);
  font-size: 12px;
  overflow: hidden;
  box-shadow: 0 40px 90px -40px rgba(0,0,0,.9);
}

.gui-bar {
  display: flex; align-items: center; gap: 10px;
  height: 34px; padding: 0 11px;
  border-bottom: 1px solid var(--g-line);
}
.gui-brand { display: inline-flex; align-items: center; gap: 8px; font-family: var(--mono); font-weight: 700; font-size: 11px; letter-spacing: 2.2px; }
.gui-brand i { width: 8px; height: 8px; border-radius: 2px; transform: rotate(45deg); background: var(--g-accent); }
.gui-crumb { flex: 1; text-align: center; font-size: 11.5px; color: var(--g-text3); }
.gui-crumb b { color: var(--g-text2); font-weight: 400; }
.gui-acts { display: flex; gap: 7px; color: var(--g-text3); }

.gui-body { display: grid; grid-template-columns: 186px minmax(0,1fr) 214px; min-height: 430px; }

.gui-side { background: var(--g-sunken); border-right: 1px solid var(--g-line); padding: 10px; display: flex; flex-direction: column; gap: 7px; }
.gui-new { display: flex; align-items: center; justify-content: center; gap: 6px; padding: 7px; background: var(--g-accent); color: #fff; font-weight: 600; font-size: 11.5px; }
.gui-find { display: flex; align-items: center; gap: 7px; padding: 6px 9px; border: 1px solid var(--g-line); color: var(--g-text3); font-size: 11px; }
.gui-grp { font-size: 9.5px; text-transform: uppercase; letter-spacing: .9px; font-weight: 600; color: var(--g-text3); padding: 8px 3px 2px; display: flex; align-items: center; gap: 6px; }
.gui-sess { padding: 7px 9px; color: var(--g-text2); font-size: 11.5px; display: flex; flex-direction: column; gap: 2px; }
.gui-sess span { font-family: var(--mono); font-size: 10px; color: var(--g-text3); }
.gui-sess.on { background: rgba(255,255,255,.065); color: var(--g-text); box-shadow: inset 2px 0 0 var(--g-accent); }

.gui-chat { padding: 15px 17px; display: flex; flex-direction: column; gap: 10px; }
.gui-user { align-self: flex-end; max-width: 84%; border: 1px solid var(--g-line); background: var(--g-overlay); padding: 8px 12px; font-size: 12.5px; line-height: 1.5; }
.gui-think { display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--g-text3); font-style: italic; }
.gui-think i { width: 9px; height: 9px; border-radius: 50%; background: var(--g-accent); animation: guiPulse 1.7s var(--ease) infinite; }
@keyframes guiPulse { 0%,100% { transform: scale(1); opacity: .7; } 50% { transform: scale(1.25); opacity: 1; } }
.gui-say { font-size: 12.5px; line-height: 1.6; }
.gui-say code { font-family: var(--mono); font-size: 11.5px; color: var(--g-text2); }

.gui-tool { border: 1px solid var(--g-line); background: var(--g-raised); }
.gui-tool.risk { border-color: rgba(157,109,214,.55); background: rgba(157,109,214,.10); }
.gui-th { display: flex; align-items: center; gap: 8px; padding: 6px 9px; color: var(--g-text3); }
.gui-th b { font-family: var(--mono); font-size: 11.5px; font-weight: 500; color: var(--g-text); }
.gui-th span { font-family: var(--mono); font-size: 11px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gui-th em { font-family: var(--mono); font-size: 10px; font-style: normal; }
.gui-tb { padding: 0 10px 8px; font-family: var(--mono); font-size: 11px; color: var(--g-text2); line-height: 1.65; }
.gui-diff span { display: block; padding: 0 6px; }
.gui-diff .add { background: rgba(78,166,114,.14); color: var(--g-ok); }
.gui-diff .del { background: rgba(229,72,77,.12); color: #e5484d; }
.gui-appr { display: flex; align-items: center; gap: 7px; padding: 2px 10px 9px; }
.gui-appr b { font-size: 11px; font-weight: 600; padding: 5px 11px; background: var(--g-accent); color: #fff; }
.gui-appr i { font-style: normal; font-size: 11px; padding: 5px 11px; border: 1px solid var(--g-line); color: var(--g-text2); }
.gui-appr em { margin-left: auto; font-family: var(--mono); font-size: 10px; font-style: normal; color: var(--g-text3); }

.gui-comp { margin-top: auto; border: 1px solid var(--g-line); background: #0e0e10; padding: 9px 11px; color: var(--g-text3); font-size: 12px; display: flex; flex-direction: column; gap: 9px; }
.gui-comp .r { display: flex; align-items: center; gap: 7px; font-family: var(--mono); font-size: 10.5px; }
.gui-comp .r .sp { flex: 1; }

.gui-det { border-left: 1px solid var(--g-line); background: var(--g-raised); padding: 12px; display: flex; flex-direction: column; gap: 9px; }
.gui-det h5 { font-size: 9.5px; text-transform: uppercase; letter-spacing: 1px; color: var(--g-text3); font-weight: 600; }
.gui-kv { display: flex; justify-content: space-between; gap: 8px; font-family: var(--mono); font-size: 10.5px; color: var(--g-text3); }
.gui-kv b { color: var(--g-text2); font-weight: 500; }
.gui-meter { height: 4px; background: var(--g-sunken); overflow: hidden; }
.gui-meter i { display: block; height: 100%; background: var(--g-accent); }

.gui-status { display: flex; align-items: center; gap: 12px; height: 26px; padding: 0 12px; border-top: 1px solid var(--g-line); background: var(--g-sunken); font-family: var(--mono); font-size: 10.5px; color: var(--g-text3); }
.gui-status .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--g-ok); }
.gui-status .sp { flex: 1; }

@media (max-width: 1080px) { .gui-body { grid-template-columns: 168px minmax(0,1fr); } .gui-det { display: none; } }
@media (max-width: 720px)  { .gui-body { grid-template-columns: 1fr; } .gui-side { display: none; } .gui { font-size: 11px; } }

/* ================= REX lockup ================= */

/* The sigil is a gate split by a rift: the hexagon is the perimeter, the
   vertical rift is the way through it, and the bar beneath it is the
   denylist that never moves. It scales off the display type so the two
   always sit on the same optical weight. */
.rex-lockup { display: flex; align-items: center; gap: clamp(14px, 2vw, 30px); }
.rex-sigil {
  width: clamp(52px, 9vw, 132px);
  height: auto;
  flex-shrink: 0;
  color: var(--blue);
  overflow: visible;
}
.rex-sigil path, .rex-sigil rect { transform-origin: 50% 50%; }
.rex-sigil path:first-child {
  stroke-dasharray: 268;
  stroke-dashoffset: 268;
  animation: sigilTrace 1.6s var(--ease) 0.35s forwards;
}
.rex-lockup.in .rex-sigil { animation: sigilPulse 4.5s var(--ease) 2s infinite; }
@keyframes sigilTrace { to { stroke-dashoffset: 0; } }
@keyframes sigilPulse {
  0%, 100% { filter: drop-shadow(0 0 0 transparent); }
  50%      { filter: drop-shadow(0 0 14px color-mix(in srgb, var(--blue) 60%, transparent)); }
}

/* ================= stat band ================= */

.stats { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--rule); }
.stat { padding: 22px 20px 22px 0; border-right: 1px solid var(--rule); }
.stat:last-child { border-right: 0; }
.stat-n { font-size: clamp(30px, 4.2vw, 56px); line-height: 1; letter-spacing: -0.04em; font-weight: 700; font-variant-numeric: tabular-nums; }
.stat-n sup { font-size: 0.42em; vertical-align: super; color: var(--blue); letter-spacing: 0; }
.stat-l { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3); margin-top: 10px; }

/* ================= marquee ================= */

.marquee-strip { border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); padding: 16px 0; overflow: hidden; background: var(--paper-2); }
.marquee { display: flex; overflow: hidden; }
.marquee-track { display: flex; gap: 0; flex-shrink: 0; animation: slide 38s linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
@keyframes slide { to { transform: translateX(-100%); } }
.marquee-track span {
  display: inline-flex; align-items: center; gap: 14px; padding: 0 20px; white-space: nowrap;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-2);
}
.marquee-track span::after { content: "◆"; font-size: 7px; color: var(--blue); }

/* ================= index list (work / repos) ================= */

.ilist { border-top: 1px solid var(--rule); }
.irow {
  position: relative; display: grid;
  grid-template-columns: 58px minmax(0, 1.5fr) minmax(0, 1fr) auto;
  gap: 20px; align-items: center;
  padding: 26px 0; border-bottom: 1px solid var(--rule);
  transition: color 0.3s var(--ease);
}
/* the acid sweep fills from the left on hover; the row inverts */
.irow::before {
  content: ""; position: absolute; inset: 0 calc(var(--gutter) * -0.5); z-index: -1;
  background: var(--ink); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 0.5s var(--ease);
}
.irow:hover::before { transform: scaleX(1); }
.irow:hover { color: var(--paper); }
.irow:hover .irow-n, .irow:hover .irow-meta, .irow:hover .irow-tags span { color: var(--paper-3); border-color: rgba(255,255,255,.28); }
.irow:hover .irow-go { background: var(--acid); color: #0b0b0c; border-color: var(--acid); transform: none; }

.irow-n { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; color: var(--ink-3); transition: color 0.3s; }
.irow-t { font-size: clamp(21px, 2.6vw, 34px); line-height: 1.05; letter-spacing: -0.03em; font-weight: 600; }
.irow-d { font-size: 14px; line-height: 1.5; color: inherit; opacity: 0.72; }
.irow-meta { font-family: var(--mono); font-size: 11px; color: var(--ink-3); transition: color 0.3s; }
.irow-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.irow-tags span {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 3px 8px; border: 1px solid var(--rule); color: var(--ink-3); transition: color 0.3s, border-color 0.3s;
}
.irow-go {
  width: 44px; height: 44px; flex-shrink: 0; display: grid; place-items: center;
  border: 1px solid var(--rule); color: inherit;
  transition: background 0.3s var(--ease), color 0.3s, border-color 0.3s, transform 0.3s var(--ease);
}

/* ================= panels ================= */

.grid { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }

.panel {
  position: relative; background: var(--paper); padding: clamp(22px, 2.6vw, 34px);
  transition: background 0.35s var(--ease);
  overflow: hidden;
}
.panel::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(420px circle at var(--cx, 50%) var(--cy, 50%), rgba(216,31,43,.10), transparent 62%);
  transition: opacity 0.35s;
}
.panel:hover { background: var(--panel); }
.panel:hover::after { opacity: 1; }
.panel-n { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.18em; color: var(--blue); margin-bottom: 20px; display: block; }
.panel h3 { margin-bottom: 12px; }
.panel p { font-size: 14.5px; line-height: 1.6; color: var(--ink-2); }
.panel-ico { width: 26px; height: 26px; color: var(--ink); margin-bottom: 18px; }

/* ================= inverted block ================= */

/* A custom property redefined in a rule also applies to that rule's OWN
   declarations, so `background: var(--ink)` here would resolve against the
   *new* --ink and paint the section in its own text colour — bone on bone,
   with the display type invisible. The element's own background and colour
   are therefore literals; the tokens are for descendants only. */
.invert {
  background: #0b0b0c;
  color: #edebe5;
  --ink: #edebe5; --ink-2: #98948c; --ink-3: #6a6760;
  --rule: rgba(237,235,229,.18); --rule-soft: rgba(237,235,229,.09); --rule-hard: rgba(237,235,229,.4);
  --paper: #0b0b0c; --paper-2: #131315; --paper-3: #1c1c1f; --panel: #131315;
}
:root[data-theme="dark"] .invert {
  background: #edebe5;
  color: #0b0b0c;
  --ink: #0b0b0c; --ink-2: #5a5751; --ink-3: #8d8981;
  --rule: rgba(11,11,12,.16); --rule-soft: rgba(11,11,12,.08); --rule-hard: rgba(11,11,12,.42);
  --paper: #edebe5; --paper-2: #f2f1ec; --paper-3: #dcd9d1; --panel: #f6f5f1;
  --blue: #d81f2b;
}

/* ================= skills ================= */

.skills { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 34px 40px; }
.skill-h { font-family: var(--mono); font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--blue); padding-bottom: 12px; border-bottom: 1px solid var(--rule); margin-bottom: 18px; }
.skill { margin-bottom: 16px; }
.skill-top { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: 14.5px; margin-bottom: 7px; }
.skill-top em { font-style: normal; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); }
.meter { height: 2px; background: var(--rule); overflow: hidden; }
.meter i { display: block; height: 100%; width: 0; background: var(--ink); transition: width 1.2s var(--ease); }

/* ================= process / timeline ================= */

.steps { border-top: 1px solid var(--rule); }
.step { display: grid; grid-template-columns: 100px minmax(0, 1fr); gap: 24px; padding: 28px 0; border-bottom: 1px solid var(--rule); }
.step-w { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; color: var(--blue); }
.step h4 { margin-bottom: 8px; }
.step p { font-size: 14.5px; line-height: 1.6; color: var(--ink-2); max-width: 62ch; }

/* ================= contact ================= */

.contact-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
.contact-card {
  position: relative; background: var(--paper); padding: 26px 24px;
  display: flex; align-items: center; gap: 16px;
  transition: background 0.3s var(--ease), color 0.3s;
  overflow: hidden;
}
.contact-card::before {
  content: ""; position: absolute; inset: 0; background: var(--blue);
  transform: translateY(101%); transition: transform 0.42s var(--ease);
}
.contact-card:hover::before { transform: translateY(0); }
.contact-card:hover { color: #fff; }
.contact-card > * { position: relative; }
.contact-card .ci { width: 24px; height: 24px; }
.contact-card b { display: block; font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.contact-card span { display: block; font-family: var(--mono); font-size: 11px; color: var(--ink-3); margin-top: 3px; transition: color 0.3s; }
.contact-card:hover span { color: rgba(255,255,255,.75); }

/* ================= downloads ================= */

.os-tabs { display: flex; flex-wrap: wrap; border: 1px solid var(--rule); width: fit-content; margin-bottom: 26px; }
.os-tab {
  display: inline-flex; align-items: center; gap: 9px; padding: 12px 18px;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-2); border-right: 1px solid var(--rule);
  transition: background 0.22s, color 0.22s;
}
.os-tab:last-child { border-right: 0; }
.os-tab:hover { background: var(--panel); color: var(--ink); }
.os-tab[aria-selected="true"] { background: var(--ink); color: var(--paper); }
.os-tab .cnt { opacity: 0.5; }

.dl-list { border-top: 1px solid var(--rule); }
.dl-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 26px; align-items: start;
  padding: 30px 0; border-bottom: 1px solid var(--rule);
}
.dl-row.primary { background: linear-gradient(90deg, color-mix(in srgb, var(--acid) 22%, transparent), transparent 42%); }
.dl-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; }
.dl-head h3 { font-size: clamp(19px, 2.1vw, 26px); letter-spacing: -0.025em; font-weight: 600; }
.dl-row > div > p { font-size: 14.5px; line-height: 1.55; color: var(--ink-2); max-width: 60ch; }
.pill {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  padding: 4px 9px; border: 1px solid var(--rule); color: var(--ink-3);
}
.pill.acid { background: var(--acid); border-color: var(--acid); color: #0b0b0c; }
.pill.blue { background: var(--blue); border-color: var(--blue); color: #fff; }
.dl-facts { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 14px; font-family: var(--mono); font-size: 11px; color: var(--ink-3); }
.dl-side { display: flex; flex-direction: column; gap: 8px; min-width: 172px; }

.kv { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.kv code {
  flex: 1; min-width: 0; font-family: var(--mono); font-size: 10.5px; color: var(--ink-3);
  border: 1px solid var(--rule); padding: 7px 10px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; background: var(--paper-2);
}
.copy-btn {
  flex-shrink: 0; font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase;
  border: 1px solid var(--rule); padding: 7px 11px; color: var(--ink-3);
  transition: color 0.18s, border-color 0.18s, background 0.18s;
}
.copy-btn:hover { color: var(--ink); border-color: var(--ink); }
.copy-btn.done { background: var(--acid); border-color: var(--acid); color: #0b0b0c; }

.code {
  font-family: var(--mono); font-size: 13px; line-height: 1.8; white-space: pre; overflow-x: auto;
  border: 1px solid var(--rule); padding: 18px 20px; background: var(--paper-2); color: var(--ink-2);
}
.code .c { color: var(--ink-3); }
.code .k { color: var(--blue); }

.slot { border: 1px dashed var(--rule-hard); padding: 40px 28px; text-align: center; color: var(--ink-3); font-size: 14px; }
.slot b { display: block; color: var(--ink-2); font-size: 17px; margin-bottom: 8px; font-weight: 600; }

/* ================= REX product bits ================= */

.spec { border-top: 1px solid var(--rule); }
.spec-row { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 20px; padding: 15px 0; border-bottom: 1px solid var(--rule); font-size: 14.5px; }
.spec-row dt { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); padding-top: 3px; }
.spec-row dd { color: var(--ink); }

.toolwall { display: grid; grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
.toolcell {
  background: var(--paper); padding: 15px 16px;
  transition: background 0.2s, color 0.2s;
}
.toolcell:hover { background: var(--ink); color: var(--paper); }
.toolcell:hover em { color: var(--paper-3); }
.toolcell b { display: block; font-family: var(--mono); font-size: 12.5px; font-weight: 500; }
.toolcell em { display: block; font-style: normal; font-size: 11.5px; color: var(--ink-3); margin-top: 4px; transition: color 0.2s; }

.ladder { border-top: 1px solid var(--rule); }
.rung { display: grid; grid-template-columns: 44px minmax(0,1fr) auto; gap: 18px; align-items: center; padding: 17px 0; border-bottom: 1px solid var(--rule); }
.rung .n { font-family: var(--mono); font-size: 11px; color: var(--ink-3); }
.rung .t { font-family: var(--mono); font-size: 14px; }
.rung .d { font-size: 13px; color: var(--ink-3); text-align: right; }
.rung.hard { background: var(--acid); }
.rung.hard .n, .rung.hard .t, .rung.hard .d { color: #0b0b0c; }
.rung.hard .t { font-weight: 700; }

/* ================= footer ================= */

.site-foot { border-top: 1px solid var(--rule); padding: clamp(48px, 8vh, 96px) 0 30px; background: var(--paper-2); }
.foot-huge { font-size: clamp(46px, 13vw, 200px); line-height: 0.84; letter-spacing: -0.055em; font-weight: 700; text-transform: uppercase; margin-bottom: 40px; }
.foot-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 30px; padding-top: 30px; border-top: 1px solid var(--rule); }
.foot-cols h5 { font-family: var(--mono); font-size: 10px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 14px; }
.foot-cols a { display: block; font-size: 14px; color: var(--ink-2); padding: 5px 0; transition: color 0.16s, transform 0.24s var(--ease); }
.foot-cols a:hover { color: var(--blue); transform: translateX(4px); }
.foot-end { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px; margin-top: 44px; padding-top: 22px; border-top: 1px solid var(--rule); font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3); }

/* ================= motion primitives ================= */

/* split text: each word/char rises out of its own clipped line */
.split .sp { display: inline-block; }
[data-split] { overflow: hidden; }
[data-split].split .sp {
  transform: translateY(105%) rotate(3deg);
  opacity: 0;
  transition: transform 0.9s var(--ease), opacity 0.6s var(--ease);
  transition-delay: calc(var(--i) * 34ms);
}
[data-split].split.in .sp { transform: none; opacity: 1; }
[data-split][data-split-fast].split .sp { transition-delay: calc(var(--i) * 14ms); }

.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.8s var(--ease), transform 0.8s var(--ease); }
.reveal.in { opacity: 1; transform: none; }
.reveal.d1 { transition-delay: 0.09s; }
.reveal.d2 { transition-delay: 0.18s; }
.reveal.d3 { transition-delay: 0.27s; }
.reveal.d4 { transition-delay: 0.36s; }

/* a rule that draws itself */
.draw { transform: scaleX(0); transform-origin: 0 50%; transition: transform 1s var(--ease); }
.draw.in { transform: scaleX(1); }

/* ================= responsive ================= */

@media (max-width: 1080px) {
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .hero-sub { grid-template-columns: 1fr; }
  .irow { grid-template-columns: 40px minmax(0, 1fr) auto; }
  .irow-meta-cell { display: none; }
}

@media (max-width: 820px) {
  :root { --nav-h: 54px; }

  .nav-toggle { display: grid; margin-left: auto; }
  .nav-tools { margin-left: 0; }
  /* the strapline wraps to two lines and crowds the controls at this width */
  .brand-sub { display: none; }

  .nav-links {
    position: fixed; inset: 0; z-index: -1;
    flex-direction: column; align-items: flex-start; justify-content: center;
    gap: 4px; padding: calc(var(--nav-h) + 30px) var(--gutter) 40px;
    background: var(--paper);
    opacity: 0; pointer-events: none;
    transition: opacity 0.32s var(--ease);
  }
  .nav-links.open { opacity: 1; pointer-events: auto; }
  .nav-links a {
    font-family: var(--sans); font-size: clamp(30px, 9vw, 52px);
    letter-spacing: -0.035em; text-transform: none; font-weight: 700;
    padding: 8px 0; color: var(--ink);
    opacity: 0; transform: translateY(18px);
    transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
  }
  .nav-links.open a { opacity: 1; transform: none; }
  .nav-links.open a:nth-child(1) { transition-delay: 0.06s; }
  .nav-links.open a:nth-child(2) { transition-delay: 0.11s; }
  .nav-links.open a:nth-child(3) { transition-delay: 0.16s; }
  .nav-links.open a:nth-child(4) { transition-delay: 0.21s; }
  .nav-links.open a:nth-child(5) { transition-delay: 0.26s; }
  .nav-links.open a:nth-child(6) { transition-delay: 0.31s; }
  .nav-links a::after { left: 0; right: auto; width: 0; bottom: 6px; transform: none; transition: width 0.3s var(--ease); }
  .nav-links a:hover::after, .nav-links a.current::after { width: 2.2em; }

  /* minmax(0, …) for the same reason as .dl-row: these hold panels, and a panel
     containing a checksum or a command in nowrap would otherwise set the floor
     for the whole page. */
  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); }
  .stats { grid-template-columns: repeat(2, 1fr); }
  .stat { border-right: 0; border-bottom: 1px solid var(--rule); padding-right: 0; }
  .stat:nth-child(odd) { border-right: 1px solid var(--rule); padding-right: 18px; }

  .irow { grid-template-columns: minmax(0, 1fr) auto; gap: 14px; padding: 22px 0; }
  .irow-n { display: none; }
  .irow::before { display: none; }
  .irow:hover { color: inherit; }
  .irow-go { width: 38px; height: 38px; }

  .step { grid-template-columns: 1fr; gap: 10px; }
  .spec-row { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  /* minmax(0, 1fr), not 1fr: a bare 1fr is minmax(auto, 1fr), and `auto` will
     not shrink a grid item below its min-content — which here is an install
     command set in nowrap. The column then refused to go narrower than the
     longest command and pushed the whole page to 548px on a 375px screen. */
  .dl-row { grid-template-columns: minmax(0, 1fr); }
  .dl-side { min-width: 0; }
  .os-tabs { width: 100%; }
  .os-tab { flex: 1; justify-content: center; }
  .shead { grid-template-columns: 1fr; gap: 12px; }
  .rex-lockup { flex-direction: column; align-items: flex-start; gap: 14px; }
  .shead-idx { padding-top: 0; }
  .cursor { display: none; }
  /* one column: the copy spans the full measure, so the scrim must too */
  :root { --gl-opacity: 0.62; }
  .hero::before {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 35%, transparent) 0%,
      color-mix(in srgb, var(--paper) 78%, transparent) 42%,
      color-mix(in srgb, var(--paper) 94%, transparent) 72%,
      var(--paper) 100%);
  }
}

@media (max-width: 480px) {
  .toolwall { grid-template-columns: 1fr 1fr; }
  .contact-grid { grid-template-columns: 1fr; }
}

/* ================= reduced motion / no-JS / print ================= */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  html { scroll-behavior: auto; }
  .reveal, [data-split].split .sp { opacity: 1 !important; transform: none !important; }
  .grain, .cursor { display: none; }
}

@media print {
  #gl, .grain, .cursor, .preloader, .curtain, .scroll-progress, .site-nav { display: none !important; }
  .reveal, [data-split].split .sp { opacity: 1 !important; transform: none !important; }
  body { background: #fff; color: #000; }
}
