/* ==========================================================================
   DRT Loom — landing page
   --------------------------------------------------------------------------
   Everything visual is a custom property in :root. That is the single swap
   point for a corporate identity: replace the token block and the page follows.
   Component rules below never hard-code a colour or a font.

   Palette is sampled from docs/assets/banner.png rather than invented, so the
   page and the repository's own artwork are the same object.
   ========================================================================== */

/* ------------------------------------------------------------------ fonts */

@font-face {
  font-family: "Archivo";
  src: url("fonts/archivo-400-800.woff2") format("woff2");
  font-weight: 400 800;
  font-stretch: 100% 125%;
  font-display: swap;
  unicode-range: U+0000-00FF, U+2000-206F, U+2190-21BB, U+2212;
}

@font-face {
  font-family: "Plex Sans";
  src: url("fonts/ibm-plex-sans-400-600.woff2") format("woff2");
  font-weight: 400 600;
  font-display: swap;
  unicode-range: U+0000-00FF, U+2000-206F, U+2190-21BB, U+2212;
}

@font-face {
  font-family: "Plex Mono";
  src: url("fonts/ibm-plex-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Plex Mono";
  src: url("fonts/ibm-plex-mono-500.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}

/* ----------------------------------------------------------------- tokens */

:root {
  /* Surface and ink */
  --paper: #f9f9f9;
  --paper-tint: #f1f2f6;
  --card: #ffffff;
  --ink: #111a2e;
  --ink-soft: #38415a;
  --slate: #6e768c;
  --rule: #e2e4ec;
  --rule-strong: #cfd3e0;
  /* The weft is structure, not a hairline: it has to read as a thread the warp
     is woven through, which --rule-strong at 1.3:1 on the paper does not. */
  --weft: #737d99;

  /* Accents */
  --violet: #7a5af0;
  --violet-deep: #5b3fd0;
  --cyan: #3fa9e0;
  --wash: #efeaff;

  /* The six domain threads, cyan → violet in registry order. Colour encodes
     which domain a thread is; it is not decoration. */
  --d-erp: #2f9bd4;
  --d-plm: #4a85df;
  --d-mes: #5e74e6;
  --d-cad: #7059ec;
  --d-documents: #8a4cf0;
  --d-logistics: #a66bf2;

  /* Type */
  --display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --body: "Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: "Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Rhythm */
  --gutter: clamp(1.25rem, 5vw, 4.5rem);
  --measure: 34rem;
  --page: 76rem;
  --band: clamp(4.5rem, 10vw, 8rem);
  --radius: 3px;

  --focus: 0 0 0 2px var(--paper), 0 0 0 4px var(--violet);
}

/* ------------------------------------------------------------------- base */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 1rem;
  line-height: 1.62;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

:where(a) {
  color: var(--violet-deep);
  text-decoration-color: color-mix(in srgb, var(--violet) 35%, transparent);
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}

:where(a:hover) {
  text-decoration-color: currentColor;
}

:where(h1, h2, h3, p, ul, ol, dl, pre, figure, table) {
  margin: 0;
}

:where(ul, ol) {
  padding: 0;
  list-style: none;
}

code,
pre,
.mono {
  font-family: var(--mono);
  font-size: 0.86em;
  font-variant-ligatures: none;
}

:where(p, li, dd) code {
  background: color-mix(in srgb, var(--violet) 8%, transparent);
  border-radius: 2px;
  padding: 0.1em 0.32em;
}

/* One focus treatment for the whole page. The shadow follows whatever radius
   the element already has, so this rule must not set one of its own. */
:focus-visible {
  outline: none;
  box-shadow: var(--focus);
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 20;
  background: var(--ink);
  color: var(--paper);
  padding: 0.7rem 1.1rem;
  font-size: 0.86rem;
}

.skip:focus {
  left: 0.5rem;
  top: 0.5rem;
}

/* --------------------------------------------------------------- masthead */

.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  padding-block: 1.35rem;
  padding-inline: max(var(--gutter), calc((100% - var(--page)) / 2));
  border-bottom: 1px solid var(--rule);
}

.mark {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  color: inherit;
}

.mark__glyph {
  width: 30px;
  height: 30px;
  flex: none;
}

.mark__word {
  font-family: var(--display);
  font-size: 0.92rem;
  font-weight: 600;
  font-stretch: 118%;
  letter-spacing: 0.13em;
  color: var(--slate);
}

.mark__word b {
  font-weight: 800;
  color: var(--ink);
}

.masthead__nav {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 2.4vw, 1.9rem);
  font-size: 0.87rem;
}

.masthead__nav a {
  color: var(--ink-soft);
  text-decoration: none;
}

.masthead__nav a:hover {
  color: var(--violet-deep);
}

.masthead__repo {
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  padding: 0.34rem 0.8rem;
}

.masthead__repo:hover {
  border-color: var(--violet);
}

/* ------------------------------------------------------------------- hero */

.hero {
  padding-block: clamp(3rem, 7vw, 5rem) clamp(2.25rem, 5vw, 3.25rem);
  padding-inline: max(var(--gutter), calc((100% - var(--page)) / 2));
}

.eyebrow {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--slate);
}

.eyebrow__sep {
  color: var(--rule-strong);
}

.hero__title {
  margin: 1.6rem 0 0;
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 122%;
  font-size: clamp(2.85rem, 9.2vw, 6.6rem);
  line-height: 0.9;
  letter-spacing: -0.035em;
  text-wrap: balance;
}

.hero__lede {
  max-width: 44rem;
  margin-top: 1.9rem;
  font-size: clamp(1.05rem, 1.5vw, 1.2rem);
  line-height: 1.62;
  color: var(--ink-soft);
}

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

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 2rem;
  margin-top: 2.4rem;
}

.clone {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  background: var(--card);
  overflow: hidden;
  max-width: 100%;
}

.clone code {
  padding: 0.72rem 0.95rem;
  font-size: 0.8rem;
  white-space: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}

.clone__copy {
  flex: none;
  border: 0;
  border-left: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0 0.95rem;
  cursor: pointer;
}

.clone__copy:hover {
  background: var(--violet-deep);
  border-left-color: var(--violet-deep);
}

/* The clone box clips its children so the command can scroll inside it, which
   would shave the page's outer focus ring down to a sliver on one edge. This
   is the same ring turned inward, in a violet that holds up against the ink. */
.clone__copy:focus-visible {
  box-shadow: inset 0 0 0 2px var(--ink), inset 0 0 0 4px #a08cff;
}

.link-arrow {
  font-size: 0.95rem;
  font-weight: 500;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--violet) 40%, transparent);
  padding-bottom: 1px;
}

.link-arrow:hover {
  border-bottom-color: currentColor;
}

.link-arrow[hidden] {
  display: none;
}

/* ------------------------------------------------------------------- loom */

.loom {
  padding-block: 0 var(--band);
  padding-inline: max(var(--gutter), calc((100% - var(--page)) / 2));
}

.loom__intro {
  max-width: var(--measure);
  color: var(--ink-soft);
  display: grid;
  gap: 0.85rem;
}

.kicker {
  font-family: var(--mono);
  font-size: 0.74rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--violet-deep);
}

.loom__hint {
  color: var(--slate);
  font-size: 0.93rem;
}

.loom__controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1.9rem 0 1.5rem;
}

.thread-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border: 1px solid var(--rule-strong);
  border-radius: 100px;
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 0.85rem;
  padding: 0.38rem 0.9rem 0.38rem 0.7rem;
  cursor: pointer;
  transition: border-color 120ms ease, background-color 120ms ease;
}

.thread-toggle__swatch {
  width: 10px;
  height: 10px;
  border-radius: 100px;
  background: var(--thread);
  flex: none;
  transition: background-color 160ms ease, box-shadow 160ms ease;
}

/* On is filled in the thread's own colour, off is empty — the same reading as
   the cloth below. Without this an on toggle and an off one differed only by
   the swatch, and the always-on core domains looked more selected than the
   optional ones that were actually selected. */
.thread-toggle[aria-pressed="true"] {
  border-color: color-mix(in srgb, var(--thread) 40%, transparent);
  background: color-mix(in srgb, var(--thread) 11%, transparent);
}

.thread-toggle[aria-pressed="false"] {
  color: var(--slate);
  background: transparent;
}

.thread-toggle[aria-pressed="false"] .thread-toggle__swatch {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--rule-strong);
}

/* Core domains are always on, so they read as filled cloth rather than as a
   disabled control — a dashed outline would say "off", which is the opposite
   of what is true. What marks them out is the CORE label and that they do not
   answer the pointer, not a weaker fill. */
.thread-toggle[disabled] {
  cursor: default;
  border-color: transparent;
  opacity: 1;
}

.thread-toggle:not([disabled]):hover {
  border-color: var(--thread, var(--violet));
}

.thread-toggle__note {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--slate);
}

.loom__figure {
  position: relative;
  margin: 0;
}

.loom__scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding-bottom: 0.5rem;
}

/* The draft has a floor below which it stops being readable, so on a narrow
   screen it scrolls rather than shrinking. The fade says so without a label. */
.loom__scroll[data-overflow="true"] {
  mask-image: linear-gradient(to right, #000 calc(100% - 3.5rem), transparent);
}

.loom__swipe {
  margin-top: 0.9rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--slate);
}

.loom__swipe[hidden] {
  display: none;
}

.loom__svg {
  display: block;
  width: 100%;
  min-width: 60rem;
  height: auto;
}

/* Drawing order: dependency band, guides, warp, links, ties, crossings. The
   links come after the warp so a pass can break the threads it runs over. */

/* The brackets above the columns: what each domain needs in order to mean
   anything. Thin and half-strength — this is the standing structure the
   passes are woven into, so it must not compete with them. */
.dep {
  stroke-width: 1.5;
  opacity: 0.55;
  transition: opacity 200ms ease;
}

.dep--off {
  opacity: 0.12;
}

.warp {
  transition: stroke 200ms ease, opacity 200ms ease;
}

.warp--off {
  stroke: var(--rule-strong);
  stroke-dasharray: 2 7;
}

/* The row guide carries no information — it exists so a phase label stays
   attached to its row. Anything heavier competes with the link. */
.weft-guide {
  stroke: var(--rule);
  stroke-width: 1;
}

/* The link: one pass of the shuttle, spanning the domains that phase ties
   together. This is the line that says something, so it is the darkest thing
   in the drawing after the crossings themselves. */
.weft-link {
  stroke: var(--weft);
  stroke-width: 3;
  stroke-linecap: round;
}

/* The short piece of weft that passes over a warp where the phase does not
   draw from that domain. This is the interlacing: without it the weft is
   always underneath, the warp threads run unbroken, and the drawing is six
   vertical lines rather than cloth.

   A 2px thread laid over a 5px one only dents it, so the pair is drawn as
   casing + thread: a paper-coloured band the width of the crossing breaks the
   warp outright, and the weft is then redrawn across the full break so the
   pick stays continuous. The warp goes under, visibly. */
.weft-clear {
  stroke: var(--paper);
  stroke-width: 8;
  stroke-linecap: butt;
}

.weft-over {
  stroke: var(--weft);
  stroke-width: 3;
  stroke-linecap: butt;
}

.cross {
  transition: opacity 220ms ease, transform 220ms ease;
  transform-origin: center;
}

.cross--off {
  opacity: 0;
  transform: scale(0.6);
}

.cross__num {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  text-anchor: middle;
  dominant-baseline: central;
}

.axis-phase {
  font-family: var(--display);
  font-size: 12px;
  font-weight: 600;
  font-stretch: 112%;
  letter-spacing: 0.04em;
  fill: var(--ink);
  text-anchor: end;
}

.axis-blurb {
  font-family: var(--body);
  font-size: 10.5px;
  fill: var(--slate);
  text-anchor: end;
}

.axis-domain {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-anchor: middle;
  transition: fill 200ms ease;
}

.tie {
  transition: opacity 200ms ease;
}

/* Open, and the same size as a crossing, so it is legible as the same kind of
   event — records written here — while the missing fill and missing number say
   it took no pass of its own. */
.tie__mark {
  stroke-width: 1.5;
  stroke-dasharray: 3 3;
}

.axis-domain--off {
  fill: var(--rule-strong);
}

.loom__caption {
  max-width: var(--measure);
  margin-top: 1.1rem;
  font-size: 0.86rem;
  color: var(--slate);
}

/* ---------------------------------------------------------------- readout */

/* Five items, so the column count is explicit rather than auto-fit: an
   auto-fit row leaves an orphan cell, and an empty cell in a row of counts
   reads as a missing number. */
.readout {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 1px;
  margin-top: 2.6rem;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}

.readout__item {
  display: grid;
  gap: 0.2rem;
  padding: 1.1rem 1.15rem 1.2rem;
  background: var(--card);
}

.readout__item--accent {
  background: var(--wash);
}

.readout__value {
  font-family: var(--display);
  font-weight: 700;
  font-stretch: 108%;
  font-size: clamp(1.7rem, 3.4vw, 2.3rem);
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.readout__value[data-changed="true"] {
  animation: tick 420ms ease;
}

@keyframes tick {
  0% {
    color: var(--violet);
  }
  100% {
    color: inherit;
  }
}

.readout__label {
  font-size: 0.8rem;
  line-height: 1.35;
  color: var(--slate);
}

.readout__note {
  max-width: var(--measure);
  margin-top: 1.1rem;
  font-size: 0.88rem;
  color: var(--slate);
}

/* --------------------------------------------------------------- sections */

/* The page column is the section's own padding, not an `auto` margin on each
   child — otherwise every child with a narrower max-width (a title, a lede)
   silently centres itself inside it. */
.section {
  padding-block: var(--band);
  padding-inline: max(var(--gutter), calc((100% - var(--page)) / 2));
  border-top: 1px solid var(--rule);
}

.section--tint {
  background: var(--paper-tint);
}

.section__title {
  max-width: 22ch;
  margin-top: 0.9rem;
  font-family: var(--display);
  font-weight: 700;
  font-stretch: 116%;
  font-size: clamp(1.9rem, 4.4vw, 3rem);
  line-height: 1.03;
  letter-spacing: -0.028em;
}

.section__lede {
  max-width: 42rem;
  margin-top: 1.25rem;
  font-size: 1.05rem;
  color: var(--ink-soft);
}

.section__foot {
  margin-top: 2rem;
}

/* ------------------------------------------------------------- 4711 cards */

.objects {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1px;
  margin-top: 2.8rem;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}

.object {
  display: grid;
  align-content: start;
  gap: 0.35rem;
  padding: 1.35rem 1.4rem 1.5rem;
  background: var(--card);
}

.object__id {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 1.02rem;
  letter-spacing: 0.01em;
  color: var(--violet-deep);
}

.object__type {
  font-family: var(--display);
  font-weight: 600;
  font-stretch: 110%;
  font-size: 0.9rem;
  letter-spacing: 0.01em;
}

.object__body {
  font-size: 0.92rem;
  color: var(--ink-soft);
}

.object__src {
  margin-top: 0.35rem;
  font-family: var(--mono);
  font-size: 0.73rem;
  color: var(--slate);
}

.chain {
  margin-top: 2.6rem;
  padding: 1.7rem 1.8rem 1.8rem;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  background: var(--card);
}

.chain__title {
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--slate);
}

.chain__path {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem 0.6rem;
  margin-top: 1rem;
}

.chain__node {
  font-family: var(--mono);
  font-size: 0.84rem;
  padding: 0.28rem 0.6rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: var(--paper);
}

.chain__node--hot {
  border-color: var(--violet);
  background: var(--wash);
  color: var(--violet-deep);
}

.chain__edge {
  font-size: 0.78rem;
  color: var(--slate);
  font-style: italic;
}

.chain__edge::before {
  content: "→ ";
  font-style: normal;
  color: var(--rule-strong);
}

.chain__foot {
  max-width: 46rem;
  margin-top: 1.4rem;
  font-size: 0.95rem;
  color: var(--ink-soft);
}

/* ----------------------------------------------------------------- panels */

.grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: 1.5rem;
  margin-top: 2.8rem;
}

.grid-2--wide-first {
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
}

.panel {
  display: grid;
  align-content: start;
  gap: 1rem;
  padding: 1.5rem 1.6rem 1.6rem;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.panel--wide {
  margin-top: 1.5rem;
}

.panel__head {
  font-family: var(--display);
  font-weight: 600;
  font-stretch: 112%;
  font-size: 0.95rem;
  letter-spacing: 0.005em;
}

.panel__foot {
  font-size: 0.89rem;
  color: var(--slate);
}

/* A panel spanning the page is wide enough that its note would otherwise run
   to a 140-character line. The code block above it still uses the full width. */
.panel--wide .panel__foot {
  max-width: 64ch;
}

.code {
  overflow-x: auto;
  margin: 0;
  padding: 1rem 1.1rem;
  background: var(--ink);
  color: #d8dcea;
  border-radius: var(--radius);
  font-size: 0.78rem;
  line-height: 1.72;
  tab-size: 2;
}

/* The closing command is one short line. Left at full page width the block
   reads as an empty band rather than as a thing to copy, so it hugs its text
   and only falls back to scrolling when the viewport is narrower than it. */
.code--wide {
  width: fit-content;
  max-width: 100%;
  margin-top: 1.8rem;
  padding: 1.15rem 1.4rem;
  font-size: 0.86rem;
}

.code .k {
  color: #9db8ff;
}

.code .s {
  color: #b7e3a8;
}

.code .c {
  color: #7c86a4;
}

.code .kw {
  color: #c9a2ff;
}

.code .t {
  color: #6fd6e8;
}

/* ----------------------------------------------------------------- checks */

/* Four checks, so two columns rather than an auto-fit three that leaves the
   fourth alone on a row of its own. */
.checks {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.8rem 3rem;
  margin-top: 2.8rem;
  max-width: 68rem;
}

/* align-content, not just align-items: the cell is stretched to the tallest
   check in its row, and without this the surplus is shared between the two
   rows — which silently pushes the shortest check's text away from its rule. */
.check {
  display: grid;
  align-content: start;
  gap: 0.5rem;
  padding-top: 1rem;
  border-top: 2px solid var(--ink);
}

.check dt code {
  font-size: 0.88rem;
  font-weight: 500;
  background: none;
  padding: 0;
}

.check dd {
  margin: 0;
  font-size: 0.93rem;
  color: var(--ink-soft);
}

.caveat {
  max-width: 44rem;
  margin-top: 2.8rem;
  padding: 1.15rem 1.3rem;
  border-left: 3px solid var(--violet);
  background: var(--wash);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 0.95rem;
  color: var(--ink-soft);
}

.caveat strong {
  color: var(--ink);
}

/* ---------------------------------------------------------- domain table */

.domains {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.domains th,
.domains td {
  text-align: left;
  padding: 0.52rem 0.7rem 0.52rem 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}

.domains th {
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--slate);
  border-bottom-color: var(--rule-strong);
}

.domains tbody tr:last-child td {
  border-bottom: 0;
}

.domains__name {
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
  font-family: var(--mono);
  white-space: nowrap;
}

.domains__name::before {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 100px;
  background: var(--thread);
  transform: translateY(-1px);
}

.domains__core {
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--slate);
}

.domains__deps,
.domains__gives {
  color: var(--ink-soft);
}

.domains__deps--none {
  color: var(--rule-strong);
}

/* ---------------------------------------------------------------- closing */

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

.closing__note {
  max-width: var(--measure);
  margin-top: 1.2rem;
  font-size: 0.95rem;
  color: var(--slate);
}

/* ----------------------------------------------------------------- footer */

.footer {
  padding-block: var(--band) 3.5rem;
  padding-inline: max(var(--gutter), calc((100% - var(--page)) / 2));
  background: var(--ink);
  color: #b9c0d4;
  font-size: 0.9rem;
}

.footer a {
  color: #e7e9f2;
  text-decoration-color: color-mix(in srgb, #ffffff 30%, transparent);
}

.footer a:hover {
  color: #ffffff;
}

.footer__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 2.2rem;
}

.footer__head {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: #7c86a4;
  margin-bottom: 0.7rem;
}

.footer__cols li {
  padding: 0.16rem 0;
}

/* The rule divides the whole footer, so it spans the whole footer; the measure
   belongs to the sentence, not to the division. Putting max-width on the
   paragraph itself would stop the rule two thirds of the way across. */
.footer__synthetic {
  margin-top: 3rem;
  padding-top: 1.6rem;
  border-top: 1px solid #2a3350;
  font-size: 0.85rem;
  color: #8a93ad;
}

.footer__synthetic > span {
  display: block;
  max-width: 46rem;
}

.footer__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.6rem 2rem;
  margin-top: 1.6rem;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: #7c86a4;
}

/* ------------------------------------------------------------ responsive */

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

  .readout__item--accent {
    grid-column: span 2;
  }
}

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

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

  /* Two columns of a phone is fourteen characters a line, and it breaks the
     command names across two lines as well. One column. */
  .checks {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The closing command is the one thing on the page a reader is meant to run.
     Wrapping it at the spaces shows all of it; scrolling it sideways hides
     half of it behind an edge with nothing to say it is there. */
  .code--wide {
    width: auto;
    white-space: pre-wrap;
  }

  .eyebrow__sep {
    display: none;
  }

  .masthead__nav a:not(.masthead__repo) {
    display: none;
  }

  .chain__path {
    gap: 0.35rem 0.5rem;
  }
}

/* ----------------------------------------------------------- page-load weave

   One orchestrated moment: the cloth weaves itself in generation order, so
   what you watch is the pipeline running. Nothing else on the page animates
   on load. */

@media (prefers-reduced-motion: no-preference) {
  /* The dash is only here for the draw-on; leaving it set afterwards would
     override .warp--off, which uses a dash of its own to mean "not selected". */
  .loom__svg[data-weaving="true"] .warp {
    stroke-dasharray: var(--len);
    stroke-dashoffset: var(--len);
    animation: draw-warp 620ms cubic-bezier(0.33, 0.9, 0.4, 1) forwards;
    animation-delay: var(--delay, 0ms);
  }

  .loom__svg[data-weaving="true"] .cross {
    opacity: 0;
    animation: knot 300ms ease forwards;
    animation-delay: var(--delay, 0ms);
  }

  @keyframes draw-warp {
    to {
      stroke-dashoffset: 0;
    }
  }

  @keyframes knot {
    from {
      opacity: 0;
      transform: scale(0.5);
    }
    to {
      opacity: 1;
      transform: scale(1);
    }
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .readout__value[data-changed="true"] {
    animation: none;
  }

  .warp,
  .cross,
  .thread-toggle {
    transition: none;
  }
}
