:root{
  /* THE MENU SURFACE — taken from #editpanel, which is the look the owner picked. Translucent grey + blur so it belongs to the
     scene rather than punching a black hole in it. Every popover, menu and bar reads these; none defines its own. */
  --surface:rgba(240,239,237,.90);
  --surface-solid:#f0efed;   /* when a popover sits over busy content and needs to stay readable */
  --surface-blur: blur(12px);
  --surface-radius: 16px;
  --surface-shadow:0 1px 2px rgba(30,30,28,.10), 0 14px 34px rgba(30,30,28,.18);
  /* ══ COLOUR — DECLARED ONCE, FOR THE WHOLE APP (Law 0a, 2026-08-23) ═══════════════════════════════════════════════
     Same reason as type, and the same fault: these were declared in THREE places — gallery.css, poses.html's inline
     <style>, and ds.css — and they had already drifted. ds.css still carried `--accent:#2dd4bf`, a teal, when
     DESIGN.md §3 says there is one near-white accent and NO teal in chrome. Whichever file loaded last won.
     Values are DESIGN.md §3. This file does not get to invent one.

     Being in one block is also what makes a THEME possible: a theme redefines these names and nothing else.

     ── THERE ARE ONLY TWO INKS FOR TEXT (owner, 2026-08-25: "Ive said this so many times. We need uniformity… can you
     make the mood board font colors the same. The design.md needs to note that all font colors should be consistent.")
       --ink    TITLES only — a pose name, a dialog heading.
       --ink-2  EVERYTHING ELSE YOU READ. Descriptions, panel rows, panel headings, notes, tags, credits, labels.
       --ink-3  PLACEHOLDERS ONLY — text that is not there yet. It is the ABSENCE of text, not a rank of it.
     MEASURED on the boards panel, which is what he was pointing at: the row and the description were both --ink-2 and
     agreed, while the HEADING and the empty note were --ink-3 and did not. Three inks meant every new element was a
     small judgement call, and the calls drifted. Hierarchy is carried by SIZE and WEIGHT — DESIGN.md already says so
     for the pose panel; this makes it true everywhere. */
  --ink:#22262b;  --ink-2:#5f6873;  --ink-3:#8c95a0;
  --accent:#22262b;  --accent-ink:#f4f3f1;  --accent-soft:rgba(0,0,0,.06);
  --hair:rgba(0,0,0,.10);  --hair-2:rgba(0,0,0,.20);
  --danger:#b4402f;
  /* 18% grey AS LIT — what the cyclorama's 18% read as under the studio key. Behind the fixed room layer; seen on
     mobile rubber-band overscroll. NOTE: the cyclorama and the key light were deleted on 2026-08-23, so this number
     no longer describes anything that exists — it is inherited, not chosen. That is why a lighter ground is on the
     table at all. */
  --bg:#f0efed;
  --page:#f0efed;      /* what html/body paint behind everything */
  /* ══ TYPE — DECLARED ONCE, FOR THE WHOLE APP (Law 0a, 2026-08-11) ═══════════════════════════════════════════════════════
     WHY HERE. ui.css is the only stylesheet EVERY page loads, and it loads first — poses.html, spike3d.html and
     gallery-legacy.html all pull it before anything else. gallery.css does not reach /spike3d, so a scale declared there
     would silently be missing on the live page. This is the only file that can hold one declaration for everything.

     WHAT WAS WRONG. The families were declared FOUR times (gallery.css, poses.html, spike3d.html, ds.css) and the scale was
     not really declared at all: 66 sizes in 14 distinct values were hand-typed across five files. `--font` was tokenised, so
     the FAMILY stayed consistent while the SCALE drifted — which is exactly why nobody could see the drift. Two selectors
     (.cname, .cbadge) were even declared twice at different sizes, the later one winning silently.

     THE RULE. Adding a size means adding a row HERE. If a size is not in this block, it is not in the app.
     Values are DESIGN.md §4 — this file does not get to invent one. */
  --font:'Inter',system-ui,-apple-system,sans-serif;
  --font-display:'Space Grotesk','Inter',system-ui,sans-serif;
  --t-brand:800 17px/1 var(--font-display);
  --t-live:   700 23px/1.15 var(--font-display);   /* Live's instruction line — read at three metres, see DESIGN.md */
  /* ⚠ THE SIZE WAS MISSING AND THE WHOLE TOKEN WAS DEAD. This read `/1.1 var(--font-display)` — no weight, no size —
     and a `font:` shorthand without a size is INVALID, so every declaration using it was thrown away entirely and the
     element fell back to whatever it inherited. That is why a pose name rendered at 13px body text instead of 19px
     display type, everywhere in the app, and why nobody could see a bug: it did not look broken, it looked plain.
     Found on 2026-08-29 while matching the mood board panel to the pose panel — the board's name and its description
     came out identical, which they could not be if the token worked.
     The value is DESIGN.md §4's, not a new one: "--t-title: 700 19px/1.1 var(--font-display)", noted there as the
     pose name and modal h3. (Quoted WITHOUT its trailing comment on purpose: pasting that in closed this comment
     early and killed the declaration a second time, which is the same class of invisible break as the missing size.)
     The block above says it in as many words — "Values are DESIGN.md §4 — this file does not get to invent one" — so
     this is the file being put back in agreement with the document, not a size being chosen. */
  --t-title:700 19px/1.1 var(--font-display);
  --t-sub:700 14px/1.2 var(--font);
  --t-body:500 13px/1.4 var(--font);
  --t-label:600 13px/1 var(--font);
  --t-card:600 12px/1.25 var(--font);    /* card / list name — DESIGN.md §4 "name 600 12px" */
  --t-meta:500 11px/1.3 var(--font);
  --t-item:500 11px/1.2 var(--font);     /* SMALL CAPTIONS ONLY — slider labels, chips, readouts. NOT a menu item, see below. */
  /* ── A MENU ITEM IS 13px WHEREVER IT IS, AND ITS HEADER IS 13px TOO (owner, 2026-08-11: "the fonts sizes are smaller than
     any other menu"). Dropdown items were --t-label (13px) while PANEL items were --t-item (11px), so the panel read as a
     smaller, lesser menu than the cascade two icons away. One role, one size: anything you click in a menu is --t-label.
     The header moved up with it, because the owner's older rule still binds — a child is never larger than its own header —
     so the heading is 13px as well and earns its rank from CASE and TRACKING, exactly as it always did. */
  --t-head:700 13px/1 var(--font);       /* menu / panel group heading — uppercase + var(--t-track) at the call site */
  --t-badge:700 10px/1 var(--font);      /* badge + menu label. Was --t-menu here and 700 10px typed in gallery.css: one value, one name. */
  --t-track: .09em;                      /* the uppercase tracking that goes with --t-head and --t-badge */

  /* ── GLYPH SIZES ARE NOT TYPE, AND ARE DELIBERATELY OFF THIS SCALE.
     A "×", a "+", a nav arrow, a count bubble and an avatar initial are drawn to fit a BOX — 34px in an 88px disc, 8px in a
     16px chip. That size is geometry set by the control, not a reading size, and forcing them onto the scale would break the
     shape while making nothing more consistent. They stay as literals at their call sites BY RULE, not by oversight.
     `#gsearch` at 16px is the same idea for a different reason: under 16px iOS zooms the page on focus, so it is a browser
     threshold, not a typographic choice. Never "tidy" it down to 13. */
}

/* ══ ONE PALETTE — PAPER. THE DARK THEME IS DELETED (owner, 2026-08-25: "u can delete the dark theme so it doesnt
   pollute the app, as i will not use it") ═══════════════════════════════════════════════════════════════════════

   The values above ARE this theme; there is no switch and no second set to keep in step. Kept here is only the
   reasoning, because the numbers are measured and someone will want to know why they are what they are.

   WHY A LIGHT GROUND AT ALL. Owner, 2026-08-23: "im starting to think the old gray background isnt working now the
   cyclorama is gone." He was right about the reason — --bg was a PHOTOGRAPHIC value, 18% grey AS LIT by a studio
   key. That rig was deleted, so the number described nothing. A gallery of photographs on a light wall is also what
   every mood-board tool lands on, for the same reason a real gallery is painted white: the pictures supply colour.

   PAPER, NOT CREAM — AND THE WARMTH IS A MEASURED QUANTITY, NOT A TASTE. He asked for the off-white Cosmos uses,
   saying "the sandy bg i have looks peach to me". Two measurements settled it and both surprised us:
   · Cosmos is NOT off-white. Its own token reads background-color-primary: lab(100% 0 0) — pure #ffffff, chroma
     0.01, dead-neutral grey ramp. What looks warm on that site is the photographs, not the page.
   · Ours was not peach either. #f3efe7 measured L 94.55, a -0.03, b +4.33 — ZERO red, all yellow. He read the
     symptom exactly right and the cause was the opposite axis to the one it looked like.
   So lightness is unchanged and the warmth is cut to a quarter: b +4.33 -> +1.1. Enough to read as paper rather
   than as a screen; not enough to read as a colour of its own.

   THE PANEL IS EXACTLY THE GROUND. It was a step lighter once and he came straight back: "the panels and background
   are still slightly different colors." A near-match is worse than a match or a contrast — it reads as a printing
   error. One paper, and the SHADOW alone separates: "their shadows will act like soft borders to make them
   distinguishable." So the shadow carries it — two layers, a tight contact edge and a wide ambient one, the same
   shape the cards use, neutralised so a warm brown shadow does not put back the cast the ground just lost.

   TWO VALUES ARE NOT A MIRROR OF THE OLD DARK SET, so nobody "tidies" them back:
   · --surface is a warm near-white at HIGHER opacity. A translucent dark panel reads as glass over a bright
     photograph; a translucent light one at the same alpha turns to milk. It needs more body, not less.
   · --surface-shadow is much softer. Dark used .42 black because a shadow has to be seen against near-black; the
     same value on paper reads as dirt and makes every card look pasted on.
   The skeleton green is untouched: it is drawn on the photograph, never on the app's ground. */

/* PoseDirect shared UI templates — the single source of truth for cascades (dropdowns), toggles and chips.
   Monochrome glass, no button borders, off-white accent. Consumes the page's design tokens (var(--font)/--ink/…). */

/* ── Cascade dropdown — one template for Sort, Filter, and any select-like control ── */
.pdd{position:relative;display:inline-block}
/* header buttons are TEXT-ONLY (match the Gallery/Live nav) — no pill, no border */
.pdd-btn{display:inline-flex;align-items:center;gap:6px;background:transparent;border:0;
  color:var(--ink-2,#aab2bd);font:var(--t-badge);letter-spacing:var(--t-track);text-transform:uppercase;padding:8px 9px;cursor:pointer;white-space:nowrap;
  text-shadow:0 1px 6px rgba(0,0,0,.45)}
.pdd-btn:hover{color:var(--accent,#eaedf1)}
.pdd.open .pdd-btn{color:var(--accent,#eaedf1)}
.pdd-btn .car{opacity:.6}
/* icon-only trigger — same 30px footprint as the account avatar so Filter/Sort/Account sit as one cluster */
.pdd-btn.icon{width:30px;height:30px;padding:0;justify-content:center;position:relative;color:var(--ink,#eceff3)}
.pdd-btn.icon svg{width:19px;height:19px;display:block}
.pdd-btn.icon:hover,.pdd.open .pdd-btn.icon{color:var(--accent,#eaedf1)}
.pdd-btn.icon .pdd-cnt{position:absolute;top:-3px;right:-4px;min-width:14px;height:14px;line-height:14px;padding:0 3px;font-size:9px;
  background:var(--accent,#eaedf1);color:var(--accent-ink,#14171c);border-radius:999px;text-align:center}
.pdd-btn .car{width:9px;height:6px;opacity:.65;transition:transform .15s}
.pdd.open .pdd-btn .car{transform:rotate(180deg)}
.pdd-cnt{background:var(--accent);color:var(--accent-ink);border-radius:999px;font:var(--t-badge);padding:2px 6px;min-width:15px;text-align:center}
.pdd-pop{position:absolute;top:calc(100% + 6px);min-width:190px;max-height:min(60vh,360px);overflow-y:auto;z-index:60;
  background:var(--surface-solid);border:1px solid var(--hair,rgba(255,255,255,.12));border-radius:var(--surface-radius);padding:6px;
  box-shadow:var(--surface-shadow);-webkit-backdrop-filter:var(--surface-blur);backdrop-filter:var(--surface-blur);
  scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}   /* thin, blended scrollbar (Firefox) */
.pdd-pop::-webkit-scrollbar{width:8px}
.pdd-pop::-webkit-scrollbar-track{background:transparent}
.pdd-pop::-webkit-scrollbar-thumb{background:rgba(0,0,0,.18);border-radius:8px;border:2px solid transparent;background-clip:padding-box}
.pdd-pop::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.30);border:2px solid transparent;background-clip:padding-box}
.pdd-pop.r{right:0} .pdd-pop.l{left:0}
.pdd-opt{display:flex;align-items:center;gap:10px;width:100%;background:transparent;border:0;color:var(--ink,#eceff3);
  font:var(--t-label);text-align:left;padding:9px 10px;border-radius:8px;cursor:pointer}
.pdd-opt:hover{background:rgba(0,0,0,.06)}
.pdd-opt .bx{flex:none;width:15px;height:15px;border-radius:4px;background:rgba(0,0,0,.10);position:relative;transition:background .12s}
.pdd-opt.sel .bx{background:var(--accent)}
.pdd-opt.sel .bx::after{content:'';position:absolute;left:5px;top:2px;width:4px;height:8px;border:solid var(--accent-ink);border-width:0 2px 2px 0;transform:rotate(45deg)}
.pdd-opt .lbl{flex:1;white-space:nowrap;text-transform:capitalize}
.pdd-opt.radio .bx{border-radius:50%}
/* ⚠ --accent-ink, NOT A HARD-TYPED #14171c. The dot sits INSIDE a .bx filled with --accent (#22262b), so a
   near-black dot on a near-black circle was invisible — the checkbox tick one line up has always used
   --accent-ink for exactly this reason. Found on 2026-09-01 when the role picker became the first radio dropdown
   in the app; DESIGN.md records this whole class of fault, hard-typed colours left behind when the palette moved. */
.pdd-opt.radio.sel .bx::after{left:4px;top:4px;width:5px;height:5px;border:0;border-radius:50%;background:var(--accent-ink);transform:none}

/* ── Menu-bar header + cascading action list (Pose ▾ / Body ▾ / Style ▾) ── */
.pdmenu .pdd-pop{min-width:210px}
.pdmenu-item{display:flex;align-items:center;justify-content:space-between;gap:14px;width:100%;background:transparent;border:0;
  color:var(--ink,#eceff3);font:var(--t-label);text-align:left;padding:10px 12px;border-radius:8px;cursor:pointer;white-space:nowrap}
.pdmenu-item:hover{color:#fff}   /* text brighten only — no gray pill (kept rows uniform width, active never looks larger) */
.pdmenu-item.sub{padding-left:24px;color:var(--ink-2,#9aa4b0);font-weight:500}
.pdmenu-item.sub:hover{color:#fff}
.pdmenu-item.danger{color:var(--danger,#ff8a7a)}
.pdmenu-sep{height:1px;background:var(--hair,rgba(255,255,255,.12));margin:5px 6px}

/* ── Toggle switch — one template. Left = off, right = on. Never a text label. ── */
.pd-switch{flex:none;width:34px;height:20px;border-radius:999px;background:rgba(0,0,0,.13);position:relative;
  transition:background .15s;cursor:pointer;border:0;padding:0}
.pd-switch::after{content:'';position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--surface-solid);transition:transform .15s}
.pd-switch.on{background:var(--accent)} .pd-switch.on::after{transform:translateX(14px);background:var(--accent-ink)}

/* ── Dialog — the in-app replacement for native confirm()/prompt()/alert() ── */
/* THE DIALOG WEARS THE THEME (owner, 2026-08-24: "Naming the board uses a dark font which i cannot see").
   It was `background:#12161c` on the card and `#0d1116` on the input — two hard-typed near-blacks — while the text
   read `var(--ink)`. The moment paper made --ink charcoal, this became CHARCOAL ON NEAR-BLACK: a text field you
   type into blind. Same fault as the Sure button and the Live controls, third time this session, and always the
   same cause — a colour typed at the call site instead of taken from the one declaration.
   The card is `--surface-solid` (a dialog must stay opaque over busy content) and the field is `--accent-soft`, a
   wash that INVERTS with the theme instead of staying dark. */
.pd-modal-ov{position:fixed;inset:0;z-index:120;display:flex;align-items:center;justify-content:center;
  background:rgba(6,8,11,.42);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
.pd-modal{width:min(92vw,380px);background:var(--surface-solid);border:1px solid var(--hair);border-radius:18px;
  padding:22px;box-shadow:var(--surface-shadow)}
.pd-modal h3{margin:0 0 6px;color:var(--ink,#eceff3);font:var(--t-title)}
/* READABLE, and the change is COLOUR not type (owner, 2026-08-15: "the fonts need to be readable"). The dialog message
   was ALREADY on the same token as the run's own on-screen lines — both `--t-body`, 500 13px/1.4 Inter — so nothing about
   the font was wrong. What differed was ink: the run's line sits near-white and this sat at `--ink-2`, the SECONDARY grey,
   which is for supporting text beside something primary. A dialog's message is not supporting text; it is the question.
   `--ink` is the declared primary and is what the heading directly above it already uses. Same family, same size, one
   value changed, and it is a value DESIGN.md already carries. App-wide on purpose: type and ink follow the ROLE, never
   the panel a control happens to be sitting in. */
.pd-modal p{margin:0;color:var(--ink,#eceff3);font:var(--t-body);white-space:pre-line}
.pd-modal-input{width:100%;box-sizing:border-box;margin-top:14px;background:var(--accent-soft);border:1px solid var(--hair);
  border-radius:10px;color:var(--ink);padding:11px 12px;font:500 14px/1.4 var(--font,sans-serif);resize:vertical}
.pd-modal-input::placeholder{color:var(--ink-3)}
.pd-modal-input:focus{outline:none;border-color:var(--hair-2)}
.pd-modal-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:18px}
.pd-modal-actions.stack{flex-direction:column}
.pd-mbtn{border:0;border-radius:10px;padding:11px 18px;font:var(--t-label);cursor:pointer}
/* THE DECLARED BUTTONS READ THE DECLARED COLOURS (owner, 2026-08-23: "the buttons are not updated for the paper
   theme… they need to adhere to the design.md"). All three typed the DARK theme's palette by hand - `#eaedf1` IS
   --accent's dark value, `#14171c` IS --accent-ink's - so a theme could repaint every token and the app's primary
   button would not move. Read is the loudest control on the New Pose brick and it was coming out a pale grey pill
   on sand. Same fault as the ground and the scrims: a colour typed at a call site instead of derived.
   Hover is `filter:brightness`, which the danger button already used, because it works in BOTH directions - it
   takes the dark theme's near-white to white and the paper theme's charcoal a step lighter, off one rule.
   Danger stays "colour alone on an otherwise identical control" (DESIGN.md 3): primary, with --danger swapped in
   for --accent and the same --accent-ink on top. */
.pd-mbtn.primary{background:var(--accent);color:var(--accent-ink)} .pd-mbtn.primary:hover{filter:brightness(1.12)}
.pd-mbtn.ghost{background:var(--accent-soft);color:var(--ink)} .pd-mbtn.ghost:hover{background:var(--hair-2)}
.pd-mbtn.danger{background:var(--danger);color:var(--accent-ink)} .pd-mbtn.danger:hover{filter:brightness(1.08)}

/* ── Active-filter chips (faceted search) — removable, shown in a slim bar ── */
.pd-chips{display:flex;gap:7px;flex-wrap:wrap;align-items:center}
/* ── A TAG PILL WEARS THE PANEL'S SURFACE (owner, 2026-08-11: "the tag pills might be better served having a background color
   the same as the filter panel for uniformity"). It was rgba(255,255,255,.08) — a lightening wash invented here, which sat
   differently on the cyclorama than every panel in the app did. --surface IS the filter panel's background (and the edit
   panel's, and every popover's), so this is not "the same colour as" the panel: it is the same declaration. The blur comes
   with it, because a translucent surface without it reads as a flat film rather than glass. */
.pd-chip{display:inline-flex;align-items:center;gap:6px;color:var(--ink,#eceff3);
  background:var(--surface,rgba(20,24,30,.62));
  -webkit-backdrop-filter:var(--surface-blur,blur(12px));backdrop-filter:var(--surface-blur,blur(12px));
  border-radius:999px;padding:5px 6px 5px 11px;font:var(--t-card);text-transform:capitalize}
.pd-chip button{background:none;border:0;color:var(--ink-2,#9aa4b0);cursor:pointer;font-size:14px;line-height:1;padding:0 2px}
.pd-chip button:hover{color:#fff}
