/* Logical Art — shared shell.
 *
 * Source of truth: siteops/shared/logical-art/logical-art.css
 * Vendored into each machine's site/css/ by scripts/sync-logical-art.py.
 * DO NOT EDIT THE VENDORED COPIES — the pre-deploy drift gate compares sha256
 * and will fail the build.
 *
 * Two rules govern everything here:
 *
 *   1. The shell is shared and neutral. Surfaces, ink, hairlines, type scale,
 *      focus ring and the studio footer are identical on every machine, so the
 *      family reads as one studio.
 *
 *   2. The accent belongs to the machine, and it has to be earned. Front Panel's
 *      red is sampled off the Altair's lamps; it is not a brand colour, it is
 *      the colour that hardware actually was. Each site sets exactly one
 *      variable, --la-instrument, and says in a comment where the value came
 *      from. A machine that cannot cite its accent should use the neutral.
 *
 * Everything is namespaced --la-* so a site can adopt this incrementally by
 * pointing its own existing variables at these, without a big-bang rewrite.
 */

:root {
  /* ---- Surfaces -----------------------------------------------------------
     Neutral charcoal, never pure black. The family used to run #0b0e11 on one
     site and #0b0d0c on the other, which was drift rather than a decision;
     this is the single reconciled value. */
  --la-page: #0b0d0f;
  --la-raised: #101315;
  --la-sunken: #08090b;
  --la-card: #141819;

  /* ---- Ink ----------------------------------------------------------------
     Never pure white. Measured against --la-page: --la-ink 16.24:1,
     --la-ink-dim 7.75:1, --la-ink-faint 5.02:1. All three clear AA for body
     text at every size we ship, so the faint tier is safe for the colophon. */
  --la-ink: #e7ebee;
  --la-ink-dim: #9aa5ad;
  --la-ink-faint: #78838b;

  /* ---- Lines -------------------------------------------------------------- */
  --la-hairline: rgba(255, 255, 255, 0.09);
  --la-hairline-strong: rgba(255, 255, 255, 0.18);

  /* ---- Instrument accent --------------------------------------------------
     Overridden per machine. The default is ACHROMATIC on purpose — 0%
     saturation, not a tasteful grey-blue.
     The first attempt used #8fa3b0, and measuring it showed why that was
     wrong: hue 204deg at 17% saturation, the same saturation as the ink. At
     the ink's near-white luminance that cast is invisible, but at an accent's
     mid luminance it reads plainly as blue — so the studio page, whose whole
     job is to frame a red machine and a green one, had quietly acquired a
     third hue competing with both.
     The studio is a gallery wall. It has no instrument, so it gets no hue, and
     that is the only choice that still works when there are four machines.
     10.36:1 on --la-page: brighter than --la-ink-dim so a hover or focus ring
     reads as emphasis, dimmer than --la-ink so it never competes with a
     heading. */
  --la-instrument: #bdbdbd;
  /* Use for solid fills behind white text. A bright accent that passes as text
     on dark will usually fail as a background, so this is a separate token
     rather than a computed shade. 6.69:1 against #fff. */
  --la-instrument-solid: #5c5c5c;

  /* ---- Tier colours -------------------------------------------------------
     Bit, nibble and byte, colour-coded so the size of a thing is legible at a
     glance without reading the word.

     These are studio metadata, not machine accents, and the palette says so:
     every earned machine accent sits between hue 0 and 160 (Front Panel's red
     at 4deg, Rangekeeper's brass at 46deg, Punch Cards' card stock at 38deg,
     LLM Lab's emerald at 160deg). The tiers are deliberately parked in the
     cool half, 190 to 256deg, where no machine has a claim. Warm means a
     machine; cool means a fact about the machine. That keeps the "an accent is
     earned" rule intact while still giving the tiers a consistent colour.

     Measured against --la-page: 11.27:1, 8.36:1 and 7.73:1. */
  --la-tier-bit: #6fd3e8;
  --la-tier-nibble: #b49cf5;
  --la-tier-byte: #7aa2f7;

  /* ---- Type ---------------------------------------------------------------
     System stacks by default: no webfont, no preload, no layout shift, and it
     matches Front Panel's existing restraint. A machine that wants a display
     face overrides --la-font-display only, and only for h1/h2. */
  --la-font-ui: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
                Helvetica, Arial, sans-serif;
  --la-font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
                  monospace;
  --la-font-display: var(--la-font-ui);

  /* Fluid scale, 320px → 1200px viewport. */
  --la-text-xs: clamp(0.76rem, 0.74rem + 0.10vw, 0.82rem);
  --la-text-sm: clamp(0.86rem, 0.83rem + 0.15vw, 0.94rem);
  --la-text-base: clamp(1rem, 0.97rem + 0.16vw, 1.08rem);
  /* Added 2026-08-13, because six machines were already asking for it. Nine
     declarations across hamming, crc, masking, baudot, interleave and pagerank
     read var(--la-text-md) for their step headings, and the token did not
     exist. An invalid var() reference is dropped, so every one of those
     font-sizes silently fell back to the inherited body size and the step
     headings were the same size as the prose under them. Nothing reports this:
     the stylesheet parses, the rule is present, and only the computed value
     shows it. */
  --la-text-md: clamp(1.06rem, 1.0rem + 0.26vw, 1.19rem);
  --la-text-lg: clamp(1.12rem, 1.05rem + 0.35vw, 1.3rem);
  --la-text-xl: clamp(1.35rem, 1.22rem + 0.62vw, 1.75rem);
  --la-text-2xl: clamp(1.7rem, 1.44rem + 1.3vw, 2.4rem);
  --la-text-3xl: clamp(2.2rem, 1.7rem + 2.4vw, 3.4rem);

  --la-leading-tight: 1.2;
  --la-leading-body: 1.62;

  /* ---- Space, radius, motion ---------------------------------------------- */
  --la-space-1: 0.35rem;
  --la-space-2: 0.6rem;
  --la-space-3: 1rem;
  --la-space-4: 1.5rem;
  --la-space-5: 2.25rem;
  --la-space-6: 3.5rem;

  --la-radius-sm: 0.35rem;
  --la-radius: 0.7rem;
  --la-radius-lg: 1rem;

  /* A length, not 68ch. `ch` resolves against the font size of whatever
     element consumes the token, so one --la-measure produced a different box
     per element: the colophon at --la-text-xs got a column roughly a fifth
     narrower than the page it sits in, which orphaned the last word of the
     credit line onto its own row. A rem is the same width everywhere it is
     used, which is the only way a shared measure can mean one thing. */
  --la-measure: 40rem;
  --la-transition: 0.2s ease;
}

/* Nothing here is theme-switchable: these are instruments, and instruments are
   dark. But contrast preferences are honoured. */
@media (prefers-contrast: more) {
  :root {
    --la-ink-dim: #d3d9dd;
    --la-ink-faint: #c2c9ce;
    --la-hairline: rgba(255, 255, 255, 0.42);
    --la-hairline-strong: rgba(255, 255, 255, 0.6);
  }
}

@media (prefers-reduced-motion: reduce) {
  :root { --la-transition: 0s; }
}

/* ---- Shared focus ring ----------------------------------------------------
   One visible ring across the family. Sites should not redefine this. */
.la-focusable:focus-visible,
.la-footer a:focus-visible,
.la-footer button:focus-visible {
  outline: 2px solid var(--la-instrument);
  outline-offset: 3px;
  border-radius: var(--la-radius-sm);
}

/* ---- The studio footer ----------------------------------------------------
   Markup is generated per-site from machines.json and injected between
   <!-- la:footer --> markers. Light DOM on purpose: these are the only
   cross-machine links in the family and they must be in the served HTML, not
   assembled by script at render time. */
.la-footer {
  /* Width is a property a host imposes too, and this is the second time that
     lesson has been learned on this component. text-align was declared here
     after it shipped centred on two machines of four; width was not, so on
     frontpanel.dev, whose whole layout is a full-bleed Altair panel, the
     footer ran to 1,240 pixels and set its prose on a line nobody can read,
     under a rule spanning the entire viewport. logicalart.us looked right only
     because it happens to sit inside a main that already has a measure.

     A shared component borrowing its host's line length is not shared. Prose
     has a readable measure whatever the page around it is doing. */
  max-width: var(--la-measure);
  margin-inline: auto;

  margin-top: var(--la-space-4);
  padding: var(--la-space-4) 0 var(--la-space-2);
  border-top: 1px solid var(--la-hairline);
  color: var(--la-ink-dim);
  font-family: var(--la-font-ui);
  font-size: var(--la-text-sm);
  line-height: var(--la-leading-body);

  /* Everything below is a property a host page can impose, declared here so
     that it cannot. This footer is dropped into four different sites and it
     inherited text-align from whatever wrapper it landed in. frontpanel.dev
     and watchitlearn.com both centre their page copy, so the machine list
     rendered with each era stranded in the left gutter and every name and
     blurb centred, while 300baud.dev rendered the identical markup from the
     identical stylesheet correctly. Nothing was missing and nothing was
     malformed, so no static check could see it.

     A shared component that depends on its host is not shared. Gated by
     scripts/la-footer-parity.mjs. */
  text-align: left;
  direction: ltr;
  text-transform: none;
  letter-spacing: normal;
  font-style: normal;
  white-space: normal;
}

.la-footer p { margin: 0 0 var(--la-space-2); max-width: var(--la-measure); }
.la-footer p:last-child { margin-bottom: 0; }

.la-footer a {
  color: var(--la-ink);
  text-decoration: underline;
  text-decoration-color: var(--la-hairline-strong);
  text-underline-offset: 0.18em;
  transition: color var(--la-transition),
              text-decoration-color var(--la-transition);
}

.la-footer a:hover {
  color: var(--la-instrument);
  text-decoration-color: currentColor;
}

.la-footer .la-studio-name { font-weight: 600; }

/* The sibling-machine list. Deliberately a real <ul> of real <a href>: it is
   the studio's internal link graph. */
/* The columns live on the <ul> and each <li> borrows them with subgrid, so the
   eras line up down the page. Putting the grid on the li instead gives every
   row its own track sizing and the machine names stagger. Subgrid went
   Baseline Widely Available in March 2026; the @supports block below keeps the
   old per-row behaviour for anything older, which is untidy but not broken.
   display:contents would also work and is not used: it removes the list-item
   box, which is a real a11y risk for no gain here. */
.la-footer .la-siblings {
  list-style: none;
  margin: var(--la-space-2) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--la-space-1) var(--la-space-3);
}

/* Two real columns, so the eras line up and the list reads as the timeline it
   is: one machine from each point in fifty years. A 4px inline margin was
   doing the job before, badly. */
.la-footer .la-siblings li {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  align-items: baseline;
}

@supports not (grid-template-columns: subgrid) {
  .la-footer .la-siblings li {
    grid-template-columns: max-content 1fr;
    gap: 0 var(--la-space-3);
  }
}

.la-footer .la-era {
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--la-ink-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Narrow screens: "1930s-1991" plus a blurb in two columns leaves the blurb
   too thin, so stack and indent instead of squeezing. */
@media (max-width: 30rem) {
  .la-footer .la-siblings { grid-template-columns: 1fr; }
  .la-footer .la-siblings li { grid-template-columns: 1fr; }
  .la-footer .la-siblings .la-machine { padding-left: var(--la-space-2); }
}

/* The blurb sits under the machine's name as its own sentence. It used to be
   a clause hung off an em-dash, which reads as generated copy rather than as
   somebody describing their own work. */
.la-footer .la-blurb {
  display: block;
  color: var(--la-ink-faint);
}

.la-footer .la-soon {
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--la-ink-faint);
  border: 1px solid var(--la-hairline);
  border-radius: var(--la-radius-sm);
  padding: 0.05em 0.45em;
  white-space: nowrap;
}

/* The small pieces, as one sentence rather than a second list. Bits and
   nibbles all live on the studio, so this is a link back to it and not a
   second cross-domain graph; a list of its own would put a dozen
   logicalart.us links into the footer of four other sites. */
.la-footer .la-small {
  margin-top: var(--la-space-2);
  color: var(--la-ink-faint);
}

.la-footer .la-colophon {
  margin-top: var(--la-space-3);
  color: var(--la-ink-faint);
  font-size: var(--la-text-xs);
}

/* ---- <la-progress> --------------------------------------------------------
   Styled here rather than inside a shadow root on purpose. Every Logical Art
   site ships `style-src 'self'` with no unsafe-inline, and Chromium blocks
   both an injected <style> and a constructable stylesheet under that policy,
   so a shadow-DOM widget renders unstyled while everything else looks fine.
   The component is light DOM and reads these rules like any other element. */
la-progress {
  display: block;
  font-family: var(--la-font-ui);
  font-size: var(--la-text-xs);
  color: var(--la-ink-faint);
}

/* Nothing to say until at least one chapter has been read. */
la-progress:not([data-started]) { visibility: hidden; }

.la-progress-row { display: flex; align-items: center; gap: var(--la-space-2); }

.la-progress-count {
  font-family: var(--la-font-mono);
  white-space: nowrap;
}

.la-progress-track {
  flex: 1 1 auto;
  height: 2px;
  background: var(--la-hairline);
  border-radius: 2px;
  overflow: hidden;
}

.la-progress-fill {
  display: block;
  height: 100%;
  width: 0%;
  background: var(--la-instrument);
  transition: width var(--la-transition);
}

la-progress button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

la-progress button:hover { color: var(--la-instrument); }

la-progress button:focus-visible {
  outline: 2px solid var(--la-instrument);
  outline-offset: 3px;
  border-radius: var(--la-radius-sm);
}

/* ---- 404 ------------------------------------------------------------------
   Shared so a lost visitor lands somewhere that looks like the studio and can
   see the other machines. Styled here rather than in a page-level <style>:
   logicalart.us serves style-src 'self' with no unsafe-inline, so its 404 was
   rendering completely unstyled in production (found 2026-08-06) - the inline
   block was refused and nothing replaced it. */
body.la-notfound {
  margin: 0;
  min-height: 100vh;
  background: var(--la-page);
  color: var(--la-ink);
  font-family: var(--la-font-ui);
  line-height: var(--la-leading-body);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--la-space-5) var(--la-space-4);
}

body.la-notfound main { width: 100%; max-width: var(--la-measure); margin: 0 auto; }
body.la-notfound h1 { font-size: var(--la-text-2xl); line-height: var(--la-leading-tight); margin: 0 0 var(--la-space-2); }
body.la-notfound p { margin: 0 0 var(--la-space-3); color: var(--la-ink-dim); }
body.la-notfound a { color: var(--la-ink); text-decoration: underline; text-underline-offset: 0.18em; }
body.la-notfound a:hover { color: var(--la-instrument); }
body.la-notfound .la-footer { max-width: var(--la-measure); margin-inline: auto; width: 100%; }


/* The tier-to-colour mapping. In the SHELL, not the studio stylesheet: every
   page type that renders a tier pill needs it, and only the studio index loads
   studio.css. Putting it there made a bit page's pill fall back to the neutral
   while the same pill on the index was cyan.

   --tier is set on the ROW (the li carries data-tier) and inherited by the
   pill. It must NOT be given a default on the pill itself: a custom property
   declared directly on an element beats the one it would have inherited, so a
   `

/* Tier to colour. In the SHELL rather than the studio stylesheet, because
   every page type that renders a tier pill needs this mapping and only the
   studio index loads studio.css. Kept there, a bit page's pill fell back to
   the neutral while the identical pill on the index was cyan.

   Set on the ancestor and inherited by the pill: never declare a default for
   --tier on the pill itself, or it shadows the inherited value and every tier
   comes out the same colour. The fallback belongs in the var() that reads it. */
[data-tier="bit"] { --tier: var(--la-tier-bit); }
[data-tier="nibble"] { --tier: var(--la-tier-nibble); }
[data-tier="byte"] { --tier: var(--la-tier-byte); }

/* The page kicker: a breadcrumb back to the studio plus the page's own
   metadata pills. Lives in the shell because three different page types render
   it (a bit page, a nibble page, a tier index) and only one of them loads the
   studio's stylesheet. It was previously .bit-kicker in punch-cards.css, which
   meant every page that was not a bit rendered it unstyled. */
.la-kicker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--la-space-2);
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--la-ink-faint);
}

.la-kicker a {
  color: var(--la-ink-dim);
  text-decoration: underline;
  text-decoration-color: var(--la-hairline-strong);
  text-underline-offset: 0.18em;
}

.la-kicker a:hover { color: var(--machine, var(--la-instrument)); }

/* A metadata pill. Neutral by default; takes the tier colour when the element
   carries data-tier, exactly like the pills on the studio timeline. */
.la-pill {
  border: 1px solid var(--la-hairline);
  border-radius: 999px;
  padding: 0.15em 0.6em;
  white-space: nowrap;
}

.la-pill[data-tier] {
  color: var(--tier, var(--la-instrument));
  background: color-mix(in srgb, var(--tier, var(--la-instrument)) 12%, transparent);
  border-color: color-mix(in srgb, var(--tier, var(--la-instrument)) 42%, transparent);
}

a.la-pill { text-decoration: none; }

a.la-pill:hover {
  border-color: var(--tier, var(--la-instrument));
}

a.la-pill:focus-visible {
  outline: 2px solid var(--tier, var(--la-instrument));
  outline-offset: 3px;
}

/* The machine's title and standfirst, which sit directly under the kicker
   above and belong here for exactly the reason the kicker does.

   They were declared in each machine's own stylesheet, and by 2026-08-13 that
   had produced three different headers across seventeen machines: five at
   --la-text-3xl with letter-spacing, five at --la-text-2xl without, and SEVEN
   with no rule at all, rendering the title at the browser's default 32px and
   the standfirst as ordinary body text. Measured in a browser rather than read
   off the stylesheets, because a rule existing proves nothing about what
   renders.

   Nothing could see it. Every page looked deliberate on its own, and no gate
   compares one machine against another; la-machine-audit reported the class as
   styled by nothing and, being report-only, was read as noise for weeks.

   3xl rather than 2xl on the tie: --la-text-3xl has no other consumer in the
   family, so standardising on 2xl would orphan a step of the scale, and it
   would leave a machine's title smaller than the 404 page's. */
.bit-head h1 {
  margin-top: var(--la-space-2);
  font-family: var(--la-font-display);
  font-size: var(--la-text-3xl);
  line-height: var(--la-leading-tight);
  letter-spacing: -0.02em;
}

/* line-height is stated rather than inherited: a shared component declares
   every property a host can impose, which is the rule the studio footer
   learned when it inherited text-align and shipped centred on two sites. */
.bit-lede {
  margin-top: var(--la-space-2);
  color: var(--la-ink-dim);
  font-size: var(--la-text-lg);
  line-height: var(--la-leading-body);
}

/* The panel the instrument sits in: everything you operate is inside it, and
   the prose about the machine is outside it. That boundary is the studio's
   central claim made visible, which is why it earns a container at all rather
   than being left to whitespace.

   Quiet on purpose. --la-raised differs from --la-page by about five parts in
   255, so this reads as a hairline boundary and not as a block of colour.
   That matters: NN/g's warning about common regions is that a full-width band
   of contrasting background creates a false floor and stops people scrolling,
   and the honesty ledger lives below this panel on every machine.

   One definition. It was eight identical copies in eight machine stylesheets,
   byte for byte, with ten more machines simply missing it, and the split was
   exactly chronological: every machine built on 2026-08-12 or later had it and
   every machine built on 2026-08-11 or earlier did not. A house style adopted
   mid-roster and never backfilled.

   Named .la-panel and not .la-machine because .la-machine already means a name
   in the studio footer's sibling list, and that footer is inside <main> on
   every machine page, so the obvious name would have drawn a card around every
   entry in it. */
/* The machine's accent, resolved once, for every machine.

   Declared on body because that is where --machine is: la-machines.css emits
   [data-machine="crc"] { --machine: #7ec8a9 } and the attribute is on <body>.
   Ten live machines have no accent in the roster on purpose and are achromatic,
   so the fallback is load-bearing rather than defensive.

   Every machine used to declare its own alias on its own root, and all three
   colour bugs found on 2026-08-13 were that pattern failing in a different way:
   --cr-drop was declared on .hm, which is a different machine's root and never
   matched; --hm-named was read five times and declared nowhere, which made the
   bit Hamming names render page-coloured on nothing; --pr-mark was declared on
   .pr and read from outside it. CSS reports none of these, because an invalid
   var() is dropped silently and the property falls back to something plausible.

   One declaration on an element every machine inherits from removes the class
   of defect rather than the three instances of it. The semantic names were an
   illusion anyway: the studio's own rule is that colour marks exactly one
   measured thing per machine, so there was never more than one role to name,
   and CRC had two names for the same value. */
body {
  --accent: var(--machine, var(--la-instrument));
}

.la-panel {
  margin-top: var(--la-space-5);
  padding: var(--la-space-4);
  background: var(--la-raised);
  border: 1px solid var(--la-hairline);
  border-radius: var(--la-radius);
}

/* On a narrow screen the panel's side padding doubles up with main's own, and
   the instrument pays for it. Bitwise's bit buttons went to 17px at 320px and
   the render matrix failed the build on WCAG 2.5.8, which is exactly what that
   gate is for.

   Measured rather than guessed, at 320px: 1.5rem gives a 16.9px button on a
   20.1px pitch and fails; 0.6rem gives 20.5 on 23.7 and still fails; 0.35rem
   gives 21.5 on 24.7 and passes by seven tenths of a pixel, which is not a
   margin worth shipping. Zero gives 22.9 on 26.1, which is exactly the geometry
   every machine had before the panel existed, so no machine can regress at this
   width against the state that was already passing.

   The border still delimits the instrument; only the doubled padding goes. */
@media (max-width: 30rem) {
  .la-panel {
    padding-left: 0;
    padding-right: 0;
  }
}

/* Wrapped separately from the meter for two reasons: the two align on the
   baseline rather than the meter riding on the text's descender, and the name
   can be given a fixed width.

   The width matters. Bit, Nibble and Byte are three, six and four characters,
   so left to themselves the pills come out three different widths and a
   vertical stack of them looks ragged rather than tabular. The pill font is
   the mono stack, where 1ch is exactly one character, so 6ch is exactly the
   longest of the three names and every pill lands on the same width. */
.tier-name {
  display: inline-block;
  width: 6ch;
  letter-spacing: 0.02em;
}

/* Visually hidden, but present for assistive technology. Used to name a link's
   destination when the visible cue for it is an icon. clip-path rather than the
   old clip rect; width/height of 1px rather than 0 so the text is still
   rendered and therefore still announced. */
.la-vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The outbound marker. Deliberately quiet: it is a hint, not a warning, and it
   must never compete with the link it follows. aria-hidden in the markup, so
   the arrow is never announced; the .la-vh span beside it names the host
   instead, which is what a screen reader user actually lacks. */
.la-ext {
  display: inline-block;
  margin-left: 0.28em;
  font-size: 0.72em;
  line-height: 1;
  vertical-align: 0.18em;
  color: var(--la-ink-faint);
  transition: color var(--la-transition);
}

.la-footer a:hover .la-ext { color: currentColor; }

/* Links inside the shared content blocks.

   These had no rule at all, so on any site without a global `a` rule they
   rendered as the browser default blue: roughly 1.6:1 against --la-page, which
   is unreadable and looks like a stylesheet failed to load. Every source
   citation on the punch-cards page was doing exactly that.

   The shell owns this because the blocks are the shell's. A shared component
   that only looks right when the host site happens to define a global `a` rule
   is not actually shared, and the two sites where this was invisible
   (300baud.dev, frontpanel.dev) were invisible by luck.

   Hover prefers the machine's own colour where one exists and falls back to
   the instrument, so the same rule is right on a machine page and on the
   studio. */
/* The floor, so a link can never fall through to browser blue.
   The rules below enumerate class names, which makes them an allowlist: any
   context nobody thought of gets the user-agent default. That is how the
   citation links once rendered at 1.6:1, and it happened again in machine
   prose, where a cross-link to Bitwise and two patent links came out at
   rgb(0,0,238) against the page for 2.07:1. Both Core Memory and Bit, Nibble,
   Byte shipped that way.

   Specificity 0-0-2 on purpose: every class-based rule below still wins, so
   this changes nothing that already had an author colour and rescues
   everything that did not. The user-agent underline is already correct, so
   colour is the whole fix. */
main a { color: var(--la-ink); }

.la-faq a,
.la-glossary a,
.la-sources a,
.la-whats-new a {
  color: var(--la-ink);
  text-decoration: underline;
  text-decoration-color: var(--la-hairline-strong);
  text-underline-offset: 0.18em;
  transition: color var(--la-transition),
              text-decoration-color var(--la-transition);
}

.la-faq a:hover,
.la-glossary a:hover,
.la-sources a:hover,
.la-whats-new a:hover {
  color: var(--machine, var(--la-instrument));
  text-decoration-color: currentColor;
}

.la-faq a:focus-visible,
.la-glossary a:focus-visible,
.la-sources a:focus-visible,
.la-whats-new a:focus-visible {
  outline: 2px solid var(--machine, var(--la-instrument));
  outline-offset: 3px;
  border-radius: var(--la-radius-sm);
}

/* ---- The section spine ----------------------------------------------------
   Every machine ends the same way: questions, a glossary, sources, and an
   honest account of what is not real. Those four are the studio's promise as
   much as the working machine is, so they are styled here rather than three
   times over, and a new machine gets them for free.

   A site opts in by using these class names. Nothing breaks if it does not;
   the older machines still carry their own equivalents and can move across
   one at a time. */

/* Cards, not hairline rules. This was a list separated by borders, and
   frontpanel.dev had a better one of its own: a bordered card on a raised
   surface with a chevron drawn in CSS rather than typed, rotating as the
   disclosure opens. Keeping both was two implementations of one surface, and
   converging on the weaker one to settle it would have been the wrong way
   round. Promoted here so every machine gets it, in its own accent.

   Spacing lives on the details rather than a flex container on purpose: the
   container is a <section> on 300baud.dev that also holds the heading, and
   making it a flex parent would have re-spaced that heading too. */
.la-faq details {
  margin-bottom: 0.5rem;
  border: 1px solid var(--la-hairline);
  border-radius: var(--la-radius-sm);
  background: var(--la-raised);
  overflow: hidden;
}

.la-faq details:last-of-type { margin-bottom: 0; }

.la-faq summary {
  cursor: pointer;
  padding: 0.95rem 1.1rem;
  font-weight: 600;
  color: var(--la-ink);
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.la-faq summary::-webkit-details-marker { display: none; }

/* A plain chevron, drawn rather than typed, so it needs no icon font. */
.la-faq summary::before {
  content: "";
  flex: none;
  width: 0.4rem;
  height: 0.4rem;
  border-right: 1.5px solid var(--machine, var(--la-instrument));
  border-bottom: 1.5px solid var(--machine, var(--la-instrument));
  transform: rotate(-45deg);
  transition: transform var(--la-transition);
}

.la-faq details[open] summary::before { transform: rotate(45deg); }

.la-faq summary:hover { color: var(--la-ink-bright, #fff); }

.la-faq details p {
  margin: 0;
  padding: 0 1.1rem 1.1rem 2.85rem;
  color: var(--la-ink-dim);
}

@media (prefers-reduced-motion: reduce) {
  .la-faq summary::before { transition: none; }
}

.la-glossary {
  margin: 0;
}

.la-glossary dt {
  margin-top: var(--la-space-3);
  color: var(--la-ink);
  font-weight: 600;
}

.la-glossary dt:first-child {
  margin-top: 0;
}

.la-glossary dd {
  margin: 0.25rem 0 0;
  color: var(--la-ink-dim);
}

.la-sources {
  margin: 0;
  padding-left: 1.1rem;
  color: var(--la-ink-dim);
}

.la-sources li {
  margin-bottom: var(--la-space-2);
}

.la-sources li:last-child {
  margin-bottom: 0;
}

/* ---- What's new -----------------------------------------------------------
   A dated changelog in the footer. A machine that is still being worked on
   should say so; it is also the honest place to record what changed under a
   visitor who last came a month ago. */
.la-whats-new {
  /* Same measure as the footer it sits beside, for the same reason and found
     the same way: the footer was constrained first and this was left behind,
     so on frontpanel.dev the changelog ran the full 1,240 while the footer
     under it sat centred at 640. A shared block that does not own its width
     will be as wide as whatever page it lands on. */
  max-width: var(--la-measure);
  margin-inline: auto;

  margin-top: var(--la-space-3);
  font-size: var(--la-text-xs);
}

.la-whats-new summary {
  cursor: pointer;
  color: var(--la-ink-dim);
  width: fit-content;
}

.la-whats-new summary:hover { color: var(--la-instrument); }

.la-whats-new summary:focus-visible {
  outline: 2px solid var(--la-instrument);
  outline-offset: 3px;
  border-radius: var(--la-radius-sm);
}

.la-whats-new ul {
  margin: var(--la-space-2) 0 0;
  padding-left: 1.1rem;
  display: grid;
  gap: var(--la-space-2);
  max-width: var(--la-measure);
}

.la-whats-new li { color: var(--la-ink-faint); }
.la-whats-new strong { color: var(--la-ink-dim); }

/* Where to go after a machine. Every page used to end with the whole roster in
   the footer and nothing else, so the only move a reader had was to scan twenty
   names for one they recognised. Two chronological neighbours, then up to three
   machines on the same subject, all generated from the roster. */
.prevnext {
  margin-top: var(--la-space-5);
  padding-top: var(--la-space-4);
  border-top: 1px solid var(--la-hairline);
}

.pn-steps {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--la-space-3);
}

.pn-steps a {
  display: grid;
  gap: 0.15rem;
  padding: var(--la-space-2) var(--la-space-3);
  border: 1px solid var(--la-hairline);
  border-radius: var(--la-radius, 6px);
  text-decoration: none;
  color: var(--la-ink);
}

.pn-steps a:hover { border-color: var(--accent, var(--la-instrument)); }

.pn-steps a:focus-visible {
  outline: 2px solid var(--accent, var(--la-instrument));
  outline-offset: 3px;
}

/* "Later" sits on the right and reads right-aligned, so the pair works as a
   direction rather than as two identical cards. Selected on rel, which the
   markup needs anyway, rather than on a class that existed only to be styled:
   the dead-class check flagged pn-prev the moment it shipped, correctly. */
.pn-steps a[rel="next"] { text-align: right; }

.pn-dir {
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--la-ink-faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.pn-name { font-weight: 600; }

.pn-era {
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--la-ink-faint);
}

.pn-kin-label {
  margin: var(--la-space-4) 0 var(--la-space-2);
  font-size: var(--la-text-sm);
  color: var(--la-ink-dim);
}

.pn-topics {
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--accent, var(--la-instrument));
}

.pn-kin {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--la-space-2);
}

.pn-kin li { font-size: var(--la-text-sm); }

.pn-blurb {
  color: var(--la-ink-dim);
  display: block;
}

@media (max-width: 34rem) {
  .pn-steps { grid-template-columns: 1fr; }
  .pn-next { text-align: left; }
}

/* The kicker is a breadcrumb, because the page carries BreadcrumbList JSON-LD
   and Google's policy is that the markup must describe something a visitor can
   see. It was already a visible trail, Logical Art then the tier, it just was
   not saying so, which also cost each page a nav landmark. The era pill sits
   beside the trail rather than inside it: a date is not a step on the way to
   anything.

   Written here rather than in the vendored copy. Appending to site/css was the
   mistake the first time and sync silently reverted it; the dead-class check
   found the consequence, which is a class in the markup that nothing styles. */
.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--la-space-2);
}
