/* ==========================================================================
 * GHZYLAB — THE LAB LOGBOOK
 * ==========================================================================
 * One idea runs through this whole file: the site is a lab notebook that can
 * be read on two different instruments.
 *
 *   PAPER    — graph paper on a warm desk. Ink, blue pen, red-pen annotation,
 *              rubber stamps, tape. This is the default.
 *   PHOSPHOR — the same notebook read off a two-trace CRT. The paper grid
 *              becomes an oscilloscope graticule, ink becomes green phosphor,
 *              the red pen becomes an amber second trace.
 *
 * They are NOT one palette inverted. They are two instruments showing the
 * same data, so a few structural things (grid, texture, glow) legitimately
 * differ between them — see `[data-world]` blocks near the bottom.
 *
 * RULE: every colour gets exactly one definition per world, as a token.
 * Never write `[data-world="phosphor"] .thing { color: ... }` for something a
 * state class also colours — that descendant selector outranks `.thing.on`
 * and you get a half-applied style that looks like a missing colour.
 * (Learned the hard way in the arcade; see game/CLAUDE.md.)
 * ====================================================================== */

/* ------------------------------------------------------------------ world: PAPER */

:root {
  /* --- surface ---------------------------------------------------------- */
  --paper:        #f4efe3;   /* the sheet itself */
  --paper-deep:   #e9e1d0;   /* under-sheet, gutters, insets */
  --paper-raised: #fbf8f1;   /* cards sitting above the sheet */
  --desk:         #ded4bf;   /* the desk the sheet lies on */

  /* --- marks on the page ----------------------------------------------- */
  --ink:          #241f1a;   /* pencil/print body text */
  /* Secondary text. Darker than it looks like it needs to be, on purpose: the
     .lab-vignette overlay is FIXED, so prose scrolls through its darkest band
     and the effective background bottoms out at #d9d4ca. At the old #6d6355
     that put captions and archive descriptions at 3.99:1 — below WCAG AA —
     purely because of decoration. Solved against that floor instead. */
  --ink-soft:     #5f5547;
  /* DECORATION ONLY — never text that carries information. At 50% alpha this
     lands near 2:1 on the vignette's darkest band, far below WCAG AA, so it is
     reserved for hairlines, disabled states, and aria-hidden glyphs (the ·
     separators, the scroll cue, the finding bullets). Anything a reader has to
     actually READ uses --ink-soft, which is solved against that same floor. */
  --ink-faint:    #a1968380;
  --pen:          #1b46c2;   /* blue biro — headings, links */
  --pen-deep:     #143391;
  --red:          #cf2f22;   /* red pen — annotations, margin rule, warnings */
  --stamp:        #17795e;   /* green rubber stamp — confirmations */
  /* Ink printed ON the green fill (.stamp--filled), NOT the green itself. It
     was set to #17795e — the same value as --stamp — which painted green text
     on a green box: every filled stamp rendered as a solid green rectangle
     with the word invisible inside it. PHOSPHOR was fine because its ink is
     dark. Near-white on this green measures 5.03:1, clearing WCAG AA. Unlike
     --ink-soft this needs no vignette allowance: the fill is opaque and sits
     above .lab-vignette (z-index -3), so the overlay never tints it. */
  --stamp-ink:    #fbf8f1;
  --highlight:    #ffe86b;   /* highlighter */
  --highlight-a:  rgba(255, 232, 107, 0.42);

  /* --- the grid --------------------------------------------------------- */
  --grid-minor:   rgba(96, 122, 148, 0.13);
  --grid-major:   rgba(96, 122, 148, 0.22);
  --grid-size:    26px;      /* major cell; minor is a quarter of this */

  /* --- structure -------------------------------------------------------- */
  --rule:         rgba(36, 31, 26, 0.14);   /* borders */
  --rule-strong:  rgba(36, 31, 26, 0.30);
  --shadow-lift:  0 2px 0 rgba(36, 31, 26, .10), 0 10px 22px -12px rgba(36, 31, 26, .34);
  --shadow-deep:  0 3px 0 rgba(36, 31, 26, .12), 0 26px 48px -20px rgba(36, 31, 26, .42);
  --tape:         rgba(255, 246, 214, 0.72);

  /* --- type ------------------------------------------------------------- */
  --f-display: 'Fraunces', Georgia, serif;      /* wonky editorial display */
  --f-prose:   'Newsreader', Georgia, serif;    /* long-form reading */
  --f-hand:    'Caveat', 'Bradley Hand', cursive;
  --f-mono:    'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* --- motion ----------------------------------------------------------- */
  --ease-out:  cubic-bezier(.22, 1, .36, 1);
  --ease-back: cubic-bezier(.34, 1.56, .64, 1);

  /* --- device insets ---------------------------------------------------- */
  --sat: env(safe-area-inset-top, 0px);
  --sab: env(safe-area-inset-bottom, 0px);

  /* --- effects that only one world uses; neutral here ------------------- */
  --bloom: none;
  --scan-opacity: 0;
  --trace-weight: 0;
}

/* --------------------------------------------------------- world: PHOSPHOR */

[data-world="phosphor"] {
  --paper:        #05090a;
  --paper-deep:   #020506;
  --paper-raised: #0b1416;
  --desk:         #010303;

  /* Two traces, like a dual-beam scope: green primary, amber secondary. */
  --ink:          #b8f5d2;
  --ink-soft:     #5f9d7d;
  --ink-faint:    #2c5c4780;
  --pen:          #52f2a0;   /* the bright trace */
  --pen-deep:     #2ad07f;
  --red:          #ffab3d;   /* amber second trace */
  --stamp:        #52f2a0;
  --stamp-ink:    #041009;
  --highlight:    #2ad07f;
  --highlight-a:  rgba(42, 208, 127, 0.20);

  --grid-minor:   rgba(82, 242, 160, 0.09);
  --grid-major:   rgba(82, 242, 160, 0.17);

  --rule:         rgba(82, 242, 160, 0.20);
  --rule-strong:  rgba(82, 242, 160, 0.42);
  --shadow-lift:  0 0 0 1px rgba(82, 242, 160, .10), 0 8px 26px -14px rgba(82, 242, 160, .34);
  --shadow-deep:  0 0 0 1px rgba(82, 242, 160, .14), 0 22px 52px -22px rgba(82, 242, 160, .40);
  --tape:         rgba(82, 242, 160, 0.10);

  --bloom: 0 0 12px rgba(82, 242, 160, .34);
  --scan-opacity: 1;
  --trace-weight: 1;
}

/* ============================================================ base + reset */

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

/* THE PAPER COLOUR LIVES HERE, ON THE ROOT ELEMENT — and that is load-bearing.
 *
 * CSS paints in this order inside the root stacking context:
 *   1. the root element's background (the "canvas")
 *   2. positioned descendants with NEGATIVE z-index
 *   3. in-flow, non-positioned block backgrounds  <-- `body` lands here
 *   4. the rest of the content
 *
 * So an opaque `background-color` on `body` paints OVER every negative-z-index
 * layer. This site has four of them (.lab-grid, .lab-vignette, #trace-canvas,
 * .lab-scan) and an earlier version of this file gave `body` the paper colour —
 * which silently hid the graph paper, the vignette, the oscilloscope trace and
 * the CRT scanlines all at once. Everything rendered; nothing was visible.
 *
 * Keep the paper on `html` and `body` transparent. */
html {
  scroll-behavior: smooth;
  background-color: var(--paper);
  transition: background-color .5s var(--ease-out);
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--f-prose);
  font-size: 1.02rem;
  line-height: 1.65;
  color: var(--ink);
  /* Transparent on purpose — see the note on `html` above. */
  background-color: transparent;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  transition: color .5s var(--ease-out);
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--f-display);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.015em;
  /* Fraunces ships a `wonk` axis and optical sizing. Turning wonk on is what
     stops headings reading like every other portfolio's Inter/Outfit. */
  font-variation-settings: 'SOFT' 40, 'WONK' 1, 'opsz' 40;
  text-wrap: balance;
}

p { margin: 0 0 1.15em; }

a { color: var(--pen); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--pen-deep); }

button, input, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; }

::selection { background: var(--highlight-a); color: var(--ink); }

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

/* ====================================================== the graph-paper grid
 * Two nested repeating gradients: a light minor cell and a heavier major
 * every 4th line. In PHOSPHOR the same geometry reads as a scope graticule.
 * Fixed attachment so it behaves like a surface the content lies on, not a
 * pattern that scrolls with it.
 * ====================================================================== */

.lab-grid {
  position: fixed;
  inset: 0;
  z-index: -4;
  pointer-events: none;
  /* No background-color: `html` paints the paper, this element only rules it. */
  background-image:
    linear-gradient(var(--grid-major) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-major) 1px, transparent 1px),
    linear-gradient(var(--grid-minor) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-minor) 1px, transparent 1px);
  background-size:
    calc(var(--grid-size) * 4) calc(var(--grid-size) * 4),
    calc(var(--grid-size) * 4) calc(var(--grid-size) * 4),
    var(--grid-size) var(--grid-size),
    var(--grid-size) var(--grid-size);
}

/* Vignette: on paper it's the shadow of the desk lamp falling off at the
   edges; on phosphor it's the curvature of the tube. */
.lab-vignette {
  position: fixed;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  background: radial-gradient(125% 95% at 50% 0%,
              transparent 42%, rgba(36, 31, 26, .13) 100%);
}
[data-world="phosphor"] .lab-vignette {
  background:
    radial-gradient(115% 88% at 50% 46%, transparent 40%, rgba(0, 0, 0, .82) 100%);
}

/* Scanlines — phosphor only. `--scan-opacity` is 0 on paper so the element
   is inert rather than conditionally rendered. */
.lab-scan {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: var(--scan-opacity);
  background: repeating-linear-gradient(
    180deg,
    rgba(0, 0, 0, .22) 0px,
    rgba(0, 0, 0, .22) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: multiply;
  transition: opacity .5s var(--ease-out);
}

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

/* --- the sheet: a page of the logbook ---------------------------------- */

.sheet {
  position: relative;
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: 2px;
  box-shadow: var(--shadow-lift);
  padding: clamp(22px, 4vw, 38px);
}

/* The red margin rule, exactly as in a school exercise book. */
.sheet--ruled { padding-left: clamp(46px, 8vw, 74px); }
.sheet--ruled::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  left: clamp(32px, 6vw, 54px);
  width: 1px;
  background: var(--red);
  opacity: .34;
}

/* --- tape: holds things onto the page ---------------------------------- */

.tape {
  position: absolute;
  width: 96px; height: 26px;
  background: var(--tape);
  border-left: 1px solid rgba(255,255,255,.28);
  border-right: 1px solid rgba(0,0,0,.05);
  box-shadow: 0 1px 3px rgba(36,31,26,.16);
  pointer-events: none;
}
.tape--tl { top: -13px; left: 22px; transform: rotate(-4deg); }
.tape--tr { top: -13px; right: 22px; transform: rotate(3.5deg); }
[data-world="phosphor"] .tape {
  border-left-color: rgba(82,242,160,.22);
  border-right-color: transparent;
  box-shadow: none;
}

/* --- section heading: a stamped, numbered logbook entry ---------------- */

.entry-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 6px;
}
.entry-num {
  flex: none;
  font-family: var(--f-mono);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .14em;
  color: var(--red);
  padding-top: .5em;
}
.entry-head h2 {
  font-size: clamp(1.55rem, 4.4vw, 2.1rem);
}
.entry-sub {
  font-family: var(--f-hand);
  font-size: 1.32rem;
  line-height: 1.25;
  color: var(--red);
  margin: 0 0 22px;
  transform: rotate(-.5deg);
  transform-origin: left center;
}

/* --- rubber stamp ------------------------------------------------------ */

.stamp {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px 4px;
  border: 2px solid var(--stamp);
  border-radius: 3px;
  color: var(--stamp);
  font-family: var(--f-mono);
  font-size: .64rem;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  transform: rotate(-2.2deg);
  /* NO opacity here. It used to be .88, for a faded-rubber-stamp look, and it
     silently undid the contrast work: opacity composites the WHOLE element,
     so the green fill rendered as #32886f rather than #17795e and the ink on
     it dropped from 5.03:1 to 4.05:1 — under WCAG AA, at .64rem. Token math
     could not see this; only a screenshot of the rendered pixels could. If the
     stamp ever needs softening again, soften the TOKEN, not the element, so
     the number stays honest. */
}
.stamp--red { border-color: var(--red); color: var(--red); }
.stamp--filled { background: var(--stamp); color: var(--stamp-ink); }

/* --- handwriting ------------------------------------------------------- */

.hand {
  font-family: var(--f-hand);
  font-size: 1.45rem;
  line-height: 1.22;
  color: var(--red);
}
.hand--ink { color: var(--pen); }
.hand--pencil { color: var(--ink-soft); }

/* In PHOSPHOR handwriting can't be ink — a pen plotter drew it. Give it the
   trace glow so it reads as drawn rather than printed. */
[data-world="phosphor"] .hand { text-shadow: var(--bloom); }

/* --- margin note: annotation in the gutter ----------------------------- */

.note-anchor { position: relative; }
.margin-note {
  font-family: var(--f-hand);
  font-size: 1.24rem;
  line-height: 1.24;
  color: var(--red);
}
@media (min-width: 1180px) {
  .margin-note {
    position: absolute;
    width: 168px;
    right: -196px;
    top: 0;
    transform: rotate(2.4deg);
  }
  .margin-note--left {
    right: auto;
    left: -196px;
    transform: rotate(-2.6deg);
  }
}
@media (max-width: 1179px) {
  .margin-note {
    display: block;
    margin: 12px 0 18px;
    padding-left: 12px;
    border-left: 2px solid var(--red);
    transform: none;
  }
}

/* --- mono label / data ------------------------------------------------- */

.mono { font-family: var(--f-mono); font-variant-numeric: tabular-nums; }
.label {
  font-family: var(--f-mono);
  font-size: .64rem;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* --- buttons ----------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 12px 20px;
  border: 1px solid var(--ink);
  border-radius: 2px;
  background: var(--paper-raised);
  color: var(--ink);
  font-family: var(--f-mono);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-decoration: none;
  box-shadow: 3px 3px 0 var(--ink);
  transition: transform .13s var(--ease-out), box-shadow .13s var(--ease-out),
              background .18s, color .18s;
}
.btn:hover {
  transform: translate(-1px, -1px);
  box-shadow: 5px 5px 0 var(--ink);
  color: var(--ink);
}
.btn:active {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--ink);
}
.btn--pen {
  border-color: var(--pen);
  box-shadow: 3px 3px 0 var(--pen);
  color: var(--pen);
}
.btn--pen:hover { box-shadow: 5px 5px 0 var(--pen); color: var(--pen); }
.btn--pen:active { box-shadow: 1px 1px 0 var(--pen); }
.btn--solid {
  background: var(--pen);
  border-color: var(--pen);
  color: var(--paper-raised);
  box-shadow: 3px 3px 0 var(--pen-deep);
}
.btn--solid:hover { color: var(--paper-raised); box-shadow: 5px 5px 0 var(--pen-deep); }
.btn--solid:active { box-shadow: 1px 1px 0 var(--pen-deep); }
.btn--quiet {
  border-style: dashed;
  border-color: var(--rule-strong);
  box-shadow: none;
  color: var(--ink-soft);
}
.btn--quiet:hover { box-shadow: none; transform: none; color: var(--ink); border-color: var(--ink); }
.btn--quiet:active { transform: translateY(1px); box-shadow: none; }

/* --- utility ----------------------------------------------------------- */

.vh {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.wrap {
  width: 100%;
  max-width: 940px;
  margin: 0 auto;
  padding: 0 clamp(16px, 4vw, 28px);
}

/* ========================================================== reduced motion */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .lab-scan { display: none; }
}
