/* Cataterra — storyboard wireframes, BLUEPRINT direction.
   Committed direction propagated from docs/ui-ux/prototype-blueprint.html:
   engineering-drawing identity, desaturated slate-teal, flat hairlines, no page-
   wide grid. Type: IBM Plex Sans for labels/headers/UI, IBM Plex Mono for numeric
   data only. Comfortable type scale + chat rail styling carried over from the
   prototype. See docs/ui-ux/decisions.md + design-direction.md. */

:root{
  --bg:#121a20; --panel:#19232b; --panel-2:#212d36; --sink:#0e151a;
  --line:#2e3e48; --line-2:#22303a; --rule:#3b4f5b;
  --text:#eef3f6; --muted:#b8c7d0; --faint:#90a1ab;
  --accent:#5fb0c2; --accent-ink:#06141a; --accent-2:#d6e2e9;
  --warn:#e0b252; --bad:#e58a86; --good:#5cbf94;
  --radius:0.125rem;
  /* Chat rail geometry. The reserved gutter is derived from --dock-w, so a
     change to the rail's width can never silently reopen the overlap (B44).
     --shell-top is whatever sits above .layout, and the rail hangs from it. */
  --dock-w:18.125rem;
  --shell-top:3.375rem;
  --ui:"IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,"Cascadia Code",Consolas,monospace;
}

* { box-sizing: border-box; }
/* Baked UI scale (~110%) via a rem base — 1rem = 17.6px, so every rem length
   renders at the prototype's size. Replaces the old body{zoom:1.1}, which
   rescaled the coordinate space and broke viewport-measuring JS. Wide-screen
   bumps + the mobile reset live at the media queries below. See decisions.md /
   ui-ux/zoom-retirement-plan.md. */
html { font-size: 110%; }
html, body { margin: 0; }
body {
  font-family: var(--ui);
  color: var(--text); background: var(--bg); line-height: 1.5; font-size: 0.9375rem;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
:where(a, button, input, select, textarea, summary):focus-visible {
  outline:0.125rem solid var(--accent); outline-offset:0.125rem;
}
.mono, code, .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
h1,h2,h3,h4 { font-family: var(--ui); font-weight: 650; margin: 0; }


/* top status bar */
.topbar { display: flex; align-items: center; gap: 1rem; background: var(--panel); border-bottom: 0.0625rem solid var(--rule); padding: 0.5625rem 1rem; }
.brand { font-weight: 700; font-size: 1.0625rem; color: var(--text); letter-spacing: .02em; }
.brand .logo { display:inline-block; width: 0.875rem; height: 0.875rem; background: var(--accent); vertical-align:-0.125rem; margin-right:0.5rem; }
.chips { display: flex; gap: 0.5rem; margin-left: auto; font-variant-numeric: tabular-nums; font-size: 0.8125rem; align-items:center; }
.chip { border: 0.0625rem solid var(--line); border-radius: var(--radius); padding: 0.125rem 0.5rem; background: var(--sink); color: var(--muted); }
/* only the resource readouts are mono; the navigation chips are words */
.chip.energy, .chip.health, .chip.gold, .chip.marks { font-family: var(--mono); }
.chip b { color: var(--text); }
.chip.acct { border:0; background:none; padding-left: 0.625rem; border-left:0.0625rem solid var(--line); }
/* energy = the always-visible core resource meter; emphasised */
.chip.energy, .chip.health { border-color:var(--rule); background:var(--sink); font-weight:600; display:flex; align-items:center; gap:0.375rem; padding:0.125rem 0.625rem; color:var(--text); }
.chip.energy .ebar, .chip.health .ebar { width:3.375rem; height:0.5rem; background:var(--bg); border:0.0625rem solid var(--line); border-radius:0.25rem; overflow:hidden; }
.chip.energy .ebar > span, .chip.health .ebar > span { display:block; height:100%; background:var(--accent); }
.chip.health .ebar > span { background:var(--good); }
/* marks = premium currency (◈) — accent-tinted, links to the premium shop */
.chip.marks { border-color:var(--accent); color:var(--accent); font-weight:600; }

/* layout */
.layout { display: flex; align-items: stretch; min-height: calc(100vh - 5.75rem); }
.skip-link { position:absolute; left:-100vw; top:0; z-index:200; padding:0.5rem 0.75rem; background:var(--panel-2); color:var(--text); border:0.0625rem solid var(--accent); border-radius:var(--radius); }
.skip-link:focus { left:0.5rem; top:0.5rem; }
.main:focus { outline:none; }
.sidebar { width: 13.125rem; flex: none; background: var(--panel); border-right: 0.0625rem solid var(--rule); padding: 0.75rem 0; }
.navgroup { margin-bottom: 0.75rem; }
.navgroup .ng-label { margin: 0 0 0.1875rem; padding: 0 0.875rem; font-size: 0.6875rem; letter-spacing: .14em; text-transform: uppercase; color: var(--faint); font-family: var(--ui); font-weight: 600; }
.sidebar a { display: block; padding: 0.25rem 0.875rem; color: var(--text); font-size: 0.875rem; }
.sidebar a:hover { background: var(--panel-2); text-decoration: none; }
.sidebar a.active { background: var(--panel-2); font-weight: 600; box-shadow: inset 0.1875rem 0 0 var(--accent); }

/* Cap the reading column so the layout doesn't sprawl on wide / 4K panels —
   without a max-width the flex:1 main grows to ~2000px and every row stretches. */
/* Scope to the <main> element — a bare `.main` also matches `.swatch.main`
   (and anything else using a `main` modifier), wrongly giving it flex:1. */
main.main { flex: 1; min-width: 0; max-width: 80rem; padding: 1.125rem 1.375rem; }
.crumbs { font-family: var(--ui); font-size: 0.75rem; color: var(--faint); margin-bottom: 0.375rem; }
.page-title { margin: 0 0 0.25rem; font-size: 1.5rem; }
.page-sub { margin: 0 0 1rem; color:var(--muted); font-size: 0.875rem; }

/* boxes + placeholders */
.box { border: 0.0625rem solid var(--line); background: var(--panel); border-radius: var(--radius); padding: 0.75rem 0.875rem; margin-bottom: 0.875rem; }
.box.outcome-good { border-left: 0.1875rem solid var(--good); }
.box.outcome-bad { border-left: 0.1875rem solid var(--bad); }
.box > :is(h2, h3) { margin: 0 0 0.625rem; font-size: 0.75rem; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); font-family: var(--ui); font-weight: 600; }
/* A box heading that carries a right-aligned secondary action (e.g. "change →"). */
.box > :is(h2, h3).with-action { display: flex; align-items: baseline; gap: 0.5rem; }
.box > :is(h2, h3).with-action a { margin-left: auto; font-weight: 400; }
.mobile-disclosure > summary { list-style:none; }
.mobile-disclosure > summary::-webkit-details-marker { display:none; }
.disclosure-summary {
  display:flex; align-items:center; gap:0.625rem; min-height:1.5rem; margin:0 0 0.625rem;
  color:var(--muted); cursor:pointer; font-size:0.8125rem; font-weight:600;
}
.disclosure-summary .summary-meta {
  margin-left:auto; color:var(--faint); font-family:var(--mono); font-size:0.6875rem;
  font-weight:400; letter-spacing:.02em;
}
/* Some of these metas carry words rather than a figure ("change region",
   "personalised plan"). The count line's typeface is for numbers, so a prose
   one opts back out to Sans without the copy changing. */
.disclosure-summary .summary-meta--words { font-family:var(--ui); letter-spacing:normal; }
.disclosure-summary::after { content:"\2212"; color:var(--accent); font-family:var(--mono); }
.mobile-disclosure:not([open]) > .disclosure-summary { margin-bottom:0; }
.mobile-disclosure:not([open]) > .disclosure-summary::after { content:"+"; }
.mobile-intro > summary, .place-summary { display:none; }
.ph { border: 0.0625rem dashed var(--rule); background: var(--sink); border-radius: var(--radius); display: flex; align-items: center; justify-content: center; text-align: center; color: var(--faint); font-family: var(--ui); font-size: 0.8125rem; padding: 0.875rem; min-height: 3.5rem; }
.ph.tall { min-height: 9.375rem; }
.ph.short { min-height: 2.125rem; padding: 0.4375rem; }

/* grids */
.grid { display: grid; gap: 0.75rem; }
.cols-3 { grid-template-columns: repeat(3,1fr); }
.cols-side { grid-template-columns: 2fr 1fr; }
@media (max-width: 760px){ .grid { grid-template-columns: 1fr; } }

/* buttons (real links) */
.btn-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.5rem 0; align-items:center; }
/* Tighter gap for rows of glyph chips (drop lists, cache contents). */
.btn-row.tight { gap: 0.375rem; }
.btn { display: inline-block; border: 0.0625rem solid var(--line); background: var(--sink); color: var(--text); padding: 0.4375rem 0.8125rem; border-radius: var(--radius); font-size: 0.875rem; }
.btn:hover { border-color: var(--accent); text-decoration: none; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
.btn.ghost { background: transparent; }
.btn.sm { padding: 0.1875rem 0.5625rem; font-size: 0.8125rem; }
.btn.disabled { color:var(--faint); border-color:var(--line-2); background:var(--panel); }
button.btn { -webkit-appearance:none; appearance:none; font:inherit; cursor:pointer; }
button.btn:disabled { cursor:not-allowed; color:var(--faint); border-color:var(--line-2); background:var(--panel); }

/* form controls (search fields, filter selects) — themed like .btn, sunk like inputs */
input[type="search"], input[type="text"], input[type="number"], select {
  -webkit-appearance:none; appearance:none; font:inherit; font-size:0.8125rem;
  border:0.0625rem solid var(--line); background:var(--sink); color:var(--text);
  padding:0.3125rem 0.5625rem; border-radius:var(--radius);
}
input[type="search"]:focus, input[type="text"]:focus, input[type="number"]:focus, select:focus {
  outline:none; border-color:var(--accent);
}
/* The global ring above uses :where(), which contributes no specificity, so the
   :focus rule beat it and left text inputs with only a border tint. Restore the
   ring at matching specificity — keyboard focus has to be as visible here as it
   is on every button (A14). */
input[type="search"]:focus-visible, input[type="text"]:focus-visible, input[type="number"]:focus-visible, select:focus-visible {
  outline:0.125rem solid var(--accent); outline-offset:0.125rem;
}
/* Placeholders are instructions, not data — they inherit Sans. Only a numeric
   field's placeholder stays mono so it lines up with the value that replaces it. */
input::placeholder { color:var(--faint); }
input[type="number"]::placeholder { font-family:var(--mono); }
select.btn.sm { padding:0.1875rem 0.5625rem; }

/* provisions strip — one-tap use of consumables (the loop's "spend matter") */
.provisions { margin-bottom:0.875rem; }
.prov-list { display:flex; flex-direction:column; gap:0.5rem; margin-top:0.375rem; }
.prov-row { display:flex; align-items:center; gap:0.75rem; padding:0.5rem 0.625rem; border:0.0625rem solid var(--line-2); border-radius:var(--radius); background:var(--panel-2); }
.prov-row .ic { width:1.625rem; height:1.625rem; flex:none; color:var(--accent); }
.prov-row .ic .gl { width:1.5rem; height:1.5rem; }
/* The item ref grows to fill the row so its info button hugs the name, while
   the effect label + Use group at the far right instead of orphaning the "i"
   mid-row (B28). The ref must be the flex grower — an auto margin on the button
   would eat the free space first and re-bunch everything to the left. */
.prov-row > .item-ref--row { flex:1 1 auto; min-width:0; }
.prov-row > .item-ref--row .item-ref__main { flex:0 1 auto; }
.prov-row .lab { flex:none; min-width:0; }
.prov-row .lab .t { font-size:0.875rem; font-weight:600; }
.prov-row .lab .d { color:var(--muted); font-size:0.8125rem; }
/* What a provision restores, in the same treatment the rest of the page gives
   those figures: energy warn, HP good, both mono. It read as muted grey body
   text here while the dispatch button beside it used .ameta .top.energy. */
.prov-row .lab .d .eff { margin-left:0.375rem; }
.prov-row .lab .d .eff:first-child { margin-left:0; }
.prov-row .lab .d .eff.energy { color:var(--warn); }
.prov-row .lab .d .eff.hp { color:var(--good); }

/* misc primitives */
.muted { color: var(--muted); font-size: 0.8125rem; }
.note { color:var(--muted); font-size:0.8125rem; font-style:italic; }
.tag { display:inline-block; border:0.0625rem solid var(--line); background:var(--sink); border-radius: var(--radius); padding:0.0625rem 0.5rem; font-size: 0.75rem; color:var(--muted); margin:0.125rem 0.125rem 0.125rem 0; }
.bar { display:block; height: 0.5rem; background:var(--bg); border:0.0625rem solid var(--line); border-radius: 0.25rem; overflow:hidden; }
.bar > span { display:block; height:100%; background:var(--accent); }
/* Semantic fill colours — the span keeps only its dynamic width inline. */
.bar.hp > span { background:var(--good); }
.bar.enemy > span { background:var(--bad); }

/* Dashboard skills panel — every skill listed (registry order), untrained dimmed. */
.skill-list { display:flex; flex-direction:column; gap:0.375rem; margin:0.25rem 0 0.6875rem; }
.skill-row { display:flex; align-items:center; gap:0.625rem; font-size:0.75rem; }
.skill-row .nm { width:6.75rem; flex:none; color:var(--text); }
.skill-row .bar { flex:1; }
.skill-row .lv { width:2.875rem; flex:none; text-align:right; color:var(--accent); font-family:var(--mono); font-variant-numeric:tabular-nums; }
.skill-row.untrained .nm { color:var(--muted); }
.skill-row.untrained .lv { color:var(--faint); }
.rows > div { display:flex; justify-content: space-between; gap:0.625rem; padding: 0.4375rem 0; border-bottom: 0.0625rem solid var(--line-2); }
.rows > div:last-child { border-bottom: 0; }
.rows.compact { font-size:0.8125rem; }
/* Expedition loadout: quantity stepper bounded by held count. */
.pack-pick { flex:none; width:1rem; height:1rem; accent-color:var(--accent); }
label.prov-row { cursor:pointer; }
label.prov-row:has(.pack-pick:checked) { border-color:var(--accent); background:var(--panel); }
table.wf { width:100%; border-collapse: collapse; font-size: 0.875rem; margin: 0.375rem 0; }
table.wf th, table.wf td { border:0.0625rem solid var(--line); padding:0.375rem 0.5rem; text-align:left; }
table.wf th { background:var(--sink); font-family: var(--ui); font-size:0.6875rem; font-weight:600; text-transform:uppercase; letter-spacing:.08em; color:var(--faint); }
/* Leaderboard: the viewer's own row. Scoped (lb-) — bare `.me` belongs to chat. */
table.wf tr.lb-me td { background:var(--sink); border-left-color:var(--accent); }
table.wf tr.lb-me td:first-child { border-left:0.1875rem solid var(--accent); }
.tabs { display:flex; gap:0.25rem; border-bottom:0.0625rem solid var(--rule); margin-bottom:0.75rem; flex-wrap:wrap; }
.tabs a { padding:0.375rem 0.75rem; border:0.0625rem solid var(--line); border-bottom:0; border-radius:var(--radius) var(--radius) 0 0; background:var(--sink); color:var(--muted); font-size:0.875rem; }
.tabs a.active { background:var(--panel); color:var(--text); font-weight:600; position:relative; top:0.0625rem; }
.arrow { text-align:center; color:var(--faint); font-size:1.375rem; margin:-0.375rem 0 0.5rem; }
.flag { background:rgba(224,178,82,.1); border:0.0625rem solid var(--warn); border-radius:var(--radius); padding:0.5rem 0.625rem; font-size:0.8125rem; color:var(--warn); margin:0.5rem 0; }

/* labeled square tiles — items / skills / recipes share one language
   (migrated from prototype-blueprint.html). Set --tile-cols per grid; the box
   holds a short mono code (icon stand-in), an optional .badge (qty), then a name
   and optional .lv (level). */
.tiles { display:grid; grid-template-columns:repeat(var(--tile-cols,6),minmax(0,1fr)); gap:0.5625rem; }
/* A fixed, centered square — width:52px (not just height) and padding/margin
   reset so it doesn't inherit the global `.box` panel's padding+margin and
   stretch to the grid column. Consistent icon size regardless of layout width. */

/* Canonical item identity. Callers choose a semantic variant and finite size;
   item glyph dimensions, quantity syntax, links, and preview stay component-owned. */
.item-ref { --item-stage:1rem; position:relative; display:inline-flex; min-width:0; align-items:center; color:var(--text); vertical-align:middle; }
.item-ref__main { min-width:0; display:inline-flex; align-items:center; gap:0.4375rem; color:inherit; }
a.item-ref__main:hover { text-decoration:none; }
.item-ref__stage { position:relative; width:var(--item-stage); height:var(--item-stage); flex:none; display:flex; align-items:center; justify-content:center; border:0.0625rem solid var(--line); border-radius:var(--radius); background:var(--sink); color:var(--accent-2); }
.item-ref__stage .gl { width:72%; height:72%; }
.item-ref__body { min-width:0; display:flex; flex-direction:column; line-height:1.2; }
.item-ref__name { min-width:0; overflow-wrap:anywhere; }
.item-ref__meta { color:var(--muted); font-family:var(--mono); font-size:0.625rem; }
.item-ref__quantity { color:var(--muted); font-family:var(--mono); font-size:0.8125rem; }
.item-ref__badge { position:absolute; right:-0.0625rem; bottom:-0.0625rem; padding:0 0.25rem; border:0.0625rem solid var(--line); border-right:0; border-bottom:0; background:var(--panel-2); color:var(--text); font-family:var(--mono); font-size:0.6875rem; line-height:1.5; }
.item-ref--xs { --item-stage:1rem; }
.item-ref--sm { --item-stage:1.5rem; }
.item-ref--md { --item-stage:2rem; }
.item-ref--lg { --item-stage:3rem; }
/* Nudge the inline glyph up ~0.08em so it optically centers on the text: flex
   `align-items:center` sits it on the line-box middle, which reads low against
   the cap/x-height because of descender space (B37). Visual-only (translate),
   inline variant only — rows/tiles/compact unaffected. */
.item-ref--inline .item-ref__stage { border:0; background:transparent; transform:translateY(-0.08em); }
.item-ref--inline .item-ref__stage .gl { width:100%; height:100%; }
.item-ref--inline .item-ref__main { gap:0.25rem; }
.item-ref--inline .item-ref__body { display:inline-flex; flex-direction:row; align-items:center; gap:0.25rem; }
.item-ref--compact .item-ref__name, .item-ref--row .item-ref__name { font-size:0.8125rem; font-weight:600; }
.item-ref--row { display:flex; }
.item-ref--row .item-ref__main { flex:1; }
.item-ref--tile { display:flex; justify-content:center; text-align:center; }
.item-ref--tile .item-ref__main { flex-direction:column; gap:0.3125rem; }
.item-ref--tile .item-ref__name { max-width:100%; color:var(--muted); font-size:0.75rem; line-height:1.2; }
.item-ref--output .item-ref__stage { border-color:var(--accent); background:rgba(95,176,194,.07); color:var(--accent); }
.item-ref--short .item-ref__stage { border-style:dashed; border-color:var(--bad); color:var(--bad); }
.item-ref--short .item-ref__quantity { color:var(--bad); }
.item-ref--locked { color:var(--faint); }
.item-ref--locked .item-ref__stage, .item-ref--rare .item-ref__stage { border-style:dashed; }
.item-ref--unknown { color:var(--bad); }
.item-ref__info { position:relative; z-index:2; width:1.375rem; height:1.375rem; margin-left:0.1875rem; padding:0; flex:none; border:0; border-radius:50%; background:transparent; color:var(--faint); font:600 0.75rem/1.375rem var(--mono); text-align:center; cursor:pointer; }
.item-ref__info::before { content:""; position:absolute; inset:-0.375rem; }
.item-ref__info:hover, .item-ref__info:focus-visible, .item-ref.is-open .item-ref__info { background:var(--panel-2); color:var(--accent); outline:0.0625rem solid var(--line); }
/* Fixed to the viewport (not absolute) so ancestor `overflow:hidden` on
   .bom / .hub / .acts / .scene / .recipe-list / .regions can't clip it and it
   can't run off-screen. item-ref.js sets left/top from the trigger rect (with
   viewport flip/clamp) and keeps it glued on scroll/resize. */
.item-ref__preview { position:fixed; z-index:90; width:15rem; padding:0.625rem 0.75rem; display:flex; flex-direction:column; gap:0.25rem; border:0.0625rem solid var(--accent); border-radius:var(--radius); background:var(--panel); box-shadow:0 0.75rem 1.875rem rgba(0,0,0,.38); color:var(--text); font-size:0.75rem; line-height:1.35; text-align:left; visibility:hidden; opacity:0; transform:translateY(-0.1875rem); pointer-events:none; transition:opacity .12s ease, transform .12s ease; }
.item-ref__preview > span { color:var(--muted); }
.item-ref__preview .item-ref__effect { color:var(--good); }
.item-ref.is-hovering .item-ref__preview, .item-ref.is-open .item-ref__preview { visibility:visible; opacity:1; transform:translateY(0); pointer-events:auto; }
.item-ref--tile .item-ref__info { position:absolute; top:-0.3125rem; right:max(0.1875rem, calc(50% - 2.125rem)); }

/* workshop mode switcher — Craft / Decompose / Synthesise */
.modes { display:flex; gap:0.375rem; margin:0 0 0.875rem; border-bottom:0.0625rem solid var(--line); }
.modes a, .modes .mode-soon { padding:0.5rem 1rem; font-size:0.875rem; font-weight:600; color:var(--muted); border:0.0625rem solid transparent; border-bottom:0; border-radius:var(--radius) var(--radius) 0 0; margin-bottom:-0.0625rem; }
.modes a:hover { color:var(--text); text-decoration:none; }
.modes a.active { color:var(--text); background:var(--panel); border-color:var(--line); }
.modes .mode-soon { color:var(--faint); cursor:default; }

/* synthon stockpile — untradeable, grouped by category */
.synthon-list { display:flex; flex-direction:column; gap:0.3125rem; margin-top:0.375rem; }
.synthon-row { display:flex; align-items:center; gap:0.625rem; padding:0.375rem 0.5625rem; border:0.0625rem solid var(--line-2); border-radius:var(--radius); background:var(--panel-2); }
.synthon-row .ic { width:1.375rem; height:1.375rem; flex:none; color:var(--accent-2); display:flex; align-items:center; }
.synthon-row .ic .gl { width:1.375rem; height:1.375rem; }

/* breakdown bench — source item -> synthon outputs, one transform per row */
.brk-list { display:flex; flex-direction:column; gap:0.5rem; margin-top:0.375rem; }
.brk-row { display:flex; align-items:center; gap:0.875rem; padding:0.5625rem 0.75rem; border:0.0625rem solid var(--line-2); border-radius:var(--radius); background:var(--panel-2); }
.brk-row .ic { width:1.375rem; height:1.375rem; flex:none; display:flex; align-items:center; justify-content:center; color:var(--accent-2); }
.brk-row .ic .gl { width:1.375rem; height:1.375rem; }
.brk-row .src { display:flex; align-items:center; gap:0.5625rem; flex:none; min-width:11.25rem; }
.brk-row .src .nm { font-size:0.875rem; font-weight:600; }
.brk-row .src .held { font-family:var(--mono); font-size:0.8125rem; color:var(--muted); }
.brk-row .arrow { color:var(--faint); flex:none; }
.brk-row .out { display:flex; flex-wrap:wrap; align-items:center; gap:0.4375rem 1rem; flex:1; min-width:0; }
.brk-row .out .yld { display:inline-flex; align-items:center; gap:0.375rem; }
/* Short of the requirement. Was an inline opacity:.55, which took the
   have/need figures to 4.35:1 — the one number the row exists to show. */
.brk-row .out .yld.short { color:var(--faint); }
/* A bar with no figure beside it is unreadable without sight (A9). */
.prog-pair { display:inline-flex; align-items:center; gap:0.5rem; }
.brk-row .out .on { font-size:0.8125rem; color:var(--text); }
.brk-row .out .oq { font-family:var(--mono); font-size:0.8125rem; color:var(--accent-2); }
.brk-row > .btn { margin-left:auto; flex:none; align-self:center; }
.synthon-row .nm { flex:1; min-width:0; font-size:0.875rem; }
.synthon-row .cat { font-size:0.6875rem; color:var(--muted); text-transform:uppercase; letter-spacing:.06em; }
.synthon-row .qty { font-family:var(--mono); font-size:0.875rem; color:var(--accent); min-width:2.5rem; text-align:right; }

/* insight gauges — the lab's five typed-knowledge counters (analysis page) */
.insight-gauges { display:flex; flex-wrap:wrap; gap:0.5rem; margin:0 0 0.875rem; }
.insight-gauge { display:flex; align-items:center; gap:0.5rem; padding:0.4375rem 0.75rem; border:0.0625rem solid var(--line); border-radius:var(--radius); background:var(--panel); min-width:9.375rem; }
.insight-gauge .ic { width:1.25rem; height:1.25rem; flex:none; color:var(--accent-2); display:flex; align-items:center; }
.insight-gauge .ic .gl { width:1.25rem; height:1.25rem; }
.insight-gauge .cat { flex:1; font-size:0.75rem; color:var(--muted); text-transform:uppercase; letter-spacing:.06em; }
.insight-gauge .qty { font-family:var(--mono); font-size:0.9375rem; color:var(--accent); }
.insight-gauge[data-empty] .qty { color:var(--faint); }
.brk-row.done { opacity:.75; }

/* energy cost convention — mono, amber, used inline next to actions */
.ecost { font-family:var(--mono); color:var(--warn); white-space:nowrap; }

/* character equipment rows (combat slice A) — slot label · equipped item ·
   unequip/equip-picker actions. One row per slot, no-JS forms. */
.equip-list { display:flex; flex-direction:column; gap:0.5rem; margin-top:0.375rem; }
.equip-row { display:flex; flex-wrap:wrap; align-items:center; gap:0.5rem 0.875rem; padding:0.5rem 0.75rem; border:0.0625rem solid var(--line-2); border-radius:var(--radius); background:var(--panel-2); }
.equip-slot { flex:none; min-width:4.5rem; font-size:0.6875rem; letter-spacing:.08em; text-transform:uppercase; color:var(--faint); }
.equip-item { flex:1 1 9rem; min-width:0; }
.equip-acts { display:flex; flex-wrap:wrap; align-items:center; gap:0.5rem; margin-left:auto; }
.equip-acts form { display:flex; align-items:center; gap:0.375rem; }
.equip-pick select { max-width:14rem; }
.equip-hint { font-size:0.75rem; }
.equip-vital { margin:0 0 0.25rem; }
.equip-vital + .bar + .equip-vital { margin-top:0.75rem; }

/* item-detail relationship rows (B26) — a recipe label over an inputs → output
   flow, so "Produced by" answers *made from what?* and "Used in" reads from this
   item's perspective. The whole row links to the recipe; item refs inside are
   link-less (no nested anchors). */
.rel-list { display:flex; flex-direction:column; gap:0.5rem; margin-top:0.375rem; }
.rel-row { display:block; padding:0.5rem 0.75rem; border:0.0625rem solid var(--line-2); border-radius:var(--radius); background:var(--panel-2); color:var(--text); }
.rel-row:hover { border-color:var(--accent); text-decoration:none; }
.rel-label { display:flex; align-items:baseline; gap:0.5rem; }
.rel-label .nm { font-weight:600; font-size:0.875rem; }
.rel-label .sk { margin-left:auto; color:var(--faint); font-size:0.6875rem; letter-spacing:.06em; text-transform:uppercase; }
.rel-flow { display:flex; flex-wrap:wrap; align-items:center; gap:0.375rem 0.5rem; margin-top:0.375rem; }
.rel-flow .arrow, .rel-flow .plus { color:var(--faint); flex:none; }

/* quest board rows — giver quests + daily commissions share .quest-row.
   objective (grows) · progress · reward chips · action. Layout lives here
   instead of the per-row inline flex hacks the placeholder scaffold carried. */
.quest-row { flex-wrap:wrap; }
.quest-row .src { flex:1 1 12.5rem; min-width:12.5rem; }
.quest-row .src .nm { flex:none; }
.quest-row .src .d { font-size:0.8125rem; }
.quest-row .qprog { display:flex; align-items:center; gap:0.5rem; flex:none; min-width:9.375rem; }
.quest-row .qprog .bar { width:7.5rem; }
.quest-row .qprog .count { font-family:var(--mono); font-size:0.8125rem; color:var(--muted); }
.quest-row .rw { display:flex; flex-wrap:wrap; align-items:center; gap:0.375rem; flex:none; }
.quest-row .qact { margin-left:auto; flex:none; min-width:6rem; text-align:right; }

/* vendor — the hub shop's shelves and sell panel (vendor-screen.md) */
.vendor-identity { display:flex; align-items:flex-start; gap:0.875rem; margin-bottom:0.875rem; }
.vendor-identity-main { min-width:0; }
.vendor-identity-main .page-sub { margin-bottom:0.375rem; }
.vendor-purse { display:flex; align-items:center; gap:0.5rem; }
.vendor-row { flex-wrap:wrap; }
.vendor-row .src { flex:1 1 11rem; min-width:11rem; display:flex; align-items:center; }
.vendor-row .vendor-price { flex:none; display:inline-flex; align-items:center; gap:0.25rem; }
.vendor-row .vendor-quota { flex:none; font-family:var(--mono); font-size:0.8125rem; min-width:5.5rem; text-align:right; }
.vendor-row > form { margin-left:auto; flex:none; }
/* Reward chip: the figure is already in <b>, so only that side is mono and the
   skill/unit it is measured in reads as a word. */
.rw-chip { font-size:0.75rem; border:0.0625rem solid var(--line); border-radius:var(--radius); padding:0.125rem 0.4375rem; background:var(--sink); color:var(--muted); white-space:nowrap; }
.rw-chip b { font-family:var(--mono); font-variant-numeric:tabular-nums; color:var(--text); font-weight:600; }
.rw-chip.gold { border-color:var(--warn); color:var(--warn); }
/* A method or a grade is a capability, not a payout — it reads in the accent
   so it doesn't sit flat next to the gold and XP numbers. */
.rw-chip.unlock { border-color:var(--accent); color:var(--accent); }

/* The thread — the main quest pointer on the dashboard. It is the first thing
   a new player reads, so it sits above the hub and states one next action. */
.thread { border-left:0.1875rem solid var(--accent); }
.thread-head { display:flex; align-items:baseline; justify-content:space-between; gap:0.5rem; }
.thread-kicker { font-size:0.6875rem; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }
.thread-count { font-size:0.75rem; color:var(--faint); }
.thread-title { margin:0.25rem 0 0.125rem; font-weight:600; }
.thread-task { margin:0 0 0.375rem; color:var(--muted); font-size:0.875rem; }
.thread-who { display:flex; align-items:center; gap:0.3125rem; flex-wrap:wrap;
  margin:0.375rem 0 0.5rem; color:var(--muted); font-size:0.875rem; }
.thread .bar { display:inline-block; vertical-align:middle; margin-right:0.375rem; }

/* ===========================================================================
   SIGNATURE COMPONENTS — ported from prototype-blueprint.html so the storyboard
   matches the verified screens. (1) hub-list launcher, (2) location anchor +
   NOTES bar + statline + action list, (3) NPC dossier. See decisions.md.
   =========================================================================== */

/* (1) HUB-LIST launcher (dashboard home) — icon + label + meta + chevron rows */
.hub { border:0.0625rem solid var(--line); background:var(--panel); border-radius:var(--radius); overflow:hidden; margin-bottom:0.875rem; }
.hub a { display:flex; align-items:center; gap:0.875rem; padding:0.625rem 1rem; border-top:0.0625rem solid var(--line-2); color:var(--text); }
.hub a:first-child { border-top:0; }
.hub a:hover { background:var(--panel-2); text-decoration:none; }
.hub .ic { width:2.25rem; height:2.25rem; flex:none; border:0.0625rem solid var(--line); background:var(--sink); display:flex; align-items:center; justify-content:center; font-family:var(--mono); font-size:0.75rem; font-weight:700; color:var(--accent); }
.hub .lab { flex:1; min-width:0; }
.hub .lab .t { font-size:1rem; font-weight:600; }
.hub .lab .d { color:var(--muted); font-size:0.8125rem; margin-top:0.0625rem; }
.hub .meta { font-family:var(--ui); font-size:0.75rem; color:var(--accent-2); white-space:nowrap; }
.hub .chev { color:var(--faint); font-family:var(--mono); }

/* (2a) LOCATION ANCHOR — full-width blueprint site drawing; flavour in NOTES bar */
.scene { border:0.0625rem solid var(--rule); background:var(--sink); border-radius:var(--radius); margin-bottom:0.875rem; overflow:hidden; }
.scene .draw { position:relative;
  background-image:repeating-linear-gradient(0deg, rgba(150,170,180,.05) 0 0.0625rem, transparent 0.0625rem 1.125rem),
                   repeating-linear-gradient(90deg, rgba(150,170,180,.05) 0 0.0625rem, transparent 0.0625rem 1.125rem); }
.scene .draw svg { display:block; width:100%; height:auto; }
.scene .corner { position:absolute; left:0; top:0; font-family:var(--mono); font-size:0.6875rem; letter-spacing:.1em; text-transform:uppercase; color:var(--accent); background:rgba(14,21,26,.88); border-right:0.0625rem solid var(--rule); border-bottom:0.0625rem solid var(--rule); padding:0.25rem 0.5rem; }
.scene .titleblock { position:absolute; right:0; bottom:0; display:flex; font-family:var(--mono); font-size:0.6875rem; letter-spacing:.04em; color:var(--muted); background:rgba(14,21,26,.88); border-left:0.0625rem solid var(--rule); border-top:0.0625rem solid var(--rule); }
.scene .titleblock span { padding:0.25rem 0.4375rem; border-left:0.0625rem solid var(--line-2); }
.scene .titleblock span:first-child { border-left:0; color:var(--accent-2); }
.scene .notes { display:flex; align-items:baseline; gap:0.875rem; padding:0.6875rem 1rem; background:var(--panel); border-top:0.0625rem solid var(--rule); }
.scene .notes .lbl { flex:none; font-family:var(--ui); font-size:0.6875rem; letter-spacing:.18em; text-transform:uppercase; color:var(--accent); }
.scene .notes p { margin:0; flex:1; min-width:0; font-size:0.875rem; line-height:1.5; color:var(--text); }
.scene .notes .ref { flex:none; margin-left:auto; font-family:var(--mono); font-size:0.6875rem; letter-spacing:.06em; text-transform:uppercase; color:var(--faint); white-space:nowrap; }
@media(max-width:680px){ .scene .notes{ flex-wrap:wrap; gap:0.375rem; } .scene .notes .ref{ margin-left:0; } }

/* (2b) STATLINE — a row of labelled readouts (skill req / energy / hazard / drop) */
.statline { display:flex; border:0.0625rem solid var(--line); background:var(--panel); border-radius:var(--radius); margin-bottom:0.875rem; }
.statline .cell { flex:1; padding:0.625rem 0.875rem; border-right:0.0625rem solid var(--line-2); }
.statline .cell:last-child { border-right:0; }
/* Labels and values inherit Sans: a stat cell holds region, station, class and
   verdict names as often as it holds a number. Numeric/coded readouts opt into
   mono per cell with `.num` (or an inner `.num` span on a mixed line). */
.statline .l { font-size:0.6875rem; letter-spacing:.14em; text-transform:uppercase; color:var(--faint); }
.statline .v { font-size:1rem; margin-top:0.375rem; color:var(--text); }
.statline .v small { color:var(--muted); font-size:0.75rem; }
.statline .v b { color:var(--accent); }
.statline .v.energy { color:var(--warn); }
/* Expedition gear-check verdict — the soft in-region pressure (never a lock). */
.gear-equipped b { color:var(--good); }
.gear-at_risk b { color:var(--warn); }
.gear-under b { color:var(--bad); }
@media(max-width:680px){ .statline{ flex-wrap:wrap; } .statline .cell{ flex:1 1 50%; } }

/* (2c) ACTION LIST — icon + label + right-aligned cost (energy/xp/gate) + sub */
.acts { border:0.0625rem solid var(--line); background:var(--panel); border-radius:var(--radius); overflow:hidden; margin-bottom:0.875rem; }
.acts a, .acts button.primary { display:flex; align-items:center; gap:0.875rem; padding:0.625rem 1rem; border-top:0.0625rem solid var(--line-2); color:var(--text); }
.acts button.primary { -webkit-appearance:none; appearance:none; width:100%; box-sizing:border-box; border-left:0; border-right:0; border-bottom:0; font:inherit; text-align:left; cursor:pointer; }
.acts a:first-child, .acts button.primary:first-child { border-top:0; }
.acts a:hover, .acts button.primary:hover { background:var(--panel-2); text-decoration:none; }
.acts a.primary, .acts button.primary { background:rgba(95,176,194,.07); }
.acts .ic { width:1.875rem; height:1.875rem; flex:none; border:0.0625rem solid var(--line); background:var(--sink); display:flex; align-items:center; justify-content:center; font-family:var(--mono); font-size:0.8125rem; color:var(--accent); }
.acts .lab { flex:1; min-width:0; }
/* These sit inside <button>, which takes phrasing content only, so they are
   spans and need the block layout a <div> used to give them (A18). */
.lab > .t, .lab > .d { display:block; }
.acts .lab .t { font-size:0.9375rem; font-weight:600; }
.acts .lab .d { color:var(--muted); font-size:0.8125rem; margin-top:0.0625rem; }
.ameta { text-align:right; white-space:nowrap; min-width:6rem; }
.ameta .top { display:block; font-family:var(--mono); font-size:0.9375rem; font-weight:700; color:var(--text); }
.ameta .top.energy { color:var(--warn); }
.ameta .top.xp { color:var(--good); }
.ameta .top.gate { color:var(--faint); font-weight:400; font-size:0.8125rem; }
/* The sub slot carries either a reward figure ("+12 xp") or a plain note
   ("est. clear"), so it inherits Sans and the numeric ones add `.num`. */
.ameta .sub { display:block; font-size:0.6875rem; letter-spacing:.08em; text-transform:uppercase; color:var(--faint); margin-top:0.3125rem; }

/* (2d) GATHER ACTION — a submit button shaped like an action-list row. Two
   stacked rows (no grid / no flex:1 to avoid wide-screen sprawl):
     head:  [ic]  Work the cedar stand .................... [⚡ / xp]
     haul:        MAIN [oak]   AUX [pine][resin][copper]
   A submit button must drop native appearance, else `appearance:auto` renders a
   stark white OS button. */
.acts .gather-act { -webkit-appearance:none; appearance:none; background:transparent; color:var(--text); font:inherit; box-sizing:border-box; width:100%; display:block; padding:0.75rem 1rem; border:0; border-top:0.0625rem solid var(--line-2); cursor:pointer; text-align:left; }
.acts .gather-act:first-child { border-top:0; }
.acts .gather-act:hover:not(:disabled) { background:var(--panel-2); }
.acts .gather-act:disabled { opacity:.5; cursor:not-allowed; }
.ga-head { display:flex; align-items:center; gap:0.75rem; }
.ga-head .t { font-size:0.9375rem; font-weight:600; }
.ga-head .ameta { margin-left:auto; }

/* Localised skill progress on each gather action — the trained skill, its level,
   and progress to the next, indented under the title past the icon. */
.gather-act .ga-skill { display:flex; align-items:center; gap:0.5625rem; margin-top:0.5625rem; padding-left:2.625rem; font-size:0.75rem; }
.ga-skill .sk-name { color:var(--text); }
.ga-skill .sk-lv { color:var(--accent); font-family:var(--mono); font-variant-numeric:tabular-nums; }
.ga-skill .bar { flex:1; max-width:12.5rem; }
.ga-skill .sk-pct { color:var(--faint); min-width:2.125rem; font-family:var(--mono); font-variant-numeric:tabular-nums; }
.ga-skill .sk-new { color:var(--faint); text-transform:uppercase; font-size:0.625rem; letter-spacing:.1em; border:0.0625rem solid var(--line-2); padding:0 0.3125rem; border-radius:var(--radius); }

/* (2d-i) ITEM SWATCHES — graphic placeholders for the haul: a framed line-glyph
   per item with its quantity (main) or drop-odds (aux) as a corner badge, in
   main / aux groups led by a small label. Indented under the title past the icon. */
.gather-act .yield { display:flex; flex-wrap:wrap; align-items:flex-start; justify-content:flex-start; gap:0.625rem; margin-top:0.6875rem; padding-left:2.625rem; }
/* Base label treatment — applies wherever `.ylabel` is used (gather yield strip,
   expedition dossier "drops", in-run "pack"), not only under `.yield`. B40c: the
   dossier/pack labels were unstyled because the rule was scoped to `.yield`. */
.ylabel { font-size:0.625rem; letter-spacing:.14em; text-transform:uppercase; color:var(--faint); }
.yield .ylabel { align-self:center; }
.yield .ylabel.sep { margin-left:0.5rem; padding-left:1rem; border-left:0.0625rem solid var(--line-2); }
.swatch { display:flex; flex-direction:column; align-items:center; gap:0.3125rem; width:3.125rem; }
.swatch .sw { position:relative; width:2.875rem; height:2.875rem; border:0.0625rem solid var(--line); background:var(--sink); border-radius:var(--radius); display:flex; align-items:center; justify-content:center; color:var(--accent-2); }
.swatch .sw .gl { width:1.625rem; height:1.625rem; }
.swatch.main .sw { border-color:var(--accent); background:rgba(95,176,194,.07); color:var(--accent); }
.swatch.rare .sw { border-style:dashed; color:var(--faint); }
.gather-act:hover:not(:disabled) .swatch .sw { border-color:var(--accent); }
.swatch .q { position:absolute; right:-0.0625rem; bottom:-0.0625rem; font-family:var(--mono); font-size:0.6875rem; line-height:1.5; color:var(--text); background:var(--panel-2); border:0.0625rem solid var(--line); border-right:0; border-bottom:0; padding:0 0.25rem; }
.swatch.main .q { color:var(--accent); }
.swatch.aux .q, .swatch.rare .q { color:var(--muted); }
.swatch .nm { max-width:100%; font-size:0.625rem; line-height:1.25; color:var(--muted); text-align:center; overflow:hidden; text-overflow:ellipsis; }
.swatch.aux .nm, .swatch.rare .nm { color:var(--faint); }

/* Gather site section — stacks the drawn anchor + statline + work actions per place. */
.place { margin-bottom:1.375rem; padding-bottom:0.625rem; border-bottom:0.0625rem dashed var(--line-2); }
.place:last-of-type { border-bottom:0; margin-bottom:0.875rem; }
/* The gather page keeps ONE width whether or not a site has an NPC, so
   travelling between regions (some with people, some without — e.g. The Cutting)
   never jumps the layout. Sites with an NPC show the people rail; sites without
   let the work reflow to fill. */
main.main:has(.place) { max-width:98.75rem; }
.place-body, .place-work { min-width:0; }
.site-people { min-width:0; margin:0.625rem 0 0.875rem; }
.site-people .npc-row { margin-top:0; }
.site-people .npc { max-width:none; }
.site-people .npc:first-child { border-top-left-radius:0; border-top-right-radius:0; }
.site-people-kicker {
  border:0.0625rem solid var(--line); border-bottom:0; background:var(--sink);
  border-radius:var(--radius) var(--radius) 0 0; padding:0.4375rem 0.625rem;
  font-size:0.6875rem; letter-spacing:.14em;
  text-transform:uppercase; color:var(--faint);
}

@media (min-width:1440px){
  .place-body.has-characters {
    display:grid; grid-template-columns:minmax(0,1fr) minmax(19.25rem,21.25rem);
    gap:1rem; align-items:start;
  }
  .place-body.has-characters .site-people { margin-top:0; }
  .npc-row-inspector { display:flex; flex-direction:column; flex-wrap:nowrap; gap:0.625rem; margin:0; }
  /* In the rail the card gets a little more presence — larger portrait, panel
     background — but keeps the same [portrait | content] shape as everywhere.
     flex:none so it sizes to content instead of growing down the column. */
  .site-people .npc { flex:none; background:var(--panel); }
  .site-people .npc-photo { width:4rem; height:5.25rem; }
  .site-people .npc-name { font-size:1.0625rem; }
}

/* (2e) CRAFT BILL OF MATERIALS — ingredient rows + an output/byproduct flow block. */
.bom, .flow { border:0.0625rem solid var(--line); border-radius:var(--radius); overflow:hidden; }
.flow { margin-top:0.625rem; }
.bom-row, .flow .out, .flow .by { display:flex; align-items:center; gap:0.75rem; padding:0.5625rem 0.875rem; border-top:0.0625rem solid var(--line-2); }
.bom-row > .item-ref, .flow .out > .item-ref { flex:1; }
.bom-row:first-child { border-top:0; }
.flow .out { border-top:0; background:rgba(95,176,194,.06); }
.flow .by { background:var(--sink); }
.bom .ic, .flow .ic { width:1.75rem; height:1.75rem; flex:none; border:0.0625rem solid var(--line); background:var(--sink); display:flex; align-items:center; justify-content:center; font-family:var(--mono); font-size:0.6875rem; color:var(--accent-2); }
.bom .ic .gl, .flow .ic .gl { width:1.125rem; height:1.125rem; }
.flow .out .ic { border-color:var(--accent); color:var(--accent); }
.bom .lab, .flow .lab { flex:1; min-width:0; }
.bom .lab .t, .flow .lab .t { display:block; font-size:0.875rem; font-weight:600; }
.bom .lab .d, .flow .lab .d { display:block; color:var(--muted); font-size:0.75rem; margin-top:0.0625rem; }
/* A status phrase ("have 3 / 5", "sold out", "claimed") is prose; only the
   counts inside it carry `.num`. The tier chip is a code, so it stays mono. */
.state { font-size:0.75rem; white-space:nowrap; }
/* Tier badge as a subtle chip so "T1" reads as a tag, not run-on mono text. */
.state.tier { border:0.0625rem solid var(--line); border-radius:0.1875rem; padding:0 0.25rem; font-size:0.6875rem; color:var(--muted); font-family:var(--mono); }
.state.ok { color:var(--good); }
.state.accent { color:var(--accent); }
.state.muted { color:var(--faint); }
.state.short { color:var(--warn); }

/* (2f) CRAFT RECIPE BROWSER — master list (left) + datasheet (right).
   A catalogue/workbench silhouette, deliberately NOT the place-page banner. */
.recipe-browser { display:grid; grid-template-columns:17.5rem 1fr; gap:1rem; align-items:start; }
@media (max-width:900px){ .recipe-browser { grid-template-columns:1fr; } }
/* An open dock costs the workspace the dock's width, so the two-column browser
   has to fold earlier than its 900px viewport breakpoint — otherwise the
   datasheet overflows its column and slides back under the dock (measured:
   overflowing from 1000px to 1240px, clean from 1280px). */
@media (max-width:1280px){
  body:has(.chatdock[open]) .recipe-browser { grid-template-columns:1fr; }
}
.recipe-list { border:0.0625rem solid var(--line); background:var(--panel); border-radius:var(--radius); overflow:hidden; }
.rl-search { display:flex; gap:0.375rem; align-items:center; padding:0.5rem; border-bottom:0.0625rem solid var(--line-2); }
.rl-search input { min-width:0; }
.rl-head { font-size:0.6875rem; letter-spacing:.14em; text-transform:uppercase; color:var(--faint); padding:0.5rem 0.75rem 0.25rem; background:var(--sink); border-top:0.0625rem solid var(--line-2); }
.rl-group:first-child .rl-head { border-top:0; }
.rl-row { display:flex; align-items:center; justify-content:space-between; gap:0.5rem; padding:0.5rem 0.75rem; border-top:0.0625rem solid var(--line-2); color:var(--text); }
.rl-row:hover { background:var(--panel-2); text-decoration:none; }
.rl-row.active { background:var(--panel-2); box-shadow:inset 0.1875rem 0 0 var(--accent); }
.rl-row.locked { color:var(--faint); }
.rl-name { font-size:0.875rem; }
.rl-gate { font-family:var(--ui); font-size:0.75rem; color:var(--accent-2); white-space:nowrap; }
.rl-row.locked .rl-gate { color:var(--faint); }

.datasheet { min-width:0; }
.ds-head { display:flex; gap:1rem; align-items:center; border:0.0625rem solid var(--rule); background:var(--sink); border-radius:var(--radius); padding:0.875rem 1rem; margin-bottom:0.875rem; }
.apparatus { width:10.625rem; flex:none; border:0.0625rem solid var(--line); background:var(--panel); border-radius:var(--radius); padding:0.375rem; }
.apparatus svg { display:block; width:100%; height:auto;
  background-image:repeating-linear-gradient(0deg, rgba(150,170,180,.05) 0 0.0625rem, transparent 0.0625rem 1rem),
                   repeating-linear-gradient(90deg, rgba(150,170,180,.05) 0 0.0625rem, transparent 0.0625rem 1rem); }
.ds-title { min-width:0; }
.ds-kicker { font-family:var(--ui); font-size:0.6875rem; letter-spacing:.12em; text-transform:uppercase; color:var(--accent); }
.ds-title h2 { font-size:1.375rem; margin:0.25rem 0 0.5rem; }
.pills { display:flex; flex-wrap:wrap; gap:0.375rem; }
.pill { font-size:0.6875rem; color:var(--muted); border:0.0625rem solid var(--line); background:var(--panel); border-radius:62.4375rem; padding:0.125rem 0.625rem; }
@media (max-width:520px){ .ds-head { flex-wrap:wrap; } .apparatus { width:100%; } }

/* (3) NPC DOSSIER — id-photo + role + meta + diegetic quote */
.dossier { border:0.0625rem solid var(--line); background:var(--panel); border-radius:var(--radius); padding:0.875rem 1rem; margin-bottom:0.875rem; }
.dossier.last { margin-bottom:0; }
.dossier .row { display:flex; gap:0.875rem; }
.dossier .idphoto { width:4.875rem; height:6rem; flex:none; border:0.0625rem solid var(--line); background:var(--sink); position:relative; overflow:hidden; }
.dossier .idphoto .ref { position:absolute; left:0; right:0; bottom:0; font-family:var(--mono); font-size:0.5625rem; color:var(--muted); background:rgba(10,26,41,.85); padding:0.0625rem 0.25rem; border-top:0.0625rem solid var(--line); }
.dossier .nm { font-size:1rem; font-weight:650; }
.dossier .role { font-family:var(--ui); font-size:0.6875rem; letter-spacing:.08em; text-transform:uppercase; color:var(--accent-2); margin:0.125rem 0 0.5rem; }
.dossier .meta { font-family:var(--ui); font-size:0.71875rem; color:var(--muted); line-height:1.7; }
.dossier .meta b { font-family:var(--mono); font-variant-numeric:tabular-nums; font-weight:400; color:var(--text); }
.dossier .quote { margin-top:0.6875rem; padding-top:0.6875rem; border-top:0.0625rem dashed var(--line); font-size:0.875rem; color:var(--text); font-style:italic; }
.dossier .quote::before { content:"\201C"; } .dossier .quote::after { content:"\201D"; }

.apparatus .cap .pill { font-family:var(--mono); font-size:0.6875rem; border:0.0625rem solid var(--line); padding:0.0625rem 0.375rem; color:var(--muted); margin-right:0.25rem; }

/* One persistent chatbar. While it is OPEN the workspace reserves its width, so
   nothing is ever rendered underneath it; collapsing the dock gives the width
   straight back. This replaces the original overlay-without-a-gutter rule
   (B44): measured against real text, the floating dock covered readable
   content at every desktop width below ~2200px — craft's Energy/Success
   readouts, gather's yield labels, the dashboard's row chevrons. A reflow the
   player causes by opening chat is cheaper than words they cannot see.

   Scoped to .layout, not body, so the topbar stays full-bleed: the dock starts
   below it (top:3.375rem) and never covers it. Mobile keeps the same surface as
   a bottom sheet below, with no gutter. */
body:has(.chatdock[open]), body:has(.chatdock:not([open])) { padding-right:0; }
body:has(.chatdock[open]) .layout { padding-right:var(--dock-w); }

/* Chat runs the entire right side (owner, 2026-08-26): the dock is a full-height
   rail flush to the right and bottom edges, not a floating card inset from them.
   It still reserves its own width rather than overlaying — that is the B44 rule
   above, and the reason the layout is padded instead of covered.

   --shell-top is whatever sits above .layout. Both banners are conditional, so a
   single hard-coded offset cannot be right: the old fixed 3.375rem assumed the
   topbar alone and left the dock overlapping the beta banner whenever access was
   open. 2.375rem per banner is the same constant .layout's own min-height uses. */
body:has(.beta-banner), body:has(.guest-banner) { --shell-top:5.75rem; }
body:has(.beta-banner):has(.guest-banner) { --shell-top:8.125rem; }

.chatdock { position:fixed; top:var(--shell-top); right:0; bottom:0; width:var(--dock-w); background:var(--panel); border:0; border-left:0.0625rem solid var(--rule); border-radius:0; box-shadow:none; font-size:0.875rem; z-index:60; display:flex; flex-direction:column; }
.chatdock:not([open]) { width:8.5rem; bottom:auto; border-bottom:0.0625rem solid var(--rule); }
/* Chromium 131+ lays a <details> element's content out inside an anonymous
   ::details-content box. That box, not .cd-body, becomes the flex item here, so
   the chain from .chatdock down to .cd-feed breaks: the feed sizes to its whole
   backlog instead of to the dock, never scrolls (scrollHeight == clientHeight,
   which quietly makes chat.js's stick() a no-op), and the dock overflows by the
   height of the history. Making the anonymous box the flex column restores it.
   Engines without the pseudo-element drop this rule and keep the older layout,
   where .cd-body already was the flex item. */
.chatdock[open]::details-content { display:flex; flex-direction:column; flex:1; min-height:0; }
.chatdock > summary { flex:none; list-style:none; cursor:pointer; padding:0.5625rem 0.875rem; background:var(--panel); border-bottom:0.0625rem solid var(--line); display:flex; align-items:center; gap:0.5rem; font-family:var(--ui); font-size:0.6875rem; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); font-weight:700; }
.chatdock > summary::before { content:"−"; color:var(--accent); font-family:var(--mono); }
.chatdock:not([open]) > summary::before { content:"+"; }
.chatdock:not([open]) > summary { border-bottom:0; }
.chatdock:not([open]) .cd-on { display:none; }
.chatdock > summary::-webkit-details-marker { display:none; }
.chatdock .cd-on { margin-left:auto; font-family:var(--mono); font-size:0.6875rem; letter-spacing:0; text-transform:none; color:var(--muted); font-weight:400; display:flex; align-items:center; gap:0.3125rem; }
.chatdock .cd-pip { width:0.375rem; height:0.375rem; border-radius:50%; background:var(--faint); display:inline-block; }
.chatdock .cd-pip.on { background:var(--good); }
.chatdock .cd-body { flex:1; min-height:0; display:flex; flex-direction:column; padding:0.625rem 0.75rem; }
.chatdock .cd-tabs { flex:none; display:flex; gap:0.25rem; margin-bottom:0.5rem; flex-wrap:wrap; }
.chatdock .cd-tabs a { font-family:var(--ui); font-size:0.75rem; padding:0.125rem 0.5rem; border:0.0625rem solid var(--line); border-radius:var(--radius); background:var(--sink); color:var(--muted); }
.chatdock .cd-tabs a.active { background:var(--panel-2); border-color:var(--accent); color:var(--accent); font-weight:600; }
.chatdock .cd-dm-menu { position:relative; }
.chatdock .cd-dm-menu > summary { list-style:none; cursor:pointer; font-family:var(--ui); font-size:0.75rem; padding:0.125rem 0.5rem; border:0.0625rem solid var(--line); border-radius:var(--radius); background:var(--sink); color:var(--muted); }
.chatdock .cd-dm-menu > summary::-webkit-details-marker { display:none; }
.chatdock .cd-dm-menu[open] > summary { border-color:var(--accent); color:var(--accent); }
.chatdock .cd-dm-panel { position:absolute; z-index:5; top:calc(100% + 0.25rem); left:0; width:15.5rem; max-height:18rem; overflow:auto; padding:0.5rem; border:0.0625rem solid var(--rule); background:var(--panel-2); box-shadow:0 0.75rem 1.75rem rgba(0,0,0,.4); }
.chatdock .cd-dm-panel > a { display:flex; justify-content:space-between; margin-bottom:0.25rem; }
.chatdock .dm-start { display:flex; gap:0.375rem; margin-top:0.5rem; padding-top:0.5rem; border-top:0.0625rem solid var(--line); }
.chatdock .dm-start input { flex:1; min-width:0; }
.chatdock .cd-input { flex:none; display:flex; gap:0.375rem; margin-top:0.5rem; align-items:center; border-top:0.0625rem solid var(--line); padding-top:0.5rem; }
.chatdock .cd-input input[type=text] { flex:1; min-width:0; }
.chatdock .cd-tabs .cd-soon { font-family:var(--ui); font-size:0.75rem; padding:0.125rem 0.5rem; border:0.0625rem dashed var(--line); border-radius:var(--radius); color:var(--faint); background:transparent; cursor:default; }
.chatdock .cd-badge { display:inline-block; font-family:var(--mono); font-size:0.625rem; background:var(--accent); color:var(--accent-ink); border-radius:0.5rem; padding:0 0.3125rem; margin-left:0.1875rem; font-weight:700; vertical-align:0.0625rem; }

@media (min-width: 1081px) {
  .chatdock:not([open]) .cd-channel { display:none; }
}

/* Chat message rows — shared by the dock and polling feed endpoint. */
.cd-feed { list-style:none; margin:0; padding:0.125rem 0.125rem 0; flex:1; min-height:0; overflow-y:auto; }
.cmsg { margin:0 0 0.5rem; line-height:1.4; font-size:0.8125rem; }
.cmsg .cm-ts { font-family:var(--mono); color:var(--faint); font-size:0.6875rem; margin-right:0.375rem; }
.cmsg .cm-name { font-family:var(--mono); color:var(--accent); font-weight:700; font-size:0.75rem; margin-right:0.375rem; text-decoration:none; }
.cmsg a.cm-name:hover { text-decoration:underline; }
.cmsg.me .cm-name { color:var(--accent-2); }
.cmsg .cm-body { color:var(--text); word-break:break-word; }
.chat-empty { display:flex; min-height:12rem; align-items:center; justify-content:center; color:var(--faint); font-size:0.75rem; text-align:center; }

/* ---- responsive: keep balance across browser widths ---- */
@media (min-width: 1500px){
  :root { --dock-w:20.625rem; }
}
@media (max-width: 1080px){
  :root { --dock-w:min(20rem, calc(100vw - 1.5rem)); }
  /* !important to beat the inline `--tile-cols` on each grid — a 6-wide tile
     grid over-packs the narrowed content column in this band (icons spilling
     their tiles). Drops to 4 until the 760px mobile rule takes over at 3. */
  .tiles { --tile-cols:4 !important; }
}
@media (max-width: 760px){
  .layout { flex-direction: column; min-height: 0; }
  .sidebar { width: auto; border-right: 0; border-bottom: 0.0625rem solid var(--rule); padding: 0.5rem 0; }
  main.main { padding: 0.875rem 1rem; }
}

/* ===========================================================================
   WORLD — location chip (shell), region launcher (dashboard), travel strip
   (gather). Blueprint tokens only: drawn accent markers (no emoji), mono meta,
   hairline rows mirroring the .hub launcher + .tag language.
   =========================================================================== */

/* location chip — sidebar header showing where you stand; a drawn accent
   square stands in for a "you are here" pin (never an emoji). */
.locchip { padding:0 0.875rem 0.625rem; margin-bottom:0.5rem; border-bottom:0.0625rem solid var(--line-2); }
.locchip a { display:flex; align-items:center; gap:0.5rem; padding:0.375rem 0.5625rem; border:0.0625rem solid var(--line); background:var(--sink); border-radius:var(--radius); color:var(--text); font-family:var(--ui); font-size:0.75rem; letter-spacing:.03em; }
.locchip a:hover { border-color:var(--accent); text-decoration:none; }
.locchip a::before { content:""; width:0.5rem; height:0.5rem; flex:none; background:var(--accent); }
.locchip b { color:var(--accent-2); font-weight:600; }

/* region launcher (dashboard) — full-width rows shaped like the .hub launcher;
   the marker box carries HUB / depth code, not an emoji. */
.regions { border:0.0625rem solid var(--line); background:var(--panel); border-radius:var(--radius); overflow:hidden; }
.regions .region { border-top:0.0625rem solid var(--line-2); }
.regions .region:first-child { border-top:0; }
.regions form { margin:0; }
.region-go { -webkit-appearance:none; appearance:none; width:100%; display:flex; align-items:center; gap:0.875rem; padding:0.6875rem 1rem; background:transparent; border:0; color:var(--text); font:inherit; text-align:left; }
button.region-go { cursor:pointer; }
button.region-go:hover:not(:disabled) { background:var(--panel-2); }
button.region-go:disabled { cursor:default; }
.region-go .rmark { width:2.25rem; height:2.25rem; flex:none; border:0.0625rem solid var(--line); background:var(--sink); display:flex; align-items:center; justify-content:center; font-family:var(--mono); font-size:0.75rem; font-weight:700; letter-spacing:.02em; color:var(--accent); }
.region-go .rlab { flex:1; min-width:0; }
.region-go .rn { display:block; font-size:0.9375rem; font-weight:600; }
.region-go .rd { display:block; color:var(--muted); font-size:0.8125rem; margin-top:0.0625rem; }
.region-go .rgo { font-size:0.75rem; letter-spacing:.04em; color:var(--accent-2); white-space:nowrap; }
.region-go.here .rmark { border-color:var(--accent); background:rgba(95,176,194,.07); }
.region-go.here .rgo { color:var(--accent); }
.region-go.locked .rmark { border-style:dashed; color:var(--faint); }
.region-go.locked .rgo { color:var(--faint); }

/* travel strip (gather) — a labelled row of region chips; the current one
   carries the drawn accent square, locked ones go dashed + faint. */
.travel { display:flex; flex-wrap:wrap; align-items:center; gap:0.5rem; margin-bottom:0.875rem; }
.travel .tv-lbl { font-size:0.6875rem; letter-spacing:.14em; text-transform:uppercase; color:var(--faint); }
.travel form { margin:0; }
.tv-chip { display:inline-flex; align-items:center; gap:0.4375rem; font-size:0.8125rem; border:0.0625rem solid var(--line); background:var(--sink); border-radius:var(--radius); padding:0.1875rem 0.625rem; color:var(--muted); }
button.tv-chip { -webkit-appearance:none; appearance:none; font:inherit; cursor:pointer; color:var(--text); }
button.tv-chip:hover { border-color:var(--accent); color:var(--accent); text-decoration:none; }
.tv-chip.here { border-color:var(--accent); background:rgba(95,176,194,.07); color:var(--accent); }
/* Every chip reserves the marker slot; only the current one paints it, so
   entering a region doesn't resize that chip and shove its neighbours. */
.tv-chip::before { content:""; width:0.4375rem; height:0.4375rem; flex:none; background:transparent; }
.tv-chip.here::before { background:var(--accent); }
.tv-chip.lock { border-style:dashed; color:var(--faint); }

/* Batch sizes. The big action stays x1 and reads exactly as it always did; the
   ladder the skill has earned sits under it as chips, each its own submitter,
   so a bigger run is one tap and needs no JavaScript. `.act-group` is a
   fieldset only so htmx can disable every submitter in one go while a request
   is in flight (see gather.html). */
.acts .act-group { display:contents; border:0; padding:0; margin:0; min-width:0; }
.batch-row { display:flex; flex-wrap:wrap; align-items:center; gap:0.375rem; }
.acts .batch-row { padding:0 1rem 0.6875rem 3.625rem; }
.batch-lbl { font-size:0.6875rem; letter-spacing:.14em; text-transform:uppercase; color:var(--faint); }
.batch-chip { -webkit-appearance:none; appearance:none; font:inherit; font-family:var(--mono); font-size:0.75rem; line-height:1.4; cursor:pointer; border:0.0625rem solid var(--line); background:var(--sink); color:var(--text); border-radius:var(--radius); padding:0.1875rem 0.5rem; }
.batch-chip:hover:not(:disabled) { border-color:var(--accent); color:var(--accent); }
.batch-chip:disabled { opacity:.45; cursor:not-allowed; }

/* ===========================================================================
   APP ADDITIONS — runtime-only chrome the storyboard CSS doesn't carry:
   guest banner, Django messages, and the centered auth card (pre-game pages).
   Same Blueprint tokens; no new register.
   =========================================================================== */
.guest-banner { background:rgba(224,178,82,.1); border-bottom:0.0625rem solid var(--warn); color:var(--warn); font-size:0.8125rem; padding:0.4375rem 1rem; display:flex; gap:0.625rem; align-items:center; }
.guest-banner a { color:var(--warn); font-weight:600; text-decoration:underline; }

.beta-banner { background:rgba(95,176,194,.1); border-bottom:0.0625rem solid var(--accent); color:var(--accent-2); font-size:0.8125rem; padding:0.4375rem 1rem; text-align:center; }

.authcard form .errorlist { color:var(--bad); }
.authcard .field-error { color:var(--bad); font-size:0.8125rem; margin:-0.375rem 0 0.625rem; }

/* Gently scale the UI up on wide displays so content isn't stranded in a narrow
   column — but only gently: most 4K monitors run at 125–150% OS scaling, so the
   browser already sees ~2560 CSS px and applies its own magnification. Stacking a
   big bump on top of that made the UI look enormous. These are small bumps over
   the 110% base (font-size %, so all rem lengths scale together — same effect the
   old body{zoom:1.18/1.3} had), measured in CSS px (post-OS-scaling). */
@media (min-width: 1900px) { html { font-size: 118%; } }
@media (min-width: 2600px) { html { font-size: 130%; } }

/* Fixed toast overlay — sits above content so a post-action notice never
   reflows the layout (it used to render above .layout and push the sidebar down). */
.messages { position:fixed; left:50%; bottom:1.125rem; transform:translateX(-50%); z-index:200; width:min(35rem,86vw); list-style:none; margin:0; padding:0; pointer-events:none; }
.messages li { display:flex; align-items:center; gap:0.5rem; pointer-events:auto; border:0.0625rem solid var(--line); border-left:0.1875rem solid var(--accent); background:var(--panel); border-radius:var(--radius); padding:0.5625rem 0.75rem; margin-bottom:0.5rem; font-size:0.875rem; box-shadow:0 0.5rem 1.75rem rgba(0,0,0,.45); }
.messages li > span { flex:1; min-width:0; }
.messages li.error { border-left-color:var(--bad); color:var(--bad); }
.messages li.success { border-left-color:var(--good); }
.msg-close { flex:none; margin:-0.25rem -0.25rem -0.25rem 0; padding:0 0.3125rem; border:0; background:transparent; color:var(--muted); font-size:1.125rem; line-height:1.4; cursor:pointer; }
.msg-close:hover, .msg-close:focus-visible { color:var(--text); }

.authwrap { display:flex; justify-content:center; padding:3rem 1rem; }
.authcard { width:100%; max-width:23.75rem; border:0.0625rem solid var(--rule); background:var(--panel); border-radius:var(--radius); padding:1.375rem 1.5rem; }
.authcard h1 { font-size:1.25rem; margin-bottom:0.875rem; }
.authcard form p { margin:0 0 0.75rem; }
.authcard label { display:block; font-size:0.75rem; letter-spacing:.08em; text-transform:uppercase; color:var(--faint); margin-bottom:0.25rem; }
.authcard input { width:100%; background:var(--sink); border:0.0625rem solid var(--line); border-radius:var(--radius); color:var(--text); font-family:var(--ui); font-size:0.9375rem; padding:0.5rem 0.625rem; }
.authcard input:focus { outline:none; border-color:var(--accent); }
.authcard input:focus-visible { outline:0.125rem solid var(--accent); outline-offset:0.125rem; }
.authcard .errorlist { list-style:none; margin:0 0 0.625rem; padding:0; color:var(--bad); font-size:0.8125rem; }
.authcard .alt { margin-top:0.875rem; font-size:0.8125rem; color:var(--muted); }
.authcard .helptext, .authcard .helptext li { color:var(--faint); font-size:0.75rem; }

/* Shelter storage — current-state first, then compact transfer rows. The
   storage slice deliberately has no decorative station furniture. */
.shelter-status { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:0.5rem; margin:0 0 0.75rem; }
.shelter-stat { min-width:0; display:flex; flex-direction:column; gap:0.0625rem; padding:0.5625rem 0.6875rem; border:0.0625rem solid var(--line); background:var(--sink); }
.shelter-stat > strong { overflow-wrap:anywhere; font-size:0.9375rem; }
.shelter-stat-label { color:var(--faint); font-size:0.625rem; letter-spacing:.1em; text-transform:uppercase; }
.shelter-layout { display:grid; grid-template-columns:1fr 1fr; gap:0.75rem; align-items:start; }
.shelter-panel { min-width:0; margin-top:0; }
.transfer-list { display:flex; flex-direction:column; gap:0.375rem; }
.transfer-row { min-width:0; display:grid; grid-template-columns:minmax(7.5rem,1fr) auto; align-items:center; gap:0.5625rem; padding:0.5rem 0.5625rem; border:0.0625rem solid var(--line-2); background:var(--panel-2); }
.transfer-identity { min-width:0; }
.transfer-identity > .item-ref { width:100%; }
.transfer-form { display:flex; align-items:center; gap:0.3125rem; }
.transfer-form input[type="number"] { width:3.625rem; font-family:var(--mono); text-align:right; }
.shelter-upgrade { margin-top:0; }
.upgrade-layout { display:grid; grid-template-columns:minmax(11.25rem,1fr) minmax(11.875rem,auto) auto; gap:0.875rem; align-items:center; }
.upgrade-layout h3 { font-size:0.9375rem; }
.upgrade-layout p { margin:0.1875rem 0 0; }
.upgrade-cost { display:flex; flex-wrap:wrap; gap:0.3125rem; }
.upgrade-cost > span { padding:0.1875rem 0.4375rem; border:0.0625rem solid var(--line); background:var(--sink); color:var(--muted); font-size:0.6875rem; }
.upgrade-cost > span.short { border-color:var(--bad); color:var(--bad); }
.sr-only { position:absolute !important; width:0.0625rem !important; height:0.0625rem !important; padding:0 !important; margin:-0.0625rem !important; overflow:hidden !important; clip:rect(0,0,0,0) !important; white-space:nowrap !important; border:0 !important; }

/* Global item acquisition plan — one dependency language for crafting,
   construction, deliveries, and future requirement surfaces. */
.acquisition-plan { border-left:0.1875rem solid var(--accent); }
.acq-quantity { display:flex; flex-wrap:wrap; align-items:center; gap:0.4375rem; margin:0 0 0.625rem; padding-bottom:0.625rem; border-bottom:0.0625rem solid var(--line-2); }
.acq-quantity label { color:var(--faint); font-size:0.6875rem; text-transform:uppercase; letter-spacing:.08em; }
.acq-quantity input { width:5.125rem; }
.acq-quantity .muted { margin-left:auto; max-width:33.75rem; }
.acq-node { min-width:0; padding:0.5rem 0 0 0.625rem; border-left:0.125rem solid var(--line); }
.acq-node.complete { border-left-color:var(--good); }
.acq-head { min-width:0; display:grid; grid-template-columns:minmax(7.5rem,1fr) auto; align-items:center; gap:0.5rem; }
.acq-count { min-width:0; display:flex; flex-direction:column; text-align:right; }
.acq-count small { color:var(--faint); font-size:0.625rem; }
.yield-item { display:inline-flex; align-items:center; gap:0.3125rem; padding:0.1875rem 0.3125rem; border:0.0625rem solid var(--line-2); border-radius:var(--radius); background:var(--sink); }
.yield-item.rare { border-style:dashed; }
.yield-chance { color:var(--muted); font-family:var(--mono); font-size:0.625rem; }
.acq-route, .acq-source { display:flex; align-items:center; gap:0.5625rem; margin:0.4375rem 0 0 2rem; padding:0.4375rem 0.5rem; border:0.0625rem solid var(--line-2); background:var(--sink); }
.acq-route-main { min-width:0; flex:1; display:flex; flex-wrap:wrap; align-items:baseline; gap:0.25rem 0.5rem; }
.acq-route-main .muted { flex-basis:100%; font-size:0.6875rem; }
.route-kind { display:inline-block; padding:0.0625rem 0.375rem; border:0.0625rem solid var(--accent); color:var(--accent); font-size:0.625rem; text-transform:uppercase; letter-spacing:.06em; }
.acq-children { margin:0.3125rem 0 0 1.375rem; }
.acq-sources { margin-top:0.3125rem; }
.acq-source > span:first-child { min-width:0; flex:1; }
.acq-source small { display:block; color:var(--muted); font-size:0.6875rem; margin-top:0.125rem; }
.acq-source form { margin:0; }
.acq-warning, .acq-synthons { margin:0.375rem 0 0 2rem; padding:0.375rem 0.5rem; border-left:0.125rem solid var(--warn); background:rgba(224,178,82,.06); color:var(--warn); font-size:0.75rem; }
.acq-synthons { border-left-color:var(--accent); color:var(--muted); }
.acq-alternatives { margin:0.375rem 0 0 2rem; color:var(--muted); font-size:0.75rem; }
.acq-alternatives summary { cursor:pointer; color:var(--accent); }
.acq-alternatives a { display:block; margin:0.1875rem 0 0 0.625rem; }
.acq-locked { color:var(--warn); font-size:0.6875rem; }


/* Characters in place — the inline conversation panel (_character.html).
   A character renders where they stand (gather site / hub); Talk expands the
   panel in place. See docs/mechanics/characters-and-conversation.md. */
.npc-row { display:flex; flex-wrap:wrap; gap:0.625rem; margin-top:0.625rem; }
/* Canonical NPC card: a framed portrait + a content column. One shape for every
   NPC surface (gather rail, quests giver, future bench master) so people read as
   the same kind of thing wherever they appear. See _npc_photo.html. */
.npc { --npc-accent: var(--accent); display:flex; gap:0.75rem; flex:1 1 20rem; max-width:35rem;
  border:0.0625rem solid var(--line); border-left:0.1875rem solid var(--npc-accent);
  border-radius:var(--radius); background:var(--sink); padding:0.625rem 0.75rem; }
.npc-main { min-width:0; flex:1; }
.npc-giver { display:flex; gap:0.75rem; margin-bottom:0.75rem; }
/* Framed generic figure — the shared avatar. */
.npc-photo { position:relative; width:3rem; height:3.9rem; flex:none; overflow:hidden;
  border:0.0625rem solid var(--line); border-radius:calc(var(--radius) - 0.0625rem);
  background:
    repeating-linear-gradient(0deg, rgba(150,170,180,.05) 0 0.0625rem, transparent 0.0625rem 0.75rem),
    repeating-linear-gradient(90deg, rgba(150,170,180,.05) 0 0.0625rem, transparent 0.0625rem 0.75rem),
    var(--panel-2); }
.npc-photo svg { position:absolute; inset:0; width:100%; height:100%; stroke:var(--faint); stroke-width:1.6; opacity:.75; }
.npc-photo__ref { position:absolute; left:0; right:0; bottom:0; font-family:var(--mono); font-size:0.5rem;
  letter-spacing:.06em; text-align:center; color:var(--muted); background:rgba(10,26,41,.82); }
.npc-head { display:flex; align-items:baseline; flex-wrap:wrap; gap:0.375rem 0.5rem; }
.npc-name { font-weight:600; }
.npc-title { color:var(--faint); font-size:0.75rem; letter-spacing:.06em; text-transform:uppercase; }
/* Collective grade mark: the band frame carries the character's accent, the
   numeral is data so it stays mono via `.num`. Sized here — `.gl` has no base. */
/* The collective-grade mark. Same geometry wherever it appears — beside an NPC
   in place, or beside your own name on a profile — so the ladder reads as one
   scale. Only the ink differs: an NPC takes their accent, you take the default. */
.npc-grade, .grade-mark { display:inline-flex; align-items:center; gap:0.1875rem; align-self:center;
  color:var(--npc-accent,var(--faint)); }
.npc-grade .gl-grade, .grade-mark .gl-grade { width:1.0625rem; height:1.0625rem; flex:none; }
.npc-grade .num, .grade-mark .num { font-size:0.6875rem; letter-spacing:.04em; color:var(--faint); }
.npc-flag { color:var(--warn); font-weight:700; font-family:var(--mono); }
.npc-talk { margin-left:auto; }
.npc-blurb { margin:0.375rem 0 0; font-size:0.8125rem; }
.npc-line { margin:0.5rem 0 0; font-style:italic; color:var(--text); }
.npc-progress { display:flex; align-items:center; gap:0.5rem; margin-top:0.5rem; font-size:0.75rem; }
.npc-progress .bar { flex:1; }
.npc-actions { display:flex; align-items:center; gap:0.625rem; margin-top:0.625rem; }
.npc-reward { font-size:0.75rem; }

/* Mobile application shell. The desktop rail and sidebar remain the canonical
   wide-screen navigation; these controls expose the same destinations at phone
   widths without introducing a second information architecture. */
.mobile-tabs, .mobile-only { display:none; }
.chatdock:not([open]) > .cd-body,
.mobile-more:not([open]) > .mobile-more-sheet { display:none; }

@media (max-width: 760px) {
  .item-ref__preview { position:fixed; z-index:80; left:1rem; right:1rem; top:auto; bottom:calc(var(--mobile-tabs-h) + var(--mobile-chat-h) + 0.5rem + env(safe-area-inset-bottom)); width:auto; max-height:45vh; overflow:auto; }
  :root {
    --mobile-tabs-h:4rem; --mobile-chat-h:2.75rem;
    /* The tab bar and the dock are fixed to the bottom, so anything scrolled
       *just* into view lands underneath them — the browser stops as soon as the
       element is technically visible. Scroll padding moves that stopping point
       above the bars, for anchor links, focus moves and scrollIntoView alike. */
    scroll-padding-bottom:calc(var(--mobile-tabs-h) + var(--mobile-chat-h) + 1rem
      + env(safe-area-inset-bottom));
  }
  html { font-size:100%; }
  html, body { max-width:100%; overflow-x:clip; }
  body, body:has(.chatdock[open]), body:has(.chatdock:not([open])) { padding-right:0; }
  /* The phone dock is a bottom sheet, not a side column — no gutter to reserve. */
  body:has(.chatdock[open]) .layout { padding-right:0; }

  .topbar {
    position:sticky; top:0; z-index:55; gap:0.5rem; min-height:2.875rem;
    padding:0.375rem 0.625rem;
  }
  .brand { display:flex; align-items:center; flex:none; min-height:2.75rem; font-size:0.9375rem; }
  .brand .logo { margin-right:0.375rem; }
  .chips { min-width:0; gap:0.3125rem; font-size:0.75rem; }
  .chip { white-space:nowrap; padding:0.1875rem 0.375rem; }
  .chip.health, .chip.acct, .chip.logout { display:none; }
  .chip.energy { min-width:0; gap:0.25rem; padding:0.1875rem 0.375rem; }
  .chip.energy .ebar { width:2.125rem; }

  .layout { display:block; min-height:0; }
  .sidebar { display:none; }
  main.main {
    width:100%; max-width:none; padding:0.875rem 0.75rem;
    padding-bottom:calc(var(--mobile-tabs-h) + var(--mobile-chat-h) + 1.5rem + env(safe-area-inset-bottom));
  }
  .page-title { font-size:1.375rem; }
  .page-sub { margin-bottom:0.8125rem; }
  .box { padding:0.6875rem 0.625rem; }

  /* The screen intro is desktop-only (owner, 2026-08-25). On a phone it spent a
     row explaining a screen the player is already looking at, so the whole
     disclosure goes rather than sitting there collapsed. The markup stays — it
     is the desktop copy, and data-mobile-collapse still reads it. */
  .mobile-intro { display:none; }
  .place-summary {
    display:flex; align-items:center; min-height:2.75rem; list-style:none; cursor:pointer;
    color:var(--accent); font-size:0.8125rem; font-weight:600;
  }
  .place-summary::-webkit-details-marker { display:none; }
  .place-summary::after {
    content:"+"; margin-left:auto; font-family:var(--mono); color:var(--accent);
  }
  .place[open] > .place-summary::after { content:"\2212"; }
  .disclosure-summary { min-height:2.75rem; margin:-0.4375rem 0 0.4375rem; }
  .mobile-disclosure:not([open]) { margin-bottom:0.5rem; }

  .dashboard-hub {
    display:grid; grid-template-columns:repeat(2, minmax(0, 1fr));
  }
  .dashboard-hub a {
    min-width:0; min-height:3.625rem; padding:0.5rem; gap:0.5rem;
    border-top:0.0625rem solid var(--line-2); border-left:0.0625rem solid var(--line-2);
  }
  .dashboard-hub a:nth-child(-n+2) { border-top:0; }
  .dashboard-hub a:nth-child(odd) { border-left:0; }
  .dashboard-hub .ic { width:1.875rem; height:1.875rem; }
  .dashboard-hub .lab .t { font-size:0.875rem; }
  .dashboard-hub .lab .d, .dashboard-hub .chev { display:none; }
  .dashboard-stats { grid-template-columns:repeat(3, minmax(0, 1fr)); gap:0.375rem; }
  .dashboard-stats .box { min-width:0; padding:0.5625rem 0.5rem; }
  .dashboard-stats .box > h3 { margin-bottom:0.375rem; font-size:0.625rem; }
  .dashboard-stats p { font-size:0.6875rem !important; overflow-wrap:anywhere; }
  .dashboard-stats a { font-size:0.6875rem; }

  details.place { padding-bottom:0; }
  details.place:not([open]) { margin-bottom:0.5rem; }
  .place-summary {
    padding:0 0.625rem; border:0.0625rem solid var(--line); background:var(--panel);
  }
  .place-summary .summary-meta {
    margin-left:auto; margin-right:0.625rem; color:var(--muted); font-family:var(--mono);
    font-size:0.6875rem; font-weight:400;
  }

  .mobile-tabs {
    position:fixed; left:0; right:0; bottom:0; z-index:100;
    height:calc(var(--mobile-tabs-h) + env(safe-area-inset-bottom));
    padding-bottom:env(safe-area-inset-bottom);
    display:grid; grid-template-columns:repeat(5, minmax(0, 1fr));
    background:var(--sink); border-top:0.0625rem solid var(--rule);
  }
  .mobile-tabs > a, .mobile-more > summary {
    min-width:0; height:var(--mobile-tabs-h); padding:0.4375rem 0.125rem 0.3125rem;
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:0.125rem; color:var(--muted); font-size:0.6875rem; line-height:1.1;
    list-style:none; border-left:0.0625rem solid var(--line-2); text-decoration:none;
  }
  .mobile-tabs > a:first-child { border-left:0; }
  .mobile-tabs > a.active { color:var(--accent); box-shadow:inset 0 0.1875rem 0 var(--accent); }
  .mobile-tabs .mt-code {
    position:relative; font-family:var(--mono); font-size:0.75rem; font-weight:700;
    letter-spacing:.04em; color:var(--accent-2);
  }
  .mobile-tabs > a.active .mt-code { color:var(--accent); }
  .mobile-more { min-width:0; }
  .mobile-more > summary::-webkit-details-marker { display:none; }
  .mobile-more[open] > summary { color:var(--accent); box-shadow:inset 0 0.1875rem 0 var(--accent); }
  .unread-dot {
    position:absolute; width:0.4375rem; height:0.4375rem; right:-0.4375rem; top:-0.1875rem;
    border-radius:50%; background:var(--warn);
  }
  .mobile-more-sheet {
    position:fixed; z-index:95; left:0; right:0;
    bottom:calc(var(--mobile-tabs-h) + env(safe-area-inset-bottom));
    max-height:calc(100dvh - var(--mobile-tabs-h) - 2.875rem);
    overflow-y:auto; overscroll-behavior:contain;
    padding:0.75rem 0.875rem 1.125rem; background:var(--panel);
    border-top:0.0625rem solid var(--rule); box-shadow:0 -1.125rem 2.5rem rgba(0,0,0,.45);
  }
  .mobile-account {
    display:flex; align-items:center; gap:0.75rem; min-height:2.75rem;
    padding:0 0.125rem 0.625rem; margin-bottom:0.625rem; border-bottom:0.0625rem solid var(--line);
    font-size:0.75rem;
  }
  /* Direct children only — the readouts wrap their numbers in a `.num` span,
     which must not inherit the auto margin that spaces the strip. */
  .mobile-account > span { margin-left:auto; color:var(--good); }
  .mobile-account a { display:flex; align-items:center; min-height:2.75rem; }
  .mobile-nav { display:grid; grid-template-columns:1fr 1fr; gap:0.25rem 1rem; }
  .mobile-nav .locchip { grid-column:1 / -1; padding:0 0 0.5rem; }
  .mobile-nav .navgroup { margin:0 0 0.5rem; }
  .mobile-nav .navgroup .ng-label { padding:0 0.25rem; }
  .mobile-nav .navgroup a { display:flex; align-items:center; min-height:2.75rem; padding:0.4375rem 0.5rem; color:var(--text); }
  .mobile-nav .navgroup a.active { background:var(--panel-2); box-shadow:inset 0.1875rem 0 0 var(--accent); }

  .chatdock {
    position:fixed; z-index:80; left:0; right:0;
    bottom:calc(var(--mobile-tabs-h) + env(safe-area-inset-bottom));
    top:auto; width:auto; height:var(--mobile-chat-h); min-height:var(--mobile-chat-h);
    border:0; border-top:0.0625rem solid var(--rule); display:flex;
    box-shadow:0 -0.375rem 1.125rem rgba(0,0,0,.22);
  }
  .chatdock[open] { height:min(72dvh, 36rem); }
  .chatdock:not([open]) { width:auto; bottom:calc(var(--mobile-tabs-h) + env(safe-area-inset-bottom)); }
  .chatdock > summary { min-height:var(--mobile-chat-h); padding:0.4375rem 0.75rem; }
  .chatdock[open] > summary { border-bottom:0.0625rem solid var(--line); }
  .chatdock .cd-body { padding:0.5625rem 0.625rem; }
  .chatdock .cd-feed { flex:1; height:auto; }
  .chatdock .cd-input { margin-top:0.3125rem; }
  .chatdock .cd-dm-panel { position:fixed; top:auto; left:0.625rem; right:0.625rem; bottom:calc(var(--mobile-tabs-h) + var(--mobile-chat-h) + 0.5rem); width:auto; max-height:45dvh; }
  .messages { top:calc(2.875rem + 0.5rem + env(safe-area-inset-top)); bottom:auto; }

  input[type="search"], input[type="text"], input[type="email"],
  input[type="password"], input[type="number"], select, textarea,
  .authcard input { min-height:2.75rem; font-size:1rem; }
  .btn, .btn.sm, button.gather-act, .tabs a, .modes a,
  .rl-row, .tv-chip, .region-go, .hub a, .prov-row button, .batch-chip {
    min-height:2.75rem; touch-action:manipulation;
  }
  /* The chips wrap rather than scroll sideways: at 320px a x1/x5 row fits, and
     a longer ladder folds onto a second line instead of hiding runs off-screen. */
  .batch-chip { padding-left:0.6875rem; padding-right:0.6875rem; }
  /* inline-flex makes a whitespace-only text node between two children an
     anonymous flex item, and those are never rendered — so a button written as
     `Label <span>0/7</span>` silently lost its space on phones only, reading
     "Consumable0/7". The gap restores the separation for every such button
     rather than patching each template. */
  .btn, .btn.sm { display:inline-flex; align-items:center; justify-content:center; gap:0.3125rem; padding:0.5rem 0.6875rem; }
  a, button, summary { touch-action:manipulation; }
  :where(a, button, summary):active:not(:disabled) { filter:brightness(1.22); }

  table.wf {
    display:block; max-width:100%; overflow-x:auto; overscroll-behavior-inline:contain;
    white-space:nowrap; -webkit-overflow-scrolling:touch;
  }
  .tiles { --tile-cols:3 !important; }
  .skill-row { min-height:2.75rem; }
  .skill-tiles { grid-template-columns:repeat(2, minmax(0,1fr)); }
  .tabs, .modes { flex-wrap:nowrap; overflow-x:auto; overscroll-behavior-inline:contain; }
  .tabs > *, .modes > * { flex:none; }
  .statline { overflow-x:auto; }
  .travel { flex-wrap:nowrap; overflow-x:auto; padding-bottom:0.3125rem; }
  .travel .tv-lbl, .travel form, .travel .tv-chip { flex:none; }
  .gather-act .ga-skill, .gather-act .yield { padding-left:0; }
  .ga-skill { flex-wrap:wrap; }
  .ga-skill .bar { min-width:5.625rem; }
  .yield .ylabel.sep { margin-left:0; padding-left:0; border-left:0; }
  .brk-row { flex-wrap:wrap; align-items:flex-start; gap:0.5625rem; }
  .brk-row .src { min-width:0; flex:1 1 11.875rem; }
  .brk-row .out { flex:1 1 100%; }
  .brk-row > .btn, .brk-row > form { width:100%; margin-left:0; }
  .brk-row > form .btn { width:100%; }
  .prov-row { gap:0.5rem; }
  .prov-row .lab .d { display:block; }
  .inventory-filters > * { flex:1 1 8.75rem; }
  .inventory-filters input[type="search"] { width:100% !important; }
  .inventory-filters > button.btn, .inventory-filters > a.btn { flex-basis:100%; }
  .inventory-filters .filter-count { flex-basis:100%; margin-left:0 !important; }
  .shelter-status { grid-template-columns:repeat(3,minmax(0,1fr)); gap:0.3125rem; }
  .shelter-stat { padding:0.4375rem 0.375rem; }
  .shelter-stat > strong { font-size:0.8125rem; }
  .shelter-stat .muted { font-size:0.625rem; line-height:1.25; }
  .shelter-layout { display:block; }
  .transfer-row { grid-template-columns:minmax(0,1fr); gap:0.4375rem; }
  .transfer-form { grid-column:1 / -1; width:100%; }
  .transfer-form input[type="number"] { flex:0 0 4.5rem; width:4.5rem; min-height:2.75rem; }
  .transfer-form .btn { flex:1; }
  .upgrade-layout { grid-template-columns:1fr; gap:0.625rem; }
  .upgrade-layout form, .upgrade-layout form .btn { width:100%; }
  .acq-quantity > * { flex:1 1 auto; }
  .acq-quantity input { flex:0 0 4.875rem; min-height:2.75rem; }
  .acq-quantity .muted { flex-basis:100%; margin-left:0; }
  .acq-node { padding-left:0.4375rem; }
  .acq-children { margin-left:0.5rem; }
  .acq-route, .acq-source { margin-left:0; align-items:flex-start; flex-wrap:wrap; }
  .acq-route > .btn, .acq-source > .btn, .acq-source > form, .acq-source > form .btn { width:100%; }
  .acq-warning, .acq-synthons, .acq-alternatives { margin-left:0; }
  .btn-row > input, .btn-row > select { min-width:0; flex:1 1 7.5rem; }
  .btn-row > button.btn, .btn-row > a.btn { flex:1 1 100%; }
  .quest-row { display:grid; grid-template-columns:minmax(0,1fr); }
  .quest-row .src {
    width:100%; min-width:0; display:flex; flex-direction:column;
    align-items:flex-start; gap:0.25rem;
  }
  .quest-row .src .nm { min-width:0; max-width:100%; display:flex; flex-wrap:wrap; align-items:center; gap:0.25rem; }
  .quest-row .src .d { display:block; width:100%; min-width:0; overflow-wrap:anywhere; }
  .quest-row .qprog { width:100%; min-width:0; }
  .quest-row .qprog .bar { width:auto; flex:1; }
  .quest-row .rw, .quest-row .qact { width:100%; min-width:0; }
  .quest-row .qact { margin-left:0; text-align:left !important; }
  .quest-row .qact form { display:flex !important; flex-direction:column; width:100%; }
  .quest-row .qact .btn { width:100%; }
  .insight-gauges { display:grid; grid-template-columns:1fr; }
  .insight-gauge { min-width:0; }
  .cd-soon::after, .mode-soon::after { content:" · soon"; color:var(--faint); }
  .npc-flag::after { content:" task"; font-family:var(--ui); font-size:0.6875rem; }

  .recipe-browser { display:flex; flex-direction:column; }
  .recipe-browser .datasheet { order:1; width:100%; }
  .recipe-browser .recipe-list { order:2; width:100%; }
  .recipe-browser:not(.browse-only) .recipe-list { display:none; }
  .recipe-browser.browse-only .recipe-list { order:1; }
  .recipe-browser.browse-only .empty-sheet { display:none; }
  .item-table .item-col-kind, .item-table .item-col-effect { display:none; }
  .item-table { display:table !important; overflow:visible !important; white-space:normal !important; }
  .item-table th, .item-table td { padding-left:0.375rem; padding-right:0.375rem; }
  .item-ref__info {
    width:2.75rem; height:2.75rem; margin-left:0.1875rem;
    font-size:0.75rem; line-height:2.75rem;
  }
  .item-ref__info::before { inset:0; }
  .item-ref--tile .item-ref__info { top:-0.6875rem; right:max(-0.5rem, calc(50% - 2.75rem)); }
  .mobile-back { align-items:center; min-height:2.75rem; margin:0 0 0.5rem; }
  .action-reason { color:var(--warn); font-size:0.6875rem; margin-top:0.25rem; justify-content:center; }
  .mobile-only { display:flex; }
  .desktop-only { display:none !important; }
}

@media (max-width: 390px) {
  .brand-name { display:none; }
  .mobile-nav { grid-template-columns:1fr; }
  /* At 320px the retained table columns made multi-word item names nearly
     vertical. Keep the same semantic table, but present each row as a compact
     item/tier line with its source below. */
  .item-table { display:block !important; }
  .item-table thead { display:none; }
  .item-table tbody { display:flex; flex-direction:column; }
  .item-table tr {
    display:grid; grid-template-columns:minmax(0,1fr) auto;
    border:0.0625rem solid var(--line); border-bottom:0;
  }
  .item-table tr:last-child { border-bottom:0.0625rem solid var(--line); }
  .item-table td { display:flex; align-items:center; min-width:0; border:0 !important; }
  .item-table td:first-child { padding-right:0; }
  .item-table td:first-child .item-ref { width:100%; }
  .item-table .item-col-tier { justify-content:flex-end; color:var(--accent-2); font-family:var(--mono); }
  .item-table .item-col-source {
    grid-column:1 / -1; gap:0.5rem; padding-top:0.25rem;
    border-top:0.0625rem solid var(--line-2) !important;
  }
  .item-table .item-col-source::before {
    content:"Source"; color:var(--faint); font-family:var(--ui);
    font-size:0.625rem; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  }
}

/* Trading Post — offer cards, ledger, and compose (docs/mechanics/market-screen.md).
   Bundles render only via _bundle.html: glyph swatches + a mono gold chip. */
.market-board-meta, .market-listing-meta, .market-list-fields {
  min-width:0; display:flex; flex-wrap:wrap; align-items:center; justify-content:flex-end; gap:0.3125rem 0.5rem;
}
.market-buy { display:flex; align-items:center; gap:0.3125rem; }
.market-buy input[type="number"], .market-list-fields input[type="number"] { width:4.25rem; font-family:var(--mono); text-align:right; }
.market-list-fields select { max-width:8.75rem; }
.market-status {
  display:flex; align-items:baseline; flex-wrap:wrap; gap:0.375rem 0.75rem;
  margin:0 0 0.875rem; padding:0.5625rem 0.75rem; border:0.0625rem solid var(--line);
  border-left:0.1875rem solid var(--accent); background:var(--panel-2); font-size:0.8125rem;
}
.market-status strong { text-transform:uppercase; letter-spacing:.08em; }
.market-status span { color:var(--muted); }
.market-status.status-available, .market-status.status-open { border-left-color:var(--good); }
.market-status.status-offline, .market-status.status-expired { border-left-color:var(--warn); }
.market-status.status-cancelled, .market-status.status-exhausted,
.market-status.status-closed, .market-status.status-withdrawn { border-left-color:var(--faint); }
.market-detail-grid > *, .market-deal-grid > * { min-width:0; }
.market-listing-dossier { margin-top:0; }
.market-detail-item { margin-bottom:0.75rem; }
.market-detail-item > .item-ref { width:100%; }
.market-detail-stats { margin-bottom:0.625rem; }
.market-seller-line { margin:0; }
.market-primary-action { border-top:0.125rem solid var(--accent); }
.market-primary-action h2, .market-withdraw h2 { font-size:1rem; margin-bottom:0.5rem; }
.market-deal-title { display:flex; align-items:flex-end; justify-content:space-between; gap:0.75rem; margin-bottom:0.625rem; }
.market-deal-title .page-title { margin-bottom:0; }
.market-eyebrow { display:block; margin-bottom:0.1875rem; color:var(--faint); font-size:0.6875rem; font-weight:650; letter-spacing:.1em; text-transform:uppercase; }
.market-section-head { display:flex; align-items:flex-start; justify-content:space-between; gap:0.75rem; margin-bottom:0.75rem; }
.market-section-head h2 { margin:0; font-size:1rem; }
.market-terms-focus { display:flex; align-items:center; justify-content:space-between; gap:1rem; padding:0.75rem; margin-bottom:0.625rem; border:0.0625rem solid var(--line); background:var(--sink); }
.market-terms-focus .market-detail-item { flex:1; margin:0; min-width:0; }
.market-terms-price { flex:none; display:flex; flex-direction:column; align-items:flex-end; text-align:right; }
.market-terms-price > .num { color:var(--accent-2); font-size:1.75rem; line-height:1.1; font-weight:650; }
.market-terms-price small { color:var(--muted); font-size:0.6875rem; }
.market-confirm-flow { display:grid; grid-template-columns:minmax(0,1fr) auto minmax(0,1fr) auto minmax(0,1fr); gap:0.375rem; align-items:stretch; margin-top:0.75rem; }
.market-confirm-node { display:flex; flex-direction:column; min-width:0; padding:0.5625rem 0.625rem; border:0.0625rem solid var(--line); background:var(--panel-2); }
.market-confirm-node strong { overflow-wrap:anywhere; }
.market-confirm-node > span:last-child { color:var(--muted); font-size:0.6875rem; }
.market-confirm-node.is-complete { border-color:var(--good); box-shadow:inset 0.125rem 0 0 var(--good); }
.market-confirm-node.is-complete > span:last-child { color:var(--good); }
.market-confirm-role { color:var(--faint); font-size:0.625rem; font-weight:650; letter-spacing:.08em; text-transform:uppercase; }
.market-confirm-join { align-self:center; color:var(--faint); }
.market-confirm-result { background:var(--sink); }
.market-deal-action > form > .btn { width:100%; min-height:2.75rem; }
.market-consequence { padding:0.5625rem 0.625rem; border-left:0.125rem solid var(--warn); background:var(--sink); font-size:0.8125rem; }
.market-counter { margin-top:0.875rem; padding-top:0.625rem; border-top:0.0625rem solid var(--line); }
.market-counter > summary { margin:0; }
.market-counter[open] > summary { margin-bottom:0.5rem; }
.market-delivery-eta { color:var(--accent-2); font-weight:650; }
.market-action-form, .market-terms-form { display:flex; flex-direction:column; align-items:stretch; gap:0.4375rem; }
/* The field label is a label; only the number the player types stays mono. */
.market-action-form label, .market-terms-form label {
  color:var(--faint); font-size:0.6875rem; letter-spacing:.08em; text-transform:uppercase;
}
.market-action-form input[type="number"], .market-terms-form input[type="number"] { width:100%; font-family:var(--mono); }
.market-action-form .field-error { margin:0; color:var(--bad); font-size:0.75rem; }
.market-linked-deals { display:flex; flex-direction:column; gap:0.375rem; margin-top:0.875rem; padding-top:0.75rem; border-top:0.0625rem solid var(--line-2); }
.market-linked-deals h3 { margin:0; font-size:0.8125rem; }
.market-price-context > p { display:flex; justify-content:space-between; gap:0.75rem; margin:0 0 0.5rem; }
.market-sale-row, .market-revision-row { grid-template-columns:minmax(5.625rem,1fr) auto auto; }
.market-channel-nav { display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:0.625rem; margin-bottom:0.75rem; }
.market-channel-nav > a { display:flex; flex-direction:column; gap:0.125rem; min-width:0; padding:0.625rem 0.75rem; border:0.0625rem solid var(--line); background:var(--panel-2); color:var(--text); }
.market-channel-nav > a:hover { border-color:var(--accent); text-decoration:none; }
.market-channel-nav > a span { color:var(--muted); font-size:0.75rem; }
.market-channel-heading { scroll-margin-top:1rem; display:flex; align-items:center; justify-content:space-between; gap:1rem; margin:1rem 0 0.75rem; padding:0.75rem; border-top:0.125rem solid var(--warn); background:var(--panel-2); }
.market-channel-heading h2, .market-channel-heading p { margin:0; }
.market-channel-heading p { max-width:48rem; color:var(--muted); font-size:0.8125rem; }
.offer-card { border:0.0625rem solid var(--line-2); background:var(--panel-2); border-radius:var(--radius); padding:0.625rem 0.75rem; margin-bottom:0.5625rem; }
.offer-head { display:flex; align-items:baseline; gap:0.375rem; flex-wrap:wrap; font-size:0.875rem; }
.offer-when { margin-left:auto; color:var(--faint); font-size:0.6875rem; }
.offer-sides { display:grid; grid-template-columns:1fr auto 1fr; gap:0.625rem; align-items:stretch; margin:0.5625rem 0 0.125rem; }
.offer-side { min-width:0; padding:0.4375rem 0.5625rem; border:0.0625rem solid var(--line-2); background:var(--sink); }
.offer-side-label { display:block; color:var(--faint); font-size:0.625rem; letter-spacing:.1em; text-transform:uppercase; margin-bottom:0.3125rem; }
.offer-swap { align-self:center; color:var(--faint); font-size:1rem; }
.offer-swap.sm { font-size:0.75rem; }
.offer-actions { margin:0.5rem 0 0; }
.bundle { display:inline-flex; flex-wrap:wrap; gap:0.25rem 0.625rem; align-items:center; min-width:0; }
.bundle-gold { padding:0.0625rem 0.4375rem; border:0.0625rem solid var(--warn); color:var(--warn); background:var(--sink); font-size:0.75rem; }
.bundle-empty { font-style:italic; }

.ledger { display:flex; flex-direction:column; }
.ledger-row { display:grid; grid-template-columns:minmax(5.625rem,8.125rem) 1fr auto; gap:0.625rem; align-items:center; padding:0.4375rem 0; border-bottom:0.0625rem solid var(--line-2); font-size:0.8125rem; }
.ledger-row:last-child { border-bottom:0; }
.ledger-who { min-width:0; }
.ledger-bundles { min-width:0; display:flex; flex-wrap:wrap; align-items:center; gap:0.25rem 0.5rem; }
.ledger-status { text-align:right; }
.ledger-note { display:block; font-size:0.6875rem; }
.tag.status-accepted { border-color:var(--good); color:var(--good); }
.tag.status-declined, .tag.status-cancelled { color:var(--faint); }

/* presence dot — the design-system online indicator (replaces emoji circles) */
.presence { display:inline-block; width:0.5rem; height:0.5rem; border-radius:50%; background:var(--line); border:0.0625rem solid var(--rule); vertical-align:baseline; margin-right:0.1875rem; }
.presence.on { background:var(--good); border-color:var(--good); }

/* Skills — the shared labeled-tile language applied to skills (mono two-letter
   code as the icon stand-in until drawn skill glyphs exist). */
.skill-tiles { display:grid; grid-template-columns:repeat(auto-fill, minmax(10.5rem, 1fr)); gap:0.5625rem; }
.skill-tile { min-width:0; display:flex; flex-direction:column; gap:0.4375rem; padding:0.5625rem 0.625rem; border:0.0625rem solid var(--line-2); background:var(--panel-2); border-radius:var(--radius); }
.skill-tile .st-head { display:flex; align-items:center; gap:0.5rem; min-width:0; }
.skill-tile .st-code { width:1.75rem; height:1.75rem; flex:none; display:flex; align-items:center; justify-content:center; border:0.0625rem solid var(--line); background:var(--sink); color:var(--accent-2); font-family:var(--mono); font-size:0.75rem; }
.skill-tile .st-name { min-width:0; font-weight:600; font-size:0.8125rem; overflow-wrap:break-word; }
.skill-tile .st-foot { display:flex; align-items:baseline; gap:0.5rem; }
.skill-tile .st-lv { flex:none; color:var(--accent); font-size:0.75rem; }
.skill-tile .st-next { margin-left:auto; color:var(--faint); font-size:0.6875rem; text-align:right; }
/* Untrained tiles take the same colour treatment as an untrained .skill-row
   rather than a blanket fade, which put the level and the caption at 2.6-2.8:1. */
.skill-tile.untrained .st-name { color:var(--muted); }
.skill-tile.untrained .st-lv { color:var(--faint); }

/* profile top-skills chips */
.top-skills { display:flex; flex-wrap:wrap; gap:0.25rem; margin-top:0.5625rem; }
/* profile columns stack their boxes with the grid's own gap (B31). */
.profile-col { display:flex; flex-direction:column; gap:0.75rem; min-width:0; }

.compose-layout { display:grid; grid-template-columns:1fr 1fr; gap:0.75rem; align-items:start; }
.compose-layout > .box { min-width:0; }
.compose-qty { display:flex; justify-content:flex-end; }
.compose-qty input[type="number"] { width:4rem; font-family:var(--mono); text-align:right; }
.compose-gold { margin-top:0.5rem; }
.compose-gold input[type="number"] { width:6rem; font-family:var(--mono); text-align:right; }
.compose-mirror { display:block; margin:0 0 0.625rem; }
.compose-test-helper { margin:0 0 0.625rem; padding:0.5rem 0.625rem; border:0.0625rem dashed var(--line); background:var(--sink); }
.compose-test-helper > summary { cursor:pointer; color:var(--faint); font-size:0.75rem; }
.compose-test-helper[open] > summary { margin-bottom:0.5rem; }
.pick-group { border:0.0625rem solid var(--line-2); background:var(--panel-2); border-radius:var(--radius); margin-bottom:0.375rem; padding:0.4375rem 0.5625rem; }
.pick-group > summary { list-style:none; }
.pick-group > summary::-webkit-details-marker { display:none; }
.pick-group > .disclosure-summary { margin:0; }
.pick-group[open] > .disclosure-summary { margin-bottom:0.5rem; }
.pick-group > .disclosure-summary::after { content:"+"; }
.pick-group[open] > .disclosure-summary::after { content:"\2212"; }

@media (max-width: 760px) {
  .market-deal-title { align-items:flex-start; }
  .market-deal-title > .btn { display:none; }
  .market-section-head { align-items:flex-start; }
  .market-terms-focus { align-items:stretch; flex-direction:column; gap:0.625rem; }
  .market-terms-price { align-items:flex-start; text-align:left; }
  .market-confirm-flow { grid-template-columns:1fr; }
  .market-confirm-join { justify-self:center; line-height:0.75rem; }
  .market-channel-nav { grid-template-columns:1fr; }
  .market-channel-heading { align-items:stretch; flex-direction:column; }
  .market-channel-heading > .btn { width:100%; }
  .offer-sides { grid-template-columns:1fr; }
  .offer-swap { display:none; }
  .offer-actions .btn { flex:1 1 45%; }
  .compose-layout { grid-template-columns:1fr; }
  .compose-qty { justify-content:stretch; min-width:0; width:100%; }
  .compose-qty input[type="number"], .compose-gold input[type="number"] { min-height:2.75rem; flex:1; }
  .compose-mirror { display:flex; align-items:flex-start; gap:0.5rem; min-height:2.75rem; }
  .compose-mirror input[type="checkbox"] { width:1.25rem; height:1.25rem; flex:none; margin:0.0625rem 0 0; }
  .market-board-filters {
    display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:0.5rem;
  }
  .market-board-filters > input[type="search"] { grid-column:1 / -1; width:100%; }
  .market-board-filters > select, .market-board-filters > .btn { width:100%; }
  .market-board-meta, .market-listing-meta, .market-list-fields { justify-content:flex-start; width:100%; }
  .market-board-meta > .btn, .market-board-meta > form, .market-board-meta > form .btn,
  .market-listing-meta > .btn, .market-listing-meta > form, .market-listing-meta > form .btn { width:100%; }
  .market-buy { width:100%; }
  .market-buy input[type="number"] { flex:0 0 4.5rem; min-height:2.75rem; font-size:1rem; }
  .market-buy .btn { flex:1; }
  .market-list-fields > input[type="number"], .market-list-fields > select { flex:1 1 6.25rem; min-height:2.75rem; max-width:none; font-size:1rem; }
  .market-list-fields > .btn { width:100%; min-height:2.75rem; }
  .market-action-form input[type="number"], .market-terms-form input[type="number"] { min-height:2.75rem; font-size:1rem; }
  .market-action-form .btn, .market-terms-form .btn, .market-primary-action > .btn { width:100%; min-height:2.75rem; }
  .market-detail-stats { overflow:visible; }
  .market-sale-row, .market-revision-row { grid-template-columns:1fr; }
  .ledger-row { grid-template-columns:1fr; gap:0.25rem; }
  .ledger-status { text-align:left; }
}

/* Quantity stepper (B36) — one shared −/value/+ control for bounded quantity
   pickers (shelter deposit/withdraw, trade offer, market buy). Built by qty.js
   wrapping any `input[data-qty]`; the native input stays the value source so it
   works JS-off. Placed last so it overrides the per-context input rules
   (.transfer-form / .compose-qty) regardless of their source order.
   The expedition loadout left this set in B40a — a slot now takes the whole
   stack, so there is no quantity to step. */
.qty-stepper { display:inline-flex; align-items:stretch; border:0.0625rem solid var(--line); border-radius:var(--radius); overflow:hidden; background:var(--panel-2); }
.qty-stepper input[type="number"] { width:2.75rem; flex:none; border:0; border-radius:0; padding:0.25rem 0.125rem; background:transparent; color:var(--text); font-family:var(--mono); text-align:center; -moz-appearance:textfield; appearance:textfield; }
.qty-stepper input[type="number"]::-webkit-outer-spin-button,
.qty-stepper input[type="number"]::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }
/* Help & settings stacks in one column and stops short of the fixed chat dock,
   so the release notes are never rendered underneath it. */
.settings-stack { display:grid; gap:0.75rem; max-width:44rem; }

/* Help & settings — release history. The only bulleted prose list in the app,
   so it carries its own restrained indent instead of the browser default. */
.release { margin-bottom:1.125rem; }
.release:last-of-type { margin-bottom:0.5rem; }
.release-head { margin:0 0 0.25rem; font-size:0.875rem; }
.release-group { font-size:0.6875rem; letter-spacing:.14em; text-transform:uppercase; color:var(--faint); margin:0.6875rem 0 0.25rem; }
.release-entries { margin:0; padding-left:1.125rem; }
.release-entries li { font-size:0.8125rem; line-height:1.5; margin-bottom:0.25rem; }
.release-older { border-top:0.0625rem solid var(--line-2); padding-top:0.5rem; margin-top:0.5rem; }

.qty-step { flex:none; width:2rem; min-height:1.9rem; border:0; background:var(--sink); color:var(--text); font-family:var(--mono); font-size:1rem; line-height:1; cursor:pointer; display:flex; align-items:center; justify-content:center; }
.qty-step:first-child { border-right:0.0625rem solid var(--line); }
.qty-step:last-child { border-left:0.0625rem solid var(--line); }
.qty-step:hover:not(:disabled) { background:var(--line); }
.qty-step:disabled { color:var(--faint); cursor:default; }

@media (max-width: 760px) {
  .qty-step { min-height:2.75rem; width:2.75rem; }
  .qty-stepper input[type="number"] { min-height:2.75rem; }
  /* Offer compose cells stretch on mobile — let the stepper fill, value grows. */
  .compose-qty .qty-stepper { width:100%; max-width:100%; min-width:0; }
  .compose-qty .qty-stepper input[type="number"] { flex:1 1 0; width:0; min-width:0; }
}

/* Achievements — profile badges + goal progress, and the collection milestone
   line. Namespaced `ach-` throughout: bare names like `.badge` collide with
   modifier classes elsewhere. */
.ach-badge { border:0.0625rem solid var(--line-2); background:var(--panel-2); border-radius:var(--radius); padding:0.4375rem 0.5625rem; margin-top:0.5rem; }
.ach-badge.retired { border-style:dashed; }
.ach-line { display:flex; align-items:baseline; gap:0.375rem; }
.ach-line .nm { font-size:0.875rem; }
.ach-line .pts { margin-left:auto; color:var(--accent); font-size:0.8125rem; }
.ach-line .count { margin-left:auto; color:var(--muted); font-size:0.8125rem; }
.ach-badge .d, .ach-goal .d { margin:0.25rem 0 0; font-size:0.8125rem; color:var(--muted); }
.ach-badge .when { margin:0.25rem 0 0; font-size:0.6875rem; color:var(--faint); }
.ach-group { border:0.0625rem solid var(--line-2); background:var(--panel-2); border-radius:var(--radius); margin-top:0.5rem; padding:0.4375rem 0.5625rem; }
.ach-group > summary { list-style:none; }
.ach-group > summary::-webkit-details-marker { display:none; }
.ach-group > .disclosure-summary::after { content:"+"; }
.ach-group[open] > .disclosure-summary::after { content:"\2212"; }
.ach-group .ach-badge { border:0; background:none; padding:0.375rem 0 0; }
.ach-goals-head { margin:0.875rem 0 0; font-size:0.6875rem; letter-spacing:.14em; text-transform:uppercase; color:var(--faint); }
.ach-goal { margin-top:0.5rem; }
.ach-goal .bar { margin-top:0.3125rem; }
.ach-milestone { display:flex; align-items:baseline; gap:0.375rem; flex-wrap:wrap; margin-top:0.5rem; font-size:0.8125rem; color:var(--muted); }
.ach-milestone .nm { color:var(--text); }
.ach-milestone .count { margin-left:auto; }
.ach-milestone.done .nm { color:var(--good); }

@media (max-width: 760px) {
  /* At 320px the badge head wraps rather than crushing the points column. */
  .ach-line { flex-wrap:wrap; }
  .ach-line .pts, .ach-line .count { margin-left:0; }
  .ach-milestone .count { margin-left:0; }
}

/* No-JS phones: the dock, in flow.
   The server authors the dock `<details open>`, and it is chat.js that collapses
   it below 760px. With scripting off there is nothing to do that, so the fixed
   overlay sits over the page — measured at 576px of an 800px viewport, with the
   content beneath it unreachable (B47; the caveat B44 recorded).
   Rather than change the authored state — which would hand no-JS *desktop*
   players a collapsed dock — drop the overlay only where there is no script to
   run: chat becomes an ordinary block at the end of the page, covering nothing.
   Engines without `scripting` simply keep today's behaviour. */
@media (max-width: 760px) and (scripting: none) {
  .chatdock,
  .chatdock[open],
  .chatdock:not([open]) {
    position:static; inset:auto; width:auto; height:auto; min-height:0; max-height:none;
    margin:0.75rem 0 calc(var(--mobile-tabs-h) + 1rem + env(safe-area-inset-bottom));
    border:0.0625rem solid var(--rule); border-radius:var(--radius); box-shadow:none;
  }
  /* The page scrolls now, so the feed does not need to own a viewport slice —
     but it still caps, so a long backlog cannot bury the send box. */
  .chatdock .cd-feed { max-height:60vh; }
}
