/* ui.css, the ui lane's: the page, the header, controls, the dock, the inspection sheet, wells, the
   machine view, forced colours, the phone and the components sheet. Soft is the room, gloss is the work:
   everything that is not a card is one clay lit from the top left (tokens: vendor/neu-tokens.css, a copy
   of r3/study/neu-bench, inlined unchanged by build.mjs). Loads first; card.css and rituals.css follow.
   Everything that moves moves by transform or opacity. */

/* A card's width and radius are lengths, resolved where they are set. Unregistered, a width written in
   cqw on the wall travelled into every card as text and resolved again against each card's own box, so
   the sleeve inside a card read a radius a third of the card's. Registered, it is one number everywhere. */
@property --card { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --r { syntax: '<length>'; inherits: true; initial-value: 0px; }

:root {
  --sans: 'Katagami Sans';
  /* the frosted slips' tile at the screen's density (card.css prints its 1x tile, a data URL, where a page sets none) */
  --frost: image-set(url("icons/frost.webp") 1x, url("icons/frost@2x.webp") 2x);
  --ctl: 40px;
  --icon: 18px;
  --lab: 14px;
  --r-ctl: 12px;
  --inset: 4px;
  --r-track: 16px;
  --ease: cubic-bezier(0.2, 0, 0, 1);
  --ease-move: cubic-bezier(0.25, 1, 0.5, 1);
  --g: 16px; --gap: 16px; --hdr: 56px; --dock: 82px; --cols: 1;
  /* the wall's card: the narrowest at which every name sets in two lines beside its squares (Prototemplate,
     3 inks, needs 162), and on a short window a cap that keeps two rows on screen, but never below --tw, the
     narrowest card whose collector number sets at its own size (card.css: 3.6cqw reaches 9 px at 250) */
  --t1: 164px; --tw: 250px; --hcap: 100000px;
  --segw: 96px; /* fixed, so a label never moves a box */
  --emb-hi: oklch(100% 0 0 / 0.95);
  --emb-lo: oklch(42% 0.035 70 / 0.42);
  --rival: oklch(52% 0.19 30); /* the rival's ink: their sleeves and their wells' rims */
}
[data-theme='dark'] { --emb-hi: oklch(100% 0 0 / 0.1); --emb-lo: oklch(8% 0.01 70 / 0.8); --rival: oklch(72% 0.15 32); }
/* From 640 the room's gutters and bars grow, and a card is capped so two rows stand on screen (never under --tw). The
   columns follow in the wall-rule block below the wall. */
@media (min-width: 640px) { :root { --g: 24px; --hdr: 64px; --dock: 96px; --hcap: calc(((100vh - var(--hdr) - var(--dock) - 24px - var(--gap)) / 2 - 12px) / 1.397); } }
/* On a phone a card's pop-outs and front effects print up to 0.085 of its width past its trim (Claude of Tanks'
   smoke), so under 640 the gutter grows with the screen until the trim stands that far inside it (g + 6 >= 0.085 x
   the card, for the columns the wall prints): the wall-rule block below the wall sets it with the columns. */
@media (pointer: coarse) { :root { --ctl: 44px; --icon: 20px; --lab: 16px; --segw: 104px; } }

html {
  font-family: var(--sans);
  font-kerning: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background: var(--clay);
  color: var(--ink-1);
  -webkit-text-size-adjust: 100%;
  scrollbar-gutter: stable;
}
/* the page behind the lens does not scroll: the lens is its own scroll container (overscroll-behavior: contain, below), so
   no wheel or swipe over it reaches the page. Locking the page instead (body overflow: hidden) repainted the whole page
   as the lens opened and again as it closed: 27 to 56 ms of paint each with the CPU slowed 4x (.work/room-ex4b/t/insp.mjs) */
body { margin: 0; background: var(--clay); overflow-x: hidden; }
button, a { font-family: inherit; color: inherit; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.i { width: var(--icon); height: var(--icon); fill: currentColor; flex: none; display: block; }
/* the icon sprite at the foot of the page: every icon is a <use> of it */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
:focus-visible { outline: 2px solid var(--ink-1); outline-offset: 2px; }
:focus:not(:focus-visible) { outline: none; }

/* ---------- one press for every pressable: raised at rest, sunk when pressed or on ----------
   Two shadow layers crossfade by opacity (box-shadow never animates); 0.97 in 90 ms, back in 220.
   Flat (both layers off) is disabled: ink-3 when native, ink-2 when it stays focusable to name its
   reason (aria-disabled); disabled never scales. */
.press, .play, .addbtn {
  position: relative; isolation: isolate; box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--ctl); min-width: var(--ctl); padding: 0 16px 0 14px; /* the icon side pads 2 px less */
  border: 1px solid transparent; border-radius: var(--r-ctl);
  font: 600 var(--lab)/20px var(--sans); text-decoration: none; letter-spacing: 0;
  cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent; touch-action: manipulation;
  transition: transform 220ms var(--ease); /* tone changes are instant: only transform and opacity move */
}
.press { background: var(--clay); color: var(--ink-1); }
.press > span { text-box: trim-both cap alphabetic; }
:is(.press, .play, .addbtn)::before, :is(.press, .play, .addbtn)::after {
  content: ''; position: absolute; inset: -1px; border-radius: inherit; z-index: -1; pointer-events: none;
  transition: opacity 220ms var(--ease);
}
:is(.press, .play, .addbtn)::before { box-shadow: var(--raise-s); }
:is(.press, .play, .addbtn)::after { box-shadow: var(--sink-s); opacity: 0; }
.press.icon { padding: 0; width: var(--ctl); }
@media (hover: hover) { .press:hover:not(:disabled, [aria-disabled='true']) { background-color: var(--clay-hi); } }
.press.is-hover { background-color: var(--clay-hi); }
:is(.press, .play, .addbtn):is(:active, .is-active):not(:disabled, [aria-disabled='true']) { transform: scale(0.97); transition-duration: 90ms; }
:is(.press, .play, .addbtn):is(:active, .is-active, [aria-pressed='true']):not(:disabled, [aria-disabled='true'])::before { opacity: 0; transition-duration: 90ms; }
:is(.press, .play, .addbtn):is(:active, .is-active, [aria-pressed='true']):not(:disabled, [aria-disabled='true'])::after { opacity: 1; transition-duration: 90ms; }
:is(.press, .play, .addbtn):is(:disabled, [aria-disabled='true']) { background: var(--clay); color: var(--ink-3); cursor: default; }
/* a control that stays focusable to say why it waits reads its reason in the off ink (4.67:1) */
:is(.press, .play, .addbtn)[aria-disabled='true'] { color: var(--ink-2); }
:is(.press, .play, .addbtn):is(:disabled, [aria-disabled='true'])::before, :is(.press, .play, .addbtn):is(:disabled, [aria-disabled='true'])::after { opacity: 0; }
/* the one filled control of a view: ink plate, clay label; hover lifts the plate 0.08 in L */
.play, .addbtn { background: var(--ink-1); color: var(--clay); padding: 0 14px; }
@media (hover: hover) { :is(.play, .addbtn):not([aria-disabled='true']):hover { background: oklch(from var(--ink-1) calc(l + 0.08) c h); } }
:is(.play, .addbtn).is-hover { background: oklch(from var(--ink-1) calc(l + 0.08) c h); }
.is-focus { outline: 2px solid var(--ink-1); outline-offset: 2px; }

/* one cell, two glyphs: Pause Motion swaps to play */
.swapi { display: grid; }
.swapi > .i { grid-area: 1 / 1; transition: opacity 300ms var(--ease), transform 300ms var(--ease); }
.swapi > .on { opacity: 0; transform: scale(0.25); }
[aria-pressed='true'] .swapi > .off { opacity: 0; transform: scale(0.25); }
[aria-pressed='true'] .swapi > .on { opacity: 1; transform: none; }

/* one cell, every label: the cell is as wide as the widest, so a state never moves a box. A label that
   leads with an icon and shows words carries the 2 px its icon side gives back; a label of words alone, or
   an icon alone, sits centred. The give-back weighs nothing (:where), so a rule that hides a label's icon or
   its words takes it back by being later. Labels cross in the 150 ms an exit takes. */
.stack2 { display: inline-grid; }
.stack2 > .lab {
  grid-area: 1 / 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  opacity: 0; transition: opacity 150ms var(--ease); text-box: trim-both cap alphabetic; white-space: nowrap;
}
.stack2 > .lab:where(:has(> .i:first-child)) { padding-inline-end: 2px; }
.lab > .t { text-box: trim-both cap alphabetic; }

/* ---------- header ----------
   The header stands on the wall's rails: as wide as its pockets, centred as they are, so Kevin Liu starts at
   the first pocket's edge and Pause ends at the last one's at every width (one rule for both). */
header.top {
  display: flex; align-items: center; gap: 12px; height: var(--hdr); padding: 0 var(--g); box-sizing: content-box; margin-inline: auto;
  max-width: calc(var(--cols) * (max(var(--tw), var(--hcap)) + 12px) + (var(--cols) - 1) * max(var(--gap), 0.132 * max(var(--tw), var(--hcap)) - 4px));
}
header.top h1 { margin: 0 auto 0 0; font: 700 20px/24px var(--sans); white-space: nowrap; text-box: trim-both cap alphabetic; } /* its cap on the controls' axis */
@media (max-width: 479px) { :root { --segw: 88px; } header.top { gap: 8px; } }
@media (max-width: 359px) { :root { --segw: 76px; } header.top { gap: 6px; } header.top h1 { font-size: 18px; } }
/* Human and Machine: a raised thumb in a sunk track. On is ink-1 at 600, off ink-2 at 500, the two
   weights stacked in each cell, so the thumb's arrival changes ink, weight and depth and moves no text. */
.seg {
  --n: 2; position: relative; isolation: isolate; display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  height: var(--ctl); padding: 0; border-radius: var(--r-track); background: var(--clay); box-shadow: var(--sink-s);
}
.seg::before {
  content: ''; position: absolute; z-index: -1; top: var(--inset); bottom: var(--inset); left: var(--inset);
  width: calc((100% - 2 * var(--inset)) / var(--n)); border-radius: var(--r-ctl); background: var(--clay); box-shadow: var(--raise-s);
  transform: translateX(calc(var(--i) * 100%)); transition: transform 220ms var(--ease-move);
}
.seg > button {
  display: grid; place-items: center; height: 100%; width: var(--segw); padding: 0; border: 1px solid transparent; border-radius: var(--r-track);
  background: none; font: var(--lab)/20px var(--sans); cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation;
  transition: scale 220ms var(--ease);
}
.seg > button:active { scale: 0.97; transition-duration: 90ms; }
.seg .l-off { opacity: 1; font-weight: 500; color: var(--ink-2); transition: opacity 150ms var(--ease); }
.seg .l-on { font-weight: 600; color: var(--ink-1); }
.seg > button[aria-pressed='true'] .l-off { opacity: 0; }
.seg > button[aria-pressed='true'] .l-on { opacity: 1; }
@media (hover: hover) { .seg > button:hover .l-off { color: var(--ink-1); } }
.seg > button.is-hover .l-off { color: var(--ink-1); }
.seg > button:focus-visible, .seg > button.is-focus { outline-offset: -2px; }

/* ---------- the wall of pockets ----------
   A subject reaches at most 0.066W past its trim (EX.md 13), so two that face each other across a column stand at
   least 8 px apart when their trims are 0.132W + 8 px apart: the column gap (--cg) is that less the two pockets'
   12 px, and never under --gap. Rows need only --gap, since no subject passes its card's top or foot. The card is
   the widest the row holds under both gaps (the narrower of the two widths), and never over its cap. */
.gallery { container-type: inline-size; padding: 12px 0 calc(var(--dock) + 40px); }
/* The wall's left and right edges are the screen's, so cutting there removes only what the screen cuts already: a
   subject's picture box may run past the screen on a phone (its clear margin), and without the cut it widened the
   page, which then panned sideways (390 px phone: 401 px wide). Never a cut across a card. */
.gallery, .inspect { overflow-x: clip; }
.wall {
  --card: min(calc((100cqw - 2 * var(--g) - (var(--cols) - 1) * var(--gap)) / var(--cols) - 12px),
    calc((100cqw - 2 * var(--g) - 8px * var(--cols) - 4px) / (var(--cols) + 0.132 * (var(--cols) - 1))), max(var(--tw), var(--hcap)));
  --cg: max(var(--gap), calc(0.132 * var(--card) - 4px));
  list-style: none; margin: 0; padding: 0 var(--g);
  display: grid; grid-template-columns: repeat(var(--cols), calc(var(--card) + 12px)); row-gap: var(--gap); column-gap: var(--cg); justify-content: center;
}
/* wall-rule:auto (node build.mjs writes this block from tools/wall-rule.mjs; edit the rule there) */
/* Columns: the most at which a card stays 240 px wide (EX.md 7: under 240 a card hides its what line, under 225 its
   move and trait text), up to the 7 x 4 wall's 7: 2 from 564, 3 from 860, 4 from 1140, 5 from 1419, 6 from 1699, 7 from 1979. The wall reads its own
   gallery's width (a container query, so a scrollbar never squeezes a card under the floor); one column holds a whole card
   on a short window but never under the floor. Under 640 the side gutter grows with the screen until g + 6 px holds
   0.085 of the card past its trim (Claude of Tanks' smoke) for the columns it prints. The root's --cols and --g (media
   queries) only set the header on the same rails. */
.wall { --cols: 1; --g: max(16px, calc(7.26cqw - 6.00px)); --hcap: calc((100vh - var(--hdr) - var(--dock) - 36px) / 1.397); }
@container (min-width: 564px) { .wall { --cols: 2; --g: inherit; --hcap: inherit; } }
@container (min-width: 860px) { .wall { --cols: 3; } }
@container (min-width: 1140px) { .wall { --cols: 4; } }
@container (min-width: 1419px) { .wall { --cols: 5; } }
@container (min-width: 1699px) { .wall { --cols: 6; } }
@container (min-width: 1979px) { .wall { --cols: 7; } }
@media (max-width: 563.98px) { :root { --g: max(16px, calc(7.26vw - 6.00px)); } }
@media (min-width: 564px) and (max-width: 639.98px) { :root { --g: max(16px, calc(3.69vw - 6.30px)); } }
@media (min-width: 564px) { :root { --cols: 2; } }
@media (min-width: 860px) { :root { --cols: 3; } }
@media (min-width: 1140px) { :root { --cols: 4; } }
@media (min-width: 1419px) { :root { --cols: 5; } }
@media (min-width: 1699px) { :root { --cols: 6; } }
@media (min-width: 1979px) { :root { --cols: 7; } }
/* wall-rule:end */
.pocket {
  --r: max(3px, calc(var(--card) * 0.048));
  position: relative; padding: 6px; border-radius: calc(var(--r) + 6px); background: var(--clay); box-shadow: var(--sink-m);
  /* Each pocket is its own stacking group, so the card's stacked parts (z 1 to 6) stay in it. Without one, a card is no
     stacking context (container-type no longer implies layout containment), so all 26 cards' 900 stacked parts shared
     the wall's one list, and every pointer move's hit test walked the other cards' parts before reaching the hovered
     one, whatever its z-index: elementFromPoint over a hovered card took 1,060 us at 1x, 170 with the group
     (.work/room-ex4b/t/hit3.mjs). Isolation clips nothing and blends nothing new: no subject reaches a neighbour's
     pocket (the column gap above), and the card's own glow and gloss blend inside the card as before. */
  isolation: isolate;
}
/* the card's ground and poster take no pointer: the card's own box does, so a move never lands on a canvas */
.card .en, .card canvas.ground, .card .poster { pointer-events: none; }
.pocket:focus-within, .pocket.up { z-index: 2; }
@media (hover: hover) { .pocket:hover { z-index: 2; } }
.pocket.dragging { z-index: 50; }

/* ---------- the dock: your wells, the gauge and Play, the rival's wells ----------
   Raised means pressable, so the dock, which floats and is never pressed, casts as a lifted card does: a dark
   shadow down and to the right of the one light, with no highlight to fog the prints it passes over. */
.dock {
  position: fixed; z-index: 40; left: 50%; bottom: 8px; translate: -50% 0;
  display: flex; align-items: center; gap: 24px; height: var(--dock); padding: 10px; box-sizing: border-box;
  border-radius: 16px; background: var(--clay); box-shadow: var(--cast-lift);
  transition: opacity 150ms var(--ease);
}
/* the cards scroll under the dock: in the dark room its cast is the lift's shape at about half the depth and reach, so it
   no longer darkens the prints beside it by up to 64 levels (judge r4-2) */
[data-theme='dark'] .dock { box-shadow: 1.34px 2.68px 6px oklch(8% 0.01 70 / 0.3), 2.68px 5.37px 12px -2px oklch(8% 0.01 70 / 0.2), 5.37px 10.73px 24px -4px oklch(8% 0.01 70 / 0.14); }
.side { display: flex; gap: 4px; }
/* A well is the card it holds plus a 3 px inset all round (its 1 px border and the hold's 2): 56 x 76 holds a
   50 x 69.8 card. Its radius is the card's 3 plus that inset, 6, and 16 = 6 + 10 nests it in the dock. */
.well {
  --wr: 6px;
  position: relative; width: 56px; height: 76px; padding: 0; box-sizing: border-box; border: 1px solid transparent;
  border-radius: var(--wr); background: var(--clay); box-shadow: var(--sink-m); color: var(--ink-2);
  display: grid; place-items: center; -webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
button.well { cursor: pointer; transition: transform 220ms var(--ease); }
button.well:active:not(:disabled) { transform: scale(0.97); transition-duration: 90ms; }
button.well:disabled { color: var(--ink-3); cursor: default; }
@media (hover: hover) { button.well:hover:not(:disabled) { color: var(--ink-1); } }
.well.is-hover { color: var(--ink-1); }
.well .hold { position: absolute; inset: 2px; border-radius: calc(var(--wr) - 2px); container-type: inline-size; }
.well .hold > .card { position: absolute; left: 0; top: 0; --card: 100cqw; width: var(--card); }
.well .hold:empty { display: none; }
/* the next pack's card for this well, cut and laid out ahead and unseen until it lands (gallery.js premini) */
.well .hold > .card.premini { visibility: hidden; }
/* Remove: a 16 px chip 1 px inside the card's corner, so its radius is the card's 3 less that 1: concentric */
.well .rm { position: absolute; z-index: 3; right: 3px; top: 3px; width: 16px; height: 16px; padding: 2px; box-sizing: border-box; border-radius: 2px; background: var(--clay); color: var(--ink-1); opacity: 0; transition: opacity 150ms var(--ease); }
.well.full .plus { opacity: 0; }
.well.full:is(:focus-visible, .is-hover) .rm { opacity: 1; }
@media (hover: hover) { .well.full:hover .rm { opacity: 1; } }
@media (pointer: coarse) { .well.full .rm { opacity: 1; } }
.well:focus-visible, .well.is-focus { outline: 2px solid var(--ink-1); outline-offset: 2px; }
.well .plus { transition: opacity 150ms var(--ease); }
.mid { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.gauge { display: flex; gap: 3px; }
.gauge i { width: 8px; height: 8px; box-sizing: border-box; border-radius: 2px; border: 1.5px solid var(--ink-2); }
.gauge i.on { background: var(--ink-1); border-color: var(--ink-1); }
/* Play: its labels share one cell; flat with its reason until both teams exist (Pick Three), and held with its reason
   while a team holds a card the battle does not hold yet (Battle Soon; its name and the status line name the card).
   That third label is drawn here, so it sizes the cell from the first paint and no state ever moves a box. */
.play .stack2::after {
  content: 'Battle Soon'; grid-area: 1 / 1; display: inline-flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 150ms var(--ease); text-box: trim-both cap alphabetic; white-space: nowrap;
}
.play .l-go { opacity: 1; }
.play:is([data-state='wait'], [data-state='held']) .l-go { opacity: 0; }
.play[data-state='wait'] .l-wait, .play[data-state='held'] .stack2::after { opacity: 1; }
.play .l-wait > .i { display: none; }
.play .l-wait { padding-inline-end: 0; } /* its icon shows only on the phone, where no label carries words */
/* the dock steps under the lens (z 30), which then takes its pointer: pointer-events is inherited, so switching it restyled
   the dock and every card in its wells, 280 to 860 elements with the CPU slowed 4x, in the frame the lens settles */
html.inspecting .dock { opacity: 0; z-index: 29; }

/* ---------- the phone: one column, the dock a tray across the foot ----------
   Every well is 46 x 62 (a 40 x 55.9 card, the same 3 px inset and radius 6) on a 50 pitch, yours and the
   rival's alike; Play is a 54 x 44 icon. At 390 they fill the tray exactly; narrower, the rival's give way
   first, since they hold cards to look at, not to press. */
@media (max-width: 639px) {
  .dock { left: 8px; right: 8px; bottom: max(8px, env(safe-area-inset-bottom)); translate: none; justify-content: space-between; gap: 4px; padding: 10px; }
  .well { width: 46px; height: 62px; }
  .side.rival { flex: 0 1 146px; min-width: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px; }
  .rival .well { width: auto; height: auto; aspect-ratio: 46 / 62; }
  .mid { width: 54px; gap: 6px; }
  .gauge { gap: 2px; }
  .gauge i { width: 6px; height: 6px; border-radius: 1.5px; border-width: 1.25px; }
  .play { width: 54px; height: 44px; padding: 0; }
  .play .lab { padding: 0; }
  .play .lab > .t, .play .stack2::after { display: none; }
  .play .l-wait > .i { display: block; }
  .play[data-state='held'] .l-wait { opacity: 1; }
}

/* ---------- inspection: the card lifted over its world, the sheet beside it ---------- */
.inspect { position: fixed; inset: 0; z-index: 30; overflow-y: auto; --iw: max(164px, min(560px, (100vh - 80px) / 1.397, 100vw - 56px)); /* 28 px a side: the pop-out reaches 0.066W past the trim */ overscroll-behavior: contain;
  --sg: max(32px, calc(var(--iw) * 0.07 + 8px)); /* the sheet stands clear of the lens card's pop-out (0.066W past its trim) */ }
.inspect:focus { outline: none; }
/* Closed, the lens stays laid out and unseen (no paint, hit, focus or accessibility node), so a lift only
   shows it: a display switch rebuilt its whole box tree in the press's frame. */
.inspect[hidden] { display: block; visibility: hidden; pointer-events: none; }
.ins-card { position: absolute; width: var(--iw); aspect-ratio: 63 / 88; left: calc((100vw - var(--iw) - 400px - var(--sg)) / 2); top: calc((100vh - var(--iw) * 1.397) / 2); }
.ins-card > .card { --card: var(--iw); position: absolute; left: 0; top: 0; transform-origin: 0 0; will-change: transform; }
/* the lift fades the lens card's type in as the card grows (rituals.js); on its own layer that fade is the
   compositor's, so no lift frame repaints and re-layers the card for it */
/* the lens's type fades in with the lift (rituals.js) and never takes a layer of its own, so its glass and type stay
   on either side of the subject */
/* the lens card stands on its world, not on clay, so its ring carries clay on both sides and reads on any
   world (2 px of ink-1 between bands of clay); a sleeved card's ring clears its sleeve first */
.ins-card > .card:is(:focus-visible, .is-focus) { box-shadow: 0 0 0 7px var(--clay); }
.ins-card > .card:is(.mine, .theirs):is(:focus-visible, .is-focus) { box-shadow: 0 0 0 calc(var(--slw) + 7px) var(--clay); }
/* the sheet: 24 = the tray's 16 + its 8 inset; Close sits 12 in, 24 = 12 + 12 */
.sheet {
  position: absolute; left: calc((100vw - var(--iw) - 400px - var(--sg)) / 2 + var(--iw) + var(--sg)); top: calc((100vh - var(--iw) * 1.397) / 2);
  width: 400px; height: calc(var(--iw) * 1.397); box-sizing: border-box; display: flex; flex-direction: column;
  border-radius: 24px; background: var(--clay); color: var(--ink-1);
  box-shadow: var(--cast-lift), 0 0 0 1px oklch(42% 0.035 70 / 0.12);
  opacity: 0; transform: translateY(12px); visibility: hidden;
  transition: opacity 150ms var(--ease), transform 150ms var(--ease), visibility 150ms;
}
/* the sheet shows on the lift's frame after the card (.open), not with it, so neither frame restyles both */
.inspect.open .sheet { opacity: 1; transform: none; visibility: visible; transition: opacity 300ms var(--ease) 120ms, transform 300ms var(--ease) 120ms, visibility 0s; }
/* The head never scrolls, so no line runs under Close, and its row keeps Close's axis at either pointer: the
   disciplines' first 22 px line hangs (--ctl - 22) / 2 below the padding, centred on Close, and a wrapped
   line hangs below it. */
.sheet-head { display: flex; align-items: flex-start; gap: 16px; padding: 12px 12px 0 24px; }
.sheet .close { flex: none; margin-left: auto; }
.sheet-body { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding: 20px 24px 24px; }
/* where the sheet scrolls, a line meets its edges through a fade instead of being cut in half: 12 px at the
   top, inside the body's 20 px of padding, and 16 at the foot, inside its 24, so at either end nothing fades */
@media (min-width: 1056px) { .sheet-body { -webkit-mask: linear-gradient(transparent, #000 12px, #000 calc(100% - 16px), transparent); mask: linear-gradient(transparent, #000 12px, #000 calc(100% - 16px), transparent); } }
.disc { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: calc((var(--ctl) - 22px) / 2) 0 0; font: 600 15px/22px var(--sans); }
.disc span { display: inline-flex; align-items: center; gap: 6px; }
.disc .i, .facts .i { width: 16px; height: 16px; }
.facts { list-style: none; margin: 0 0 20px; padding: 0; display: grid; gap: 12px; }
.facts li { display: grid; grid-template-columns: 16px 1fr; gap: 10px; align-items: start; }
.facts li .i { margin-top: 2.8px; } /* its centre on the 17 px numeral's cap band: 11 - 8 - 0.2, measured */
.facts p { margin: 0; font: 500 15px/22px var(--sans); }
.facts b { font-weight: 700; font-size: 17px; font-variant-numeric: tabular-nums; }
.facts li.lit { color: var(--ink-1); }
/* receipts set in the face's own proportional figures: its tabular figures widen the hyphen too (22.2 to 31.1 px at
   48 px), so Kevin-Liu-01 and 2026-10-30 read as spaced dashes; only the facts' numerals line up in columns */
code { display: block; margin-top: 2px; font: 500 12px/16px var(--sans); color: var(--ink-2); overflow-wrap: anywhere; word-break: break-word; }
.sheet .trait { margin: 0 0 20px; }
.sheet .trait p, .moves p { margin: 0; font: 500 15px/22px var(--sans); text-wrap: pretty; }
.sheet .trait b, .moves b { font: 600 17px/24px var(--sans); }
.moves { list-style: none; margin: 0 0 20px; padding: 0; display: grid; gap: 14px; }
/* the pinned commit: 12 px type on a whole 40 px row (44 coarse); the row's extra height hangs into the
   space around it, so the text sits where it always did */
.pin { display: flow-root; margin: 0 0 12px; font: 500 12px/16px var(--sans); }
.pin a { display: flex; align-items: center; min-height: var(--ctl); margin-block: calc((16px - var(--ctl)) / 2); color: var(--ink-1); text-underline-offset: 3px; -webkit-tap-highlight-color: transparent; }
.credit { margin: 0; font: 500 12px/16px var(--sans); color: var(--ink-2); }
/* the tray: sized by what it holds, 8 in from the sheet's corner, its buttons 4 in (16 = 12 + 4) */
.acts { display: flex; gap: 4px; align-self: flex-start; margin: 0 8px 8px; padding: 4px; border-radius: var(--r-track); background: var(--clay); box-shadow: var(--sink-s); }
.addbtn { flex: none; }
.addbtn .rot { transition: transform 300ms var(--ease); }
.addbtn .l-add { opacity: 1; }
.addbtn:not([data-state='add']) .l-add { opacity: 0; }
.addbtn[data-state='remove'] .l-remove, .addbtn[data-state='full'] .l-full, .addbtn[data-state='over'] .l-over { opacity: 1; }
.addbtn[data-state='remove'] .rot { transform: rotate(45deg); }
.visit { flex: none; }
@media (max-width: 1055px) {
  .inspect { overflow-y: auto; }
  .ins-card { position: relative; left: auto; top: auto; margin: 16px auto 0; }
  .sheet { position: relative; left: auto; top: auto; width: auto; height: auto; margin: 16px 16px 96px; }
  .sheet-body { overflow: visible; }
  /* The tray floats at the foot on a clay plate as wide as the screen, which rises out of the sheet through
     a 24 px fade and is solid from 8 px above the tray, so no line of prose ever runs behind or beside it.
     The plate and the tray's own body are the tray's two back layers, plate first. */
  .acts { position: fixed; left: 50%; bottom: max(8px, env(safe-area-inset-bottom)); translate: -50% 0; margin: 0; z-index: 2; background: none; box-shadow: none; }
  .acts::before, .acts::after { content: ''; position: absolute; z-index: -1; pointer-events: none; }
  .acts::before { left: 50%; width: 100vw; translate: -50% 0; top: -32px; bottom: calc(-1 * max(8px, env(safe-area-inset-bottom))); background: linear-gradient(oklch(from var(--clay) l c h / 0), var(--clay) 24px); }
  .acts::after { inset: 0; border-radius: inherit; background: var(--clay); box-shadow: var(--cast-lift); }
}
/* without a GPU the room is the card's world as the pour prints it, baked once and placed by gallery.js,
   fading in as the world would pour. Its dithered cells are drawn nearest only where one baked px lands on a
   whole number of device px (.whole: the 560 px lens at 1x and 2x); at any other scale nearest would drop cells */
.room { display: none; }
html[data-nogpu] .room { display: block; position: fixed; inset: 0; background: var(--clay) var(--room, none) var(--room-at, 50% 40% / cover) no-repeat; opacity: 0; pointer-events: none; }
html[data-nogpu] .room.baked.whole { image-rendering: pixelated; }

/* ---------- ?og=a,b,c: the home share loop, a team of three on the clay (gallery.js ogFrame) ---------- */
.og3 { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; gap: 5vw; background: var(--clay); }
.og3 .card { --card: min(28vw, calc((100vh - 150px) / 1.397)); flex: none; }
html[data-og] .human > :not(.og3, .inspect, #world), html[data-og] .inspect .sheet { visibility: hidden; }
html[data-og] .ins-card { left: calc((100vw - var(--iw)) / 2); }

/* ---------- the machine view: Katagami's CRT tokens in the one face ---------- */
main.machine { display: none; }
html[data-mode='machine'] .human { display: none; }
html[data-mode='machine'] body { background: #060807; }
html[data-mode='machine'] main.machine { display: block; }
main.machine {
  --crt-bg: #060807; --crt-fg: #c9dccd; --crt-hi: #effff1; --crt-dim: #6b8070; --crt-link: #8fb6ff; --crt-tick: #ff8072;
  min-height: 100vh; box-sizing: border-box; background: var(--crt-bg); color: var(--crt-fg);
  font: 500 14px/1.6 var(--sans); padding: 18px 22px 64px; text-shadow: 0 0 6px rgb(201 220 205 / 0.18);
}
main.machine nav { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; margin-bottom: 24px; color: var(--crt-dim); }
main.machine a { color: var(--crt-link); text-decoration: none; }
@media (hover: hover) { main.machine a:hover { text-decoration: underline; } }
main.machine .tick { color: var(--crt-tick); }
main.machine button { background: none; border: 0; color: var(--crt-dim); font: inherit; padding: 0 4px; cursor: pointer; min-height: var(--ctl); min-width: var(--ctl); }
main.machine button[aria-pressed='true'] { color: var(--crt-hi); }
main.machine pre { font: inherit; white-space: pre-wrap; overflow-wrap: anywhere; margin: 0 0 22px; max-width: 110ch; }
main.machine .dim { color: var(--crt-dim); }
main.machine .hi { color: var(--crt-hi); font-weight: 700; }
main.machine :focus-visible { outline-color: var(--crt-hi); }

/* ---------- reduced motion: every state is a still, crossfaded ---------- */
@media (prefers-reduced-motion: reduce) {
  .seg::before, .seg .l-off, .swapi > .i, .stack2 > .lab, .play .stack2::after, .addbtn .rot, .well, .well .rm, .well .plus,
  .press, .play, .addbtn, :is(.press, .play, .addbtn)::before, :is(.press, .play, .addbtn)::after { transition: none; }
  .inspect.open .sheet, .sheet { transform: none; transition: opacity 150ms linear, visibility 150ms linear; }
  .seg > button { transition: none; }
}
/* ---------- forced colours: every edge and state by system colour, never by shadow ---------- */
@media (forced-colors: active) {
  .pocket, .well, .acts, .seg, .dock { border: 1px solid CanvasText; }
  .seg::before { forced-color-adjust: none; background: Highlight; }
  .seg > button[aria-pressed='true'] .l-on { forced-color-adjust: none; color: HighlightText; }
  .gauge i { forced-color-adjust: none; border-color: CanvasText; }
  .gauge i.on { background: CanvasText; }
  :is(.play, .addbtn)[aria-disabled='true'] { color: GrayText; border-color: GrayText; }
}

/* ---------- components.html: every component, every state, both themes ---------- */
body.components { padding: 0; }
.cmp { background: var(--clay); color: var(--ink-1); padding: 40px 48px 56px; }
.cmp h2 { font: 700 20px/24px var(--sans); margin: 0 0 28px; }
.cmp .mx { display: grid; grid-template-columns: 152px repeat(6, minmax(0, 1fr)); gap: 28px 20px; align-items: center; justify-items: center; }
.cmp .mx > .h { font: 500 12px/16px var(--sans); color: var(--ink-2); align-self: end; }
.cmp .mx > .rh { justify-self: start; font: 600 14px/20px var(--sans); }
.cmp .mx > .c { display: grid; place-items: center; min-height: 48px; }
.cmp .row2 { display: flex; flex-wrap: wrap; gap: 28px 24px; align-items: end; margin-top: 40px; }
.cmp figure { margin: 0; display: grid; gap: 10px; justify-items: center; }
.cmp figcaption { font: 500 12px/16px var(--sans); color: var(--ink-2); }
.cmp .cw { padding: 6px; border-radius: calc(var(--r) + 6px); --r: max(3px, calc(var(--card) * 0.048)); box-shadow: var(--sink-m); background: var(--clay); }
.cmp .ic { display: flex; gap: 10px; align-items: center; }
.cmp .ic span { display: grid; place-items: center; width: var(--s); height: var(--s); }
.cmp .ic .i { width: var(--s); height: var(--s); }
.cmp .dockspec .dock { position: relative; left: auto; right: auto; bottom: auto; translate: none; }
.cmp .acts { margin: 0; }
