@charset "utf-8";

/* ==========================================================================
   DWMM Holdings — "Editorial light"

   Direction B from src/static/explore/directions.html. Warm paper, near-black
   type, copper as the single accent. It should read as a printed annual report
   rather than a terminal.

   Four rules that keep it a system:

   1. Display type is Instrument Serif. Everything else — labels, prose, UI —
      is Archivo or the mono. Two display decisions, never mixed inside a line.
   2. Copper is the only accent and it has two values. --cu is the graphic
      weight: rules, bars, the italic in a headline. It measures 3.84:1 on
      paper, so small text never uses it — that is --cu-ink, at 5.2:1.
   3. Structure comes from rules, not from boxes. Hairlines, collapsed grids,
      and a column layer behind the content. No shadows anywhere: on paper an
      elevated card is a lie.
   4. Every mono, uppercase, tracked string is a reading — a designator, a
      count, a date, a sheet number, a section rule.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Brand — Logos/Colors/brand-colors.css. On paper the copper has to be
     darkened to carry text, hence --cu-ink and --cu-deep below. */
  --brand-accent: #c2ae8c;
  --brand-highlight: #aa8b61;

  /* Paper. Warm, four steps, close together — on a light ground the
     separation comes from rules, so the surfaces stay quiet. */
  --paper: #f2ede3;
  --paper-raised: #f8f5ee;
  --paper-sunk: #ece5d8;
  --paper-hi: #e9e2d6;

  /* Ink */
  --ink: #1c1917; /* headings                      ~15:1 */
  --ink-body: #4a433d; /* body copy                  8.3:1 */
  --ink-mute: #6d6156; /* labels, meta               5.2:1 */

  /* Copper */
  --cu: #96703f; /* graphic weight only            3.8:1 */
  --cu-ink: #7d5c2f; /* the text weight            5.2:1 */
  --cu-deep: #5c4322;
  --cu-wash: #e5dccb;

  /* Rules. One ink hairline at four weights. */
  --rule: rgba(31, 30, 30, 0.13);
  --rule-mid: rgba(31, 30, 30, 0.2);
  --rule-strong: rgba(31, 30, 30, 0.34);
  --rule-col: rgba(31, 30, 30, 0.055);

  /* State. Always paired with a written label — never colour alone. */
  --state-shipping: #4a7245;
  --state-active: var(--cu-ink);
  --state-prototype: #8a6410;
  --state-spec: #6d6156;
  --state-archived: #8d8478;

  /* Type */
  --font-serif: 'Instrument Serif', 'Iowan Old Style', Georgia, serif;
  --font-sans: 'Archivo', ui-sans-serif, 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;

  --text-2xs: 0.6875rem;
  --text-xs: 0.75rem;
  --text-sm: 0.8125rem;
  --text-base: 1rem;
  --text-md: 1.0625rem;
  --text-lg: 1.25rem;

  /* Display. The serif carries the jumps, so they are wide. */
  --display-sm: 1.75rem;
  --display: 2.5rem;
  --display-lg: 3.25rem;
  --display-xl: clamp(2.875rem, 7vw, 6.5rem);

  --lh-display: 0.98;
  --lh-snug: 1.14;
  --lh-body: 1.62;

  --track-mono: 0.14em;

  /* Space — 4px scale */
  --space-2xs: 0.25rem;
  --space-xs: 0.5rem;
  --space-sm: 0.75rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2.25rem;
  --space-2xl: 3.25rem;
  --space-3xl: 4.5rem;

  /* Semantic spacing, so tightening the page is one edit and not thirty.
     --section-pad is half the gap between two sections. */
  --section-pad: 1.75rem;
  --pad-card: 1.25rem;

  --radius-sm: 2px;
  --radius: 3px;

  --shell: 74rem;
  --gutter: clamp(1.25rem, 4.5vw, 3rem);
  --header-h: 4.25rem;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 130ms;
  --dur: 220ms;
  --dur-slow: 480ms;
}

/* Registered so they interpolate. --mx/--my/--gloss must inherit: the wash is
   painted by a pseudo-element, and a non-inheriting registered property
   resolves to its initial value there. */
@property --mx {
  syntax: '<length-percentage>';
  inherits: true;
  initial-value: 50%;
}
@property --my {
  syntax: '<length-percentage>';
  inherits: true;
  initial-value: 50%;
}
@property --gloss {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

/* --------------------------------------------------------------------------
   2. Reset and base
   -------------------------------------------------------------------------- */

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

* {
  margin: 0;
}

html {
  /* `clip`, not `hidden`: stops sideways drift without creating a scroll
     container, so position: sticky keeps working. */
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + var(--space-lg));
  background: var(--paper);
}

body {
  position: relative;
  overflow-x: clip;
  min-height: 100dvh;
  background: var(--paper);
  color: var(--ink-body);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--lh-body);
  /* Instrument Serif ships one weight. Synthesising a bold from it produces a
     smeared outline, so synthesis is off everywhere. */
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
}

/* The column layer: hairlines on the layout columns, fixed, so they read as
   ruled paper the content sits on rather than as part of any one section.
   The rules land at 0, 1/3, 2/3 and 1 of the content width — exactly where the
   card grid's gutters fall, so a rule always shows through a gap and never
   through a card. */
body::before {
  content: '';
  position: fixed;
  top: 0;
  bottom: 0;
  left: 50%;
  translate: -50% 0;
  width: min(100% - var(--gutter) * 2, var(--shell) - var(--gutter) * 2);
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(90deg, var(--rule-col) 0 1px, transparent 1px),
    linear-gradient(90deg, transparent calc(100% - 1px), var(--rule-col) calc(100% - 1px)),
    repeating-linear-gradient(90deg, var(--rule-col) 0 1px, transparent 1px calc(100% / 3));
}

/* One column of cards: the thirds no longer mean anything. */
@media (max-width: 44rem) {
  body::before {
    background-image:
      linear-gradient(90deg, var(--rule-col) 0 1px, transparent 1px),
      linear-gradient(90deg, transparent calc(100% - 1px), var(--rule-col) calc(100% - 1px));
  }
}

img,
svg {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
}

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

::selection {
  background: var(--cu-wash);
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   3. Type
   -------------------------------------------------------------------------- */

h1,
h2,
h3 {
  font-family: var(--font-serif);
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -0.015em;
  line-height: var(--lh-snug);
  text-wrap: balance;
}

h1 {
  font-size: var(--display-lg);
  line-height: var(--lh-display);
  letter-spacing: -0.022em;
}

h2 {
  font-size: var(--display);
  letter-spacing: -0.018em;
}

h3 {
  font-size: var(--display-sm);
  line-height: 1.08;
}

/* h4 stays in the sans: below this size the serif stops reading as display and
   starts reading as a mistake. */
h4 {
  font-family: var(--font-sans);
  font-size: var(--text-md);
  font-weight: 600;
  font-variation-settings: 'wdth' 100;
  letter-spacing: -0.005em;
  color: var(--ink);
}

em {
  font-style: italic;
  color: var(--cu-ink);
}

p {
  text-wrap: pretty;
}

strong {
  color: var(--ink);
  font-weight: 600;
}

code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  color: var(--cu-deep);
  background: var(--cu-wash);
  padding: 0.14em 0.36em;
  border-radius: var(--radius-sm);
}

/* A reading. Mono, tracked, uppercase — designators, counts, dates, sheet
   numbers, section rules. Never a sentence. */
.silk {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: 500;
  letter-spacing: var(--track-mono);
  line-height: var(--lh-body);
  text-transform: uppercase;
  color: var(--cu-ink);
}

.silk--dim {
  color: var(--ink-mute);
  font-weight: 400;
}

.mono {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  font-variant-numeric: tabular-nums;
}

.lead {
  font-size: var(--text-md);
  color: var(--ink-body);
  max-width: 52ch;
}

.prose {
  max-width: 68ch;
}

.prose > * + * {
  margin-top: var(--space-md);
}

/* --------------------------------------------------------------------------
   4. Kinetic headings

   Archivo carries a width axis and the sans headings still use it. Instrument
   Serif has no variable axis at all, so app.js checks the computed family
   before animating and gives a serif heading a plain fade instead of a width
   sweep. Driving `font-variation-settings` on a static font does nothing
   visible and costs a paint per frame.
   -------------------------------------------------------------------------- */

.kinetic {
  font-variation-settings: normal;
}

/* --------------------------------------------------------------------------
   5. Layout
   -------------------------------------------------------------------------- */

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

.sheet {
  position: relative;
  padding-block: var(--section-pad);
}

.sheet::before {
  content: '';
  position: absolute;
  top: 0;
  left: var(--gutter);
  right: var(--gutter);
  height: 1px;
  background: var(--rule-mid);
}

.sheet--open::before {
  display: none;
}

/* The section band. Label and title left, lead and reading right, sharing the
   full measure — a single narrow column left every title stranded at 42% of
   the shell with 700px of nothing beside it, on every section of every page. */
/* One column. The label and its reading share the top rule — that fills the
   measure without splitting the prose into columns, which is the thing that
   made the old band read as two unrelated halves. */
.sheet-head {
  display: grid;
  gap: var(--space-sm);
  margin-bottom: var(--space-xl);
  padding-bottom: var(--space-md);
  border-bottom: 1px solid var(--rule);
}

.sheet-head__title {
  display: grid;
  gap: var(--space-2xs);
}

.sheet-head__body {
  display: grid;
  gap: var(--space-sm);
}

.sheet-head .lead {
  max-width: 62ch;
}

.sheet-head__meta {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}

.sheet-head--page {
  padding-block: var(--space-2xl) var(--space-md);
}

.sheet-head--page h1 {
  font-size: var(--display-xl);
  letter-spacing: -0.028em;
  line-height: var(--lh-display);
}

/* Label left, reading right, on one line above the title. */
.sheet-head__rule {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-lg);
  padding-bottom: var(--space-xs);
  border-bottom: 1px solid var(--rule);
  margin-bottom: var(--space-sm);
}

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

.skip-link {
  position: absolute;
  left: var(--space-md);
  top: var(--space-md);
  z-index: 100;
  padding: var(--space-sm) var(--space-md);
  background: var(--ink);
  color: var(--paper);
  font-weight: 600;
  transform: translateY(-200%);
  transition: transform var(--dur) var(--ease-out);
}

.skip-link:focus-visible {
  transform: none;
}

/* --------------------------------------------------------------------------
   6. Header
   -------------------------------------------------------------------------- */

.title-block {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: blur(14px) saturate(130%);
  border-bottom: 1px solid var(--rule-mid);
  view-transition-name: title-block;
}

.title-block__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  height: var(--header-h);
}

.mark {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  gap: var(--space-sm);
  transition: opacity var(--dur) var(--ease-out);
}

.mark img {
  height: 1.375rem;
  width: auto;
}

.mark__rule {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
  padding-left: var(--space-sm);
  border-left: 1px solid var(--rule-mid);
}

.mark:hover {
  opacity: 0.7;
}

.title-block__end {
  display: flex;
  align-items: center;
  gap: clamp(var(--space-md), 2.4vw, var(--space-xl));
}

.nav {
  display: flex;
  align-items: center;
  gap: clamp(var(--space-md), 2.4vw, var(--space-xl));
}

.nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
  transition: color var(--dur) var(--ease-out);
}

.nav a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.7rem;
  height: 1px;
  background: var(--cu-ink);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease-out);
}

.nav a:hover,
.nav a[aria-current='page'] {
  color: var(--ink);
}

.nav a:hover::after,
.nav a[aria-current='page']::after {
  transform: scaleX(1);
}

/* Reading progress, laid along the foot of the header. */
.ruler {
  position: fixed;
  top: var(--header-h);
  left: 0;
  right: 0;
  height: 2px;
  z-index: 49;
  pointer-events: none;
}

.ruler::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--cu);
  transform-origin: left;
  transform: scaleX(0);
}

@supports (animation-timeline: scroll()) {
  .ruler::after {
    animation: lay-copper linear;
    animation-timeline: scroll(root block);
  }
}

@keyframes lay-copper {
  to {
    transform: scaleX(1);
  }
}

/* --------------------------------------------------------------------------
   7. Footer
   -------------------------------------------------------------------------- */

.rev-block {
  margin-top: var(--space-2xl);
  border-top: 1px solid var(--rule-strong);
  background: var(--paper-sunk);
}

.rev-block__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(2, minmax(0, 1fr));
  gap: var(--space-xl);
  padding-block: var(--space-xl) var(--space-lg);
}

.rev-block img {
  width: 6.5rem;
  margin-bottom: var(--space-md);
}

.rev-block ul {
  display: grid;
  list-style: none;
  padding: 0;
  font-size: var(--text-sm);
}

.rev-block a {
  display: inline-flex;
  align-items: center;
  min-height: 2.25rem;
  color: var(--ink-body);
  transition: color var(--dur) var(--ease-out);
}

.rev-block a:hover {
  color: var(--cu-ink);
}

.rev-block__base {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-lg);
  padding-block: var(--space-md) var(--space-xl);
  border-top: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}

.rev-block__base b {
  color: var(--ink);
  font-weight: 500;
}

@media (max-width: 52rem) {
  .rev-block__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-lg);
  }
}

/* --------------------------------------------------------------------------
   8. Pointer wash

   On paper the pointer does not light a surface, it lifts it — a soft warm
   wash under the cursor. Same mechanism as the dark build, inverted.
   -------------------------------------------------------------------------- */

.cu {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.cu::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: var(--gloss);
  background: radial-gradient(
    12rem 12rem at var(--mx) var(--my),
    rgba(255, 255, 255, 0.3),
    transparent 62%
  );
  transition: opacity var(--dur) var(--ease-out);
}

.cu:hover,
.cu:focus-within {
  --gloss: 1;
}

/* --------------------------------------------------------------------------
   9. Buttons
   -------------------------------------------------------------------------- */

.btn {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-edge: var(--rule-strong);
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 2.875rem;
  padding: 0 var(--space-lg);
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid var(--btn-edge);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: 500;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  cursor: pointer;
  transition:
    background var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

.btn:hover {
  --btn-edge: var(--ink);
  --btn-bg: var(--paper-hi);
  transform: translateY(-1px);
}

.btn:active {
  transform: translateY(0);
}

.btn--pad {
  --btn-bg: var(--ink);
  --btn-fg: var(--paper);
  --btn-edge: var(--ink);
}

.btn--pad:hover {
  --btn-bg: var(--cu-deep);
  --btn-edge: var(--cu-deep);
  --btn-fg: var(--paper);
}

.btn__arrow {
  position: relative;
  z-index: 2;
  transition: transform var(--dur) var(--ease-out);
}

.btn:hover .btn__arrow {
  transform: translateX(3px);
}

.btn > span {
  position: relative;
  z-index: 2;
}

.link {
  color: var(--cu-ink);
  background-image: linear-gradient(var(--cu-ink), var(--cu-ink));
  background-size: 0% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition:
    background-size var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}

.link:hover {
  color: var(--cu-deep);
  background-size: 100% 1px;
}

/* --------------------------------------------------------------------------
   10. Status stamp
   -------------------------------------------------------------------------- */

.stamp {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
  white-space: nowrap;
}

/* The dot is redundant with the word beside it, deliberately: colour is never
   the only thing carrying state here. */
.stamp::before {
  content: '';
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: 50%;
  background: var(--stamp, var(--ink-mute));
}

.stamp[data-status='shipping'] {
  --stamp: var(--state-shipping);
}
.stamp[data-status='active'] {
  --stamp: var(--state-active);
}
.stamp[data-status='prototype'] {
  --stamp: var(--state-prototype);
}
.stamp[data-status='spec'] {
  --stamp: var(--state-spec);
}
.stamp[data-status='archived'] {
  --stamp: var(--state-archived);
}

.stamp--boxed {
  padding: 0.25em 0.6em;
  border: 1px solid var(--rule-mid);
  border-radius: var(--radius-sm);
}

.stamp--boxed::before {
  display: none;
}

/* --------------------------------------------------------------------------
   11. Hero

   Type-led, as the direction has it: a measured rule, the headline across the
   full measure, then the standfirst in two columns. The fan-out diagram is not
   here — it belongs beside the disciplines it maps, one section down.
   -------------------------------------------------------------------------- */

.hero {
  padding-block: var(--space-2xl) var(--space-xl);
}

/* A dimension line: what this is on the left, what it measures on the right. */
.hero__rule {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--rule-strong);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
  animation: rise 640ms var(--ease-out) 80ms both;
}

.hero__rule b {
  color: var(--cu-ink);
  font-weight: 500;
}

.hero__title {
  margin-top: var(--space-lg);
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: var(--display-xl);
  line-height: var(--lh-display);
  letter-spacing: -0.028em;
  color: var(--ink);
  max-width: 16ch;
}

/* Each line is clipped by its own box and slides up, so the reveal reads as
   type being set rather than as a fade. */
.line {
  display: block;
  overflow: hidden;
  /* The clip box is the line box, and Instrument Serif's italic descenders fall
     below it — which sheared the 'g' of "ledgers" clean off. Pad the box down
     far enough to hold them and pull the same amount back out of the flow, so
     the reveal still clips the line but the letters survive. */
  padding-bottom: 0.18em;
  margin-bottom: -0.18em;
}

.line > span {
  display: block;
  transform: translateY(105%);
  animation: set-line 820ms var(--ease-out) both;
  animation-delay: calc(95ms * var(--i, 0) + 180ms);
}

@keyframes set-line {
  to {
    transform: none;
  }
}

.hero__title em {
  font-style: italic;
  color: var(--cu);
}

.hero__standfirst {
  margin-top: var(--space-lg);
  max-width: 58ch;
  font-size: var(--text-lg);
  animation: rise 820ms var(--ease-out) 520ms both;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: var(--space-xl);
  animation: rise 820ms var(--ease-out) 640ms both;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (max-width: 46rem) {
  .hero__title {
    max-width: none;
  }
}

/* Counters along the foot of the hero, as a ruled strip. */
.readout {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-top: var(--space-xl);
  border-top: 1px solid var(--rule-strong);
  border-bottom: 1px solid var(--rule);
  animation: rise 820ms var(--ease-out) 760ms both;
}

.readout > div {
  padding: var(--space-sm) var(--space-md) var(--space-sm) 0;
  border-right: 1px solid var(--rule);
}

.readout > div:last-child {
  border-right: 0;
}

.readout > div:not(:first-child) {
  padding-left: var(--space-md);
}

.readout--flat {
  margin-top: var(--space-lg);
  animation: none;
}

.readout__value {
  display: block;
  font-family: var(--font-serif);
  font-size: var(--display-sm);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.readout__label {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
}

@media (max-width: 46rem) {
  .readout {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .readout > div:nth-child(2) {
    border-right: 0;
  }
}

/* --------------------------------------------------------------------------
   12. The discipline explorer

   Six tabs over one panel. Replaces the fan-out diagram: same six routes, but
   the panel shows the actual work behind a discipline instead of drawing its
   count as a trace.
   -------------------------------------------------------------------------- */

.explorer {
  margin-bottom: var(--space-xl);
  border: 1px solid var(--rule-mid);
  background: var(--paper-raised);
}

.explorer__tabs {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  border-bottom: 1px solid var(--rule-mid);
}

.explorer__tab {
  display: grid;
  gap: var(--space-2xs);
  justify-items: start;
  padding: var(--space-sm) var(--space-md);
  min-height: 5rem;
  align-content: start;
  background: transparent;
  border: 0;
  border-right: 1px solid var(--rule);
  border-bottom: 2px solid transparent;
  cursor: pointer;
  text-align: left;
  transition:
    background var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out);
}

.explorer__tab:last-child {
  border-right: 0;
}

.explorer__tab:hover {
  background: var(--paper-hi);
}

.explorer__tab[aria-selected='true'] {
  background: var(--paper);
  border-bottom-color: var(--cu);
}

.explorer__ref {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  color: var(--cu-ink);
}

.explorer__name {
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  line-height: 1.08;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.explorer__count {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}

.explorer__tab[aria-selected='true'] .explorer__count {
  color: var(--cu-ink);
}

.explorer__panel {
  padding: var(--space-lg);
}

.explorer__panel[hidden] {
  display: none;
}

.explorer__blurb {
  max-width: 62ch;
  margin-bottom: var(--space-md);
  color: var(--ink-body);
}

.explorer__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
  gap: 0 var(--space-lg);
  list-style: none;
  padding: 0;
  border-top: 1px solid var(--rule);
}

.explorer__list a {
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr) auto;
  gap: var(--space-sm);
  align-items: baseline;
  padding: var(--space-xs) var(--space-2xs);
  border-bottom: 1px solid var(--rule);
  transition:
    background var(--dur-fast) var(--ease-out),
    padding-left var(--dur-fast) var(--ease-out);
}

.explorer__list a:hover {
  background: var(--paper-hi);
  padding-left: var(--space-xs);
}

.explorer__pref {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  color: var(--cu-ink);
}

.explorer__pname {
  color: var(--ink);
  font-size: var(--text-sm);
}

.explorer__list a:hover .explorer__pname {
  color: var(--cu-deep);
}

.explorer__pmeta {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
  justify-self: end;
}

.explorer__all {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin-top: var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--cu-ink);
  transition: gap var(--dur) var(--ease-out);
}

.explorer__all:hover {
  gap: var(--space-sm);
  color: var(--cu-deep);
}

@media (max-width: 62rem) {
  .explorer__tabs {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .explorer__tab:nth-child(3n) {
    border-right: 0;
  }

  .explorer__tab:nth-child(-n + 3) {
    border-bottom: 1px solid var(--rule);
  }
}

@media (max-width: 40rem) {
  .explorer__tabs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .explorer__tab:nth-child(3n) {
    border-right: 1px solid var(--rule);
  }

  .explorer__tab:nth-child(2n) {
    border-right: 0;
  }
}

/* --------------------------------------------------------------------------
   13. Discipline table
   -------------------------------------------------------------------------- */

.disciplines {
  border-top: 1px solid var(--rule-strong);
}

.discipline {
  position: relative;
  display: grid;
  grid-template-columns: 3rem minmax(0, 14rem) minmax(0, 1fr) auto;
  gap: var(--space-lg);
  align-items: baseline;
  padding: var(--space-sm);
  border-bottom: 1px solid var(--rule);
  transition:
    background var(--dur) var(--ease-out),
    padding-left var(--dur) var(--ease-out);
}

.discipline:hover {
  background: var(--paper-hi);
  padding-left: var(--space-md);
}

.discipline__ref {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  color: var(--cu-ink);
}

.discipline__name {
  font-family: var(--font-serif);
  font-size: var(--display-sm);
  line-height: 1.1;
  letter-spacing: -0.012em;
  color: var(--ink);
}

.discipline p {
  font-size: var(--text-sm);
}

.discipline__count {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
  justify-self: end;
}

/* Three columns, and every cell placed by hand. Left to auto-placement the
   description claims row 2 and pushes the count onto a row of its own. */
@media (max-width: 56rem) {
  .discipline {
    grid-template-columns: 3rem minmax(0, 1fr) auto;
    row-gap: var(--space-2xs);
  }

  .discipline__ref {
    grid-area: 1 / 1;
  }

  .discipline__name {
    grid-area: 1 / 2;
  }

  .discipline__count {
    grid-area: 1 / 3;
  }

  .discipline p {
    grid-area: 2 / 2 / 3 / -1;
  }
}

/* --------------------------------------------------------------------------
   14. Parts — the project grid

   A collapsed grid, ruled the way a table of parts is ruled. Cards are cells,
   not floating objects.
   -------------------------------------------------------------------------- */

.placement {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--rule-strong);
  border-left: 1px solid var(--rule);
}

@media (max-width: 66rem) {
  .placement {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 44rem) {
  .placement {
    grid-template-columns: minmax(0, 1fr);
  }
}

.part {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-rows: auto auto 1fr auto auto auto;
  gap: var(--space-sm);
  padding: var(--pad-card);
  align-content: start;
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  transition: background var(--dur) var(--ease-out);
}

.part::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: var(--gloss);
  background: radial-gradient(14rem 14rem at var(--mx) var(--my), var(--cu-wash), transparent 70%);
  transition: opacity var(--dur) var(--ease-out);
}

.part:hover,
.part:focus-within {
  --gloss: 1;
  background: var(--paper-hi);
}

.part__ref {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  color: var(--cu-ink);
}

.part__name {
  font-family: var(--font-serif);
  font-size: var(--display-sm);
  line-height: 1.06;
  letter-spacing: -0.014em;
  color: var(--ink);
  transition: color var(--dur) var(--ease-out);
}

/* The whole part is the target; the name carries the link, so the accessible
   name and the tab stop stay on one element. */
.part__link::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
}

.part__link:focus-visible {
  outline: none;
}

.part:has(.part__link:focus-visible) {
  outline: 2px solid var(--cu-ink);
  outline-offset: -2px;
}

.part:hover .part__name,
.part:focus-within .part__name {
  color: var(--cu-deep);
}

.part__value {
  font-size: var(--text-sm);
  align-self: start;
}

/* Language share. Real proportions read from the repository, drawn small. */
.part__mix {
  display: grid;
  gap: var(--space-2xs);
}

.part__mix-label {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.05em;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

.part__stack {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  list-style: none;
  padding: 0;
}

.part__stack li {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  line-height: 1.6;
  letter-spacing: 0.04em;
  color: var(--ink-mute);
  padding: 0.15em 0.5em;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--paper-raised);
}

.part__stack-more {
  color: var(--cu-ink);
}

.part__spec {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: var(--space-2xs) var(--space-md);
  align-items: baseline;
  padding-top: var(--space-sm);
  border-top: 1px solid var(--rule);
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.part__meta {
  grid-column: 1 / -1;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.05em;
  font-variant-numeric: tabular-nums;
}

.part__domain {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--cu-ink);
  justify-self: end;
}

/* The lead part runs the full width and is placed explicitly — auto-placement
   cannot know which of seven children belongs in which column. */
.part--lead {
  grid-column: 1 / -1;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  grid-template-rows: auto auto 1fr auto auto;
  column-gap: var(--space-2xl);
  padding: var(--space-lg) var(--pad-card);
  background: var(--paper-raised);
}

.part--lead:hover,
.part--lead:focus-within {
  background: var(--paper-hi);
}

.part--lead .part__ref {
  grid-area: 1 / 1 / 2 / -1;
}

.part--lead .part__name {
  grid-area: 2 / 1;
  font-size: var(--display);
  line-height: 1.02;
  letter-spacing: -0.02em;
}

.part--lead .part__claim {
  grid-area: 3 / 1;
  align-self: start;
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  line-height: 1.24;
  color: var(--cu-deep);
}

.part--lead .part__value {
  grid-area: 2 / 2 / 4 / 3;
  font-size: var(--text-base);
}

.part--lead .part__mix {
  grid-area: 4 / 1;
  align-self: end;
}

.part--lead .part__stack {
  grid-area: 4 / 2;
  align-self: end;
}

.part--lead .part__spec {
  grid-area: 5 / 1 / 6 / -1;
}

@media (max-width: 60rem) {
  .part--lead {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto;
    row-gap: var(--space-sm);
  }

  .part--lead .part__ref,
  .part--lead .part__name,
  .part--lead .part__claim,
  .part--lead .part__value,
  .part--lead .part__mix,
  .part--lead .part__stack,
  .part--lead .part__spec {
    grid-area: auto;
    grid-column: 1;
  }
}

.part[hidden] {
  display: none;
}

/* --------------------------------------------------------------------------
   15. Filter bar
   -------------------------------------------------------------------------- */

.filters {
  position: sticky;
  top: var(--header-h);
  z-index: 20;
  margin-bottom: var(--space-lg);
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  backdrop-filter: blur(14px) saturate(130%);
  border-bottom: 1px solid var(--rule-mid);
}

.filters__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  padding-block: var(--space-sm);
}

.filters__group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
  border: 0;
  padding: 0;
  margin: 0;
}

.filters__legend {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
  padding-right: var(--space-sm);
  float: left;
  line-height: 2.5rem;
}

.chip {
  min-height: 2.5rem;
  padding: 0 var(--space-md);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-body);
  cursor: pointer;
  transition:
    background var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}

.chip:hover {
  background: var(--paper-hi);
  border-color: var(--rule-strong);
  color: var(--ink);
}

.chip[aria-pressed='true'] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

.chip__n {
  opacity: 0.62;
  margin-left: 0.45em;
  font-variant-numeric: tabular-nums;
}

.filters__count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

.empty {
  padding: var(--space-2xl) 0;
  text-align: center;
  color: var(--ink-mute);
}

/* --------------------------------------------------------------------------
   16b. The contribution matrix

   A rolling year, one column per week. The ramp is the copper ramp: five steps
   from an empty cell to the busiest day, each a clear value apart so the shape
   of a year reads at a glance. Colour is never the only carrier — every day
   with commits has the count in its title and aria-label, and the caption
   states the totals.
   -------------------------------------------------------------------------- */

.cal-wrap {
  display: grid;
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
  min-width: 0;
}

/* Full content width. `table-layout: fixed` is what lets the 53 week columns
   share the measure evenly instead of sizing to their contents; below the
   breakpoint the table keeps a floor width and the wrapper scrolls. */
.cal__scroll {
  min-width: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--cu) transparent;
  padding-bottom: var(--space-2xs);
}

.cal {
  width: 100%;
  min-width: 44rem;
  /* 53 columns plus the weekday gutter. Without a ceiling the squares grow with
     the shell — at the full 2270px they reach 40px each and the thing stops
     reading as a contribution graph and starts reading as a game board. */
  max-width: calc(53 * 1.4rem + 2.5rem);
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 2px;
}

.cal__corner {
  width: 2.5rem;
}

.cal__month {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: left;
  color: var(--ink-mute);
  padding-bottom: 4px;
  /* A short run would otherwise print its label across the next month's
     columns. Clip it to the run it belongs to. */
  overflow: hidden;
  white-space: nowrap;
}

.cal__dow {
  width: 2.25rem;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: 400;
  letter-spacing: 0.04em;
  text-align: left;
  color: var(--ink-mute);
  padding-right: var(--space-2xs);
}

/* The square lives in a span, not on the <td>: aspect-ratio on a table cell is
   not reliably honoured, and the cells have to stay square while the columns
   flex. */
.cal__day {
  padding: 0;
}

.cal__day::after {
  content: '';
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 2px;
  background: var(--lvl, var(--paper-sunk));
  box-shadow: inset 0 0 0 1px rgba(31, 30, 30, 0.05);
  transition:
    box-shadow var(--dur-fast) var(--ease-out),
    scale var(--dur-fast) var(--ease-out);
}

.cal__day[data-level='void']::after {
  background: transparent;
  box-shadow: none;
}

/* Hover and keyboard focus land on the same visual state. */
.cal__day.is-active::after {
  box-shadow: inset 0 0 0 1px var(--ink);
  scale: 1.35;
}

.cal:focus-visible {
  outline: 2px solid var(--cu-ink);
  outline-offset: 4px;
}

/* Crosshair: the hovered day's week and weekday both dim up, so a square can
   be read back to its column and row without counting. */
.cal.is-reading .cal__day:not(.is-row):not(.is-col)::after {
  opacity: 0.45;
}

.cal__day::after {
  transition:
    box-shadow var(--dur-fast) var(--ease-out),
    scale var(--dur-fast) var(--ease-out),
    opacity var(--dur-fast) var(--ease-out);
}

/* The tooltip. A native `title` waits a second and cannot be styled; this
   appears immediately and sits where the square is. */
.cal-tip {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  padding: 0.35em 0.6em;
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.04em;
  white-space: nowrap;
  opacity: 0;
  translate: -50% -125%;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.cal-tip.is-open {
  opacity: 1;
}

.cal-tip b {
  color: var(--brand-accent);
  font-weight: 500;
}

/* The ramp. Five steps, monotonic, each distinguishable from its neighbour on
   paper as well as on screen. */
.cal__day[data-level='0'],
.cal__key[data-level='0'] {
  --lvl: #e6ded0;
}
.cal__day[data-level='1'],
.cal__key[data-level='1'] {
  --lvl: #ddc9a3;
}
.cal__day[data-level='2'],
.cal__key[data-level='2'] {
  --lvl: #c3a273;
}
.cal__day[data-level='3'],
.cal__key[data-level='3'] {
  --lvl: var(--cu);
}
.cal__day[data-level='4'],
.cal__key[data-level='4'] {
  --lvl: var(--cu-deep);
}

/* A day the snapshot does not cover is not a zero-commit day; it is drawn as
   nothing at all rather than as an empty square. */
.cal__day[data-level='void'] {
  --lvl: transparent;
  outline: none;
}

.cal__foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-lg);
  padding-top: var(--space-xs);
  border-top: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.cal__scale {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}

.cal__key {
  width: 11px;
  height: 11px;
  border-radius: 2px;
  background: var(--lvl);
  outline: 1px solid rgba(31, 30, 30, 0.05);
  outline-offset: -1px;
}

/* --------------------------------------------------------------------------
   17. Assembly (collections)
   -------------------------------------------------------------------------- */

.assembly {
  display: grid;
  gap: var(--space-sm) var(--space-2xl);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
  padding-block: var(--space-md);
  border-top: 1px solid var(--rule);
}

.assembly:last-child {
  border-bottom: 1px solid var(--rule);
}

@media (max-width: 52rem) {
  .assembly {
    grid-template-columns: minmax(0, 1fr);
  }
}

.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  list-style: none;
  padding: 0;
  align-content: start;
}

.chip-list a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 2.375rem;
  padding: 0 var(--space-sm);
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  color: var(--ink-body);
  transition:
    border-color var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out),
    background var(--dur) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

.chip-list a:hover {
  border-color: var(--rule-strong);
  color: var(--ink);
  background: var(--paper-hi);
  transform: translateY(-1px);
}

.chip-list b {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: 400;
  letter-spacing: 0.08em;
  color: var(--cu-ink);
}

/* --------------------------------------------------------------------------
   18. Datasheet — the project page
   -------------------------------------------------------------------------- */

.ds-head {
  position: relative;
  padding-block: var(--space-xl) var(--space-md);
}

.crumb {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
  margin-bottom: var(--space-lg);
}

.crumb a {
  transition: color var(--dur) var(--ease-out);
}

.crumb a:hover {
  color: var(--cu-ink);
}

.ds-head h1 {
  font-size: var(--display-xl);
  letter-spacing: -0.028em;
  line-height: var(--lh-display);
}

.ds-head__claim {
  margin-top: var(--space-md);
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  color: var(--cu-deep);
  letter-spacing: -0.008em;
  max-width: 46ch;
  line-height: 1.26;
}

.ds-head__stamps {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-lg);
  align-items: center;
  margin-top: var(--space-lg);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--rule);
}

.ds-body {
  display: grid;
  gap: var(--space-2xl);
  grid-template-columns: minmax(0, 1.8fr) minmax(0, 1fr);
  align-items: start;
  padding-top: var(--space-lg);
  border-top: 1px solid var(--rule-strong);
}

@media (max-width: 56rem) {
  .ds-body {
    grid-template-columns: minmax(0, 1fr);
  }
}

.ds-section + .ds-section {
  margin-top: var(--space-xl);
}

.ds-section > h2 {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: 500;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--cu-ink);
  padding-bottom: var(--space-xs);
  margin-bottom: var(--space-md);
  border-bottom: 1px solid var(--rule-mid);
}

/* Project-owned support, policy and documentation pages. They borrow the
   datasheet's materials without pretending a policy is a product spec. */
.resource-list {
  display: grid;
  gap: var(--space-sm);
  list-style: none;
  padding: 0;
}

.resource-list a {
  display: grid;
  gap: 0.2rem;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--rule);
  background: var(--paper-raised);
  transition: border-color var(--dur) var(--ease-out), background var(--dur) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}

.resource-list a:hover {
  border-color: var(--acc, var(--cu));
  background: var(--paper-hi);
  transform: translateY(-1px);
}

.resource-list strong { color: var(--ink); }
.resource-list span { color: var(--ink-mute); font-size: var(--text-sm); }

.doc-head { padding-block: var(--space-xl) var(--space-md); }
.doc-head h1 { margin-top: var(--space-sm); font-size: var(--display-xl); letter-spacing: -0.028em; line-height: var(--lh-display); }
.doc-head__lead { max-width: 58ch; margin-top: var(--space-md); font-family: var(--font-serif); font-size: var(--text-lg); line-height: 1.3; color: var(--cu-deep); }
.doc-head__meta { display: flex; flex-wrap: wrap; gap: var(--space-sm) var(--space-lg); align-items: center; margin-top: var(--space-lg); color: var(--ink-mute); font-size: var(--text-sm); }
.project-nav { display: flex; flex-wrap: wrap; gap: 0; margin-top: var(--space-lg); border-block: 1px solid var(--rule-strong); }
.project-nav a { padding: var(--space-sm) var(--space-md); border-right: 1px solid var(--rule); color: var(--ink-mute); font-family: var(--font-mono); font-size: var(--text-2xs); letter-spacing: var(--track-mono); text-transform: uppercase; }
.project-nav a:first-child { border-left: 1px solid var(--rule); }
.project-nav a:hover, .project-nav a[aria-current="page"] { color: var(--ink); background: var(--paper-raised); }
.doc-body { max-width: 70rem; padding-top: var(--space-lg); }
.doc-section { max-width: 72ch; padding-block: var(--space-lg); border-top: 1px solid var(--rule); }
.doc-section h2 { margin-bottom: var(--space-md); font-family: var(--font-mono); font-size: var(--text-2xs); font-weight: 500; letter-spacing: var(--track-mono); text-transform: uppercase; color: var(--acc-ink, var(--cu-ink)); }
.doc-section .prose p + p { margin-top: var(--space-md); }
.doc-section .btn { margin-top: var(--space-xs); }

/* Features, numbered the way a datasheet numbers them. */
.features {
  display: grid;
  list-style: none;
  padding: 0;
  counter-reset: feature;
}

.features li {
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  gap: var(--space-sm);
  padding-block: var(--space-sm);
  border-bottom: 1px solid var(--rule);
  color: var(--ink-body);
}

.features li::before {
  content: counter(feature, decimal-leading-zero);
  counter-increment: feature;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  color: var(--cu-ink);
  line-height: var(--lh-body);
}

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  list-style: none;
  padding: 0;
}

.tags li {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  color: var(--ink-body);
  padding: 0.35em 0.7em;
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
}

/* The specification table. Real repository facts, presented as ratings. */
.spec {
  position: sticky;
  top: calc(var(--header-h) + var(--space-lg));
  border: 1px solid var(--rule-strong);
  background: var(--paper-raised);
}

.spec__title {
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--rule-strong);
  background: var(--paper-sunk);
}

.spec__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-md);
  align-items: baseline;
  padding: var(--space-xs) var(--space-md);
  border-bottom: 1px solid var(--rule);
  font-size: var(--text-sm);
  transition: background var(--dur-fast) var(--ease-out);
}

.spec__row:hover {
  background: var(--paper-hi);
}

.spec__row dt {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.spec__row dd {
  margin: 0;
  color: var(--ink);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.spec__note {
  padding: var(--space-sm) var(--space-md);
  font-size: var(--text-sm);
  color: var(--ink-mute);
}

.spec__action {
  padding: var(--space-md);
  border-top: 1px solid var(--rule);
}

.spec__action .btn {
  width: 100%;
  justify-content: center;
}

/* Composition bar: language share, proportional and labelled. */
.mix {
  display: grid;
  gap: var(--space-sm);
  padding: var(--space-md);
  border-bottom: 1px solid var(--rule);
}

.mix__bar {
  display: flex;
  height: 4px;
  gap: 1px;
  border-radius: 1px;
  overflow: hidden;
}

/* Segments carry their own language colour inline; these are the fallback ramp
   for the repositories GitHub has no colour for. */
.mix__bar span:nth-child(1) {
  background: var(--cu-deep);
}
.mix__bar span:nth-child(2) {
  background: var(--cu-ink);
}
.mix__bar span:nth-child(3) {
  background: var(--cu);
}
.mix__bar span:nth-child(4) {
  background: #b79a6d;
}
.mix__bar span:nth-child(5) {
  background: #cdbb9c;
}
.mix__bar span:nth-child(n + 6) {
  background: #ded4bd;
}

.mix__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-sm);
  list-style: none;
  padding: 0;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.05em;
  color: var(--ink-body);
}

.mix__list b {
  color: var(--ink-mute);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

/* Sheet navigation. */
.sheets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  border-top: 1px solid var(--rule-strong);
  border-left: 1px solid var(--rule);
  margin-top: var(--space-2xl);
}

.sheets a {
  display: grid;
  gap: var(--space-2xs);
  padding: var(--space-lg);
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  transition: background var(--dur) var(--ease-out);
}

.sheets a:hover {
  background: var(--paper-hi);
}

.sheets a:last-child {
  text-align: right;
}

.sheets strong {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: var(--display-sm);
  line-height: 1.1;
  letter-spacing: -0.012em;
  color: var(--ink);
  transition: color var(--dur) var(--ease-out);
}

.sheets a:hover strong {
  color: var(--cu-deep);
}

/* --------------------------------------------------------------------------
   19. About — notes on the drawing
   -------------------------------------------------------------------------- */

.note {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: var(--space-xl);
  padding-block: var(--space-xl);
  border-top: 1px solid var(--rule);
}

.note > h2 {
  position: sticky;
  top: calc(var(--header-h) + var(--space-lg));
  align-self: start;
  font-size: var(--display-sm);
}

@media (max-width: 52rem) {
  .note {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-md);
  }

  .note > h2 {
    position: static;
  }
}

/* Draft copy is marked, never disguised — you can see what is still mine. */
.draft {
  position: relative;
  padding: var(--space-lg);
  border: 1px dashed var(--rule-strong);
  background: var(--paper-raised);
  max-width: none;
}

.draft::before {
  content: 'Placeholder copy';
  position: absolute;
  top: -0.65rem;
  left: var(--space-md);
  padding: 0 var(--space-xs);
  background: var(--paper);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--cu-ink);
}

.rules {
  display: grid;
}

.rules > div {
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  gap: var(--space-2xs) var(--space-lg);
  padding-block: var(--space-md);
  border-bottom: 1px solid var(--rule);
}

.rules > div:first-child {
  border-top: 1px solid var(--rule);
}

.rules dt {
  grid-column: 2;
  font-family: var(--font-serif);
  font-size: var(--display-sm);
  line-height: 1.1;
  letter-spacing: -0.012em;
  color: var(--ink);
}

.rules dd {
  grid-column: 2;
  margin: 0;
  max-width: 68ch;
}

.rules__n {
  grid-row: 1 / 3;
  grid-column: 1;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  color: var(--cu-ink);
  line-height: 2.4;
}

/* --------------------------------------------------------------------------
   20. Contact block
   -------------------------------------------------------------------------- */

/* Two columns: what it is on the left, the one action on the right. A single
   full-width column left two thirds of the block empty. */
.contact {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-sm) var(--space-2xl);
  padding-block: var(--space-xl);
  border-top: 1px solid var(--rule-strong);
  border-bottom: 1px solid var(--rule-strong);
}

.contact > .silk {
  grid-column: 1;
}

.contact h2 {
  grid-column: 1;
  max-width: 20ch;
}

.contact p {
  grid-column: 1;
  max-width: 52ch;
}

.contact .btn {
  grid-column: 2;
  grid-row: 1 / -1;
  justify-self: end;
}

@media (max-width: 52rem) {
  .contact {
    grid-template-columns: minmax(0, 1fr);
    justify-items: start;
  }

  .contact .btn {
    grid-column: 1;
    grid-row: auto;
    margin-top: var(--space-md);
  }
}

/* --------------------------------------------------------------------------
   21. Scroll reveals

   Driven by one IntersectionObserver in app.js, not a scroll timeline.
   `animation-timeline: view()` was the previous approach and it is the reason
   the page read as empty mid-scroll: an `animation-range` measured in
   percentages of the element resolves to an enormous scroll distance on any
   element taller than the viewport, so whole sections sat at opacity 0 long
   after they had arrived. An observer fires once, on arrival, regardless of
   element height — and app.js reveals everything unconditionally after a
   moment as a backstop, so no content can ever be stranded invisible.
   -------------------------------------------------------------------------- */

.rise {
  --rise-delay: min(calc(45ms * var(--i, 0)), 240ms);
}

.js .rise {
  opacity: 0;
  transform: translateY(10px);
  transition:
    opacity var(--dur-slow) var(--ease-out) var(--rise-delay),
    transform var(--dur-slow) var(--ease-out) var(--rise-delay);
}

.js .rise.is-visible {
  opacity: 1;
  transform: none;
}

/* Already on screen when the page painted: shown, not revealed. */
.js .rise.no-reveal {
  transition: none;
}

/* --------------------------------------------------------------------------
   22. View transitions
   -------------------------------------------------------------------------- */

@view-transition {
  navigation: auto;
}

::view-transition-group(*) {
  animation-duration: 340ms;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

/* The title block is shared furniture — hold it still while the sheet changes. */
::view-transition-old(title-block),
::view-transition-new(title-block) {
  animation: none;
}

::view-transition-old(root) {
  animation: sheet-out 200ms var(--ease-in-out) both;
}

::view-transition-new(root) {
  animation: sheet-in 340ms var(--ease-out) both;
}

@keyframes sheet-out {
  to {
    opacity: 0;
    transform: translateY(-6px);
  }
}

@keyframes sheet-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}

::view-transition-group(part-name) {
  animation-duration: 420ms;
}

/* --------------------------------------------------------------------------
   23. Motion preference

   `prefers-reduced-motion: reduce` means "do not move things through space" —
   translation, parallax, spinning, zooming, anything that loops. It does not
   mean "remove all feedback." Killing every transition as well leaves an
   interface that feels broken rather than calm, so what follows is surgical:
   movement and loops go, colour and opacity stay.

   The preference can also be overridden per-visitor. `data-motion="full"` on
   the root opts back into everything; `data-motion="reduced"` opts out even
   when the system has no preference. app.js writes the attribute and the
   control in the title block flips it.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion='full']) {
    scroll-behavior: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion='full']) .js .rise,
  :root:not([data-motion='full']) .rise {
    transform: none;
  }

  :root:not([data-motion='full']) .ruler::after {
    animation: none !important;
    animation-timeline: auto !important;
  }

  :root:not([data-motion='full']) .js .scope.is-visible .scope__track {
    animation: none;
    -webkit-mask-image: none;
    mask-image: none;
  }

  :root:not([data-motion='full']) .line > span,
  :root:not([data-motion='full']) .hero__rule,
  :root:not([data-motion='full']) .hero__standfirst,
  :root:not([data-motion='full']) .hero__actions,
  :root:not([data-motion='full']) .readout {
    animation-name: fade-only;
    animation-duration: 400ms;
    transform: none;
  }

  :root:not([data-motion='full']) .js .board.is-visible .board__trace {
    animation: none !important;
    stroke-dashoffset: 0;
  }

  /* A charge endlessly circling a trace is exactly what this setting is for. */
  :root:not([data-motion='full']) .js .board.is-visible .board__pulse {
    display: none;
  }

  /* Hover lifts are movement too. */
  :root:not([data-motion='full']) .chip-list a:hover,
  :root:not([data-motion='full']) .btn:hover {
    transform: none;
  }

  :root:not([data-motion='full']) .pad:hover .pad__ring,
  :root:not([data-motion='full']) .pad:focus-visible .pad__ring {
    scale: 1;
  }

  /* Cross-fade between sheets instead of sliding them. */
  :root:not([data-motion='full'])::view-transition-old(root),
  :root:not([data-motion='full'])::view-transition-new(root) {
    animation-name: fade-only;
    animation-duration: 180ms;
  }
}

/* The same, for a visitor who asked for less motion on a system that did not. */
:root[data-motion='reduced'] .js .rise,
:root[data-motion='reduced'] .rise {
  transform: none;
}

:root[data-motion='reduced'] .ruler::after {
  animation: none !important;
  animation-timeline: auto !important;
}

:root[data-motion='reduced'] .js .scope.is-visible .scope__track {
  animation: none;
  -webkit-mask-image: none;
  mask-image: none;
}

:root[data-motion='reduced'] .line > span,
:root[data-motion='reduced'] .hero__rule,
:root[data-motion='reduced'] .hero__standfirst,
:root[data-motion='reduced'] .hero__actions,
:root[data-motion='reduced'] .readout {
  animation-name: fade-only;
  animation-duration: 400ms;
  transform: none;
}

:root[data-motion='reduced'] .js .board.is-visible .board__trace {
  animation: none !important;
  stroke-dashoffset: 0;
}

:root[data-motion='reduced'] .js .board.is-visible .board__pulse {
  display: none;
}

:root[data-motion='reduced'] .chip-list a:hover,
:root[data-motion='reduced'] .btn:hover {
  transform: none;
}

:root[data-motion='reduced'] .pad:hover .pad__ring,
:root[data-motion='reduced'] .pad:focus-visible .pad__ring {
  scale: 1;
}

@keyframes fade-only {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* The control itself. */
.motion-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  min-height: 2.375rem;
  padding: 0 var(--space-sm);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
  cursor: pointer;
  transition:
    border-color var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out),
    background var(--dur) var(--ease-out);
}

.motion-toggle:hover {
  border-color: var(--rule-strong);
  background: var(--paper-hi);
  color: var(--ink);
}

.motion-toggle::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  transition: box-shadow var(--dur) var(--ease-out);
}

.motion-toggle[aria-pressed='true'] {
  color: var(--cu-ink);
  border-color: var(--rule-mid);
}

.motion-toggle[aria-pressed='true']::before {
  background: var(--cu-ink);
  box-shadow: 0 0 0 3px var(--cu-wash);
}

@media (max-width: 54rem) {
  .motion-toggle span {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   24. Narrow viewports
   -------------------------------------------------------------------------- */

@media (max-width: 40rem) {
  :root {
    --section-pad: 1.5rem;
    --space-2xl: 2.25rem;
    --space-3xl: 3rem;
    --header-h: 3.5rem;
  }

  .mark__rule {
    display: none;
  }

  .spec {
    position: static;
  }

  .sheets a:last-child {
    text-align: left;
  }
}

/* --------------------------------------------------------------------------
   25. Search palette, card detail, sort, timeline
   -------------------------------------------------------------------------- */

.search-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 2.375rem;
  padding: 0 var(--space-sm);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
  cursor: pointer;
  transition:
    border-color var(--dur) var(--ease-out),
    background var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}

.search-trigger:hover {
  border-color: var(--rule-strong);
  background: var(--paper-hi);
  color: var(--ink);
}

kbd {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 0.1em 0.4em;
  border: 1px solid var(--rule-mid);
  border-radius: var(--radius-sm);
  background: var(--paper-raised);
  color: var(--ink-mute);
}

.palette[hidden] {
  display: none;
}

.palette {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: start center;
  padding-top: 12vh;
}

.palette__scrim {
  position: absolute;
  inset: 0;
  background: rgba(31, 30, 30, 0.28);
  backdrop-filter: blur(2px);
}

.palette__box {
  position: relative;
  width: min(38rem, calc(100vw - 2rem));
  background: var(--paper-raised);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  overflow: hidden;
  animation: palette-in 200ms var(--ease-out) both;
}

@keyframes palette-in {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
}

.palette__input {
  width: 100%;
  padding: var(--space-md);
  border: 0;
  border-bottom: 1px solid var(--rule);
  background: transparent;
  font-family: var(--font-sans);
  font-size: var(--text-md);
  color: var(--ink);
}

.palette__input:focus {
  outline: none;
}

.palette__results {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 22rem;
  overflow-y: auto;
}

.palette__results a {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr) auto;
  gap: var(--space-sm);
  align-items: baseline;
  padding: var(--space-xs) var(--space-md);
}

.palette__results .is-on a {
  background: var(--paper-hi);
  box-shadow: inset 2px 0 0 var(--cu);
}

.palette__ref {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  color: var(--cu-ink);
}

.palette__name {
  color: var(--ink);
}

.palette__meta {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--ink-mute);
  justify-self: end;
}

.palette__empty,
.palette__hint {
  padding: var(--space-sm) var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  color: var(--ink-mute);
}

.palette__hint {
  display: flex;
  gap: 0.35em;
  align-items: center;
  border-top: 1px solid var(--rule);
  background: var(--paper-sunk);
}

/* Card detail. The toggle is its own control, so it never competes with the
   card-wide link for the same click. */
.part__toggle {
  position: absolute;
  top: var(--space-xs);
  right: var(--space-xs);
  z-index: 3;
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--ink-mute);
  cursor: pointer;
  opacity: 0;
  transition:
    opacity var(--dur) var(--ease-out),
    background var(--dur) var(--ease-out),
    rotate var(--dur) var(--ease-out);
}

.part:hover .part__toggle,
.part:focus-within .part__toggle,
.part__toggle:focus-visible,
.part__toggle[aria-expanded="true"] {
  opacity: 1;
}

.part__toggle:hover {
  background: var(--paper);
  border-color: var(--rule-mid);
  color: var(--ink);
}

.part__toggle[aria-expanded="true"] {
  rotate: 180deg;
}

.part__detail {
  grid-column: 1 / -1;
  margin-top: var(--space-xs);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--rule);
  display: grid;
  gap: var(--space-sm);
  animation: detail-in 240ms var(--ease-out) both;
}

.part__detail[hidden] {
  display: none;
}

@keyframes detail-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

.part__points {
  display: grid;
  gap: var(--space-2xs);
  margin: 0;
  padding-left: 1.1rem;
  font-size: var(--text-sm);
}

.part__points li::marker {
  color: var(--cu-ink);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
}

.part__fullstack {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  list-style: none;
  padding: 0;
}

.part__fullstack li {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  padding: 0.15em 0.45em;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--paper);
  color: var(--ink-mute);
}

.part__detail-meta,
.part__detail-note {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--ink-mute);
}

.part__detail-link {
  position: relative;
  z-index: 3;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cu-ink);
  justify-self: start;
}

.part__detail-link:hover {
  color: var(--cu-deep);
}

/* Sorted out of its curated slot, the lead card is just a card. */
.part--demoted {
  grid-column: auto;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto;
  background: transparent;
}

.part--demoted .part__ref,
.part--demoted .part__name,
.part--demoted .part__claim,
.part--demoted .part__value,
.part--demoted .part__mix,
.part--demoted .part__stack,
.part--demoted .part__spec {
  grid-area: auto;
  grid-column: 1;
}

.part--demoted .part__name {
  font-size: var(--display-sm);
}

.view-toggle[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* Timeline */
.timeline {
  padding-block: var(--space-lg) var(--space-2xl);
}

.timeline[hidden] {
  display: none;
}

.timeline__axis {
  position: relative;
  height: 1.25rem;
  margin-left: 11rem;
  border-bottom: 1px solid var(--rule-mid);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  color: var(--ink-mute);
}

.timeline__axis span {
  position: absolute;
  top: 0;
  translate: -50% 0;
}

.timeline__group {
  margin-top: var(--space-lg);
}

.timeline__domain {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--cu-ink);
  padding-bottom: var(--space-2xs);
  border-bottom: 1px solid var(--rule);
}

.timeline__row {
  display: grid;
  grid-template-columns: 11rem minmax(0, 1fr);
  gap: var(--space-md);
  align-items: center;
  padding-block: var(--space-2xs);
  border-bottom: 1px solid var(--rule);
  transition: background var(--dur-fast) var(--ease-out);
}

.timeline__row:hover {
  background: var(--paper-hi);
}

.timeline__name {
  font-size: var(--text-sm);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.timeline__track {
  position: relative;
  height: 1.1rem;
}

.timeline__bar {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  height: 0.55rem;
  min-width: 3px;
  border-radius: 2px;
  background: var(--cu);
  transition: background var(--dur-fast) var(--ease-out);
}

.timeline__row[data-status="shipping"] .timeline__bar {
  background: var(--state-shipping);
}

.timeline__row[data-status="prototype"] .timeline__bar {
  background: var(--state-prototype);
}

.timeline__row[data-status="archived"] .timeline__bar {
  background: var(--state-archived);
}

.timeline__row:hover .timeline__bar {
  background: var(--cu-deep);
}

.timeline__undated {
  margin-top: var(--space-lg);
  font-size: var(--text-sm);
  color: var(--ink-mute);
}

.timeline__undated a {
  color: var(--cu-ink);
}

@media (max-width: 46rem) {
  .timeline__axis {
    margin-left: 7rem;
  }

  .timeline__row {
    grid-template-columns: 7rem minmax(0, 1fr);
  }
}

/* --------------------------------------------------------------------------
   26. Project identity

   Each project carries the colour GitHub assigns to its largest language,
   resolved at build time into three forms: --acc for fills, --acc-ink for
   anything that has to be legible on paper, --acc-on for text sitting on the
   fill. A project with no language data keeps the site's copper rather than
   being given an invented colour.
   -------------------------------------------------------------------------- */

.chip-mark {
  display: inline-grid;
  place-items: center;
  min-width: 1.9rem;
  height: 1.35rem;
  padding: 0 0.4em;
  border-radius: var(--radius-sm);
  background: var(--acc, var(--cu));
  color: var(--acc-on, var(--paper));
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: 500;
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  transition: scale var(--dur) var(--ease-out);
}

.part:hover .chip-mark {
  scale: 1.06;
}

.chip-mark--lg {
  min-width: 3.25rem;
  height: 2.25rem;
  border-radius: var(--radius);
  font-size: var(--text-sm);
  letter-spacing: 0.1em;
}

.ds-head__mark {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
}

/* The card takes a hairline of its own colour along the top edge on hover, so
   the accent means something at the card level and not only on the chip. */
.part {
  box-shadow: inset 0 0 0 0 transparent;
  transition:
    background var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out);
}

.part:hover,
.part:focus-within {
  box-shadow: inset 0 2px 0 0 var(--acc, var(--cu));
}

.part:hover .part__name,
.part:focus-within .part__name {
  color: var(--acc-ink, var(--cu-deep));
}

.part__domain {
  color: var(--acc-ink, var(--cu-ink));
}

/* Language chips wear their language. */
.chip--lang::before {
  content: '';
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 0.5em;
  border-radius: 50%;
  background: var(--acc, var(--cu));
  vertical-align: baseline;
}

/* --------------------------------------------------------------------------
   27. Filter bar

   A two-column grid: label, then chips. Every row lines up on the same left
   edge, and the sort row is a row like the others instead of a group floated
   into whatever space was left.
   -------------------------------------------------------------------------- */

.filters__inner {
  display: grid;
  gap: var(--space-2xs) var(--space-md);
  padding-block: var(--space-sm);
}

.filters__row {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr);
  gap: var(--space-sm);
  align-items: start;
}

.filters__legend {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
  line-height: 2.25rem;
  float: none;
  padding: 0;
}

.filters__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

.filters__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-top: var(--space-2xs);
  padding-top: var(--space-xs);
  border-top: 1px solid var(--rule);
}

.filters__views {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

.filters__count {
  margin-left: 0;
}

.chip {
  min-height: 2.25rem;
}

@media (max-width: 46rem) {
  .filters__row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2xs);
  }

  .filters__legend {
    line-height: 1.6;
  }
}

/* --------------------------------------------------------------------------
   28. Width

   The shell was a fixed 74rem, which is most of a laptop and under half of a
   wide display. It now grows with the viewport to about two thirds, with a
   floor so it never gets narrower than it was and a ceiling so a very wide
   monitor does not stretch a three-card row to the horizon.
   -------------------------------------------------------------------------- */

:root {
  --shell: clamp(74rem, 66vw, 160rem);
}

/* More width means more columns, not wider cards. */
.placement {
  grid-template-columns: repeat(auto-fill, minmax(min(21rem, 100%), 1fr));
}

@media (max-width: 66rem) {
  .placement {
    grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr));
  }
}

/* The lead card keeps its two-column split whatever the row count. */
.part--lead {
  grid-column: 1 / -1;
}

/* Prose stays readable regardless of how wide the shell gets. */
.hero__title {
  max-width: 18ch;
}

.sheet-head .lead,
.hero__standfirst {
  max-width: 62ch;
}

/* ==========================================================================
   29. Full-width layout: rail + 12 columns

   The site was a centred document with full-width rules drawn across it: 98%
   of its text height sat in elements narrower than half the shell, so widening
   the container only lengthened the hairlines. This replaces the container with
   a real layout — a rail that holds the horizontal axis permanently, and a
   twelve-column grid the content is placed into rather than centred in.
   ========================================================================== */

:root {
  --rail-w: 15rem;
  --col-gap: clamp(1rem, 1.4vw, 2rem);
}

.app {
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  align-items: start;
}

.app__body {
  min-width: 0;
}

/* The shell no longer centres anything: the rail sets the left edge and the
   viewport sets the right. */
.shell {
  width: 100%;
  max-width: none;
  margin-inline: 0;
  padding-inline: var(--gutter);
}

/* --- The rail ----------------------------------------------------------- */

.rail {
  position: sticky;
  top: 0;
  height: 100dvh;
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: var(--space-lg);
  padding: var(--space-lg) var(--space-md);
  border-right: 1px solid var(--rule-mid);
  background: var(--paper-sunk);
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 40;
}

.rail__mark {
  display: grid;
  gap: var(--space-2xs);
}

.rail__mark img {
  height: 1.5rem;
  width: auto;
}

.rail__rule {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
}

.rail__nav {
  display: grid;
  gap: var(--space-2xs);
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule);
}

.rail__nav a {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 2.25rem;
  padding-left: var(--space-sm);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-mute);
  border-left: 2px solid transparent;
  transition:
    color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    padding-left var(--dur) var(--ease-out);
}

.rail__nav a::after {
  display: none;
}

.rail__nav a:hover {
  color: var(--ink);
  padding-left: var(--space-md);
}

.rail__nav a[aria-current='page'] {
  color: var(--ink);
  border-left-color: var(--cu);
}

/* The page index. Filled by app.js from the sections actually present, and
   marked as you pass them. */
.rail__index {
  display: grid;
  align-content: start;
  gap: 0;
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule);
  min-height: 0;
  overflow-y: auto;
}

.rail__index[hidden] {
  display: none;
}

.rail__index a {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 1.9rem;
  padding-left: var(--space-sm);
  border-left: 2px solid var(--rule);
  font-size: var(--text-sm);
  color: var(--ink-mute);
  transition:
    color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out);
}

.rail__index a:hover {
  color: var(--ink);
}

.rail__index a[aria-current='true'] {
  color: var(--ink);
  border-left-color: var(--cu);
}

.rail__foot {
  display: grid;
  gap: var(--space-xs);
  align-content: end;
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule);
}

.rail__controls {
  display: grid;
  gap: var(--space-2xs);
}

.rail__controls .search-trigger,
.rail__foot .motion-toggle {
  width: 100%;
  justify-content: flex-start;
}

.rail__contact {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.04em;
  color: var(--ink-mute);
  word-break: break-all;
}

.rail__contact:hover {
  color: var(--cu-ink);
}

/* --- Sections as twelve columns ----------------------------------------- */

.sheet,
.note {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--col-gap);
  align-items: start;
}

/* The head is the section's rail: label, title, lead and its readings, held in
   place while its content scrolls past. */
.sheet > .sheet-head,
.note > .sheet-head {
  grid-column: 1 / span 3;
  position: sticky;
  top: var(--space-lg);
  display: grid;
  gap: var(--space-sm);
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: 0;
  align-content: start;
}

.sheet-body {
  grid-column: 4 / -1;
  min-width: 0;
}

.sheet-head .lead {
  max-width: none;
  font-size: var(--text-base);
}

/* The readings that fill the rail below the lead. */
.sheet-meta {
  display: grid;
  gap: 0;
  margin-top: var(--space-md);
  border-top: 1px solid var(--rule);
}

.sheet-meta__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-block: var(--space-2xs);
  border-bottom: 1px solid var(--rule);
}

.sheet-meta dt {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.sheet-meta dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: right;
}

.sheet--flush > .sheet-head {
  position: static;
}

/* --- Hero --------------------------------------------------------------- */

.hero {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--col-gap);
  align-items: start;
}

.hero__rule {
  grid-column: 1 / -1;
}

.hero__lead {
  grid-column: 1 / span 8;
}

.hero__title {
  max-width: none;
  font-size: clamp(3rem, 6.5vw, 9rem);
}

.hero__standfirst {
  max-width: 46ch;
}

/* The counters stack down the right instead of running as a strip, so the hero
   occupies both axes. */
.readout--stack {
  grid-column: 10 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin-top: var(--space-lg);
  border-top: 1px solid var(--rule-strong);
  border-bottom: 0;
}

.readout--stack > div {
  padding: var(--space-sm) 0;
  border-right: 0;
  border-bottom: 1px solid var(--rule);
}

.readout--stack > div:not(:first-child) {
  padding-left: 0;
}

.readout--stack .readout__value {
  font-size: var(--display);
}

/* --- Cards get their generated mark -------------------------------------- */

.part__ref {
  position: relative;
  padding-left: 3.25rem;
  min-height: 2.5rem;
  align-items: flex-start;
}

.part__art {
  position: absolute;
  left: 0;
  top: 0;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--rule);
  transition:
    scale var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out);
}

.part:hover .part__art {
  scale: 1.05;
  border-color: var(--acc, var(--cu));
}

.part--lead .part__art {
  width: 4rem;
  height: 4rem;
}

.part--lead .part__ref {
  padding-left: 4.75rem;
  min-height: 4rem;
}

.ds-head__mark .part__art,
.ds-head__art {
  width: 4.5rem;
  height: 4.5rem;
  border-radius: var(--radius);
  border: 1px solid var(--rule);
}

/* --- Footer ------------------------------------------------------------- */

.rev-block {
  margin-top: var(--space-2xl);
}

/* --- Collapse to a bar on anything that is not wide ---------------------- */

@media (max-width: 68rem) {
  .app {
    grid-template-columns: minmax(0, 1fr);
  }

  .rail {
    position: sticky;
    top: 0;
    height: auto;
    grid-template-rows: none;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: var(--space-md);
    padding: var(--space-sm) var(--gutter);
    border-right: 0;
    border-bottom: 1px solid var(--rule-mid);
    background: color-mix(in srgb, var(--paper) 88%, transparent);
    backdrop-filter: blur(14px) saturate(130%);
    overflow: visible;
  }

  .rail__mark {
    grid-auto-flow: column;
    align-items: center;
    gap: var(--space-sm);
  }

  .rail__nav {
    display: flex;
    gap: var(--space-md);
    justify-content: flex-end;
    padding-top: 0;
    border-top: 0;
  }

  .rail__nav a {
    padding-left: 0;
    border-left: 0;
    border-bottom: 2px solid transparent;
  }

  .rail__nav a[aria-current='page'] {
    border-left-color: transparent;
    border-bottom-color: var(--cu);
  }

  .rail__index {
    display: none;
  }

  .rail__foot {
    grid-auto-flow: column;
    align-items: center;
    gap: var(--space-xs);
    padding-top: 0;
    border-top: 0;
  }

  .rail__controls {
    grid-auto-flow: column;
  }

  .rail__contact {
    display: none;
  }

  .sheet,
  .note,
  .hero {
    grid-template-columns: minmax(0, 1fr);
  }

  .sheet > .sheet-head,
  .note > .sheet-head {
    grid-column: 1;
    position: static;
    margin-bottom: var(--space-lg);
    padding-bottom: var(--space-md);
    border-bottom: 1px solid var(--rule);
  }

  .sheet-body,
  .hero__lead,
  .readout--stack {
    grid-column: 1;
  }

  .readout--stack {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-lg);
  }

  .sheet-meta {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-lg);
  }
}

/* The rail owns the left edge, so anything that used to hang off the old top
   header has to be re-anchored to it. */
@media (min-width: 68.01rem) {
  .ruler {
    top: 0;
    left: var(--rail-w);
  }

  .filters {
    top: 0;
  }

  .spec,
  .note > h2 {
    top: var(--space-lg);
  }

  html {
    scroll-padding-top: var(--space-lg);
  }
}

/* Page heads and datasheets are compositions too, not full-bleed rows. A
   reading stretched across 3,000px with its value pinned to the far right is
   the same stretched-rule problem in miniature. */
@media (min-width: 68.01rem) {
  .sheet-head--page {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--col-gap);
    align-items: end;
  }

  .sheet-head--page > .silk,
  .sheet-head--page > h1 {
    grid-column: 1 / span 8;
  }

  .sheet-head--page > .lead {
    grid-column: 1 / span 6;
    max-width: 62ch;
  }

  .sheet-head--page > .sheet-meta {
    grid-column: 10 / -1;
    grid-row: 1 / span 3;
    align-self: end;
    margin-top: 0;
  }

  .ds-head {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--col-gap);
  }

  .ds-head > * {
    grid-column: 1 / span 9;
  }

  .ds-head__claim {
    grid-column: 1 / span 6;
  }

  .ds-head__stamps {
    grid-column: 1 / -1;
  }

  .ds-body {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--col-gap);
  }

  .ds-body > div:first-child {
    grid-column: 1 / span 7;
  }

  .ds-body > .spec {
    grid-column: 9 / -1;
  }

  /* Features have room to breathe at this width. */
  .features li {
    grid-template-columns: 3rem minmax(0, 1fr);
  }
}

/* --------------------------------------------------------------------------
   30. Corrections found by photographing the real window

   The browser MCP downscales 3440px to 1568 and refuses to resize below about
   1500, so these three were invisible until scripts/shoot.sh started capturing
   actual windows at actual sizes through framewatch.
   -------------------------------------------------------------------------- */

/* The rail's live block: the space between the index and the controls was
   290px of nothing at 1280 and 490px at 1920. */
.rail__now {
  display: grid;
  gap: var(--space-2xs);
  align-content: start;
  padding-top: var(--space-md);
  border-top: 1px solid var(--rule);
}

.rail__now-link {
  display: grid;
  gap: 1px;
}

.rail__now-name {
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  line-height: 1.1;
  color: var(--ink);
  transition: color var(--dur) var(--ease-out);
}

.rail__now-link:hover .rail__now-name {
  color: var(--cu-deep);
}

.rail__now-meta,
.rail__now-total {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.04em;
  color: var(--ink-mute);
}

.rail__now-total {
  margin-top: var(--space-xs);
}

@media (min-width: 68.01rem) {
  .rail {
    grid-template-rows: auto auto minmax(0, 1fr) auto auto;
  }
}

@media (max-width: 68rem) {
  .rail__now {
    display: none;
  }
}

/* A three-column section head is 235px at 1280 — too narrow for a serif title,
   which wrapped to "What the / workshop does". Sections only go twelve-column
   once there is genuinely room; between the rail appearing and that point they
   stack, head above body, at full width. */
@media (min-width: 68.01rem) and (max-width: 96rem) {
  .sheet,
  .note,
  .hero,
  .sheet-head--page,
  .ds-head,
  .ds-body {
    grid-template-columns: minmax(0, 1fr);
  }

  .sheet > .sheet-head,
  .note > .sheet-head,
  .sheet-body,
  .hero__rule,
  .hero__lead,
  .readout--stack,
  .sheet-head--page > *,
  .ds-head > *,
  .ds-body > * {
    grid-column: 1;
  }

  .sheet > .sheet-head,
  .note > .sheet-head {
    position: static;
    margin-bottom: var(--space-lg);
    padding-bottom: var(--space-md);
    border-bottom: 1px solid var(--rule);
  }

  .sheet-head--page > .sheet-meta {
    grid-row: auto;
  }

  .sheet-meta,
  .readout--stack {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    column-gap: var(--space-lg);
  }

  .readout--stack > div {
    border-bottom: 0;
  }
}

/* The counters used to end 140px above the rest of the hero and float there.
   Sit them on the same baseline as the actions instead. */
@media (min-width: 96.01rem) {
  .hero {
    align-items: end;
  }

  .hero__rule {
    align-self: start;
  }

  .readout--stack {
    align-self: end;
    margin-bottom: 0;
  }
}

/* --------------------------------------------------------------------------
   31. What the size sweep found

   Every page photographed at 430 / 1280 / 1920 / 3440 with scripts/shoot.sh.
   -------------------------------------------------------------------------- */

/* The contact block is its own two-column composition, so dropping it straight
   into the twelve-column section grid placed it in one column: 228px wide, with
   its inner columns collapsing to 6px of text and a three-line button. It lives
   inside .sheet-body now, but belt and braces — anything unwrapped in a section
   spans the grid rather than landing in column one. */
.sheet > *:not(.sheet-head):not(.sheet-body) {
  grid-column: 1 / -1;
}

.contact {
  width: 100%;
}

/* The collapsed rail was a single non-wrapping row: mark, three nav items,
   search and the motion control. That is wider than 430px, so every page
   scrolled sideways and clipped its right edge — the readings, the filter
   chips and the result count were all cut off. */
@media (max-width: 68rem) {
  .rail {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    row-gap: var(--space-xs);
  }

  .rail__mark {
    grid-area: 1 / 1;
  }

  .rail__foot {
    grid-area: 1 / 2;
    justify-self: end;
  }

  .rail__nav {
    grid-area: 2 / 1 / 3 / -1;
    justify-content: flex-start;
    gap: var(--space-lg);
    overflow-x: auto;
    scrollbar-width: none;
  }

  .rail__nav::-webkit-scrollbar {
    display: none;
  }
}

/* Nothing may push the page wider than the window. Belt and braces for the
   rest: a stray min-width anywhere used to take the whole document with it. */
.app,
.app__body,
main {
  min-width: 0;
  max-width: 100%;
}

@media (max-width: 46rem) {
  /* The matrix keeps its own scroller; it must not widen the page. */
  .cal {
    min-width: 38rem;
  }

  .sheet-meta {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* About: the placeholder frames had no measure at all and ran to 1,180px —
   about 140 characters a line — while the prose beside them was capped at 68ch.
   One measure for both. */
.draft {
  max-width: 68ch;
}

.note .prose,
.note .draft {
  max-width: 68ch;
}

/* Project sheets: the section rules spanned the whole column while their prose
   stopped at 68ch, so every rule overshot its content by 300px. Let the rule
   agree with what it is underlining. */
@media (min-width: 96.01rem) {
  .ds-section > h2,
  .features,
  .ds-section .prose {
    max-width: 74ch;
  }

  .ds-section .tags,
  .ds-section .chip-list {
    max-width: 74ch;
  }
}
