/* css/app.css — Pharloom: layout and components.
   The skeleton's share of hallownest-calculator's css/app.css: the base, the labels, the four
   kinds of button and the other controls (its design/11-components.md), the header, the screen
   bar, the atmosphere, the notices and the responsive rules. Each screen adds its own block
   below as it's built. All colour comes from css/tokens.css. */

/* ── Base ────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-md);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
.page { min-height: 100vh; background: var(--page-bg); padding-bottom: var(--sp-16); transition: opacity var(--dur-slow) var(--ease-fade); }
/* Leaving or landing on a game reloads the page: it fades to black before, and the new one comes
   in once it's in place, as the game's loading screens do. */
html.is-leaving .page, html.is-landing .page { opacity: 0; }
html.is-leaving .page { transition-duration: var(--dur); }
.shell {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--page-pad);
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
}
h1, h2, h3, h4, p, ol, ul { margin: 0; }
ol, ul { padding: 0; list-style: none; }
img { display: block; max-width: 100%; }
/* Hidden things stay hidden even if their class sets display. */
[hidden] { display: none !important; }
button { font-family: inherit; color: inherit; cursor: pointer; border: 0; background: none; }
button:disabled { cursor: default; opacity: 0.45; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A screen's title takes the focus on opening (for screen readers, tabindex="-1"): it isn't a
   control, so it shows no ring. */
:is(h1, h2, h3)[tabindex="-1"]:focus-visible { outline: none; }
/* The screen bar, stuck at the top, mustn't cover what has just been focused. */
html { scroll-padding-block-start: calc(var(--sticky-h) + var(--sp-3)); }

/* ── Figures ───────────────────────────────────────────────────────────
   Every number goes in the sans with tabular figures (tokens.css, decision 1). */
.hero-num, .metric .val, .stat-value, .nav-num, .toast-num, .chip, .delta {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
  letter-spacing: 0;
}
/* The unit goes with the number; it doesn't weigh the same. */
.u { font-size: 0.62em; font-weight: 400; font-style: normal; color: var(--muted); }

/* ── Labels: two levels and no more ────────────────────────────────────
   Level 1, Cinzel, for section headers. Level 2, the sans, for field labels. */
.lbl, .block-head, .banner-tag {
  font-family: var(--font-num);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  font-size: var(--fs-2xs);
  font-weight: 600;
  color: var(--label);
}
.block-head { margin: 0 0 var(--sp-3); }

/* The frame's corners (App.brackets): the curl drawn once for the top left, mirrored for the
   other three, reaching a little past the plate's edge. */
.bk { position: absolute; width: var(--frame-corner); height: var(--frame-corner); color: var(--bracket); pointer-events: none; }
.bk-art { display: block; width: 100%; height: 100%; overflow: visible; }
.bk.tl { top: calc(var(--sp-1) * -1); left: calc(var(--sp-1) * -1); }
.bk.tr { top: calc(var(--sp-1) * -1); right: calc(var(--sp-1) * -1); scale: -1 1; }
.bk.bl { bottom: calc(var(--sp-1) * -1); left: calc(var(--sp-1) * -1); scale: 1 -1; }
.bk.br { bottom: calc(var(--sp-1) * -1); right: calc(var(--sp-1) * -1); scale: -1 -1; }
/* The thin filigree line inset all round, between the corners, and the plate's black inside it
   only (the plate itself is clear: nothing black shows past the line). The top line stops either
   side of the title's plaque (--title-half, js/app.js sizeTitles). */
.frame-line {
  position: absolute; inset: var(--frame-inset); z-index: -1; pointer-events: none; background: var(--inv-bg);
  border: 1px solid color-mix(in oklab, var(--lamp) var(--lamp-frame), transparent);
}
:is(.screen, .saves-body):has(.screen-head) .frame-line { border-top-color: transparent; }
:is(.screen, .saves-body):has(.screen-head) .frame-line::before,
:is(.screen, .saves-body):has(.screen-head) .frame-line::after {
  content: ''; position: absolute; top: -1px; width: calc(50% - var(--title-half, 6rem) + 1px); height: 1px;
  background: color-mix(in oklab, var(--lamp) var(--lamp-frame), transparent);
}
:is(.screen, .saves-body):has(.screen-head) .frame-line::before { left: -1px; }
:is(.screen, .saves-body):has(.screen-head) .frame-line::after { right: -1px; }
/* Each tab's light (tokens.css, --tab-*): the frame, the corners, the plaque, and a light from the top inside. */
.screen[data-view="home"], .screen[data-view="saves"] { --tab: var(--tab-home); }
.screen[data-view="game"] { --tab: var(--tab-game); }
.screen[data-view="progress"] { --tab: var(--tab-progress); }
.screen[data-view="map"] { --tab: var(--tab-map); }
.screen[data-view="journal"] { --tab: var(--tab-journal); }
.screen[data-view="tools"] { --tab: var(--tab-tools); }
.screen[data-view="fight"] { --tab: var(--tab-fight); }
.screen .frame-line {
  background: radial-gradient(ellipse 80% 320px at 50% 0%, color-mix(in oklab, var(--tab) var(--tab-light), transparent), transparent), var(--inv-bg);
  border-color: color-mix(in oklab, var(--tab) var(--tab-frame), transparent);
  box-shadow: 0 0 40px -10px color-mix(in oklab, var(--tab) 50%, transparent);
}
.screen:has(.screen-head) .frame-line::before, .screen:has(.screen-head) .frame-line::after { background: color-mix(in oklab, var(--tab) var(--tab-frame), transparent); }
.screen .bk { color: color-mix(in oklab, var(--tab) 80%, transparent); }
.screen .screen-plaque { color: var(--tab); }

/* ── The screens' frame ─────────────────────────────────────────────────
   Each screen is a pause-menu screen, framed and titled in the game's white filigree, the same
   for all (the corner brackets, the title and the rule under it, the line under the screen bar),
   with the warm bone of the Crest screen's spotlight as its light. The game titles its panes and
   selects in white, never with a hue (design/02-silksong.md §3 and §5): the sections are told
   apart by their names and the screen bar, not by a colour. Since step 1's third round
   (design/24-shell-round2-variants.html, E) each screen's frame is lit in its tab's colour
   (--tab-*): the frame, its corners and plaque, and a light from its top; the black and the
   white filigree of what's inside stay. */
.page, .screen {
  --lamp: var(--filigree);
  --lamp-light: var(--spotlight);
  --bracket: color-mix(in oklab, var(--lamp) var(--lamp-line), transparent);
  --scene-glow: color-mix(in oklab, var(--lamp-light) var(--lamp-glow), transparent);
  --scene-glow-low: color-mix(in oklab, var(--lamp-light) var(--lamp-glow-low), transparent);
  --halo-plate: radial-gradient(closest-side, color-mix(in oklab, var(--lamp-light) var(--lamp-plate), transparent),
    color-mix(in oklab, var(--lamp-light) var(--lamp-plate-edge), transparent) 58%, transparent);
}
.sec-title {
  font-family: var(--font-display); text-transform: uppercase; letter-spacing: var(--track-caps);
  font-weight: 600; font-size: var(--fs-lg); line-height: 1.2; color: var(--filigree);
}

/* The interface icons (tokens.css): the width sets the size, the height follows the viewBox. */
.ic { flex: none; width: var(--icon-sm); height: auto; stroke-width: var(--icon-stroke); }
.ic * { vector-effect: non-scaling-stroke; }
.ic.is-lg { width: var(--icon); }
.chev { transition: transform var(--dur) var(--ease); }
.chev.up { transform: rotate(180deg); }

/* ── A screen ──────────────────────────────────────────────────────────
   Every screen is a page of the game's inventory: the black field with the corner brackets in
   the filigree, and its title centred in Cinzel over the rule with the diamond (App.screenHead). */
.screen { position: relative; isolation: isolate; padding: var(--sp-8); --inv-pad: var(--sp-8); }
/* The title sits on the frame's line, centred on it, the black knocked out behind it and a
   hollow diamond either side; it reaches a little above the plate, onto the page. What goes
   with it (.screen-lead) opens the screen, centred. */
.screen-head {
  position: absolute; z-index: 1; top: var(--frame-inset); left: 50%; transform: translate(-50%, -50%);
  display: flex; align-items: center; max-width: calc(100% - var(--frame-corner) * 2);
  padding: var(--sp-2) var(--sp-8); white-space: nowrap;
}
/* The plaque: a thin double outline with pointed ends, stretched to the title. */
.screen-plaque { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; color: color-mix(in oklab, var(--filigree) 80%, transparent); pointer-events: none; }
.screen-plaque polygon:not(.is-inner) { fill: var(--inv-bg); } /* solid: the plaque straddles the page and the plate, so it can't be clear */
.screen-plaque .is-inner { opacity: 0.4; }
.screen-head .sec-title { position: relative; overflow: hidden; text-overflow: ellipsis; }
.screen-lead { display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-6); text-align: center; }

/* ── Controls ────────────────────────────────────────────────────────── */
/* Four kinds of button, by job (hallownest-calculator's design/11-components.md, round 2). The
   first rule for all four: it has to read as a button at rest, with no mouse over it, on a phone
   and the first time. Accent, because it's what responds to the finger.
   · Menu (.btn): a screen's or a dialog's actions. The needle's frame (design/03-redesign.md,
     step 2): a rule above and below, closed by a hollow diamond at each end in the accent's
     line; the primary one (.btn-primary) with its diamonds filled and a veil of the accent, as
     any under the finger; one that throws something away (.is-danger), in red.
   · Text (.text-btn): an action inside a line or a list. Underlined with the accent's line.
   · Icon (.icon-btn): a glyph alone (close, help, − +), in a thin ring.
   · Disclosure (.disc-btn): opens something below. The label on the left, the chevron in the ring. */
.btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--ctl); padding: var(--sp-1) var(--sp-6); border: 1px solid var(--line-ctl); border-inline-color: transparent; background: none;
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-xs); letter-spacing: var(--track-caps); text-transform: uppercase;
  color: var(--ink);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
  --bk: var(--accent-line);
}
.btn::before, .btn::after {
  content: ''; position: absolute; top: 50%; left: 0; width: calc(var(--diamond) + var(--sp-half)); height: calc(var(--diamond) + var(--sp-half));
  border: 1px solid var(--bk); background: var(--inv-bg); transform: translate(-50%, -50%) rotate(45deg); pointer-events: none;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.btn::after { left: 100%; }
.btn:is(:hover, :focus-visible):not(:disabled)::before, .btn:is(:hover, :focus-visible):not(:disabled)::after,
.btn-primary::before, .btn-primary::after { background: var(--bk); }
.btn:is(:hover, :focus-visible):not(:disabled) { color: var(--ink-strong); border-block-color: var(--accent-line); background-color: var(--accent-soft); }
.btn-primary { color: var(--ink-strong); border-block-color: var(--accent-line); background-color: color-mix(in oklab, var(--accent) 8%, transparent); --bk: var(--accent); }
.btn-primary:is(:hover, :focus-visible):not(:disabled) { background-color: color-mix(in oklab, var(--accent) 18%, transparent); }
.btn.is-danger { color: var(--bad); --bk: var(--bad); }
.btn.is-danger:is(:hover, :focus-visible):not(:disabled) { color: var(--bad); border-block-color: var(--bad); background-color: var(--bad-soft); }
.btn:disabled { color: var(--muted); background-color: transparent; --bk: var(--line-ctl); cursor: default; }
.text-btn {
  display: inline-flex; align-items: center; gap: var(--sp-1); min-height: var(--ctl-sm); padding: 0;
  font-family: var(--font-body); font-size: var(--fs-md); color: var(--ink-2);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px;
  text-decoration-color: color-mix(in oklab, var(--accent-line) 75%, transparent);
  transition: color var(--dur) var(--ease), text-decoration-color var(--dur) var(--ease);
}
.text-btn:is(:hover, :focus-visible):not(:disabled) { color: var(--accent); text-decoration-color: var(--accent); }
.text-btn:disabled { color: var(--muted); text-decoration-color: transparent; cursor: default; }
.icon-btn {
  position: relative; display: inline-grid; place-items: center; flex: none; width: var(--ctl-sm); height: var(--ctl-sm); padding: 0;
  font-family: var(--font-num); font-size: var(--fs-md); line-height: 1; color: var(--ink-2);
  transition: color var(--dur) var(--ease);
}
.icon-btn::before {
  content: ''; position: absolute; top: 50%; left: 50%; width: var(--ring); height: var(--ring); transform: translate(-50%, -50%);
  border: 1px solid color-mix(in oklab, var(--accent-line) 70%, transparent); border-radius: 50%; pointer-events: none;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
  --ring: calc(var(--sp-6) + var(--sp-half));
}
.icon-btn:is(:hover, :focus-visible):not(:disabled) { color: var(--ink-strong); }
.icon-btn:is(:hover, :focus-visible):not(:disabled)::before { border-color: var(--accent); background-color: var(--accent-soft); }
.icon-btn:disabled { color: var(--line-ctl); cursor: default; }
.icon-btn:disabled::before { border-color: var(--line-ctl); }
.disc-btn {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%; min-height: var(--ctl); padding: var(--sp-1) 0; text-align: left;
  border-top: 1px solid var(--line-ctl); border-bottom: 1px solid var(--line-ctl);
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-xs); letter-spacing: var(--track-caps); text-transform: uppercase;
  color: var(--ink); transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.disc-ring {
  display: inline-grid; place-items: center; flex: none; margin-left: auto;
  width: calc(var(--sp-6) + var(--sp-half)); height: calc(var(--sp-6) + var(--sp-half));
  border: 1px solid var(--accent-line); border-radius: 50%; color: var(--accent-ink);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.disc-btn:is(:hover, :focus-visible) { color: var(--ink-strong); }
.disc-btn:is(:hover, :focus-visible, [aria-expanded="true"]) .disc-ring { color: var(--ink-strong); border-color: var(--accent); background-color: var(--accent-soft); }
.disc-btn.is-pick { border-top: 0; font-family: var(--font-body); font-weight: 400; font-size: var(--fs-md); letter-spacing: 0; text-transform: none; }
.disc-btn.is-pick .disc-ring { margin-left: 0; }
/* On/off (.check): the game's diamond on the accent's line; on, filled with the accent and lit
   (design/03-redesign.md, step 2). No tick: a row of them over the map stays quiet. */
.check {
  display: inline-flex; align-items: center; gap: var(--sp-2); min-height: var(--ctl-sm); padding: 0;
  font-family: var(--font-body); font-size: var(--fs-sm); color: var(--ink-2); cursor: pointer;
  transition: color var(--dur) var(--ease);
}
.check:hover { color: var(--ink-strong); }
.check-box, .check input[type="checkbox"] {
  flex: none; display: grid; place-items: center; width: var(--sp-3); height: var(--sp-3); margin: 0 var(--sp-half);
  border: 1px solid color-mix(in oklab, var(--accent-line) 80%, transparent); border-radius: 0; background: none; transform: rotate(45deg);
  color: var(--scene-bg); -webkit-appearance: none; appearance: none; cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.check:hover :is(.check-box, input[type="checkbox"]) { border-color: var(--accent); }
:is([aria-checked="true"], [aria-pressed="true"]) > .check-box, .check input:checked { background-color: var(--accent); border-color: var(--accent); box-shadow: var(--glow); }
.check input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.check-box > svg { display: none; }
/* Search (.search): a sunken well with its rule and the lens in front. */
.search {
  display: flex; align-items: center; gap: var(--sp-2); min-height: var(--ctl); padding: 0 var(--sp-3);
  background: var(--card); border-bottom: 1px solid var(--line-ctl); color: var(--muted); cursor: text;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.search:hover { border-bottom-color: var(--accent-line); color: var(--accent-ink); }
.search:focus-within { border-bottom-color: var(--accent); color: var(--accent-ink); box-shadow: inset 0 -1px 0 var(--accent); }
.search input {
  flex: 1 1 auto; min-width: 0; min-height: var(--ctl); padding: 0; margin: 0;
  font-family: var(--font-body); font-size: var(--fs-md); color: var(--ink);
  background: none; border: 0; border-radius: 0; -webkit-appearance: none; appearance: none;
}
.search input::placeholder { color: var(--muted); }
.search input:focus-visible { outline: none; }
/* Choose one (.seg): a group where one is always chosen. The upgrade pips (design/03-redesign.md,
   step 2): a hollow diamond under each word, on the accent's line; the chosen one in bone, its
   pip filled and lit. The header's language (.langsel) keeps the plain underline: it's loose
   text, as a game menu's. */
.seg { display: inline-flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; }
.seg button {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-1);
  padding: var(--sp-1) 0; min-height: var(--ctl); min-width: var(--ctl-sm);
  font-size: var(--fs-sm); color: var(--ink-2); text-align: center;
  transition: color var(--dur) var(--ease);
}
.seg button::after {
  content: ''; flex: none; width: var(--diamond); height: var(--diamond); transform: rotate(45deg);
  border: 1px solid color-mix(in oklab, var(--accent-line) 80%, transparent);
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.seg button:is(:hover, :focus-visible):not(:disabled):not([aria-pressed="true"]) { color: var(--accent); }
.seg button:is(:hover, :focus-visible):not(:disabled)::after { border-color: var(--accent); }
.seg button[aria-pressed="true"] { color: var(--ink-strong); }
.seg button[aria-pressed="true"]::after { background: var(--accent); border-color: var(--accent); box-shadow: var(--glow); }
.seg button:disabled { color: var(--muted); cursor: default; }
.seg button:disabled::after { border-color: var(--line-ctl); }
.seg.sm { gap: var(--sp-3); }
.seg.sm button { min-height: var(--ctl-sm); font-size: var(--fs-xs); }
/* The language: words on a faint line, the chosen one on the accent's, doubled. */
.langsel button { flex-direction: row; min-width: 0; border-bottom: 1px solid color-mix(in oklab, var(--accent-line) 40%, transparent);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.langsel button::after { display: none; }
.langsel button:is(:hover, :focus-visible):not([aria-pressed="true"]) { border-bottom-color: var(--accent-line); }
.langsel button[aria-pressed="true"] { border-bottom-color: var(--accent); box-shadow: 0 1px 0 var(--accent); }

/* ── Showing ───────────────────────────────────────────────────────────
   A frame says "press me", so what only informs carries none. */
.tag {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase; line-height: 1.4;
  color: var(--ink-2);
}
.tag::before, .chip::before {
  content: ''; flex: none; width: var(--diamond); height: var(--diamond); border: 1px solid currentColor; transform: rotate(45deg); opacity: 0.8;
}
/* Notice (.banner): the level's rule on the left and a breath of its colour behind. Three levels:
   danger (the default), a condition (.is-run) and information (.is-hint). */
.banner {
  --lv: var(--bad); --lv-ink: var(--ink-2);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4); padding: var(--sp-3) var(--sp-4);
  border-left: 2px solid var(--lv); background: color-mix(in oklab, var(--lv) 7%, transparent);
}
.banner-tag { flex-shrink: 0; color: var(--lv); }
.banner-text { flex: 1 1 24ch; font-size: var(--fs-sm); color: var(--lv-ink); }
.banner .btn { flex-shrink: 0; }
.banner.is-run { --lv: var(--cond); }
.banner.is-hint { --lv: var(--ink-strong); }
#banner:not(:empty) { display: flex; flex-direction: column; gap: var(--sp-2); }
/* Empty (App.emptyHtml): centred under a short rule with its diamond, in the secondary ink, with
   the action that solves it when there is one. */
.empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); margin: 0; padding: var(--sp-4) var(--sp-3);
  text-align: center; font-size: var(--fs-sm); line-height: 1.5; color: var(--ink-2); text-wrap: balance; list-style: none;
}
.empty > .rule { width: calc(var(--sp-16) * 2); height: auto; color: var(--bracket); }
.empty .text-btn { font-size: var(--fs-sm); }
.empty.is-inline { flex-direction: row; flex-wrap: wrap; justify-content: flex-start; gap: var(--sp-1) var(--sp-3); padding: 0 var(--sp-3); text-align: left; }
.empty.is-inline > .rule { display: none; }

/* ── Header ──────────────────────────────────────────────────────────────
   Two rows: the filigree crown above, and the title below with the language on its left, which
   belongs to the site, and the save selector on its right, which is yours (the brand spans both
   rows and shares them, subgrid, as the sibling's). The two side columns grow equally, so the
   title lands dead centre. Behind it, like behind the whole page, the main menu's embers. */
.masthead {
  padding: var(--sp-4) 0 0; display: grid; grid-template-columns: 1fr auto 1fr; grid-template-rows: auto auto;
  align-items: center; gap: 0 var(--sp-6);
}
.masthead .brand { grid-column: 2; grid-row: 1 / 3; display: grid; grid-template-rows: subgrid; row-gap: var(--sp-1); justify-items: center; }
.masthead .mh-tools { grid-column: 1; grid-row: 2; justify-self: start; flex-wrap: nowrap; }
.masthead .mh-save { grid-column: 3; grid-row: 2; justify-self: end; }
.brand { display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); min-width: 0; color: inherit; text-decoration: none; }
/* The title leads to the start screen: on hover, the crown lights up fully. */
.mh-crown { display: block; width: calc(var(--sp-16) * 3); height: auto; color: color-mix(in oklab, var(--filigree) var(--lamp-line), transparent); transition: color var(--dur) var(--ease); }
.brand:hover .mh-crown { color: var(--filigree); }
.title {
  font-family: var(--font-display); font-weight: 900; font-size: var(--fs-brand);
  line-height: 1.05; letter-spacing: var(--track-title); color: var(--ink-strong); text-align: center;
  text-wrap: balance;
}
/* Beta, by the title: a small label in the secondary ink, raised like an exponent. */
.mh-beta {
  margin-left: var(--sp-2); vertical-align: super;
  font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--ink-2);
}
/* The language, as loose text like a game menu: small, unboxed, so the title rules. */
.mh-tools { --tools-gap: var(--sp-6); display: flex; align-items: center; gap: var(--tools-gap); flex-wrap: wrap; }
.langsel { position: relative; display: flex; align-items: center; gap: var(--sp-2); }
.mh-tools > .langsel:not(:last-child)::after {
  content: ''; position: absolute; top: 50%; right: calc(var(--tools-gap) / -2 - 0.5px);
  width: 1px; height: var(--sp-4); transform: translateY(-50%); background: var(--line-ctl);
}
.langsel button, .mh-link {
  display: inline-flex; align-items: center; min-height: var(--ctl-sm); padding: 0;
  font-size: var(--fs-sm); letter-spacing: var(--track-label);
}

/* ── The screen bar ──────────────────────────────────────────────────────
   The pages of the game's pause menu: their names in the serif, in lowercase. Stuck at the top
   and across the full window width. */
.page { overflow-x: clip; }
.screens { display: flex; flex-direction: column; gap: var(--sp-8); }
.screens > :empty { display: none; }
/* Switching screens (js/app.js, go): the one you arrive at fades in. */
.screens > .is-entering { animation: title-in var(--dur-slow) var(--ease-fade); }
@keyframes title-in { from { opacity: 0; } to { opacity: 1; } }
@media (max-width: 899px) { .screens { gap: var(--sp-6); } }
/* The footer: the fan-project notice and the sources, small and muted under a hairline. */
.colophon {
  display: flex; flex-direction: column; gap: var(--sp-1);
  padding-top: var(--sp-4); border-top: 1px solid var(--line);
  font-size: var(--fs-xs); line-height: 1.5; color: var(--muted); text-align: center; text-wrap: balance;
}
.colophon a {
  color: var(--accent); text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--accent-line); transition: color var(--dur) var(--ease);
}
.colophon a:hover { color: var(--accent-ink); }
.colophon .gh { display: inline-flex; vertical-align: -3px; margin-left: var(--sp-1); text-decoration: none; }
/* The privacy notice, folded: its summary responds to the finger, so it takes the links' accent; open,
   a short left-aligned list in the footer's muted ink, no wider than a comfortable line. */
.colophon-privacy summary {
  display: inline-flex; align-items: center; gap: var(--sp-1); cursor: pointer; list-style: none;
  color: var(--accent); transition: color var(--dur) var(--ease);
}
.colophon-privacy summary:hover { color: var(--accent-ink); }
.colophon-privacy summary::-webkit-details-marker { display: none; }
.colophon-privacy summary::after { content: '▾'; transition: transform var(--dur) var(--ease); }
.colophon-privacy[open] summary::after { transform: rotate(180deg); }
.colophon-privacy ul {
  max-width: 60ch; margin: var(--sp-2) auto 0; padding-left: var(--sp-4);
  display: flex; flex-direction: column; gap: var(--sp-1); text-align: left; text-wrap: pretty;
}
/* The About block: the page's text for search engines and for whoever lands from one, as a guide
   page on the main menu's red with no box (tools/pages.js; design/23-static-pages-round2.html, D). */
.about {
  display: flex; flex-direction: column; gap: var(--sp-8);
  padding-top: var(--sp-12); border-top: 1px solid var(--line);
  font-size: var(--fs-md); line-height: 1.65; color: var(--ink-2);
}
.about p { margin: 0; }
.about h2 { margin: 0 0 var(--sp-3); font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.about-crown { display: block; margin: 0 auto; color: var(--filigree); opacity: 0.75; }
.about-hero { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: var(--sp-8); align-items: center; }
.about-art { position: relative; display: grid; place-items: center; height: var(--about-art); }
.about-art::before { content: ''; position: absolute; inset: -8% 6%; background: var(--journal-light); pointer-events: none; }
.about-art img { position: relative; max-width: 100%; max-height: 100%; width: auto; min-height: 45%; object-fit: contain; filter: drop-shadow(0 12px 30px rgba(0, 0, 0, 0.6)); }
.about-head { display: grid; gap: var(--sp-4); justify-items: start; }
.about h1 { margin: 0; display: grid; gap: var(--sp-2); font-family: var(--font-display); line-height: 1; color: var(--ink-strong); text-wrap: balance; }
.about-name { font-weight: 700; font-size: clamp(2.2rem, 1.4rem + 3vw, 4.5rem); text-shadow: 0 0 40px color-mix(in oklab, var(--spotlight) 25%, transparent); }
.about-sub { font-weight: 400; font-size: var(--fs-lg); line-height: 1.3; letter-spacing: var(--track-title); color: var(--ink-2); }
.about-name.is-long { font-size: clamp(1.8rem, 1.2rem + 2vw, 3rem); }
.about-colon { display: none; }
.about-quote { font-family: var(--font-hand); font-size: var(--fs-lg); color: var(--ink); }
.about-cta {
  display: inline-flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-6); border: 1px solid var(--filigree);
  font-family: var(--font-display); font-size: var(--fs-sm); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-strong); text-decoration: none;
  box-shadow: var(--glow); transition: background-color var(--dur) var(--ease);
}
.about-cta:is(:hover, :focus-visible) { background: var(--veil); color: var(--ink-strong); }
/* A boss's figures in a row between filigree rules. */
.about-figs { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); margin: 0; border-top: 1px solid color-mix(in oklab, var(--filigree) 25%, transparent); border-bottom: 1px solid color-mix(in oklab, var(--filigree) 25%, transparent); }
.about-figs div { display: flex; flex-direction: column-reverse; align-items: center; gap: var(--sp-1); padding: var(--sp-4) var(--sp-2); text-align: center; }
.about-figs div + div { border-left: 1px solid color-mix(in oklab, var(--filigree) 18%, transparent); }
.about-figs dd { margin: 0; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-2xl); line-height: 1.1; color: var(--ink-strong); }
.about-figs dt { font-size: var(--fs-xs); color: var(--muted); }
.about-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-12); }
.about-attacks { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: var(--sp-6); }
.about-attacks li { break-inside: avoid; display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2); padding: var(--sp-2) 0; border-bottom: 1px solid color-mix(in oklab, var(--filigree) 12%, transparent); font-size: var(--fs-sm); }
.about-masks { display: inline-flex; gap: 2px; flex: none; }
.about-masks img { height: var(--sp-4); width: auto; }
.about-fight { font-size: var(--fs-xs); color: var(--muted); }
.about-bar { position: relative; height: 8px; margin: var(--sp-2) 0 var(--sp-8); background: color-mix(in oklab, var(--slot-red) 70%, transparent); }
.about-bar i { position: absolute; top: -4px; bottom: -4px; width: 2px; margin-left: -1px; background: var(--ink-strong); }
.about-bar i span { position: absolute; top: calc(100% + var(--sp-1)); left: 50%; transform: translateX(-50%); white-space: nowrap; font-style: normal; font-size: var(--fs-2xs); color: var(--muted); }
.about-reward { color: var(--ink); }
.about-text { display: grid; gap: var(--sp-3); max-width: 70ch; margin: 0 auto; }
/* Folded: what's said in words that the figures above already show. */
.about-details > summary, .about-faq summary { list-style: none; cursor: pointer; }
.about-details > summary::-webkit-details-marker, .about-faq summary::-webkit-details-marker { display: none; }
.about-details > summary { display: inline-flex; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--ink-2); border-bottom: 1px solid var(--accent-line); }
.about-details > summary::after { content: '⌄'; }
.about-details[open] > summary { margin-bottom: var(--sp-3); }
.about-details p { max-width: 70ch; margin-bottom: var(--sp-3); font-size: var(--fs-sm); }
.about-faq details { border-bottom: 1px solid color-mix(in oklab, var(--filigree) 18%, transparent); }
.about-faq summary { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3); padding: var(--sp-3) 0; }
.about-faq summary::after { content: ''; flex: none; width: var(--diamond); height: var(--diamond); border: 1px solid var(--filigree); transform: rotate(45deg); }
.about-faq details[open] summary::after { background: var(--filigree); }
.about-faq h3 { margin: 0; font-family: var(--font-body); font-size: var(--fs-md); font-weight: 400; color: var(--ink); }
.about-faq details p { margin: 0 0 var(--sp-3); font-size: var(--fs-sm); }
/* Every boss as a portrait; the other pages with their icon. */
.about-bosses ul { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: var(--sp-4) var(--sp-2); }
.about-bosses a { display: grid; justify-items: center; gap: var(--sp-1); text-decoration: none; font-size: var(--fs-2xs); line-height: 1.25; text-align: center; color: var(--ink-2); }
.about-bosses .hj-ring { width: 52px; }
.about-bosses a:is(:hover, :focus-visible) { color: var(--accent); }
.about-bosses a:is(:hover, :focus-visible) .hj-ring { transform: scale(1.08); }
.about-more ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-4) var(--sp-6); }
.about-more a { display: grid; justify-items: center; gap: var(--sp-1); width: 120px; text-decoration: none; text-align: center; font-size: var(--fs-xs); color: var(--ink-2); }
.about-more img { width: var(--sp-12); height: var(--sp-12); object-fit: contain; filter: drop-shadow(0 0 8px color-mix(in oklab, var(--spotlight) 30%, transparent)); }
.about-more a:is(:hover, :focus-visible) { color: var(--accent); }
.about-more h2, .about-bosses h2, .about-faq h2 { text-align: center; }
/* Folded with a save loaded (js/app.js, renderAbout): only its disclosure shows. */
.about.is-folded > :not(.about-fold) { display: none; }
.about.is-folded { padding-top: 0; border-top: 0; }
.about code { font-family: var(--font-num); font-size: var(--fs-xs); color: var(--ink); overflow-wrap: anywhere; }
.about strong { color: var(--ink); font-weight: 600; }
.about-text a, .about-details a, .about-faq a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--accent-line); }
@media (max-width: 899px) {
  .about-hero, .about-two { grid-template-columns: minmax(0, 1fr); }
  .about-hero { text-align: center; }
  .about-head { justify-items: center; }
  .about { --about-art: 220px; }
  .about-figs div:nth-child(odd) { border-left: 0; }
  .about-attacks { columns: 1; }
}
.nav {
  position: sticky; top: 0; z-index: 30; isolation: isolate;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-2) var(--sp-4);
  min-height: var(--sticky-h);
}
.nav::before {
  content: ''; position: absolute; z-index: -1; top: 0; bottom: 0; left: calc(50% - 50vw); right: calc(50% - 50vw);
  background: var(--bg); border-bottom: 1px solid color-mix(in oklab, var(--lamp) 22%, transparent);
}
.nav-tabs { position: relative; display: flex; flex-shrink: 0; align-self: stretch; align-items: stretch; gap: var(--sp-1) var(--sp-4); }
.nav-tab {
  display: inline-flex; align-items: center; gap: var(--sp-2); min-height: 44px; padding: 0;
  font-family: var(--font-body); font-size: var(--fs-lg); line-height: 1.2; color: var(--muted); text-decoration: none;
  transition: color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.nav-tab:hover { color: var(--accent); }
/* The current tab: an accent rule on its bottom edge, while Hornet isn't on the bar to mark it
   (a phone's width, or before the script runs). */
.nav-tab[aria-current="page"] { color: var(--ink-strong); box-shadow: inset 0 -2px 0 var(--accent); }
.nav-tabs.has-hn > .nav-tab[aria-current="page"] { box-shadow: none; }
.nav-num { font-size: var(--fs-xs); font-weight: 600; color: var(--ink-2); white-space: nowrap; }
.nav-num:empty { display: none; }
/* The two groups: your game, then the tools after a thin rule. */
.nav-sep { width: 1px; height: 1.4em; align-self: center; background: var(--line-ctl); }
/* The phone's fold (js/app.js, renderNav): the Build tab and the second rows only show there. */
.nav-tools, .nav-sub { display: none; }

/* ── The Hornet who walks the page (js/app-hornet.js) ─────────────────
   One sprite, drawn from strips of 120 × 120 cells (assets/hornet/, tools/extract-hornet.py):
   standing, running (ten frames, stepped at --dur-step) or sitting; her feet at 116 of the cell
   and her middle at 62. She faces left, as the game draws her, and turns with a mirror. On the
   screen bar she's --hn-bar tall, her feet on its bottom edge; on the Map (.mp-hornet) she's
   --hn-map tall, her feet on her bench. Reduced motion leaves her on her first frame and, from
   the JS, only standing or sitting. Decorative: the button only exists for the click (a Bind). */
.hn {
  --n: 1; --hn-h: var(--hn-bar);
  position: absolute; z-index: 1; width: var(--hn-h); height: var(--hn-h); padding: 0; border: 0; margin: 0;
  background: url(../assets/hornet/stand.png) 0 0 / auto 100% no-repeat; cursor: default;
  opacity: 1; transition: opacity var(--dur-slow) var(--ease-fade);
}
.hn:focus { outline: none; }
.hn.is-run { background-image: url(../assets/hornet/run.png); --n: 10; }
.hn.is-sit { background-image: url(../assets/hornet/sit.png); }
.hn.is-right { --flip: -1; scale: -1 1; }
.hn.is-out { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  .hn.is-run { animation: hn-cycle calc(var(--dur-step) * var(--n)) steps(var(--n)) infinite; }
  /* A Bind (a click): standing still, a white light swells around her and fades. */
  .hn.is-bind { animation: hn-bind var(--dur-flash) var(--ease-fade); }
}
@keyframes hn-cycle { to { background-position-x: calc(-1 * var(--hn-h) * var(--n)); } }
@keyframes hn-bind { 40% { filter: drop-shadow(0 0 3px var(--focus-glow)) drop-shadow(0 0 14px var(--focus-glow)); } }
/* On the screen bar: sitting at its bottom edge, under the current tab's title. */
.nav-tabs > .hn { bottom: 0; left: 0; }

/* ── Atmosphere ──────────────────────────────────────────────────────────
   The main menu behind the whole page (tokens.css): fixed to the window and behind everything.
   Below, the red light, the vignette and the embers (.atmos, js/app.js); above, the grain. */
.page { position: relative; isolation: isolate; }
.atmos { position: fixed; inset: 0; z-index: -2; overflow: hidden; pointer-events: none; background: var(--vignette), var(--ember-glow); }
.page::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: var(--grain); opacity: var(--grain-opacity); mix-blend-mode: overlay;
}
.atmos i {
  position: absolute; left: var(--x); bottom: 0; width: var(--s); height: calc(var(--s) * 3); border-radius: 50%;
  background: var(--mote); box-shadow: 0 0 6px var(--mote); opacity: 0; transform: rotate(var(--tilt));
}
@media (prefers-reduced-motion: no-preference) {
  .atmos i { animation: mote var(--t) linear infinite; animation-delay: var(--d); }
}
@keyframes mote {
  0% { opacity: 0; transform: translate(0, 0) rotate(var(--tilt)); }
  15%, 80% { opacity: var(--o); }
  100% { opacity: 0; transform: translate(var(--dx), -100vh) rotate(var(--tilt)); }
}

/* ── Floating notice ─────────────────────────────────────────────────── */
/* The notices (toast in js/app.js), like the game's on-screen messages, just under the screen bar:
   the text in the game's face between two short rules with their diamond, over a soft dark veil. */
.toast {
  position: fixed; left: 50%; top: calc(var(--sticky-h) + var(--sp-3)); transform: translateX(-50%); z-index: 80; isolation: isolate;
  display: flex; align-items: center; justify-content: center; gap: var(--sp-3);
  width: max-content; max-width: min(640px, calc(100vw - var(--sp-8))); padding: var(--sp-3) var(--sp-6);
  font-family: var(--font-display); font-size: var(--fs-lg); font-weight: 600; letter-spacing: var(--track-title); line-height: 1.3;
  color: var(--ink-strong); text-align: center; pointer-events: none;
  opacity: 0; transition: opacity var(--dur-slow) var(--ease-fade);
}
.toast.is-on { opacity: 1; }
.toast::before {
  content: ''; position: absolute; z-index: -1; inset: -35% -12% -70%;
  background: radial-gradient(closest-side, color-mix(in oklab, var(--scene-bg) 96%, transparent) 64%, transparent);
}
.toast-rule { flex: none; color: var(--ink-2); }
.toast-t { min-width: 0; text-wrap: balance; }
.toast-num { font-weight: 600; }
.toast-art { flex: none; width: 42px; height: 48px; object-fit: contain; }
@media (max-width: 599px) {
  .toast { gap: var(--sp-2); padding-inline: var(--sp-2); font-size: var(--fs-md); }
  .toast-rule { width: var(--sp-6); }
  .toast-art { width: 35px; height: 40px; }
}

/* ── Responsive ──────────────────────────────────────────────────────── */
@media (max-width: 899px) {
  :root { --page-pad: var(--sp-4); --sticky-h: 60px; --pg-ring: 180px; --pg-rg-ring: 104px; --pg-md: 52px; --pg-tab-art: 48px; }
  .shell { gap: var(--sp-6); }
  .screen { padding: var(--sp-6); --inv-pad: var(--sp-6); }
  .nav { flex-direction: column; align-items: stretch; gap: 0; }
  .nav-tabs { gap: var(--sp-1) var(--sp-4); }
  /* No room beside a tab for Hornet here (js/app-hornet.js): the rule marks the tab. */
  .nav-tabs > .hn { display: none; }
  /* Seven tabs don't fit: the tools fold into one, Build, and the Inventory under Your game;
     while you're in either pair a second row switches between its two. */
  .nav-tool, .nav-sep, .nav-inv { display: none; }
  .nav-tools { display: inline-flex; }
  .nav-tabs { flex-wrap: nowrap; justify-content: space-between; }
  .nav-home.is-parent { color: var(--ink-strong); box-shadow: inset 0 -2px 0 var(--accent); }
  .nav-sub:not([hidden]) { display: flex; justify-content: center; gap: var(--sp-8); border-top: 1px solid var(--line-row); }
  .nav-sub .nav-tab { min-height: var(--ctl); font-size: var(--fs-md); }
  /* The bar's height follows its rows. */
  :root:has(.nav-sub:not([hidden])) { --sticky-h: 104px; }
}
@media (max-width: 599px) {
  .screen { padding: var(--sp-4); --inv-pad: var(--sp-4); }
  /* The header in a single row: the title on the left, smaller, and the language on the right. */
  .masthead { display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: center; gap: var(--sp-2); padding-top: var(--sp-3); }
  .brand, .masthead .brand { display: flex; flex: 1 1 auto; align-items: flex-start; order: -1; }
  .masthead .mh-tools { order: 1; flex: none; flex-wrap: nowrap; --tools-gap: var(--sp-3); }
  .title { font-size: var(--fs-lg); text-align: left; letter-spacing: var(--track-title); }
  /* The bar, on a phone: the five tabs spread out in one row. */
  .nav-tabs { justify-content: space-between; gap: 0 var(--sp-3); }
  .mh-crown { display: none; }
  /* The title rides the frame's line: the plate opens lower so the text clears it. */
  .screen { padding-top: var(--sp-8); }
  .nav-tab { flex-direction: column; justify-content: center; gap: 0; min-height: 44px; font-size: var(--fs-sm); letter-spacing: 0; text-align: center; }
  .nav-sep { display: none; }
}
@media (min-width: 900px) {
  .nav-tab { white-space: nowrap; }
}

/* ── Hover, across the site ───────────────────────────────────────────── */
@media (hover: hover) {
  .nav-tab:hover:not([aria-current="page"]) { box-shadow: inset 0 -2px 0 color-mix(in oklab, var(--accent) 35%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
}

/* ── Saves: the game's profile screen ────────────────────────────────────
   The four slots stacked like the game's bars: the number, the masks with the
   HUD's sprites, the nail and what the game carries (charms found, Journal, Hall). No boxes:
   rules in the spotlight's bone between slots, and the whole bar responds to the finger. The slot you're
   playing carries the accent's line on its edge, as the equipped charm does, and a light from
   that edge. Free mode, which is nobody's game, is the one sheet with a frame. */
/* Saves draws its own plate (.saves-body, with the frame): the screen around it adds none. */
.screen[data-view="saves"] { padding: 0; }
.saves-body {
  --inv-pad: var(--sp-8);
  --inv-line: color-mix(in oklab, var(--spotlight) 13%, transparent);
  position: relative; isolation: isolate; padding: var(--inv-pad);
}
.saves-note { max-width: var(--measure); margin: 0; font-size: var(--fs-sm); color: var(--muted); text-wrap: pretty; }
/* Free mode and the four share their columns (subgrid): the slot, then its buttons, stacked as a
   menu in a column of their own behind a rule (free mode's note, there), so the numbers, the masks,
   the nails and the buttons line up from one slot to the next whatever each one holds. */
.saves-lists { display: grid; grid-template-columns: minmax(0, 1fr) auto; }
.saves-list {
  grid-column: 1 / -1; list-style: none; margin: 0; padding: 0; border-bottom: 1px solid var(--inv-line);
  display: grid; grid-template-columns: subgrid;
}
.save {
  grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; align-items: center;
  border-top: 1px solid var(--inv-line); transition: background var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.save-main {
  width: 100%; display: grid; grid-template-columns: var(--sp-12) minmax(0, 1fr) calc(var(--sp-16) * 2);
  grid-template-areas: "n hud nail" "n facts nail"; align-items: center; gap: var(--sp-3) var(--sp-6);
  padding: var(--sp-4); text-align: left; font-size: var(--fs-md); color: var(--ink);
}
/* The slot is one piece, its buttons included: the hover, the light of the one you're in and the
   focus ring go on the whole row, not only on the part that loads it. The hover is a light coming
   in from the left, the side the number is on; the nail catches it. */
.save:has(> .save-main:hover) { background: linear-gradient(90deg, var(--accent-soft), transparent 75%); }
.save.is-active {
  box-shadow: inset var(--sp-half) 0 0 var(--accent-line);
  background: linear-gradient(90deg, color-mix(in oklab, var(--accent-line) 16%, transparent), transparent 60%);
}
.save.is-active:has(> .save-main:hover) { background: linear-gradient(90deg, color-mix(in oklab, var(--accent-line) 24%, transparent), transparent 75%); }
.save-main:focus-visible { outline: none; }
.save:has(> .save-main:focus-visible) { outline: 2px solid var(--accent); outline-offset: -2px; }
.save-n {
  grid-area: n; justify-self: center; text-align: center;
  font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-2xl); line-height: 1; color: var(--ink-strong);
}
.save-hud { grid-area: hud; display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
.save-masks { display: flex; flex-wrap: wrap; gap: var(--sp-half); }
.save-masks img { height: var(--sp-6); width: auto; }
/* You're here: the rule's diamond, lit, and the words, at the head of the card. */
.save.is-active:not(.is-free) .save-main { grid-template-areas: "n tag nail" "n hud nail" "n facts nail"; }
.save-tag {
  grid-area: tag; justify-self: start; display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-2xs); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink-2);
}
.save-tag::before {
  content: ''; width: var(--sp-2); height: var(--sp-2); transform: rotate(45deg);
  background: var(--ink-strong); box-shadow: 0 0 8px color-mix(in oklab, var(--ink-strong) 55%, transparent);
}
.save-nail { grid-area: nail; display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); font-size: var(--fs-xs); color: var(--ink-2); text-align: center; text-wrap: balance; }
.save-nail img { height: calc(var(--sp-16) * 1.5); width: auto; transition: filter var(--dur-slow) var(--ease); }
.save:has(> .save-main:hover) .save-nail img { filter: drop-shadow(0 0 10px color-mix(in oklab, var(--spotlight) 45%, transparent)); }
.save-facts { grid-area: facts; display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-6); }
.save-fact { display: inline-flex; align-items: baseline; gap: var(--sp-2); }
.save-k { font-size: var(--fs-2xs); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--label); }
.save-fact b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-md); color: var(--ink-strong); }
.save-fact .u { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xs); }
/* The slot's buttons: a menu in the second column, behind a rule, one under the other. */
.save-foot {
  display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: var(--sp-1);
  align-self: stretch; margin-block: var(--sp-4); padding-inline: var(--sp-4) var(--sp-6);
  border-left: 1px solid var(--inv-line);
}
/* The file the slot follows, above its buttons; on the one you're in, how the link is. */
.save-link { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-1); padding: 0 var(--sp-4) var(--sp-2); font-size: var(--fs-xs); color: var(--ink-2); }
.save-link-t { font-family: var(--font-num); overflow-wrap: anywhere; }
.save-link b { font-family: var(--font-body); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase; font-size: var(--fs-2xs); color: var(--ink-strong); }
.save-link.is-live b { color: var(--good); }
.save-link.is-paused b, .save-link.is-lost b { color: var(--cond); }
.save-link .text-btn { font-size: var(--fs-xs); }
.save-ask { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-1); }
.save-ask-q { padding-inline: var(--sp-4); font-size: var(--fs-sm); color: var(--ink-strong); }
/* Under the question: only the site's copy goes, the game's file isn't touched. */
.save-ask-note { max-width: 26ch; padding-inline: var(--sp-4); font-size: var(--fs-xs); line-height: 1.4; color: var(--muted); text-wrap: pretty; }
.save-ask-yn { display: flex; gap: var(--sp-2); }
/* The slot's buttons, as the game's menu items (js/app-saves.js, FLEURS): capitals in the menu's
   face, no box, and the menu's two pointers fading in on either side of the one you're on.
   Quieter than the slot itself: they're what you do to it, not what it holds. */
.save-act {
  position: relative; display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: var(--ctl); padding: var(--sp-1) var(--sp-4);
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-xs); letter-spacing: var(--track-caps); text-transform: uppercase;
  color: var(--label); transition: color var(--dur) var(--ease);
}
.save-act:hover, .save-act:focus-visible { color: var(--ink-strong); }
.save-act.is-clear:hover, .save-act.is-clear:focus-visible { color: var(--bad); }
/* The pointers are the focus mark, as in the game's menus: no ring on top of them. */
.save-act:focus-visible { outline: none; }
.save-act-short { display: none; }
.save-fleur {
  position: absolute; top: 50%; width: var(--sp-3); height: calc(var(--sp-4) + var(--sp-1)); display: flex; color: var(--ink-strong);
  opacity: 0; transform: translateY(-50%) scaleX(0.3); pointer-events: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.save-fleur svg { width: 100%; height: 100%; }
.save-fleur.is-l { left: calc(var(--sp-1) * -1); }
.save-fleur.is-r { right: calc(var(--sp-1) * -1); }
.save-fleur.is-r svg { transform: scaleX(-1); }
.save-act:hover .save-fleur, .save-act:focus-visible .save-fleur,
.save-main:hover .save-new .save-fleur, .save-main:focus-visible .save-new .save-fleur { opacity: 1; transform: translateY(-50%) scaleX(1); }
/* The icons, in the text's ink. Import's arrow dips into the tray; Clear's bin lifts its lid. */
.save-ico { width: var(--sp-4); height: var(--sp-4); flex-shrink: 0; overflow: visible; }
.save-ico-arrow, .save-ico-lid { transition: transform var(--dur) var(--ease); transform-box: fill-box; }
.save-ico-lid { transform-origin: 100% 100%; }
.save-act.is-import:hover .save-ico-arrow, .save-act.is-import:focus-visible .save-ico-arrow { transform: translateY(1.5px); }
.save-act.is-clear:hover .save-ico-lid, .save-act.is-clear:focus-visible .save-ico-lid { transform: translate(0.5px, -1.5px) rotate(14deg); }
.save-ico-turn { transform-origin: 50% 50%; transition: transform var(--dur-slow) var(--ease); transform-box: fill-box; }
.save-act.is-follow:hover .save-ico-turn, .save-act.is-follow:focus-visible .save-ico-turn { transform: rotate(180deg); }
/* Free mode, above the four and apart from them: nobody's game, so a sheet of its own, with
   Hornet standing instead of a number and its name on its own row. */
.saves-list.is-free {
  margin-bottom: var(--sp-8); border: 1px solid var(--inv-line);
  background: linear-gradient(180deg, color-mix(in oklab, var(--spotlight) 5%, transparent), transparent 70%);
}
.saves-list.is-free .save { border-top: 0; }
.save-note { max-width: calc(var(--sp-16) * 3); padding-inline: var(--sp-6); font-size: var(--fs-sm); line-height: 1.5; color: var(--muted); text-wrap: pretty; }
.save.is-free .save-main { grid-template-areas: "n name nail" "n hud nail" "n facts nail"; padding-block: var(--sp-6); }
.save.is-free .save-n {
  position: relative; isolation: isolate; display: grid; place-items: center;
}
.save.is-free .save-n::before {
  content: ''; position: absolute; z-index: -1; width: calc(var(--sp-16) * 1.5); aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(closest-side, color-mix(in oklab, var(--spotlight) 20%, transparent), transparent);
}
.save.is-free .save-n img { height: var(--sp-12); width: auto; }
.save-name { grid-area: name; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-4); }
.save-name b { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-caps); font-size: var(--fs-md); color: var(--ink-strong); }
/* An empty slot is its one way in, where the game's says New Game: the number unlit and «Import
   from the game», with its tray, between the menu's pointers (a save only comes from the file). */
.save.is-empty .save-main { grid-template-areas: "n new new"; min-height: calc(var(--sp-16) + var(--sp-6)); }
.save.is-empty .save-n { color: var(--muted); transition: color var(--dur) var(--ease); }
.save-new {
  grid-area: new; justify-self: start; position: relative; display: inline-flex; align-items: center; gap: var(--sp-2);
  margin-inline-start: calc(var(--sp-4) * -1); padding-inline: var(--sp-4);
  font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-caps); font-size: var(--fs-md);
  color: var(--ink-2); transition: color var(--dur) var(--ease);
}
.save.is-empty .save-main:hover .save-new, .save.is-empty .save-main:focus-visible .save-new,
.save.is-empty .save-main:hover .save-n { color: var(--ink-strong); }
.save.is-empty .save-main:hover .save-ico-arrow, .save.is-empty .save-main:focus-visible .save-ico-arrow { transform: translateY(1.5px); }
/* Going into a game (js/app-saves.js, leave): the slot's nail catches the light and its masks
   glow before the page fades. */
.save.is-loading { pointer-events: none; }
.save.is-loading .save-nail img { animation: lit-in 600ms var(--ease) both; }
.save.is-loading .save-masks img { animation: light-up 600ms var(--ease) both; }

/* Clear Save, seen (js/app-saves.js, shatter): the masks break from the right one after another
   (a flash and gone, as on the HUD), and the rest of the slot fades
   while dust rises out of it. Then the slot is cleared and its Import fades in. */
.save { position: relative; }
.save-where { flex-basis: 100%; font-family: var(--font-display); font-size: var(--fs-sm); letter-spacing: var(--track-title); color: var(--ink); }
.save.is-clearing { pointer-events: none; }
.save.is-clearing .save-foot { opacity: 0; transition: opacity var(--dur) var(--ease); }
.save.is-clearing .save-masks img { animation: save-break 420ms var(--ease) both; animation-delay: calc(var(--r) * 70ms); }
.save.is-clearing .save-main { animation: save-fade 700ms var(--ease-fade) calc(var(--masks) * 70ms + 150ms) both; }
.save.is-clearing { background: transparent; }
.save-dust { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.save-dust i {
  position: absolute; left: var(--x); top: var(--y); width: var(--s); height: var(--s); border-radius: 50%;
  background: var(--mote); box-shadow: 0 0 6px var(--mote); opacity: 0;
  animation: save-dust 900ms var(--ease-fade) both; animation-delay: calc(var(--masks) * 70ms + var(--d) * 0.6);
}
.save.is-cleared .save-new, .save.is-cleared .save-n, .save.is-cleared .save-foot { animation: title-in 700ms var(--ease-fade) both; }
@keyframes save-break {
  30% { opacity: 1; filter: brightness(2.6); transform: scale(1.12); }
  100% { opacity: 0; filter: brightness(2.6); transform: scale(0.7); }
}
@keyframes save-drain { to { opacity: 0; filter: brightness(0.4); } }
@keyframes save-fade { to { opacity: 0; filter: blur(3px); } }
@keyframes save-dust {
  0% { opacity: 0; transform: translate(0, 0); }
  25% { opacity: var(--o); }
  100% { opacity: 0; transform: translate(var(--dx), -48px); }
}
/* ── The import view ────────────────────────────────────────────────────
   It takes the list's place on the same sheet. Left, the three steps to find the game's file,
   each on a medallion (the game's achievement icons: a clean circle with a white line) joined by
   a thread; right, the zone to drop it on: Hornet under a light that breathes, as a bench's
   lamp does, and brightens when a file comes over it. Once read, the zone shows the save as the
   game's profile screen would (masks appearing one by one, the nail, the time and the %) before
   anything is written. Only fades, and Hornet floats (design/02 §5: no slides, no bounce). */
.imp-bar { display: flex; margin-bottom: calc(var(--sp-4) * -1); }
.imp-back { gap: var(--sp-2); min-height: var(--ctl); font-size: var(--fs-sm); }
.imp .saves-note { margin-inline: auto; }
.imp-mobile {
  max-width: var(--measure); margin: var(--sp-6) auto 0; padding: var(--sp-3) var(--sp-4);
  border-left: 2px solid var(--bracket); background: var(--card); font-size: var(--fs-sm); color: var(--ink-2);
}
.imp-grid {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: var(--sp-8);
  align-items: stretch; margin-top: var(--sp-8);
}

.imp-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.imp-step { position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-4); padding-bottom: var(--sp-8); }
.imp-step:last-child { padding-bottom: 0; }
/* The thread from one medallion to the next. */
.imp-step:not(:last-child)::before {
  content: ''; position: absolute; left: calc(var(--sp-12) / 2); top: var(--sp-12); bottom: 0; width: 1px;
  background: linear-gradient(var(--bracket), transparent);
}
.imp-num {
  display: grid; place-items: center; width: var(--sp-12); height: var(--sp-12); border-radius: 50%;
  border: 1px solid var(--bracket); background: var(--card);
  box-shadow: inset 0 0 0 3px var(--scene-bg), inset 0 0 0 4px color-mix(in oklab, var(--spotlight) 18%, transparent);
  font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-lg); color: var(--ink-strong);
}
.imp-step-body { min-width: 0; padding-top: var(--sp-2); display: flex; flex-direction: column; gap: var(--sp-3); }
.imp-step-title { margin: 0; font-family: var(--font-display); font-weight: 600; font-size: var(--fs-lg); letter-spacing: var(--track-title); line-height: 1.25; color: var(--ink-strong); }
.imp-text { margin: 0; max-width: var(--measure); font-size: var(--fs-sm); line-height: 1.6; color: var(--ink-2); text-wrap: pretty; }
.imp-os { align-self: flex-start; }
.imp-path {
  display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line-ctl); background: var(--card);
}
.imp-path code { flex: 1; min-width: 0; font-family: var(--font-num); font-size: var(--fs-sm); color: var(--ink); overflow-wrap: anywhere; user-select: all; }
.imp-copy { flex-shrink: 0; font-size: var(--fs-sm); }
.imp-copy.is-done { color: var(--ink-strong); text-decoration-color: transparent; }
.imp-copy.is-done::before { content: '✓ '; }
kbd {
  display: inline-block; padding: 0 var(--sp-1); border: 1px solid var(--line-ctl); border-bottom-width: 2px;
  background: var(--card); font-family: var(--font-num); font-size: var(--fs-xs); line-height: 1.5; color: var(--ink-strong); white-space: nowrap;
}
.imp-files { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.imp-files li {
  display: inline-flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--line); font-family: var(--font-num); font-size: var(--fs-xs); color: var(--ink);
}
.imp-files li.is-bak { color: var(--muted); border-style: dashed; text-decoration: line-through; text-decoration-color: var(--muted); }
.imp-ficon { flex-shrink: 0; color: var(--label); }

.imp-drop {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; flex-direction: column; min-height: calc(var(--sp-16) * 7);
  border: 1px dashed color-mix(in oklab, var(--spotlight) 32%, transparent); background: var(--scene-bg);
  transition: border-color var(--dur) var(--ease);
}
.imp-light {
  position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: 0.7;
  background: radial-gradient(55% 48% at 50% 40%, color-mix(in oklab, var(--spotlight) 17%, transparent), transparent 72%);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}
.imp-drop.is-drag { border-style: solid; border-color: var(--accent-line); }
.imp-drop.is-drag .imp-light { opacity: 1; transform: scale(1.15); }
.imp-drop-in { flex: 1; display: flex; align-items: center; justify-content: center; padding: var(--sp-8) var(--sp-6) var(--sp-4); }
.imp-still { display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); text-align: center; }
.imp-figure { height: calc(var(--sp-16) * 2.25); width: auto; margin-bottom: var(--sp-3); transition: opacity var(--dur-slow) var(--ease), filter var(--dur-slow) var(--ease); }
.imp-figure.is-fallen { height: calc(var(--sp-16) * 1.75); }
.imp-drop.is-drag .imp-figure { filter: drop-shadow(0 0 18px color-mix(in oklab, var(--spotlight) 45%, transparent)); }
.imp-drop[data-state="reading"] .imp-figure { opacity: 0.45; }
.imp-drop-title, .imp-drop-drag { margin: 0; font-family: var(--font-display); font-weight: 600; font-size: var(--fs-xl); letter-spacing: var(--track-title); color: var(--ink-strong); }
.imp-drop-drag, .imp-drop.is-drag .imp-drop-title { display: none; }
.imp-drop.is-drag .imp-drop-drag { display: block; }
.imp-drop.is-drag .imp-or, .imp-drop.is-drag .imp-still .btn { visibility: hidden; }
.imp-or { margin: 0; font-size: var(--fs-sm); color: var(--muted); }
.imp-err { margin: 0; max-width: 30ch; font-size: var(--fs-md); color: var(--ink); text-wrap: balance; }
.imp-private { margin: 0; padding: 0 var(--sp-6) var(--sp-4); font-size: var(--fs-xs); color: var(--muted); text-align: center; text-wrap: balance; }

.imp-found {
  display: grid; grid-template-columns: auto auto; justify-content: center; align-items: center;
  grid-template-areas: "fname fname" "nail hud" "nail meta" "facts facts" "warn warn" "sync sync" "actions actions";
  gap: var(--sp-3) var(--sp-6); text-align: center;
}
.imp-fname { grid-area: fname; margin: 0 0 var(--sp-2); display: inline-flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: var(--sp-2); font-family: var(--font-num); font-size: var(--fs-sm); color: var(--ink-2); }
.imp-steel { margin-left: var(--sp-2); color: var(--ink-strong); }
.imp-hud { grid-area: hud; display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); justify-content: flex-start; }
.imp-hud .save-masks img { height: var(--sp-8); }
.imp-nail { grid-area: nail; display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); font-size: var(--fs-xs); color: var(--ink-2); }
.imp-nail img { height: calc(var(--sp-16) + var(--sp-8)); width: auto; }
.imp-meta { grid-area: meta; margin: 0; display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); align-items: baseline; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-sm); color: var(--ink); }
.imp-meta b { font-weight: 600; color: var(--ink-strong); }
.imp-ver { font-family: var(--font-body); font-size: var(--fs-xs); color: var(--muted); }
.imp-meta .save-k { margin-right: var(--sp-1); }
.imp-facts { grid-area: facts; justify-content: center; margin: var(--sp-2) 0 0; }
.imp-warn { grid-area: warn; margin: 0; font-size: var(--fs-sm); color: var(--bad); }
/* Following the game (js/live.js): a row of the game's options menu. What it is in the text's
   ink, and its value on a button (.btn, its frame says it can be pressed) with the rule's
   diamond: lit in bone when it's on, hollow and dimmer when it's off. */
.imp-sync { grid-area: sync; justify-self: center; display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); max-width: 44ch; margin-top: var(--sp-3); }
.imp-sync-k { font-size: var(--fs-md); color: var(--ink-strong); }
.imp-sync-v { font-size: var(--fs-md); }
.imp-sync-v[aria-checked="true"] { color: var(--ink-strong); }
.imp-sync-note { margin: 0; font-size: var(--fs-sm); line-height: 1.45; color: var(--muted); text-wrap: pretty; }
.imp-actions { grid-area: actions; display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); margin-top: var(--sp-3); }

@media (prefers-reduced-motion: no-preference) {
  .imp.is-fresh .imp-step { animation: title-in var(--dur-slow) var(--ease-fade) both; animation-delay: calc(var(--i) * 140ms + 100ms); }
  .imp.is-fresh .imp-drop { animation: title-in 700ms var(--ease-fade) 300ms both; }
  .imp-light { animation: imp-breathe 5s var(--ease-fade) infinite alternate; }
  .imp-drop.is-drag .imp-light { animation: none; }
  .imp-figure { animation: imp-float 4.2s ease-in-out infinite; }
  .imp-figure.is-fallen { animation: title-in var(--dur-slow) var(--ease-fade) both, imp-float 3.2s ease-in-out infinite; }
  .imp-drop[data-state="reading"] .imp-drop-title { animation: notch-pending 700ms var(--ease-fade) infinite alternate; }
  .imp-drop-in > * { animation: title-in var(--dur-slow) var(--ease-fade) both; }
  .imp-hud img { animation: mask-mend 500ms var(--ease) both; animation-delay: calc(var(--i) * 70ms + 150ms); }
  .imp-meta, .imp-facts, .imp-warn, .imp-actions { animation: title-in 600ms var(--ease-fade) 600ms both; }
}
@keyframes imp-breathe { from { opacity: 0.5; } to { opacity: 0.85; } }
@keyframes imp-float { 50% { transform: translateY(-6px); } }
/* In the header, Hornet and the slot you're playing; on mobile, only the number beside her. */
/* The save selector (saveLink): Hornet at a bench, under a lamp's light that breathes, and
   the label in the game's menu capitals, like the slots' buttons (.save-act). It's the way into
   your saves, so it reads as pressable at rest (27-sep): Hornet at full light and the menu's
   pointers already either side of the label, small; on hover or focus they open whole, the lamp
   swells and Hornet glows (the focus mark). */
.mh-save {
  display: inline-flex; align-items: center; gap: 0; min-height: var(--ctl); white-space: nowrap; text-decoration: none;
  font-family: var(--font-display); font-weight: 600; font-size: var(--fs-xs); letter-spacing: var(--track-caps); text-transform: uppercase;
  color: var(--ink); transition: color var(--dur) var(--ease);
}
.mh-save:focus-visible { outline: none; }
.mh-save-fig { position: relative; isolation: isolate; display: grid; place-items: center; width: var(--sp-12); height: var(--sp-12); }
.mh-save-fig img { height: var(--sp-12); width: auto; transition: filter var(--dur) var(--ease); }
.mh-save-light {
  position: absolute; inset: -25%; z-index: -1; pointer-events: none; opacity: 0.7;
  background: radial-gradient(closest-side, color-mix(in oklab, var(--spotlight) 22%, transparent), transparent);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}
.mh-save-lbl { position: relative; padding-inline: var(--sp-4); }
.mh-save .save-fleur { opacity: 0.5; transform: translateY(-50%) scaleX(0.6); }
.mh-save:is(:hover, :focus-visible, [aria-current="page"]) { color: var(--ink-strong); }
.mh-save:is(:hover, :focus-visible, [aria-current="page"]) .mh-save-fig img { filter: drop-shadow(0 0 6px var(--mote)); }
.mh-save:is(:hover, :focus-visible) .mh-save-light { opacity: 1; transform: scale(1.15); }
.mh-save:is(:hover, :focus-visible) .save-fleur { opacity: 1; transform: translateY(-50%) scaleX(1); }
@media (prefers-reduced-motion: no-preference) {
  .mh-save-light { animation: imp-breathe 5s var(--ease-fade) infinite alternate; }
  .mh-save:is(:hover, :focus-visible) .mh-save-light { animation: none; }
}
.mh-save-short { display: none; }
/* Following the game (js/live.js): under the label, the rule's diamond and the state in small
   capitals. Live, the diamond lit in bone, breathing like the lamp; paused or with the file
   missing, hollow, in the notice's colour. On mobile, only the diamond, over the save's number. */
.mh-save-lbl:has(.mh-live) { display: inline-flex; flex-direction: column; align-items: flex-start; gap: var(--sp-half); }
.mh-live { display: inline-flex; align-items: center; gap: var(--sp-2); font-family: var(--font-body); font-weight: 600; font-size: var(--fs-2xs); letter-spacing: var(--track-label); color: var(--ink-2); }
.mh-live-dot { width: var(--sp-2); height: var(--sp-2); transform: rotate(45deg); border: 1px solid currentColor; }
.mh-save.is-live .mh-live-dot { background: var(--ink-strong); border-color: var(--ink-strong); box-shadow: 0 0 8px color-mix(in oklab, var(--ink-strong) 55%, transparent); }
.mh-save.is-paused .mh-live, .mh-save.is-lost .mh-live { color: var(--cond); }
@media (prefers-reduced-motion: no-preference) {
  .mh-save.is-live .mh-live-dot { animation: live-breathe 2.4s var(--ease-fade) infinite alternate; }
}
@keyframes live-breathe { from { opacity: 0.55; } to { opacity: 1; } }
@media (max-width: 899px) {
  /* A slot takes the whole row, and a full one's menu (or free mode's note) goes under it. */
  .save .save-main { grid-column: 1 / -1; }
  .save.is-free .save-note { grid-column: 1 / -1; max-width: none; margin: 0; padding: 0 var(--sp-4) var(--sp-6) calc(var(--sp-12) + var(--sp-6) + var(--sp-4)); border-left: 0; }
  .save:not(.is-free) .save-foot {
    grid-column: 1 / -1; flex-direction: row; flex-wrap: wrap; justify-content: flex-end; align-items: center;
    margin: calc(var(--sp-2) * -1) 0 0; padding: 0 var(--sp-2) var(--sp-2); border-left: 0;
  }
  .save-ask { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: flex-end; }
  .save-ask-note { order: 1; flex: 1 1 100%; max-width: none; text-align: right; }
  .save-link { flex: 1 0 100%; flex-direction: row; flex-wrap: wrap; align-items: baseline; justify-content: flex-end; gap: var(--sp-1) var(--sp-4); padding: 0 var(--sp-3); }
  .imp-grid { grid-template-columns: minmax(0, 1fr); }
  .imp-drop { min-height: calc(var(--sp-16) * 6); }
}
@media (max-width: 599px) {
  .saves-body { --inv-pad: var(--sp-4); }
  .imp-found { grid-template-areas: "fname fname" "nail hud" "meta meta" "facts facts" "warn warn" "sync sync" "actions actions"; }
  .imp-meta { justify-content: center; }
  .imp-bar { margin-bottom: var(--sp-2); }
  /* On a phone an empty slot says only «Import». */
  .save.is-empty .save-main { min-height: calc(var(--sp-16) + var(--sp-2)); }
  .save-act-long { display: none; }
  .save-act-short { display: inline; }
  .save-act { padding-inline: var(--sp-3); letter-spacing: var(--track-title); }
  .save-new { font-size: var(--fs-sm); letter-spacing: var(--track-caps); }
  .imp-hud .save-masks img { height: var(--sp-6); }
  .save-main { grid-template-columns: var(--sp-8) minmax(0, 1fr) var(--sp-8); gap: var(--sp-2) var(--sp-3); padding: var(--sp-3) var(--sp-2); }
  .save.is-free .save-main { padding-block: var(--sp-4) var(--sp-2); }
  .save.is-free .save-note { padding: 0 var(--sp-2) var(--sp-4) calc(var(--sp-8) + var(--sp-3) + var(--sp-2)); font-size: var(--fs-xs); }
  .save-new { white-space: nowrap; }
  /* The facts as a little table: the label, then the figure lined up on the right. */
  .save-facts { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: baseline; gap: var(--sp-1) var(--sp-half); }
  .save-where { grid-column: 1 / -1; }
  .save-fact { display: contents; }
  .save-fact b { text-align: right; }
  .save.is-free .save-n::before { width: var(--sp-16); }
  .save.is-free .save-n img { height: var(--sp-8); }
  .save-masks img { height: var(--sp-4); }
  .save-nail img { height: var(--sp-12); }
  .save-nail span { display: none; }
  /* On a phone Hornet alone didn't say "your saves": larger, with a short word under her
     (Saves, or Save 1 after the state's diamond when it follows the game), in a 44 px target. */
  .mh-save-lbl, .mh-save-lbl:has(.mh-live) { display: none; }
  .mh-save { flex-direction: column; justify-content: center; gap: var(--sp-half); min-width: var(--ctl); min-height: var(--ctl); }
  .mh-save-fig { width: var(--sp-8); height: var(--sp-8); }
  .mh-save-fig img { height: var(--sp-8); }
  .mh-save-short {
    display: inline-flex; align-items: center; gap: var(--sp-1);
    font-family: var(--font-body); font-size: var(--fs-2xs); letter-spacing: 0; text-transform: none; line-height: 1.2;
  }
  .mh-save:is(.is-live, .is-paused, .is-lost) .mh-save-short::before {
    content: ''; flex: none; width: var(--diamond); height: var(--diamond); transform: rotate(45deg); border: 1px solid var(--cond);
  }
  .mh-save.is-live .mh-save-short::before { border-color: var(--ink-strong); background: var(--ink-strong); box-shadow: 0 0 8px color-mix(in oklab, var(--ink-strong) 55%, transparent); }
}


@keyframes lit-in {
  from { filter: brightness(0.2) saturate(0); }
  40% { filter: brightness(1.8) drop-shadow(0 0 8px var(--focus-glow)); }
}
@keyframes light-up { 30% { filter: brightness(1.9) drop-shadow(0 0 10px var(--focus-glow)); opacity: 1; } }
@keyframes mask-mend { from { filter: brightness(2.4); opacity: 0; transform: scale(0.5); } }
@keyframes notch-pending { to { opacity: 0.55; } }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Your game, the start screen (js/app-home.js) ─────────────────────────
   In free mode, the ghost bench: the card a save shows, unlit, and the file dropped here goes
   into the first empty save. In a save: the bench card, three columns and the ways on (design/03-redesign.md,
   step 3). Figures in --font-num, as everywhere. */
/* Hornet on the bench she rests at (js/benches.js, from the game's files): both of the wiki's
   sprites at one scale (--hn-rest), her hem on the bench's seat; the box is theirs together
   (--w, its width in the sprites' pixels, and its aspect ratio, set inline with the places). */
.hm-rest { position: relative; display: block; width: calc(var(--w) * var(--hn-rest) * 1px); max-width: 100%; margin-block: var(--sp-2); filter: drop-shadow(0 0 18px color-mix(in oklab, var(--spotlight) 18%, transparent)); }
.hm-rest img { position: absolute; max-width: none; height: auto; }
.hm-invite-acts { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--sp-6); margin-top: var(--sp-1); }
/* Free mode (the ghost bench): the card unlit, the kingdom's name in faint ink, Hornet a
   silhouette with a faint rim and the figures waiting. Dropping a file on it lights it. */
.hm-card.is-ghost { border: 1px dashed transparent; transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease); }
.hm-card.is-ghost::before { background: radial-gradient(ellipse 50% 70% at 50% 45%, var(--scene-glow-low), transparent 75%); }
.is-ghost .hm-sup { color: var(--muted); }
.is-ghost .hm-area { color: color-mix(in oklab, var(--ink) 30%, transparent); text-shadow: none; }
/* The bench and Hornet one silhouette. */
.is-ghost .hm-rest { filter: brightness(0) drop-shadow(0 0 1px color-mix(in oklab, var(--ink) 45%, transparent)); opacity: 0.85; }
.is-ghost .hm-fig b { color: var(--line-ctl); }
.hm-ghost { margin: var(--sp-2) 0 0; font-family: var(--font-display); font-size: var(--fs-lg); letter-spacing: var(--track-title); color: var(--ink-strong); }
.hm-invite.is-drag { border-color: var(--accent-line); background-color: var(--accent-soft); }
.hm-check { margin: var(--sp-1) 0 0; font-size: var(--fs-xs); color: var(--muted); text-align: center; }
.hm-check.is-off { color: var(--cond); }
/* The bench card: the game's area title (a small «Resting at» over the area's name in its capitals),
   on the area's own light from the map screen (--area-line, --area-fill: set inline from the
   area's tokens, js/app-home.js). The name is the area's line; the light its fill, rising
   behind Hornet. */
.hm-card {
  --area-line: var(--spotlight); --area-fill: var(--scene-glow);
  position: relative; isolation: isolate; display: grid; justify-items: center; gap: var(--sp-2);
  padding: var(--sp-6) var(--sp-4) var(--sp-4); margin: 0 0 var(--sp-6); text-align: center;
}
.hm-card::before {
  content: ''; position: absolute; inset: calc(var(--sp-4) * -1) 0 0; z-index: -1; pointer-events: none;
  background: radial-gradient(ellipse 60% 75% at 50% 40%, color-mix(in oklab, var(--area-fill) 85%, transparent), transparent 75%);
}
.hm-sup { margin: 0; font-family: var(--font-display); font-size: var(--fs-sm); letter-spacing: var(--track-wide); text-transform: uppercase; color: color-mix(in oklab, var(--area-line) 70%, var(--ink-2)); }
.hm-area {
  margin: 0; font-family: var(--font-display); font-weight: 400; font-size: var(--fs-hero); line-height: 1; letter-spacing: var(--track-title);
  text-transform: uppercase; color: var(--area-line); text-wrap: balance;
  text-shadow: 0 0 24px color-mix(in oklab, var(--area-line) 35%, transparent);
}
.hm-sub { margin: 0; font-size: var(--fs-sm); color: var(--ink-2); }
.hm-figs { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-4) var(--sp-12); }
.hm-fig { display: grid; justify-items: center; gap: var(--sp-1); }
.hm-fig-k { font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--label); }
.hm-fig b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xl); font-weight: 600; line-height: 1; color: var(--ink-strong); }
.hm-fig:first-child b { font-size: var(--fs-2xl); }
.hm-fig .u { font-size: var(--fs-xs); color: var(--muted); }
.hm-road { margin: 0 0 var(--sp-6); text-align: center; font-size: var(--fs-sm); }
/* The map of the area she rests in (js/app-map.js, App.mapAreaHtml): beside the card on a
   computer, under it on a phone. The Map's own drawing on a canvas, cropped to the area's box
   (--r: its width over its height), faded at its edges, with Hornet on her bench and the Map's own
   marks over it. */
.hm-hero { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-4) var(--sp-8); align-items: center; }
.hm-hero > .hm-card { margin-bottom: 0; }
.hm-hero { margin-bottom: var(--sp-6); }
/* In half the width the area's name is smaller, so that a long one wraps instead of running under the map. */
@media (min-width: 900px) {
  .hm-hero.has-map { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hm-hero.has-map .hm-area { font-size: clamp(var(--fs-2xl), 1rem + 3vw, 4rem); }
}
.hmB-map { display: grid; justify-items: center; margin: 0; min-width: 0; }
/* No frame: the drawing fades out on every side (the canvas only: the marks stay as the Map has them). */
.hmB-view { position: relative; display: block; width: min(100%, calc(var(--hm-map-max) * var(--r))); aspect-ratio: var(--r); }
.hmB-view:focus-visible { outline: 1px solid var(--ink-2); outline-offset: var(--sp-1); }
.hmB-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; filter: var(--map-tone);
  -webkit-mask: radial-gradient(ellipse closest-side at 50% 50%, #000 62%, transparent 100%); mask: radial-gradient(ellipse closest-side at 50% 50%, #000 62%, transparent 100%); }
/* The Map's own marks over it (.mp-mk, sized by --k as there), as pictures. */
.hmB-svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.hmB-svg .mp-plate { fill: url(#hmB-plate); }
#hmB-plate stop { stop-color: var(--map-plate); }
#hmB-plate stop:nth-child(2) { stop-color: var(--map-plate-mid); }
#hmB-plate stop:last-child { stop-color: transparent; }
/* Three columns under the card: since the previous save, closest to the bench, the 100% by part. */
.hm-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: var(--sp-8); }
.hm-col { display: grid; gap: var(--sp-3); align-content: start; }
/* One column alone (no previous save, a bench off the room graph) keeps a list's measure. */
.hm-cols:has(> :only-child) { max-width: var(--column); margin-inline: auto; }
.hm-col > .text-btn { justify-self: start; font-size: var(--fs-sm); }
/* The lists' rows (design/03-redesign.md, step 3, the lists round, A, Progress's clean rows): the
   thing's picture, its name in the game's face with where under it, and at the end the Map's pin.
   The picture: plain, a Tool on its slot's diamond, or the map's own mark (a piece's disc, a
   gauntlet's champion). */
.hm-rows { list-style: none; margin: 0; padding: 0; }
.hm-row { display: grid; grid-template-columns: var(--hm-art) minmax(0, 1fr) auto; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-1); border-bottom: 1px solid var(--line-row); }
.hm-row:last-child { border-bottom: 0; }
.hm-row-txt { display: grid; min-width: 0; }
.hm-row-name { font-family: var(--font-display); font-size: var(--fs-sm); color: var(--ink-strong); overflow-wrap: anywhere; }
.hm-row-sub { font-size: var(--fs-xs); color: var(--muted); }
.hm-art { position: relative; display: grid; place-items: center; width: var(--hm-art); height: var(--hm-art); }
.hm-art img { position: relative; width: calc(var(--hm-art) - var(--sp-1)); height: calc(var(--hm-art) - var(--sp-1)); object-fit: contain; }
.hm-art.is-slot::before { content: ''; position: absolute; inset: 22%; transform: rotate(45deg); border: 1.5px solid var(--c); background: color-mix(in oklab, var(--c) 14%, var(--scene-bg)); }
.hm-art.is-slot img { width: 72%; height: 72%; }
.hm-art.is-mark :is(.mp-dot, .mp-pin) { position: static; margin: 0; --mp-s: calc(var(--hm-art) - var(--sp-1)); }
.hm-art.is-glyph::before { content: ''; width: var(--sp-3); height: var(--sp-3); transform: rotate(45deg); background: var(--accent); box-shadow: var(--glow); }
/* A group's head inside a list: small capitals and its count. */
.hm-kind { display: flex; align-items: baseline; gap: var(--sp-2); margin: var(--sp-2) 0 0; font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.hm-kind-n { font-weight: 400; letter-spacing: 0; color: var(--muted); }
/* The Journal's entries, their portraits. */
.hm-faces { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin: 0; padding: var(--sp-1) 0; }
.hm-faces img { width: var(--sp-8); height: var(--sp-8); object-fit: contain; border-radius: 50%; border: 1px solid var(--line-ctl); background: var(--scene-bg); }
.hm-col > .text-btn[aria-expanded] { margin-top: var(--sp-1); }
/* A gauge: what there was, and what was added lit. */
.hm-bar { position: relative; display: block; height: 3px; background: var(--ring-track); }
.hm-bar i { position: absolute; inset-block: 0; left: 0; background: color-mix(in oklab, var(--accent) 45%, transparent); }
.hm-bar i + i { background: var(--accent); box-shadow: var(--glow); }
/* The 100% by part: picture, name, count, and the gauge under; a full one lit, with its diamond. */
.hm-parts { list-style: none; margin: 0; padding: 0; }
.hm-parts a { display: grid; grid-template-columns: var(--hm-art) minmax(0, 1fr) auto; align-items: center; gap: var(--sp-1) var(--sp-3); padding: var(--sp-2) var(--sp-1); border-bottom: 1px solid var(--line-row); color: var(--ink); text-decoration: none; }
.hm-part:last-child a { border-bottom: 0; }
.hm-parts .hm-art { grid-row: span 2; }
.hm-part-name { font-family: var(--font-display); font-size: var(--fs-sm); color: var(--ink-strong); transition: color var(--dur) var(--ease); }
.hm-parts a:is(:hover, :focus-visible) .hm-part-name { color: var(--accent); }
.hm-part-n { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-sm); color: var(--ink-strong); white-space: nowrap; }
.hm-part-n .u { font-size: var(--fs-2xs); font-style: normal; color: var(--muted); }
.hm-parts .hm-bar { grid-column: 2 / -1; }
.hm-part.is-full .hm-bar i { background: var(--accent); }
.hm-part.is-full .hm-part-n::before { content: ''; display: inline-block; width: var(--diamond); height: var(--diamond); margin-right: var(--sp-2); transform: rotate(45deg); background: var(--accent); box-shadow: var(--glow); vertical-align: 1px; }
.hm-part.is-none .hm-art img { filter: var(--missing-soft); opacity: var(--missing-soft-opacity); }
.hm-row, .hm-parts a { transition: background-color var(--dur) var(--ease); }
.hm-parts a:hover { background: var(--veil); }
/* Arriving at the screen, the rows fade in one by one (js/app-home.js, .is-enter). */
@media (prefers-reduced-motion: no-preference) {
  .hm.is-enter :is(.hm-row, .hm-kind, .hm-faces, .hm-part) { animation: hm-row-in var(--dur-slow) calc(var(--i, 0) * var(--dur-step)) var(--ease-fade) both; }
}
@keyframes hm-row-in { from { opacity: 0; transform: translateY(var(--sp-1)); } }
/* The ways on: the game's pictures over the screen's name with the accent's arrow, stepping
   forward under the finger (the sibling's). */
.hm-next { margin-top: var(--sp-8); padding-top: var(--sp-6); border-top: 1px solid var(--inv-line); }
.hm-next ul { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-4); }
.hm-go { display: grid; justify-items: center; gap: var(--sp-1); padding: var(--sp-3); text-align: center; text-decoration: none; font-size: var(--fs-xs); color: var(--ink-2); }
.hm-go img { width: var(--sp-12); height: var(--sp-12); object-fit: contain; transition: filter var(--dur) var(--ease); }
.hm-go b { font-size: var(--fs-md); font-weight: 600; color: var(--ink-strong); }
.hm-go b::after { content: ' →'; display: inline-block; color: var(--accent); transition: transform var(--dur) var(--ease); }
.hm-go:is(:hover, :focus-visible) b::after { transform: translateX(var(--sp-1)); }
.hm-go:is(:hover, :focus-visible) img { filter: drop-shadow(0 0 8px color-mix(in oklab, var(--spotlight) 45%, transparent)); }
@media (max-width: 599px) {
  .hm-area { font-size: var(--fs-2xl); }
  .hm-figs { gap: var(--sp-4) var(--sp-8); }
  .hm-next ul { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Progress (js/app-progress.js): the ledger ────────────────────────────
   The sibling's (design/03-redesign.md, step 5): the 100% large, the road to the next Act at a
   glance, then each category as a row: its name, its things as the game's pictures or as pips,
   its count and the disclosure's ring; open, its things one per line: a tick when you have it,
   a Tool's slot colour (data, the slot tokens), the name, and its Act and area on the right,
   quiet. What belongs to a later Act than yours dims. */
.pg-total { display: grid; justify-items: center; gap: var(--sp-1); margin: var(--sp-6) 0 var(--sp-4); text-align: center; }
.pg-total .lbl { margin: 0; }
/* The figure in a ring that fills with it (design/20-progress-total-variants.html, A): the
   Journal's Memento made larger, a thin filigree circle inside, the figure in the game's face. */
.pg-ring { position: relative; display: grid; place-content: center; justify-items: center; gap: var(--sp-1); width: var(--pg-ring); aspect-ratio: 1; border-radius: 50%; }
.pg-ring::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(var(--accent) calc(var(--p) * 3.6deg), var(--ring-track) 0);
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - var(--ring) * 2), var(--ring-mask) calc(100% - var(--ring) * 2 + 1px));
  mask: radial-gradient(closest-side, transparent calc(100% - var(--ring) * 2), var(--ring-mask) calc(100% - var(--ring) * 2 + 1px));
  filter: drop-shadow(0 0 8px color-mix(in oklab, var(--spotlight) 40%, transparent));
}
.pg-ring::after { content: ''; position: absolute; inset: var(--sp-4); border-radius: 50%; border: 1px solid color-mix(in oklab, var(--filigree) 25%, transparent); }
.pg-total-n { margin: 0; font-family: var(--font-display); line-height: 1; color: var(--ink-strong); text-shadow: 0 0 24px color-mix(in oklab, var(--spotlight) 35%, transparent); }
.pg-total-n b { font-size: calc(var(--pg-ring) / 3); font-weight: 400; }
.pg-total-n .u { font-size: calc(var(--pg-ring) / 8); color: var(--ink-2); margin-left: 0.08em; }
.pg-ring-k { margin: 0; font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
/* The journey: the road's steps as diamonds on one silk thread, lit up to where you are. */
.pg-journey { display: grid; justify-items: center; gap: var(--sp-3); max-width: var(--column); margin: var(--sp-6) auto var(--sp-2); text-align: center; }
.pg-journey-steps { position: relative; display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); width: 100%; margin: 0; padding: 0; list-style: none; }
.pg-journey-steps::before {
  content: ''; position: absolute; top: calc(var(--sp-3) - 0.5px); left: calc(50% / var(--n)); right: calc(50% / var(--n)); height: 1px;
  background: linear-gradient(90deg, var(--silk) var(--f), color-mix(in oklab, var(--silk-rim) 35%, transparent) var(--f));
}
.pg-journey-steps li { position: relative; display: grid; justify-items: center; align-content: start; gap: var(--sp-2); }
.pg-journey-steps i { display: grid; place-items: center; width: var(--sp-6); height: var(--sp-6); }
.pg-journey-steps i::before { content: ''; width: var(--sp-3); height: var(--sp-3); transform: rotate(45deg); border: 1px solid var(--silk-rim); background: var(--scene-bg); }
.pg-journey-steps li.is-done i::before { background: var(--silk); border-color: var(--silk); box-shadow: 0 0 8px color-mix(in oklab, var(--silk) 60%, transparent); }
.pg-journey-steps li.is-cur i::before { width: calc(var(--sp-4) + var(--sp-half)); height: calc(var(--sp-4) + var(--sp-half)); border-color: var(--accent); box-shadow: var(--glow); }
.pg-journey-steps span { font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--muted); line-height: 1.3; }
.pg-journey-steps li.is-cur span { color: var(--ink-strong); }
/* The whole game (Act 3): the endings after the Acts, their diamonds set apart by a small ring. */
.pg-journey.is-game .pg-journey-steps::before { background: linear-gradient(90deg, var(--silk) calc(100% * 2 / (var(--n) - 1)), color-mix(in oklab, var(--silk-rim) 35%, transparent) 0); }
.pg-journey-steps li.is-ending i::before { border-radius: 50%; transform: none; }
.pg-journey-steps li.is-ending.is-done span { color: var(--ink-strong); }
.pg-seg { justify-content: center; }
/* The two tabs (design/27b-progress-variants.html, 1 D): two cards, each with its own picture of
   how far you are. The game selects with value, not hue: the open one white on the picked grey. */
.pg-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); max-width: calc(var(--column) + var(--sp-8)); margin: var(--sp-2) auto var(--sp-4); }
.pg-tab {
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--sp-1) var(--sp-4);
  padding: var(--sp-3) var(--sp-4); border: 1px solid var(--line); color: var(--muted); text-align: left;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.pg-tab-art { grid-row: span 2; display: grid; place-items: center; width: var(--pg-tab-art); height: var(--pg-tab-art); }
.pg-tab-art img { width: var(--pg-tab-art); height: var(--pg-tab-art); object-fit: contain; filter: var(--missing-soft); }
.pg-tab-art .pg-ring { --pg-ring: var(--pg-tab-art); }
.pg-tab-art .pg-ring::after { inset: var(--sp-2); }
.pg-tab-art .pg-total-n b { font-size: var(--fs-md); }
.pg-tab-art .pg-total-n .u { font-size: var(--fs-2xs); }
.pg-tab-l { font-family: var(--font-display); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; font-size: var(--fs-md); }
.pg-tab-s { font-size: var(--fs-xs); }
.pg-tab-s b { font-family: var(--font-num); font-size: var(--fs-lg); font-weight: 600; }
.pg-tab:is(:hover, :focus-visible) { border-color: var(--accent); color: var(--ink-2); }
.pg-tab[aria-selected="true"] { color: var(--ink-strong); border-color: var(--accent-line); background: var(--picked); box-shadow: 0 0 18px color-mix(in oklab, var(--spotlight) 12%, transparent); }
.pg-tab[aria-selected="true"] .pg-tab-art img { filter: none; }
.pg-tab[aria-selected="true"] .pg-tab-s { color: var(--ink-2); }
.pg-tab:not([aria-selected="true"]) .pg-ring::before { opacity: 0.6; }
/* What's left for the next Act (2 C): the step you're on with its picture, then one ring per
   part of it, the game's picture inside and the figure under; under each only what's missing. */
.pg-now { display: flex; align-items: center; justify-content: center; gap: var(--sp-3); max-width: var(--column); margin: var(--sp-6) auto; text-align: left; }
.pg-now img { flex: none; width: var(--sp-12); height: var(--sp-12); object-fit: contain; }
.pg-now h3 { margin: 0; font-family: var(--font-display); font-weight: 400; font-size: var(--fs-xl); color: var(--ink-strong); text-wrap: balance; }
.pg-rings { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--sp-8) var(--sp-6); max-width: calc(var(--column) + var(--sp-16)); margin: var(--sp-6) auto 0; }
/* The road's step as a summary (design/03-redesign.md, step 5, E): its parts' rings small, side by
   side, over their lists (.pg-groups); and, when there are several, the line that all are needed. */
.pg-lead { max-width: var(--column); margin: 0 auto var(--sp-4); text-align: center; font-size: var(--fs-sm); color: var(--ink-2); text-wrap: balance; }
.pg-rings.is-sum { --pg-rg-ring: calc(var(--pg-tab-art) * 2); grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr)); gap: var(--sp-6) var(--sp-3); margin: 0 auto var(--sp-8); }
.pg-rings.is-sum .pg-rg-n { font-size: var(--fs-xl); }
.pg-rings.is-sum .pg-rg-n .u { font-size: var(--fs-sm); }
@media (max-width: 599px) { .pg-rings.is-sum { --pg-rg-ring: calc(var(--pg-tab-art) * 1.5); } }
.pg-rg { display: grid; justify-items: center; align-content: start; gap: var(--sp-2); text-align: center; }
.pg-rg-ring { --pg-ring: var(--pg-rg-ring); }
.pg-rg-ring::after { inset: var(--sp-3); }
.pg-rg-ring img { position: relative; width: calc(var(--pg-rg-ring) * 0.46); height: calc(var(--pg-rg-ring) * 0.46); object-fit: contain; }
.pg-rg.is-done .pg-rg-ring::before { filter: drop-shadow(0 0 12px color-mix(in oklab, var(--silk) 70%, transparent)); }
.pg-rg-n { margin: 0; font-family: var(--font-display); font-size: var(--fs-2xl); line-height: 1; color: var(--ink-strong); }
.pg-rg-n .u { font-family: var(--font-num); font-size: var(--fs-md); color: var(--muted); }
.pg-rg.is-done .pg-rg-n b { color: var(--good); }
.pg-rg-k { margin: 0; font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
/* A medallion: a missing thing's picture in a lit circle, its name under it, a pin to the Map. */
.pg-mds { display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start; gap: var(--sp-3) var(--sp-2); margin: var(--sp-2) 0 0; padding: 0; list-style: none; }
.pg-md { position: relative; display: grid; justify-items: center; align-content: start; gap: var(--sp-1); width: calc(var(--pg-md) + var(--sp-6)); text-align: center; }
.pg-md-art {
  position: relative; display: grid; place-items: center; width: var(--pg-md); height: var(--pg-md); border-radius: 50%; border: 1px solid var(--accent-line);
  background: radial-gradient(closest-side, color-mix(in oklab, var(--spotlight) 14%, transparent), transparent);
  box-shadow: 0 0 14px color-mix(in oklab, var(--spotlight) 18%, transparent);
}
.pg-md-art img { width: calc(var(--pg-md) * 0.72); height: calc(var(--pg-md) * 0.72); object-fit: contain; }
.pg-md-badge { position: absolute; top: calc(var(--sp-1) * -1); right: calc(var(--sp-1) * -1); min-width: var(--sp-4); padding: 0 var(--sp-1); border-radius: var(--sp-3); background: var(--accent); color: var(--scene-bg); font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 700; line-height: 1.6; }
.pg-md-pin { position: absolute; top: calc(var(--pg-md) - var(--sp-6)); right: 0; display: grid; place-items: center; width: var(--sp-6); height: var(--sp-6); border-radius: 50%; border: 1px solid var(--line-ctl); background: var(--scene-bg); color: var(--ink-2); }
.pg-md-pin .ic { width: var(--sp-3); }
.pg-md-pin:is(:hover, :focus-visible) { border-color: var(--accent); color: var(--accent); }
.pg-md-name { font-size: var(--fs-2xs); line-height: 1.25; color: var(--ink); text-wrap: balance; }
.pg-md-sub { font-size: var(--fs-2xs); line-height: 1.25; color: var(--muted); text-wrap: balance; }
.pg-md-more { align-self: center; font-family: var(--font-num); font-size: var(--fs-sm); color: var(--ink-2); }
/* The steps after the one you're on: their pictures, dim. */
.pg-later { gap: var(--sp-4); max-width: calc(var(--column) + var(--sp-16)); margin: var(--sp-8) auto 0; padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.pg-md.is-later .pg-md-art { border-style: dashed; background: none; box-shadow: none; }
.pg-md.is-later .pg-md-art img { filter: var(--missing-soft); opacity: var(--missing-soft-opacity); }
.pg-md.is-later .pg-md-name { color: var(--muted); }
/* The bridge to the 100% tab, and the whole road under it. */
.pg-reach { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--sp-2) var(--sp-6); max-width: calc(var(--column) + var(--sp-16)); margin: var(--sp-8) auto 0; padding: var(--sp-4); border: 1px solid var(--line); background: color-mix(in oklab, var(--tab) 5%, transparent); }
.pg-reach p { margin: 0; display: flex; align-items: baseline; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--ink-2); }
.pg-reach b { font-family: var(--font-display); font-weight: 400; font-size: var(--fs-2xl); line-height: 1; color: var(--ink-strong); }
.pg-whole { display: flex; justify-content: center; margin-top: var(--sp-4); }
.pg-controls { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2) var(--sp-12); margin-bottom: var(--sp-2); }
.pg-part {
  display: flex; align-items: baseline; justify-content: center; gap: var(--sp-3); margin: var(--sp-8) 0 var(--sp-2);
  font-family: var(--font-display); font-size: var(--fs-xl); font-weight: 600; letter-spacing: var(--track-title); color: var(--filigree);
}
.pg-part b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-lg); color: var(--ink-2); }
.pg > .pg-note { text-align: center; }
.pg-groups { display: grid; max-width: calc(var(--column) + var(--sp-16) * 2); margin: var(--sp-4) auto 0; border-top: 1px solid var(--line-row); }
.pg-group { min-width: 0; border-bottom: 1px solid var(--line-row); }
.pg-head { margin: 0; font-size: var(--fs-md); font-weight: 400; }
.pg-open {
  display: grid; grid-template-columns: minmax(0, 13rem) minmax(0, 1fr) auto auto; align-items: center; gap: var(--sp-4);
  width: 100%; min-height: var(--sp-12); padding: var(--sp-2) 0; text-align: left; color: var(--ink);
  transition: color var(--dur) var(--ease);
}
.pg-open:is(:hover, :focus-visible) { color: var(--ink-strong); }
.pg-open:is(:hover, :focus-visible) .disc-ring, .pg-group.is-open .disc-ring { color: var(--ink-strong); border-color: var(--accent); background-color: var(--accent-soft); }
.pg-title { font-family: var(--font-body); font-size: var(--fs-md); }
.pg-group.is-done .pg-title { color: var(--ink-2); }
.pg-group.is-done .pg-count b { color: var(--good); }
.pg-strip { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-half); min-width: 0; }
.pg-strip img { width: var(--sp-6); height: var(--sp-6); object-fit: contain; }
.pg-strip img.is-missing { filter: var(--missing-soft); opacity: var(--missing-soft-opacity); }
.pg-pips { gap: 3px; }
.pg-pips i { width: var(--diamond); height: var(--diamond); transform: rotate(45deg); border: 1px solid var(--accent-line); }
.pg-pips i.is-on { background: var(--accent); border-color: var(--accent); }
.pg-group.is-open > :not(.pg-head) { margin-left: calc(13rem + var(--sp-4)); }
.pg-group.is-open > :last-child { margin-bottom: var(--sp-4); }
/* Free mode: a missing row's box marks it had (the Inventory's marks). */
.pg-mark.is-own { min-height: 0; }
@media (max-width: 599px) {
  .pg-open { grid-template-columns: minmax(0, 1fr) auto auto; }
  .pg-strip { display: none; }
  .pg-group.is-open > :not(.pg-head) { margin-left: 0; }
  /* Every step keeps its name (round 1, 7 A), smaller; Act 3's seven in small letters. */
  .pg-journey-steps span { font-size: calc(var(--fs-2xs) * 0.9); letter-spacing: 0; text-wrap: balance; }
  .pg-journey.is-game .pg-journey-steps span { text-transform: none; font-weight: 400; }
  .pg-tabs { gap: var(--sp-2); }
  .pg-tab { grid-template-columns: minmax(0, 1fr); justify-items: center; text-align: center; padding: var(--sp-2); }
  .pg-tab-art { grid-row: auto; }
  .pg-tab-l { font-size: var(--fs-xs); letter-spacing: var(--track-label); }
  .pg-now h3 { font-size: var(--fs-lg); }
  .pg-rings { grid-template-columns: 1fr 1fr; gap: var(--sp-6) var(--sp-3); }
  .pg-md { width: calc(var(--pg-md) + var(--sp-3)); }
  .pg-reach { flex-direction: column; text-align: center; }
}
.pg-count { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-md); color: var(--ink-strong); white-space: nowrap; }
.pg-count .u { font-size: var(--fs-xs); font-style: normal; color: var(--muted); }
.pg-note { margin: 0 0 var(--sp-2); font-size: var(--fs-xs); color: var(--muted); }
/* An open category's rows (design/25-progress-lists-variants.html, B): the picture, the name in
   the game's face and how to get it under it, then how far and where, and a pin to the Map. */
.pg-list { list-style: none; margin: 0; padding: 0; }
.pg-act { margin: var(--sp-4) 0 var(--sp-1); font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.pg-act:first-child { margin-top: var(--sp-2); }
.pg-item {
  display: grid; grid-template-columns: var(--sp-4) 40px minmax(0, 1fr) auto 28px; align-items: center; gap: 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-1); border-bottom: 1px solid color-mix(in oklab, var(--filigree) 10%, transparent); color: var(--ink);
  transition: background-color var(--dur-fast) var(--ease);
}
.pg-item:hover { background: var(--veil); }
.pg-item.is-plain { grid-template-columns: var(--sp-4) minmax(0, 1fr) auto 28px; }
.pg-mark { color: var(--ink-strong); }
.pg-mark .ic { width: var(--icon-sm); height: var(--icon-sm); }
.pg-art { display: grid; place-items: center; width: 40px; height: 40px; }
.pg-art img { max-width: 36px; max-height: 36px; object-fit: contain; }
.pg-item.is-got .pg-art img { opacity: 0.55; }
.pg-main { display: grid; gap: var(--sp-half); min-width: 0; }
.pg-name { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; font-family: var(--font-display); font-size: var(--fs-md); color: var(--ink-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pg-slot { flex: none; width: 7px; height: 7px; transform: rotate(45deg); }
.pg-slot.is-red { background: var(--slot-red); }
.pg-slot.is-blue { background: var(--slot-blue); }
.pg-slot.is-yellow { background: var(--slot-yellow); }
.pg-how { font-size: var(--fs-xs); color: var(--ink-2); }
.pg-place { display: grid; justify-items: end; gap: var(--sp-half); text-align: right; }
.pg-where { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0 var(--sp-2); font-size: var(--fs-xs); color: var(--muted); }
.pg-where span + span::before { content: '·'; margin-right: var(--sp-2); }
:is(.pg-item, .mp-near-list, .hm-row) .pg-onmap { display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 1px solid var(--line-ctl); border-radius: 50%; background: none; color: var(--ink-2); cursor: pointer; transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.pg-item .pg-onmap.is-none { border: 0; cursor: default; }
:is(.pg-item, .mp-near-list, .hm-row) .pg-onmap .ic { width: 10px; }
:is(.pg-item, .mp-near-list, .hm-row) .pg-onmap:is(:hover, :focus-visible) { color: var(--accent); border-color: var(--accent); outline: none; }
.pg > .pg-buy { text-align: center; }
.pg-item.is-got .pg-name { color: var(--ink-2); }
.pg-item.is-later { opacity: 0.55; }
.pg-done { display: flex; align-items: center; gap: var(--sp-2); margin: var(--sp-2) 0 0; font-size: var(--fs-sm); color: var(--ink-2); }
/* The road to the next Act: numbered steps in one column, as the game's objectives read, each
   titled with its count or a tick; a step done goes quiet, one the Phantom's way skips too. */
.pg-road { max-width: var(--column); margin: 0 auto; }
.pg-road > .pg-note { text-align: center; }
.pg-road > .pg-note.is-warn { color: var(--cond); }
.pg-steps { list-style: none; margin: var(--sp-4) 0 0; padding: 0; display: grid; gap: var(--sp-6); }
.pg-step { min-width: 0; }
.pg-step-h {
  display: flex; align-items: baseline; gap: var(--sp-3); margin: 0 0 var(--sp-2);
  padding-bottom: var(--sp-2); border-bottom: 1px solid var(--line);
  font-family: var(--font-display); font-size: var(--fs-md); font-weight: 600; letter-spacing: var(--track-title); color: var(--ink-strong);
}
.pg-step-n { font-family: var(--font-num); font-variant-numeric: tabular-nums; color: var(--muted); }
.pg-step-t { flex: 1; min-width: 0; text-wrap: pretty; }
.pg-step.is-ok .pg-step-h, .pg-step.is-bypassed .pg-step-h { color: var(--ink-2); border-bottom-color: color-mix(in oklab, var(--filigree) 45%, transparent); }
.pg-sub {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); margin: var(--sp-4) 0 var(--sp-1);
  font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label);
}
.pg-sub .pg-count { font-size: var(--fs-sm); letter-spacing: normal; text-transform: none; }
@media (max-width: 640px) {
  /* The place goes under the name; the pin stays on the right. */
  .pg-item { grid-template-columns: var(--sp-4) 36px minmax(0, 1fr) 28px; }
  .pg-item.is-plain { grid-template-columns: var(--sp-4) minmax(0, 1fr) 28px; }
  .pg-place { grid-column: 3; grid-row: 2; justify-items: start; text-align: left; display: flex; flex-wrap: wrap; gap: 0 var(--sp-2); }
  .pg-item.is-plain .pg-place { grid-column: 2; }
  .pg-where { justify-content: flex-start; }
  .pg-item .pg-onmap { grid-row: 1 / span 2; grid-column: -2; }
  .pg-name { white-space: normal; }
}

/* Since the previous save (js/app-home.js, js/changes.js): opened by what the completion went up
   by, from and to on one gauge, and when the save before was made. */
.hm-gain { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: var(--sp-1) var(--sp-3); padding: var(--sp-1) 0 var(--sp-3); border-bottom: 1px solid var(--line-panel); }
.hm-gain-k { font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--label); }
.hm-gain-v { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-md); font-weight: 600; color: var(--ink-strong); white-space: nowrap; }
.hm-gain .hm-bar { grid-column: 1 / -1; height: 4px; }
.hm-gain-at { grid-column: 1 / -1; font-size: var(--fs-xs); color: var(--muted); }

/* ── The Inventory (js/app-game.js): the sibling's sheet ─────────────────
   Two columns (design/03-redesign.md, step 4): what Hornet is on the left, what she carries on
   the right. The Needle is the focal point; the body, rows of the game's own pieces; the Tools sit
   on their slots. In a save the controls are still but at full light (read, not greyed). */
.inv-presets { display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: var(--sp-1) var(--sp-3); margin: 0; }
.inv-presets .lbl { color: var(--muted); }
.inv-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: var(--sp-12); align-items: start; }
.inv-col { display: grid; gap: var(--sp-8); min-width: 0; }
.inv-sec { display: grid; gap: var(--sp-2); align-content: start; min-width: 0; }
.inv-n { margin-left: var(--sp-1); font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xs); letter-spacing: 0; color: var(--ink-2); }
.inv-n i { font-style: normal; font-weight: 400; color: var(--muted); }
.inv :is(button:disabled) { opacity: 1; cursor: default; }
/* The Needle: five upright, the one carried taller, lit by the spotlight, its damage larger. */
.inv-needles { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); align-items: end; gap: var(--sp-1); }
.inv-np { position: relative; isolation: isolate; display: grid; justify-items: center; gap: var(--sp-2); padding: var(--sp-2) 0 var(--sp-1); }
.inv-np::before { content: ''; position: absolute; z-index: -1; inset: 0 8%; background: var(--halo-plate); opacity: 0; transition: opacity var(--dur) var(--ease); }
.inv-np.is-on::before { opacity: 1; }
.inv-np img { height: calc(var(--sp-16) * 1.5); width: auto; opacity: 0.38; filter: grayscale(0.5); transition: opacity var(--dur) var(--ease), filter var(--dur) var(--ease), height var(--dur) var(--ease); }
.inv-np.is-on img { height: calc(var(--sp-16) * 2); opacity: 1; filter: none; }
.inv-np:is(:hover, :focus-visible):not(:disabled) img { opacity: 0.85; filter: none; }
.inv-np b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-lg); color: var(--muted); padding-bottom: var(--sp-1); border-bottom: 2px solid transparent; }
.inv-np.is-on b { font-size: var(--fs-xl); color: var(--ink-strong); border-bottom-color: var(--accent-line); }
.inv-caption { margin: var(--sp-1) 0 0; text-align: center; font-family: var(--font-display); font-size: var(--fs-md); letter-spacing: var(--track-title); color: var(--ink-strong); }
/* The body: a row per ladder, the figure on the right, the pieces under it. */
.inv-field { display: grid; gap: var(--sp-2); padding: var(--sp-3) 0; }
.inv-field + .inv-field { border-top: 1px solid var(--inv-line); }
.inv-frow { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); }
.inv-fname { font-size: var(--fs-md); color: var(--ink); }
.inv-fnote { display: block; font-size: var(--fs-xs); color: var(--muted); }
.inv-fnum { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xl); font-weight: 600; color: var(--ink-strong); }
.inv-fnum i { font-style: normal; font-size: var(--fs-xs); font-weight: 400; color: var(--muted); }
.inv-pieces { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.inv-pc { display: grid; place-items: center; min-width: var(--ctl-sm); min-height: var(--ctl-sm); padding: 0; transition: transform var(--dur) var(--ease); }
.inv-pc img { height: calc(var(--sp-6) + var(--sp-half)); width: auto; filter: var(--missing-art); transition: filter var(--dur) var(--ease); }
.inv-pc.is-on img { filter: none; }
.inv-pc:is(:hover, :focus-visible):not(:disabled) { transform: translateY(calc(var(--sp-half) * -1)); }
/* The silk: one thread across, a bead per silk strung on it (the game's diamond), lit up to yours. */
.inv-pieces.is-masks { display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); justify-items: start; }
.inv-pieces.is-masks .inv-pc { min-width: 0; width: 100%; max-width: var(--ctl-sm); }
.inv-pieces.is-spools { position: relative; display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); gap: 0; }
.inv-pieces.is-spools::before {
  content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 1px;
  background: linear-gradient(90deg, var(--silk) var(--f), color-mix(in oklab, var(--silk-rim) 35%, transparent) var(--f));
}
.inv-pieces.is-spools .inv-pc { position: relative; min-width: 0; }
.inv-bead { width: 10px; height: 10px; transform: rotate(45deg); border: 1px solid var(--silk-rim); background: var(--scene-bg); transition: background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.inv-pc.is-on .inv-bead { background: var(--silk); border-color: var(--silk); box-shadow: 0 0 8px color-mix(in oklab, var(--silk) 60%, transparent); }
.inv-pc.is-base .inv-bead { width: 8px; height: 8px; }
@keyframes inv-lit { 0% { filter: brightness(2.2) drop-shadow(0 0 10px var(--focus-glow)); } 100% { filter: none; } }
@media (prefers-reduced-motion: no-preference) {
  .inv .is-lit :is(img, .inv-bead) { animation: inv-lit var(--dur-flash) var(--ease-fade); }
}
.inv-levels { display: grid; gap: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--inv-line); }
.inv-level { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--sp-1) var(--sp-3); }
.inv-level img { grid-row: span 2; width: var(--sp-12); height: var(--sp-12); object-fit: contain; }
.inv-level img.is-missing { filter: var(--missing-soft); opacity: var(--missing-soft-opacity); }
.inv-level .seg { gap: var(--sp-1); flex-wrap: nowrap; }
/* Plates: the art, the name, what it gives. */
.inv-plates { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: var(--sp-3) var(--sp-2); }
.inv-plate { display: grid; justify-items: center; align-content: start; gap: var(--sp-1); width: 100%; padding: 0; text-align: center; }
.inv-plate-art { position: relative; isolation: isolate; display: grid; place-items: center; width: 100%; height: calc(var(--sp-12) + var(--sp-2)); }
.inv-plate-art::before { content: ''; position: absolute; z-index: -1; inset: -8% 12%; background: var(--halo-plate); opacity: 0; transition: opacity var(--dur) var(--ease); }
.inv-plate:is(:hover, :focus-visible, .is-picked) .inv-plate-art::before { opacity: 1; }
.inv-plate img { max-height: calc(var(--sp-12) - var(--sp-1)); max-width: calc(var(--sp-16) + var(--sp-2)); object-fit: contain; }
.inv-plate.is-missing img { filter: var(--missing-soft); opacity: var(--missing-soft-opacity); }
/* Had or missing, said twice (design/08-missing-variants.html, A + C): what you have stands in the
   spotlight's glow, and under every picture the game's diamond, lit had, hollow missing. */
.inv-plate-art { order: 0; }
.inv-plate::after {
  content: ''; order: 1; width: var(--diamond); height: var(--diamond); transform: rotate(45deg);
  border: 1px solid var(--accent-line); transition: background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.inv-plate:not(.is-missing)::after { background: var(--accent); border-color: var(--accent); box-shadow: var(--glow); }
.inv-plate-name { order: 2; }
.inv-plate-v { order: 3; }
.inv-plate:not(.is-missing) .inv-plate-art::before { opacity: 0.8; }
.inv-plate:not(.is-missing) img { filter: drop-shadow(0 0 6px color-mix(in oklab, var(--spotlight) 35%, transparent)); }
.inv-plate-name { font-size: var(--fs-xs); line-height: 1.25; color: var(--ink-2); }
.inv-plate:not(.is-missing) .inv-plate-name { color: var(--ink); }
.inv-plate-v { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-md); font-weight: 700; color: var(--ink-strong); }
.inv-plate-v.is-none { font-family: var(--font-body); font-size: var(--fs-xs); font-weight: 400; color: var(--muted); }
/* The grids: one fixed square per cell, so every row lines up. */
.inv-sub { display: flex; align-items: center; gap: var(--sp-2); margin: var(--sp-2) 0 0; font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--label); }
.inv-sub::before { content: ''; width: var(--sp-2); height: var(--sp-2); transform: rotate(45deg); background: var(--c); }
.inv-sub b { color: var(--ink-2); }
.inv-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, calc(var(--sp-12) + var(--sp-2))); gap: var(--sp-2); }
.inv-grid > li { display: grid; justify-items: center; gap: var(--sp-half); }
.inv-cell {
  position: relative; display: grid; place-items: center; width: calc(var(--sp-12) + var(--sp-2)); height: calc(var(--sp-12) + var(--sp-2)); padding: 0;
  border: 1px solid transparent; background: none; cursor: pointer; transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.inv-cell img { position: relative; z-index: 1; width: var(--sp-8); height: var(--sp-8); object-fit: contain; transition: filter var(--dur) var(--ease); }
.inv-cell:not(.is-slot) img { width: calc(var(--sp-8) + var(--sp-2)); height: calc(var(--sp-8) + var(--sp-2)); }
.inv-cell.is-missing img { filter: var(--missing-soft); opacity: var(--missing-soft-opacity); }
.inv-cell:not(.is-missing):not(.is-slot) { background: radial-gradient(closest-side, color-mix(in oklab, var(--spotlight) 22%, transparent), transparent); }
.inv-cell:not(.is-missing) img { filter: drop-shadow(0 0 6px color-mix(in oklab, var(--spotlight) 40%, transparent)); }
.inv-cell::after {
  content: ''; position: absolute; left: 50%; bottom: calc(var(--sp-2) * -1); width: var(--diamond); height: var(--diamond);
  transform: translateX(-50%) rotate(45deg); border: 1px solid var(--accent-line);
}
.inv-cell:not(.is-missing)::after { background: var(--accent); border-color: var(--accent); box-shadow: var(--glow); }
.inv-grid > li { padding-bottom: var(--sp-3); row-gap: var(--sp-4); }
/* Under the pointer only the picture lights up: no square behind it (Albert). The keyboard keeps its focus ring. */
.inv-cell:is(:hover, :focus-visible) img { filter: drop-shadow(0 0 6px color-mix(in oklab, var(--spotlight) 45%, transparent)); }
.inv-cell.is-missing:is(:hover, :focus-visible) img { filter: grayscale(1) brightness(0.8); }
/* The one whose text is open needs no frame: the text under its shelf says it (Albert: the white square was too much). */
/* A Tool on its slot: the Crest screen's diamond in the slot's colour; lacking it, the game's locked slot: solid grey with its pale rim. */
.inv-cell.is-slot::before {
  content: ''; position: absolute; inset: calc(var(--sp-2) + var(--sp-half)); transform: rotate(45deg);
  border: 1.5px solid var(--c); background: color-mix(in oklab, var(--c) 8%, var(--scene-bg));
}
.inv-cell.is-slot.is-missing::before { border: 1px solid var(--slot-locked-rim); background: color-mix(in oklab, var(--slot-locked) 45%, var(--scene-bg)); }
/* On the grey slot the picture a touch brighter than elsewhere, so it still reads (design/09-locked-tool-variants.html, B). */
.inv-cell.is-slot.is-missing img { filter: grayscale(1) brightness(0.75); opacity: 0.6; }
/* Free mode's marks: All · None on a shelf's head, and − N + under a counted item. */
.inv-all { display: inline-flex; align-items: baseline; gap: var(--sp-2); margin-left: auto; padding-left: var(--sp-3); order: 3; font-family: var(--font-body); font-size: var(--fs-xs); font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--muted); }
.inv-all .text-btn { font-size: var(--fs-xs); min-height: 0; }
.inv-h:has(.inv-all)::after { order: 2; }
.inv-grid.is-items { grid-template-columns: repeat(auto-fill, calc(var(--sp-16) + var(--sp-6))); }
.inv-step { display: inline-flex; align-items: center; gap: var(--sp-half); }
.inv-step b { min-width: var(--sp-6); text-align: center; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-sm); color: var(--ink-strong); }
.inv-step .icon-btn { width: var(--sp-8); height: var(--sp-8); }
.inv-count { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-2xs); color: var(--ink-2); }
/* The purse (design/29-inventory-variants.html, A): each currency a figure with its picture, a bar
   for the Shell Shards' cap, then what breaks into it and the sum. */
.inv-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-6); }
.inv-cur { display: grid; gap: var(--sp-3); align-content: start; min-width: 0; }
.inv-fig { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 0 var(--sp-3); }
.inv-fig-art { grid-row: span 3; display: grid; place-items: center; padding: 0; }
.inv-fig-art img { width: calc(var(--sp-12) + var(--sp-2)); height: calc(var(--sp-12) + var(--sp-2)); object-fit: contain; filter: drop-shadow(0 0 6px color-mix(in oklab, var(--spotlight) 35%, transparent)); transition: filter var(--dur) var(--ease); }
.inv-fig-art:is(:hover, :focus-visible, .is-picked) img { filter: drop-shadow(0 0 8px color-mix(in oklab, var(--spotlight) 55%, transparent)); }
.inv-fig-name { font-size: var(--fs-xs); color: var(--ink-2); }
.inv-fig-num { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-2xl); font-weight: 600; line-height: 1.1; color: var(--ink-strong); }
.inv-fig-num i { font-style: normal; font-size: var(--fs-sm); font-weight: 400; color: var(--muted); }
.inv-bar { height: 3px; margin: var(--sp-1) 0; background: var(--inv-line); }
.inv-bar::after { content: ''; display: block; height: 100%; width: var(--f); background: var(--accent-line); }
.inv-in { width: 9ch; padding: var(--sp-1) var(--sp-2); background: transparent; border: 1px solid var(--line-ctl); color: var(--ink-strong); font: inherit; font-family: var(--font-num); font-size: var(--fs-xl); font-variant-numeric: tabular-nums; }
.inv-in:focus-visible { outline: 1px solid var(--accent-line); border-color: var(--accent-line); }
.inv-broken { margin: 0; font-size: var(--fs-xs); color: var(--muted); }
/* What's open under a shelf: the accent's bar on the left over the chosen veil (the open row). */
.inv-open { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-1) var(--sp-6); align-items: center; margin-top: var(--sp-2); padding: var(--sp-4) var(--sp-6); border-left: 2px solid var(--accent); background: var(--picked); }
.inv-open img { grid-row: 1 / span 3; width: calc(var(--sp-16) + var(--sp-2)); height: calc(var(--sp-16) + var(--sp-2)); object-fit: contain; }
.inv-open img.is-missing { filter: var(--missing-soft); opacity: var(--missing-soft-opacity); }
.inv-name { margin: 0; font-family: var(--font-display); font-size: var(--fs-lg); font-weight: 600; letter-spacing: var(--track-title); color: var(--ink-strong); text-wrap: balance; }
.inv-not { margin: 0; font-family: var(--font-num); font-size: var(--fs-2xs); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--muted); }
.inv-desc { margin: 0; font-size: var(--fs-sm); line-height: 1.55; color: var(--ink-2); text-wrap: pretty; }
@media (max-width: 899px) {
  .inv-body { grid-template-columns: minmax(0, 1fr); gap: var(--sp-8); }
}
@media (max-width: 599px) {
  .inv-two { grid-template-columns: minmax(0, 1fr); }
  .inv-open { grid-template-columns: minmax(0, 1fr); padding: var(--sp-3) var(--sp-4); }
  .inv-open img { grid-row: auto; width: var(--sp-12); height: var(--sp-12); }
  .inv-pieces.is-spools .inv-bead { width: 8px; height: 8px; }
}

/* ── The Crest screen (js/app-tools.js) ─────────────────────────────────────
   design/18-crest-variants.html, A (the game's pane): the figures as big numbers, the levels as
   − N + steppers; then the Crest drawn as the game draws it, its slots where the game puts them
   (js/crest-slots.js: units from its centre, --k px to the unit), each slot the game's frame
   (inline in that file, --g, as a mask won't take a file over file://) tinted by its colour; beside it the Tool list by colour and the one pointed at,
   described. Figures in --font-num. */
/* The figures as the game's HUD (design/19-crest-figures-variants.html, A): the Needle at its
   level with the slash large beside it; damage a second and the Needle Strike in the middle;
   Hornet as the HUD shows her on the right, her masks drawn and the spool with its silk. */
.ct-band { display: flex; justify-content: center; align-items: flex-end; margin-top: var(--sp-4); }
/* One centred block, its three parts on one baseline, a thin filigree rule between them. */
.ct-band > * { padding: 0 var(--sp-8); }
.ct-band > * + * { border-left: 1px solid color-mix(in oklab, var(--filigree) 22%, transparent); }
.ct-slash { display: flex; align-items: flex-end; gap: var(--sp-3); }
.ct-slash img { height: calc(var(--sp-16) + var(--sp-8)); width: auto; filter: drop-shadow(0 0 10px color-mix(in oklab, var(--spotlight) 30%, transparent)); }
.ct-slash b { display: block; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: calc(var(--fs-2xl) * 1.7); line-height: 1; color: var(--ink-strong); }
.ct-fig2 span, .ct-slash span { display: block; margin-top: var(--sp-1); font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.ct-mid { display: flex; align-items: flex-end; gap: var(--sp-8); }
.ct-fig2 b { display: block; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-2xl); line-height: 1.1; color: var(--ink-strong); }
/* Hornet as the HUD draws her: the masks in a row, the spool under them with its silk. */
.ct-hud { display: grid; justify-items: start; gap: var(--sp-2); }
.ct-masks { display: flex; gap: var(--sp-1); }
.ct-masks img { height: calc(var(--sp-6) + var(--sp-1)); width: auto; }
.ct-spool { display: flex; align-items: center; gap: var(--sp-2); }
.ct-spool img { height: calc(var(--sp-4) + var(--sp-1)); width: auto; }
.ct-spool b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-lg); line-height: 1; color: var(--ink-strong); }
.ct-heal { font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
/* The levels as the game's upgrade pips, on one centred row with the moment's switches. */
.ct-levels { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--sp-3) var(--sp-6); margin-top: var(--sp-6); padding: var(--sp-4) 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.ct-lv { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-xs); color: var(--ink-2); }
.ct-lv img { width: var(--sp-6); height: var(--sp-6); object-fit: contain; }
.ct-lv .icon-btn { width: var(--ctl-sm); height: var(--ctl-sm); }
.ct-lv .icon-btn:disabled { visibility: hidden; }
.ct-pips { display: inline-flex; align-items: center; gap: var(--sp-1); }
.ct-pip { display: block; width: calc(var(--diamond) + 3px); height: calc(var(--diamond) + 3px); padding: 0; transform: rotate(45deg); border: 1px solid color-mix(in oklab, var(--accent-line) 80%, transparent); background: none; }
button.ct-pip { cursor: pointer; }
button.ct-pip:is(:hover, :focus-visible) { border-color: var(--accent); outline: none; }
.ct-pip.is-on { background: var(--accent); border-color: var(--accent); box-shadow: var(--glow); }
.ct-checks { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
.ct-body { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-8); align-items: start; margin-top: var(--sp-8); }
.ct-side { display: grid; justify-items: center; gap: var(--sp-3); }
.ct-crest { --k: var(--crest-k); position: relative; }
/* The box is centred on the Crest's centre, where the slots are measured from, and reaches every
   Crest's art and slots (±3.1 units across, ±3.25 up and down). The art's own centre sits off it by
   (--ax, --ay): the game trims it in its atlas (js/crest-slots.js). */
.ct-crest-art { position: relative; width: calc(var(--k) * 6.2); height: calc(var(--k) * 6.5); }
.ct-crest-art > img {
  position: absolute; left: calc(50% + var(--ax) * var(--k)); top: calc(50% - var(--ay) * var(--k));
  width: calc(var(--k) * var(--w)); height: calc(var(--k) * var(--h)); transform: translate(-50%, -50%);
  filter: drop-shadow(0 0 14px color-mix(in oklab, var(--silk) 22%, transparent));
}
.ct-slot {
  --c: var(--slot-red);
  position: absolute; left: calc(50% + var(--x) * var(--k)); top: calc(50% - var(--y) * var(--k)); transform: translate(-50%, -50%);
  display: grid; place-items: center; width: calc(var(--k) * 0.95); height: calc(var(--k) * 0.95); padding: 0; border: 0; background: none;
}
.ct-float .ct-slot { position: relative; left: auto; top: auto; transform: none; }
/* The Vesticrest's slots on a row under the Crest's box, never over its art. */
.ct-float { display: flex; justify-content: center; gap: var(--sp-2); margin-top: var(--sp-2); }
.ct-slot::before { content: ''; position: absolute; inset: 0; background: var(--c); -webkit-mask: var(--g) center / contain no-repeat; mask: var(--g) center / contain no-repeat; opacity: 0.9; }
.ct-slot.is-blue { --c: var(--slot-blue); }
.ct-slot.is-yellow { --c: var(--slot-yellow); }
.ct-slot.is-skill { --c: var(--slot-skill); }
.ct-slot.is-locked::before { background: var(--slot-locked); opacity: 0.7; }
.ct-slot.is-full { cursor: pointer; -webkit-tap-highlight-color: transparent; }
/* A worn one keeps its slot's colour (the game's frame round it); the Silk Skill's white glyph dims under its see-through art. */
.ct-slot.is-skill.is-full::before { opacity: 0.4; }
.ct-slot img { position: relative; width: 84%; height: 84%; object-fit: contain; filter: drop-shadow(0 0 6px color-mix(in oklab, var(--c) 55%, transparent)); transition: transform var(--dur) var(--ease); }
.ct-slot.is-full:is(:hover, :focus-visible) { outline: none; }
.ct-slot.is-full:is(:hover, :focus-visible) img { transform: scale(1.1); }
.ct-crest-name { display: flex; align-items: center; gap: var(--sp-4); text-align: center; }
.ct-crest-name h3 { margin: 0; font-family: var(--font-display); font-size: var(--fs-xl); font-weight: 400; letter-spacing: var(--track-title); color: var(--ink-strong); }
.ct-stage { font-size: var(--fs-xs); color: var(--muted); }
.ct-arrow { font-size: var(--fs-lg); }
/* The Tool list: a row per colour and the Silk Skills, each on its slot as the Inventory draws it
   (.inv-cell.is-slot: the diamond in the slot's colour), only the ones you have, as the game
   lists them. Only the diamond here, no pip under it (Albert): a worn one's diamond is filled
   with its colour and lit. */
.ct-pick { display: grid; gap: var(--sp-4); min-width: 0; }
.ct-shelf { display: grid; gap: var(--sp-2); }
.ct-tools > li { padding-bottom: 0; }
.ct-tools .inv-cell::after { content: none; }
.ct-tools .inv-cell.is-on::before { border-width: 2px; background: color-mix(in oklab, var(--c) 32%, var(--scene-bg)); box-shadow: 0 0 12px color-mix(in oklab, var(--c) 45%, transparent); }
.ct-tools .inv-cell:focus { outline: none; }
.ct-tools .inv-cell:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; }
/* The one pointed at: name in its colour's rule, the game's words, its numbers. */
.ct-desc { display: grid; gap: var(--sp-2); min-height: calc(var(--sp-16) * 1.5); padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.ct-desc-name { margin: 0; font-family: var(--font-display); font-size: var(--fs-lg); font-weight: 400; color: var(--ink-strong); }
.ct-desc-text { margin: 0; font-size: var(--fs-sm); color: var(--ink-2); text-wrap: pretty; }
.ct-desc-nums { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-3); margin: 0; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xs); color: var(--muted); }
.ct-desc-nums b { font-size: var(--fs-xl); color: var(--ink-strong); }
.ct-desc-none { margin: 0; font-size: var(--fs-sm); color: var(--muted); }
.ct-figs { display: grid; gap: var(--sp-6); position: sticky; top: calc(var(--sticky-h) + var(--sp-4)); }   /* Combat's figures (js/app-fight.js) */
.ct-block { display: grid; gap: var(--sp-2); }
/* A block's title (design/03-redesign.md, step 2): the screen title's mark one level down, a
   hollow diamond in front and a rule that fades out to the right. */
.ct-h {
  display: flex; align-items: center; gap: var(--sp-2); margin: 0;
  font-family: var(--font-display); font-size: var(--fs-sm); font-weight: 600; letter-spacing: var(--track-title); color: var(--filigree);
}
.ct-h::before { content: ''; flex: none; width: var(--diamond); height: var(--diamond); border: 1px solid var(--filigree); transform: rotate(45deg); }
.ct-h::after { content: ''; flex: 1 1 var(--sp-8); height: 1px; background: linear-gradient(90deg, color-mix(in oklab, var(--filigree) 40%, transparent), transparent); }
.ct-fig { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; column-gap: var(--sp-3); }
.ct-fig-k { font-size: var(--fs-sm); color: var(--ink); }
.ct-fig-v b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xl); color: var(--ink-strong); }
.ct-fig-sub { grid-column: 1 / -1; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xs); color: var(--muted); }
.ct-mods { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); font-size: var(--fs-xs); color: var(--ink-2); }
.ct-mods li::before { content: '+ '; color: var(--muted); }
.ct-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-1); }
.ct-list li { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; grid-template-areas: "i n v" "i s v"; align-items: center; column-gap: var(--sp-2); color: var(--ink-2); }
.ct-list li.is-on { color: var(--ink-strong); }
.ct-list img { grid-area: i; width: 28px; height: 28px; object-fit: contain; }
.ct-list-name { grid-area: n; font-size: var(--fs-sm); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ct-list-sub { grid-area: s; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-2xs); color: var(--muted); }
.ct-list b { grid-area: v; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-md); color: var(--ink-strong); }
.ct-list li:not(.is-on) b { color: var(--ink-2); }
@media (max-width: 1100px) {
  .ct-body { grid-template-columns: minmax(0, 1fr); justify-items: center; }
  .ct-pick { width: 100%; }
  .ct-figs { grid-column: 1 / -1; position: static; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
}
@media (max-width: 599px) {
  .ct-crest { --k: var(--crest-k-sm); }
  .ct-band { flex-direction: column; align-items: center; gap: var(--sp-4); text-align: center; }
  .ct-band > * { padding: 0; }
  .ct-band > * + * { border-left: 0; padding-top: var(--sp-4); border-top: 1px solid color-mix(in oklab, var(--filigree) 22%, transparent); }
  .ct-mid { justify-content: center; gap: var(--sp-6); }
  .ct-hud { justify-items: center; }
  .ct-masks { justify-content: center; }
  .ct-lv { width: 100%; }
  .ct-lv-k { margin-right: auto; }
}
/* The figures in one strip on the window's bottom edge, only where the full list has gone below
   the controls: the Inventory's detail bar, the same frame. */
.ct-sum { display: none; }
@media (max-width: 1100px) {
  .ct-sum {
    position: sticky; bottom: 0; z-index: 2; display: grid; grid-auto-flow: column; grid-auto-columns: auto; justify-content: space-between; gap: var(--sp-2);
    margin-top: var(--sp-6); padding: var(--sp-2) var(--sp-3); border-top: 1px solid var(--filigree);
    background: var(--scene-bg); box-shadow: 0 -12px 24px var(--scene-bg);
  }
  .ct-sum-cell { display: grid; justify-items: center; align-content: start; gap: var(--sp-half); min-width: 0; text-align: center; }
  .ct-sum-cell img { width: 20px; height: 20px; object-fit: contain; }
  .ct-sum-k { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-xs); color: var(--label); }
  .ct-sum-cell b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-lg); color: var(--ink-strong); }
  .ct-sum-cell img + .ct-sum-k { display: none; }
}
/* The passive Tools' effects: icon and the game's own text, quiet. */
.ct-share { margin-top: var(--sp-3); }

/* ── The Hunter's Journal (js/app-journal.js) ─────────────────────────────
   design/16-journal-top-variants.html, B (next hunts): on top, the Memento as a large ring (the
   complete in bone, the seen and not complete dimmer) beside the four closest missing as cards;
   under it, two small toggles and the grid full width, by area under the Crest screen's heads
   (.ct-h) or in the Journal's own order. Each portrait sits in a ring that fills with its kills
   (design/15-journal-variants.html, A), lit bone once complete; one not seen is in shadow in a
   dashed ring. A tap opens the entry as the game's page (.hj-sheet, below), with the Hunter's note in the hand the site keeps
   for it (--font-hand). An optional entry carries a hollow diamond. */
.hj-hero { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-8); align-items: center; margin-top: var(--sp-4); padding-bottom: var(--sp-6); border-bottom: 1px solid var(--line); }
.hj-memento { position: relative; display: grid; place-items: center; width: 150px; aspect-ratio: 1; border-radius: 50%; }
.hj-memento::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(var(--accent) calc(var(--d) * 360deg), color-mix(in oklab, var(--silk-rim) 45%, transparent) 0 calc(var(--s) * 360deg), var(--ring-track) 0);
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - var(--ring) * 2), var(--ring-mask) calc(100% - var(--ring) * 2 + 1px));
  mask: radial-gradient(closest-side, transparent calc(100% - var(--ring) * 2), var(--ring-mask) calc(100% - var(--ring) * 2 + 1px));
}
.hj-memento div { display: grid; justify-items: center; line-height: 1.15; font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.hj-memento b { font-size: var(--fs-2xl); color: var(--ink-strong); }
.hj-memento .u { font-style: normal; font-size: var(--fs-sm); color: var(--muted); }
.hj-memento span { margin-top: var(--sp-1); font-size: var(--fs-2xs); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.hj-hero:not(:has(.hj-hunts)) { grid-template-columns: minmax(0, 1fr); justify-items: center; }
.hj-hunts .ct-h { margin-top: 0; }
.hj-hunt-row { list-style: none; margin: var(--sp-3) 0 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-3); }
.hj-hunt {
  display: grid; justify-items: center; align-content: start; gap: var(--sp-1); width: 100%; height: 100%; padding: var(--sp-3) var(--sp-2);
  border: 1px solid var(--line); text-align: center; cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.hj-hunt .hj-ring { width: 72px; margin-bottom: var(--sp-1); }
.hj-hunt b { font-family: var(--font-display); font-weight: 400; font-size: var(--fs-sm); color: var(--ink-strong); }
.hj-hunt-left { font-size: var(--fs-xs); color: var(--muted); }
.hj-hunt:focus { outline: none; }
.hj-hunt:is(:hover, :focus-visible), .hj-hunt.is-picked { border-color: var(--accent-line); background: var(--veil); }
.hj-tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--sp-6); margin: var(--sp-4) 0 var(--sp-2); }
.hj-find { flex: 1 1 16rem; max-width: 24rem; margin-right: auto; }
.hj-tog { gap: var(--sp-2); }
.hj-shelf { display: grid; gap: var(--sp-6); min-width: 0; }
.hj-area-group { display: grid; gap: var(--sp-3); }
.hj-area-n { margin-left: auto; order: 3; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xs); letter-spacing: 0; color: var(--muted); }
.hj-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); gap: var(--sp-2); }
.hj-cell {
  position: relative; display: grid; place-items: center; width: 100%; padding: var(--sp-1);
  border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.hj-cell:focus { outline: none; }
.hj-ring { position: relative; display: grid; place-items: center; width: 60px; aspect-ratio: 1; border-radius: 50%; transition: transform var(--dur) var(--ease); }
.hj-ring::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(var(--silk) calc(var(--f) * 360deg), var(--ring-track) 0);
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - var(--ring)), var(--ring-mask) calc(100% - var(--ring) + 1px));
  mask: radial-gradient(closest-side, transparent calc(100% - var(--ring)), var(--ring-mask) calc(100% - var(--ring) + 1px));
}
.hj-ring img { width: 80%; height: 80%; object-fit: contain; transition: filter var(--dur) var(--ease), opacity var(--dur) var(--ease); }
.hj-ring.is-done::before { background: var(--accent); box-shadow: var(--glow); }
.hj-ring.is-seen img { opacity: 0.7; filter: grayscale(0.5); }
.hj-ring.is-unseen::before { background: none; border: 1px dashed var(--line-ctl); -webkit-mask: none; mask: none; }
.hj-ring.is-unseen img { filter: var(--unseen-art); opacity: 0.9; }
@media (hover: hover) {
  .hj-cell:hover .hj-ring { transform: scale(1.06); }
  .hj-cell:hover .hj-ring:not(.is-unseen) img { opacity: 1; filter: none; }
}
.hj-cell:focus-visible .hj-ring, .hj-cell.is-picked .hj-ring { outline: 1px solid var(--accent); outline-offset: var(--sp-1); }
.hj-opt {
  position: absolute; top: var(--sp-1); right: var(--sp-1); width: var(--diamond); height: var(--diamond);
  border: 1px solid var(--ink-2); transform: rotate(45deg);
}
.hj-entry { display: grid; justify-items: center; gap: var(--sp-3); text-align: center; }
.hj-area { margin: 0; font-size: var(--fs-xs); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--muted); }
.hj-kills { margin: 0; color: var(--ink-2); font-size: var(--fs-sm); }
.hj-kills b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-lg); color: var(--ink-strong); }
.hj-kills .u { font-family: var(--font-num); font-style: normal; color: var(--muted); }
.hj-note {
  margin: var(--sp-2) 0 0; padding-top: var(--sp-3); border-top: 1px solid var(--line);
  font-family: var(--font-hand); font-size: var(--fs-md); line-height: 1.45; color: var(--ink); text-wrap: pretty;
}
.hj-by { margin: 0; font-size: var(--fs-xs); color: var(--muted); }
/* The entry, on a tap, as the game's page (design/17-journal-entry-variants.html, A): a modal
   <dialog> (in the top layer, over the header), wide and centred, the whole drawing on the pane's
   light at the size the game draws it (its pixels as they are, only capped by the box), beside it
   the name under a filigree rule, the kills, the description and the notes under the Hunter's
   symbol. On a phone, from the foot, the drawing above the text. */
.hj-sheet {
  position: fixed; inset: 0; margin: auto; max-width: none; max-height: calc(100vh - var(--sp-8));
  width: min(900px, calc(100vw - var(--sp-8))); height: fit-content; overflow: hidden auto; scrollbar-width: thin;
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: var(--sp-8); align-items: center;
  padding: var(--sp-12) var(--sp-8); color: var(--ink); background: var(--scene-floor); border: 1px solid var(--line-ctl); box-shadow: var(--shadow-md);
  animation: hj-sheet-in var(--dur) var(--ease) both;
}
.hj-sheet:not([open]) { display: none; }
.hj-sheet::backdrop { background: color-mix(in oklab, var(--scene-floor) 60%, transparent); }
.hj-sheet:focus { outline: none; }
.hj-sheet-x { position: absolute; top: var(--sp-2); right: var(--sp-2); }
.hj-art { position: relative; display: grid; place-items: center; min-height: 380px; }
.hj-art::before { content: ''; position: absolute; inset: -6%; background: var(--journal-light); pointer-events: none; }
.hj-art img { position: relative; max-width: 100%; max-height: 440px; }
.hj-art.is-unseen img { filter: var(--unseen-art); }
.hj-name { margin: 0; font-family: var(--font-display); font-weight: 400; font-size: var(--fs-xl); letter-spacing: var(--track-title); color: var(--ink-strong); text-wrap: balance; }
.hj-name::after { content: ''; display: block; width: 60%; height: 1px; margin: var(--sp-3) auto 0; background: linear-gradient(90deg, transparent, color-mix(in oklab, var(--filigree) 60%, transparent), transparent); }
.hj-sym { display: block; width: 150px; margin: var(--sp-2) auto 0; opacity: 0.8; }
.hj-sym.is-locked { opacity: 0.25; }
.hj-sheet .hj-note { margin: 0; padding: 0; border: 0; }
.hj-later { margin: 0; font-size: var(--fs-xs); font-style: italic; color: var(--muted); }
@keyframes hj-sheet-in { from { opacity: 0; transform: translateY(var(--sp-4)); } }
@media (max-width: 899px) {
  .hj-hero { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: var(--sp-4); }
  .hj-memento { width: 120px; }
  .hj-hunts { width: 100%; }
  .hj-hunt-row { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-1); }
  .hj-hunt { padding: var(--sp-2) 0; border-color: transparent; }
  .hj-hunt .hj-ring { width: 56px; }
  .hj-hunt b { font-size: var(--fs-2xs); letter-spacing: 0; max-width: 100%; overflow-wrap: anywhere; }
  .hj-hunt-left { display: none; }
  .hj-tools { justify-content: center; gap: var(--sp-3) var(--sp-6); }
  .hj-find { flex-basis: 100%; max-width: none; margin-right: 0; }
  .hj-sheet { inset: auto 0 0 0; margin: 0; width: auto; max-height: 90vh; grid-template-columns: minmax(0, 1fr); gap: var(--sp-4); padding: var(--sp-8) var(--sp-4) var(--sp-6); border-width: 1px 0 0; animation-name: hj-sheet-up; }
  .hj-art { min-height: 200px; }
  .hj-art img { max-height: 260px; }
  @keyframes hj-sheet-up { from { opacity: 0; transform: translateY(var(--sp-8)); } }
}
@media (max-width: 480px) {
  .hj-grid { grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)); gap: var(--sp-1); }
  .hj-ring { width: 48px; }
}
}

/* ── Combat (js/app-fight.js): the duel ────────────────────────────────────
   design/21-combat-variants.html, A (the sibling's): the enemy in a dropdown; Hornet and the
   enemy face to face on the Journal's light, the slashes that win and the hits that take you down
   between them, its health as a bar; the quickest way in one line; your attacks as cards and its
   with their masks; how it's worked out, folded. */
.ft-choose { position: relative; z-index: 3; max-width: 560px; margin: var(--sp-4) auto 0; }
.ft-choose > summary {
  display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-3); list-style: none; cursor: pointer;
  border-bottom: 1px solid var(--line-ctl); -webkit-tap-highlight-color: transparent;
}
.ft-choose > summary::-webkit-details-marker { display: none; }
.ft-choose > summary:focus { outline: none; }
.ft-choose > summary:focus-visible { border-color: var(--accent); }
.ft-choose > summary img { width: var(--sp-8); height: var(--sp-8); object-fit: contain; }
.ft-choose-name { flex: 1; min-width: 0; font-family: var(--font-display); color: var(--ink-strong); }
.ft-choose-v { color: var(--ink-2); transition: transform var(--dur) var(--ease); }
.ft-choose[open] .ft-choose-v { transform: rotate(180deg); }
/* A <details>' content doesn't take its box as the containing block (Chrome slots it), so the
   list hangs from a zero-height relative wrapper instead. */
.ft-choose-body { position: relative; z-index: 5; height: 0; }
.ft-choose-body > * { position: absolute; left: 0; right: 0; top: 0; padding: var(--sp-3); background: var(--scene-floor); border: 1px solid var(--line-ctl); box-shadow: var(--shadow-md); }
.ft-choose-body .ft-foes { max-height: 50vh; }
.ft-pick { display: grid; gap: var(--sp-3); }
.ft-foes { list-style: none; margin: 0; padding: 0; max-height: 70vh; overflow: auto; display: grid; gap: var(--sp-half); }
.ft-foe {
  display: grid; grid-template-columns: 32px minmax(0, 1fr) auto; align-items: center; gap: var(--sp-2); width: 100%;
  padding: var(--sp-1) var(--sp-2); border: 1px solid transparent; background: none; color: var(--ink-2); text-align: left; font: inherit; font-size: var(--fs-sm); cursor: pointer;
}
.ft-foe img { width: 32px; height: 36px; object-fit: contain; }
.ft-foe:is(:hover, :focus-visible) { border-color: color-mix(in oklab, var(--filigree) 40%, transparent); background: var(--veil); color: var(--ink); outline: none; }
.ft-foe.is-on { border-color: var(--filigree); background: var(--veil); color: var(--ink-strong); }
.ft-boss { font-style: normal; font-size: var(--fs-2xs); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--muted); }
/* The duel: Hornet · the figures · the enemy, on one baseline. */
.ft-duel { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: var(--sp-6); align-items: end; margin-top: var(--sp-8); }
.ft-fighter { display: grid; justify-items: center; gap: var(--sp-2); text-align: center; }
.ft-fighter h3 { margin: 0; font-family: var(--font-display); font-size: var(--fs-xl); font-weight: 400; letter-spacing: var(--track-title); color: var(--ink-strong); }
.ft-duel { --ft-art: calc(var(--sp-16) * 3.5); }
.ft-art { position: relative; display: grid; place-items: center; width: 100%; height: var(--ft-art); }
.ft-art::before { content: ''; position: absolute; inset: -6% 10%; background: var(--journal-light); pointer-events: none; }
.ft-art img { position: relative; max-width: 100%; max-height: var(--ft-art); object-fit: contain; }
.ft-masks { display: flex; flex-wrap: wrap; justify-content: center; gap: 3px; }
.ft-masks img { height: var(--sp-6); width: auto; }
.ft-hpbar { position: relative; display: block; width: min(100%, 320px); height: 6px; background: linear-gradient(90deg, color-mix(in oklab, var(--slot-red) 75%, transparent) var(--h, 100%), var(--ring-track) 0); box-shadow: 0 0 10px color-mix(in oklab, var(--slot-red) 35%, transparent); transition: background var(--dur) var(--ease); }
.ft-masks img.is-off { filter: brightness(0.25) grayscale(1); }
/* Her silk: the HUD's spool, lit as far as it's full. */
.ft-silk { display: flex; align-items: center; gap: var(--sp-2); }
.ft-silk > span { position: relative; display: block; width: calc(var(--sp-16) * 1.6); height: var(--sp-4); }
.ft-silk img { position: absolute; inset: 0; width: 100%; height: 100%; }
.ft-silk .is-dim { filter: brightness(0.25) grayscale(1); }
.ft-silk .is-lit { clip-path: inset(0 calc(100% - var(--s)) 0 0); }
.ft-silk b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-md); color: var(--ink-strong); }
.ft-bind { margin-top: var(--sp-1); }
.ft-fighter.is-down .ft-art img { filter: grayscale(1) brightness(0.45); transform: rotate(-8deg); transition: filter var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease); }
.ft-hpbar i { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; background: var(--ink-strong); }
.ft-hpn { font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.ft-mid { display: grid; justify-items: center; gap: var(--sp-6); padding-bottom: var(--sp-12); text-align: center; }
.ft-mid p { margin: 0; display: grid; justify-items: center; gap: var(--sp-1); }
.ft-mid b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: calc(var(--fs-2xl) * 2); line-height: 1; color: var(--ink-strong); }
.ft-mid .is-sub b { font-size: var(--fs-2xl); }
.ft-mid span { font-size: var(--fs-xs); color: var(--muted); }
.ft-plan { max-width: 640px; margin: var(--sp-4) auto 0; text-align: center; font-size: var(--fs-sm); color: var(--ink-2); text-wrap: balance; }
.ft-plan b { font-family: var(--font-display); font-weight: 400; color: var(--ink-strong); }
/* Your attacks as cards, the one that needs fewest framed; its attacks with their masks. */
.ft-two { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: var(--sp-8); align-items: start; margin-top: var(--sp-8); }
.ft-side { display: grid; gap: var(--sp-4); }
.ft-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: var(--sp-3); }
.ft-cardx {
  display: grid; justify-items: center; align-content: start; gap: var(--sp-1); width: 100%; height: 100%; padding: var(--sp-3) var(--sp-2);
  border: 1px solid transparent; background: none; text-align: center; cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: background-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.ft-cardx:is(:hover, :focus-visible):not(:disabled) { background: var(--veil); outline: none; }
.ft-cardx:active:not(:disabled) { transform: scale(0.96); }
.ft-cardx:disabled { cursor: default; opacity: 0.4; }
.ft-cardx small { font-family: var(--font-num); font-size: var(--fs-2xs); color: var(--label); }
.ft-cardx img { width: var(--sp-12); height: var(--sp-12); object-fit: contain; }
.ft-cardx img.is-needle { transform: rotate(35deg); }
.ft-cardx span { font-size: var(--fs-sm); color: var(--ink-2); }
.ft-cardx b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xl); color: var(--ink-strong); }
.ft-cardx em { font-style: normal; font-size: var(--fs-xs); color: var(--muted); }
.ft-cardx.is-best { border-color: var(--accent-line); background: var(--picked); }
.ft-hits { list-style: none; margin: 0; padding: 0; display: grid; }
.ft-hits li { border-bottom: 1px solid var(--line); }
.ft-hit {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "n m" "e e"; gap: 0 var(--sp-3); width: 100%; padding: var(--sp-2) var(--sp-1);
  border: 0; background: none; text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent; transition: background-color var(--dur-fast) var(--ease);
}
.ft-hit:is(:hover, :focus-visible):not(:disabled) { background: color-mix(in oklab, var(--slot-red) 10%, transparent); outline: none; }
.ft-hit:disabled { cursor: default; opacity: 0.4; }
.ft-hit-name { grid-area: n; font-size: var(--fs-sm); color: var(--ink); }
.ft-hit-masks { grid-area: m; display: inline-flex; align-items: center; gap: 2px; }
.ft-hit-masks img { height: var(--sp-4); width: auto; }
.ft-hit-masks b { font-family: var(--font-num); font-size: var(--fs-xs); color: var(--ink-2); }
.ft-hits em { grid-area: e; font-style: normal; font-size: var(--fs-xs); color: var(--muted); }
/* Enemy gauntlets: the arena's summary centred, then its waves. */
.ft-summary { display: grid; justify-items: center; gap: var(--sp-2); margin-top: var(--sp-6); text-align: center; }
.ft-summary .inv-name { margin: 0; }
.ft-hp { margin: 0; color: var(--ink-2); }
.ft-hp b { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-2xl); color: var(--ink-strong); }
.ft .ct-figs.ft-waves { position: static; margin-top: var(--sp-6); }
/* The fight's log, newest first, with undo and start over. */
/* The log, in three voices (the sibling's): what you did on the left with a slash of the Needle,
   what it did to you on the right with a mask, the fight's own lines centred between two rules. */
.ft-log { max-width: 720px; margin: var(--sp-8) auto 0; }
.ft-log-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-1); font-family: var(--font-num); font-size: var(--fs-xs); color: var(--muted); }
.ft-log-lines li { display: flex; align-items: center; gap: var(--sp-2); }
.ft-log-lines li.is-foe { justify-content: flex-end; text-align: right; }
.ft-log-lines li.is-sys { justify-content: center; font-family: var(--font-display); letter-spacing: var(--track-caps); color: var(--label); }
.ft-log-lines li.is-sys::before, .ft-log-lines li.is-sys::after { content: ''; flex: 0 1 var(--sp-12); border-top: 1px solid var(--line); }
.ft-log-lines li.is-last { font-size: var(--fs-sm); color: var(--ink-strong); animation: ft-in var(--dur) var(--ease); }
.ft-log-mask { height: var(--sp-3); width: auto; }
.ft-log-tools { display: flex; justify-content: center; gap: var(--sp-4); }
.ft-log-tools .ic, .ft-undo .ic { margin-right: var(--sp-1); vertical-align: -1px; }
@keyframes ft-in { from { opacity: 0; transform: translateY(-4px); } }
/* How it's worked out, folded. */
.ft-how { margin-top: var(--sp-8); }
.ft-how > summary { list-style: none; cursor: pointer; }
.ft-how > summary::-webkit-details-marker { display: none; }
.ft-how-body { display: grid; gap: var(--sp-3); padding-top: var(--sp-4); }
.ft-mods { display: grid; gap: var(--sp-1); width: 100%; }
.ft-mods ol { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--sp-1); }
.ft-mods li { display: grid; gap: var(--sp-half); padding: var(--sp-1) 0; border: 1px solid var(--line); font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.ft-mods li span { font-size: var(--fs-2xs); color: var(--muted); }
.ft-mods li b { font-size: var(--fs-sm); font-weight: 500; color: var(--ink-2); }
.ft-mods li.is-needle { border-color: var(--filigree); }
.ft-mods li.is-needle b, .ft-mods li.is-kit b { color: var(--ink-strong); }
.ft-mods li.is-kit:not(.is-needle) { border-style: dashed; border-color: var(--ink-2); }
.ft-stagger { margin: 0; font-size: var(--fs-sm); color: var(--ink-2); }
.ft-phases { font-size: var(--fs-sm); color: var(--ink-2); }
.ft-phases ul { margin: var(--sp-1) 0 0; padding: 0; list-style: none; }
.ft-phases li { font-variant-numeric: tabular-nums; }
.ft-list li { grid-template-columns: 28px minmax(0, 1fr) auto auto; grid-template-areas: "i n v u" "i s v u"; column-gap: var(--sp-3); }
.ft-list.is-theirs li { grid-template-columns: minmax(0, 1fr) auto auto; grid-template-areas: "n v u" "s v u"; }
.ft-uses { grid-area: u; min-width: 8ch; text-align: right; font-size: var(--fs-xs); color: var(--ink-2); }
/* The fight played out (js/fight.js): Hornet's HUD, its bar, the clock, the end. What changes
   flashes once when the action causes it (the sibling's): a hit whitens the one hit, its figure
   rises and fades, the bar leaves a pale trail, a mask breaks, a Bind glows; nothing bounces. */
.ft-masks img.is-plasm { filter: sepia(1) hue-rotate(170deg) saturate(3) brightness(0.8); }
.ft-masks img.is-fractured { outline: 1px dashed var(--mask-line); outline-offset: 1px; }
.ft-masks img.is-broken { animation: ft-break var(--dur-slow) var(--ease); }
@keyframes ft-break { from { filter: none; transform: scale(1.2); } }
.ft-silk.is-changed b { animation: ft-glow var(--dur-flash) var(--ease); }
@keyframes ft-glow { from { text-shadow: 0 0 8px var(--focus-glow); } }
.ft-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-1); font-size: var(--fs-2xs); letter-spacing: var(--track-caps); color: var(--label); }
.ft-chips li { padding: var(--sp-half) var(--sp-2); border: 1px solid var(--line); }
.ft-binding { display: grid; justify-items: center; gap: var(--sp-1); max-width: 260px; font-size: var(--fs-xs); color: var(--ink); }
/* Hornet's sprite faces left: mirrored, she faces the enemy (scale, not transform, so the hit's shake still runs). */
.ft-hornet .ft-art img { scale: -1 1; }
.ft-hornet.is-hit .ft-art img { animation: ft-hit var(--dur-slow) var(--ease); }
.ft-hornet.is-bind .ft-art img, .ft-hornet.is-binding .ft-art img { filter: drop-shadow(0 0 12px var(--focus-glow)); transition: filter var(--dur) var(--ease); }
.ft-enemy.is-hit .ft-art img { animation: ft-hit var(--dur-slow) var(--ease); }
@keyframes ft-hit { 0% { filter: brightness(3); transform: translateX(4px); } 100% { filter: none; transform: none; } }
.ft-enemy.is-staggered .ft-art img { transform: rotate(6deg) translateY(4%); filter: brightness(0.8); transition: transform var(--dur) var(--ease), filter var(--dur) var(--ease); }
.ft-dmg { position: absolute; top: 20%; left: 50%; font-family: var(--font-num); font-size: var(--fs-xl); font-weight: 600; color: var(--ink-strong); pointer-events: none; animation: ft-rise var(--dur-flash) var(--ease) forwards; }
@keyframes ft-rise { from { opacity: 1; transform: translate(-50%, 0); } to { opacity: 0; transform: translate(-50%, -40px); } }
.ft-dust { position: absolute; left: calc(20% + var(--i) * 6%); bottom: 30%; width: 4px; height: 4px; border-radius: 50%; background: var(--ink); opacity: 0; animation: ft-dust calc(var(--dur-flash) * 1.4) var(--ease) calc(var(--i) * 40ms) forwards; }
@keyframes ft-dust { from { opacity: 0.9; transform: none; } to { opacity: 0; transform: translateY(-60px); } }
.ft-enemy.is-dying .ft-art img { animation: ft-fade calc(var(--dur-flash) * 1.4) var(--ease) forwards; }
@keyframes ft-fade { to { opacity: 0.35; filter: grayscale(1); } }
.ft-hpbar { background: var(--ring-track); box-shadow: 0 0 10px color-mix(in oklab, var(--slot-red) 35%, transparent); }
.ft-hpbar > .ft-fill, .ft-hpbar > .ft-trail { position: absolute; top: 0; bottom: 0; left: 0; transition: width var(--dur) var(--ease); }
.ft-hpbar > .ft-fill { background: color-mix(in oklab, var(--slot-red) 75%, transparent); }
.ft-hpbar > .ft-trail { background: color-mix(in oklab, var(--ink) 55%, transparent); animation: ft-trail var(--dur-flash) var(--ease) forwards; }
@keyframes ft-trail { to { opacity: 0; } }
.ft-hpbar.is-low > .ft-fill { background: var(--slot-red); }
.ft-phase-n, .ft-part-name { font-size: var(--fs-xs); letter-spacing: var(--track-caps); color: var(--label); }
.ft-stag { font-family: var(--font-num); font-size: var(--fs-2xs); color: var(--muted); }
.ft-stag.is-down { color: var(--ink-strong); }
.ft-clock { font-family: var(--font-num); font-size: var(--fs-xs); color: var(--muted); }
/* Your moves in groups; a card that can't be played says why. The critical sits beside the slash. */
.ft-group { display: grid; gap: var(--sp-2); }
.ft-group-h { margin: 0; font-size: var(--fs-xs); font-style: italic; font-weight: 400; color: var(--muted); border-bottom: 1px solid var(--line-row); }
.ft-cards li.has-crit { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr); grid-column: span 2; }
.ft-cardx.is-crit { border-left: 1px dashed var(--line); }
.ft-card-glyph { display: grid; place-items: center; width: var(--sp-12); height: var(--sp-12); font-size: var(--fs-xl); color: var(--ink-2); }
.ft-cardx .ft-why { color: var(--slot-red); }
.ft-targets { display: grid; gap: var(--sp-1); }
.ft-target { display: grid; grid-template-columns: minmax(0, 1fr) 30% auto; align-items: center; gap: var(--sp-2); padding: var(--sp-1) var(--sp-2); border: 1px solid transparent; background: none; color: var(--ink-2); font: inherit; font-size: var(--fs-sm); text-align: left; cursor: pointer; }
.ft-target.is-on { border-color: var(--filigree); color: var(--ink-strong); background: var(--veil); }
.ft-target:disabled { opacity: 0.4; cursor: default; }
.ft-target b { font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.ft-target .ft-hpbar { width: 100%; height: 4px; }
/* The end, titled as the game titles an area, with the fight in figures. */
.ft-end { display: grid; justify-items: center; gap: var(--sp-2); margin: var(--sp-8) auto 0; text-align: center; }
.ft-end.is-fresh { animation: ft-in var(--dur-slow) var(--ease-fade); }
.ft-end-title { margin: 0; font-family: var(--font-display); font-size: var(--fs-2xl); letter-spacing: var(--track-title); color: var(--ink-strong); }
.ft-end-rule { display: flex; align-items: center; gap: var(--sp-2); width: 220px; color: var(--filigree); }
.ft-end-rule i { flex: 1; border-top: 1px solid currentColor; }
.ft-end-rule svg { fill: none; stroke: currentColor; }
.ft-end-note { margin: 0; color: var(--ink-2); }
.ft-sum { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2) var(--sp-6); margin: var(--sp-2) 0; }
.ft-sum div { display: grid; justify-items: center; }
.ft-sum dt { order: 2; font-size: var(--fs-2xs); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.ft-sum dd { margin: 0; font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-lg); color: var(--ink-strong); }
/* The band: the duel in small, stuck under the bar when the duel scrolls away. */
.ft-band { position: sticky; top: var(--ft-band-top, 0); z-index: 6; height: 0; }
.ft-band-in { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); padding: var(--sp-2) var(--sp-3); background: var(--scene-bg); border-bottom: 1px solid var(--line); box-shadow: var(--shadow-md);
  opacity: 0; visibility: hidden; transform: translateY(-8px); transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility 0s linear var(--dur); }
.ft-band.is-on .ft-band-in { opacity: 1; visibility: visible; transform: none; transition-delay: 0s; }
.ft-band-me { display: flex; align-items: center; gap: var(--sp-2); }
.ft-band-me .ft-masks img { height: var(--sp-3); }
.ft-band-me b, .ft-band-vs b { font-family: var(--font-num); color: var(--ink-strong); }
.ft-band-vs { font-family: var(--font-num); font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
.ft-band-foe { display: grid; justify-items: end; gap: var(--sp-1); min-width: 0; font-size: var(--fs-xs); color: var(--ink); }
.ft-band-foe .ft-hpbar { width: 140px; height: 4px; }
@media (prefers-reduced-motion: reduce) {
  .ft-dmg, .ft-dust, .ft-hpbar > .ft-trail { display: none; }
  .ft [class*="is-"] img, .ft-end.is-fresh, .ft-log-lines li.is-last { animation: none !important; }
}
@media (max-width: 899px) {
  .ft-duel { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-4); }
  .ft-mid { grid-column: 1 / -1; grid-row: 2; grid-template-columns: repeat(2, auto); justify-content: center; align-items: end; gap: var(--sp-2) var(--sp-8); padding: 0; }
  .ft-mid > .ft-clock, .ft-mid > .ft-log-tools, .ft-mid > .ft-clock:first-child { grid-column: 1 / -1; }
  .ft-duel { --ft-art: calc(var(--sp-16) * 2.2); }
  .ft-two { grid-template-columns: minmax(0, 1fr); }
  .ft-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ft-masks img { height: var(--sp-4); }
}

/* ── The Map (js/app-map.js) ──────────────────────────────────────────────
   The game's map on the pause menu's black, which scrolls when zoomed; on it, Hornet at your bench
   and a dot per piece missing, each kind in its own colour (the map's own tints are the areas';
   the dots take the HUD's: bone for shards, silk white for fragments, the slots' for the rest). */
/* The map first (design/03-redesign.md, step 6): the view fills the plate to the frame's inner
   edge; on it, the search top left, the zoom top right, the legend bottom left. */
.mp-stage { position: relative; margin: 0 calc(var(--inv-pad) * -1 + var(--frame-inset) + 1px); background: var(--scene-bg); }
/* The view: a fixed window the map moves and scales in (js/app-map.js): drag, wheel, pinch. */
.mp-view { position: relative; overflow: hidden; height: min(72vh, 720px, 62vw + 120px); background: var(--scene-bg);
  touch-action: none; cursor: grab; user-select: none; -webkit-user-select: none; }
.mp-view.is-grabbing { cursor: grabbing; }
/* No will-change: it would keep the layer rasterised at the fitted size, blurred close up. */
.mp-view img { -webkit-user-drag: none; pointer-events: none; }
/* The marks and names keep their size on screen whatever the zoom (--k, the scale's inverse). */
/* The large map, as the sibling's (the zoom's third button): the window's whole width, centred
   over the page's column, and the window's height under the bar. */
.mp-big[aria-pressed="true"] { color: var(--ink-strong); background: var(--picked); }
@media (min-width: 900px) {
  .mp.is-big .mp-stage { --map-w: calc(var(--page-w, 100vw) - 2 * var(--page-pad)); width: var(--map-w); margin-inline: calc((100% - var(--map-w)) / 2); }
  .mp.is-big .mp-view { height: max(480px, calc(100vh - var(--sticky-h) - var(--sp-6))); }
}
@media (max-width: 899px) { .mp-big { display: none; } }
/* Full screen (the zoom's last button; js/app-map.js, setFull): the stage over the whole window,
   with the search and the zoom on it; the page doesn't scroll behind. On a phone too: there it's
   what makes the map big. After the large map's rules, which it overrides: both can be on. */
.mp-full[aria-pressed="true"] { color: var(--ink-strong); background: var(--picked); }
/* The screen is a layer of its own (isolation), under the bar: over it. */
.screen:has(.mp.is-full) { z-index: 50; }
.mp.is-full .mp-stage { position: fixed; z-index: 4; inset: 0; width: auto; margin: 0; display: block; }
.mp.is-full .mp-view { height: 100%; }
.mp.is-full .mp-big { display: none; }
.mp.is-full .mp-find { position: absolute; order: 0; top: var(--sp-3); left: var(--sp-3); margin: 0; padding: 0; width: auto; right: calc(var(--ctl-sm) * 3 + var(--sp-3) * 2 + var(--sp-2)); max-width: 20rem; }
.mp.is-full .mp-tools { top: var(--sp-3); }
html:has(.mp.is-full) { overflow: hidden; }
.mp-float { background: color-mix(in oklab, var(--scene-bg) 88%, transparent); border: 1px solid var(--line-ctl); }
.mp-find { position: absolute; z-index: 4; top: var(--sp-3); left: var(--sp-3); width: min(20rem, calc(100% - var(--sp-16) * 2)); }
.mp-find .search { background: color-mix(in oklab, var(--scene-bg) 88%, transparent); border: 1px solid var(--line-ctl); }
.mp-found { list-style: none; margin: var(--sp-1) 0 0; padding: var(--sp-1) 0; background: color-mix(in oklab, var(--scene-bg) 94%, transparent); border: 1px solid var(--line-ctl); }
.mp-found button { width: 100%; padding: var(--sp-1) var(--sp-3); min-height: var(--ctl-sm); text-align: left; font-size: var(--fs-sm); color: var(--ink); }
.mp-found button:is(:hover, :focus-visible) { background: var(--veil); color: var(--ink-strong); }
.mp-found button { display: flex; flex-direction: column; align-items: flex-start; gap: 0; }
.mp-found small { font-size: var(--fs-2xs); color: var(--muted); }
.mp-found-none { padding: var(--sp-1) var(--sp-3); font-size: var(--fs-sm); color: var(--muted); }
.mp-tools { position: absolute; z-index: 4; top: var(--sp-3); right: var(--sp-3); display: flex; border: 1px solid var(--line-ctl); background: color-mix(in oklab, var(--scene-bg) 88%, transparent); }
.mp-tools button { width: var(--ctl-sm); height: var(--ctl-sm); font-family: var(--font-num); font-size: var(--fs-md); color: var(--ink-2); }
.mp-tools button + button { border-left: 1px solid var(--line-ctl); }
.mp-tools button:is(:hover, :focus-visible):not(:disabled) { color: var(--ink-strong); background: var(--veil); }
/* The selector under the map (design/03-redesign.md, step 6, the selector; the sibling's filter):
   its head (title, show all · hide all, the Acts and the three choices), then the groups, each a
   row that folds over its layers, its title its switch. */
.mp-legend { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-4) 0 0; }
.mp-legend-h { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--sp-1) var(--sp-6); }
.mp-legend-t { margin: 0; font-family: var(--font-display); font-size: var(--fs-sm); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-strong); }
.mp-all { display: inline-flex; gap: var(--sp-4); }
.mp-all .text-btn { font-size: var(--fs-sm); }
.mp-opts { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-8); }
.mp-checks { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-6); }
.mp-sec { border-top: 1px solid var(--inv-line); }
.mp-sec:last-child { border-bottom: 1px solid var(--inv-line); }
.mp-sec-h { display: flex; align-items: center; gap: var(--sp-3); }
.mp-fold { display: grid; place-items: center; flex: none; width: var(--ctl-sm); min-height: var(--ctl); padding: 0; }
/* A caret in plain ink: the frame's light is the tab's colour, never a control's. */
.mp-caret { flex: none; width: 10px; height: 10px; color: var(--ink-2); transition: rotate var(--dur) var(--ease); }
.mp-sec.is-open .mp-fold .mp-caret, .mp-more[aria-expanded="true"] .mp-caret { rotate: 90deg; }
/* The group's title is its switch: all on in ink, some in ink-2, none muted. */
.mp-sec-sw { display: flex; flex: 1; align-items: center; gap: var(--sp-3); min-height: var(--ctl); padding: 0; text-align: left; color: var(--ink-strong); }
.mp-sec-sw.is-some { color: var(--ink-2); }
.mp-sec-sw.is-none { color: var(--muted); }
.mp-sec-sw:is(:hover, :focus-visible) .mp-sec-t, .mp-fold:is(:hover, :focus-visible) + .mp-sec-sw .mp-sec-t { color: var(--accent); }
.mp-sec-t { font-family: var(--font-display); font-size: var(--fs-sm); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; transition: color var(--dur) var(--ease); }
.mp-kinds { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-6); padding: 0 0 var(--sp-3) var(--sp-6); }
/* A layer: the game's picture, bare, is the switch; off, in grey (the Inventory's missing look). */
.mp-kind { display: inline-flex; align-items: center; gap: var(--sp-2); min-height: var(--ctl-sm); padding: 0; font-size: var(--fs-sm); color: var(--ink); text-align: left; }
.mp-kind-t { transition: color var(--dur) var(--ease); }
.mp-kind:is(:hover, :focus-visible) .mp-kind-t { color: var(--accent); }
.mp-kind.is-off .mp-kind-t, .mp-kind.is-off .mp-n b { color: var(--muted); }
.mp-art { --a: 28px; flex: none; display: inline-grid; place-items: center; width: var(--a); height: var(--a); color: var(--ink-2); }
.mp-art > img { display: block; width: var(--a); height: var(--a); max-width: none; object-fit: contain; transition: filter var(--dur) var(--ease), opacity var(--dur) var(--ease); }
.mp-art > svg { width: 70%; height: 70%; }
/* The Needle, a long thin line, laid across as on the map. */
.mp-art.is-needle > img { transform: rotate(35deg) scale(1.15); }
.mp-kind.is-off .mp-art > img { filter: var(--missing-art); }
.mp-kind.is-off .mp-art > svg { opacity: 0.35; }
/* A set with some of its layers off: its picture half lit. */
.mp-kind.is-some .mp-art > img { filter: grayscale(0.5) brightness(0.75); }
/* How many: had of total in the figures' face; complete, in --good. */
.mp-n { font-family: var(--font-num); font-variant-numeric: tabular-nums; font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
.mp-n b { font-weight: 700; color: var(--ink-strong); }
.mp-n i { font-style: normal; }
.mp-n.is-full b { color: var(--good); }
.mp-sec-sw .mp-n { font-size: var(--fs-2xs); }
/* A set (the relics) and its caret, which opens its layers in a row of their own. */
.mp-set { display: inline-flex; align-items: center; gap: var(--sp-2); }
.mp-more { display: inline-grid; place-items: center; flex: none; width: 24px; height: 24px; padding: 0; border: 1px solid var(--line-ctl); border-radius: 50%; color: var(--ink-2); }
.mp-more .mp-caret { width: 9px; height: 9px; color: inherit; }
.mp-more:is(:hover, :focus-visible), .mp-more[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
@media (pointer: coarse) { .mp-more { width: 32px; height: 32px; } }
.mp-inside { flex-basis: 100%; display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); margin: 0 0 var(--sp-2) var(--sp-8); padding: var(--sp-1) var(--sp-4); border-left: 1px solid var(--line); }
.mp-inside .mp-kind { font-size: var(--fs-xs); }
.mp-inside .mp-art { --a: 22px; }
/* The area names, on the map in the game's capitals, lit only enough to read. */
/* A mark is tappable: a finger's cursor, and the one whose card is open ringed in the accent. */
/* The card over the map, the sibling's: above its mark (below when there's no room), inside the box. */
.mp-card-wrap { position: absolute; z-index: 5; transform: translate(-50%, calc(-100% - var(--sp-3))); }
.mp-card-wrap.is-below { transform: translate(-50%, calc(var(--sp-8) + var(--sp-1))); }
.mp-card {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--sp-1) var(--sp-3);
  width: max-content; max-width: min(24rem, 80vw); padding: var(--sp-3) var(--sp-2) var(--sp-3) var(--sp-4);
  background: color-mix(in oklab, var(--scene-bg) 94%, transparent); border: 1px solid var(--line-ctl); box-shadow: var(--shadow-md);
}
.mp-card-art { grid-row: span 2; display: grid; place-items: center; }
.mp-card-art :is(.mp-dot, .mp-pin) { position: static; margin: 0; --mp-s: 36px; }
.mp-card-t { display: flex; flex-direction: column; gap: var(--sp-half); min-width: 0; font-size: var(--fs-xs); color: var(--muted); }
.mp-card-t b { font-size: var(--fs-md); font-weight: 600; color: var(--ink-strong); }
.mp-card-note { color: var(--ink-2); text-wrap: pretty; }
.mp-card > .text-btn, .mp-card-state { grid-column: 2; justify-self: start; font-size: var(--fs-sm); }
.mp-card-state { color: var(--muted); }
.mp-card-x { grid-column: 3; grid-row: 1; align-self: start; }
.mp-card-acts { grid-column: 2; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-4); font-size: var(--fs-sm); }
.mp-card-acts .mp-card-state { grid-column: auto; }
/* Another screen's way onto the Map. */
.pg-onmap, .hj-onmap { justify-self: start; font-size: var(--fs-xs); }

/* Where a search result is: a ring that swells and fades. */
.mp-ping { position: absolute; z-index: 3; width: var(--sp-12); height: var(--sp-12); margin: calc(var(--sp-6) * -1) 0 0 calc(var(--sp-6) * -1); border: 2px solid var(--accent); border-radius: 50%; opacity: 0; pointer-events: none; }
@keyframes mp-ping { 0% { opacity: 1; transform: scale(0.4); } 100% { opacity: 0; transform: scale(1.6); } }
.mp-ping.is-on { animation: mp-ping 1.2s var(--ease-fade) 3; }
@media (prefers-reduced-motion: reduce) { .mp-ping.is-on { opacity: 1; } }
@media (max-width: 899px) {
  .mp-find { width: auto; right: calc(var(--ctl-sm) * 4 + var(--sp-3) * 2); }
}
/* Whole on a phone the names would cover the map: they come with the zoom. */
@media (max-width: 599px) {
  .mp-view:not(.is-zoomed) .mp-name { display: none; }
  /* Whole, the map is too short to float the search on: it goes above it. */
  .mp-stage { display: flex; flex-direction: column; }
  .mp-find { position: static; order: -1; width: auto; margin: 0 var(--sp-3) var(--sp-2); padding-right: calc(var(--ctl-sm) * 4 + var(--sp-3)); }
  /* Whole, the map is small: smaller marks, so they don't cover it. */
  .mp-view:not(.is-zoomed) { --mp-s: 16px; }
  .mp-tools { top: 0; }
  .mp-legend-h { grid-template-columns: minmax(0, 1fr); }
  .mp-opts { gap: var(--sp-1) var(--sp-6); }
  .mp-checks { flex-direction: column; align-items: flex-start; }
  .mp-kinds { padding-left: 0; gap: var(--sp-1) var(--sp-4); }
  .mp-inside { margin-left: var(--sp-2); }
}
/* A piece: the game's round badge, a dark disc with a rim in the kind's colour, the picture inside. */
.mp-dot {
  --c: var(--ink); position: absolute; z-index: 2; display: grid; place-items: center; width: var(--mp-s, 22px); height: var(--mp-s, 22px);
  margin: calc(var(--mp-s, 22px) / -2) 0 0 calc(var(--mp-s, 22px) / -2); border-radius: 50%;
  background: var(--scene-bg); border: 1px solid var(--line-ctl);
}
.mp-dot img { width: calc(var(--mp-s, 22px) * 0.86); height: calc(var(--mp-s, 22px) * 0.86); object-fit: contain; }
/* The Needle, a long thin line, laid across the disc as the fight's card lays it (.ft-cardx). */
.mp-dot.is-needle img { transform: rotate(35deg) scale(1.2); }
/* The map in SVG, as the sibling's: the view is its viewBox, the marks keep their size (--k). */
.mp-svg { position: relative; display: block; width: 100%; height: 100%; touch-action: none; cursor: grab; user-select: none; }
/* The map's pieces, composed on a canvas under the SVG (js/map-paint.js), in the map's tone. */
.mp-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; filter: var(--map-tone); }
.mp-view.is-grabbing .mp-svg { cursor: grabbing; }
.mp-svg image { pointer-events: none; }
/* A mark: a dark disc with a thin rim and the thing's picture almost as big; the rim lit when
   pointed at or chosen. */
.mp-mk { transform: translate(var(--px), var(--py)) scale(var(--k, 1)) translate(var(--ox, 0px), var(--oy, 0px)); cursor: pointer; }
.mp-mk > circle:first-of-type { fill: var(--scene-bg); stroke: var(--line-ctl); stroke-width: 0.06; }
.mp-mk:is(:hover, :focus-visible, .is-sel) > circle:first-of-type { stroke: var(--accent); stroke-width: 0.1; }
.mp-mk:focus { outline: none; }
.mp-mk.is-off { opacity: 0.4; }
.mp-glyph { color: var(--ink-strong); }
/* Behind the picture, the inventory's pale plate (--halo-plate), so a dark portrait or a thin
   needle reads on the black disc. */
.mp-plate { fill: url(#mp-plate); pointer-events: none; }
#mp-plate stop { stop-color: var(--map-plate); }
#mp-plate stop:nth-child(2) { stop-color: var(--map-plate-mid); }
#mp-plate stop:last-child { stop-color: transparent; }
.mp-from { pointer-events: none; }
.mp-from > circle:first-of-type { stroke: var(--silk); stroke-width: 0.12; }
/* The areas' names, keeping their size on screen, with the map's black round their letters. */
.mp-name { font-family: var(--font-display); font-size: calc(var(--k, 1) * 0.5px); letter-spacing: var(--track-caps); text-transform: uppercase;
  text-anchor: middle; dominant-baseline: middle; fill: color-mix(in oklab, var(--ink) 72%, transparent);
  stroke: var(--scene-bg); stroke-width: calc(var(--k, 1) * 0.12px); paint-order: stroke; cursor: pointer; }
.mp-name:focus { outline: none; }
.mp-name:is(:hover, :focus-visible, .is-sel) { fill: var(--ink-strong); }
.mp-name.is-unmapped { fill: color-mix(in oklab, var(--muted) 70%, transparent); }
/* The caches and walls from afar: a count per room. */
.mp-cluster { pointer-events: none; }
.mp-cluster text { font-family: var(--font-num); font-size: 0.42px; text-anchor: middle; dominant-baseline: middle; fill: var(--ink-2); }
.mp-view.is-close .mp-cluster { display: none; }
/* The caches and walls: so many they're drawn close up only; from afar, a count per room. */
.mp-view:not(.is-close) .is-fine { display: none; }
/* An area whose map isn't bought yet. */
/* Hornet's way from the previous bench: a silk thread over the rooms, from a hollow ring. */
.mp-way polyline { fill: none; stroke: var(--silk); stroke-width: 4; stroke-dasharray: 10 8; stroke-linecap: round; stroke-linejoin: round; opacity: 0.75; vector-effect: non-scaling-stroke; }

/* The Map's admin mode (js/app-map-admin.js, #…&admin=1; design/30-map-admin-variants.html, all A):
   a band under the map, above the legend; on the map, a moved mark's way from the data's point
   (in the accent while unsaved), a hidden one faint and struck through, an added one with a "+". */
.mp-adm { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); gap: var(--sp-3) var(--sp-8); align-items: start; padding: var(--sp-4) 0; border-bottom: 1px solid var(--inv-line); font-size: var(--fs-xs); color: var(--ink-2); }
.mp-adm-col { display: grid; gap: var(--sp-3); align-content: start; min-width: 0; }
.mp-adm-h { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-3); }
.mp-adm-h b { font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.mp-adm-h span { font-family: var(--font-num); font-variant-numeric: tabular-nums; color: var(--muted); }
.mp-adm .is-pending { color: var(--accent); }
.mp-adm-sel { display: grid; grid-template-columns: var(--sp-8) minmax(0, 1fr); gap: var(--sp-2) var(--sp-3); align-items: start; }
.mp-adm-art { display: grid; place-items: center; height: var(--sp-8); }
.mp-adm :is(.mp-dot, .mp-pin) { position: static; margin: 0; --mp-s: 24px; }
.mp-adm-name { display: block; font-family: var(--font-display); font-weight: 400; font-size: var(--fs-sm); color: var(--ink-strong); }
.mp-adm :is(code, .mp-adm-xy, .mp-adm-in) { font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.mp-adm code { font-size: var(--fs-2xs); color: var(--muted); word-break: break-all; }
.mp-adm-body { grid-column: 1 / -1; display: grid; gap: var(--sp-2); }
.mp-adm-xy { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-1) var(--sp-3); margin: 0; }
.mp-adm-xy dt { color: var(--muted); }
.mp-adm-xy dd { margin: 0; color: var(--ink); }
.mp-adm-xy small { color: var(--muted); }
.mp-adm-acts, .mp-adm-foot { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); align-items: center; }
.mp-adm :is(.mp-adm-acts, .mp-adm-foot) .text-btn { font-size: var(--fs-xs); }
.mp-adm-foot .mp-adm-hint { margin-left: auto; }
.mp-adm-state { color: var(--muted); font-style: italic; }
.mp-adm-hint { font-size: var(--fs-2xs); color: var(--muted); }
.mp-adm-field { display: grid; gap: var(--sp-1); }
.mp-adm-field > span, .mp-adm-pick p { margin: 0; font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.mp-adm .seg button { min-width: var(--sp-8); }
.mp-adm-in { width: 100%; min-width: 0; padding: var(--sp-1) var(--sp-2); background: var(--scene-bg); color: var(--ink); border: 1px solid var(--line-ctl); font-size: var(--fs-xs); }
.mp-adm-in:focus { outline: none; border-color: var(--accent-line); }
.mp-adm-in.is-bad { border-color: var(--line-panel); color: var(--muted); text-decoration: underline wavy var(--line-panel); }
.mp-adm-text { min-height: calc(var(--sp-16) * 2); resize: vertical; }
.mp-adm-pick { display: grid; gap: var(--sp-2); }
.mp-adm-grid { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.mp-adm-grid button { display: grid; place-items: center; width: var(--ctl-sm); height: var(--ctl-sm); border: 1px solid transparent; }
.mp-adm-grid button:hover { background: var(--veil); }
.mp-adm-grid button[aria-pressed="true"] { border-color: var(--accent-line); background: var(--picked); }
.mp-adm-list details { border-top: 1px solid color-mix(in oklab, var(--filigree) 10%, transparent); padding-top: var(--sp-1); }
.mp-adm-list summary { cursor: pointer; font-family: var(--font-num); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--label); }
.mp-adm-list summary span { color: var(--muted); font-weight: 400; letter-spacing: 0; }
.mp-adm-list ul { list-style: none; margin: var(--sp-1) 0 0; padding: 0; display: grid; gap: var(--sp-1); }
.mp-adm-list li button { display: grid; grid-template-columns: var(--sp-6) minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; width: 100%; padding: var(--sp-1); text-align: left; color: var(--ink-2); }
.mp-adm-list li button:hover { background: var(--veil); color: var(--ink-strong); }
.mp-adm-list li :is(.mp-dot, .mp-pin) { --mp-s: 20px; }
.mp-view.is-adding .mp-svg { cursor: crosshair; }
.mp-adm-ways { pointer-events: none; }
.mp-adm-ways line { stroke: var(--ink-strong); stroke-width: 1.5; stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; opacity: 0.7; }
.mp-adm-ways circle { fill: none; stroke: var(--ink-strong); stroke-width: 1.5; vector-effect: non-scaling-stroke; opacity: 0.7; }
.mp-adm-ways .is-pending :is(line, circle) { stroke: var(--accent); opacity: 1; }
.mp-mk.is-hidden { opacity: 0.35; }
.mp-adm-strike { stroke: var(--ink-strong); stroke-width: 0.09; stroke-linecap: round; }
.mp-adm-plus circle { fill: var(--scene-bg); stroke: var(--accent); stroke-width: 0.06; }
.mp-adm-plus path { stroke: var(--accent); stroke-width: 0.07; stroke-linecap: round; }
/* What's missing closest to your bench: a short list under the map. */
.mp-near-list { list-style: none; margin: var(--sp-3) 0 0; padding: 0; display: grid; gap: var(--sp-1); }
.mp-near-list li { display: grid; grid-template-columns: var(--sp-6) minmax(0, 1fr) auto auto; align-items: center; gap: var(--sp-3); font-size: var(--fs-sm); color: var(--ink); }
.mp-near-list .mp-dot, .mp-near-list .mp-pin { position: static; margin: 0; --mp-s: 20px; }
.mp-near-list li > img { width: 18px; height: 20px; object-fit: contain; }
.mp-near-list .text-btn { justify-self: start; text-align: left; font-size: var(--fs-sm); }
.hj-where { margin: 0; font-size: var(--fs-sm); color: var(--ink-2); }
.mp-near-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mp-near-area { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
/* A place: the game's own map pin; a gauntlet, the badge with its needles crossed, in the slots'
   red; dimmed while your game hasn't opened it. */
.mp-pin {
  position: absolute; z-index: 2; display: grid; place-items: center; width: var(--mp-s, 22px); height: var(--mp-s, 22px);
  margin: calc(var(--mp-s, 22px) / -2) 0 0 calc(var(--mp-s, 22px) / -2); object-fit: contain; filter: drop-shadow(0 1px 2px var(--badge-shadow));
}
span.mp-pin { border-radius: 50%; background: var(--scene-bg); border: 1px solid var(--line-ctl); color: var(--ink-strong); }
.mp-pin svg { width: 55%; height: 55%; }
.mp-pin.is-gauntlet img { width: 80%; height: 80%; object-fit: contain; }
.mp-pin.is-badge img { width: calc(var(--mp-s, 22px) * 0.8); height: calc(var(--mp-s, 22px) * 0.8); object-fit: contain; }
.mp-pin.is-off { filter: var(--missing-soft) drop-shadow(0 1px 2px var(--badge-shadow)); opacity: var(--missing-soft-opacity); }
/* Hornet at her bench: the .hn sprite (sitting; running while js/app-hornet.js walks her from the
   previous bench), her feet and her middle on the point. */
.mp-hornet.hn, .hmB-hornet.hn {
  --hn-h: var(--hn-map); z-index: 2; translate: calc(-1 * var(--hn-map) * 62 / 120) calc(-1 * var(--hn-map) * 116 / 120);
  filter: drop-shadow(0 0 8px color-mix(in oklab, var(--spotlight) 60%, transparent)); transition: none;
}
