/* 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;

  /* ---- 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);
  --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;

  --la-measure: 68ch;
  --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 {
  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);
}

.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;
}

.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 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. */

.la-faq details {
  margin-bottom: var(--la-space-3);
  padding-bottom: var(--la-space-3);
  border-bottom: 1px solid var(--la-hairline);
}

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

.la-faq summary {
  cursor: pointer;
  color: var(--la-ink);
}

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

.la-faq details p {
  margin: var(--la-space-2) 0 0;
  color: var(--la-ink-dim);
}

.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 {
  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); }
