/* /archive — the wall as the page's head, the same 520 in order below it,
   and the concept viewer and lightbox they open into.

   One job, from the lab's brief: make the breadth felt in a second, then
   make any piece two gestures away. So the first screen IS the body of
   work — all 520 artefacts on a living, endless wall — and the page's
   words sit on one paper card over it. Ported from the founder-approved
   master (docs/design/labs/archive-wall-2026-09/master.html); every
   colour, size and duration here is the master's, expressed in the site's
   tokens. The few additions are --x-* and argued where they are declared.
   The tiles are images of the work and keep the work's own light
   appearance in both themes. */

[data-page="archive"] {
  /* The wall's ground is Paper raised, not white: most artefacts are white
     UI on white, and they need a ground to stand on or the wall reads as
     one grey smear. */
  --x-ground: var(--paper-3);
  --x-veil: 238 238 238;
  --x-tile-line: rgb(17 17 17 / .08);
  --x-tile-shadow: 0 1px 1px rgb(17 17 17 / .04), 0 6px 14px -8px rgb(17 17 17 / .2);
  --x-tile-lift: 0 2px 4px rgb(17 17 17 / .06), 0 24px 44px -14px rgb(17 17 17 / .38);
  --x-card-shadow: 0 1px 2px rgb(17 17 17 / .06), 0 30px 70px -26px rgb(17 17 17 / .34);
  --x-cap-shadow: 0 4px 12px -4px rgb(17 17 17 / .24);
  --x-img-dim: 1;
  /* the page column: the left edge of every .wrap's content, the same
     edge the name mark and the index heading sit on */
  --x-col: calc(max(0px, (100% - var(--container)) / 2) + var(--gutter));
}
[data-theme="dark"] [data-page="archive"] {
  --x-ground: var(--paper-ink);
  --x-veil: 10 10 10;
  --x-tile-line: rgb(255 255 255 / .13);
  --x-tile-shadow: 0 1px 1px rgb(0 0 0 / .5), 0 8px 18px -8px rgb(0 0 0 / .7);
  --x-tile-lift: 0 2px 4px rgb(0 0 0 / .5), 0 26px 48px -14px rgb(0 0 0 / .9);
  --x-card-shadow: 0 0 0 1px rgb(255 255 255 / .04), 0 30px 70px -24px rgb(0 0 0 / .9);
  --x-cap-shadow: 0 4px 12px -4px rgb(0 0 0 / .7);
  /* on a dark wall the (light) work is dimmed a touch so a field of white
     UI does not glare */
  --x-img-dim: .86;
}
/* tile sizes: the wall's two units. The script reads these back, so CSS
   and the layout engine can never disagree about how big a tile is. */
:root { --u1w: 128px; --u1h: 96px; --ug: 8px; }
@media (max-width: 719px) { :root { --u1w: 84px; --u1h: 63px; --ug: 6px; } }
/* locking scroll under the sheet must never shift the wall sideways */
html:has(body[data-page="archive"]) { scrollbar-gutter: stable; }
body[data-page="archive"] { overflow-x: clip; }
.hero, .ix, .sheet { line-height: 1.5; }
[data-page="archive"] [hidden] { display: none !important; }
p.vh { overflow: clip; }

/* ── the masthead floats quiet over the wall ─────────────────────────
   The site's own masthead, made transparent here: the veil below gives it
   ground. Only its controls take the pointer, so a drag that starts under
   the bar still moves the wall. It scrolls away with the wall rather than
   sticking: below, the index's own bar is what stays in reach. */
body[data-page="archive"] .masthead { position: absolute; inset: 0 0 auto; background: transparent; border-color: transparent;
  backdrop-filter: none; pointer-events: none; }
/* it never becomes the scrolled material here: it has scrolled away */
body[data-page="archive"] .masthead.is-scrolled { background: transparent; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
body[data-page="archive"] .masthead :is(a, button) { pointer-events: auto; }
/* on a phone the open menu owns the screen, here as everywhere */
body[data-page="archive"] .masthead.is-menu { position: fixed; background: var(--ground); }
body[data-page="archive"] .masthead .nav { pointer-events: auto; }
@media (min-width: 760px) { body[data-page="archive"] .masthead .nav { pointer-events: none; } }

/* ── the hero: the wall is the first screen ──────────────────────────
   It stops 64px short of the fold so the index's heading peeks: the page
   says, without an arrow, that the wall is not all there is. */
.hero { position: relative; height: calc(100svh - 64px); min-height: 620px; max-height: 1100px;
  background: var(--x-ground); isolation: isolate; overflow: clip; }
.stage { position: absolute; inset: 0; overflow: clip; }
/* touch-action pan-y: a vertical swipe belongs to the page, a sideways one
   to the wall; the browser settles which before a pixel moves. The foot
   recedes into its own ground over 72px instead of guillotining tiles. */
.wall { position: absolute; inset: 0; cursor: grab; touch-action: pan-y;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 72px), transparent); mask-image: linear-gradient(to bottom, #000 calc(100% - 72px), transparent); }
.wall.is-dragging, .wall.is-dragging .t { cursor: grabbing; }
/* The plane carries the camera as ONE composited transform; tiles repaint
   only when they wrap round the torus (offscreen) or swell under the
   pointer. 520 tiles, one layer: that is the 60fps budget. */
.plane { position: absolute; left: 0; top: 0; width: 0; height: 0; will-change: transform; }

.t { position: absolute; left: 0; top: 0; display: block; overflow: hidden; border-radius: 5px;
  width: var(--u1w); height: var(--u1h); background: var(--paper-2); color: var(--ink); text-decoration: none;
  box-shadow: 0 0 0 1px var(--x-tile-line), var(--x-tile-shadow); transform-origin: 50% 50%;
  transition: box-shadow .22s var(--ease-out); }
/* featured pieces get the presence: a 2×2 cell, so the curated set is
   legible inside the texture before anyone points at anything */
.t.f { width: calc(var(--u1w) * 2 + var(--ug)); height: calc(var(--u1h) * 2 + var(--ug)); border-radius: 8px; cursor: pointer; }
.t img { display: block; width: 100%; height: 100%; max-width: none; object-fit: cover; pointer-events: none;
  -webkit-user-drag: none; opacity: 0; filter: brightness(var(--x-img-dim)); transition: opacity .24s var(--ease-out); }
.t img.ld { opacity: 1; }
.t.is-hot { z-index: 2; box-shadow: 0 0 0 1px var(--x-tile-line), var(--x-tile-lift); }
.t.is-hot img { filter: none; }
.t.is-out { visibility: hidden; pointer-events: none; }
/* virtualised: tiles beyond the load margin are not rendered at all, and
   so cannot be reached by focus either */
.t.far { display: none; }
.t:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; z-index: 3; }

/* The caption: a featured tile names itself — title, then area and
   family. It wraps rather than truncates: a clipped title is silent
   content deletion. */
.cap { position: absolute; left: 8px; bottom: 8px; max-width: calc(100% - 16px); display: grid; gap: 1px;
  padding: 5px 8px 6px; border-radius: 6px; background: var(--paper); font-family: var(--mono);
  box-shadow: 0 0 0 1px var(--x-tile-line), var(--x-cap-shadow);
  opacity: 0; transform: translateY(4px); transition: opacity .16s var(--ease-out), transform .22s var(--ease-out); pointer-events: none; }
.cap b { font-family: var(--ui); font-weight: 500; font-size: 13px; line-height: 1.3; letter-spacing: -.005em; color: var(--ink); overflow-wrap: anywhere; }
/* metadata keeps the manifest's own casing: one grammar, in the DOM, on
   screen and in the accessible name alike */
.cap span { font-size: 11px; line-height: 1.3; color: var(--graphite); }
.t.is-hot .cap, .t:focus-visible .cap { opacity: 1; transform: none; }

/* veil: the masthead gets ground of its own — a solid band the height of
   the bar (no tile ever shows through behind nav text), then a short
   falloff — and the card's corner a soft falloff so its edge never fights
   a busy tile */
.veil { position: absolute; inset: 0; pointer-events: none; z-index: 4;
  background:
    linear-gradient(to bottom, rgb(var(--x-veil) / 1) 0, rgb(var(--x-veil) / 1) var(--nav-h), rgb(var(--x-veil) / 0) calc(var(--nav-h) + 56px)),
    radial-gradient(ellipse 44% 66% at 0% 100%, rgb(var(--x-veil) / .72) 0, rgb(var(--x-veil) / .4) 50%, rgb(var(--x-veil) / 0) 100%); }

.hint { position: absolute; right: var(--gutter); bottom: 36px; z-index: 5; pointer-events: none; margin: 0;
  display: flex; align-items: center; gap: 8px; padding: 7px 12px 7px 9px; border-radius: 999px;
  background: var(--paper); box-shadow: 0 0 0 1px var(--x-tile-line), 0 8px 20px -10px rgb(17 17 17 / .3);
  font: 500 13px/1.2 var(--ui); color: var(--ink-2);
  transition: opacity .4s var(--ease-out), transform .4s var(--ease-out); }
.hint svg { width: 14px; height: 14px; color: var(--graphite); flex: none; }
.hint kbd { font: 550 11px/1.2 var(--mono); padding: 2px 6px; border-radius: 4px; background: var(--paper-3); color: var(--ink); }
.hint .k-desk { display: inline-flex; align-items: center; gap: 6px; }
.hint.is-gone { opacity: 0; transform: translateY(6px); }

/* ── the card: the page's words, on paper, over the wall ─────────────
   Opaque paper, never glass: a translucent card over 520 images is a
   contrast lottery. The card also marks the one region tiles are never
   parked under (safeRect in the script). Its TEXT sits on the page column
   — the brand's and the index heading's edge. Its foot sits the gutter's
   depth off the wall's bottom (a percentage in `bottom` reads the hero's
   height, so the centring term drops out and only the gutter is left). */
.wall-card { --inset: calc(var(--x-col) - 32px);
  position: absolute; z-index: 6; bottom: max(24px, var(--inset)); left: var(--inset);
  width: 540px; padding: 30px 32px 0; border-radius: 16px; background: var(--paper); color: var(--ink);
  box-shadow: 0 0 0 1px var(--rule-soft), var(--x-card-shadow); }
.wall-card h1 { font-size: clamp(3rem, 2rem + 2.4vw, 4.5rem); line-height: 1.02; font-weight: 550; letter-spacing: -.04em; }
.wall-card .lede { margin-top: 14px; font-size: 16.5px; line-height: 1.5; color: var(--ink-2); max-width: 45ch; text-wrap: pretty; }
.wall-card .lede b { font-weight: 500; color: var(--ink); font-variant-numeric: tabular-nums; }

/* The families: real filter controls, each with its live count and a dot
   that lights while the pointer rests on one of its tiles — the card reads
   the family under your hand, so even the uncaptioned texture is named. */
.fams { margin-top: 20px; display: flex; flex-wrap: wrap; gap: 6px; }
/* the chips are drawn by the script; their row keeps its height from the
   first paint so nothing below it moves when they arrive */
.wall-js .fams:empty { min-height: 32px; }
.fam { display: inline-flex; align-items: center; gap: 7px; min-height: 32px; padding: 0 11px 0 10px; border-radius: 999px;
  font-size: 13px; font-weight: 500; line-height: 1.2; color: var(--ink-2); white-space: nowrap;
  box-shadow: inset 0 0 0 1px var(--rule);
  transition: color .16s var(--ease-out), background-color .16s var(--ease-out), box-shadow .16s var(--ease-out), transform var(--d-press) var(--ease-out); }
.fam[data-type="all"] { padding-left: 12px; }
.fam small { font-family: var(--mono); font-size: 11px; line-height: 1.2; color: var(--graphite); font-variant-numeric: tabular-nums; }
.fam i { width: 6px; height: 6px; border-radius: 999px; box-shadow: inset 0 0 0 1px var(--rule-strong);
  transition: background-color .16s var(--ease-out), box-shadow .16s var(--ease-out), transform .22s var(--ease-out); }
.fam.is-reading { color: var(--ink); }
/* reading is momentary, so it is a hollow ring; only a choice is filled */
.fam.is-reading i { background: none; box-shadow: inset 0 0 0 1.5px var(--ink); transform: scale(1.35); }
/* The accent marks a choice the visitor made, never the default: 'All' at
   rest is pressed but quiet (Paper raised, Ink); a chosen family takes the
   Selection tint and the filled indigo dot. */
.fam[aria-pressed="true"] { color: var(--ink); background: var(--paper-3); box-shadow: none; }
.fam[aria-pressed="true"]:not([data-type="all"]) { color: var(--accent); background: var(--accent-soft); }
.fam[aria-pressed="true"]:not([data-type="all"]) small { color: var(--accent); }
.fam[aria-pressed="true"]:not([data-type="all"]) i { background: var(--accent); box-shadow: none; }
@media (hover: hover) and (pointer: fine) {
  .fam:not([aria-pressed="true"]):hover { color: var(--ink); background: var(--paper-2); }
  .now-all:hover { text-decoration: underline; text-underline-offset: 3px; }
}
.fam:active { transform: scale(.97); }

/* The status row: ONE sentence that says what the wall is showing, the
   way back while a filter narrows it, and the handoff to the index. */
.now { margin: 18px -32px 0; padding: 10px 24px 12px 32px; border-top: 1px solid var(--rule-soft);
  display: flex; align-items: center; gap: 4px 14px; flex-wrap: wrap; min-height: 54px; }
.now-t { font-family: var(--mono); font-size: 12px; line-height: 1.4; color: var(--graphite); margin-right: auto; font-variant-numeric: tabular-nums; }
.now-t b { font-weight: 500; color: var(--ink); }
.txt { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 0 8px; margin: 0 -8px; border-radius: 6px;
  font-size: 13.5px; font-weight: 500; line-height: 1.2; color: var(--ink); text-decoration: none; white-space: nowrap;
  transition: color .16s var(--ease-out), background-color .16s var(--ease-out); }
.txt svg { width: 14px; height: 14px; flex: none; transition: transform .22s var(--ease-out); }
/* the escape states its destination in a quieter voice; the next move
   ('See them in order') is the one primary in the row */
.now-all { color: var(--ink-2); }
@media (hover: hover) and (pointer: fine) {
  .txt:hover { background: var(--paper-2); color: var(--ink); }
  .txt:hover svg { transform: translateY(2px); }
}

/* No scripting: the card is the page head, the families are jump links
   into the index, and every artefact is visible below. The inline script
   at the top of the page sets .wall-js before the hero paints, so this
   never flashes for everyone else. */
.jump { margin-top: 20px; display: flex; flex-wrap: wrap; gap: 4px 16px; padding-bottom: 26px; font-size: 14px; line-height: 1.4; }
.jump a { min-height: 32px; display: inline-flex; align-items: center; gap: 6px; color: var(--ink); text-underline-offset: 4px; text-decoration-color: var(--rule-strong); }
.jump small { font-family: var(--mono); font-size: 11px; line-height: 1.2; color: var(--graphite); }
.wall-js .jump { display: none; }
html:not(.wall-js) :is(.stage, .veil, .hint, .fams, .now, .ix-bar) { display: none; }
html:not(.wall-js) body[data-page="archive"] .masthead { position: relative; }
html:not(.wall-js) .hero { height: auto; min-height: 0; max-height: none; background: var(--ground); padding: 8px 0; }
html:not(.wall-js) .wall-card { position: relative; left: auto; bottom: auto; width: auto; max-width: 760px;
  margin-inline: max(0px, (100% - var(--container)) / 2); padding: 20px var(--gutter) 0; box-shadow: none; background: none; }

/* ── the index: the same 520, in order ───────────────────────────────
   The wall is for feeling the breadth; the index is for finding a thing.
   Grouped by family in the manifest's own order, the curated pieces large
   and named, so the eye learns one rule twice. It is also the whole no-JS
   page: every artefact visible, every featured one a link. A floor under
   it: a filter can never shorten the document so far that the browser
   clamps the scroll and throws the sticky bar. */
.ix { position: relative; background: var(--ground); padding-bottom: 112px; min-height: calc(100svh + 220px); border-top: 1px solid var(--rule);
  /* Paint containment is a measured decision, not a habit: every move of
     the wall re-layerises the document, and without it the index's ~1000
     paint chunks made that cost ~130ms a frame (traced in the lab: Layerize).
     With it, ~3ms. The index never paints outside its own box anyway. */
  contain: paint; }
html:not(.wall-js) .ix { min-height: 0; }
.ix-head { padding-top: 20px; display: flex; align-items: baseline; justify-content: space-between; gap: 8px 32px; flex-wrap: wrap; }
.ix-head h2 { font-size: clamp(1.75rem, 1.4rem + 1.2vw, 2.5rem); line-height: 1.1; font-weight: 600; letter-spacing: -.03em; border-radius: 4px; }
.ix-head h2:focus { outline: none; }
.ix-head p { font-size: 16px; line-height: 1.5; color: var(--ink-2); max-width: 64ch; }
/* sticky: however deep in the index, the filter and its way back stay in
   reach — a way back that scrolls away is a way back that disappeared */
.ix-bar { position: sticky; top: 0; z-index: 5; margin-top: 20px; background: var(--ground); box-shadow: 0 1px 0 var(--rule-soft);
  transition: opacity .16s var(--ease-out); }
.ix-bar-in { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 24px; min-height: 60px; }
.ix-bar .fams { margin: 0; flex-wrap: nowrap; }
.ix-bar .now-t { margin-left: auto; margin-right: 0; white-space: nowrap; }
.ix-bar .txt { margin: 0 -8px 0 0; }
.ix-bar.is-away { opacity: 0; visibility: hidden; pointer-events: none; }
.ix-g { margin-top: 36px; scroll-margin-top: 76px; }
.ix-g h3 { display: flex; align-items: baseline; gap: 10px; font-size: 18px; line-height: 1.3; font-weight: 600; letter-spacing: -.015em; }
.ix-g h3 small { font-family: var(--mono); font-size: 12px; line-height: 1.3; font-weight: 400; color: var(--graphite); letter-spacing: 0; }
.ix-g.is-out { display: none; }
/* Each family opens on its named pieces, large enough to read, each with
   its ordinal and title painted at rest (so touch has them too); the
   texture follows in a denser grid. */
.ix-feat { margin-top: 16px; display: grid; grid-template-columns: repeat(auto-fill, minmax(176px, 1fr)); gap: 24px 16px; }
.ix-f a { display: block; color: var(--ink); text-decoration: none; border-radius: 8px; }
.ix-t img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; background: var(--paper-2); box-shadow: 0 0 0 1px var(--rule-soft); }
.ix-f img { border-radius: 8px; transition: transform .16s var(--ease-out), box-shadow .16s var(--ease-out); }
.ix-name { display: flex; gap: 8px; align-items: baseline; margin-top: 10px; font-size: 13px; line-height: 1.35; font-weight: 500; }
.ix-name i { font-style: normal; font-family: var(--mono); font-size: 12px; line-height: 1.35; font-weight: 400; color: var(--graphite); font-variant-numeric: tabular-nums; }
.ix-area { display: block; margin-top: 2px; padding-left: 26px; font-size: 12.5px; line-height: 1.35; color: var(--graphite); }
@media (hover: hover) and (pointer: fine) {
  .ix-f a:hover img { transform: translateY(-2px); box-shadow: 0 0 0 1px var(--rule-soft), var(--shadow-card); }
}
.ix-grid { margin-top: 24px; display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 6px; }
.ix-grid:empty { display: none; }
.ix-t { position: relative; min-width: 0; }
.ix-grid img { border-radius: 5px; }
.archive-foot { margin-top: 64px; color: var(--graphite); font-size: var(--t-small); }
[data-theme="dark"] .ix img { box-shadow: 0 0 0 1px var(--rule); filter: brightness(var(--x-img-dim)); }

/* ── the sheet: a featured piece, previewed in place ─────────────────
   The 480w image, what it is, and the way to where it lives: "Open"
   goes to the concept viewer, the lightbox or the piece's page. The wall
   behind keeps its place exactly. The image takes ~63% of the sheet and
   bleeds to the sheet's own corners: the piece gets bigger when it opens. */
.sheet { width: min(1040px, calc(100vw - 48px)); max-width: none; max-height: calc(100svh - 48px); padding: 0; border: 0; border-radius: 16px;
  background: var(--paper); color: var(--ink); box-shadow: 0 40px 90px -30px rgb(0 0 0 / .55); overflow: auto; }
.sheet:not([open]) { pointer-events: none; }
.sheet::backdrop { background: rgb(17 17 17 / .56); }
.sheet-in { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(250px, 1fr); }
.sheet-fig { margin: 0; background: var(--paper-2); border-radius: 16px 0 0 16px; overflow: hidden; }
.sheet-fig img { display: block; width: 100%; height: 100%; min-height: 100%; aspect-ratio: 4 / 3; object-fit: cover; background: var(--paper-3); transform-origin: 0 0; }
.sheet-meta { position: relative; padding: 28px 28px 24px; display: flex; flex-direction: column; min-width: 0; }
.sheet-k { font-family: var(--mono); font-size: 12px; line-height: 1.4; color: var(--graphite); padding-right: 44px; }
/* the title takes focus so a screen reader starts at the piece's name; it
   is not a control, so it draws no ring */
#sheet-title:focus { outline: none; }
.sheet-meta h2 { margin-top: 10px; font-size: 26px; line-height: 1.15; font-weight: 600; letter-spacing: -.025em; border-radius: 4px; text-wrap: balance; }
.sheet-area { margin-top: 6px; font-size: 16px; line-height: 1.5; color: var(--ink-2); }
.sheet-why { margin-top: 16px; font-size: 15px; line-height: 1.55; color: var(--ink); text-wrap: pretty; }
.sheet-go { margin-top: 24px; align-self: flex-start; display: inline-flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 16px 0 18px;
  border-radius: 8px; background: var(--accent); color: var(--accent-ink); font-size: 15px; font-weight: 500; line-height: 1.2; text-decoration: none;
  transition: background-color .16s var(--ease-out), transform .12s var(--ease-out); }
.sheet-go svg { width: 16px; height: 16px; transition: transform .22s var(--ease-out); }
.sheet-go:active { transform: scale(.97); }
@media (hover: hover) and (pointer: fine) { .sheet-go:hover { background: var(--accent-2); color: var(--accent-ink); } .sheet-go:hover svg { transform: translateX(2px); } }
.sheet-where { margin-top: 10px; font-size: 13px; line-height: 1.4; color: var(--graphite); }
.sheet-nav { margin-top: auto; padding-top: 28px; display: flex; align-items: center; gap: 8px; }
.sheet-nav .pos { margin-left: auto; font-family: var(--mono); font-size: 11px; line-height: 1.4; color: var(--graphite); font-variant-numeric: tabular-nums; }
.btn-q { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 36px; min-width: 36px; padding: 0 12px; border-radius: 8px;
  font-size: 13px; font-weight: 500; line-height: 1.2; color: var(--ink); box-shadow: inset 0 0 0 1px var(--rule);
  transition: background-color .16s var(--ease-out); }
.btn-q svg { width: 14px; height: 14px; }
@media (hover: hover) and (pointer: fine) { .btn-q:hover { background: var(--paper-2); } }
.sheet-x { position: absolute; top: 16px; right: 16px; padding: 0; width: 40px; min-height: 40px; }

/* ── widths ────────────────────────────────────────────────────────── */
/* 720–1099: the card spans the bottom of the wall; tiles are parked above
   it instead of beside it, and the hint moves up out of its way. Below
   1100 the card's box, not its text, takes the column: a 32px overhang
   would leave it hard against the glass. */
@media (min-width: 720px) and (max-width: 1099px) {
  .wall-card { left: var(--x-col); width: min(560px, calc(100% - 2 * var(--x-col))); bottom: 24px; }
  .wall-card .fams { margin: 18px -32px 0; padding: 0 32px; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .wall-card .fams::-webkit-scrollbar { display: none; }
}
@media (min-width: 720px) and (max-height: 820px) {
  .wall-card { bottom: 28px; padding-top: 26px; }
  .wall-card h1 { font-size: 3.5rem; }
  .wall-card .lede { margin-top: 10px; font-size: 16px; }
  .wall-card .fams { margin-top: 16px; }
  .now { margin-top: 14px; }
}
@media (max-width: 1279px) { .ix-bar-in { padding: 10px 0; } .ix-bar .fams { flex-wrap: wrap; } .ix-bar .now-t { margin-left: 0; } }
/* a scrolling chip row states its own overflow with a measured fade, drawn
   only while something is actually hidden on that side */
.fams.is-more-r { mask-image: linear-gradient(to right, #000 calc(100% - 56px), transparent); }
.fams.is-more-l { mask-image: linear-gradient(to left, #000 calc(100% - 56px), transparent); }
.fams.is-more-l.is-more-r { mask-image: linear-gradient(to right, transparent, #000 40px, #000 calc(100% - 56px), transparent); }

/* Phone: the wall fills the top of the first screen and the card sits
   below it on paper — at 390px there is no honest way to lay words over
   imagery and still show imagery. Filters become one scrolling row, and
   every target grows to 44px for a thumb. */
@media (max-width: 719px) {
  /* the card is first in the document (it is read first) but the wall is
     seen first: flex order puts the wall on top without touching the
     reading or Tab order */
  .hero { height: auto; min-height: 0; max-height: none; background: var(--ground); overflow: visible; display: flex; flex-direction: column; }
  .stage { order: -1; position: relative; inset: auto; height: clamp(340px, 48svh, 440px); background: var(--x-ground); }
  .veil { bottom: auto; height: 100%; background: linear-gradient(to bottom, rgb(var(--x-veil) / 1) 0, rgb(var(--x-veil) / 1) var(--nav-h), rgb(var(--x-veil) / 0) calc(var(--nav-h) + 52px)); }
  .hint { left: 0; right: 0; bottom: 14px; top: auto; margin-inline: auto; width: max-content; padding: 6px 12px 6px 9px; }
  .hint .k-desk { display: none; }
  .wall-card { position: relative; left: auto; bottom: auto; width: auto; padding: 20px var(--gutter) 0; border-radius: 0; box-shadow: none; background: var(--ground); }
  .wall-card h1 { font-size: 2.75rem; }
  .wall-card .lede { margin-top: 10px; font-size: 15px; line-height: 1.45; }
  .fams { margin: 16px calc(-1 * var(--gutter)) 0; padding: 0 var(--gutter); flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; scroll-padding-inline: var(--gutter); }
  .fams::-webkit-scrollbar { display: none; }
  .fam { min-height: 44px; padding: 0 14px 0 13px; }
  .wall-js .fams:empty { min-height: 44px; }
  .now { margin: 12px calc(-1 * var(--gutter)) 0; padding: 6px var(--gutter) 8px; min-height: 0; border-top: 0; gap: 0 14px; }
  .now-t { flex-basis: 100%; }
  .txt { min-height: 44px; }
  .ix-head { padding-top: 28px; }
  .ix-bar-in { gap: 0; padding: 0 0 4px; }
  .ix-bar .fams { margin: 0 calc(-1 * var(--gutter)); flex: 1 1 100%; padding-top: 8px; }
  .ix-bar .now-t { flex: 1; }
  .ix-feat { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 12px; }
  .ix-grid { grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); gap: 4px; }
  .sheet { width: 100%; max-width: 100%; max-height: 92svh; margin: auto 0 0; border-radius: 16px 16px 0 0; }
  .sheet-in { grid-template-columns: 1fr; }
  .sheet-fig { border-radius: 16px 16px 0 0; }
  .sheet-meta { padding: 20px 20px 16px; }
  .sheet-meta h2 { font-size: 22px; }
  .sheet-go { margin-top: 18px; align-self: stretch; justify-content: center; }
  .sheet-nav { padding-top: 18px; }
  .btn-q { min-height: 44px; min-width: 44px; }
  .sheet-x { top: 10px; right: 10px; width: 44px; min-height: 44px; }
}

/* reduced: a composed, still wall. Nothing drifts, flings or develops;
   captions and states change without travelling. Fully usable. */
@media (prefers-reduced-motion: reduce) {
  .t, .t img, .cap, .hint, .fam, .fam i, .ix-f img, .ix-bar, .txt svg, .sheet-go svg { transition: none !important; }
}


/* ── the concept viewer, and the lightbox that shares its frame ──── */
.concept-viewer, .lightbox { position: fixed; inset: 0; z-index: 60; display: grid; grid-template-rows: auto minmax(0, 1fr);
  background: var(--ground); color: var(--ink); }
body.viewing { overflow: hidden; }
.viewer-toolbar { display: flex; align-items: center; gap: var(--s2); padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--rule); min-width: 0; min-height: 64px; }
.viewer-toolbar button, .viewer-toolbar select {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: 40px; padding: 8px 12px;
  border: 1px solid var(--rule); border-radius: var(--r-md); background: var(--paper); color: var(--ink);
  font-size: var(--t-caption); white-space: nowrap; cursor: pointer;
}
.viewer-toolbar button svg { width: 14px; height: 14px; }
@media (hover: hover) and (pointer: fine) { .viewer-toolbar button:hover, .viewer-toolbar select:hover { background: var(--paper-2); } }
.viewer-toolbar button:disabled { opacity: .45; cursor: default; }
.viewer-title { min-width: 0; flex: 1; padding-inline: var(--s2); display: flex; align-items: baseline; gap: var(--s3); }
.viewer-title h2 { display: block; min-width: 0; font-size: 1.0625rem; font-weight: 600; letter-spacing: -.015em; border-radius: 3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; outline-offset: 4px; }
.viewer-label { min-width: 0; display: flex; align-items: center; gap: var(--s3); padding: 4px 0; }
#back svg, #lightbox-back svg { transform: scaleX(-1); }
.concept-data-note { font-family: var(--mono); font-size: var(--t-eyebrow); color: var(--graphite); white-space: nowrap; padding: 2px 7px;
  border: 1px solid var(--rule); border-radius: var(--r-pill); }
#detail-deck { color: var(--graphite); font-size: var(--t-caption); white-space: nowrap; }
#position { font-family: var(--mono); font-size: var(--t-eyebrow); color: var(--graphite); white-space: nowrap; margin-inline: var(--s1); }
.detail-scroll { position: relative; margin: 3vh 8vw 5vh; min-width: 0; min-height: 0; overflow: auto; overscroll-behavior: contain;
  scrollbar-gutter: stable; background: #fff; border: 1px solid var(--rule); border-radius: var(--r-lg); overflow-anchor: none; }
.detail-loading { position: absolute; inset: 0; display: grid; place-items: center; color: var(--edition-palette-ink-quiet); font-size: var(--t-small); }
.detail-scroll:has(#detail-mount:not(:empty)) .detail-loading { display: none; }
#detail-mount { position: relative; min-height: 0; overflow: hidden; contain: layout paint; }
#concept-info { position: absolute; z-index: 2; right: 16px; top: 80px; bottom: 16px; width: min(370px, calc(100vw - 32px));
  padding: var(--s6); background: var(--paper); border: 1px solid var(--rule); border-radius: var(--r-lg);
  box-shadow: var(--shadow-dialog); overflow: auto; }
.info-heading { display: flex; justify-content: space-between; align-items: center; gap: var(--s5); }
.info-heading h2 { font-size: var(--t-heading); font-weight: 600; }
.info-heading button { min-height: 36px; padding-inline: 10px; border: 1px solid var(--rule); border-radius: var(--r-md); font-size: var(--t-caption); }
#concept-info > p { line-height: var(--lh-prose); margin-top: var(--s4); }
#detail-status { color: var(--accent); font-size: var(--t-caption); }
#concept-info dl { display: grid; grid-template-columns: 80px 1fr; gap: 10px; font-size: var(--t-caption); padding-block: var(--s5);
  margin-block: var(--s5); border-block: 1px solid var(--rule-soft); }
#concept-info dt { color: var(--graphite); }
#copy { min-height: 40px; padding-inline: 12px; border: 1px solid var(--rule); border-radius: var(--r-md); font-size: var(--t-caption); }
#copy-status { margin-top: var(--s2); font-size: var(--t-caption); color: var(--graphite); }

/* The lightbox: one brand piece as its full tile, captioned beneath. The
   image keeps the ground it was drawn on; the frame is the wall's. */
#lightbox-position { flex: 1; text-align: right; font-family: var(--mono); font-size: var(--t-eyebrow); color: var(--graphite);
  white-space: nowrap; margin-inline: var(--s1); }
.lightbox-figure { display: grid; grid-template-rows: minmax(0, 1fr) auto; justify-items: center; align-items: center;
  gap: var(--s4); margin: 0; padding: 3vh 4vw 4vh; min-height: 0; }
.lightbox-figure img { display: block; width: auto; height: auto; max-width: 100%; max-height: 100%;
  border: 1px solid var(--rule); border-radius: var(--r-lg); background: var(--paper-2); }
.lightbox-figure figcaption { display: grid; justify-items: center; gap: 2px; text-align: center;
  font-size: var(--t-small); font-weight: 500; color: var(--ink); }
.lightbox-figure figcaption small { font-size: var(--t-caption); font-weight: 400; color: var(--graphite); }
#lightbox-title { outline-offset: 4px; border-radius: 3px; }

@media (max-width: 1100px) { #position, #detail-deck { display: none; } }
@media (max-width: 650px) {
  .viewer-toolbar { display: grid; grid-template-columns: auto 1fr auto auto; gap: 4px; padding: 6px 8px; min-height: 0; }
  .viewer-title { grid-column: 2 / 5; grid-row: 1; padding: 0 6px; }
  .viewer-title h2 { font-size: .9375rem; }
  .viewer-toolbar button, .viewer-toolbar select { min-height: 36px; font-size: var(--t-eyebrow); padding: 5px 8px; }
  .zoom-label { grid-row: 2; grid-column: 1; }
  .zoom-label select { max-width: 122px; }
  #info { grid-column: 2; grid-row: 2; }
  #prev { grid-column: 3; grid-row: 2; }
  #next { grid-column: 4; grid-row: 2; }
  #concept-info { top: 96px; }
  .detail-scroll { margin: 4px; border-radius: var(--r-md); }
  .lightbox .viewer-toolbar { grid-template-columns: auto 1fr auto auto; }
  .lightbox-figure { padding: 12px; gap: var(--s3); }
}
