/* Logical Art studio index.
 *
 * Surfaces, ink, type scale and the footer come from logical-art.css, which is
 * loaded first. This file only holds what is specific to the index: the card
 * grid.
 *
 * The studio page is deliberately UNACCENTED. It used to wear LLM Lab's
 * emerald, which made the parent read as one of its own children. Each machine
 * gets to have a colour because its hardware had one; the studio has none, and
 * the two card images supply all the colour this page needs.
 */
* { margin: 0; padding: 0; box-sizing: border-box; }

body {
    font-family: var(--la-font-ui);
    background: var(--la-page);
    color: var(--la-ink);
    line-height: var(--la-leading-body);
    -webkit-font-smoothing: antialiased;
}

/* One column, one width. The page used to run three: 880px for the page,
   560px for the hero text, 46rem for the card images. Nothing lined up on the
   right, which reads as accident rather than as design. --la-measure is the
   width the shared footer already uses, so adopting it here makes the whole
   document agree with itself. */
main {
    max-width: var(--la-measure);
    margin: 0 auto;
    padding: 4.5rem 1.5rem 3rem;
}

.studio-head h1 {
    font-family: var(--la-font-display);
    font-size: var(--la-text-3xl);
    line-height: var(--la-leading-tight);
    letter-spacing: -0.02em;
    color: var(--la-ink);
}

.tagline {
    margin-top: 0.4rem;
    font-size: clamp(1.05rem, 2.5vw, 1.3rem);
    color: var(--la-ink);
    font-weight: 600;
}

.intro {
    margin-top: 0.8rem;
    color: var(--la-ink-dim);
}

/* One way in. Every machine's own page offers a time-boxed door ("two
   minutes", "train it now"); the studio had four equal links and no
   suggestion, which is the one question a first-time visitor actually has. */
.door {
    margin-top: var(--la-space-3);
    font-size: var(--la-text-sm);
    color: var(--la-ink-dim);
}

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

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

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

.migrate-note {
    margin-top: 0.6rem;
    font-size: 0.85rem;
    color: var(--la-instrument);
}

footer {
    margin-top: var(--la-space-5);
}

@media (prefers-reduced-motion: reduce) {
    .work-link { transition: none; }
    .work-link:hover { transform: none; }
}

/* ---- The timeline ---------------------------------------------------------
   The studio's whole claim is that these machines sit at different points on
   one long arc, so the index IS the chronology. Oldest first.

   The rule and the nodes are decorative: the order is already carried by an
   <ol> and the era text, so they are aria-hidden and exempt from the 3:1
   contrast floor. Each node wears its machine's own accent via --machine,
   which arrives from the generated la-machines.css. The studio stays grey;
   the colour on this page belongs to the machines.
*/
.timeline {
  list-style: none;
  margin: var(--la-space-6) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 2.5rem var(--la-space-4);
}

.tl-item {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  position: relative;
}

@supports not (grid-template-columns: subgrid) {
  .tl-item { grid-template-columns: max-content 1fr; gap: 0 var(--la-space-4); }
}

/* One machine ends and the next begins. The chronology used to run on spacing
   alone, which reads as a single long column on a wide screen: the eye finds
   the card images, not the boundaries between entries. A hairline in the same
   weight as the connector and the card borders is enough to separate them
   without turning the page into a table.

   It sits above the row rather than below it, so the last entry does not end on
   a rule with nothing under it, and it spans both columns so the era and the
   card start together. */
.tl-item + .tl-item {
  border-top: 1px solid var(--la-hairline);
  padding-top: 2.25rem;
}

/* The connector is drawn from the item's own top edge, so the padding above
   would leave a gap in the line exactly where the rule crosses it. Start it at
   the top instead, and let the two hairlines meet. */
.tl-item + .tl-item::before { top: 0; }

/* The connector: one continuous rule behind the nodes. */
.tl-item::before {
  content: "";
  position: absolute;
  left: var(--tl-rule-x, 0);
  top: 0.2em;
  bottom: -2.5rem;
  border-left: 1px solid var(--la-hairline);
}
.tl-item:last-child::before { bottom: auto; height: 0.5rem; }

/* Era and size share the left column. The tier name rides along because it is
   how the bit/nibble/byte vocabulary gets taught: seen next to a one-minute
   thing and next to a whole machine, it needs no glossary entry. */
.tl-meta {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding-left: 1.35rem;
}

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

/* The size pill, colour-coded by tier.

   The pill used to wear the machine's own accent, which meant colour could not
   teach you anything about size: a bit and a byte were different colours
   because their machines were. Now the colour IS the tier, and it comes from
   the cool half of the wheel where no machine has a claim, so it reads as a
   fact about the thing rather than as the thing's own colour.

   The eight-cell meter stays alongside it. Colour carries the tier for anyone
   who can see it; the meter carries the same information for anyone who cannot,
   and for a greyscale print. */
/* --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
   `.tl-tier { --tier: ... }` fallback silently overrode every tier colour and
   rendered all nine pills the same grey. The fallback belongs in the var()
   that consumes it, not in a declaration that shadows the cascade. */

.tl-tier {
  align-self: start;
  display: inline-flex;
  align-items: baseline;
  width: fit-content;
  gap: 0.1rem;
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  line-height: 1.2;
  color: var(--tier, var(--la-instrument));
  background: color-mix(in srgb, var(--tier, var(--la-instrument)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--tier, var(--la-instrument)) 42%, transparent);
  border-radius: 999px;
  padding: 0.15em 0.6em;
  white-space: nowrap;
}

/* The size meter: eight cells, of which a bit lights one, a nibble four and a
   byte all eight. It rides inside the pill and inherits its colour. Kept
   alongside the hue rather than replaced by it, because a colour alone fails
   for the roughly one in twelve men with a colour vision deficiency, and fails
   completely in greyscale. */
.tier-meter {
  display: inline-flex;
  gap: 1px;
  margin-left: 0.4rem;
  vertical-align: 0.05em;
}

.tier-meter i {
  display: block;
  width: 2px;
  height: 0.62em;
  background: currentColor;
  opacity: 0.22;
}

.tier-meter i.on { opacity: 1; }

/* An unbuilt machine keeps its tier colour: the size of a thing is true
   whether or not it exists yet. Only the fill drops away. */
.is-unbuilt .tl-tier { background: transparent; }

.tl-meta::before {
  content: "";
  position: absolute;
  left: -0.3rem;
  top: 0.62em;
  width: 0.62rem;
  height: 0.62rem;
  border-radius: 50%;
  background: var(--machine, var(--la-instrument));
  box-shadow: 0 0 0 4px var(--la-page), 0 0 14px -2px var(--machine, transparent);
}

.is-unbuilt .tl-meta::before {
  background: var(--la-page);
  border: 1px solid var(--machine, var(--la-ink-faint));
  box-shadow: 0 0 0 4px var(--la-page);
}

.is-unbuilt .tl-era,
.is-unbuilt .tl-blurb { color: var(--la-ink-faint); }

/* An unbuilt row states what it is and gets out of the way. It used to carry
   the same vertical weight as a working machine, which put a thing that does
   not exist in the highest-attention row on the page. */
.is-unbuilt .tl-name { font-size: var(--la-text-lg); }

.tl-body { min-width: 0; }

.tl-shot {
  display: block;
  margin-bottom: var(--la-space-3);
  border: 1px solid var(--la-hairline);
  border-radius: var(--la-radius-lg);
  overflow: hidden;
  transition: border-color var(--la-transition), transform var(--la-transition);
}

.tl-shot img { display: block; width: 100%; height: auto; }

.tl-item:hover .tl-shot,
.tl-item:focus-within .tl-shot {
  border-color: var(--machine);
  transform: translateY(-2px);
}

.tl-name {
  font-family: var(--la-font-display);
  font-size: var(--la-text-xl);
  line-height: var(--la-leading-tight);
  letter-spacing: -0.01em;
  font-weight: 600;
}

.tl-name a {
  color: var(--la-ink);
  text-decoration: none;
}

.tl-name a:hover { color: var(--machine); }

.tl-name a:focus-visible {
  outline: 2px solid var(--machine, var(--la-instrument));
  outline-offset: 4px;
  border-radius: var(--la-radius-sm);
}

/* The whole row is clickable for a built machine, without nesting a second
   <a> inside the heading's link. */
.tl-name a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.tl-status {
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--la-ink-faint);
  border: 1px solid var(--la-hairline);
  border-radius: var(--la-radius-sm);
  padding: 0.1em 0.5em;
  vertical-align: 0.18em;
  white-space: nowrap;
}

.tl-blurb {
  margin-top: 0.4rem;
  color: var(--la-ink-dim);
}

.tl-host {
  display: inline-block;
  margin-top: 0.7rem;
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--machine);
  border: 1px solid color-mix(in srgb, var(--machine) 35%, transparent);
  border-radius: var(--la-radius-sm);
  padding: 0.22em 0.6em;
}

/* Narrow: the era becomes a label above its machine and the rule moves to the
   far left, which keeps the chronology readable without a cramped column. */
@media (max-width: 34rem) {
  .timeline { grid-template-columns: 1fr; gap: 2rem 0; }
  .tl-item { grid-template-columns: 1fr; padding-left: 1.35rem; }
  .tl-item::before { left: 0; bottom: -2rem; }
  /* Era and tier go side by side rather than stacked: two lines of metadata
     above every row costs more vertical space than it is worth on a phone. */
  .tl-meta {
    flex-direction: row;
    align-items: baseline;
    gap: var(--la-space-2);
    padding-left: 0;
    margin-bottom: 0.35rem;
  }
  .tl-meta::before { left: -1.65rem; }
  .tl-shot { border-radius: var(--la-radius); }
}

@media (prefers-reduced-motion: reduce) {
  .tl-shot { transition: none; }
  .tl-item:hover .tl-shot { transform: none; }
}

/* ---- Why these machines ---------------------------------------------------
   The studio asserted its thesis in one sentence and never argued it. The most
   interesting thing about the roster, that every large machine is one the
   operator actually used, was invisible to a visitor. */
.why {
  margin-top: var(--la-space-6);
  padding-top: var(--la-space-5);
  border-top: 1px solid var(--la-hairline);
}

.why h2 {
  font-family: var(--la-font-display);
  font-size: var(--la-text-xl);
  line-height: var(--la-leading-tight);
  letter-spacing: -0.01em;
}

.why p {
  margin-top: var(--la-space-3);
  color: var(--la-ink-dim);
}

.why-close {
  color: var(--la-ink);
  font-weight: 600;
}

/* The three sizes, generated from the roster with live counts, so the
   paragraph explaining what this place is cannot go stale. */
.tiers {
  margin-top: var(--la-space-4);
  display: grid;
  gap: var(--la-space-3);
}

.tier-row {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--la-space-1) var(--la-space-4);
  align-items: baseline;
}

.tier-row dt {
  display: inline-flex;
  align-items: baseline;
  gap: 0.1rem;
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--tier, var(--la-instrument));
  border: 1px solid color-mix(in srgb, var(--tier, var(--la-instrument)) 42%, transparent);
  background: color-mix(in srgb, var(--tier, var(--la-instrument)) 12%, transparent);
  border-radius: 999px;
  padding: 0.15em 0.6em;
  white-space: nowrap;
}

/* The definition pill is a link to that tier's index. It inherits the pill's
   colour rather than carrying its own, so there is one pill here, not a link
   styled as a pill inside a pill. */
.tier-row dt a {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: baseline;
  gap: 0.1rem;
  /* Same reason as the legend above: the anchor carries the padding so the
     pill is the target. The negative margin lets it fill the <dt> that already
     has padding, rather than making the pill grow. */
  margin: -0.15em -0.6em;
  padding: 0.35em 0.6em;
}

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

.tier-row dt a:focus-visible {
  outline: 2px solid var(--tier, var(--la-instrument));
  outline-offset: 4px;
  border-radius: 999px;
}

.tier-row dd {
  margin: 0;
  color: var(--la-ink-dim);
  font-size: var(--la-text-sm);
}

.tier-count {
  color: var(--la-ink-faint);
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  white-space: nowrap;
}

@media (max-width: 30rem) {
  .tier-row { grid-template-columns: 1fr; }
  .tier-row dt { justify-self: start; }
}

/* The outbound marker on the timeline, sized to the heading it follows. */
.tl-name .la-ext { color: var(--la-ink-faint); }
.tl-name a:hover .la-ext { color: var(--machine, var(--la-instrument)); }
.door .la-ext { color: var(--la-ink-faint); }

/* ---- The planned filter ---------------------------------------------------
   Six of the ten roster entries are not built, and four of them fall in one
   unbroken run right after the first row, so the page opened with more vapour
   than work. Hidden by default.

   The default lives in a class on the served markup rather than in script, so
   a visitor without JavaScript gets the clean view and nobody watches the full
   list flash and then collapse.

   Deliberately NOT applied to the tier index pages: /nibbles/ has one entry and
   it is planned, so defaulting it off there would serve an empty page. A tier
   index is where you go to see the whole tier. */
.filter {
  margin-top: var(--la-space-5);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--la-space-2) var(--la-space-3);
}

/* A real switch rather than a text button. The control has two states and it
   should look like it does: a track, a knob that moves, and a border that
   picks up the instrument colour when it is on. Everything is expressed in
   both colour AND position, so the state survives greyscale and colour
   blindness. */
.filter-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--la-space-2);
  font: inherit;
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--la-ink-dim);
  background: var(--la-raised);
  border: 1px solid var(--la-hairline);
  border-radius: 999px;
  padding: 0.4em 0.9em 0.4em 0.55em;
  cursor: pointer;
  transition: border-color var(--la-transition), color var(--la-transition),
              background var(--la-transition);
}

.filter-switch {
  position: relative;
  display: block;
  width: 1.7rem;
  height: 0.9rem;
  border-radius: 999px;
  background: var(--la-sunken);
  border: 1px solid var(--la-hairline-strong);
  flex: none;
  transition: background var(--la-transition), border-color var(--la-transition);
}

.filter-knob {
  position: absolute;
  top: 50%;
  left: 0.12rem;
  width: 0.52rem;
  height: 0.52rem;
  margin-top: -0.26rem;
  border-radius: 50%;
  background: var(--la-ink-faint);
  transition: left var(--la-transition), background var(--la-transition);
}

.filter-label b { font-weight: 700; color: var(--la-ink); }

.filter-btn:hover {
  color: var(--la-ink);
  border-color: var(--la-hairline-strong);
}

.filter-btn:hover .filter-knob { background: var(--la-ink-dim); }

.filter-btn:focus-visible {
  outline: 2px solid var(--la-instrument);
  outline-offset: 3px;
}

.filter-btn:active .filter-knob { background: var(--la-ink); }

/* On. The knob travels the width of the track, and the track fills, so the
   state is legible from the shape alone. */
.filter-btn[aria-pressed="true"] {
  color: var(--la-ink);
  border-color: var(--la-instrument);
}

.filter-btn[aria-pressed="true"] .filter-switch {
  background: color-mix(in srgb, var(--la-instrument) 32%, transparent);
  border-color: var(--la-instrument);
}

.filter-btn[aria-pressed="true"] .filter-knob {
  left: calc(100% - 0.64rem);
  background: var(--la-instrument);
}

/* What the control is currently doing, in words, next to it. A switch tells
   you there are two states; this tells you which one you are looking at. */
.filter-state {
  font-family: var(--la-font-mono);
  font-size: var(--la-text-xs);
  color: var(--la-ink-faint);
}

@media (prefers-reduced-motion: reduce) {
  .filter-btn, .filter-switch, .filter-knob { transition: none; }
}

.hide-planned .tl-item.is-unbuilt { display: none; }

/* The connector rule stops at the last row. With planned rows hidden, the last
   VISIBLE row is not necessarily :last-child, so terminate the rule at the last
   row that has no built sibling after it. Without this the line runs on past
   the end of the list into empty space. */
.hide-planned .tl-item:not(.is-unbuilt):not(:has(~ .tl-item:not(.is-unbuilt)))::before {
  bottom: auto;
  height: 0.5rem;
}

/* The three sizes, named before the chronology instead of after it.
   Same grammar as the counted list at the foot of the page, because that is
   what made that one read well: the pills share a column and align, and the
   definition sits beside them rather than inside them. Tighter above the
   chronology than below it, and carrying no counts. */
.tiers-brief {
  margin-top: var(--la-space-3);
  gap: var(--la-space-2);
}

.tiers-brief dd {
  font-size: var(--la-text-sm);
}

/* The method page: prose sections with the same measure as the studio's own
   explanatory text, and a numbered source order that reads as a hierarchy
   rather than a list of steps. */
.method-section {
  margin-top: var(--la-space-5);
}

.method-section h2 {
  font-size: var(--la-text-lg);
  margin-bottom: var(--la-space-2);
}

.method-section p {
  color: var(--la-ink-dim);
  margin-bottom: var(--la-space-2);
}

.method-order {
  margin: var(--la-space-2) 0 var(--la-space-3);
  padding-left: 1.4em;
  color: var(--la-ink-dim);
  display: grid;
  gap: var(--la-space-2);
}

.method-order li b {
  color: var(--la-ink);
}
