/* Will of Fire — interface styles.
 * Visual direction: a shinobi field kit. Dark indigo ink-wash panels with faint paper grain, edged like a
 * forehead-protector steel plate with brass rivets. Chakra blue, health green→red, XP purple, and the
 * chosen character's color as --accent (set by SP.HUD / SP.Menu at runtime).
 * One deliberate dark theme: the UI always sits over the 3D scene. */

/* ============================================================== tokens */
:root {
  color-scheme: dark;

  /* ink (panel and page) */
  --ink-0: #0b0d18;
  --ink-1: #10142a;
  --ink-2: #181e3c;
  --ink-3: #232a52;
  --ink-line: rgba(160, 176, 232, 0.16);
  --ink-line-2: rgba(160, 176, 232, 0.28);

  /* forehead-protector steel + brass rivets */
  --steel-hi: #eef1f7;
  --steel: #a3acbf;
  --steel-lo: #555e73;
  --steel-dk: #262b38;
  --brass-hi: #ffe7a8;
  --brass: #c9a24f;
  --brass-dk: #5a4113;

  /* text */
  --text: #efe9da;
  --text-dim: #b3b6cc;
  --text-faint: #8f94b0;
  --text-ink: #22190f;

  /* resources */
  --hp: #3ccf5c;
  --chakra: #3d8bff;
  --chakra-hi: #8cc4ff;
  --chakra-dk: #163f8f;
  --xp: #9b6cff;
  --xp-hi: #d0b8ff;
  --xp-dk: #3d2585;

  /* factions and status */
  --hostile: #ff5a4f;
  --neutral: #ffd84a;
  --friendly: #5fe07a;
  --ally: #7fb2ff;
  --gold: #ffcf4a;
  --error: #ff4d3d;
  --quest: #ffd166;
  --good: #79e28f;

  /* paper scroll */
  --paper: #f1e3bf;
  --paper-2: #e6d3a6;
  --paper-edge: #b98e4e;
  --sumi: #20170e;
  --seal-red: #b3261e;
  --wood-dk: #3a1d0b;
  --wood: #84502a;
  --wood-hi: #cf9255;

  /* accent (overwritten with the character color) */
  --accent: #f28c28;

  /* type */
  --f-title: 'Shojumaru', Georgia, serif;
  /* Pictogram boxes: the inline SVG (SP.Icons) is 1em square, so this font-size sets the icon size. */
  --f-glyph: 'Barlow Semi Condensed', 'Arial Narrow', system-ui, sans-serif;
  --f-ui: 'Barlow Semi Condensed', 'Arial Narrow', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* layout */
  --gl: max(16px, env(safe-area-inset-left));
  --gr: max(16px, env(safe-area-inset-right));
  --gt: max(12px, env(safe-area-inset-top));
  --gb: max(12px, env(safe-area-inset-bottom));
  --slot: 52px;
  --slot-gap: 6px;

  /* textures */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .06 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  --paper-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.55 .08' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .35 0 0 0 0 .22 0 0 0 0 .08 0 0 0 .16 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23p)'/%3E%3C/svg%3E");
  --steel-edge: linear-gradient(180deg, var(--steel-hi) 0%, var(--steel) 22%, var(--steel-lo) 62%, var(--steel-dk) 100%);
  --rivet: radial-gradient(circle, var(--brass-hi) 0 0.8px, var(--brass) 1.6px, var(--brass-dk) 2.3px, transparent 2.9px);
}

/* ============================================================== page */
*, *::before, *::after { box-sizing: border-box; }
html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--ink-0);
  color: var(--text);
  font-family: var(--f-ui);
  font-size: 15px;
  line-height: 1.35;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  overscroll-behavior: none;
}
#game {
  position: fixed;
  inset: 0;
  height: 100%;
  background: var(--ink-0);
  transition: filter 0.8s ease;
}
#game canvas { display: block; width: 100%; height: 100%; touch-action: none; }
body.is-dead #game { filter: grayscale(0.85) brightness(0.8) contrast(1.05); }
body.is-dead .hud-plates, body.is-dead .hud-floats, body.is-dead .hud-bubbles, body.is-dead .interact-prompt { visibility: hidden; }
#ui {
  position: fixed;
  inset: 0;
  height: 100%;
  pointer-events: none;
  overflow: hidden;
  z-index: 1;
  user-select: none;
  -webkit-user-select: none;
}
#boot-error {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(520px, calc(100vw - 32px));
  padding: 22px 24px;
  z-index: 100;
  color: var(--text);
  background: var(--grain), linear-gradient(180deg, #1d2346, #0f1329);
  border: 2px solid var(--steel-lo);
  border-radius: 8px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.6);
  font-size: 16px;
}
#boot-error[hidden] { display: none; }
#boot-error h1, #boot-error h2 { font-family: var(--f-title); font-weight: 400; margin: 0 0 8px; color: var(--accent); }

.is-hidden { display: none !important; }
button { font: inherit; color: inherit; }
kbd {
  display: inline-block;
  min-width: 1.6em;
  padding: 1px 6px;
  margin: 0 2px;
  border-radius: 4px;
  border: 1px solid var(--ink-line-2);
  border-bottom-width: 2px;
  background: linear-gradient(180deg, #2a3160, #181d3b);
  color: var(--text);
  font: 600 12px/1.4 var(--f-ui);
  text-align: center;
  letter-spacing: 0.02em;
}
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ============================================================== the field-kit panel */
.kit {
  position: relative;
  border: 2px solid transparent;
  border-radius: 7px;
  background:
    var(--grain) padding-box,
    radial-gradient(120% 80% at 15% 0%, rgba(86, 104, 190, 0.22), transparent 62%) padding-box,
    linear-gradient(180deg, rgba(27, 32, 66, 0.93), rgba(12, 15, 33, 0.95)) padding-box,
    var(--steel-edge) border-box;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.kit::before {
  content: '';
  position: absolute;
  inset: 2px;
  pointer-events: none;
  border-radius: 5px;
  background:
    var(--rivet) left top / 8px 8px no-repeat,
    var(--rivet) right top / 8px 8px no-repeat,
    var(--rivet) left bottom / 8px 8px no-repeat,
    var(--rivet) right bottom / 8px 8px no-repeat;
  z-index: 2;
}

/* ============================================================== buttons */
/* `.lk button`: the login-key box (SP.Menu keyHtml) writes bare buttons to stay small; they look like .btn. */
.btn, .lk button {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 38px;
  padding: 6px 16px;
  border-radius: 6px;
  border: 1px solid var(--ink-line-2);
  background: linear-gradient(180deg, #2b3366, #1a1f42);
  color: var(--text);
  font: 600 15px/1.1 var(--f-ui);
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 2px 6px rgba(0, 0, 0, 0.35);
  transition: transform 0.08s ease, filter 0.15s ease, border-color 0.15s ease;
  pointer-events: auto;
}
.btn:hover:not(:disabled), .lk button:hover:not(:disabled) { filter: brightness(1.18); border-color: var(--accent); }
.btn:active:not(:disabled), .lk button:active:not(:disabled) { transform: translateY(1px) scale(0.98); }
.btn:disabled, .lk button:disabled { cursor: not-allowed; opacity: 0.55; filter: grayscale(0.6); }
.btn--primary {
  border-color: rgba(255, 255, 255, 0.25);
  background: linear-gradient(180deg, var(--accent), #8a3d12);
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 92%, white), color-mix(in srgb, var(--accent) 55%, black));
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
}
.btn--wide { width: 100%; }
.btn--big { min-height: 48px; padding: 10px 28px; font-size: 18px; }
.btn--ink { background: linear-gradient(180deg, #232842, #13162a); color: var(--text-dim); }
.btn--danger { border-color: rgba(255, 90, 79, 0.45); background: linear-gradient(180deg, #5a1c22, #2c0c12); }
.btn.is-on { border-color: var(--good); box-shadow: inset 0 0 12px rgba(121, 226, 143, 0.25); }
.btn.is-armed { border-color: var(--error); background: linear-gradient(180deg, #8a1f1f, #4a0c0c); color: #fff; animation: armed 0.7s ease-in-out infinite alternate; }
@keyframes armed { to { box-shadow: 0 0 16px rgba(255, 77, 61, 0.55); } }

/* ============================================================== pictograms (SP.Icons inline SVG) */
.ico { display: block; width: 1em; height: 1em; flex: none; overflow: visible; }
/* The ink drop shadow inside a pictogram (SP.Icons.svg(id, color, {shadow: true})): every shape of the copy
 * takes the copy's dark color, also those with a fill or stroke color of their own. */
.ico-sh [fill]:not([fill="none"]) { fill: currentColor; }
.ico-sh [stroke]:not([stroke="none"]) { stroke: currentColor; }

/* ============================================================== hand-seal chips (cast bars, tooltips, scroll) */
/* One labeled chip per seal ("Snake", "Ram"...), lit in order while the hand seals are formed. */
.seal {
  --school: var(--chakra-hi);
  flex: 0 0 auto;
  min-width: 0;
  height: 24px;
  padding: 0 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  border: 1px solid rgba(160, 176, 232, 0.22);
  background: linear-gradient(180deg, #1c2246, #0e1229);
  color: var(--text-faint);
  font: 700 11px/1 var(--f-ui);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: transform 0.12s ease, color 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.seal.is-done {
  color: var(--text);
  border-color: var(--school);
  background: linear-gradient(180deg, #24306a, #121a3d);
  box-shadow: inset 0 0 10px rgba(61, 139, 255, 0.35);
}
.seal.is-active {
  color: #fff;
  border-color: #fff;
  background: radial-gradient(circle at 50% 35%, var(--school), #1a2a66 75%);
  box-shadow: 0 0 14px var(--school), inset 0 0 8px rgba(255, 255, 255, 0.45);
  transform: translateY(-3px) scale(1.08);
  animation: sealPulse 0.42s ease-in-out infinite alternate;
}
@keyframes sealPulse { to { box-shadow: 0 0 22px var(--school), inset 0 0 12px rgba(255, 255, 255, 0.6); } }

/* rank pips */
.pips { display: inline-flex; gap: 3px; vertical-align: middle; }
.pips i {
  width: 8px; height: 8px;
  transform: rotate(45deg);
  border: 1px solid rgba(255, 210, 120, 0.6);
  background: rgba(0, 0, 0, 0.35);
}
.pips i.is-on { background: linear-gradient(135deg, var(--brass-hi), var(--brass)); border-color: var(--brass-hi); box-shadow: 0 0 5px rgba(255, 207, 74, 0.7); }

/* mission rank tag (D / C / B / A) */
.tq-rank {
  display: inline-grid;
  place-items: center;
  width: 18px; height: 18px;
  margin-right: 6px;
  border-radius: 3px;
  font: 400 12px/1 var(--f-title);
  color: #fff;
  background: #4c6b3a;
  flex: none;
}
.tq-rank.rank-C { background: #3a5f8f; }
.tq-rank.rank-B { background: #7a4a9a; }
.tq-rank.rank-A { background: #a8322a; box-shadow: 0 0 8px rgba(255, 90, 79, 0.5); }

.muted { color: var(--text-faint); font-size: 14px; }
.sec-h {
  margin: 14px 0 8px;
  font: 700 12px/1 var(--f-ui);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-dim);
  display: flex;
  align-items: center;
  gap: 8px;
}
.sec-h::after { content: ''; flex: 1; height: 1px; background: linear-gradient(90deg, var(--ink-line-2), transparent); }

/* ============================================================== HUD root */
/* will-change: opacity keeps the HUD in one compositor layer of its own, in play and during the title-screen
 * raster warm alike (docs/INTERFACE_AND_CONTROLS.md "HUD raster warm"): the browser then rasters the HUD the
 * same way in both, so the GPU programs the warm compiles are the ones play needs. */
.hud { position: absolute; inset: 0; z-index: 10; pointer-events: none; font-variant-numeric: tabular-nums; will-change: opacity; }
.hud-layer { position: absolute; inset: 0; z-index: 0; isolation: isolate; pointer-events: none; overflow: hidden; } /* own stacking context: depth-sorted plate z-indexes stay below the HUD panels */
.hud-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  background: radial-gradient(ellipse at center, transparent 55%, rgba(170, 10, 10, 0.42) 100%);
}
.hud-vignette.is-on { opacity: 1; }
.hud-vignette.is-critical { animation: danger 0.9s ease-in-out infinite alternate; }
@keyframes danger { from { opacity: 0.55; } to { opacity: 1; } }

/* ============================================================== unit frames */
.unit {
  position: absolute;
  top: var(--gt);
  display: flex;
  align-items: center;
  gap: 10px;
  width: 290px;
  padding: 8px 10px;
}
.unit--player { left: var(--gl); }
.unit--target { left: calc(var(--gl) + 304px); }
.unit-portrait {
  position: relative;
  flex: none;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background:
    radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.28), transparent 55%),
    var(--accent);
  border: 3px solid transparent;
  background-clip: padding-box;
  box-shadow: 0 0 0 2px var(--steel-dk), 0 0 0 3px var(--steel), inset 0 -6px 12px rgba(0, 0, 0, 0.35);
}
/* Glyph shadows are drawn inside the pictogram (SP.Icons {shadow: true}, .ico-sh), not with filter: drop-shadow:
 * a CSS filter on HUD icons compiled the browser's blur programs at the first Start (docs/INTERFACE_AND_CONTROLS.md
 * "HUD raster warm"). */
.unit-glyph { font: 400 31px/1 var(--f-glyph); color: #fff; }
.unit-level {
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  min-width: 24px;
  padding: 2px 5px;
  border-radius: 9px;
  background: linear-gradient(180deg, #2a3160, #10142a);
  border: 1px solid var(--steel);
  font: 700 12px/1 var(--f-ui);
  text-align: center;
  color: var(--text);
}
.unit-level.is-skull { color: #fff; background: linear-gradient(180deg, #b3261e, #4a0a08); border-color: #ff8a80; }
.unit-combat {
  position: absolute;
  top: -5px;
  right: -6px;
  width: 22px; height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font: 400 13px/1 var(--f-glyph);
  color: #fff;
  background: radial-gradient(circle, #e0402f, #6e0d07);
  padding: 0;
  border: 1px solid #ffb0a0;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.unit.in-combat .unit-combat { opacity: 1; transform: scale(1); animation: combatGlint 1.1s ease-in-out infinite alternate; }
.unit.in-combat { box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 77, 61, 0.45), 0 0 18px rgba(255, 77, 61, 0.18); }
@keyframes combatGlint { to { box-shadow: 0 0 10px rgba(255, 90, 60, 0.9); } }
.unit-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.unit-name { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; min-width: 0; }
.unit-nm {
  font: 700 16px/1.1 var(--f-ui);
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}
.unit-sub, .unit-range { font: 500 12px/1 var(--f-ui); color: var(--text-faint); white-space: nowrap; }
.unit-title { font: 500 12px/1.1 var(--f-ui); color: var(--text-dim); }
.unit.is-dead { filter: grayscale(0.9); }
.unit.is-dead .unit-portrait { opacity: 0.6; }

/* bars */
.bar {
  position: relative;
  height: 16px;
  border-radius: 3px;
  overflow: hidden;
  background: linear-gradient(180deg, #05060c, #151a30);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(0, 0, 0, 0.6);
}
.bar-fill, .bar-trail {
  position: absolute;
  inset: 0;
  transform-origin: 0 50%;
  will-change: transform;
}
.bar--hp { --hp-h: 125; }
.bar--hp .bar-fill {
  background: linear-gradient(180deg, hsl(var(--hp-h), 78%, 62%), hsl(var(--hp-h), 74%, 38%) 60%, hsl(var(--hp-h), 70%, 30%));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
.bar--hp .bar-trail { background: rgba(255, 236, 200, 0.75); }
.bar--hp.is-low .bar-fill { animation: lowHp 0.8s ease-in-out infinite alternate; }
@keyframes lowHp { to { filter: brightness(1.45); } }
.bar--chakra { height: 11px; }
.bar--chakra .bar-fill {
  background: linear-gradient(180deg, var(--chakra-hi), var(--chakra) 45%, var(--chakra-dk));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.bar-txt, .bar-pct {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 700 12px/1 var(--f-ui);
  color: #fff;
  text-shadow: 0 0 2px #000, 0 1px 2px #000;
  font-variant-numeric: tabular-nums;
}
.bar--chakra .bar-txt { font-size: 10px; }
.bar-pct { justify-content: flex-end; padding-right: 6px; }
.unit--target .bar-txt { justify-content: flex-start; padding-left: 6px; }

/* target frame: faction colors, elite/boss dragon border */
.unit--target { flex-direction: row; }
.unit--target .unit-portrait { background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.25), transparent 55%), #4a5068; }
.unit--target.fac-hostile .unit-nm { color: var(--hostile); }
.unit--target.fac-hostile .unit-portrait { background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.22), transparent 55%), #7a2a26; }
.unit--target.fac-neutral .unit-nm { color: var(--neutral); }
.unit--target.fac-neutral .unit-portrait { background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.22), transparent 55%), #7a6522; }
.unit--target.fac-friendly .unit-nm { color: var(--friendly); }
.unit--target.fac-friendly .unit-portrait { background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.22), transparent 55%), #2d6a3c; }
.unit--target.fac-ally .unit-nm { color: var(--ally); }
.unit-tag {
  position: absolute;
  top: -8px;
  left: 50%;
  transform: translateX(-50%);
  padding: 1px 6px;
  border-radius: 3px;
  font: 400 10px/1.3 var(--f-title);
  letter-spacing: 0.06em;
  color: #2a1a00;
  background: linear-gradient(180deg, var(--brass-hi), var(--brass));
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
  white-space: nowrap;
}
.unit-tag:empty { display: none; }
.unit--target.is-elite .unit-portrait,
.unit--target.is-boss .unit-portrait {
  box-shadow: 0 0 0 2px var(--brass-dk), 0 0 0 4px var(--brass), 0 0 16px rgba(255, 196, 80, 0.45), inset 0 -6px 12px rgba(0, 0, 0, 0.35);
}
.unit--target.is-elite .unit-portrait::before,
.unit--target.is-boss .unit-portrait::before {
  /* dragon-scale crown: a ring of gold spikes behind the portrait */
  content: '';
  position: absolute;
  inset: -11px;
  z-index: -1;
  border-radius: 50%;
  background: repeating-conic-gradient(from -10deg, var(--brass) 0 9deg, transparent 9deg 20deg);
  -webkit-mask: radial-gradient(circle, transparent 58%, #000 59%, #000 70%, transparent 71%);
  mask: radial-gradient(circle, transparent 58%, #000 59%, #000 70%, transparent 71%);
}
.unit--target.is-boss { box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 196, 80, 0.5); }
.unit-buffs { display: flex; flex-wrap: wrap; gap: 3px; min-height: 0; }
.unit-buffs:empty { display: none; }
.unit--target .castbar { position: absolute; top: calc(100% + 8px); left: 0; right: 0; }

/* ============================================================== cast bars with hand seals */
.castbar { --school: var(--chakra-hi); display: flex; flex-direction: column; align-items: center; gap: 7px; pointer-events: none; }
.castbar--player { width: min(380px, calc(100vw - 32px)); }
.cb-seals { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; width: 100%; min-height: 0; }
.castbar:not(.has-seals) .cb-seals { display: none; }
.castbar--player .seal { --school: inherit; }
.cb-seals .seal { --school: inherit; }
.castbar .seal { border-color: rgba(160, 176, 232, 0.22); }
.castbar .seal.is-done { border-color: var(--school); }
/* Long chains (Zabuza's nine seals) use smaller chips and may wrap to a second row. */
.castbar.many-seals .seal { height: 20px; padding: 0 6px; font-size: 10px; letter-spacing: 0.03em; }
.cb-bar {
  position: relative;
  width: 100%;
  height: 22px;
  border-radius: 4px;
  overflow: hidden;
  border: 2px solid transparent;
  background: linear-gradient(180deg, #06070e, #171c36) padding-box, var(--steel-edge) border-box;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
}
.cb-fill {
  position: absolute;
  inset: 0;
  transform-origin: 0 50%;
  transform: scaleX(0);
  background: linear-gradient(180deg, #e0a84a, #8a5a10 45%, #4a2e06);
  will-change: transform;
}
.cb-name, .cb-time {
  position: absolute;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  font: 700 13px/1 var(--f-ui);
  color: #fff;
  text-shadow: 0 0 2px #000, 0 1px 2px #000;
}
.cb-name { left: 8px; right: 44px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cb-time { right: 8px; font-variant-numeric: tabular-nums; }
.castbar.is-done .cb-fill { background: linear-gradient(180deg, #fffbe6, #ffe08a); }
.castbar.is-done .cb-bar { box-shadow: 0 0 18px rgba(255, 220, 120, 0.7); }
.castbar.is-failed .cb-fill { background: linear-gradient(180deg, #ff8a80, #b3261e 50%, #5a0c08); }
.castbar.is-failed .seal { opacity: 0.45; filter: grayscale(0.8); }
.castbar.is-fading { opacity: 0; transition: opacity 0.3s ease; }
.castbar--target { gap: 5px; }
.castbar--target .cb-bar { height: 17px; }
.castbar--target .cb-fill { background: linear-gradient(180deg, #e08a3a, #9a4a14 50%, #5a2408); }
.castbar--target .seal { height: 18px; padding: 0 5px; font-size: 9.5px; letter-spacing: 0.02em; }


/* ============================================================== right column: minimap + tracker */
.hud-right {
  position: absolute;
  top: var(--gt);
  right: var(--gr);
  width: 210px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}
.minimap { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.mm-rim {
  position: relative;
  width: 168px;
  height: 168px;
  border-radius: 50%;
  padding: 5px;
  background:
    var(--rivet) 50% 1px / 8px 8px no-repeat,
    var(--rivet) 50% calc(100% - 1px) / 8px 8px no-repeat,
    var(--rivet) 1px 50% / 8px 8px no-repeat,
    var(--rivet) calc(100% - 1px) 50% / 8px 8px no-repeat,
    conic-gradient(from 210deg, var(--steel-hi), var(--steel-lo), var(--steel), var(--steel-dk), var(--steel-hi), var(--steel-lo), var(--steel-hi));
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.5);
}
.mm-canvas { display: block; width: 100%; height: 100%; border-radius: 50%; background: #2d3b2a; box-shadow: inset 0 0 0 1px #000; }
.mm-north {
  position: absolute;
  top: -4px;
  left: 50%;
  transform: translateX(-50%);
  width: 20px; height: 20px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font: 400 11px/1 var(--f-title);
  color: #2a1a00;
  background: linear-gradient(180deg, var(--brass-hi), var(--brass));
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
}
/* U3: the two buttons hang off the rim's lower corners, outside the ring the gate arrows are drawn on (hud.js drawGates: the tips stand
   1 px outside the canvas circle). The inner corner of each box is 50% - 2px from the centre along the diagonal, whatever the rim's size
   (168 / 140 / 100 px) or the button's (24 px, 22 px on a phone), so no arrow can hide under one. */
.mm-zoom {
  position: absolute;
  top: calc(50% + (50% - 2px) * 0.7072);
  width: 24px; height: 24px;
  border-radius: 50%;
  border: 1px solid var(--steel);
  background: linear-gradient(180deg, #2b3366, #12162e);
  color: var(--text);
  font: 700 15px/1 var(--f-ui);
  cursor: pointer;
  pointer-events: auto;
  display: grid;
  place-items: center;
  padding: 0;
}
.mm-zoom:hover { border-color: var(--accent); }
.mm-zoom--in { left: calc(50% + (50% - 2px) * 0.7072); }
.mm-zoom--out { right: calc(50% + (50% - 2px) * 0.7072); }
.mm-zone {
  max-width: 100%;
  font: 700 14px/1.15 var(--f-ui);
  text-align: center;
  text-shadow: 0 1px 2px #000, 0 0 6px rgba(0, 0, 0, 0.8);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text);
}
.zone-safe { color: #8fe3ff; }
.zone-contested { color: #ffe08a; }
.zone-danger { color: #ff8a7a; }
.zone-wild { color: var(--text-dim); }
.mm-coords { font: 500 11px/1 var(--f-ui); color: var(--text-faint); text-shadow: 0 1px 2px #000; font-variant-numeric: tabular-nums; }

/* buffs */
.buffbar {
  position: absolute;
  top: var(--gt);
  right: calc(var(--gr) + 224px);
  display: flex;
  flex-direction: row-reverse;
  flex-wrap: wrap;
  gap: 5px;
  max-width: 280px;
}
.buff {
  position: relative;
  width: 36px; height: 36px;
  border-radius: 6px;
  display: grid;
  place-items: center;
  background: radial-gradient(circle at 35% 25%, rgba(255, 255, 255, 0.35), transparent 60%), var(--icon-bg, #444);
  border: 2px solid var(--steel);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
  pointer-events: auto;
  cursor: default;
}
.buff.is-debuff { border-color: #e0402f; box-shadow: 0 0 8px rgba(224, 64, 47, 0.5); }
.bf-glyph { font: 400 21px/1 var(--f-glyph); color: #fff; } /* shadow: SP.Icons {shadow: true} (see .unit-glyph) */
.bf-stk { position: absolute; right: 2px; bottom: 1px; font: 700 11px/1 var(--f-ui); text-shadow: 0 0 2px #000; }
.bf-time {
  position: absolute;
  bottom: -15px;
  left: 50%;
  transform: translateX(-50%);
  font: 600 11px/1 var(--f-ui);
  color: var(--text);
  text-shadow: 0 1px 2px #000;
  white-space: nowrap;
}
.buff.is-expiring .bf-time { color: var(--neutral); }
.buff.is-expiring { animation: expiring 0.6s ease-in-out infinite alternate; }
@keyframes expiring { to { opacity: 0.55; } }
.buff.is-small { width: 22px; height: 22px; border-width: 1px; border-radius: 4px; }
.buff.is-small .bf-glyph { font-size: 15px; }
.buff.is-small .bf-time { bottom: auto; top: 50%; left: auto; right: -2px; transform: none; font-size: 0; }

/* mission tracker */
.tracker { padding: 0; pointer-events: auto; }
.tr-head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px 6px;
  border: 0;
  background: transparent;
  cursor: pointer;
  text-align: left;
}
.tr-glyph { font: 400 16px/1 var(--f-glyph); color: var(--accent); }
.tr-title { flex: 1; font: 400 13px/1 var(--f-title); letter-spacing: 0.04em; color: var(--text); }
.tr-count {
  min-width: 18px;
  padding: 2px 5px;
  border-radius: 9px;
  font: 700 11px/1 var(--f-ui);
  text-align: center;
  background: rgba(255, 255, 255, 0.08);
}
.tr-count:empty { display: none; }
.tr-caret { color: var(--text-faint); transition: transform 0.2s ease; }
.tracker.is-collapsed .tr-caret { transform: rotate(-90deg); }
.tracker.is-collapsed .tr-list { display: none; }
.tr-list { list-style: none; margin: 0; padding: 0 8px 8px; display: flex; flex-direction: column; gap: 4px; }
.tq-btn {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 3px;
  padding: 6px 6px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
  text-align: left;
}
.tq-btn:hover { background: rgba(255, 255, 255, 0.05); }
.tq-name { display: flex; align-items: center; font: 700 13px/1.15 var(--f-ui); color: var(--quest); }
.tq-obj { font: 500 12px/1.2 var(--f-ui); color: var(--text-dim); padding-left: 24px; }
.tq-obj b { color: var(--text); font-weight: 700; }
.tq-obj.is-done { color: var(--good); }
.tq-obj.is-done::before { content: '✓ '; }
.tq-prog { display: block; height: 3px; margin-left: 24px; border-radius: 2px; background: rgba(255, 255, 255, 0.08); overflow: hidden; }
.tq-prog i { display: block; height: 100%; background: linear-gradient(90deg, var(--quest), #ffb347); transform-origin: 0 50%; }
.tq-zone { font: 500 11px/1 var(--f-ui); color: var(--text-faint); padding-left: 24px; }
.tq.is-complete .tq-name { color: var(--good); }
.tq--empty { padding: 4px 6px 2px; font: 500 12px/1.3 var(--f-ui); color: var(--text-faint); }
/* "Next jutsu" line: the nearest pending unlock (level + kill goal). */
.tq--jutsu { margin-top: 2px; border-top: 1px solid var(--ink-line); padding-top: 2px; }
.tq--jutsu .tq-name { color: #9fd0ff; }
.tq-ico {
  flex: none;
  width: 18px; height: 18px;
  margin-right: 6px;
  border-radius: 4px;
  display: grid;
  place-items: center;
  font-size: 13px;
  color: #fff;
  background: var(--icon-bg, #444);
}
.tq--jutsu .tq-prog i { background: linear-gradient(90deg, #5fb0ff, #9fe0ff); }

/* ============================================================== chat / combat log */
.chat {
  position: absolute;
  left: var(--gl);
  bottom: var(--gb);
  width: 390px;
  height: 178px;
  display: flex;
  flex-direction: column;
  padding: 0;
  pointer-events: auto;
  transition: opacity 0.6s ease;
}
.chat.is-idle { opacity: 0.45; }
.chat-tabs { display: flex; gap: 2px; padding: 5px 8px 0; border-bottom: 1px solid var(--ink-line); }
.chat-tab {
  padding: 4px 10px 5px;
  border: 0;
  border-radius: 4px 4px 0 0;
  background: transparent;
  color: var(--text-faint);
  font: 600 12px/1 var(--f-ui);
  letter-spacing: 0.04em;
  cursor: pointer;
}
.chat-tab.is-on { color: var(--text); background: rgba(255, 255, 255, 0.07); box-shadow: inset 0 -2px 0 var(--accent); }
.chat-lines {
  flex: 1;
  overflow-y: auto;
  padding: 5px 10px 8px;
  font: 500 13.5px/1.35 var(--f-ui);
  scrollbar-width: thin;
  scrollbar-color: var(--steel-lo) transparent;
  user-select: text;
  -webkit-user-select: text;
}
.ln { color: #e6e1cf; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8); overflow-wrap: anywhere; }
.ln.k-system { color: #ffeaa8; }
.ln.k-combat { color: #d8dbe8; }
.ln.k-quest { color: var(--quest); }
.ln.k-xp { color: #c7a8ff; }
.ln.k-error { color: #ff7a6e; }
.ln.k-loot { color: var(--good); }
.ln.k-say { color: #fff; font-style: italic; }
.ln.k-combat-in { color: #ffb0a6; }
/* Incoming melee swings flood General in pack fights: they show in the Combat tab only. */
.chat:not(.filter-combat) .ln.k-combat-in { display: none; }
.chat.filter-combat .ln:not(.k-combat):not(.k-combat-in) { display: none; }

/* ============================================================== bottom center: prompt, cast, action bar, XP */
.hud-bottom {
  position: absolute;
  left: 50%;
  bottom: var(--gb);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.interact-prompt {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px 6px 8px;
  border-radius: 20px;
  border: 1px solid var(--ink-line-2);
  background: rgba(12, 15, 33, 0.82);
  color: var(--text);
  font: 600 14px/1 var(--f-ui);
  cursor: pointer;
  pointer-events: auto;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
  animation: promptIn 0.25s ease;
}
.interact-prompt kbd { background: linear-gradient(180deg, var(--accent), #6a2f0c); border-color: rgba(255, 255, 255, 0.3); color: #fff; }
@keyframes promptIn { from { opacity: 0; transform: translateY(6px); } }

.actionbar { display: flex; gap: var(--slot-gap); padding: 7px; }
/* The action bar's row (SP.HUD): the bar, X / G (absolute: beside it, or above its right end) and the XP bar under it,
 * so a wider clone bar, duel chip or cast bar above never moves X / G or stretches the XP bar. */
.bar-row { position: relative; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.slot {
  position: relative;
  width: var(--slot);
  height: var(--slot);
  padding: 0;
  border-radius: 6px;
  border: 2px solid transparent;
  background: linear-gradient(#0a0c18, #0a0c18) padding-box, var(--steel-edge) border-box;
  cursor: pointer;
  pointer-events: auto;
  overflow: visible;
  transition: transform 0.08s ease;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.slot-icon {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  display: grid;
  place-items: center;
  overflow: hidden;
  background:
    radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.38), transparent 52%),
    radial-gradient(circle at 70% 90%, rgba(0, 0, 0, 0.45), transparent 60%),
    var(--icon-bg, #2a2f4a);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.55), inset 0 -8px 14px rgba(0, 0, 0, 0.3);
}
.slot-glyph {
  position: absolute;
  left: 50%;
  top: 42%;
  transform: translate(-50%, -50%);
  font: 400 24px/1 var(--f-glyph);
  color: #fff; /* shadow: SP.Icons {shadow: true} (see .unit-glyph) */
}
.slot-short {
  position: absolute;
  left: 2px;
  right: 2px;
  bottom: 3px;
  font: 700 9px/1 var(--f-ui);
  letter-spacing: 0.01em;
  text-align: center;
  color: #fff;
  text-shadow: 0 0 2px #000, 0 1px 2px #000;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: clip;
}
.slot-tint { position: absolute; inset: 0; border-radius: 4px; opacity: 0; transition: opacity 0.12s ease; }
.slot.no-chakra .slot-tint { opacity: 1; background: rgba(24, 52, 170, 0.55); }
.slot.out-of-range .slot-tint { opacity: 1; background: rgba(170, 20, 20, 0.45); }
.slot.out-of-range .slot-key { color: #ff6b5e; }
.slot-cd {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  opacity: 0;
  background: conic-gradient(transparent 0deg var(--rev, 360deg), rgba(4, 6, 16, 0.76) var(--rev, 360deg) 360deg);
}
.slot.on-cd .slot-cd, .slot.on-gcd .slot-cd { opacity: 1; }
.slot.on-gcd .slot-cd { background: conic-gradient(transparent 0deg var(--rev, 360deg), rgba(4, 6, 16, 0.5) var(--rev, 360deg) 360deg); }
.slot-cdtxt {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font: 700 19px/1 var(--f-ui);
  color: #fff;
  text-shadow: 0 0 3px #000, 0 1px 2px #000;
  font-variant-numeric: tabular-nums;
}
.slot-key {
  position: absolute;
  top: 2px;
  left: 4px;
  font: 700 12px/1 var(--f-ui);
  color: #f3f0e6;
  text-shadow: 0 0 2px #000, 0 1px 2px #000;
  z-index: 2;
}
.slot-pips { position: absolute; right: 4px; top: 4px; display: flex; gap: 2px; z-index: 2; }
.slot-pips i { width: 6px; height: 6px; transform: rotate(45deg); background: rgba(0, 0, 0, 0.55); border: 1px solid rgba(255, 220, 140, 0.55); }
.slot-pips i.is-on { background: var(--brass-hi); border-color: #fff4cf; box-shadow: 0 0 4px rgba(255, 207, 74, 0.9); }
.slot-lock {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  background: repeating-linear-gradient(135deg, rgba(8, 10, 22, 0.8) 0 6px, rgba(18, 22, 44, 0.8) 6px 12px);
}
.slot-lock b { font: 400 16px/1 var(--f-glyph); color: #d6c7a0; }
.slot-lv { font: 700 10px/1 var(--f-ui); color: var(--text); white-space: nowrap; text-shadow: 0 1px 2px #000; }
.slot.is-locked .slot-lock { display: flex; }
.slot.is-locked .slot-pips { opacity: 0.4; }
.slot-badge {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 20px; height: 20px;
  border-radius: 50%;
  display: none;
  place-items: center;
  font: 800 13px/1 var(--f-ui);
  color: #2a1a00;
  background: radial-gradient(circle at 40% 35%, #fff2b0, var(--gold) 60%, #b07a10);
  box-shadow: 0 0 10px rgba(255, 207, 74, 0.8);
  z-index: 3;
}
.slot.is-learnable .slot-badge { display: grid; animation: badge 1s ease-in-out infinite alternate; }
@keyframes badge { to { transform: scale(1.15); } }
.slot:hover { filter: brightness(1.15); }
.slot.is-pressed { transform: scale(0.92); }
.slot.is-pressed .slot-icon { box-shadow: inset 0 0 0 2px #fff, inset 0 0 16px rgba(255, 255, 255, 0.5); }
.slot.is-ready .slot-icon { animation: ready 0.45s ease-out; }
@keyframes ready { 0% { box-shadow: inset 0 0 0 2px #fff, 0 0 18px #fff; filter: brightness(1.8); } 100% { filter: none; } }
.slot.is-casting { background: linear-gradient(#0a0c18, #0a0c18) padding-box, linear-gradient(180deg, #fff2b0, var(--gold)) border-box; }
/* M1: a finished string opens a 1 s window in which the character's taijutsu jutsu hits 20% harder: its slot glows. */
.slot.is-ender { animation: enderGlow 0.4s ease-in-out infinite alternate; }
@keyframes enderGlow { from { box-shadow: 0 0 6px 1px rgba(255, 207, 74, 0.6); } to { box-shadow: 0 0 20px 5px rgba(255, 207, 74, 0.95), 0 0 0 2px #ffe9a0; } }
.slot.is-empty { cursor: default; opacity: 0.4; }

/* XP bar */
.xpbar {
  position: relative;
  width: 100%;
  height: 10px;
  border-radius: 3px;
  overflow: hidden;
  pointer-events: auto;
  background: linear-gradient(180deg, #05060c, #161233);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(0, 0, 0, 0.7), 0 0 0 2px rgba(163, 172, 191, 0.35);
}
.xp-fill {
  position: absolute;
  inset: 0;
  transform-origin: 0 50%;
  background: linear-gradient(180deg, var(--xp-hi), var(--xp) 50%, var(--xp-dk));
  transition: transform 0.4s ease;
}
.xp-ticks {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(90deg, transparent 0 calc(5% - 1px), rgba(0, 0, 0, 0.6) calc(5% - 1px) 5%);
}
.xp-txt {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 700 10px/1 var(--f-ui);
  color: #fff;
  text-shadow: 0 0 2px #000, 0 0 3px #000;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.xpbar:hover { height: 16px; }
.xpbar:hover .xp-txt { opacity: 1; font-size: 11px; }
.xpbar.is-max .xp-fill { background: linear-gradient(180deg, #fff2b0, var(--gold) 50%, #8a5a08); }
.xpbar.is-gain .xp-fill { animation: xpGain 0.7s ease; }
@keyframes xpGain { 0% { filter: brightness(2.2); } 100% { filter: none; } }
/* SP.HUD restarts an animation by switching to an identical copy of its keyframes (is-gain ↔ is-gain-b,
 * is-pop ↔ is-pop-b, bump ↔ bump-b, banner is-replay): a new animation-name restarts it without the
 * layout read the remove / reflow / add trick needs. */
.xpbar.is-gain-b .xp-fill { animation: xpGainB 0.7s ease; }
@keyframes xpGainB { 0% { filter: brightness(2.2); } 100% { filter: none; } }

/* ============================================================== micro menu */
.micro {
  position: absolute;
  right: var(--gr);
  bottom: var(--gb);
  display: flex;
  gap: 5px;
  padding: 5px;
  border-radius: 8px;
  background: rgba(10, 12, 26, 0.6);
  box-shadow: 0 0 0 1px var(--ink-line);
}
.micro-btn {
  position: relative;
  width: 42px;
  height: 44px;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  border-radius: 6px;
  border: 1px solid var(--steel-lo);
  background: radial-gradient(circle at 50% 20%, rgba(120, 140, 230, 0.25), transparent 70%), linear-gradient(180deg, #252c58, #10142a);
  cursor: pointer;
  pointer-events: auto;
  transition: transform 0.08s ease, border-color 0.15s ease;
}
.micro-btn:hover { border-color: var(--accent); }
.micro-btn:active { transform: scale(0.94); }
.micro-btn.is-open { border-color: var(--accent); box-shadow: inset 0 0 12px rgba(255, 255, 255, 0.12), 0 0 0 1px var(--accent); }
.micro-glyph { font: 400 19px/1 var(--f-glyph); color: var(--text); }
.micro-btn .ico { display: block; }
.micro-key { font: 700 10.5px/1 var(--f-ui); color: var(--text-faint); letter-spacing: 0.04em; }
.micro-badge {
  position: absolute;
  top: -7px;
  right: -7px;
  min-width: 19px;
  height: 19px;
  padding: 0 5px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  font: 800 12px/1 var(--f-ui);
  color: #2a1a00;
  background: radial-gradient(circle at 40% 35%, #fff2b0, var(--gold) 60%, #b07a10);
  box-shadow: 0 0 10px rgba(255, 207, 74, 0.8);
  animation: badge 1s ease-in-out infinite alternate;
}
.saved-chip {
  position: absolute;
  right: var(--gr);
  bottom: calc(var(--gb) + 62px);
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 5px;
  border-radius: 14px;
  background: rgba(12, 15, 33, 0.8);
  border: 1px solid var(--ink-line-2);
  font: 600 12px/1 var(--f-ui);
  color: var(--text-dim);
}
.saved-chip span {
  width: 20px; height: 20px;
  border-radius: 4px;
  display: grid;
  place-items: center;
  background: var(--seal-red);
  color: #fff;
  font: 400 14px/1 var(--f-glyph);
}
.saved-chip.is-pop { animation: savedPop 1.8s ease forwards; }
@keyframes savedPop { 0% { opacity: 0; transform: translateY(6px); } 15% { opacity: 1; transform: none; } 75% { opacity: 1; } 100% { opacity: 0; } }
.saved-chip.is-pop-b { animation: savedPopB 1.8s ease forwards; }
@keyframes savedPopB { 0% { opacity: 0; transform: translateY(6px); } 15% { opacity: 1; transform: none; } 75% { opacity: 1; } 100% { opacity: 0; } }

/* ============================================================== center messages and banners */
.center-msgs {
  position: absolute;
  z-index: 40; /* on #ui, above .panels (30): feedback from panel buttons stays visible */
  top: calc(var(--gt) + 92px);
  left: 50%;
  transform: translateX(-50%);
  width: min(620px, calc(100vw - 32px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  pointer-events: none;
  will-change: opacity; /* its own compositor layer, like .hud (raster warm) */
}
.cmsg {
  font: 700 18px/1.2 var(--f-ui);
  text-align: center;
  text-shadow: 0 0 3px #000, 0 2px 4px rgba(0, 0, 0, 0.9);
  transition: opacity 0.6s ease;
  animation: msgIn 0.18s ease-out;
}
.cmsg--error { color: #ff4d3d; }
.cmsg--quest { color: var(--quest); }
.cmsg--info { color: #f4f1e6; }
.cmsg.is-fading { opacity: 0; }
.cmsg.bump { animation: msgBump 0.22s ease-out; }
.cmsg.bump-b { animation: msgBumpB 0.22s ease-out; }
@keyframes msgIn { from { opacity: 0; transform: translateY(-6px); } }
@keyframes msgBump { 50% { transform: scale(1.08); } }
@keyframes msgBumpB { 50% { transform: scale(1.08); } }

.banner {
  position: absolute;
  left: 50%;
  top: 22%;
  transform: translateX(-50%);
  width: min(920px, calc(100vw - 32px));
  text-align: center;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
}
.banner--level { top: 33%; }
.banner.is-playing { visibility: visible; animation: bannerShow 4s ease forwards; }
.banner--zone.is-playing { animation-duration: 3.8s; }
.banner--level.is-quest.is-playing { animation-duration: 3.6s; }
@keyframes bannerShow {
  0% { opacity: 0; transform: translateX(-50%) translateY(10px) scale(0.96); }
  10% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
  78% { opacity: 1; }
  100% { opacity: 0; transform: translateX(-50%) translateY(-6px); }
}
.banner.is-playing.is-replay { animation-name: bannerShowB; }
@keyframes bannerShowB {
  0% { opacity: 0; transform: translateX(-50%) translateY(10px) scale(0.96); }
  10% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
  78% { opacity: 1; }
  100% { opacity: 0; transform: translateX(-50%) translateY(-6px); }
}
.bn-kicker { font: 700 13px/1 var(--f-ui); letter-spacing: 0.32em; text-transform: uppercase; color: var(--text-dim); text-shadow: 0 1px 3px #000; margin-bottom: 6px; }
.bn-title {
  font: 400 clamp(28px, 4.6vw, 46px)/1.08 var(--f-title);
  color: #fff8e6;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.55), 0 0 22px rgba(0, 0, 0, 0.8), 0 0 40px rgba(0, 0, 0, 0.5);
  overflow-wrap: anywhere;
}
.bn-stroke {
  width: min(380px, 70%);
  height: 10px;
  margin: 8px auto 6px;
  border-radius: 50% 40% 60% 30% / 60% 50% 40% 50%;
  background: linear-gradient(90deg, transparent, var(--accent) 12%, var(--accent) 70%, transparent);
  opacity: 0.9;
  transform: skewX(-18deg);
}
.bn-sub { font: 600 17px/1.25 var(--f-ui); color: var(--text); text-shadow: 0 1px 3px #000, 0 0 10px rgba(0, 0, 0, 0.8); }
.banner.zone-safe .bn-sub { color: #8fe3ff; }
.banner.zone-contested .bn-sub { color: #ffe08a; }
.banner.zone-danger .bn-sub { color: #ff8a7a; }
.banner.is-level .bn-title { color: #ffe9a6; text-shadow: 0 0 18px rgba(255, 200, 80, 0.85), 0 2px 0 rgba(0, 0, 0, 0.6); }
.banner.is-level .bn-stroke { background: linear-gradient(90deg, transparent, var(--gold) 12%, var(--gold) 70%, transparent); }
.banner.is-level .bn-kicker { color: var(--gold); }
.banner.is-quest .bn-kicker { color: var(--quest); }
.banner.is-unlock .bn-kicker { color: #9fd0ff; }
.banner.is-unlock .bn-title { color: #e6f4ff; text-shadow: 0 0 18px rgba(95, 176, 255, 0.85), 0 2px 0 rgba(0, 0, 0, 0.6); }
.banner.is-unlock .bn-stroke { background: linear-gradient(90deg, transparent, #5fb0ff 12%, #5fb0ff 70%, transparent); }
.banner.is-quest .bn-title { font-size: clamp(24px, 4vw, 38px); }

/* ============================================================== tooltip */
.hud-tip {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 50;
  width: max-content;
  max-width: min(310px, calc(100vw - 24px));
  padding: 10px 12px;
  pointer-events: none;
  font: 500 13.5px/1.35 var(--f-ui);
}
.tt-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.tt-glyph {
  width: 36px; height: 36px;
  border-radius: 6px;
  display: grid;
  place-items: center;
  font: 400 21px/1 var(--f-glyph);
  color: #fff;
  background: radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.38), transparent 55%), var(--icon-bg, #333);
  border: 1px solid var(--steel);
}
.tt-name { font: 700 16px/1.15 var(--f-ui); color: #fff; }
.tt-name.is-debuff { color: var(--hostile); }
.tt-rank { font: 600 12px/1.2 var(--f-ui); color: var(--brass-hi); }
.tt-grid { display: grid; grid-template-columns: 1fr auto; gap: 1px 14px; color: var(--text); font-size: 13px; }
.tt-grid span:nth-child(even) { text-align: right; }
.tt-seals { display: flex; gap: 3px; margin: 7px 0 2px; flex-wrap: wrap; }
.tt-seals .seal { height: 18px; padding: 0 6px; font-size: 9.5px; letter-spacing: 0.03em; color: var(--text-dim); }
.tt-text { margin: 6px 0 0; color: #ffd98a; }
.tt-warn { margin: 6px 0 0; color: #ff7a6e; }
.tt-good { margin: 6px 0 0; color: var(--good); }
.tt-key { margin-top: 6px; color: var(--text-faint); font-size: 12px; }
.tt-req { margin: 4px 0 0; padding-left: 18px; color: var(--text); font-size: 13px; }
.tt-req li.is-met { color: var(--good); }
.tt-req li.is-met::marker { content: '✓  '; }
.tt-hint { margin: 4px 0 0; color: var(--text-faint); font-size: 12px; }

/* ============================================================== nameplates */
.plate {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
  will-change: transform;
  pointer-events: none;
}
.np-in {
  transform: translate(-50%, -100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  white-space: nowrap;
}
.np-name {
  display: flex;
  align-items: center;
  gap: 4px;
  font: 700 13px/1.1 var(--f-ui);
  color: var(--text);
  text-shadow: 0 0 2px #000, 0 1px 2px #000, 0 0 4px #000;
}
.plate.fac-hostile .np-txt { color: var(--hostile); }
.plate.fac-neutral .np-txt { color: var(--neutral); }
.plate.fac-friendly .np-txt { color: var(--friendly); }
.np-lvl { font: 800 11px/1 var(--f-ui); }
.np-lvl:empty { display: none; }
.con-red { color: #ff4d3d; }
.con-orange { color: #ff9a3d; }
.con-yellow { color: #ffe14a; }
.con-green { color: #5fe07a; }
.con-grey { color: #9a9a9a; }
.np-title { font: 500 11px/1 var(--f-ui); color: #c8f0d0; text-shadow: 0 0 2px #000, 0 1px 2px #000; }
/* Step 8 (docs/INTERFACE_AND_CONTROLS.md "Name tags over other players"): another player's tag is the ally blue; red while he can attack me; in the groups
 * mode the title line is the group's badge: a dot in the group colour (--g) and its name in the plain text colour, so a red group (the Akatsuki) is not a red name. */
.plate.fac-ally .np-txt { color: var(--ally); }
.plate.is-pvp .np-txt { color: var(--hostile); }
.np-title.is-grp { color: var(--text); font-weight: 700; }
.np-title.is-grp::before { content: ''; display: inline-block; width: 8px; height: 8px; margin-right: 4px; border-radius: 50%; background: var(--g); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.85), 0 0 2px #000; vertical-align: -1px; }
.np-bar {
  width: 78px;
  height: 7px;
  border-radius: 2px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.75);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.9);
}
.np-bar i {
  display: block;
  height: 100%;
  transform-origin: 0 50%;
  --hp-h: 125;
  background: hsl(var(--hp-h), 72%, 46%);
}
.plate.fac-hostile .np-bar i { background: linear-gradient(180deg, #ff6e5e, #b8261b); }
.plate.fac-neutral .np-bar i { background: linear-gradient(180deg, #ffe46e, #b8941b); }
.plate.is-target .np-bar { width: 96px; height: 9px; box-shadow: 0 0 0 1px #000, 0 0 0 2px rgba(255, 255, 255, 0.85); }
.plate.is-target .np-name { font-size: 14px; }
.plate.is-hover .np-name { filter: brightness(1.3); }
.plate.is-boss .np-bar { width: 120px; height: 10px; box-shadow: 0 0 0 1px #000, 0 0 0 2px var(--brass); }
.plate.is-boss .np-txt { font-family: var(--f-title); font-weight: 400; letter-spacing: 0.03em; }
.np-marker {
  font: 800 26px/1 var(--f-ui);
  color: var(--gold);
  text-shadow: 0 0 3px #000, 0 2px 3px #000, 0 0 12px rgba(255, 207, 74, 0.8);
  animation: markerBob 1.2s ease-in-out infinite alternate;
}
.np-marker.is-active { color: #c9c9c9; text-shadow: 0 0 3px #000, 0 2px 3px #000; font-size: 20px; animation: none; }
@keyframes markerBob { to { transform: translateY(-3px); } }
.np-cast { position: relative; width: auto; min-width: 96px; max-width: 160px; height: 14px; border-radius: 2px; overflow: hidden; background: rgba(0, 0, 0, 0.75); box-shadow: 0 0 0 1px #000; }
.np-cast i { position: absolute; inset: 0; transform-origin: 0 50%; background: linear-gradient(180deg, #e08a3a, #9a4a14); }
.np-cast span { position: relative; display: block; padding: 0 4px; font: 700 11px/14px var(--f-ui); color: #fff; text-shadow: 0 0 2px #000, 0 0 3px #000; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ============================================================== floating combat text */
.ft {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 50% 50%;
  will-change: transform, opacity;
  translate: -50% -50%;
  white-space: nowrap;
  text-align: center;
  font: 800 21px/1 var(--f-ui);
  color: #fff;
  text-shadow: 0 0 2px #000, 0 2px 2px #000, 0 0 6px rgba(0, 0, 0, 0.7);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}
.ft.is-crit { font-size: 31px; color: #ffe14a; }
.ft.is-crit::after { content: '!'; }
.ft--pet { font-size: 15px; opacity: 0.85; color: #e8e4d8; }
.ft--dot { font-size: 16px; font-weight: 700; }
.ft--taken { color: #ff4d3d; }
.ft--taken.is-crit { color: #ff2a1a; }
.ft--heal { color: #6dff8a; }
.ft--miss { color: #e6e6f0; font-style: italic; font-size: 18px; letter-spacing: 0.04em; }
.ft--xp { color: #c9a8ff; font-size: 16px; }

/* ============================================================== speech bubbles */
.bubble {
  position: absolute;
  left: 0;
  top: 0;
  max-width: 220px;
  padding: 7px 11px;
  border-radius: 12px;
  transform-origin: 0 0;
  will-change: transform;
  width: max-content;
  min-width: 60px;
  translate: -50% -100%;
  background: #fbf6e8;
  color: var(--sumi);
  font: 700 14px/1.25 var(--f-ui);
  text-align: center;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(0, 0, 0, 0.6);
  transition: opacity 0.35s ease;
}
.bubble::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -7px;
  margin-left: -7px;
  border: 7px solid transparent;
  border-bottom: 0;
  border-top-color: #fbf6e8;
}
.bubble.is-boss { background: #1a1f3a; color: #e8f2ff; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.6), 0 0 0 2px #6fb6ff; font-style: italic; }
.bubble.is-boss::after { border-top-color: #1a1f3a; }
.bubble.is-fading { opacity: 0; }

/* ============================================================== panels */
.panels { position: absolute; inset: 0; pointer-events: none; z-index: 30; }
.pn-backdrop {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, rgba(8, 10, 22, 0.35), rgba(8, 10, 22, 0.7));
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease;
}
.pn-backdrop.is-on { opacity: 1; visibility: visible; pointer-events: auto; }
.panel {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100% - var(--gt) - var(--gb) - 24px);
  display: flex;
  flex-direction: column;
  pointer-events: auto;
  outline: none;
  animation: panelIn 0.18s ease-out;
}
.panel--wide { width: min(860px, calc(100vw - 32px)); }
.panel--map { width: min(720px, calc(100vw - 32px)); }
.panel--menu { width: min(340px, calc(100vw - 32px)); }
.panel--dialog { left: max(var(--gl), calc(50% - 470px)); transform: translateY(-50%); width: min(420px, calc(100vw - 32px)); }
@keyframes panelIn { from { opacity: 0; transform: translate(-50%, calc(-50% + 10px)); } }
.panel--dialog { animation-name: dialogIn; }
@keyframes dialogIn { from { opacity: 0; transform: translate(-10px, -50%); } }
.panel--dialog .pn-head { border-bottom: 0; background: none; padding-bottom: 0; }
.panel--dialog .pn-glyph { display: none; }
.panel--dialog .pn-title { visibility: hidden; }
.pn-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px 9px 14px;
  border-bottom: 1px solid var(--ink-line);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent);
  flex: none;
}
.pn-glyph {
  width: 30px; height: 30px;
  border-radius: 5px;
  display: grid;
  place-items: center;
  font: 400 18px/1 var(--f-glyph);
  color: #fff;
  background: var(--accent);
  box-shadow: inset 0 -4px 8px rgba(0, 0, 0, 0.3);
}
.pn-title { flex: 1; margin: 0; font: 400 19px/1.1 var(--f-title); letter-spacing: 0.03em; color: var(--text); }
.pn-x {
  width: 36px; height: 36px;
  border-radius: 6px;
  border: 1px solid var(--ink-line-2);
  background: linear-gradient(180deg, #2b3366, #141834);
  color: var(--text);
  font: 400 22px/1 var(--f-ui);
  cursor: pointer;
  display: grid;
  place-items: center;
  padding: 0;
}
.pn-x:hover { border-color: var(--hostile); color: #fff; }
.pn-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 4px 18px 18px;
  scrollbar-width: thin;
  scrollbar-color: var(--steel-lo) transparent;
}

/* ---------------------------------------------------------------- jutsu scroll */
.panel--jutsu {
  width: min(640px, calc(100vw - 32px));
  height: min(880px, calc(100% - var(--gt) - var(--gb) - 16px));
  max-height: none;
  animation: unroll 0.32s cubic-bezier(0.2, 0.8, 0.2, 1);
}
@keyframes unroll { from { opacity: 0; transform: translate(-50%, -50%) scaleY(0.12); } }
.scroll { position: relative; height: 100%; display: flex; flex-direction: column; }
.scroll-roller {
  position: relative;
  flex: none;
  height: 26px;
  margin: 0 -14px;
  border-radius: 13px;
  background: linear-gradient(180deg, var(--wood-dk) 0%, var(--wood) 22%, var(--wood-hi) 42%, var(--wood) 64%, var(--wood-dk) 100%);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.4);
  z-index: 2;
}
.scroll-roller::after {
  content: '';
  position: absolute;
  inset: 8px 22px;
  border-radius: 6px;
  background: repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.12) 0 2px, transparent 2px 18px);
}
.knob {
  position: absolute;
  top: -5px;
  width: 22px;
  height: 36px;
  border-radius: 8px;
  background: linear-gradient(90deg, #2a1206, #6a3a18 35%, #b9783e 55%, #4a2410);
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.55), inset 0 0 0 1px rgba(0, 0, 0, 0.5);
}
.knob::after { content: ''; position: absolute; inset: 13px 6px; border-radius: 50%; background: radial-gradient(circle, var(--brass-hi), var(--brass) 60%, var(--brass-dk)); }
.knob--l { left: -10px; }
.knob--r { right: -10px; }
.scroll-paper {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 18px 26px 18px;
  color: var(--sumi);
  background:
    var(--paper-grain),
    linear-gradient(90deg, rgba(110, 70, 25, 0.32), transparent 5%, transparent 95%, rgba(110, 70, 25, 0.32)),
    linear-gradient(180deg, rgba(90, 55, 20, 0.28), transparent 3%, transparent 97%, rgba(90, 55, 20, 0.28)),
    linear-gradient(180deg, var(--paper), var(--paper-2));
  box-shadow: inset 0 0 30px rgba(120, 80, 30, 0.25), 0 18px 30px rgba(0, 0, 0, 0.6); /* the scroll's shadow: no filter (S3 PERFUX-3) */
  scrollbar-width: thin;
  scrollbar-color: rgba(90, 55, 20, 0.5) transparent;
  user-select: text;
  -webkit-user-select: text;
}
.pn-x--scroll {
  position: absolute;
  top: 34px;
  right: 10px;
  z-index: 3;
  background: rgba(32, 23, 14, 0.85);
  border-color: rgba(32, 23, 14, 0.4);
  color: var(--paper);
}
.sc-head { display: flex; align-items: center; gap: 14px; padding-right: 40px; }
.sc-emblem {
  font: 400 34px/1 var(--f-glyph);
  color: var(--sumi);
  padding: 6px 12px 6px 2px;
  border-right: 2px solid rgba(32, 23, 14, 0.25);
}
.sc-titles { flex: 1; min-width: 0; }
.sc-titles h2 { margin: 0; font: 400 30px/1.05 var(--f-title); color: var(--sumi); letter-spacing: 0.02em; }
.sc-sub { font: 600 14px/1.3 var(--f-ui); color: #5a4630; margin-top: 2px; }
.sc-points {
  flex: none;
  width: 78px;
  height: 78px;
  border-radius: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #fff4e0;
  background: radial-gradient(circle at 40% 35%, #c9473d, var(--seal-red) 60%, #6e100b);
  box-shadow: 0 0 0 3px rgba(179, 38, 30, 0.35), 0 4px 10px rgba(80, 20, 10, 0.4);
  transform: rotate(-6deg);
  opacity: 0.6;
}
.sc-points.has-points { opacity: 1; animation: pointsPulse 1.4s ease-in-out infinite alternate; }
@keyframes pointsPulse { to { box-shadow: 0 0 0 3px rgba(179, 38, 30, 0.5), 0 0 22px rgba(255, 120, 60, 0.6); } }
.sc-points b { font: 800 30px/1 var(--f-ui); font-variant-numeric: tabular-nums; } /* Shojumaru drew 0 as a swirl */
.sc-points.no-points { filter: grayscale(0.5); }
.sc-points span { font: 700 10px/1.1 var(--f-ui); text-transform: uppercase; letter-spacing: 0.08em; text-align: center; }
.sc-hint { margin: 10px 0 6px; font: 500 13.5px/1.35 var(--f-ui); color: #5e4a33; }
.sc-list { list-style: none; margin: 0; padding: 0; }
.sj {
  display: grid;
  grid-template-columns: 64px 1fr auto;
  gap: 4px 14px;
  align-items: start;
  padding: 14px 4px 14px 0;
  border-bottom: 2px solid transparent;
  border-image: linear-gradient(90deg, transparent, rgba(32, 23, 14, 0.35) 15%, rgba(32, 23, 14, 0.2) 85%, transparent) 1;
}
.sj:last-child { border-bottom: 0; }
.sj-icon {
  width: 64px; height: 64px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  background: radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.4), transparent 55%), var(--icon-bg, #555);
  box-shadow: 0 0 0 2px rgba(32, 23, 14, 0.75), 0 4px 8px rgba(60, 40, 20, 0.35), inset 0 -8px 14px rgba(0, 0, 0, 0.3);
}
.sj-icon { grid-template-rows: 1fr auto; padding: 6px 2px 4px; gap: 2px; }
.sj-icon span { font: 400 32px/1 var(--f-glyph); color: #fff; } /* shadow: SP.Icons {shadow: true} (see .unit-glyph) */
.sj-icon small { font: 700 10px/1 var(--f-ui); color: #fff; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8); white-space: nowrap; max-width: 100%; overflow: hidden; }
.sj.is-locked .sj-icon { filter: grayscale(0.85) brightness(0.8); opacity: 0.75; }
.sj.is-locked h3 { color: #6b5a44; }
.sj.is-learnable .sj-icon { box-shadow: 0 0 0 2px var(--gold), 0 0 16px rgba(255, 180, 40, 0.7), inset 0 -8px 14px rgba(0, 0, 0, 0.3); }
.sj.is-stamped .sj-icon { animation: stamp 0.45s cubic-bezier(0.3, 1.6, 0.5, 1); }
@keyframes stamp { from { transform: scale(1.6) rotate(-10deg); opacity: 0; } }
.sj-main { min-width: 0; }
.sj-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sj-top h3 { margin: 0; font: 700 19px/1.1 var(--f-ui); color: var(--sumi); }
.sj .pips i { border-color: rgba(32, 23, 14, 0.55); background: rgba(32, 23, 14, 0.12); }
.sj .pips i.is-on { background: linear-gradient(135deg, #e5483c, var(--seal-red)); border-color: #7a140f; box-shadow: none; }
.sj-key { margin-left: auto; font: 700 11px/1 var(--f-ui); color: #5e4a33; padding: 3px 6px; border: 1px solid rgba(32, 23, 14, 0.25); border-radius: 4px; }
.sj-req { font: 600 13px/1.2 var(--f-ui); color: #8a3a1a; margin-top: 3px; }
.sj.is-mastered .sj-req { color: #5a4630; }
.sj-seals { display: flex; flex-wrap: wrap; gap: 4px; margin: 8px 0 6px; }
.sj-seals .seal {
  flex: none;
  height: 22px;
  color: var(--sumi);
  background: rgba(255, 255, 255, 0.35);
  border-color: rgba(32, 23, 14, 0.35);
}
/* Unlock requirements of a locked jutsu, each with a live progress bar. */
.sj-goal { list-style: none; margin: 8px 0 4px; padding: 0; display: flex; flex-direction: column; gap: 5px; max-width: 340px; }
.sj-g { display: flex; flex-direction: column; gap: 3px; }
.sj-g-t { font: 600 13.5px/1.2 var(--f-ui); color: #4a3a28; }
.sj-g-t b { color: #8a2a1a; }
.sj-g.is-met .sj-g-t b { color: #2f6b3a; }
.sj-g.is-met .sj-g-t::before { content: '✓ '; color: #2f6b3a; font-weight: 800; }
.sj-g-bar { display: block; height: 6px; border-radius: 3px; overflow: hidden; background: rgba(32, 23, 14, 0.15); box-shadow: inset 0 0 0 1px rgba(32, 23, 14, 0.2); }
.sj-g-bar i { display: block; height: 100%; transform-origin: 0 50%; background: linear-gradient(90deg, #b3261e, #e0662b); }
.sj-g.is-met .sj-g-bar i { background: linear-gradient(90deg, #2f6b3a, #5aa05a); }
.sj-lock { font: 400 30px/1 var(--f-glyph); color: #6b5a44; }
.sj-seals--none { font: italic 500 12.5px/1.2 var(--f-ui); color: #5e4a33; }
.sj-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; font: 600 13px/1.2 var(--f-ui); color: #3e3020; }
.sj-meta span:empty { display: none; }
.sj-desc { margin: 6px 0 0; font: 500 14.5px/1.38 var(--f-ui); color: var(--sumi); }
.sj-next { margin: 6px 0 0; padding-left: 10px; border-left: 3px solid rgba(179, 38, 30, 0.5); font: 500 13.5px/1.35 var(--f-ui); color: #4a3a28; }
.sj-next b { color: #8a2a1a; }
.sj-act { display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 96px; padding-top: 4px; }
.btn--stamp {
  min-width: 92px;
  min-height: 42px;
  border-radius: 4px;
  border: 2px solid #6e100b;
  background: radial-gradient(circle at 40% 30%, #d4473b, var(--seal-red) 55%, #7d130d);
  color: #fff6e6;
  font: 400 15px/1 var(--f-title);
  letter-spacing: 0.04em;
  transform: rotate(-2deg);
  box-shadow: 0 3px 0 #5a0c08, 0 4px 10px rgba(80, 20, 10, 0.35);
}
.btn--stamp:hover:not(:disabled) { border-color: #ffdf9e; filter: brightness(1.1); }
.btn--stamp:active:not(:disabled) { transform: rotate(-2deg) translateY(2px); box-shadow: 0 1px 0 #5a0c08; }
.btn--stamp:disabled { background: linear-gradient(180deg, #a59a85, #857a64); border-color: #6b604c; color: #f3ead6; box-shadow: none; filter: none; opacity: 0.8; }
.btn--stamp:focus-visible { outline-color: var(--sumi); }
.sj-why { font: 600 12px/1.2 var(--f-ui); color: #8a3a1a; text-align: center; max-width: 110px; }
.sj-why.is-ok { color: #2f6b3a; }
.sj-hanko {
  width: 58px; height: 58px;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 2px;
  font: 400 24px/1 var(--f-glyph);
  color: var(--seal-red);
  border: 3px double var(--seal-red);
  border-radius: 6px;
  transform: rotate(-8deg);
  opacity: 0.85;
}
.sj-hanko small { font: 700 9.5px/1 var(--f-ui); letter-spacing: 0.04em; text-transform: uppercase; }
.sc-foot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(32, 23, 14, 0.25); font: 500 13px/1.3 var(--f-ui); color: #5e4a33; }
.sc-foot .btn--ink { background: rgba(32, 23, 14, 0.85); color: var(--paper); border-color: rgba(32, 23, 14, 0.5); }
.sc-foot .btn.is-armed { background: linear-gradient(180deg, #8a1f1f, #4a0c0c); color: #fff; }

/* ---------------------------------------------------------------- character */
.ch-head { display: grid; grid-template-columns: auto 1fr; gap: 6px 16px; align-items: center; padding-top: 12px; }
.ch-emblem {
  grid-row: span 2;
  width: 84px; height: 84px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font: 400 46px/1 var(--f-glyph);
  color: #fff;
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.3), transparent 55%), var(--accent);
  box-shadow: 0 0 0 3px var(--steel-dk), 0 0 0 5px var(--steel), 0 6px 14px rgba(0, 0, 0, 0.5);
}
.ch-name { font: 400 22px/1.1 var(--f-title); }
.ch-title { font: 600 14px/1.2 var(--f-ui); color: var(--text-dim); }
.ch-level { font: 600 14px/1.2 var(--f-ui); color: var(--text-dim); margin-top: 4px; }
.ch-level b { color: var(--text); font-size: 16px; }
.ch-xp { position: relative; height: 14px; margin-top: 6px; border-radius: 3px; overflow: hidden; background: #0a0818; box-shadow: inset 0 1px 3px #000; }
.ch-xp i { position: absolute; inset: 0; transform-origin: 0 50%; background: linear-gradient(180deg, var(--xp-hi), var(--xp) 50%, var(--xp-dk)); }
.ch-xp span { position: absolute; inset: 0; display: grid; place-items: center; font: 700 11px/1 var(--f-ui); text-shadow: 0 0 2px #000; }
.ch-quote { font: italic 600 15px/1.2 var(--f-ui); color: var(--accent); }
.ch-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 0 22px; }
.ch-cols h3 { margin: 16px 0 6px; font: 700 12px/1 var(--f-ui); text-transform: uppercase; letter-spacing: 0.14em; color: var(--text-dim); }
.st-row { display: flex; justify-content: space-between; gap: 10px; padding: 5px 0; border-bottom: 1px solid var(--ink-line); font-size: 14px; }
.st-row span { color: var(--text-dim); }
.st-row b { font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; }
.ch-buffs { font-size: 13.5px; color: var(--text); }

/* ---------------------------------------------------------------- mission log */
.ql-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.ql {
  padding: 10px 12px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--ink-line);
}
.ql.is-complete { border-color: rgba(121, 226, 143, 0.45); }
.ql-top { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; }
.ql-top h3 { margin: 0; flex: 1; min-width: 0; font: 700 16px/1.2 var(--f-ui); color: var(--quest); }
.ql.is-complete .ql-top h3 { color: var(--good); }
.ql-zone { font: 500 12px/1 var(--f-ui); color: var(--text-faint); }
.ql-obj { margin-top: 6px; font-size: 14px; color: var(--text-dim); }
.ql-obj b { color: var(--text); }
.ql-prog { height: 5px; margin-top: 4px; border-radius: 3px; overflow: hidden; background: rgba(255, 255, 255, 0.08); }
.ql-prog i { display: block; height: 100%; transform-origin: 0 50%; background: linear-gradient(90deg, var(--quest), #ffb347); }
.ql-text { margin: 8px 0 0; font: italic 500 14px/1.4 var(--f-ui); color: #d9d3c2; }
.ql-by { display: block; margin-top: 2px; font-style: normal; color: var(--text-faint); font-size: 12.5px; }
.ql-done { margin: 8px 0 0; color: var(--good); font-size: 14px; }
.ql-reward { margin-top: 6px; font: 600 12.5px/1 var(--f-ui); color: #c7a8ff; }
.ql--small { display: flex; align-items: center; gap: 4px; padding: 7px 10px; font-size: 14px; }
.ql--small b { flex: 1; }
.ql--small span:last-child { color: var(--text-faint); font-size: 12.5px; }
.ql--done { opacity: 0.75; }
.ql-up { margin-top: 12px; }

/* ---------------------------------------------------------------- world map */
.mp-frame { display: grid; place-items: center; padding-top: 12px; }
.mp-wrap {
  position: relative;
  width: min(100%, calc(100vh - 230px));
  min-width: min(280px, 100%);
  aspect-ratio: 1 / 1;
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0 0 0 2px var(--steel-dk), 0 0 0 4px var(--steel-lo), inset 0 0 40px rgba(0, 0, 0, 0.5);
  /* Zone and landmark names are sized in cqw (% of the map's width), so SP.Panels can place them in map units
   * (placeMapLabels: inside the map, clear of each other and of the compass) whatever the map's size. */
  container-type: inline-size;
}
.mp-canvas { display: block; width: 100%; height: 100%; } /* its tone (saturate 0.9, contrast 1.05) is in the image (panels.js toneMap) */
.mp-wrap::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--paper-grain), radial-gradient(ellipse at center, transparent 55%, rgba(20, 12, 4, 0.45));
  mix-blend-mode: multiply;
}
.mp-zone {
  position: absolute;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1.5px dashed rgba(255, 255, 255, 0.35);
}
.mp-zone.zone-safe { border-color: rgba(143, 227, 255, 0.5); }
.mp-zone.zone-danger { border-color: rgba(255, 138, 122, 0.55); }
.mp-zone.is-quest { border-style: solid; border-color: var(--quest); background: rgba(255, 209, 102, 0.12); box-shadow: 0 0 18px rgba(255, 209, 102, 0.3); }
.mp-label {
  position: absolute;
  transform: translate(-50%, -50%);
  text-align: center;
  white-space: nowrap;
  pointer-events: none;
  text-shadow: 0 0 3px #000, 0 1px 2px #000, 0 0 8px rgba(0, 0, 0, 0.8);
  z-index: 1;
}
/* Font sizes and line heights mirror LBL / LBL_LH in js/ui/panels.js (the label's size in % of the map). */
.mp-label b { display: block; font: 400 clamp(9px, 3cqw, 15px)/1.1 var(--f-title); color: #fff6e0; }
.mp-label span { display: block; font: 700 clamp(8px, 2.4cqw, 12px)/1.2 var(--f-ui); }
.mp-npc, .mp-boss {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 20px; height: 20px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font: 800 14px/1 var(--f-ui);
  color: #10140c;
  background: var(--friendly);
  box-shadow: 0 0 0 2px #10140c;
  z-index: 2;
}
.mp-npc.is-avail, .mp-npc.is-turnin { background: var(--gold); box-shadow: 0 0 0 2px #10140c, 0 0 12px rgba(255, 207, 74, 0.8); }
.mp-boss { width: 26px; height: 26px; font: 400 16px/1 var(--f-glyph); color: #fff; background: radial-gradient(circle, #d93a2a, #5a0a06); box-shadow: 0 0 0 2px #000, 0 0 14px rgba(255, 60, 40, 0.7); }
.mp-landmark {
  position: absolute;
  transform: translate(-50%, 2cqw); /* below its point, clear of NPC markers at the gate (LBL.lmDrop in panels.js) */
  font: 600 clamp(8px, 2.2cqw, 11px)/1 var(--f-ui);
  color: #f3ead2;
  text-shadow: 0 0 3px #000, 0 1px 2px #000;
  white-space: nowrap;
  pointer-events: none;
  z-index: 1;
}
.mp-player {
  position: absolute;
  width: 22px; height: 22px;
  transform: translate(-50%, -50%);
  z-index: 3;
  background: #fff6dc;
  clip-path: polygon(50% 0, 92% 100%, 50% 78%, 8% 100%);
  filter: drop-shadow(0 0 2px #000) drop-shadow(0 0 6px var(--accent));
}
.mp-compass {
  position: absolute;
  top: 8px;
  right: 10px;
  width: 30px; height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font: 400 14px/1 var(--f-title);
  color: #2a1a00;
  background: linear-gradient(180deg, var(--brass-hi), var(--brass));
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.6);
  z-index: 3;
}
.mp-legend { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 16px; margin-top: 12px; font-size: 13px; color: var(--text-dim); }
.mp-legend span { display: inline-flex; align-items: center; gap: 6px; }
.lg { display: inline-grid; place-items: center; width: 16px; height: 16px; border-radius: 50%; font: 800 11px/1 var(--f-ui); font-style: normal; }
.lg--you { background: #fff6dc; clip-path: polygon(50% 0, 92% 100%, 50% 78%, 8% 100%); border-radius: 0; }
.lg--q { background: var(--gold); color: #10140c; }
.lg--boss { background: #b3261e; color: #fff; font: 400 11px/1 var(--f-glyph); }
.lg--quest { border: 2px solid var(--quest); background: rgba(255, 209, 102, 0.2); }

/* ---------------------------------------------------------------- help */
.hp-cols { display: grid; grid-template-columns: 1.1fr 1fr; gap: 0 26px; }
.keys { width: 100%; border-collapse: collapse; font-size: 14px; }
.keys td { padding: 5px 0; border-bottom: 1px solid var(--ink-line); vertical-align: middle; }
.keys td:first-child { white-space: nowrap; padding-right: 12px; }
.keys td:last-child { color: var(--text-dim); }
.keys .or { margin: 0 4px; color: var(--text-faint); font-size: 12px; }
.tips { margin: 0; padding-left: 20px; font-size: 14px; color: var(--text-dim); display: flex; flex-direction: column; gap: 6px; }
.tips b { color: var(--text); }
.demo { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* Developer / admin tools, first in Help (docs/INTERFACE_AND_CONTROLS.md "Help"): two columns down to 400 px wide;
 * touch targets reach 44 px through the (pointer: coarse) rules. The Jutsu Scroll stage picker is .sj-dev. */
.hp-tools > summary { margin: 4px 0 8px; min-height: 40px; font-size: 14px; color: var(--text); cursor: pointer; list-style: none; }
.hp-tools > summary::-webkit-details-marker { display: none; }
.hp-tools > summary::before { content: '▸'; font-size: 14px; }
.hp-tools[open] > summary::before { content: '▾'; }
.hp-tools .demo { grid-template-columns: 1fr 1fr; margin-bottom: 8px; }
.dt-lv { display: grid; grid-template-columns: auto repeat(5, 1fr); gap: 6px; align-items: center; margin-bottom: 8px; font-size: 14px; color: var(--text-dim); }
.dt-lv .btn, .dt-ev .btn { padding: 6px 4px; }
.dt-ev { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 6px; margin-bottom: 4px; }
/* The story-flag picker (D1): a flag of the character's chain or the shared story, then Set flag. */
.dt-fl { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 6px; align-items: center; margin-bottom: 8px; font-size: 14px; color: var(--text-dim); }
.dt-fl select { min-width: 0; min-height: 38px; padding: 4px 6px; border-radius: 6px; border: 1px solid var(--ink-line-2); background: var(--ink-2); color: var(--text); font: 600 13.5px/1.2 var(--f-ui); }
.dt-fl option, .dt-fl optgroup { background: var(--ink-1); color: var(--text); }
.sj-dev { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px; font: 700 13px/1 var(--f-ui); color: #3e3020; }
.sj-dev .btn { min-width: 40px; }
.sj-dev b { min-width: 2.2em; text-align: center; }
.sj-dev .segs { flex: 1 1 220px; }
/* While Help shows the tools (#ui.help-tools, SP.Panels syncLayer) the centre messages sit at the bottom, off the tools. */
.help-tools > .center-msgs { top: auto; bottom: calc(var(--gb) + 12px); }
.chk { display: flex; align-items: center; gap: 8px; margin-top: 12px; font-size: 14px; color: var(--text-dim); cursor: pointer; }
.chk input { width: 18px; height: 18px; accent-color: var(--accent); }

/* ---------------------------------------------------------------- dialog */
.dl-head { display: flex; align-items: center; gap: 14px; padding-top: 12px; }
.dl-portrait {
  width: 64px; height: 64px;
  flex: none;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font: 400 34px/1 var(--f-glyph);
  color: #fff;
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.25), transparent 55%), #2d6a3c;
  box-shadow: 0 0 0 3px var(--steel-dk), 0 0 0 5px var(--steel);
}
.dl-name { font: 400 20px/1.1 var(--f-title); color: var(--friendly); }
.dl-title { font: 600 13px/1.2 var(--f-ui); color: var(--text-dim); }
.dl-say {
  margin: 14px 0 12px;
  padding: 12px 14px;
  border-radius: 6px;
  font: 500 15px/1.45 var(--f-ui);
  color: var(--sumi);
  background: var(--paper-grain), linear-gradient(180deg, var(--paper), var(--paper-2));
  box-shadow: inset 0 0 14px rgba(120, 80, 30, 0.25);
  user-select: text;
  -webkit-user-select: text;
}
.dl-opts { display: flex; flex-direction: column; gap: 6px; }
.dl-opt {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 44px;
  padding: 7px 10px;
  border-radius: 6px;
  border: 1px solid var(--ink-line);
  background: rgba(255, 255, 255, 0.04);
  text-align: left;
  cursor: pointer;
}
.dl-opt:hover { border-color: var(--accent); background: rgba(255, 255, 255, 0.08); }
.dl-mark {
  flex: none;
  width: 30px; height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font: 800 18px/1 var(--f-ui);
  color: #10140c;
  background: #8a8f9e;
}
.dl-mark.is-avail, .dl-mark.is-turnin { background: var(--gold); box-shadow: 0 0 10px rgba(255, 207, 74, 0.6); }
.dl-mark.is-active { background: #6b7080; color: #e6e6e6; }
.dl-opt--ramen .dl-mark { background: #c9772b; color: #fff; font: 400 18px/1 var(--f-glyph); }
.dl-opt--bye .dl-mark { background: transparent; color: var(--text-faint); box-shadow: inset 0 0 0 1px var(--ink-line-2); }
.dl-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dl-txt b { font: 700 15px/1.2 var(--f-ui); color: var(--text); }
.dl-txt small { font: 500 12.5px/1.25 var(--f-ui); color: var(--text-dim); }
.dl-quest { padding-top: 12px; }
.dl-quest .ql-top h3 { font-size: 18px; }
.dl-objective { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--ink-line); font-size: 14px; }
.dl-objective span { color: var(--text-dim); }
.dl-btns { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.dl-btns .btn { flex: 1; }

/* ---------------------------------------------------------------- game menu */
.gm { display: flex; flex-direction: column; gap: 8px; padding-top: 14px; }
.gm-vol { display: flex; flex-direction: column; gap: 6px; padding: 8px 2px; font-size: 14px; color: var(--text-dim); }
.gm-vol span { display: flex; justify-content: space-between; }
.gm-vol b { color: var(--text); font-variant-numeric: tabular-nums; }
.gm-vol input { width: 100%; accent-color: var(--accent); height: 28px; cursor: pointer; }
.gm-note { margin: 4px 0 0; text-align: center; font-size: 12.5px; }

/* ---------------------------------------------------------------- defeat */
.death-screen {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: auto;
  background: radial-gradient(ellipse at center, rgba(30, 4, 6, 0.72), rgba(12, 2, 4, 0.9));
  animation: deathIn 1s ease;
  outline: none;
}
@keyframes deathIn { from { opacity: 0; } }
.ds-inner {
  position: relative;
  width: min(520px, calc(100vw - 32px));
  text-align: center;
  padding: 24px;
}
.ds-emblem {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -58%);
  font: 400 min(280px, 56vw)/1 var(--f-glyph);
  color: rgba(179, 38, 30, 0.2);
  pointer-events: none;
  z-index: -1;
}
.ds-title { margin: 0; font: 400 clamp(30px, 6vw, 48px)/1.1 var(--f-title); color: #ffdcd2; text-shadow: 0 0 22px rgba(255, 40, 20, 0.5), 0 2px 0 #000; }
.ds-by { margin: 10px 0 0; font-size: 16px; color: var(--text-dim); }
.ds-by b { color: var(--hostile); }
.ds-tip { margin: 16px auto 22px; max-width: 420px; font: 500 15px/1.45 var(--f-ui); color: var(--text); }
.ds-note { margin: 12px 0 0; font-size: 13px; color: var(--text-faint); }

/* ============================================================== title screen */
.title-screen {
  position: absolute;
  inset: 0;
  z-index: 50;
  display: flex;
  pointer-events: none;
}
.ts-panel {
  position: relative;
  width: min(480px, 100%);
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: calc(var(--gt) + 18px) 34px calc(var(--gb) + 18px) calc(var(--gl) + 12px);
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  background:
    var(--grain),
    linear-gradient(90deg, rgba(10, 12, 26, 0.95) 0%, rgba(10, 12, 26, 0.9) 72%, rgba(10, 12, 26, 0.55) 92%, rgba(10, 12, 26, 0) 100%);
  scrollbar-width: none;
  animation: tsIn 0.5s ease-out;
}
.ts-panel::-webkit-scrollbar { display: none; }
@keyframes tsIn { from { opacity: 0; transform: translateX(-16px); } }
.ts-head { position: relative; padding-top: 6px; }
.ts-kicker { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font: 700 12px/1 var(--f-ui); letter-spacing: 0.22em; text-transform: uppercase; color: var(--accent); }
.ts-kicker-ico { font-size: 20px; line-height: 0; }
.ts-title {
  position: relative;
  z-index: 1;
  margin: 0;
  font: 400 clamp(40px, 9vw, 60px)/1 var(--f-title);
  color: #fff6e0;
  letter-spacing: 0.01em;
  text-shadow: 0 3px 0 rgba(0, 0, 0, 0.6), 0 0 30px rgba(0, 0, 0, 0.6);
}
.ts-brush { display: block; width: min(380px, 92%); height: 30px; margin: -8px 0 0 -4px; fill: var(--accent); stroke: var(--accent); opacity: 0.95; transition: fill 0.3s ease; }
.ts-brush-dry { stroke: var(--accent); opacity: 0.6; }
.ts-note { margin: 6px 0 0; font: 600 13px/1.2 var(--f-ui); letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-faint); }
.ts-continue { display: block; }
.ts-cont {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 8px;
  border: 1px solid var(--cc);
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.02));
  cursor: pointer;
  text-align: left;
}
.ts-cont:hover { background: linear-gradient(90deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.04)); }
.ts-cont-emblem { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; font: 400 22px/1 var(--f-glyph); color: #fff; background: var(--cc); flex: none; }
.ts-cont-txt { flex: 1; display: flex; flex-direction: column; }
.ts-cont-txt b { font: 400 17px/1.1 var(--f-title); }
.ts-cont-txt span { font: 600 13px/1.2 var(--f-ui); color: var(--text-dim); }
.ts-cont-go { font: 700 22px/1 var(--f-ui); color: var(--cc); }
.ts-h { margin: 6px 0 0; font: 700 12px/1 var(--f-ui); text-transform: uppercase; letter-spacing: 0.16em; color: var(--text-dim); }
.ts-cards { display: flex; flex-direction: column; gap: 8px; }
.cc {
  --cc: var(--accent);
  position: relative;
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 4px 14px;
  width: 100%;
  padding: 10px 12px;
  border-radius: 8px;
  border: 2px solid transparent;
  background: linear-gradient(180deg, rgba(30, 36, 72, 0.9), rgba(14, 17, 36, 0.9)) padding-box, linear-gradient(180deg, var(--steel-lo), var(--steel-dk)) border-box;
  text-align: left;
  cursor: pointer;
  transition: transform 0.12s ease, filter 0.15s ease;
}
.cc:hover { filter: brightness(1.12); }
.cc.is-selected {
  background: radial-gradient(120% 120% at 0% 0%, rgba(255, 255, 255, 0.08), transparent 55%) padding-box, linear-gradient(180deg, rgba(34, 40, 80, 0.96), rgba(14, 17, 36, 0.96)) padding-box, linear-gradient(180deg, var(--cc), var(--steel-dk)) border-box;
  box-shadow: 0 0 22px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.06);
}
.cc-emblem {
  width: 56px; height: 56px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font: 400 30px/1 var(--f-glyph);
  color: #fff;
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.3), transparent 55%), var(--cc);
  box-shadow: 0 0 0 2px var(--steel-dk), 0 0 0 3px var(--steel-lo);
  transition: transform 0.2s ease;
}
.cc.is-selected .cc-emblem { transform: scale(1.06); box-shadow: 0 0 0 2px var(--steel-dk), 0 0 0 3px var(--steel), 0 0 18px var(--cc); }
.cc-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; justify-content: center; }
.cc-name { font: 400 18px/1.15 var(--f-title); color: var(--text); }
.cc-title { font: 600 13px/1.2 var(--f-ui); color: var(--text-dim); }
.cc-meta { display: flex; align-items: center; gap: 10px; margin-top: 3px; }
.cc-role { font: 700 11px/1 var(--f-ui); text-transform: uppercase; letter-spacing: 0.1em; color: var(--cc); padding: 3px 6px; border-radius: 3px; background: rgba(255, 255, 255, 0.06); }
.cc-diff { display: inline-flex; align-items: center; gap: 3px; }
.cc-diff-l { font: 600 11px/1 var(--f-ui); color: var(--text-faint); margin-right: 3px; }
.cc-diff i { width: 7px; height: 7px; transform: rotate(45deg); background: rgba(255, 255, 255, 0.12); }
.cc-diff i.is-on { background: var(--cc); }
.cc-detail { grid-column: 1 / -1; display: none; flex-direction: column; gap: 8px; padding-top: 6px; }
.cc.is-selected .cc-detail { display: flex; animation: detailIn 0.25s ease; }
@keyframes detailIn { from { opacity: 0; transform: translateY(-4px); } }
.cc-blurb { font: 500 14.5px/1.4 var(--f-ui); color: var(--text); }
.cc-jutsu { display: grid; grid-template-columns: 1fr 1fr; gap: 5px 10px; }
.cj { display: flex; align-items: flex-start; gap: 7px; min-width: 0; }
.cj-glyph {
  width: 24px; height: 24px;
  flex: none;
  border-radius: 4px;
  display: grid;
  place-items: center;
  font: 400 16px/1 var(--f-glyph);
  color: #fff;
  background: radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.35), transparent 55%), var(--icon-bg, #444);
}
.cj-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.cj-name { min-width: 0; font: 600 13px/1.15 var(--f-ui); color: var(--text); white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.cj-lv { font: 600 11px/1.2 var(--f-ui); color: var(--text-faint); font-variant-numeric: tabular-nums; }
.cj-lv.is-starter { color: var(--good); }
.cc-quote { font: italic 700 14px/1 var(--f-ui); color: var(--cc); }
.ts-start, .ts-go { width: 100%; font-family: var(--f-title); font-weight: 400; letter-spacing: 0.04em; }
/* Armed "Begin" (a save would be replaced): a red seal look until the second click. */
.ts-start.is-warn { background: linear-gradient(180deg, #d9473a, #7a160e); border-color: #ffb0a6; font-family: var(--f-ui); font-weight: 700; letter-spacing: 0; }
.ts-hint { margin: 2px 0 0; font: 500 13px/1.8 var(--f-ui); color: var(--text-dim); }
.ts-hint kbd { font-size: 11px; }
.ts-hint-keys { display: block; color: var(--text-faint); line-height: 1.3; }
.ts-legal { margin: auto 0 0; padding-top: 8px; font: 500 11.5px/1.35 var(--f-ui); color: var(--text-faint); }

/* Online co-op entry + join form on the title screen (SP.Menu E22, docs/MULTIPLAYER.md §10.2). */
.ts-online { display: flex; flex-direction: column; gap: 6px; }
.ts-online-btn { --cc: #4fa3d9; }
.ts-online-emblem { font-size: 20px; }
.ol-box {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: linear-gradient(180deg, rgba(30, 36, 72, 0.9), rgba(14, 17, 36, 0.9));
}
.ol-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.ol-head b { font: 400 17px/1.1 var(--f-title); }
.ol-head span { font: 600 13px/1.2 var(--f-ui); color: var(--text-dim); }
.ol-form { display: flex; flex-direction: column; gap: 10px; margin: 0; }
.ol-field { display: flex; flex-direction: column; gap: 4px; }
.ol-field span { font: 700 11px/1 var(--f-ui); text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-dim); }
.ol-field input {
  width: 100%;
  min-height: 40px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--steel-lo);
  background: rgba(8, 10, 22, 0.85);
  color: var(--text);
  font: 600 16px/1.2 var(--f-ui);
}
.ol-field input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.ol-btns { display: flex; gap: 8px; }
.ol-btns .btn--primary { flex: 1; }
.ol-status { margin: 0; min-height: 0; font: 500 13px/1.3 var(--f-ui); color: var(--text-dim); }
.ol-status:empty { display: none; }
.ol-error { margin: 0; font: 600 13.5px/1.35 var(--f-ui); color: var(--error); }
.ol-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px 8px 12px;
  border-radius: 8px;
  border: 1px solid rgba(121, 226, 143, 0.35);
  background: linear-gradient(90deg, rgba(121, 226, 143, 0.1), rgba(255, 255, 255, 0.02));
}
.ol-dot { width: 9px; height: 9px; flex: none; border-radius: 50%; background: var(--good); box-shadow: 0 0 8px var(--good); }
.ol-who { flex: 1; min-width: 0; font: 600 14px/1.25 var(--f-ui); color: var(--text-dim); overflow-wrap: anywhere; }
.ol-who b { color: var(--text); }
.ol-leave { flex: none; min-height: 34px; padding: 4px 12px; }
/* Login key (docs/MULTIPLAYER.md §12.3): a <details> under the online form's lines and in the Game Menu online. The
 * summary is the "Login key" entry; a BAD_SECRET opens it with .is-ask, which hides Copy (this browser's own key is
 * not the one wanted then) and says where a key comes from (CSS text: no game JS). One masked field takes a pasted key
 * and, when the clipboard fails, shows this key. */
.lk { font: 500 13px/1.35 var(--f-ui); color: var(--text-dim); }
.lk > summary { width: max-content; max-width: 100%; min-height: 32px; padding: 6px 2px; cursor: pointer; list-style: none; font: 700 13px/20px var(--f-ui); letter-spacing: 0.04em; color: var(--text); }
.lk > summary::-webkit-details-marker { display: none; }
.lk > summary::before { content: '▸ '; color: var(--text-dim); }
.lk[open] > summary::before { content: '▾ '; }
.lk p { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0 0; }
.lk.is-ask .lk-c { display: none; }
.lk.is-ask .lk-out::before { content: 'Copy it in the browser where this name works (Game Menu \203A  Login key), then paste it here.'; flex: 1 1 100%; }
.lk input {
  flex: 1 1 100%;
  min-width: 0;
  min-height: 40px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--steel-lo);
  background: rgba(8, 10, 22, 0.85);
  color: var(--text);
  font: 600 16px/1.2 var(--f-ui);   /* 16 px: phones do not zoom into the field */
}
.lk input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.lk button { min-height: 36px; padding: 6px 12px; font-size: 14px; }
.lk [data-act="key-use"] { margin-left: auto; border-color: rgba(255, 255, 255, 0.25); color: #fff; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 92%, white), color-mix(in srgb, var(--accent) 55%, black)); }
.lk-msg { font-weight: 700; color: var(--text); }
.lk-msg:empty { margin: 0; }
.cc-meta { flex-wrap: wrap; row-gap: 4px; }
.cc-online[hidden] { display: none; }
.cc-online { font: 700 11px/1 var(--f-ui); letter-spacing: 0.04em; color: var(--text-faint); padding: 3px 6px; border-radius: 3px; background: rgba(255, 255, 255, 0.06); }
.cc-online.is-saved { color: var(--good); }

/* ============================================================== medium screens */
@media (max-width: 1180px) {
  .chat { width: 320px; height: 150px; bottom: calc(var(--gb) + 128px); }
}
@media (max-width: 900px) {
  :root { --slot: 50px; --slot-gap: 5px; }
  .unit { width: 250px; }
  .unit--target { left: calc(var(--gl) + 262px); }
  .buffbar { right: calc(var(--gr) + 190px); max-width: 200px; }
  .hud-right { width: 172px; }
  .mm-rim { width: 140px; height: 140px; }
  .micro-btn { width: 38px; height: 40px; }
  .chat { width: 280px; height: 132px; }
  .hp-cols, .ch-cols { grid-template-columns: 1fr; }
}

/* ============================================================== phones (≈400 px) */
@media (max-width: 640px) {
  /* Six slots plus the bar's padding and border fill the width inside the 16 px gutters (55 px at 400 px wide). */
  :root { --slot: clamp(46px, calc((100vw - 32px - 34px) / 6), 55px); --slot-gap: 4px; }
  html, body { font-size: 14px; }
  .unit { width: min(212px, calc(100vw - 150px)); padding: 5px 7px; gap: 7px; }
  .unit-portrait { width: 42px; height: 42px; }
  .unit-glyph { font-size: 23px; }
  .unit-nm { font-size: 13.5px; }
  .unit-sub, .unit-range { font-size: 11px; }
  .bar { height: 13px; }
  .bar--chakra { height: 8px; }
  .bar--chakra .bar-txt { display: none; }
  .bar-txt, .bar-pct { font-size: 10.5px; }
  .unit--target { left: var(--gl); top: calc(var(--gt) + 64px); }
  .unit--target .castbar { top: calc(100% + 6px); }
  .hud-right { width: 120px; gap: 6px; }
  .mm-rim { width: 100px; height: 100px; padding: 4px; }
  .mm-zoom { width: 22px; height: 22px; font-size: 13px; }
  .minimap { gap: 6px; }   /* FX U3: the corner buttons hang 6 px below the 100 px rim */
  .mm-zone { font-size: 11.5px; }
  .mm-coords { font-size: 10px; }
  /* Phones: the right column (minimap + tracker) has no room for buffs; they sit just above the chat log. */
  .buffbar { top: auto; bottom: calc(var(--gb) + 318px); left: var(--gl); right: auto; flex-direction: row; max-width: min(240px, calc(100vw - 32px)); }
  .buff { width: 30px; height: 30px; }
  .bf-glyph { font-size: 16px; }
  .tr-head { padding: 6px 8px; gap: 5px; }
  .tr-title { font-size: 10.5px; letter-spacing: 0; }
  .tr-caret { display: none; }
  .unit--player .unit-sub { display: none; } /* the role is on the Character panel; the name needs the room */
  .hud-bottom { bottom: calc(var(--gb) + 50px); gap: 6px; }
  .bar-row { gap: 6px; }
  .actionbar { padding: 5px; }
  .slot-glyph { font-size: 22px; }
  /* Labels scale with the slot so the longest one ("Wind Rasen") still fits: 10 px at 400 px wide. */
  .slot-short { left: 1px; right: 1px; font-size: clamp(8.5px, calc((var(--slot) - 8px) / 4.7), 10px); letter-spacing: 0; }
  .slot-cdtxt { font-size: 17px; }
  .castbar--player { width: min(320px, calc(100vw - 32px)); }
  .castbar--player .seal { height: 22px; padding: 0 6px; font-size: 10px; }
  .micro { left: 50%; right: auto; transform: translateX(-50%); padding: 3px; gap: 4px; background: rgba(10, 12, 26, 0.7); }
  .micro-btn { width: 44px; height: 38px; }
  .micro-glyph { font-size: 16px; }
  .micro-key { display: none; }
  .saved-chip { bottom: auto; top: calc(var(--gt) + 4px); right: auto; left: 50%; transform: translateX(-50%); }
  .chat { left: var(--gl); bottom: calc(var(--gb) + 236px); width: min(270px, calc(100vw - 32px)); height: auto; }
  .chat-tabs { display: none; }
  .chat-lines { max-height: calc(3 * 1.35em + 12px); font-size: 12.5px; padding: 5px 8px; }
  .center-msgs { top: calc(var(--gt) + 250px); }
  .cmsg { font-size: 15px; }
  .banner { top: 22%; }              /* M1: phones keep the banners above the row of Sprint / Sub / Charge / Target (about 48% down) */
  .banner--level { top: 33%; }
  .bn-sub { font-size: 14px; }
  .np-bar { width: 62px; }
  .plate.is-boss .np-bar { width: 96px; }
  .ft { font-size: 18px; }
  .ft.is-crit { font-size: 26px; }
  .panel { max-height: calc(100% - var(--gt) - var(--gb) - 12px); }
  .panel--dialog { left: 50%; transform: translate(-50%, -50%); }
  @keyframes dialogIn { from { opacity: 0; } }
  .pn-body { padding: 4px 12px 14px; }
  .scroll-paper { padding: 14px 14px; }
  .sc-emblem { display: none; }
  .sc-titles h2 { font-size: 23px; }
  .sc-points { width: 62px; height: 62px; }
  .sc-points b { font-size: 24px; }
  .sj { grid-template-columns: 48px 1fr; }
  .sj-icon { width: 48px; height: 48px; }
  .sj-icon span { font-size: 24px; }
  .sj-icon small { display: none; }
  .sj-act { grid-column: 2; flex-direction: row; justify-content: flex-start; align-items: center; min-width: 0; }
  .sj-why { text-align: left; max-width: none; }
  .demo { grid-template-columns: 1fr; }
  .ts-panel { width: 100%; padding-right: calc(var(--gr) + 4px); background: var(--grain), linear-gradient(180deg, rgba(10, 12, 26, 0.92), rgba(10, 12, 26, 0.84) 60%, rgba(10, 12, 26, 0.93)); }
  .cc-jutsu { grid-template-columns: 1fr; }
}

/* ============================================================== touch controls (built by SP.Input.createTouchControls on coarse pointers)
 * The stick bottom-left; Jump, Target, Sprint (latch), Sub and Charge (hold) bottom-right. The HUD moves them with
 * --wf-touch-stick-left / --wf-touch-stick-bottom / --wf-touch-btn-right / --wf-touch-jump-bottom (was a style tag that
 * js/engine/input.js injected; S3 moved it here, next to the HUD rules that must stay clear of these buttons). */
.wf-touch-controls { position: fixed; inset: 0; pointer-events: none; z-index: 30; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.wf-touch-stick {
  position: absolute; width: 120px; height: 120px; border-radius: 50%; pointer-events: auto; touch-action: none;
  left: calc(var(--wf-touch-stick-left, 18px) + env(safe-area-inset-left, 0px));
  bottom: calc(var(--wf-touch-stick-bottom, 22px) + env(safe-area-inset-bottom, 0px));
  background: radial-gradient(circle at 50% 50%, rgba(20, 24, 48, 0.25) 0, rgba(20, 24, 48, 0.42) 62%, rgba(150, 170, 210, 0.3) 63%, rgba(20, 24, 48, 0.38) 70%);
  border: 2px solid rgba(190, 205, 235, 0.38); box-shadow: 0 2px 14px rgba(0, 0, 0, 0.35), inset 0 0 18px rgba(0, 0, 0, 0.35); opacity: 0.72; transition: opacity 0.15s;
}
.wf-touch-stick::before { content: ""; position: absolute; inset: -14px; border-radius: 50%; }
.wf-touch-stick.wf-touch-active { opacity: 1; }
.wf-touch-knob {
  position: absolute; left: 50%; top: 50%; width: 52px; height: 52px; margin: -26px 0 0 -26px; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle at 38% 34%, #f6f2e6 0, #c9ccd6 38%, #7d8597 100%); border: 2px solid rgba(40, 44, 70, 0.7);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45); will-change: transform;
}
.wf-touch-knob::after { content: ""; position: absolute; inset: 14px; border-radius: 50%; border: 2px solid rgba(40, 44, 70, 0.45); }
.wf-touch-btn {
  position: absolute; width: 64px; height: 64px; border-radius: 50%; pointer-events: auto; touch-action: none; padding: 0; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px; color: #f3ead2;
  background: radial-gradient(circle at 40% 30%, rgba(70, 78, 120, 0.85), rgba(20, 24, 48, 0.88)); border: 2px solid rgba(190, 205, 235, 0.5);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4); font-family: "Barlow Semi Condensed", system-ui, sans-serif; -webkit-tap-highlight-color: transparent;
}
.wf-touch-btn.wf-touch-pressed { transform: scale(0.92); border-color: rgba(255, 190, 110, 0.9); }
.wf-touch-glyph { font-size: 24px; line-height: 0; }
.wf-touch-glyph svg { display: block; }
.wf-touch-label { font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; opacity: 0.85; }
/* M1 layout (docs/INTERFACE_AND_CONTROLS.md "Phones"): Punch and Kick, the two big round buttons, in the bottom-right corner; on
 * phones the jutsu bar fans out around them on two arcs (below); Jump above the arcs, then a row of Sprint (latch), Sub, Charge
 * (hold) and Target. Everything is measured from the corner; the HUD moves it with --wf-touch-btn-right, --wf-touch-strike-bottom
 * (Kick's bottom edge) and --wf-touch-jump-bottom (Jump's bottom edge). Punch and Kick are held: a finger kept down keeps striking. */
.wf-touch-punch, .wf-touch-kick { width: 76px; height: 76px; }
.wf-touch-punch .wf-touch-glyph, .wf-touch-kick .wf-touch-glyph { font-size: 31px; }
.wf-touch-punch { right: calc(var(--wf-touch-btn-right, 14px) + env(safe-area-inset-right, 0px)); bottom: calc(var(--wf-touch-strike-bottom, 66px) + 60px + env(safe-area-inset-bottom, 0px)); }
.wf-touch-kick { right: calc(var(--wf-touch-btn-right, 14px) + 82px + env(safe-area-inset-right, 0px)); bottom: calc(var(--wf-touch-strike-bottom, 66px) + env(safe-area-inset-bottom, 0px)); }
.wf-touch-punch.wf-touch-pressed, .wf-touch-kick.wf-touch-pressed { background: radial-gradient(circle at 40% 30%, rgba(190, 120, 60, 0.9), rgba(60, 30, 20, 0.92)); }
/* M2: Guard, the third round button (hold to block), 60 px: the corner pocket under Punch (box to box 2 px clear) and above the menu buttons. */
.wf-touch-guard { width: 60px; height: 60px; right: calc(var(--wf-touch-btn-right, 14px) + 8px + env(safe-area-inset-right, 0px)); bottom: calc(var(--wf-touch-strike-bottom, 66px) - 2px + env(safe-area-inset-bottom, 0px)); }
.wf-touch-guard .wf-touch-glyph { font-size: 27px; }
.wf-touch-guard.wf-touch-pressed { background: radial-gradient(circle at 40% 30%, rgba(96, 150, 214, 0.92), rgba(22, 36, 72, 0.94)); }
.wf-touch-jump { right: calc(var(--wf-touch-btn-right, 14px) + env(safe-area-inset-right, 0px)); bottom: calc(var(--wf-touch-jump-bottom, 296px) + env(safe-area-inset-bottom, 0px)); }
.wf-touch-sprint, .wf-touch-sub, .wf-touch-charge, .wf-touch-tab { width: 46px; height: 46px; bottom: calc(var(--wf-touch-jump-bottom, 296px) + 74px + env(safe-area-inset-bottom, 0px)); }
.wf-touch-sprint .wf-touch-glyph, .wf-touch-sub .wf-touch-glyph, .wf-touch-charge .wf-touch-glyph, .wf-touch-tab .wf-touch-glyph { font-size: 17px; }
.wf-touch-sprint .wf-touch-label, .wf-touch-sub .wf-touch-label, .wf-touch-charge .wf-touch-label, .wf-touch-tab .wf-touch-label { font-size: 9px; }
.wf-touch-sprint { right: calc(var(--wf-touch-btn-right, 14px) + 2px + env(safe-area-inset-right, 0px)); }
.wf-touch-sub { right: calc(var(--wf-touch-btn-right, 14px) + 54px + env(safe-area-inset-right, 0px)); }
.wf-touch-charge { right: calc(var(--wf-touch-btn-right, 14px) + 106px + env(safe-area-inset-right, 0px)); }
.wf-touch-tab { right: calc(var(--wf-touch-btn-right, 14px) + 158px + env(safe-area-inset-right, 0px)); }
.wf-touch-btn.wf-touch-on { border-color: rgba(120, 200, 255, 0.95); box-shadow: 0 0 14px rgba(120, 200, 255, 0.55); }

/* ============================================================== touch screens: joystick bottom-left, Punch and Kick bottom-right, jutsu on two arcs
 * M1 (docs/INTERFACE_AND_CONTROLS.md "Phones"). Keys 1-5 fan out on the inner arc (radius --tc-r1, from straight up to straight
 * left around the corner point --tc-cx / --tc-cy), keys 6-8 on the outer arc (--tc-r2), all around Punch and Kick; pure CSS: the
 * slots are placed with sin() and cos() of their angle. The left column (target frame, buffs, chat, clone bar, duel chip) stacks
 * from the top, the right column ends above the cluster, so nothing of either meets a button. The 400 x 800 check in
 * server/e2e/offline-smoke.spec.js measures all of it. */
@media (pointer: coarse) and (max-width: 940px) {
  .hud.is-touch {
    --slot: 50px;
    --wf-touch-stick-bottom: 84px; /* the stick's hit ring must clear the menu buttons below it */
    --tc-cx: 80px; --tc-cy: 118px; --tc-r1: 138px; --tc-r2: 198px;
  }
  .hud.is-touch .hud-bottom { left: auto; right: var(--gr); transform: none; bottom: calc(var(--gb) + 50px); align-items: flex-end; }
  .hud.is-touch .actionbar { position: fixed; right: 0; bottom: 0; width: 0; height: 0; padding: 0; border: 0; background: none; box-shadow: none; display: block; }
  .hud.is-touch .actionbar::before { display: none; }
  .hud.is-touch .actionbar > :is(.slot, .jlk) {
    --tc-r: var(--tc-r1);
    --tc-a: 0deg;
    position: absolute;
    right: calc(var(--tc-cx) + var(--tc-r) * sin(var(--tc-a)) - var(--slot) / 2 + env(safe-area-inset-right, 0px));
    bottom: calc(var(--tc-cy) + var(--tc-r) * cos(var(--tc-a)) - var(--slot) / 2 + env(safe-area-inset-bottom, 0px));
  }
  /* The angles are not even: square slots on an even arc overlap at its diagonal (neighbours must clear each other by a slot's
   * side along x or y), so the fan spreads out where the arc runs at 45 degrees. PH2: the spot a jutsu took when it opened (data-spot,
   * js/ui/hud.js spot: the inner arc from the top, then the outer arc), not its key; the lock button takes the next free spot. */
  .hud.is-touch .actionbar > [data-spot="0"] { --tc-a: 0deg; }
  .hud.is-touch .actionbar > [data-spot="1"] { --tc-a: 22deg; }
  .hud.is-touch .actionbar > [data-spot="2"] { --tc-a: 49deg; }
  .hud.is-touch .actionbar > [data-spot="3"] { --tc-a: 74deg; }
  .hud.is-touch .actionbar > [data-spot="4"] { --tc-a: 102deg; }
  .hud.is-touch .actionbar > [data-spot="5"] { --tc-r: var(--tc-r2); --tc-a: 8deg; }
  .hud.is-touch .actionbar > [data-spot="6"] { --tc-r: var(--tc-r2); --tc-a: 34deg; }
  .hud.is-touch .actionbar > [data-spot="7"] { --tc-r: var(--tc-r2); --tc-a: 60deg; }
  /* The XP strip: across the screen just above the menu buttons, under Kick. */
  .hud.is-touch .xpbar { position: fixed; left: var(--gl); right: var(--gr); bottom: calc(var(--gb) + 46px); width: auto; height: 6px; }
  /* Cast bar and prompt sit above the cluster's top row. */
  .hud.is-touch .castbar--player { position: fixed; left: 50%; bottom: 58%; transform: translateX(-50%); }
  .hud.is-touch .interact-prompt { position: fixed; left: 50%; bottom: 52%; transform: translateX(-50%); min-height: 44px; font-size: 15px; }
  .hud.is-touch .micro { left: 50%; right: auto; transform: translateX(-50%); }
  .hud.is-touch .micro-key { display: none; }
  .hud.is-touch .interact-prompt kbd { display: none; }
  .hud.is-touch .slot-key { display: none; }
  .hud.is-touch .hit-counter { left: var(--gl); top: calc(var(--gt) + 392px); align-items: flex-start; }
}
/* Phones: the left column stacks from the top under the target frame: buffs, the chat log, the clone bar, the duel chip.
 * The right column (minimap, story line, at most 2 event chips + "+N more", tracker) ends above Jump and the row of
 * Sprint / Sub / Charge / Target (S3 PERFUX-2, PERFUX-5; M1 moved the cluster up). */
@media (pointer: coarse) and (max-width: 640px) {
  .hud.is-touch .buffbar { top: calc(var(--gt) + 130px); bottom: auto; left: var(--gl); right: auto; max-width: min(234px, calc(100vw - var(--gl) - 150px)); }
  .hud.is-touch .chat { top: calc(var(--gt) + 166px); bottom: auto; left: var(--gl); width: min(234px, calc(100vw - var(--gl) - 150px)); max-width: none; }
  .hud.is-touch .clonebar, .hud.is-touch .duel-chip { position: fixed; left: var(--gl); right: auto; top: calc(var(--gt) + 242px); bottom: auto; max-width: min(234px, calc(100vw - var(--gl) - 150px)); }
  .hud.is-touch .clonebar { justify-content: flex-start; }
  .hud.is-touch .dc-txt { white-space: normal; }
  .hud.is-touch .hud-bottom:has(> .clonebar:not(.is-hidden)) > .duel-chip { top: calc(var(--gt) + 372px); }
  .hud.is-touch .hud-right { max-height: calc(100% - var(--gt) - 440px); overflow: hidden; }   /* it clips what hangs outside its 120 px: the zoom buttons stay inside the rim's 10 px margin (FX U3) */
  .hud.is-touch .center-msgs { top: calc(var(--gt) + 380px); }
}
@media (pointer: coarse) and (max-width: 940px) and (min-width: 641px) {
  .hud.is-touch .clonebar, .hud.is-touch .duel-chip { position: fixed; left: var(--gl); right: auto; bottom: calc(var(--gb) + 280px); max-width: calc(100vw - var(--gl) - 160px); }
  .hud.is-touch .clonebar { justify-content: flex-start; }
  .hud.is-touch .dc-txt { white-space: normal; }
  .hud.is-touch .hud-bottom:has(> .clonebar:not(.is-hidden)) > .duel-chip { bottom: calc(var(--gb) + 440px); }
  .hud.is-touch .chat { bottom: calc(var(--gb) + 210px); width: calc(100vw - var(--gl) - 210px); max-width: 300px; }
}
@media (pointer: coarse) and (max-height: 520px) {
  .hud.is-touch .chat { display: none; }
  .hud.is-touch .buffbar { top: var(--gt); bottom: auto; left: auto; right: calc(var(--gr) + 130px); flex-direction: row-reverse; }
  .hud.is-touch { --tc-cy: 96px; --tc-r1: 124px; --tc-r2: 180px; --wf-touch-jump-bottom: 250px; }
  /* FX U3: a landscape touch phone (844 x 390) draws the jutsu slots over the minimap's lower left, so the "-" button would sit under a slot in
   * the lower-left corner: there the pair keeps its old stack at the rim's right edge (the arrows on that side are covered by Jump and the slots
   * anyway; docs/INTERFACE_AND_CONTROLS.md "The minimap"). */
  .hud.is-touch .mm-zoom { top: auto; left: auto; right: -2px; }
  .hud.is-touch .mm-zoom--in { bottom: 26px; }
  .hud.is-touch .mm-zoom--out { bottom: 0; }
}

/* ============================================================== motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .banner.is-playing { animation: none !important; opacity: 1; transform: translateX(-50%); }
  .saved-chip.is-pop, .saved-chip.is-pop-b { animation: none; } /* else the 0.001 ms animation ends at opacity 0; the JS timer hides it */
  .seal.is-active { transform: none; }
}

/* ============================================================== game menu: graphics quality */
.gm-q { display: flex; flex-direction: column; gap: 6px; padding: 4px 2px 8px; font-size: 14px; color: var(--text-dim); }
.gm-q > span { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.gm-q small { font: 600 12px/1.2 var(--f-ui); color: var(--text-faint); }
.segs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.seg {
  min-height: 36px;
  border-radius: 6px;
  border: 1px solid var(--ink-line-2);
  background: linear-gradient(180deg, #232842, #13162a);
  color: var(--text-dim);
  font: 700 14px/1 var(--f-ui);
  cursor: pointer;
  pointer-events: auto;
}
.seg:hover { border-color: var(--accent); color: var(--text); }
.seg.is-on { background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 80%, white), color-mix(in srgb, var(--accent) 50%, black)); color: #fff; border-color: rgba(255, 255, 255, 0.3); }
.seg:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hp-touch { margin: 0 0 10px; padding: 10px 12px; border-radius: 8px; background: rgba(255, 255, 255, 0.05); font: 500 14px/1.45 var(--f-ui); color: var(--text); }

/* ============================================================== touch targets (≥ 44 px) */
@media (pointer: coarse) {
  .micro-btn { height: 44px; }
  .pn-x { width: 44px; height: 44px; }
  .btn, .lk button { min-height: 44px; }
  .lk > summary { padding: 12px 2px; }   /* 44 px, the text centred */
  .seg { min-height: 44px; }
  .tr-head { min-height: 44px; }
  .mm-zoom::before { content: ''; position: absolute; inset: calc((100% - 44px) / 2); } /* bigger hit area, same look: 44 px at any size of the button (24 px: 10 each way; the phone's 22 px: 11; FX U3) */
}

/* ============================================================== anime update (W2 / UI): 6-card select, tools, map players */
/* ---- title screen: six cards in a 2-column grid; the selected card spans both columns with its details.
 * dense flow refills the hole a spanning card leaves. One column below 600 px. Begin sticks to the panel's
 * bottom edge so it is never scrolled out of reach (400 x 800 phones). */
@media (min-width: 600px) {
  .ts-panel { width: min(560px, 100%); }
}
.ts-cards { display: grid; grid-template-columns: 1fr 1fr; grid-auto-flow: row dense; gap: 8px; }
.ts-cards .cc { grid-template-columns: 44px 1fr; gap: 4px 10px; padding: 8px 10px; min-width: 0; }
.ts-cards .cc-emblem { width: 44px; height: 44px; font-size: 24px; }
.ts-cards .cc-name { font-size: 15px; overflow-wrap: anywhere; }
.ts-cards .cc-title { display: none; }
.ts-cards .cc-meta { gap: 6px; }
.ts-cards .cc-diff-l { display: none; }
.ts-cards .cc.is-selected { grid-column: 1 / -1; grid-template-columns: 56px 1fr; gap: 4px 14px; padding: 10px 12px; }
.ts-cards .cc.is-selected .cc-emblem { width: 56px; height: 56px; font-size: 30px; }
.ts-cards .cc.is-selected .cc-name { font-size: 18px; }
.ts-cards .cc.is-selected .cc-title { display: block; }
.ts-cards .cc.is-selected .cc-diff-l { display: inline; }
@media (max-width: 599px) {
  .ts-cards { grid-template-columns: 1fr; }
}
/* The start bar: Begin, or Continue and New game side by side for a ninja with a save; New game asks above them first. */
.ts-startbar { position: sticky; bottom: 0; z-index: 2; margin-top: 4px; display: grid; gap: 6px; }
.ts-start, .ts-go { box-shadow: 0 -10px 18px rgba(10, 12, 26, 0.85), 0 4px 14px rgba(0, 0, 0, 0.45); }
.ts-btns { display: flex; gap: 8px; }
.ts-btns > .btn { flex: 1 1 0; min-width: 0; }
.ts-go[hidden], .ts-ask[hidden] { display: none; }
.ts-ask { padding: 10px 12px; border: 1px solid #ffb0a6; border-radius: 10px; background: rgba(24, 12, 16, 0.96); color: var(--text); font: 600 14px/1.35 var(--f-ui); }
.ts-ask p { margin: 0 0 8px; }
.ts-ask-btns { display: flex; gap: 8px; }
.ts-ask-btns > .btn { flex: 1 1 0; }
.ts-ask .is-warn { background: linear-gradient(180deg, #d9473a, #7a160e); border-color: #ffb0a6; }
/* "Preparing jutsu... NN%": the shader prewarm's progress fills the button from the left. */
.ts-start.is-preparing:not(.is-warn) {
  background:
    linear-gradient(90deg, rgba(255, 255, 255, 0.22) 0 var(--prep, 0%), transparent var(--prep, 0%)),
    linear-gradient(180deg, color-mix(in srgb, var(--accent) 60%, #2a2f4a), color-mix(in srgb, var(--accent) 30%, black));
  font-family: var(--f-ui);
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* ---- HUD: utility slots (Substitution X, Chakra Charge hold G) right of the action bar */
.utilbar {
  position: absolute;
  left: calc(100% + 6px);
  bottom: 18px; /* the XP bar (10 px) + the row gap (8 px) */
  display: flex;
  gap: var(--slot-gap);
  padding: 7px;
}
.slot--util { touch-action: none; }
.slot--util .slot-key { color: #9fd0ff; }
.slot--util .slot-short { letter-spacing: 0; }
.slot--util.on-cd .slot-cd { background: conic-gradient(transparent 0deg var(--rev, 360deg), rgba(4, 6, 16, 0.72) var(--rev, 360deg) 360deg); }
.slot--util.is-casting .slot-icon { animation: utilCharge 0.7s ease-in-out infinite alternate; }
@keyframes utilCharge { to { box-shadow: inset 0 0 0 2px #bfe6ff, inset 0 0 18px rgba(127, 211, 255, 0.85); } }
.hud.is-touch .utilbar { display: none; } /* touch screens have the Sub / Charge buttons */
/* Bottom row fit (SP.HUD fitBottom; docs/INTERFACE_AND_CONTROLS.md "Bottom row"). The bar holds 6-8 slots, so no fixed
 * breakpoint can keep it and X / G clear of the chat log and the menu buttons: the HUD measures them after layout,
 * places the bar's centre (--bar-x) and keeps X / G beside the bar, or above its right end (util-up). The row then
 * makes room above the bar, so the cast bar, the clone bar and a story dialogue stay clear of X / G. Before the first
 * measurement (or without a ResizeObserver) the bar is centred and X / G sit above it at 1180 px and below. */
.hud:not(.is-touch) .hud-bottom.is-fit { left: var(--bar-x, 50%); width: max-content; } /* max-content: right of the centre a column would only get W - left px, and wrap */
.hud-bottom.util-up .bar-row { margin-top: calc(var(--slot) + 2px); }
.hud-bottom.util-up .utilbar { left: auto; right: 0; bottom: calc(100% + 2px); padding: 0 7px; }
.hud-bottom.util-up ~ .dlg .dlg-box { margin-bottom: calc(var(--slot) + 6px); }
@media (max-width: 1180px) {
  .hud:not(.is-touch) .hud-bottom:not(.is-fit) .bar-row { margin-top: calc(var(--slot) + 2px); }
  .hud-bottom:not(.is-fit) .utilbar { left: auto; right: 0; bottom: calc(100% + 2px); padding: 0 7px; }
}
@media (max-height: 520px) {
  .hud-bottom.util-up ~ .dlg .dlg-box { margin-bottom: 0; } /* short screens: the dialogue sits at the bottom like subtitles */
}
/* The pieces above the row (clone bar, duel chip, cast bar, prompt) are centred on the bar: fitBottom moves each right of
 * a chat log beside it (--top-dx) and caps it to the room left (--top-max); the clone bar then wraps its order buttons
 * under the portraits (S3 PERFUX-7: an 8-clone bar ran over the chat log from 720 to 1280 px). */
.hud:not(.is-touch) .hud-bottom.is-fit > :not(.bar-row) { translate: var(--top-dx, 0px) 0; max-width: var(--top-max, calc(100vw - 32px)); }
.clonebar, .cl-list { flex-wrap: wrap; justify-content: center; }
/* Narrow desktop windows: the menu buttons as a 3 x 2 grid, so 7-8 slots fit between the gutter and them (S3 PERFUX-8:
 * at 720 px the buttons covered the 8th slot and the XP bar). */
@media (min-width: 641px) and (max-width: 800px) {
  .hud:not(.is-touch) .micro { display: grid; grid-template-columns: repeat(3, auto); }
}

/* ---- HUD: chakra bar pulses while Shift sprint drains it */
.bar--chakra.is-draining .bar-fill { animation: chakraDrain 0.55s ease-in-out infinite alternate; }
@keyframes chakraDrain { to { filter: brightness(1.5) saturate(1.3); } }
.bar--chakra.is-draining { box-shadow: 0 0 8px rgba(127, 196, 255, 0.55); }

/* ---- M1: Punch / Kick buttons beside X / G, the hit counter ("12 HITS", right of centre) */
.slot--strike .slot-key { color: #ffd9a0; }
.slot--strike .slot-icon { font-size: 22px; }
.hit-counter {
  position: absolute;
  left: calc(50% + 17vmin);
  top: 36%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  pointer-events: none;
  color: #ffd86b;
  text-shadow: 0 0 2px #000, 0 2px 2px #000, 0 0 6px rgba(0, 0, 0, 0.7);
  transform-origin: 0 50%;
  transition: opacity 0.35s ease-out;
  will-change: transform, opacity;
  font-variant-numeric: tabular-nums;
}
.hit-counter.is-out { opacity: 0; }
.hc-n { font: italic 800 clamp(34px, 6.5vmin, 64px)/0.95 var(--f-ui); }
.hc-l { font: 800 clamp(13px, 2.2vmin, 22px)/1 var(--f-ui); letter-spacing: 0.22em; text-transform: uppercase; color: #fff3d0; }
.hit-counter.is-bump { animation: hitBump 0.18s ease-out; }
.hit-counter.is-bump-b { animation: hitBumpB 0.18s ease-out; }
@keyframes hitBump { 0% { transform: scale(1.35); } 100% { transform: scale(1); } }
@keyframes hitBumpB { 0% { transform: scale(1.35); } 100% { transform: scale(1); } }

/* ---- floating text: combo finisher and absorbed hits */
.ft--combo { color: #ffb347; font-size: 20px; font-style: italic; letter-spacing: 0.06em; text-transform: uppercase; }
.ft--absorb { color: #e9cf8f; font-style: italic; font-size: 18px; letter-spacing: 0.04em; }

/* ---- world map: other ninja (online), roster list */
.mp-online { display: flex; justify-content: center; margin-top: 6px; font: 700 13px/1 var(--f-ui); letter-spacing: 0.08em; text-transform: uppercase; color: #9fd0ff; }
.mp-others { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
.mp-other {
  --pc: #7fb2ff;
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  pointer-events: none;
}
.mp-other i { width: 13px; height: 13px; border-radius: 50%; background: var(--pc); box-shadow: 0 0 0 2px #0b1430, 0 0 0 3.5px #7fb2ff, 0 0 10px rgba(127, 178, 255, 0.8); }
.mp-other span {
  position: absolute;
  top: calc(100% + 1px);
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 700 clamp(9px, 1.5vw, 11.5px)/1 var(--f-ui);
  color: #dbeaff;
  text-shadow: 0 0 3px #000, 0 1px 2px #000;
}
.mp-other.is-dead i { background: #6a7084; box-shadow: 0 0 0 2px #0b1430, 0 0 0 3.5px #8a93a8; }
.mp-other.is-dead span { color: #a9afbf; text-decoration: line-through; }
.mp-other.is-pulse i { animation: mpPulse 0.5s ease-out 3; }
@keyframes mpPulse { 0% { transform: scale(1); } 50% { transform: scale(2); box-shadow: 0 0 0 2px #0b1430, 0 0 0 4px #fff, 0 0 18px #9fd0ff; } 100% { transform: scale(1); } }
.lg--other { background: #7fb2ff; box-shadow: 0 0 0 2px #0b1430; width: 12px; height: 12px; }
.mp-roster { list-style: none; margin: 10px auto 0; padding: 0; max-width: 520px; display: flex; flex-direction: column; gap: 4px; }
.mp-row {
  --pc: #7fb2ff;
  width: 100%;
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 10px;
  border-radius: 6px;
  border: 1px solid var(--ink-line-2);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  font: 500 13.5px/1.2 var(--f-ui);
  text-align: left;
  cursor: pointer;
  pointer-events: auto;
}
.mp-row:hover { border-color: #7fb2ff; background: rgba(127, 178, 255, 0.1); }
.mp-row i { flex: none; width: 10px; height: 10px; border-radius: 50%; background: var(--pc); align-self: center; }
.mp-row b { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; }
.mp-row span { color: var(--text-dim); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mp-row.is-dead { opacity: 0.7; }
.mp-alone { text-align: center; font-size: 13px; color: var(--text-faint); }
@media (pointer: coarse) { .mp-row { min-height: 44px; } }

/* ---- game menu: sound and sprint settings */
.gm-sound { display: flex; flex-direction: column; gap: 0; }
.gm-sound .gm-vol { padding: 5px 2px; }
.gm-music { margin-top: 2px; padding: 2px 2px 6px; }
.segs--2 { grid-template-columns: repeat(2, 1fr); }

/* ---- help + jutsu scroll notes */
.hp-sticky { font-size: 12.5px; }
.sc-universal { margin-top: 10px; }

/* ---- title-screen raster warm (SP.HUD.warmRaster, SP.Panels.warmRaster; docs/INTERFACE_AND_CONTROLS.md
 * "HUD raster warm"): painted under the title but invisible and inert. 0.01, not 0: the browser does not
 * raster a layer at exactly 0, and the point is to make it raster (and compile its GPU programs) now.
 * will-change: opacity gives it its own compositor layer, so its content is rastered at full opacity and
 * the 0.01 is applied when the frame is composited: rastered inside an opacity group instead, every draw
 * is folded with the alpha and Skia picks different programs than the same draws at Start. */
.is-warm { opacity: 0.01 !important; will-change: opacity; pointer-events: none !important; }
.is-warm * { pointer-events: none !important; }
.panel.is-warm { animation: none !important; }
/* The story-update sample of the warm Help panel (SP.Panels warmSample: the Jutsu Scroll, the map, ...): over the panel's
 * box, never at the end of its scrolling body, where it was not rastered (S3 PERFUX-3). */
.pn-warm { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }

/* ---- title screen: a selected (expanded) card is scrolled into view above the sticky Begin bar
 * (SP.Menu revealCard); the padding also keeps keyboard focus scrolling clear of it. */
.ts-panel { scroll-padding-top: 12px; scroll-padding-bottom: 78px; }

/* ============================================================== story update (S1 UI; docs/INTERFACE_AND_CONTROLS.md "Story update") */
/* ---- action bar: keys 7-8 show only when the character has them (D2); charges, toggle glow, stack badge,
 * requirement tint ("Needs Gate 6") and the legacy mark of a rank kept from a v1 save. */
.slot.is-none { display: none; }
.slot-ch, .slot-stk { position: absolute; bottom: 13px; z-index: 3; pointer-events: none; font: 800 11px/1 var(--f-ui); color: #fff; text-shadow: 0 0 2px #000, 0 1px 2px #000; }
.slot-ch { left: 3px; min-width: 15px; height: 15px; padding: 0 3px; border-radius: 8px; display: none; place-items: center; color: #cfe8ff; background: rgba(10, 14, 34, 0.86); box-shadow: 0 0 0 1px #9fd0ff; }
.slot.has-ch .slot-ch { display: grid; }
.slot-stk { right: 3px; color: var(--gold); font-size: 13px; }
.slot-stk:empty { display: none; }
.slot-leg { position: absolute; left: 50%; top: -9px; z-index: 3; transform: translateX(-50%); display: none; padding: 1px 4px; border-radius: 3px; font: 700 9px/1.2 var(--f-ui); letter-spacing: 0.04em; color: #2a1a00; background: linear-gradient(180deg, var(--brass-hi), var(--brass)); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6); pointer-events: none; }
.slot.is-legacy .slot-leg { display: block; }
.slot.is-toggled .slot-icon { box-shadow: inset 0 0 0 2px #9fe8ff, inset 0 0 16px rgba(120, 220, 255, 0.75); }
.slot.is-toggled { animation: toggleGlow 1.1s ease-in-out infinite alternate; }
@keyframes toggleGlow { to { box-shadow: 0 0 12px rgba(120, 220, 255, 0.7); } }
.slot.is-req .slot-tint { opacity: 1; background: repeating-linear-gradient(135deg, rgba(120, 30, 160, 0.42) 0 5px, rgba(60, 10, 90, 0.42) 5px 10px); }
.slot--util.is-override .slot-key { color: var(--gold); }
/* ---- D2 (docs/INTERFACE_AND_CONTROLS.md "Transformations and the D2 UI"): a slot that casts another jutsu than its own (an
 * override: the fox's keys 1-5 with the Tailed Beast Ball on 3, Amaterasu, X as Clone Swap) wears a rim in that jutsu's
 * colour; each swap pops it once (0.4 s; is-swap-b is an identical copy, so a second swap restarts it). The warm sample in
 * hud.js paints both, so the first swap rasters nothing new. Cooldowns of a minute or more (2:30, 10m) use a smaller digit. */
.slot.is-override:not(.is-casting) {
  background: linear-gradient(#0a0c18, #0a0c18) padding-box,
    linear-gradient(180deg, color-mix(in srgb, var(--icon-bg, #8a0f14) 40%, #fff3d6), color-mix(in srgb, var(--icon-bg, #8a0f14) 75%, #fff)) border-box;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 12px 2px color-mix(in srgb, var(--icon-bg, #8a0f14) 60%, #fff);
}
.slot.is-override:not(.is-pressed):not(.is-toggled) .slot-icon {
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--icon-bg, #8a0f14) 45%, #fff), inset 0 -8px 14px rgba(0, 0, 0, 0.3);
}
.slot.is-swap { animation: slotSwap 0.4s ease-out; }
.slot.is-swap.is-swap-b { animation-name: slotSwapB; }
.slot.is-swap .slot-icon { animation: ready 0.4s ease-out; }
@keyframes slotSwap { 0% { transform: scale(1); } 35% { transform: scale(1.15); box-shadow: 0 0 0 2px #fff, 0 0 22px 4px #fff; } 100% { transform: scale(1); } }
@keyframes slotSwapB { 0% { transform: scale(1); } 35% { transform: scale(1.15); box-shadow: 0 0 0 2px #fff, 0 0 22px 4px #fff; } 100% { transform: scale(1); } }
.slot-cdtxt.is-long { font-size: clamp(11px, calc(var(--slot) * 0.34), 17px); letter-spacing: -0.01em; }
/* A key refused for silence or a sealed chakra right now (the fox's 6, 7 and G; Spent): a grey hatch, the tooltip says why. */
.slot.is-silenced .slot-tint { opacity: 1; background: repeating-linear-gradient(135deg, rgba(12, 12, 20, 0.7) 0 5px, rgba(64, 64, 78, 0.62) 5px 10px); }
.slot.is-silenced .slot-short { color: #b9bccb; }

/* ---- character gauges (SP.HUD.addGauge: the sand of Gaara, the seal reserve of Sakura) under the player frame */
.gauges { position: absolute; top: calc(var(--gt) + 86px); left: var(--gl); width: 290px; display: flex; flex-direction: column; gap: 4px; padding: 0 4px; pointer-events: none; }
.gauge { --g: var(--brass); display: flex; align-items: center; gap: 8px; }
.g-lb { flex: none; min-width: 44px; font: 700 11px/1 var(--f-ui); letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); text-shadow: 0 1px 2px #000; }
.g-bar { position: relative; flex: 1; height: 10px; border-radius: 3px; overflow: hidden; background: linear-gradient(180deg, #05060c, #151a30); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(0, 0, 0, 0.6); }
.g-fill { position: absolute; inset: 0; transform-origin: 0 50%; will-change: transform; background: linear-gradient(180deg, color-mix(in srgb, var(--g) 70%, white), var(--g) 50%, color-mix(in srgb, var(--g) 55%, black)); }
.g-notch { position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px; background: rgba(10, 8, 4, 0.85); }
.g-txt { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font: 700 9.5px/1 var(--f-ui); color: #fff; text-shadow: 0 0 2px #000, 0 1px 2px #000; }

/* ---- target frame: a PvP-hostile ninja reads red; the Duel button (docs/PVP.md) */
.unit--target.is-pvp .unit-nm { color: var(--hostile); }
.unit--target.is-pvp { box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 90, 79, 0.55); }
.unit-duel { position: absolute; left: 14px; bottom: -12px; z-index: 3; display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px 3px 7px; border-radius: 12px; border: 1px solid rgba(255, 138, 128, 0.7); background: linear-gradient(180deg, #7a2a26, #3a0e0c); color: #ffe6df; font: 700 11.5px/1 var(--f-ui); letter-spacing: 0.04em; cursor: pointer; pointer-events: auto; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5); }
.unit-duel .ico { font-size: 13px; }
.unit-duel:hover { border-color: #fff; color: #fff; }

/* ---- floating text: blocked / reflected / sealed, kit callouts ("U! Zu! Ma! Ki!"), passives */
.ft--block { color: #cfe8ff; font-style: italic; font-size: 18px; letter-spacing: 0.04em; }
.ft--shield::after { content: ""; display: inline-block; width: 0.7em; height: 0.8em; margin-left: 0.25em; background: #cfe8ff; clip-path: polygon(0 0, 100% 0, 100% 55%, 50% 100%, 0 55%); }   /* M2: a hit a guard cut */
.ft--callout { color: #ffd23f; font-size: 19px; letter-spacing: 0.03em; }

/* ---- right column: the story tracker line (SP.Story.next) and world-event chips (js/ui/hud-story.js) */
.st-line { display: flex; align-items: stretch; padding: 0; pointer-events: auto; }
.st-main { flex: 1; min-width: 0; display: flex; align-items: flex-start; gap: 7px; padding: 7px 8px 7px 10px; border: 0; background: transparent; text-align: left; cursor: pointer; }
.st-main:hover { background: rgba(255, 255, 255, 0.05); }
.st-ico { flex: none; margin-top: 1px; font-size: 15px; color: var(--gold); }
.st-txt { min-width: 0; font: 700 13px/1.25 var(--f-ui); color: var(--quest); overflow-wrap: anywhere; }
.st-line.is-wait .st-txt { color: var(--text-dim); }
.st-go { flex: none; align-self: center; min-height: 30px; margin-right: 6px; padding: 4px 10px; font-size: 13px; }
.ev-chips { display: flex; flex-direction: column; gap: 4px; }
.ev-chips:empty { display: none; }
.ev-chip { display: flex; align-items: center; gap: 6px; padding: 5px 9px; border-radius: 6px; border: 1px solid rgba(255, 90, 79, 0.35); background: rgba(40, 10, 12, 0.8); color: #ffd9d3; font: 700 12px/1.2 var(--f-ui); text-align: left; cursor: pointer; pointer-events: auto; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4); }
.ev-chip > span { flex: none; font-size: 13px; color: #ff8a7a; }
.ev-chip.is-live { border-color: var(--hostile); animation: evLive 1s ease-in-out infinite alternate; }
@keyframes evLive { to { box-shadow: 0 0 12px rgba(255, 90, 79, 0.65); } }

/* ---- bottom: the duel chip and the clone bar (portraits, stances, timers, orders; a radial on touch) */
.duel-chip { display: flex; align-items: center; gap: 8px; padding: 5px 6px 5px 10px; pointer-events: auto; }
.dc-ico { flex: none; font-size: 16px; color: var(--hostile); }
.dc-txt { font: 700 13.5px/1.2 var(--f-ui); color: #ffe6df; white-space: nowrap; }
.dc-quit { min-height: 28px; padding: 3px 10px; font-size: 12.5px; }
.duel-chip.is-out { animation: duelOut 0.5s ease-in-out infinite alternate; }
.duel-chip.is-out .dc-txt { color: #ff8a7a; }
@keyframes duelOut { to { box-shadow: 0 0 14px rgba(255, 77, 61, 0.7); } }
.clonebar { position: relative; display: flex; align-items: center; gap: 8px; padding: 5px 7px; pointer-events: auto; max-width: calc(100vw - 32px); }
.cl-list { display: flex; flex-wrap: wrap; gap: 7px; min-width: 0; }
/* 3d, the faces row: the ring colour is the stance (--rc); a gather ring fills to ready (--arc); the thin outer ring is the seconds of a
 * summon that still expires (--sec). docs/INTERFACE_AND_CONTROLS.md "Clone control". */
@property --arc { syntax: '<angle>'; inherits: false; initial-value: 360deg; }
@property --sec { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
@keyframes clFill { from { --arc: 0deg; } to { --arc: 360deg; } }
@keyframes clDrain { from { --sec: 360deg; } to { --sec: 0deg; } }
/* --fd / --fe: a gather ring fills over --fd s and --fe s have run; --sd / --se: the thin outer ring drains over --sd s, --se s have run (0 s: the end state, a full ring and no outer ring) */
.cl-c { --rc: #f1ead6; position: relative; isolation: isolate; flex: none; width: 28px; height: 28px; padding: 2.5px; border: 0; border-radius: 50%; cursor: pointer; background: conic-gradient(var(--rc) var(--arc, 360deg), rgba(255, 255, 255, 0.18) 0);
  animation: clFill var(--fd, 0s) linear calc(var(--fe, 0s) * -1) both, clDrain var(--sd, 0s) linear calc(var(--se, 0s) * -1) both; }
.cl-c::before { content: ''; position: absolute; inset: -3px; z-index: -1; border-radius: 50%; background: conic-gradient(#ffd23f var(--sec, 0deg), transparent 0); }
.cl-g { display: grid; place-items: center; width: 100%; height: 100%; overflow: hidden; border-radius: 50%; background: radial-gradient(circle at 50% 30%, #2b3366, #10142a); font: 400 15px/1 var(--f-glyph); color: #cfe0ff; }
.st-move, .st-hold { --rc: #6fa8ff; }
.st-attack { --rc: #ff5a4f; }
.st-gather { --rc: #ffd23f; }
.st-control { --rc: #8fe3ff; }
.cl-c.is-sel { box-shadow: 0 0 0 2px var(--ally), 0 0 10px rgba(127, 178, 255, 0.6); }
.cl-c.is-ready { box-shadow: 0 0 10px 2px rgba(255, 210, 63, 0.9); }
.cl-e { position: absolute; right: -4px; bottom: -4px; display: grid; place-items: center; width: 14px; height: 14px; border-radius: 50%; background: #0c1330; box-shadow: 0 0 0 1px var(--ink-line-2); color: #8fe3ff; font-size: 10px; }
.cl-t { position: absolute; top: -8px; right: -8px; min-width: 18px; padding: 1px 3px; border-radius: 7px; font: 700 9.5px/1.2 var(--f-ui); color: #fff; background: rgba(10, 14, 34, 0.94); box-shadow: 0 0 0 1px var(--ink-line-2); }
.cl-t:empty { display: none; }
.cl-b { display: inline-flex; align-items: center; gap: 5px; min-height: 30px; padding: 3px 9px 3px 6px; border: 1px solid var(--ink-line-2); border-radius: 6px; background: linear-gradient(180deg, #2b3366, #1a1f42); color: var(--text); font: 700 12px/1 var(--f-ui); cursor: pointer; }
.cl-b:hover { border-color: var(--accent); }
.cl-c:hover { box-shadow: 0 0 0 2px var(--accent); }
.cl-bi { display: inline-flex; font-size: 15px; color: #cfe0ff; }
.cl-b kbd { min-width: 0; padding: 0 4px; font-size: 10px; }
.ctl-chip { display: inline-flex; align-items: center; gap: 5px; padding: 4px 9px 4px 6px; border-radius: 12px; background: rgba(12, 19, 48, 0.9); box-shadow: 0 0 0 1px var(--ink-line-2); color: #cfe9ff; font: 700 12.5px/1 var(--f-ui); white-space: nowrap; }
.cc-i { display: inline-flex; font-size: 15px; color: #8fe3ff; }
/* The control bar (a clone is the body): Q, E, X and key 3 only; the other keys refuse, so phones lose Guard and Charge too. */
.hud.is-ctl .actionbar .slot:not([data-slot="2"]), .hud.is-ctl .slot--util[data-util="3"], .hud.is-ctl .wf-touch-guard, .hud.is-ctl .wf-touch-charge { display: none; }
.hud.is-touch .clonebar { gap: 4px; }
.hud.is-touch .cl-list { gap: 3px; }
.hud.is-touch .cc-i { display: none; }
.hud.is-touch .cl-c { width: 24px; height: 24px; padding: 2px; }
.hud.is-touch .cl-g { font-size: 13px; }
.hud.is-touch .cl-b { min-width: 44px; min-height: 44px; justify-content: center; }
.hud.is-touch .cl-b kbd, .hud.is-touch .cl-b:not([data-hud="cloneControl"]) b { display: none; }

/* ---- duel request toast (Accept / Decline; "Block duel requests" declines at once) */
.duel-req { position: absolute; left: 50%; top: 26%; z-index: 6; transform: translateX(-50%); width: min(380px, calc(100vw - 32px)); display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 12px 14px; text-align: center; pointer-events: auto; animation: promptIn 0.25s ease; }
.dr-txt { font: 700 15px/1.3 var(--f-ui); color: var(--text); }
.dr-btns { display: flex; gap: 8px; }

/* ---- story dialogue overlay (SP.Dialogue, js/ui/dialogue.js): letterbox bars, speaker, Next / Skip */
.dlg { position: absolute; inset: 0; z-index: 8; pointer-events: none; }
.dlg-lb { position: absolute; left: 0; right: 0; height: 7vh; display: none; background: rgba(0, 0, 0, 0.88); }
.dlg-lb--t { top: 0; }
.dlg-lb--b { bottom: 0; }
.dlg.is-letterbox .dlg-lb { display: block; animation: lbIn 0.35s ease-out; }
@keyframes lbIn { from { transform: scaleY(0); } }
.dlg-box { position: absolute; left: 50%; bottom: calc(var(--gb) + 132px); transform: translateX(-50%); width: min(720px, calc(100vw - 32px)); display: flex; align-items: flex-start; gap: 14px; padding: 12px 14px; pointer-events: auto; animation: promptIn 0.25s ease; }
.dlg-por { flex: none; width: 58px; height: 58px; border-radius: 50%; display: grid; place-items: center; font: 400 30px/1 var(--f-glyph); color: #fff; background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.25), transparent 55%), var(--accent); box-shadow: 0 0 0 3px var(--steel-dk), 0 0 0 5px var(--steel); }
.dlg-main { flex: 1; min-width: 0; }
.dlg-who { font: 400 17px/1.15 var(--f-title); color: var(--quest); }
.dlg-emo { margin-left: 6px; font: italic 600 12.5px/1 var(--f-ui); color: var(--text-faint); }
.dlg-txt { margin: 6px 0 0; font: 500 16px/1.42 var(--f-ui); color: var(--text); overflow-wrap: anywhere; }
.dlg-side { flex: none; display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
.dlg-prog { font: 600 11.5px/1 var(--f-ui); color: var(--text-faint); text-align: center; }

/* ---- Jutsu Scroll: stage rows with gates and costs, branch options, the path picker, kept (legacy) ranks */
.sj-stages { list-style: none; margin: 8px 0 4px; padding: 0; display: flex; flex-direction: column; gap: 3px; max-width: 480px; }
.sj-st { display: grid; grid-template-columns: 20px minmax(0, 1fr) auto auto; align-items: baseline; gap: 2px 8px; padding: 3px 6px; border-radius: 4px; font: 600 13px/1.25 var(--f-ui); color: #4a3a28; background: rgba(32, 23, 14, 0.05); }
.sj-st b { display: inline-grid; place-items: center; width: 18px; height: 18px; border-radius: 50%; font: 800 11px/1 var(--f-ui); color: #f8eedb; background: rgba(32, 23, 14, 0.45); }
.sj-st.is-done b { background: linear-gradient(135deg, #e5483c, var(--seal-red)); }
.sj-st.is-next { background: rgba(179, 38, 30, 0.1); box-shadow: inset 3px 0 0 rgba(179, 38, 30, 0.6); }
.sj-st-n { min-width: 0; font-weight: 700; color: var(--sumi); overflow-wrap: anywhere; }
.sj-st.is-done .sj-st-n { color: #2f4a2a; }
.sj-st-g { font-size: 12px; color: #8a3a1a; text-align: right; }
.sj-st-g.is-met { color: #2f6b3a; }
.sj-st-g.is-met::before { content: '✓ '; }
.sj-pts { padding: 2px 5px; border-radius: 3px; font: 800 10.5px/1 var(--f-ui); letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap; color: #fff6e6; background: #7d4a12; }
/* How a stage's cast moves (castMove, D1), under its name where it changes: "Stand still to form", "Formed on the run". */
.sj-mv { grid-column: 2 / -1; font: 600 11.5px/1.2 var(--f-ui); color: #6a4a1c; }
/* The keys a stage's form buff turns into (D2): "While Nine-Tails Fox is on: 1 Fox Claw, 2 Tailed Beast Roar, ...". */
.sj-ov { grid-column: 2 / -1; padding-left: 7px; border-left: 2px solid rgba(179, 38, 30, 0.55); font: 600 11.5px/1.3 var(--f-ui); color: #7a2a14; }
.sj-br { display: flex; flex-wrap: wrap; gap: 4px; padding: 2px 0 4px 26px; }
.sj-opt { max-width: 230px; padding: 3px 8px; border-radius: 4px; border: 1px dashed rgba(32, 23, 14, 0.35); font: 500 12px/1.25 var(--f-ui); color: #5e4a33; }
.sj-opt b { color: var(--sumi); }
.sj-opt.is-on { border-style: solid; border-color: var(--seal-red); background: rgba(179, 38, 30, 0.08); }
.sj-pick { display: flex; flex-direction: column; gap: 6px; }
.sj-pick .btn--stamp { min-width: 104px; min-height: 36px; font-size: 13.5px; }
.sj-leg { padding: 3px 6px; border-radius: 4px; border: 1px solid #b98e4e; font: 700 10.5px/1 var(--f-ui); letter-spacing: 0.05em; text-transform: uppercase; color: #5a4113; background: rgba(201, 162, 79, 0.25); }

/* ---- Character panel: attribute training (2 points a level; Reset is free out of combat) */
.ch-attrs { margin-top: 14px; }
.ch-attrs h3 { margin: 0 0 6px; display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font: 700 12px/1 var(--f-ui); letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); }
.at-pts { font: 700 12.5px/1 var(--f-ui); letter-spacing: 0.02em; text-transform: none; color: var(--gold); }
.at-row { display: grid; grid-template-columns: minmax(0, 1fr) 34px auto; align-items: center; gap: 10px; padding: 5px 0; border-bottom: 1px solid var(--ink-line); }
.at-n { min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; font: 700 14px/1.2 var(--f-ui); color: var(--text); }
.at-n small { flex-basis: 100%; font: 500 12px/1.25 var(--f-ui); color: var(--text-faint); }
.at-aff { padding: 2px 4px; border-radius: 3px; font: 800 11px/1 var(--f-ui); color: var(--good); background: rgba(121, 226, 143, 0.12); }
.at-aff.is-low { color: #ff9a8a; background: rgba(255, 90, 79, 0.12); }
.at-row b { font: 800 17px/1 var(--f-ui); text-align: center; font-variant-numeric: tabular-nums; }
.at-add { min-width: 36px; min-height: 32px; padding: 2px 10px; font-size: 18px; }
.at-foot { display: flex; align-items: center; gap: 10px; margin-top: 8px; font-size: 12.5px; color: var(--text-faint); }

/* ---- Story Log: chains by act, the next step, pin, start and choices */
.sl-next { display: flex; align-items: center; gap: 8px; margin: 12px 0 4px; padding: 8px 10px; border-radius: 6px; border: 1px solid rgba(255, 209, 102, 0.3); background: rgba(255, 209, 102, 0.08); font-size: 14px; color: var(--text); }
.sl-next .ico { font-size: 16px; color: var(--gold); }
.sl-next b { color: var(--quest); }
.sl-act .sec-h small { margin-left: 4px; font: 600 11px/1 var(--f-ui); letter-spacing: 0.04em; text-transform: none; color: var(--text-faint); }
.sl-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.sl { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; padding: 6px 10px; border-radius: 6px; border: 1px solid var(--ink-line); background: rgba(255, 255, 255, 0.03); font-size: 14px; }
.sl-i { flex: none; width: 16px; text-align: center; font: 800 13px/1 var(--f-ui); color: var(--text-faint); }
.sl .tq-rank { margin-right: 0; }
.sl-m { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.sl-m b { font: 700 14.5px/1.2 var(--f-ui); color: var(--text); }
.sl-m span { font: 500 12.5px/1.25 var(--f-ui); color: var(--text-dim); }
.sl-lv { flex: none; font: 600 12px/1 var(--f-ui); color: var(--text-faint); }
.sl-a { flex: none; display: flex; gap: 4px; }
.btn--sm { min-height: 28px; padding: 3px 10px; font-size: 12.5px; }
.sl--active, .sl--complete { border-color: rgba(255, 209, 102, 0.45); }
.sl--active .sl-i, .sl--active .sl-m b { color: var(--quest); }
.sl--complete .sl-i, .sl--complete .sl-m b, .sl--turnedIn .sl-i { color: var(--good); }
.sl--available .sl-i { color: var(--gold); }
.sl--turnedIn { opacity: 0.72; }
.sl--locked { opacity: 0.55; }

/* ---- world map: world events, the story pin, own clones (hollow) */
.mp-event { position: absolute; z-index: 2; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; gap: 2px; pointer-events: none; }
.mp-event > .ico { box-sizing: content-box; width: 14px; height: 14px; padding: 4px; border-radius: 50%; color: #fff; background: radial-gradient(circle, #9a2e24, #3a0a06); box-shadow: 0 0 0 2px #000; }
.mp-event.is-live > .ico { background: radial-gradient(circle, #ff5a4f, #7a120c); box-shadow: 0 0 0 2px #000, 0 0 14px rgba(255, 90, 79, 0.9); animation: evLive 1s ease-in-out infinite alternate; }
/* An event's name sits on the spot SP.Panels picked around its skull (inline left / top: 50% + an offset in cqw, % of the
 * map); its size in cqw like a zone name's level line (LBL.lvl 2.4, line 1.2), so the panel can keep it clear (S3 PERFUX-4). */
.mp-event span { position: absolute; transform: translate(-50%, -50%); font: 700 clamp(8px, 2.4cqw, 12px)/1.2 var(--f-ui); color: #ffd9d3; text-shadow: 0 0 3px #000, 0 1px 2px #000; white-space: nowrap; }
.mp-pin { position: absolute; z-index: 3; transform: translate(-50%, -100%); font-size: 22px; color: var(--gold); pointer-events: none; animation: markerBob 1.2s ease-in-out infinite alternate; }
.mp-clone { position: absolute; z-index: 2; width: 9px; height: 9px; transform: translate(-50%, -50%); border-radius: 50%; border: 2px solid var(--ally); pointer-events: none; }
.lg--pin { color: var(--gold); font-size: 14px; }

/* ---- story update: medium screens, phones, touch */
@media (max-width: 900px) {
  .gauges { width: 250px; }
}
@media (max-width: 640px) {
  .gauges { top: calc(var(--gt) + 128px); width: min(212px, calc(100vw - 150px)); }
  .hud:not(.is-touch) .actionbar:has(> .slot:nth-child(7):not(.is-none)) { --slot: clamp(38px, calc((100vw - 32px - 38px) / 7), 55px); }
  .hud:not(.is-touch) .actionbar:has(> .slot:nth-child(8):not(.is-none)) { --slot: clamp(34px, calc((100vw - 32px - 42px) / 8), 55px); }
  /* M1 review PLAY-7: the right column is 120 px wide here, and Begin beside the text left the text a 19 px sliver ("Sto / p / Miz /
   * uki"). Begin has its own row under the text; the star and the paddings shrink so the text keeps about 93 px (the line is also
   * shorter than before: 93 px instead of 131 px high at 400 px wide). */
  .st-line { flex-direction: column; }
  .st-main { flex: none; padding: 6px 3px 5px 5px; gap: 3px; }
  .st-ico { font-size: 12px; }
  .st-txt { font-size: 11.5px; }
  .st-go { align-self: stretch; margin: 0 5px 5px; }
  .ev-chip { padding: 4px 6px; font-size: 11px; }
  .dlg-box { bottom: calc(var(--gb) + 180px); flex-wrap: wrap; gap: 8px 12px; padding: 10px 12px; }
  .dlg-por { width: 42px; height: 42px; font-size: 22px; }
  .dlg-txt { font-size: 14.5px; }
  .dlg-side { flex-direction: row; align-items: center; justify-content: flex-end; width: 100%; }
  .dlg-prog { margin-right: auto; }
  .duel-req { top: 30%; }
  .sj-st { grid-template-columns: 20px minmax(0, 1fr) auto; }
  .sj-st-g { grid-column: 2 / -1; text-align: left; }
  /* "2 points" / "by itself" under the gate, not in the 20 px number column (D2: it ran under the fox's key line). */
  .sj-pts { grid-column: 2 / -1; justify-self: start; }
  .sj-br { padding-left: 0; }
}
@media (pointer: coarse) {
  .btn--sm, .at-add, .st-go, .dc-quit, .dt-fl select { min-height: 44px; }
}
/* Short screens (landscape phones, small windows): the dialogue sits at the bottom like subtitles, so the unit frames
 * (the target frame's Duel button) above it stay usable while it shows. */
@media (max-height: 520px) {
  .dlg-box { bottom: calc(var(--gb) + 4px); }
  .dlg-lb { height: 5vh; }
}
/* Step 3 (L-UI, docs/INTERFACE_AND_CONTROLS.md "Levels and milestones"): the co-op hint under a level wait on the tracker line,
 * the +5 training button beside +, and the three-digit level badge (levels run to 100; the badge grows from its centre). */
.st-txt[data-hint]::after { content: attr(data-hint); display: block; margin-top: 2px; font: 500 11px/1.25 var(--f-ui); color: var(--text-faint); }
.at-row { grid-template-columns: minmax(0, 1fr) 40px auto auto; }
.at-add5 { font-size: 14px; }
.unit-level { padding: 2px 4px; letter-spacing: -0.02em; }
/* The Developer tools' Level row has seven buttons now (+1, +5, four milestone levels, 100): the label takes its own row and the buttons share equal columns. */
.dt-lv { grid-template-columns: repeat(auto-fit, minmax(52px, 1fr)); }
.dt-lv > span { grid-column: 1 / -1; }

/* ---- step 4 W2 (W-UI): the map's Region / World tabs, the world tools, the travel bar (docs/INTERFACE_AND_CONTROLS.md "Step 4: regions in the UI") */
.mp-tabs { width: min(260px, 100%); margin: 2px auto 8px; }
.mp-pane[hidden] { display: none; }
.castbar--travel .cb-fill { background: linear-gradient(180deg, #b6ecff, #3b9ac4 50%, #1c4a66); }
.mp-pane .mp-wrap { width: min(100%, calc(100vh - 284px)); }   /* the tab bar's 46-54 px come out of the 230 px the map was sized for */
.mp-pane--world .mp-wrap { width: min(100%, calc((100vh - 284px) * 720 / 528)) !important; }   /* SP.WorldMap sets its width inline */
/* FX U4: a landscape phone (844 x 390) has the width, not the height. The panel is 720 px wide, but the title and the tab bar leave about 220 px
 * and the Region map drew at its 280 px minimum: cut at the bottom and its labels crowded (their sizes are in % of the map and hold their layout only
 * from about 340 px up, so the 9 px / 8 px floors made them bigger than planned). Short windows give it 400 px; the panel body already scrolls under
 * its title. The World atlas keeps its overview size (280 x 205 fits whole; it is drawn at half scale), and taller windows keep the sizes above. */
@media (max-height: 520px) {
  .mp-pane--region .mp-wrap { width: min(100%, 400px); min-width: min(400px, 100%); }
}

/* ---- step 4 W3 (W-BOSSES): the developer tool "Map: enemy levels" (docs/GAMEPLAY.md "Testing step 4"): a dot per spawn point */
.mp-lv { position: absolute; z-index: 2; width: 7px; height: 7px; transform: translate(-50%, -50%); border-radius: 50%; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.65); pointer-events: none; }

/* 4b fight modes */
/* docs/FIGHT_MODES.md, docs/INTERFACE_AND_CONTROLS.md "Fight modes": the group chip and its sheet under the minimap (js/ui/fight-ui.js),
 * the group row and the grey (taken) cards of the title screen (js/ui/menu.js). */
.fm { position: relative; z-index: 4; display: flex; justify-content: center; pointer-events: none; }
.fm-chip { position: relative; max-width: 100%; display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px 0 9px; border-radius: 16px; border: 1px solid var(--g, var(--ink-line-2)); background: linear-gradient(180deg, rgba(27, 32, 66, 0.93), rgba(12, 15, 33, 0.95)); color: var(--text); font: 700 13px/1 var(--f-ui); letter-spacing: 0.02em; cursor: pointer; pointer-events: auto; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4); }
.fm-chip:disabled { cursor: default; color: var(--text-dim); }
.fm-chip i, .gp i { flex: none; width: 10px; height: 10px; border-radius: 50%; background: var(--g, #8a93b8); }
.fm-chip span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The sheet hangs in the HUD root, not in the right column (a phone's column clips with overflow: hidden): js sets --fm-top under the chip.
 * A 2 x 2 grid keeps it about 150 px high, so on a phone it ends above the row of Sprint / Sub / Charge / Target. */
.fm-sheet { position: absolute; z-index: 5; top: var(--fm-top, 190px); right: var(--gr); display: grid; grid-template-columns: 1fr 1fr; gap: 6px; width: 236px; padding: 10px; pointer-events: auto; }
.fm-hint { grid-column: 1 / -1; margin: 2px 2px 0; font: 500 12.5px/1.3 var(--f-ui); color: var(--text-dim); }
.gp { display: inline-flex; align-items: center; gap: 7px; min-height: 34px; padding: 6px 12px 6px 10px; border-radius: 17px; border: 1px solid var(--ink-line-2); background: rgba(255, 255, 255, 0.05); color: var(--text); font: 700 13px/1 var(--f-ui); cursor: pointer; }
.gp:hover:not(:disabled) { background: rgba(255, 255, 255, 0.12); }
.gp[aria-checked="true"] { border-color: var(--g); box-shadow: inset 0 0 0 1px var(--g); background: rgba(255, 255, 255, 0.1); }
.gp:disabled { opacity: 0.5; cursor: not-allowed; }
.unit-title.is-grp { color: var(--g); font-weight: 700; }
.ts-groups { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; padding: 2px 2px 4px; }
.gp-l { margin-right: 4px; font: 700 11px/1 var(--f-ui); text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-dim); }
.ol-fm { margin: 0; font: 600 13px/1.35 var(--f-ui); color: var(--text); }
.ol-fm:empty { display: none; }
.cc.is-taken .cc-emblem, .cc.is-taken .cc-name, .cc.is-taken .cc-title, .cc.is-taken .cc-role, .cc.is-taken .cc-diff { opacity: 0.45; }
.cc.is-taken .cc-emblem { background: var(--steel-lo); }
.cc.is-taken:hover { filter: none; }
.cc.is-taken .cc-online { color: var(--neutral); background: rgba(255, 216, 74, 0.12); }
.ts-cont:disabled { opacity: 0.5; cursor: not-allowed; }
@media (pointer: coarse) {
  .fm-chip::after { content: ''; position: absolute; inset: -6px 0; }
  .fm-sheet .gp, .ts-groups .gp { min-height: 44px; }
}
@media (max-width: 640px) {
  .fm-chip { padding: 0 8px; font-size: 11.5px; }
  .fm-sheet { width: min(236px, calc(100vw - 32px)); }
}
@media (pointer: coarse) and (max-width: 640px) {
  /* The chip takes 38 px of the phone's right column (S3 ends it at a fixed 440 px, 25 px above the row of Sprint / Sub / Charge / Target):
   * while it shows, end the column 4 px above that row, by the variable that places the row, so the open tracker keeps its first mission. */
  .hud.is-touch .hud-right:has(> .fm:not(.is-hidden)) { max-height: calc(100% - var(--gt) - var(--wf-touch-jump-bottom, 296px) - 120px - env(safe-area-inset-bottom, 0px) - 4px); }
}
/* 4b part 2, the gate's LOOK notes (F-ORCH-ASK GATE-5, GATE-7): the disabled sticky Begin bar ("Taken by ...") stays opaque over the
 * cards that scroll under it; a duel request lies above an open story dialogue (both in the HUD root), so Accept / Decline stay whole;
 * on a phone the centre line moves below the open group sheet instead of over its second row (the sheet closes within 12 s). */
.ts-start:disabled { opacity: 1; filter: none; color: var(--text-dim); text-shadow: none; border-color: var(--ink-line-2); background: linear-gradient(180deg, #3a3f5c, #22263b); }
.duel-req { z-index: 9; }
@media (max-width: 640px) {
  .hud:has(> .fm-sheet:not(.is-hidden)) ~ .center-msgs { top: calc(var(--gt) + 372px); }
}
/* Step 8 (S8-UI, STEP8 DESIGN 2.7.2; the F2 gate's P3-4): on a touch phone that line (gt + 372 px) lay on the labels of the top thumb row (Target,
 * Charge, Sub, Sprint: 46 px high, 370 px up from the bottom). There it hangs from 8 px above the row instead, by the variable that places the
 * row: it grows upward, and a long line covers the sheet's lower edge for its few seconds, never a button. */
@media (pointer: coarse) and (max-width: 640px) {
  .hud:has(> .fm-sheet:not(.is-hidden)) ~ .center-msgs { top: auto; bottom: calc(var(--wf-touch-jump-bottom, 296px) + 128px + env(safe-area-inset-bottom, 0px)); }
}
/* Step 8 (S8-UI, STEP8 DESIGN 2.7.2): the phone banners. A banner plays inside the HUD, under the panels, so the Jutsu Scroll (nearly the whole screen of a phone)
 * hid it. On a phone the Scroll now starts 112 px lower and the level banner plays in the band above it (the zone banner waits: one banner in the band). A banner's
 * second line holds two lines at most (a milestone's unlock list wrapped to four and reached down over the thumb row); on a touch phone the level banner hangs
 * from 8 px above that row, by the same variable as the centre line. */
@media (max-width: 640px) {
  .panel--jutsu { height: min(880px, calc(100% - var(--gt) - var(--gb) - 128px)); top: calc(50% + 56px); }
  #ui:has(.panel--jutsu:not(.is-hidden)) .banner--level { top: calc(var(--gt) + 4px); bottom: auto; }
  #ui:has(.panel--jutsu:not(.is-hidden)) .banner--zone { display: none; }
  .bn-sub { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
}
@media (pointer: coarse) and (max-width: 640px) {
  .banner--level { top: auto; bottom: calc(var(--wf-touch-jump-bottom, 296px) + 128px + env(safe-area-inset-bottom, 0px)); }
}

/* PH, the phone HUD declutter (2026-11-01, the user's priority; docs/INTERFACE_AND_CONTROLS.md "Phones: the three small buttons"; js/ui/hud.js
 * fb / fold / fbN, js/ui/hud-story.js): the chat log, the right column's text (the story line, the event chips, the Missions tracker) and the clone
 * panel fold into small round buttons (.fb-log in the chat, .fb-trk in the right column, .fb-cl ending the clone bar). The root carries o-log / o-trk /
 * o-cl while a panel is open: closed by default on a touch HUD, open on a desktop (as before); a tap toggles, the choice is kept on the device. */
.fb { position: relative; flex: none; display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 2px solid var(--ink-line-2); border-radius: 50%; background: linear-gradient(180deg, #2b3366, #141a3a); color: #cfe0ff; font-size: 15px; line-height: 1; cursor: pointer; pointer-events: auto; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45); }
.fb:hover { border-color: var(--accent); }
.fb > b { position: absolute; top: -6px; right: -7px; min-width: 18px; padding: 2px 4px; border-radius: 9px; font: 700 11px/1.15 var(--f-ui); color: #fff; text-align: center; background: #b8322a; box-shadow: 0 0 0 1.5px #0c1330; pointer-events: none; }
.fb > b:empty { display: none; }
.fb.is-new > b { animation: fb-new 1s ease-out; }
@keyframes fb-new { 30% { transform: scale(1.6); background: #ffd23f; color: #241a00; } }
.hud.o-log .fb-log, .hud.o-trk .fb-trk, .hud.o-cl .fb-cl { border-color: var(--accent); background: linear-gradient(180deg, #3d4790, #1d2452); }
/* Folded: the panel's content goes; the chat and the clone bar shrink to their button, which never takes the log's idle fade (in control the bar
 * stays whole: Back is a phone's way back). */
.hud:not(.o-log) .chat > :not(.fb), .hud:not(.o-trk) .hud-right > :is(.st-line, .ev-chips, .tracker), .hud:not(.o-cl):not(.is-ctl) .clonebar > :not(.fb) { display: none; }
.hud:not(.o-log) .chat, .hud:not(.o-cl):not(.is-ctl) .clonebar { width: auto; height: auto; padding: 0; border: 0; background: none; box-shadow: none; opacity: 1; }
.hud:not(.o-log) .chat::before, .hud:not(.o-cl):not(.is-ctl) .clonebar::before { display: none; }
/* A desktop: the log button sits in the open chat's tab row (folded: where the chat was), the column's at the minimap's top left corner. */
.hud.o-log .fb-log { position: absolute; top: 3px; right: 4px; z-index: 3; }
.hud-right > .fb-trk { position: absolute; top: 0; left: 0; z-index: 3; }
/* A touch HUD: the three buttons in one row under the frames and a hero's gauge row (44 px), each panel opens over the scene. */
@media (pointer: coarse) {
  .hud.is-touch .fb, .hud.is-touch.o-log .fb-log { position: fixed; z-index: 4; top: calc(var(--gt) + 120px); right: auto; left: var(--gl); width: 44px; height: 44px; font-size: 20px; }
  .hud.is-touch .fb-trk { left: calc(var(--gl) + 50px); }
  .hud.is-touch .fb-cl { left: calc(var(--gl) + 100px); }
}
/* Portrait phones: the row in the chat's old place (under the target frame and the buffs), the open log and clone panel under the row. */
@media (pointer: coarse) and (max-width: 640px) {
  .hud.is-touch .fb, .hud.is-touch.o-log .fb-log { top: calc(var(--gt) + 166px); }
  .hud.is-touch .chat, .hud.is-touch .clonebar { top: calc(var(--gt) + 216px); }
}
/* Landscape phones: the row under the player frame (and the gauges at gt + 86 px), the open log and clone panel beside it (the stick lies below);
 * the column's text opens in a second grid column left of the minimap (which keeps its place and width) and under the target frame's line,
 * clear of the thumb controls that cover the minimap's side. */
@media (pointer: coarse) and (max-height: 520px) {
  .hud.is-touch .fb, .hud.is-touch.o-log .fb-log { top: calc(var(--gt) + 120px); }
  .hud.is-touch .chat { display: flex; }
  .hud.is-touch .chat, .hud.is-touch .clonebar { position: fixed; top: calc(var(--gt) + 120px); bottom: auto; left: calc(var(--gl) + 150px); right: auto; max-width: min(340px, calc(100vw - var(--gl) - 390px)); }
  .hud.is-touch.o-log .chat { width: min(340px, calc(100vw - var(--gl) - 390px)); height: auto; }
  .hud.is-touch.o-log .chat-lines { max-height: calc(100vh - var(--gt) - 120px - 150px); }
  .hud.is-touch.o-trk .hud-right { display: grid; grid-template-columns: 210px 210px; grid-template-rows: 72px; width: auto; align-items: start; max-height: calc(100% - var(--gt) - 70px); overflow: hidden; }
  .hud.is-touch.o-trk .hud-right > .minimap { grid-area: 1 / 2 / 5 / 3; }
  .hud.is-touch.o-trk .hud-right > .fm { grid-area: 5 / 2; }
  .hud.is-touch.o-trk .hud-right > .st-line { grid-area: 2 / 1; }
  .hud.is-touch.o-trk .hud-right > .ev-chips { grid-area: 3 / 1; }
  .hud.is-touch.o-trk .hud-right > .tracker { grid-area: 4 / 1; }
}
/* The text column ends 236 px from the right edge at every width (the thumb cluster takes 226 px there): the gap grows as the minimap's column
 * narrows (210 px, 172 px up to 900 px wide, 120 px up to 640 px). */
@media (pointer: coarse) and (max-height: 520px) and (max-width: 900px) {
  .hud.is-touch.o-trk .hud-right { grid-template-columns: 210px 172px; column-gap: 48px; }
}
@media (pointer: coarse) and (max-height: 520px) and (max-width: 640px) {
  .hud.is-touch.o-trk .hud-right { grid-template-columns: 180px 120px; column-gap: 100px; }
}
/* PH-GATE: a touch screen wider than 940 px (a large tablet in landscape) keeps the action bar's column centred with a transform, and a
 * transformed ancestor is the containing block of a fixed element: the fixed chip went off the screen. There the chip stays at the end of the
 * clone bar (above the action bar), as on a desktop; the log's and the column's buttons keep the row. */
@media (pointer: coarse) and (min-width: 941px) {
  .hud.is-touch .fb-cl { position: relative; top: auto; left: auto; }
}

/* PH2, the phone jutsu bar (2026-11-01, the user's request after PH; docs/INTERFACE_AND_CONTROLS.md "Phones: the lock button, the order the
 * slots fill, the column"; js/ui/hud.js spot / press, R.lk). A touch HUD shows a jutsu slot once it is open: data-spot is the spot it took
 * (kept until a new game; the arc's angles in the phone block above, the bar's order on a touch screen wider than 940 px). The locked jutsu
 * wait behind the lock button (.jlk > .fb-lk, a PH fold: o-lk) in the next free spot; its list (.jlk-l) opens over the scene, clear of the
 * thumb controls: by default at the left under the row of four and above the stick (a portrait phone too low for the column), beside PH's
 * row in landscape, left of the column above the arc where the column stands (a match card ends 224 px from the right edge there), above
 * the lock button in a wide tablet's bar. */
.hud.is-touch .actionbar > .slot:not([data-spot]) { display: none; }
.hud.is-touch .actionbar > [data-spot="1"] { order: 1; }
.hud.is-touch .actionbar > [data-spot="2"] { order: 2; }
.hud.is-touch .actionbar > [data-spot="3"] { order: 3; }
.hud.is-touch .actionbar > [data-spot="4"] { order: 4; }
.hud.is-touch .actionbar > [data-spot="5"] { order: 5; }
.hud.is-touch .actionbar > [data-spot="6"] { order: 6; }
.hud.is-touch .actionbar > [data-spot="7"] { order: 7; }
.hud.is-touch .actionbar > .jlk { --slot: 40px; flex: none; align-self: center; width: var(--slot); height: var(--slot); pointer-events: none; }
.hud.is-touch .jlk > .fb { position: relative; top: auto; left: auto; width: 100%; height: 100%; font-size: 18px; }
.hud.o-lk .jlk > .fb { border-color: var(--accent); background: linear-gradient(180deg, #3d4790, #1d2452); }
.hud.is-ctl .jlk { display: none; }
.jlk-l { display: none; }
.hud.o-lk .jlk-l {
  position: fixed; z-index: 6; left: var(--gl); top: calc(100% - 364px); right: auto; bottom: auto;
  width: 140px; max-height: 154px; overflow: auto;
  display: flex; flex-direction: column; gap: 5px; padding: 7px 10px; pointer-events: auto;
}
.jlk-l p { margin: 0; font: 600 11.5px/1.3 var(--f-ui); color: var(--text-dim); }
.jlk-l b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 700 13px/1.3 var(--f-ui); color: var(--text); }
@media (pointer: coarse) and (max-height: 520px) {
  .hud.o-lk .jlk-l { left: calc(var(--gl) + 150px); top: calc(var(--gt) + 120px); width: min(240px, calc(100vw - var(--gl) - 390px)); max-height: calc(100% - var(--gt) - 192px); }
}
/* The column: on a portrait phone at least 764 px high (the S25+ in Chrome is about 788) and a touch screen at least 840 px high (a tablet:
 * its minimap is larger), Target, Charge, Sub and Sprint stand at the right edge, centred over Jump, top to bottom (Sprint 8 px above Jump,
 * 52 px apart); the top one stays under the minimap, its place label and the group chip. Lower screens keep the row (the four would reach the
 * minimap). While the right column's text or the group sheet is open the four step aside (that column would cover them; any tap outside
 * closes it and they are back). The centred texts (the centre lines, the banners, the cast bar, the prompt, a duel request) stay clear. */
@media (pointer: coarse) and (max-width: 640px) and (min-height: 764px), (pointer: coarse) and (min-width: 641px) and (min-height: 840px) {
  .wf-touch-sprint, .wf-touch-sub, .wf-touch-charge, .wf-touch-tab { right: calc(var(--wf-touch-btn-right, 14px) + 9px + env(safe-area-inset-right, 0px)); }
  .wf-touch-sprint { bottom: calc(var(--wf-touch-jump-bottom, 296px) + 72px + env(safe-area-inset-bottom, 0px)); }
  .wf-touch-sub { bottom: calc(var(--wf-touch-jump-bottom, 296px) + 124px + env(safe-area-inset-bottom, 0px)); }
  .wf-touch-charge { bottom: calc(var(--wf-touch-jump-bottom, 296px) + 176px + env(safe-area-inset-bottom, 0px)); }
  .wf-touch-tab { bottom: calc(var(--wf-touch-jump-bottom, 296px) + 228px + env(safe-area-inset-bottom, 0px)); }
  .hud.o-trk :is(.wf-touch-sprint, .wf-touch-sub, .wf-touch-charge, .wf-touch-tab),
  .hud:has(> .fm-sheet:not(.is-hidden)) :is(.wf-touch-sprint, .wf-touch-sub, .wf-touch-charge, .wf-touch-tab) { display: none; }
  .center-msgs { width: min(620px, calc(100vw - 152px)); }
  .hud.is-touch .banner { width: min(920px, calc(100vw - 152px)); }
  .hud.is-touch .castbar--player, .duel-req { width: min(380px, calc(100vw - 152px)); }
  .hud.is-touch .interact-prompt { max-width: calc(100vw - 152px); }
  .hud.o-lk .jlk-l { left: auto; top: auto; right: calc(var(--wf-touch-btn-right, 14px) + 66px); bottom: 345px; width: 200px; max-height: calc(100% - 345px - var(--gt) - 222px); }
}
@media (pointer: coarse) and (max-width: 640px) and (min-height: 764px) {
  body:has(> .mx-bar) .hud.o-lk .jlk-l { width: 142px; max-height: calc(100% - 545px); }   /* a phone's match card (match-ui.js) */
}
@media (pointer: coarse) and (min-width: 641px) and (min-height: 840px) {
  .hud.o-lk .jlk-l { width: 220px; }
}
/* A touch screen wider than 940 px: the bar at the bottom keeps its width (every key's room), the open jutsu from its left end, so a jutsu that
 * opens never moves another; the list opens above the lock button (the bar's column is transformed: no fixed place). */
@media (pointer: coarse) and (min-width: 941px) {
  .hud.is-touch .actionbar { justify-content: flex-start; min-width: calc(7 * var(--slot) + 6 * var(--slot-gap) + 18px); }
  .hud.is-touch .actionbar:has(> .slot:nth-child(8):not(.is-none)) { min-width: calc(8 * var(--slot) + 7 * var(--slot-gap) + 18px); }
  .hud.is-touch .actionbar:not(:has(> .slot:nth-child(7):not(.is-none))) { min-width: calc(6 * var(--slot) + 5 * var(--slot-gap) + 18px); }
  .hud.is-touch .actionbar > .jlk { position: relative; }
  .hud.o-lk .jlk-l { position: absolute; left: auto; top: auto; right: 0; bottom: calc(100% + 12px); width: 240px; max-height: 50vh; }
}
/* A 360 px wide phone: the arc's keys 4 and 5 reached into the stick (a 120 px ring at 18 px; before PH2 too): there the stick is 88 % of its
 * size from 12 px (its knob and its travel scale with it). */
@media (pointer: coarse) and (max-width: 380px) {
  .hud.is-touch { --wf-touch-stick-left: 12px; }
  .hud.is-touch .wf-touch-stick { transform: scale(0.88); transform-origin: 0 100%; }
}
