:root {
  --paper: #efe9dd;
  --ink: #2b2724;
  --line: rgba(43, 39, 36, 0.22);
}

* { box-sizing: border-box; }

/* hidden means gone. The browser's own [hidden] rule loses to any display rule of ours on the same element — a flex row, a
   grid cell, a block button — and a thing meant to be hidden stayed in the layout: a tail tab on a human as an empty slot, a
   paint row on a part that takes no paint, and the texture strip's flat ball on skin or cloth — hidden, still displayed, its
   never-painted 300×150 canvas lying over the DENSITY slider and taking every drag meant for it. Said once, over everything,
   instead of once more at every site it bit */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--paper);
  color: var(--ink);
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.04em;
}

#stage {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* Stated explicitly. Relying on DOM order alone is not enough. */
  z-index: 0;
}

.bar {
  position: fixed;
  z-index: 2;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  gap: 14px;
  align-items: baseline;
  padding: 14px 18px;
  pointer-events: none;
}

.mark { font-weight: 700; letter-spacing: 0.18em; text-decoration: none; }
.sub { opacity: 0.5; }
/* The header lets clicks fall through to the canvas (pointer-events: none), but links still have to be clickable */
.bar a { pointer-events: auto; color: inherit; }
.status { margin-left: auto; opacity: 0.6; }

/* One nav, the same screens on every page (the HTML carries it; the current one is a .here span, not a link).
   Every screen judges a different thing and they are useless if you cannot get from one to the next — before this,
   the grid linked to nothing at all and the other five each named a different subset */
.nav { display: flex; gap: 10px; pointer-events: auto; font-size: 10px; letter-spacing: 0.14em; }
.nav a { opacity: 0.45; text-decoration: none; }
.nav a:hover { opacity: 0.9; text-decoration: underline; }
.nav .here { opacity: 0.85; font-weight: 700; }

.deck {
  position: fixed;
  z-index: 10;
  left: 18px;
  bottom: 18px;
  width: 208px;
  display: grid;
  gap: 8px;
}

.card {
  border: 1px solid var(--line);
  /* If it were translucent the creature behind would show through and the buttons would look buried under the drawing */
  background: #f2ecdf;
  box-shadow: 0 2px 12px rgba(43, 39, 36, 0.12);
  padding: 8px 12px;
}

/* A folding card — one <details> holding several groups. The debug screen has nine cards' worth of controls and
   at 1×1 they covered half the creature; folded, the judging ones are one line until wanted. A .group is a card's
   contents without its frame (the fold is the frame) */
.fold > summary {
  cursor: pointer;
  list-style: none;
  font-size: 10px;
  letter-spacing: 0.18em;
  opacity: 0.55;
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::after { content: " ▾"; }
.fold:not([open]) > summary::after { content: " ▸"; }
.fold > summary:hover { opacity: 0.9; }
/* Folded, the summary says what is on — otherwise a screen pinned to BIND looks like a bug */
.foldNow { letter-spacing: 0.1em; opacity: 0.75; }
.fold[open] .foldNow { display: none; }
.foldBody { display: grid; gap: 8px; margin-top: 8px; }

/* The deck may run past the screen (debug at a small window) — it scrolls instead of hiding its last card */
.debug .deck { max-height: calc(100vh - 76px); overflow-y: auto; padding-right: 2px; }

.card h2, .group h2 {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 0 0 6px;
  font-size: 10px;
  letter-spacing: 0.22em;
  /* Dimmed via color, not opacity — what rides inside an h2 (a fold's note) has to stay at full ink */
  color: rgba(43, 39, 36, 0.55);
  font-weight: 600;
}

button {
  font: inherit;
  color: inherit;
  background: transparent;
  border: 1px solid var(--line);
  padding: 6px 8px;
  cursor: pointer;
}

button:hover { background: rgba(43, 39, 36, 0.06); }
button.on { background: var(--ink); color: var(--paper); border-color: var(--ink); }
button.wide { width: 100%; }

select {
  font: inherit;
  color: inherit;
  background: transparent;
  border: 1px solid var(--line);
  padding: 6px 8px;
  cursor: pointer;
}
select.wide { width: 100%; }

/* Two selects side by side (the gallery FIX), or a select with a button after it (the editor's SPECIES · NEW) */
.seg select { flex: 1; min-width: 0; }
.seg button.narrow { flex: 0 0 auto; padding: 6px 10px; }

.seg { display: flex; gap: 4px; }
.seg button { flex: 1; padding: 6px 4px; }

.hint { margin: 0; opacity: 0.45; }
.hint a, .sub a { color: inherit; }

/* Parts gallery — the canvas is pushed right of the deck and below the header so the cards never cover the drawing.
   Labels sit in the same box as the canvas and gallery.js places them by camera projection */
.gallery #stage,
.gallery .labels {
  left: 252px;
  top: 52px;
  width: calc(100% - 252px);
  height: calc(100% - 52px);
}
.gallery .deck { top: 52px; bottom: auto; }
.labels {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
.labels span {
  position: absolute;
  transform: translate(-50%, 0);
  font-size: 11px;
  letter-spacing: 0.14em;
  opacity: 0.6;
}

/* The stage — the canvas is the camera's handle: a drag goes round, so the cursor says grab, and a finger's drag is the
   camera's rather than the page's scroll (touch-action) */
.stage #stage { cursor: grab; touch-action: none; }
.stage.dragging #stage { cursor: grabbing; }

.noscript {
  position: fixed;
  inset: 0;
  display: grid;
  place-content: center;
}

/* Mobile — the deck leaves the left edge and becomes a bottom strip: cards run left to right and scroll
   sideways. Only the main and debug screens; gallery and audit assume a wide screen and keep their desktop
   decks. The canvas stays fullscreen behind it. */
@media (max-width: 700px) {
  body:not(.gallery):not(.audit) .deck {
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    display: flex;
    align-items: stretch;
    gap: 8px;
    padding: 10px 12px;
    overflow-x: auto;
  }
  /* Cards shrink-wrap their controls; the strip scrolls instead of squeezing them */
  body:not(.gallery):not(.audit) .card { flex: 0 0 auto; }
  .sub .tag { display: none; }   /* the tagline does not fit beside the title on a phone — the nav next to it stays */
  .nav { gap: 7px; letter-spacing: 0.06em; }
}


.how h1 { margin: 0 0 14px; font-size: 15px; letter-spacing: 0.24em; text-transform: uppercase; font-weight: 700; }
.how .foot p { margin: 0 0 4px; }

/* The medium page (how.html) — the only screen that scrolls: a document, not a board.
   Every figure is a card holding one canvas; the bar gets a paper back so the text passes under it */
body.how { height: auto; }
.how .bar { background: var(--paper); border-bottom: 1px solid var(--line); pointer-events: auto; }
.how .bar .seg { flex: 0 0 auto; }
.how .bar .seg button { flex: 0 0 auto; padding: 3px 9px; font-size: 10px; letter-spacing: 0.14em; }
.how .page { max-width: 880px; margin: 0 auto; padding: 68px 20px 48px; }
.how .lede, .how section > p { max-width: 660px; opacity: 0.8; }
.how h2 {
  margin: 44px 0 6px;
  font-size: 11px;
  letter-spacing: 0.24em;
  color: rgba(43, 39, 36, 0.55);
}
.how h3 {
  margin: 30px 0 4px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(43, 39, 36, 0.45);
}
/* The kinds' controls — the size the three lines draw at, and AGAIN. Buttons size to their label rather than
   stretching, so the row reads as a control strip and not as a segment spanning the page */
.how .kinds { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.how .kinds button { flex: 0 0 auto; padding: 4px 12px; letter-spacing: 0.16em; }

.how .figs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px 24px;
  margin-top: 14px;
}
.how figure {
  margin: 0;
  border: 1px solid var(--line);
  background: #f2ecdf;   /* the card back — the canvases are transparent and sit on it */
  padding: 12px 12px 10px;
  /* A grid item grows to its min-content — nine unbreakable hex labels would push the card past a phone
     screen. Let the card shrink and clip instead */
  min-width: 0;
  overflow: hidden;
}
.how figure.wide { grid-column: 1 / -1; }
/* how.js caps a figure at ~300 px per world unit (the board's own scale) and centres it in the card */
.how figure canvas { display: block; width: 100%; margin: 0 auto; }
.how .subs { display: flex; margin: 2px auto 0; }
.how .subs span { flex: 1; text-align: center; font-size: 10px; letter-spacing: 0.1em; opacity: 0.55; }
/* The goofy material's own name, over its row of balls — the density labels underneath say the step, not which material it is */
.how .matname {
  display: flex; align-items: baseline; gap: 8px;
  margin-bottom: 8px; font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
}
.how .matname .kind { font-size: 10px; letter-spacing: 0.08em; text-transform: none; opacity: 0.5; }
.how figcaption { margin-top: 8px; font-size: 11px; line-height: 1.6; opacity: 0.85; }
.how code { font: inherit; background: rgba(43, 39, 36, 0.08); padding: 0 3px; }
.how .foot { margin-top: 52px; opacity: 0.6; }
.how .note { font-size: 11px; line-height: 1.6; opacity: 0.75; max-width: 880px; }
.how a { color: inherit; }
@media (max-width: 700px) {
  .how .figs { grid-template-columns: 1fr; }
  .how .subs span { font-size: 8px; letter-spacing: 0; }   /* nine hex codes have to fit one phone row */
}

/* The anatomy — one card holding a row per habit: a label on the left, the line on the right (how.js) */
.how .anatomy {
  margin-top: 14px;
  border: 1px solid var(--line);
  background: #f2ecdf;
  padding: 6px 14px;
}
.how .anatomy figure {
  border: 0;
  background: none;
  padding: 10px 0;
  display: grid;
  grid-template-columns: 168px 1fr;
  align-items: center;
  gap: 14px;
}
.how .anatomy figure + figure { border-top: 1px solid rgba(43, 39, 36, 0.08); }
.how .anatomy .who b { font-weight: 600; letter-spacing: 0.06em; }
.how .anatomy .who span { display: block; margin-top: 2px; font-size: 10px; letter-spacing: 0.04em; opacity: 0.5; }
.how .anatomy .ln { min-width: 0; }
@media (max-width: 700px) {
  .how .anatomy figure { grid-template-columns: 1fr; gap: 4px; }
}

/* Face part audit — the deck is wide (it holds the report) and the canvas is pushed over by that much */
.audit .deck { width: 360px; }
.audit #stage { left: 0; width: 100%; }   /* Canvas takes the whole screen — individuals have to be big for the pixel check to be accurate. The deck floats above it */
.audit .report {
  margin: 0;
  max-height: 60vh;
  overflow: auto;
  white-space: pre-wrap;
  font-size: 11px;
  line-height: 1.5;
}

/* Pixel diff — the two boards are drawn off screen at one fixed size; the stage shows one of them (or the diff) fitted next to the deck */
.pixeldiff .offstage { position: fixed; left: -20000px; top: 0; width: 1050px; height: 750px; }
.pixeldiff #stage { left: 380px; top: 52px; width: calc(100% - 380px); height: calc(100% - 52px); object-fit: contain; }
.pixeldiff #boardSeg { margin-top: 4px; flex-wrap: wrap; }
.pixeldiff #boardSeg button { flex: 0 0 auto; min-width: 28px; }

/* The editor deck carries every slot, colour and proportion at once, so it is wider than the others and
   scrolls — the same treatment the debug deck already gets. */
/* One column that can never grow past the deck (minmax(0, 1fr)): a card's minimum width used to widen the track and push
   every card's right edge under the scrollbar. The cards fit the column; what does not fit a card wraps or clips inside it */
.editor .deck { width: 244px; max-height: calc(100vh - 76px); overflow-y: scroll; overflow-x: hidden; scrollbar-gutter: stable; padding-right: 2px; grid-template-columns: minmax(0, 1fr); }
/* The editor's cards keep less at the sides than the other decks' — 8px against 12: the deck is 244px with a scrollbar in it, and
   every row on it (the material previews, the palette, the part tabs) wants the width more than the card wants its margin */
.editor .card { padding: 8px 8px; }
/* The editor's one scrollbar, the deck's, is always there. The platform's overlay bar came and went with the hand, and a
   bar that appears is a thing that moved. A drawn bar (::-webkit-scrollbar) is never an overlay, so it stays; the gutter is
   reserved (overflow-y: scroll, scrollbar-gutter) so nothing shifts when a card grows past the fold either.
   No scrollbar-width / scrollbar-color here: in Chromium the standard properties win over ::-webkit-scrollbar and keep the
   bar an overlay, which is the bar that comes and goes */
.editor .deck::-webkit-scrollbar { width: 7px; }
.editor .deck::-webkit-scrollbar-track { background: transparent; }
.editor .deck::-webkit-scrollbar-thumb { background: rgba(43, 39, 36, 0.28); border-radius: 4px; border: 1px solid transparent; background-clip: padding-box; }
.editor .deck::-webkit-scrollbar-thumb:hover { background: rgba(43, 39, 36, 0.45); background-clip: padding-box; }
/* A folding card's title folds the way the debug deck's do (▾ open, ▸ shut); open, the title keeps the same 6px over its
   contents that a plain card's title has (h2 margin) */
.editor summary { cursor: pointer; list-style: none; }
.editor details[open] summary { margin-bottom: 6px; }
.editor summary::-webkit-details-marker { display: none; }
.editor summary h2 { display: inline; }
.editor summary::after { content: " ▾"; opacity: 0.45; }
.editor details:not([open]) summary::after { content: " ▸"; }
.fields { display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; }
.editor .wide + .fields { margin-top: 10px; }   /* a control, then the next label line — the .sect rhythm (HAND: NEW HAND, then WOBBLE) */   /* one column that never grows past the card: an auto track took the width of the widest row (a row of four buttons) and every row with it, and a slider ran out of the card */
/* The PART card, the way a browser holds its tabs: a darker strip across the top with the part tabs in one row (each
   part alone, thumbs.js), the open tab a lighter shape with rounded top corners that is one piece with the panel under it —
   no line between — and the others flat on the strip. The strip scrolls sideways by trackpad and by opening a part; like a
   browser's it shows no bar */
.partCard { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; align-items: start; }
.tabs { display: flex; flex-wrap: nowrap; gap: 1px; overflow-x: scroll; overflow-y: hidden; padding: 4px 4px 0; background: rgba(43, 39, 36, 0.08); border-radius: 5px 5px 0 0; }
.tabs::-webkit-scrollbar { display: none; }
.tab { flex: 0 0 40px; width: 40px; display: grid; justify-items: center; gap: 1px; padding: 4px 2px 4px; min-width: 0; border: 0; border-radius: 5px 5px 0 0; background: transparent; color: inherit; cursor: pointer; font-size: 8px; letter-spacing: 0.04em; line-height: 1.2; }
.tab span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; opacity: 0.6; }
.tab:hover { background: rgba(43, 39, 36, 0.06); }
/* the panel's ground — the tab and the panel read as one piece. `color: inherit` on purpose: the generic button.on rule
   turns text paper-coloured, which on a paper-coloured tab made the open tab's name vanish */
.tab.on { background: #f8f3e8; color: inherit; }
.tab.on span { opacity: 1; font-weight: 700; }
.tab canvas { display: block; max-width: 100%; height: auto; }   /* until thumbs.js paints it a canvas is 300×150 — kept inside its cell */
/* Under the strip, the open part's panel: SHAPE, the properties, then MATERIAL, each under a label line (.sect — the deck's one
   grammar). SHAPE and MATERIAL are a dropdown each: what the part has on (its picture, then its name — no caret: the line is
   plainly a line to click), and a click on the line opens the list of what it could have under it, every row the same way —
   picture, then name. The
   properties stand open. Three text tabs swapping one panel between them (SHAPE · MATERIAL · PROPERTY) were tried first: the
   thing wanted was always on the other tab, and a form and a material each read fine on one line */
.partPanel { background: #f8f3e8; border-radius: 0 0 5px 5px; padding: 2px 4px 6px; display: grid; gap: 4px; }
/* The dropdown — the line (.pick) and, opened, the list (.menu) floated over the deck (position: fixed, put there by editor.js
   `place`), the way a select's list is: in the flow of the card it pushed the properties down and pulled them back on every
   pick, and the card's length went up and down with it. Fixed, its box is the viewport's, so the deck's overflow does not cut
   it — as long as nothing over the deck takes a transform or a filter. Past six rows, or what the screen has room for, the
   list scrolls inside itself; .up is a list opened over its line, its shadow thrown the other way */
.drop { display: grid; gap: 2px; }
.pick, .opt { display: grid; align-items: center; gap: 0 8px; padding: 2px 8px 2px 2px; text-align: left; border: 1px solid transparent; background: transparent; color: inherit; cursor: pointer; font-size: 11px; letter-spacing: 0.04em; line-height: 1.3; }
.pick { grid-template-columns: 44px minmax(0, 1fr); border-color: var(--line); background: #f2ecdf; }
.opt { grid-template-columns: 44px minmax(0, 1fr); }
.pick:hover, .opt:hover { background: rgba(43, 39, 36, 0.06); }
.pick[aria-expanded="true"] { border-color: var(--ink); }
.pick .thumb, .opt .thumb { display: grid; place-items: center; width: 44px; height: 44px; }
.pick canvas, .opt canvas { display: block; max-width: 100%; max-height: 100%; }   /* a canvas not yet painted is 300×150 — kept inside its cell */
.pick .name, .opt .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu, .opts { display: grid; gap: 1px; }
.menu { position: fixed; z-index: 3; max-height: 312px; overflow-y: auto; padding: 2px; border: 1px solid var(--line); background: #f2ecdf; box-shadow: 0 6px 18px rgba(43, 39, 36, 0.18); scrollbar-width: thin; }
.menu.up { box-shadow: 0 -4px 18px rgba(43, 39, 36, 0.18); }
.opt .name { opacity: 0.75; }
.opt.on { border-color: var(--ink); background: #f8f3e8; color: inherit; }   /* color: inherit against button.on's paper-coloured text, as on .tab.on */
.opt.on .name { opacity: 1; font-weight: 700; }
/* A material's ball on its line or row — smaller than MATERIALS' previews, centred in the 44px cell the form pictures take */
.pick .ball.preview, .opt .ball.preview { width: 36px; height: 36px; }
/* + — a new material for the part, first row of what it may wear, its ball an empty ring the size of the others */
.opt.add .plus { display: grid; place-items: center; width: 36px; height: 36px; border-radius: 50%; border: 1px dashed var(--line); font-size: 18px; line-height: 1; opacity: 0.55; }
.opt.add:hover .plus { opacity: 1; border-style: solid; }
/* ✎ — the pen that opens the dialog on a material (EDIT MATERIAL): at the right end of every row of a MATERIAL list, and at
   the end of the MATERIAL line itself (the pen ends it). A line or a row is a
   button and cannot hold one, so the pen is its own button beside the row (the row's frame ending before it), and over the
   line's right end — absolute against .drop, whose position: relative makes no box for the fixed list (only a transform or
   a filter would); the line keeps room at its end for it. The row picks, the line opens, the pen edits */
.edit { padding: 0; border: 1px solid transparent; background: transparent; color: inherit; font-size: 14px; line-height: 1; opacity: 0.5; cursor: pointer; }
.edit:hover { opacity: 1; background: rgba(43, 39, 36, 0.06); }
.optRow { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px; align-items: stretch; }
.optRow .edit { width: 30px; }
.drop { position: relative; }
.drop.hasPen .pick { padding-right: 40px; }
.drop > .edit { position: absolute; right: 6px; top: 50%; margin-top: -14px; width: 28px; height: 28px; }
/* MATERIAL — a label line and a dropdown per surface; a part with more than one names each (MATERIAL · PUPIL, then · WHITE) */
.wear { display: grid; gap: 4px; }

/* The state dropdown (NORMAL · GHOST) sits under the species one in the same card */
#stateSel { margin-top: 4px; }
.field { display: grid; grid-template-columns: 74px 1fr; align-items: center; gap: 6px; }
.field > * { min-width: 0; }
.field > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; opacity: 0.7; }
.field input[type="range"] { margin: 0; box-sizing: border-box; }   /* the platform's range carries a margin of its own, and at width 100% it ran a pixel past the card */
.field select, .field input[type="range"] { min-width: 0; width: 100%; }
.field .readout { grid-column: 2; font-variant-numeric: tabular-nums; opacity: 0.5; }
.field.swatches { grid-template-columns: 74px 1fr; align-items: start; }
/* A bare row has no name column — the material panel's rows (editor.js field/fieldRow with label null) */
.field.bare, .field.swatches.bare { grid-template-columns: 1fr; }
.field.bare .readout { grid-column: 1; }
.strip { display: flex; flex-wrap: wrap; gap: 3px; }
/* Material balls (src/balls.js) — a 3D program's shader ball. The preview is colour, material and density
   together; a sample is one material in the creature's own colour and density, ringed when it is the one. */
.field canvas.preview { display: block; width: 72px; height: 72px; }
/* A material card — the ball and its name together, the card itself the button: MATERIALS' previews (the one being edited
   framed). The others sit bare on the panel */
/* PROPERTY under a part — a slider or a row of values per property; a part with none has no section.
   A property is a block, not a row: its name on a line of its own (the material panel's section type), a slider's reading
   at the right end of that line, and the control under it at the full width of the panel. In a 74px name column the
   values had 113px between them — three buttons overflowed by 7px, LONG · MEDIUM · SHORT · VERYLONG by 73 — and the
   grid, taking the widest row's width, ran the sliders out of the card with them */
.props { padding: 0; gap: 10px; }   /* label line → control 4px, control → the next label 10px: the .sect rhythm */
.partPanel .props { padding-top: 6px; }
.props .field { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name reading" "control control"; gap: 4px 8px; align-items: baseline; }
.props .field > span { grid-area: name; font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 600; color: rgba(43, 39, 36, 0.55); opacity: 1; }
.props .field .readout { grid-area: reading; grid-column: auto; font-size: 11px; letter-spacing: 0.04em; opacity: 0.8; text-align: right; }
.props .field .seg, .props .field select, .props .field input[type="range"], .props .field > button { grid-area: control; }
/* the values at the height of the deck's other segments (SPEC, VIEW's POSE), a size down to fit three or four in the width */
.props .field .seg button { min-width: 0; padding: 6px 2px; font-size: 11px; letter-spacing: 0.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Four values go two by two — four in a row is 46px a button, and VERYLONG is 47px of letters */
.props .field .seg[data-count="4"] { display: grid; grid-template-columns: 1fr 1fr; }
/* MATERIALS' previews run in one row and scroll sideways, the way the part tabs do — the card being edited is
   scrolled into view (editor.js) */
.previews .strip { flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; gap: 3px; padding: 0 1px 2px; scrollbar-width: none; }
.previews .strip::-webkit-scrollbar { display: none; }
/* Tight both ways: the ball and its name close together, little over, under and beside — the row is the tallest thing on the deck
   and it was mostly air (6px over the ball, 2px between ball and name, 4px under, 10px a side, 6px between cards) */
.previews .pv { flex: 0 0 auto; padding: 2px 3px 1px; gap: 0; }
.previews .pv .cap { line-height: 1.2; }
.pv { display: grid; justify-items: center; gap: 2px; padding: 6px 10px 4px; border: 1px solid transparent; background: transparent; color: inherit; cursor: pointer; }
.pv:hover { background: rgba(43, 39, 36, 0.06); }
.pv.on { border-color: var(--ink); background: #f8f3e8; color: inherit; box-shadow: 0 1px 6px rgba(43, 39, 36, 0.12); }
.ball.preview { width: 72px; height: 72px; border-radius: 50%; }
.ball.preview canvas { display: block; width: 100%; height: 100%; }
/* The material's name under its ball — main, then mat1, mat2 … the one being edited at full ink */
.cap { font-size: 10px; letter-spacing: 0.14em; opacity: 0.5; }
.pv.on .cap { opacity: 1; font-weight: 700; }
/* + beside a card's title — MATERIALS' new material. Small, round, at the end of the title line */
.card h2 .plus { margin-left: auto; width: 18px; height: 18px; padding: 0; border: 1px solid var(--line); border-radius: 50%; font-size: 13px; line-height: 1; letter-spacing: 0; color: var(--ink); opacity: 0.6; display: grid; place-items: center; }
.card h2 .plus:hover { opacity: 1; background: rgba(43, 39, 36, 0.06); }
/* A label line — the deck's one grammar, on every card: a name in small capitals, the current value at the right end of the
   same line, and the control under it at the full width. MATERIALS' sections, the SHAPE and MATERIAL lines under a part, the
   property rows and HAND (.props .field) all read this way. A value on a line of its own — three lines a section — was the
   MATERIALS card's alone, and made it the tallest thing on the deck */
.sect { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-top: 6px; font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 600; color: rgba(43, 39, 36, 0.55); }
.sect > span { flex: 0 0 auto; }
.sect .val { flex: 1 1 auto; min-width: 0; text-align: right; font-size: 11px; letter-spacing: 0.04em; font-weight: 400; text-transform: none; color: var(--ink); opacity: 0.8; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
/* NAME is typed over its line — the same line the other sections read their value on */
.sect input.val { font: inherit; font-size: 11px; letter-spacing: 0.04em; text-align: left; color: var(--ink); background: transparent; border: 0; border-bottom: 1px dashed var(--line); padding: 0; outline: none; opacity: 1; }
.sect input.val:focus { border-bottom-style: solid; border-bottom-color: var(--ink); }
.sect input.val::placeholder { color: var(--ink); opacity: 0.45; }
.balls .ball { display: block; width: 28px; height: 28px; padding: 0; border: 0; background: transparent; border-radius: 50%; position: relative; }
.balls .ball canvas { display: block; width: 100%; height: 100%; }   /* an unpainted canvas is 300×150 — inside its 28px button, never over the slider under it */
.balls .ball:hover { background: rgba(43, 39, 36, 0.06); }
.balls .ball.on { outline: 2px solid var(--ink); outline-offset: 1px; }
/* NEW MATERIAL · EDIT MATERIAL — the panel + and a ✎ under a part open (editor.js openDraft): one more card, floated over the
   page — a plain dialog, not a modal: no backdrop (a dim over the page hid the very colour just picked) and the page under
   it alive. It stands beside the deck, at the height of the MATERIAL line it was opened from (editor.js placeDraft sets its
   place — left and top, the margin cleared), the deck's width, above the deck. The ball and the material's three in
   MATERIALS' own rows (.pv, .sect, .balls, .swatches); a pick is the edit, and there are no buttons */
.modal { position: fixed; z-index: 20; width: 244px; max-width: calc(100vw - 32px); color: var(--ink); }
.modal .draft { justify-self: center; cursor: default; }
.modal .draft:hover { background: #f8f3e8; }
/* A swatch is the colour itself, so the only state it can show is a ring around it. */
.swatch { width: 16px; height: 16px; padding: 0; border: 1px solid var(--line); line-height: 1; }
.swatch.on { outline: 2px solid var(--ink); outline-offset: 1px; }
.notes { margin: 6px 0 0; padding-left: 16px; display: grid; gap: 3px; }
.notes.empty::before { content: "the board would draw this one"; opacity: 0.45; margin-left: -16px; }
.notes li { opacity: 0.8; }

/* The buttons that stand at a clicked creature's feet — REDRAW · BACK · OPEN · SAVE. Positioned by projecting
   its cell onto the screen each frame, the way the gallery places its labels, so they follow the board through
   a resize. */
.pin { position: fixed; z-index: 2; display: flex; align-items: center; gap: 4px; transform: translate(-50%, 0); }
/* On the same paper as the cards so they do not sink into the drawing behind them. */
.pin button { display: grid; place-items: center; width: 26px; height: 26px; padding: 0; background: #f2ecdf; }
/* The glyph takes the button's ink, so it reads as drawn with the same pen as the board's chrome. */
.pin svg { width: 16px; height: 16px; fill: currentColor; }
.pin button:disabled { opacity: 0.35; cursor: default; }
/* A row that follows a wide button inside one card (BOARD: NEW BOARD, then SAVE · OPEN). */
.mt { margin-top: 4px; }

/* The type the name screen's card is written in (src/medium/type.js) — Noto Sans and Noto Sans KR, served from fonts/ and named
   for this page, so a copy installed on the machine never stands in. The card traces them off a canvas; nothing is set in them */
@font-face { font-family: "Menagerie Sans"; src: url("./fonts/noto-sans-500.woff2") format("woff2"); font-weight: 500; font-display: block; }
@font-face { font-family: "Menagerie Sans"; src: url("./fonts/noto-sans-700.woff2") format("woff2"); font-weight: 700; font-display: block; }
@font-face { font-family: "Menagerie Sans KR"; src: url("./fonts/noto-sans-kr-500.woff2") format("woff2"); font-weight: 500; font-display: block; }
@font-face { font-family: "Menagerie Sans KR"; src: url("./fonts/noto-sans-kr-700.woff2") format("woff2"); font-weight: 700; font-display: block; }

/* The name screen — the front door (index.html, guidelines/name.md): one row of controls and the card under it, nothing else.
   The card is sized from the height the screen leaves it, and its width follows at the trading card's 5:7 — never wider than the
   screen. Its one canvas is the scene, the words drawn into it */
body.front main { position: fixed; inset: 0; display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 18px; }
.front .controls { display: flex; gap: 6px; width: min(100%, 560px); }
.front .controls input { flex: 1 1 auto; min-width: 0; font: inherit; font-size: 14px; color: inherit; background: #f2ecdf; border: 1px solid var(--line); padding: 6px 10px; border-radius: 0; }
.front .controls input::placeholder { color: var(--ink); opacity: 0.4; letter-spacing: 0.14em; }
.front .controls input:focus { outline: none; border-color: var(--ink); }
.front .controls select, .front .controls button { flex: 0 0 auto; letter-spacing: 0.14em; background: #f2ecdf; }
.front .controls #draw { background: var(--ink); color: var(--paper); border-color: var(--ink); font-weight: 700; }
.namecard { position: relative; margin: 0; aspect-ratio: 5 / 7; height: min(calc(100vh - 86px), calc((100vw - 36px) * 1.4)); box-shadow: 0 3px 18px rgba(43, 39, 36, 0.2); }
@supports (height: 100dvh) { .namecard { height: min(calc(100dvh - 86px), calc((100vw - 36px) * 1.4)); } }
.namecard canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* Read out, not shown — the card's words for a screen reader (the canvas holds the ones you see) */
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* A phone: the field takes the first row and the rest share the second */
@media (max-width: 520px) {
  .front .controls { flex-wrap: wrap; }
  .front .controls input { flex-basis: 100%; }
  .front .controls select, .front .controls button { flex: 1 1 0; }
  .namecard { height: min(calc(100vh - 124px), calc((100vw - 36px) * 1.4)); }
  @supports (height: 100dvh) { .namecard { height: min(calc(100dvh - 124px), calc((100vw - 36px) * 1.4)); } }
}
