/* ============================================================
   scratch.md · minimal framework
   tokens · reset · base · layout · components
   Nothing section-specific. Nothing one-off.
   Add to this file only when a rule is used on 2+ pages.
============================================================ */

@layer tokens, reset, base, layout, components;

/* ---- TOKENS ---------------------------------------------- */
@layer tokens {
  :root {
    /* color · Mnemosyne ivory + warm ink */
    --paper: #f6f3ec;
    --ink: #181410;
    --ink-soft: #6e6655;
    --accent: #2868ff;
    --rule-color: var(--ink);

    /* color · accent states for panels, inline highlights, and the
       comparison-grid columns. Used inside color-mix() for variants. */
    --info: #a4dded; /* blue · info panels, neutral surfaces */
    --mark: #ffff66; /* highlighter yellow · hover, approve flash */
    --caution: #e08585; /* rose · caution panels, "without scratch" */
    --ship: #f5c84a; /* amber · "with scratch", ship/approve */
    --attention: #ffb39c; /* peach · attention/warning */

    /* type · serif reserved for H1; mono everywhere else */
    --serif: "Fraunces", Georgia, "Times New Roman", serif;
    --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

    --text-h1: clamp(2.5rem, 5.5vw, 4rem);
    --text-h2: clamp(2rem, 4vw, 3rem);
    --text-h3: 1.0625rem;
    --text-body: 1rem;
    --text-small: 0.8125rem;

    /* grid · the base unit. Every block measurement is a multiple. */
    --grid: 5mm;
    --grid-color: color-mix(in srgb, var(--ink) 3%, var(--paper));

    /* space · all multiples of --grid, so blocks land on grid lines. */
    --space-1: var(--grid); /*  1 cell ·  5mm */
    --space-2: calc(var(--grid) * 2); /*  2 cells · 10mm */
    --space-3: calc(var(--grid) * 4); /*  4 cells · 20mm */
    --space-4: calc(var(--grid) * 6); /*  6 cells · 30mm */
    --space-section: calc(var(--grid) * 10); /* 10 cells · 50mm */

    /* layout · also grid-aligned. */
    --page-max: calc(var(--grid) * 72); /* 72 cells · 360mm */
    --column-max: calc(var(--grid) * 48); /* 48 cells · 240mm */
    --prose-max: 96ch; /* text-length comfort, not grid */

    /* radius · three sizes. Consolidated from 2/3/4/6/999px to sm/md/pill. */
    --radius-sm: 4px;
    --radius-md: 6px;
    --radius-pill: 999px;
  }
}

/* ---- RESET ----------------------------------------------- */
@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }
  html,
  body {
    margin: 0;
    padding: 0;
  }
  html {
    overflow-x: clip;
  } /* canvas elements that break out won't trigger horizontal scroll */
  body {
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }
  img,
  svg,
  video {
    display: block;
    max-width: 100%;
    height: auto;
  }
  button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
  }
  a {
    color: inherit;
    text-decoration: inherit;
  }
  ul,
  ol {
    margin: 0;
    padding: 0;
    list-style: none;
  }
  hr {
    border: 0;
    border-top: 1px solid var(--rule-color);
    margin: 0;
  }
}

/* ---- BASE ------------------------------------------------ */
@layer base {
  :where(html) {
    font-family: var(--mono);
    font-size: 16px;
    line-height: 1.55;
    color: var(--ink);
    background: var(--paper);
    background-image:
      linear-gradient(to right, var(--grid-color) 1px, transparent 1px),
      linear-gradient(to bottom, var(--grid-color) 1px, transparent 1px);
    background-size: var(--grid) var(--grid);
  }

  :where(h1) {
    font-family: var(--serif);
    font-weight: 400;
    font-size: var(--text-h1);
    line-height: 1.05;
    letter-spacing: -0.015em;
    margin: 0;
  }

  :where(h2) {
    font-family: var(--serif);
    font-weight: 400;
    font-size: var(--text-h2);
    line-height: 1.05;
    letter-spacing: -0.015em;
    margin: 0;
  }

  :where(h3, h4) {
    font-family: var(--mono);
    font-weight: 500;
    font-size: var(--text-h3);
    line-height: 1.3;
    margin: 0;
  }

  :where(p) {
    margin: 0;
    max-width: var(--prose-max);
  }

  /* Markdown wraps single-line custom elements in <p>. Make those
     wrappers transparent so block-level customs aren't constrained. */
  :where(p):has(
    > canvas-frame,
    > section-label,
    > cycle-arrow,
    > cycle-mark,
    > skill-path
  ) {
    display: contents;
  }

  /* Custom elements default to inline; force block where needed. The
     homepage uses markdown-it-attrs to wrap H2 sections in named
     containers (`## … {loop-section}` → `<loop-section>`). */
  :where(
    hero-section,
    demo-section,
    loop-section,
    why-section,
    founder-section,
    start-section
  ) {
    display: block;
  }

  /* Prose-only link styling — nav, headers, buttons stay unstyled. */
  :where(p, li, dd, lede) a {
    color: var(--accent);
    border-bottom: 1px solid currentColor;
    transition: opacity 0.15s;
  }
  :where(p, li, dd, lede) a:hover {
    opacity: 0.6;
  }

  :where(strong) {
    font-weight: 600;
    color: var(--ink);
  }
  :where(em) {
    font-style: italic;
  }
  :where(small) {
    font-size: var(--text-small);
    color: var(--ink-soft);
  }
}

/* ---- LAYOUT ---------------------------------------------- */
@layer layout {
  /* Body is the page grid. Every page: hero (col 1, row 1) + nav (col 2,
     row 1) + main (full width, row 2+) + footer (full width, last row). */
  :where(body) {
    display: grid;
    /* minmax(0, ...) instead of plain 2fr/1fr · without it, a wide child
       (the comparison-grid table, an unbreakable URL, etc.) grows the
       track to its min-content size and blows out the page even though
       html has overflow-x: clip. minmax(0, ...) caps the track at the
       available width and lets inner overflow:auto containers actually
       scroll. */
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    column-gap: var(--space-3);
    max-width: var(--page-max);
    margin: 0 auto;
    padding: var(--space-4) var(--space-3);
  }

  :where(body) > nav {
    grid-column: 2;
    grid-row: 1;
    align-self: start;
  }
  :where(body) > hero-section {
    grid-column: 1;
    grid-row: 1;
  }
  :where(body) > main {
    grid-column: 1 / -1;
  }
  :where(body) > footer {
    grid-column: 1 / -1;
  }

  /* Hero summary · the page's metadata card. On detail pages it holds
     lede + article-meta. On list pages it holds lede + skill-counter (in
     a 2-col grid). The H1 stays on paper as the dominant element; the
     panel reads as a metadata card; the body begins fresh on paper
     after it. Same sky-blue as the comparison-grid on the homepage. */
  body:has(hero-summary) > main {
    margin-top: var(--space-3);
  }
  hero-summary {
    display: block;
    margin-top: var(--space-2);
    padding: var(--space-2) var(--space-3);
    background: color-mix(in srgb, var(--info) 55%, transparent);
    border-radius: var(--radius-md);
  }
  /* When a counter is in the panel, lay them out side-by-side: lede
     flexes wide on the left, counter takes its natural width on the
     right. */
  hero-summary:has(> skill-counter) {
    display: grid;
    grid-template-columns: 1fr auto;
    column-gap: var(--space-3);
    align-items: start;
  }
  hero-summary > lede {
    color: var(--ink);
  }
  hero-summary > article-meta {
    margin-top: var(--space-1);
    color: color-mix(in srgb, var(--ink) 70%, transparent);
  }

  /* In-section rhythm · 10mm above any block that follows another block.
     Order matters: this comes BEFORE `main > * + *` so the section rule
     wins for top-level children of <main>. Inside <article> (and other
     non-main containers) only this rule applies. */
  :where(* + h1, * + h2, * + h3, * + p, * + lede, * + pre, * + ul, * + ol) {
    margin-top: var(--space-2);
  }
  :where(li + li) {
    margin-top: var(--space-1);
  }

  /* Section rhythm — top-level blocks in <main> get 50mm. */
  :where(main > * + *) {
    margin-top: var(--space-section);
  }
}

/* ---- COMPONENTS ------------------------------------------ */
@layer components {
  section-label {
    display: block;
    margin-top: var(--space-1);
    font-family: var(--mono);
    font-size: var(--text-small);
    color: var(--ink-soft);
    letter-spacing: 0.04em;
    text-transform: lowercase;
  }
  section-label::before {
    content: "// ";
  }

  lede {
    display: block;
    font-family: var(--mono);
    font-size: var(--text-body);
    line-height: 1.65;
    color: var(--ink-soft);
    max-width: var(--prose-max);
  }

  pre {
    font-family: var(--mono);
    font-size: var(--text-small);
    line-height: 1.6;
    color: var(--paper);
    background: var(--ink);
    padding: var(--space-3);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    margin: 0;
  }

  /* Copy block · wraps a <pre> with a "copy" button overlay top-right.
     Symmetric 20mm above/below — heavier breathing room than the 10mm
     content rhythm, matching the block's visual weight on the page. */
  copy-block {
    position: relative;
    display: block;
    margin-top: var(--space-3);
    margin-bottom: var(--space-3);
    border-radius: var(--radius-md);
    overflow: hidden;
  }
  .copy-button {
    position: absolute;
    top: var(--space-1);
    right: var(--space-1);
    z-index: 2;
    padding: 0.4em 0.9em;
    background: var(--paper);
    color: var(--ink);
    border: 1px solid color-mix(in srgb, var(--paper) 65%, transparent);
    border-radius: var(--radius-sm);
    font-family: var(--mono);
    font-size: var(--text-small);
    font-weight: 600;
    cursor: pointer;
    transition: opacity 0.15s;
  }
  .copy-button:hover {
    opacity: 0.85;
  }
  .copy-button[data-copied] {
    background: color-mix(in srgb, var(--mark) 65%, var(--paper));
  }

  code {
    font-family: var(--mono);
    font-size: 0.95em;
    background: color-mix(in srgb, var(--ink) 8%, transparent);
    padding: 0.1em 0.35em;
    border-radius: var(--radius-sm);
  }
  pre code {
    background: transparent;
    padding: 0;
    font-size: inherit;
  }

  /* Buttons · restrained. Personality lives elsewhere on the page. */
  :where(button, .button, a.button) {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
    padding: 0.75em 1.5em;
    color: var(--paper);
    background: var(--ink);
    border-radius: var(--radius-md);
    transition: opacity 0.15s;
  }
  :where(button:hover, .button:hover) {
    opacity: 0.85;
  }

  /* Hero · just text. H1 + ledes + CTAs stack vertically. */
  hero-section h1 {
    max-width: calc(var(--grid) * 48);
  }

  /* Trust badge at the foot of the hero. Small, subdued. */
  hero-trust {
    display: block;
    margin-top: var(--space-3);
    font-family: var(--mono);
    font-size: var(--text-small);
    color: var(--ink-soft);
  }
  hero-trust a {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    color: inherit;
    transition: opacity 0.15s;
  }
  hero-trust a:hover {
    opacity: 0.6;
  }
  hero-trust svg {
    color: #f26522;
  } /* YC orange */

  /* Site nav · yellow block, mono, compact. */
  nav {
    background: color-mix(in srgb, var(--mark) 65%, transparent);
    color: var(--ink);
    padding: var(--space-2);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    font-family: var(--mono);
    font-size: var(--text-small);
  }
  nav .brand {
    display: flex;
    align-items: center;
    gap: 0.4em;
  }
  nav .brand img {
    height: 22px;
    width: auto;
  }
  nav a {
    display: block;
    color: var(--ink);
    transition: opacity 0.15s;
  }
  nav a:hover {
    opacity: 0.6;
  }
  nav a[data-key]::before {
    content: "[" attr(data-key) "] ";
    text-transform: uppercase;
    opacity: 0.5;
  }

  /* ===================================================================
     Loop diagram · 3x3 grid. Four corners are stations on a clockwise
     cycle; the four cardinal cells are arrows; the center is the brand
     mark spinning. All four station panels share one element name
     (loop-panel) and one head/foot/grid/card pattern; variants are
     driven by data-realm and data-style attributes.
  =================================================================== */
  loop-diagram {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    grid-template-areas:
      "tl tm tr"
      "ml mm mr"
      "bl bm br";
    gap: var(--space-2);
    margin-top: var(--space-3);
    align-items: stretch;
  }
  loop-panel[data-corner="tl"] {
    grid-area: tl;
  }
  loop-panel[data-corner="tr"] {
    grid-area: tr;
  }
  loop-panel[data-corner="bl"] {
    grid-area: bl;
  }
  loop-panel[data-corner="br"] {
    grid-area: br;
  }
  cycle-arrow[data-pos="tm"] {
    grid-area: tm;
  }
  cycle-arrow[data-pos="ml"] {
    grid-area: ml;
  }
  cycle-arrow[data-pos="mr"] {
    grid-area: mr;
  }
  cycle-arrow[data-pos="bm"] {
    grid-area: bm;
  }
  cycle-mark {
    grid-area: mm;
  }

  /* Loop panel · the unified corner block. data-realm sets background
     tint (cloud = sky blue, laptop = paper + device frame); data-style
     can override (review = yellow). */
  loop-panel {
    display: block;
    padding: var(--space-2);
    border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
    border-radius: var(--radius-md);
    background: var(--paper);
  }
  loop-panel[data-realm="cloud"] {
    background: color-mix(in srgb, var(--info) 22%, var(--paper));
    border-color: color-mix(in srgb, var(--info) 55%, transparent);
  }
  loop-panel[data-realm="laptop"] {
    background: color-mix(in srgb, var(--attention) 22%, var(--paper));
    border-color: color-mix(in srgb, var(--attention) 55%, transparent);
  }
  loop-panel[data-style="review"] {
    padding: var(--space-2) 0 0;
    background: color-mix(in srgb, var(--mark) 22%, var(--paper));
    border-color: color-mix(in srgb, var(--mark) 60%, transparent);
    overflow: hidden;
  }
  loop-panel[data-style="caution"] {
    padding: var(--space-2) 0 0;
    background: color-mix(in srgb, var(--caution) 16%, var(--paper));
    border-style: dashed;
    border-color: color-mix(in srgb, var(--caution) 55%, transparent);
    overflow: hidden;
  }

  /* Panel head · used by every station. Inline <small> is the muted
     subtitle line. */
  panel-head {
    display: block;
    margin-bottom: var(--space-1);
    font-family: var(--mono);
    font-size: var(--text-h3);
    font-weight: 600;
    color: var(--ink);
  }
  panel-head small {
    display: block;
    margin-top: 0.15em;
    font-weight: 400;
    font-size: 0.85em;
    color: var(--ink-soft);
  }
  loop-panel[data-style="review"] panel-head,
  loop-panel[data-style="caution"] panel-head {
    padding: 0 var(--space-2);
  }
  loop-panel[data-style="review"] panel-foot,
  loop-panel[data-style="caution"] panel-foot {
    margin-top: var(--space-1);
    padding: var(--space-1) var(--space-2) 0;
  }

  /* Contrast grid · side-by-side comparison panels (used in why-files). */
  contrast-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-2);
    margin-top: var(--space-3);
  }

  /* Panel foot · the muted caption under a panel's content */
  panel-foot {
    display: block;
    margin-top: var(--space-1);
    font-family: var(--mono);
    font-size: var(--text-small);
    font-style: italic;
    color: var(--ink-soft);
  }

  /* Step number · oversized circled glyph leading each panel head */
  step-num {
    display: inline-block;
    margin-right: 0.2em;
    font-family: var(--mono);
    font-weight: 700;
    font-size: 2.4rem;
    line-height: 1;
    color: var(--ink);
    vertical-align: -0.25em;
  }

  /* Logo grid · used for sources, AI tools, and publish destinations */
  logo-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }
  logo-card {
    display: flex;
    align-items: center;
    gap: 0.6em;
    padding: 0.4em 0.6em;
    background: color-mix(in srgb, var(--paper) 70%, transparent);
    border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
    border-radius: var(--radius-sm);
    font-family: var(--mono);
    font-size: var(--text-small);
  }
  logo-card img {
    width: 18px;
    height: 18px;
    object-fit: contain;
    flex-shrink: 0;
  }
  logo-name {
    display: block;
    white-space: nowrap;
  }

  /* Cycle arrow · cardinal connector between corner stations. Direction
     set per-instance via data-dir. */
  cycle-arrow {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.2em;
    padding: var(--space-1) 0.4em;
    text-align: center;
  }
  cycle-arrow::before {
    content: "↓";
    font-family: var(--mono);
    font-weight: 700;
    font-size: 3rem;
    line-height: 1;
    color: var(--ink);
  }
  cycle-arrow[data-dir="right"]::before {
    content: "→";
  }
  cycle-arrow[data-dir="left"]::before {
    content: "←";
  }
  cycle-arrow[data-dir="up"]::before {
    content: "↑";
  }
  arrow-label {
    display: block;
    max-width: 18ch;
    font-family: var(--mono);
    font-size: var(--text-small);
    font-weight: 600;
    line-height: 1.3;
    color: var(--ink);
  }
  arrow-sub {
    display: block;
    max-width: 18ch;
    font-family: var(--mono);
    font-size: var(--text-small);
    font-style: italic;
    line-height: 1.3;
    color: var(--ink-soft);
  }

  /* Cycle mark · Scratch chick spinning at the cycle's hub */
  cycle-mark {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5em;
    padding: var(--space-1);
  }
  cycle-mark img {
    width: 160px;
    height: 160px;
    animation: cycle-rotate 6s linear infinite;
  }
  mark-label {
    display: block;
    font-family: var(--mono);
    font-size: var(--text-small);
    font-style: italic;
    color: var(--ink-soft);
    letter-spacing: 0.04em;
    text-transform: lowercase;
  }
  @keyframes cycle-rotate {
    from {
      transform: scaleX(-1) rotate(0deg);
    }
    to {
      transform: scaleX(-1) rotate(-360deg);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    cycle-mark img {
      animation: none;
    }
  }

  /* Review row · the line items inside the review panel */
  review-row {
    display: block;
    padding: 0.4em var(--space-2);
    font-family: var(--mono);
    font-size: var(--text-small);
    line-height: 1.5;
    color: var(--ink);
    border-top: 1px dashed color-mix(in srgb, var(--ink) 10%, transparent);
  }
  review-row:first-of-type {
    margin-top: var(--space-1);
    border-top-color: color-mix(in srgb, var(--ink) 14%, transparent);
  }
  review-row[data-state="reject"] {
    color: color-mix(in srgb, var(--ink) 70%, transparent);
  }
  review-row[data-state="more"] {
    color: color-mix(in srgb, var(--ink) 45%, transparent);
    font-style: italic;
  }

  /* Validator note · optional-feature annotation below the diagram */
  validator-note {
    display: flex;
    align-items: center;
    gap: 0.7em;
    margin-top: var(--space-2);
    padding: 0.7em 0.9em;
    background: color-mix(in srgb, var(--info) 12%, var(--paper));
    border: 1px dashed color-mix(in srgb, var(--info) 50%, transparent);
    border-radius: var(--radius-sm);
    font-family: var(--mono);
    font-size: var(--text-small);
    line-height: 1.5;
    color: var(--ink-soft);
  }
  validator-note .shield-icon {
    flex-shrink: 0;
    color: color-mix(in srgb, var(--ink) 70%, transparent);
  }
  validator-note strong {
    color: var(--ink);
  }

  /* Highlighter · warm marker stroke on a key word in H1. */
  :where(mark) {
    background: var(--ship);
    padding: 0 0.12em;
    color: inherit;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
  }

  /* Second hero lede · marginalia note. Serif italic for typographic
     emphasis, breaks the mono pattern deliberately. */
  hero-section lede + lede {
    font-family: var(--serif);
    font-style: italic;
    font-size: var(--text-h3);
    color: var(--ink);
  }

  hero-ctas {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-2);
  }
  hero-ctas a:not(.button) {
    color: var(--ink-soft);
    transition: color 0.15s;
  }
  hero-ctas a:not(.button):hover {
    color: var(--ink);
  }

  /* Canvas frame · reusable container for video / graphic / screenshot /
     placeholder. The Hobonichi layer happens here. */
  canvas-frame {
    display: block;
    position: relative;
    background: var(--paper);
    border: 1px solid color-mix(in srgb, var(--ink) 14%, var(--paper));
    border-radius: var(--radius-md);
    padding: var(--space-3);
    box-shadow:
      0 1px 2px rgba(0, 0, 0, 0.05),
      0 10px 28px rgba(0, 0, 0, 0.06);
  }

  /* Placeholder variant · 16:9 blue panel, centered label. */
  canvas-frame[data-placeholder] {
    display: grid;
    place-items: center;
    aspect-ratio: 16 / 9;
    background: color-mix(in srgb, var(--info) 65%, transparent);
    border: none;
    color: var(--ink);
    font-family: var(--mono);
    font-size: var(--text-h2);
    letter-spacing: 0.04em;
    text-transform: lowercase;
    padding: 0;
    box-shadow: none;
  }

  /* Sticker label · uppercase mono badge, slightly tilted. */
  canvas-frame[data-label]::before {
    content: attr(data-label);
    position: absolute;
    top: calc(var(--space-1) * -1);
    left: var(--space-2);
    padding: 0.3em 0.8em;
    font-family: var(--mono);
    font-size: var(--text-small);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--paper);
    background: var(--ink);
    border-radius: var(--radius-sm);
    transform: rotate(-2deg);
  }

  /* Sitewide footer · structural + soft hierarchy. */
  footer {
    padding: var(--space-3) 0;
    font-size: var(--text-small);
    color: var(--ink-soft);
    border-top: 2px dashed color-mix(in srgb, var(--ship) 60%, transparent);
    margin-top: var(--space-4);
  }

  footer-top {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-3);
  }

  footer-col {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
  }
  footer h4 {
    font-size: var(--text-small);
    color: var(--ink);
    margin: 0 0 var(--space-1);
  }
  footer a {
    color: var(--ink);
    transition: opacity 0.15s;
  }
  footer a:hover {
    opacity: 0.6;
  }

  footer-badge {
    display: block;
    margin-top: var(--space-3);
  }
  footer-badge a {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    color: var(--ink-soft);
  }

  footer > span {
    display: block;
    margin-top: var(--space-2);
  }

  /* ===================================================================
     List pages · /skills/, /blog/, /changelog/. The hero is H1 on paper;
     the lede + counter live together inside <hero-summary> (the blue
     metadata panel — see the hero-summary rules near the top of the
     components layer). The skill-counter rules below style the counter
     itself; layout for the lede/counter row lives on hero-summary.
  =================================================================== */
  skill-counter {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    text-align: right;
    font-family: var(--mono);
  }
  skill-counter strong {
    font-family: var(--serif);
    font-size: clamp(2.5rem, 5vw, 4rem);
    font-weight: 400;
    line-height: 1;
    color: var(--ink);
  }
  skill-counter em {
    display: block;
    margin-top: 0.4em;
    font-style: normal;
    font-size: var(--text-small);
    line-height: 1.3;
    color: var(--ink-soft);
    text-transform: lowercase;
    letter-spacing: 0.04em;
  }

  /* Comparison grid · task | without scratch | with scratch table on the
     homepage demo slot. The shape is a dense content-list (Notion / CMS
     bulk-edit feel): numbered rows, tight padding, solid hairline rules,
     "with scratch" column gets a highlighter wash like a copy editor's
     marker pass. Sky-blue panel acts as the desk under the document. */
  comparison-grid {
    display: block;
    overflow-x: auto;
    background: color-mix(in srgb, var(--info) 80%, transparent);
    padding: var(--space-2) var(--space-3) var(--space-3);
    border-radius: var(--radius-md);
  }

  comparison-grid::before {
    display: block;
    font-family: var(--mono);
    font-size: 0.75rem;
    color: color-mix(in srgb, var(--ink) 65%, transparent);
    letter-spacing: 0.04em;
    margin-bottom: var(--space-2);
  }

  comparison-grid table {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--mono);
    font-size: var(--text-small);
    line-height: 1.45;
    counter-reset: row;
  }
  comparison-grid thead th {
    text-align: left;
    padding: 0.6em 0.7em;
    font-weight: 700;
    font-size: 1.25rem;
    color: var(--ink);
    text-transform: lowercase;
    letter-spacing: 0.01em;
    border-bottom: 2px solid color-mix(in srgb, var(--ink) 55%, transparent);
    white-space: nowrap;
  }
  comparison-grid thead th:first-child {
    padding-left: 0;
  }

  comparison-grid tbody tr {
    counter-increment: row;
  }
  comparison-grid tbody td {
    padding: 0.55em 0.7em;
    vertical-align: top;
    border-top: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  }

  /* Task column: numbered, bold, tight max-width so the comparison cells
     get the breathing room. */
  comparison-grid tbody td:first-child {
    padding-left: 0;
    width: 24%;
    font-weight: 600;
    color: var(--ink);
  }
  comparison-grid tbody td:first-child::before {
    content: counter(row, decimal-leading-zero);
    display: inline-block;
    min-width: 2em;
    margin-right: 0.6em;
    color: color-mix(in srgb, var(--ink) 35%, transparent);
    font-weight: 400;
    font-variant-numeric: tabular-nums;
  }

  /* Without scratch · rose-tinted cell (same caution color used on the
     "old way" panel of the why-section). Reads as struck-through-in-red
     without the actual strike; full-ink text stays readable. */
  comparison-grid tbody td:nth-child(2) {
    width: 36%;
    background-color: color-mix(in srgb, var(--caution) 38%, transparent);
    color: var(--ink);
  }

  /* With scratch · full-saturation highlighter wash (same yellow as
     <mark> in the H1). With motion enabled, the wash paints in
     left-to-right like a marker stroke (rules below override the
     background-color and animate it). */
  comparison-grid tbody td:last-child {
    width: 40%;
    background-color: color-mix(in srgb, var(--ship) 65%, transparent);
    color: var(--ink);
    font-weight: 500;
  }

  /* Row hover · brightens both columns. */
  comparison-grid tbody tr:hover td:nth-child(2) {
    background-color: color-mix(in srgb, var(--caution) 55%, transparent);
  }
  comparison-grid tbody tr:hover td:last-child {
    background-color: color-mix(in srgb, var(--ship) 80%, transparent);
  }

  /* Closing line under the table — mono, body size, full-ink color.
     Mirrors the hero lede typography because the content is a workflow
     restatement, not a punchline. Hairline divider above sets it apart
     from the last row. */
  comparison-grid panel-foot {
    display: block;
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
    font-family: var(--mono);
    font-style: normal;
    font-size: var(--text-body);
    line-height: 1.6;
    color: var(--ink);
    max-width: var(--prose-max);
  }

  /* Motion: rows cascade in, then each column's background paints across
     the row as a progress bar — and crucially, the "without" bar crawls
     while the "with" bar snaps. The speed difference IS the message.
     The IntersectionObserver in base.njk sets per-row --row-i and adds
     .in-view when the grid scrolls into view. */
  @media (prefers-reduced-motion: no-preference) {
    /* Rows cascade up — 60ms between rows gives a visible waterfall. */
    comparison-grid tbody tr {
      opacity: 0;
      transform: translateY(8px);
      transition:
        opacity 0.35s ease-out,
        transform 0.35s ease-out;
      transition-delay: calc(var(--row-i, 0) * 60ms);
    }
    comparison-grid.in-view tbody tr {
      opacity: 1;
      transform: translateY(0);
    }

    /* Without scratch · 1.8s LINEAR crawl. Mechanical, laborious.
       Looks like a loading bar that won't finish. */
    comparison-grid tbody td:nth-child(2) {
      background-color: transparent;
      background-image: linear-gradient(
        to right,
        color-mix(in srgb, var(--caution) 38%, transparent),
        color-mix(in srgb, var(--caution) 38%, transparent)
      );
      background-size: 0% 100%;
      background-repeat: no-repeat;
      transition:
        background-size 1.8s linear,
        background-color 0.15s ease-out;
      transition-delay: calc(var(--row-i, 0) * 60ms + 0.15s);
    }
    comparison-grid.in-view tbody td:nth-child(2) {
      background-size: 100% 100%;
    }

    /* With scratch · 0.18s ease-out snap. Feels like one click.
       Finishes ten times faster than the "without" bar in the same row,
       so the eye sees yellow done while pink is still creeping. */
    comparison-grid tbody td:last-child {
      background-color: transparent;
      background-image: linear-gradient(
        to right,
        color-mix(in srgb, var(--ship) 65%, transparent),
        color-mix(in srgb, var(--ship) 65%, transparent)
      );
      background-size: 0% 100%;
      background-repeat: no-repeat;
      transition:
        background-size 0.18s ease-out,
        background-color 0.15s ease-out;
      transition-delay: calc(var(--row-i, 0) * 60ms + 0.15s);
    }
    comparison-grid.in-view tbody td:last-child {
      background-size: 100% 100%;
    }
  }

  /* List stacks · /skills/ and /blog/ both stack clickable cards. Same
     shape, same hover; each card wraps its content in a single anchor. */
  :where(skill-list, blog-feed) {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }
  :where(skill-card, blog-feed article) {
    padding: var(--space-2);
    border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
    border-radius: var(--radius-md);
    background: var(--paper);
    transition:
      background 0.15s,
      border-color 0.15s;
  }
  :where(skill-card, blog-feed article):hover {
    background: color-mix(in srgb, var(--ship) 22%, var(--paper));
    border-color: color-mix(in srgb, var(--ship) 55%, transparent);
  }
  /* Latest item · soft marker-yellow tint + yellow border accent so the
     eye lands on the most recent skill/post first. The list is iterated
     `| reverse`, so :first-child is the newest. */
  :where(skill-list, blog-feed) > :first-child {
    background: color-mix(in srgb, var(--ship) 16%, var(--paper));
    border-color: color-mix(in srgb, var(--ship) 60%, transparent);
  }

  /* Keyboard-nav focus ring · j/k navigation focuses the link inside;
     :has() lifts the visual outline to the card so the whole row reads
     as the focused thing. */
  :where(skill-card, blog-feed article, changelog entry):has(:focus-visible) {
    outline: 2px solid color-mix(in srgb, var(--ship) 85%, transparent);
    outline-offset: 3px;
    border-radius: var(--radius-md);
  }
  :where(skill-card, blog-feed article) > a:focus,
  :where(skill-card, blog-feed article) > a:focus-visible,
  changelog entry desc a:focus,
  changelog entry desc a:focus-visible {
    outline: none;
  }
  :where(skill-card, blog-feed article) > a {
    display: block;
    color: inherit;
  }
  :where(skill-card, blog-feed article) h3 {
    margin-top: var(--space-1);
    font-family: var(--serif);
    font-size: 1.25rem;
    font-weight: 500;
    line-height: 1.25;
  }
  :where(skill-card, blog-feed article) p {
    margin-top: var(--space-1);
    font-size: var(--text-small);
    color: var(--ink-soft);
    line-height: 1.55;
  }

  /* Skill path · /skills/<em>name</em>/ pseudo-breadcrumb under the h3. */
  skill-path {
    display: block;
    margin-top: 0.3em;
    font-family: var(--mono);
    font-size: var(--text-small);
    color: var(--ink-soft);
  }
  skill-path em {
    font-style: normal;
    color: var(--ink);
  }
  /* Meta rows · kicker is the breadcrumb above the H1 on entry/post/skill
     pages and in the blog feed; article-meta is the tag/runtime row under
     the H1; skill-meta is the in-card meta. Same compact mono row. */
  :where(kicker, article-meta, skill-meta) {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5em;
    font-family: var(--mono);
    font-size: var(--text-small);
    color: var(--ink-soft);
  }
  :where(article-meta, skill-meta) {
    margin-top: var(--space-1);
  }
  kicker a {
    color: var(--ink);
    transition: opacity 0.15s;
  }
  kicker a:hover {
    opacity: 0.6;
  }

  /* Inline meta tokens · used inside kicker/article-meta/skill-meta and
     standalone. tag = pill chip, sep = faded dot. */
  tag {
    display: inline-block;
    padding: 0.15em 0.6em;
    background: color-mix(in srgb, var(--ink) 6%, transparent);
    border-radius: var(--radius-pill);
    color: var(--ink);
  }
  sep {
    color: color-mix(in srgb, var(--ink) 28%, transparent);
  }

  /* Changelog · date-led entry rows, dashed dividers. */
  changelog {
    display: block;
  }
  changelog entry {
    display: grid;
    grid-template-columns: 8em 1fr;
    gap: var(--space-2);
    padding: var(--space-2) 0;
    border-top: 1px dashed color-mix(in srgb, var(--ink) 14%, transparent);
  }
  changelog entry:last-child {
    border-bottom: 1px dashed color-mix(in srgb, var(--ink) 14%, transparent);
  }
  /* Latest changelog entry · marker-yellow top border + a "/latest"
     badge under the date so the most recent release reads as the headline
     of the page. */
  changelog entry:first-child {
    border-top: 2px solid color-mix(in srgb, var(--ship) 70%, transparent);
  }
  changelog entry:first-child date::after {
    content: "/ latest";
    display: block;
    margin-top: 0.2em;
    font-size: 0.9em;
    font-weight: 600;
    color: color-mix(in srgb, var(--ship) 90%, transparent);
  }
  changelog date {
    font-family: var(--mono);
    font-size: var(--text-small);
    color: var(--ink-soft);
    letter-spacing: 0.02em;
  }
  changelog desc {
    font-family: var(--mono);
    font-size: var(--text-body);
    line-height: 1.55;
  }
  changelog desc a {
    color: var(--ink);
    border-bottom: 1px solid color-mix(in srgb, var(--ink) 18%, transparent);
    transition: opacity var(--fade, 0.15s);
  }
  changelog desc a:hover {
    opacity: 0.65;
  }
  changelog desc strong {
    font-weight: 600;
  }

  /* Stagger fade-up on list-page items · 50ms between each, same vibe as
     the homepage comparison-grid reveal. base.njk sets per-item
     --list-i. */
  @media (prefers-reduced-motion: no-preference) {
    :where(skill-list > *, blog-feed > *, changelog > entry) {
      opacity: 0;
      transform: translateY(8px);
      animation: list-fade-in 0.4s ease-out forwards;
      animation-delay: calc(var(--list-i, 0) * 50ms);
    }
  }
  @keyframes list-fade-in {
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
}

/* ---- RESPONSIVE ------------------------------------------ */
@media (max-width: 48rem) {
  :root {
    --space-section: var(--space-4);
    /* Smaller headings on phones. Desktop floor (2.5rem H1 / 2rem H2)
       wraps a six-word H1 to four lines on a 375 px viewport. */
    --text-h1: clamp(2rem, 7.5vw, 2.75rem);
    --text-h2: clamp(1.625rem, 5.5vw, 2.25rem);
  }
  :where(body) {
    grid-template-columns: minmax(0, 1fr);
    /* Body grid had no row-gap, so nav butted up against the hero. */
    row-gap: var(--space-2);
    padding: var(--space-2) var(--space-1);
  }
  :where(body) > nav,
  :where(body) > hero-section {
    grid-column: 1;
    grid-row: auto;
  }

  /* Hero CTAs stack on phones · the button + "or talk to curtis" link
     side-by-side gets cramped under a narrower H1, and the secondary
     link is easy to miss. Stack and left-align. */
  hero-ctas {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
  }
  footer-top,
  contrast-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Spacing tokens are physical units (--space-3 = 20mm ≈ 76px) and don't
     scale down on their own. Trim the heaviest horizontal padding so
     content isn't crushed into a sliver on narrow screens. */
  hero-summary {
    padding: var(--space-2);
  }
  comparison-grid {
    padding: var(--space-2) var(--space-1) var(--space-2);
  }
  pre {
    padding: var(--space-2);
  }
  canvas-frame {
    padding: var(--space-2);
  }

  /* Site nav · horizontal bar on phones instead of a vertical block.
     Brand on the left, links right. Single row by design: keyboard
     hints are dropped (single-key nav is a desktop affordance), and the
     "open app" link is hidden because the app is desktop-only — no
     point linking to it from a phone. */
  nav {
    flex-direction: row;
    align-items: center;
    column-gap: var(--space-1);
    padding: 0.6em var(--space-1);
  }
  nav .brand {
    margin-right: auto;
  }
  nav .brand img {
    height: 18px;
  }
  nav a {
    padding: 0.25em 0;
  }
  nav a[data-key]::before {
    content: none;
  }
  nav a[href*="app.scratch.md"] {
    display: none;
  }

  /* Comparison grid · let header cells wrap and shrink type so the
     three columns fit a phone without horizontal scroll. The body grid's
     minmax(0, 1fr) above is the safety net if a cell can't shrink. */
  comparison-grid table {
    font-size: 0.75rem;
  }
  comparison-grid thead th {
    white-space: normal;
    font-size: 1rem;
    padding: 0.5em 0.35em;
  }
  comparison-grid tbody td {
    padding: 0.5em 0.35em;
  }
  comparison-grid tbody td:first-child::before {
    min-width: 1.6em;
    margin-right: 0.3em;
  }
  comparison-grid panel-foot {
    font-size: var(--text-small);
  }

  /* Hero summary stacks lede + counter (defined above with minmax(0,1fr)). */
  hero-summary:has(> skill-counter) {
    grid-template-columns: minmax(0, 1fr);
  }
  hero-summary > skill-counter {
    align-items: flex-start;
    text-align: left;
  }
  changelog entry {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.2em;
  }

  /* Loop diagram on narrow screens · 3x3 grid collapses to a single
     column. Stations stack in cycle order; arrows all point down so the
     vertical flow reads naturally. */
  loop-diagram {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "tl"
      "tm"
      "tr"
      "mr"
      "br"
      "bm"
      "bl"
      "ml"
      "mm";
    gap: var(--space-1);
  }
  cycle-arrow::before,
  cycle-arrow[data-dir="right"]::before,
  cycle-arrow[data-dir="left"]::before,
  cycle-arrow[data-dir="up"]::before {
    content: "↓";
    font-size: 2rem;
  }
  /* Spin mark · 160 px is bigger than necessary on a phone. */
  cycle-mark img {
    width: 110px;
    height: 110px;
  }
  /* Oversized step number reads heavy on a narrow column. */
  step-num {
    font-size: 1.75rem;
  }
}

/* ---- RESPONSIVE · small phones --------------------------- */
@media (max-width: 24rem) {
  :where(body) {
    padding: var(--space-2) calc(var(--grid) * 0.6);
  }
  comparison-grid table {
    font-size: 0.7rem;
  }
}
