/* ══════════════════════════════════════════════════════════════════════════
   src/styles/shell.css — the World shell's windows, widgets and launcher row
   (docs/design/WORLD_SHELL.md; ART_DIRECTION.md §9 chrome, §10 type).
   Tokens only, not one colour literal. Windows wear FORGED STEEL (2026-10-24,
   below): the steel pixel 9-slices (assets/ui/steel-frame*.png, 6 and 3 art px =
   12 and 6 px at the x2 density), so framed boxes carry radius 0. Shadow only on
   windows (overlays); no glow. Type: Cinzel for window titles and section heads,
   --f-ui for the body (ART_DIRECTION §10). The HUD widgets keep the older iron
   frame until their lane moves them (hunt.css .hh-panel).
   ══════════════════════════════════════════════════════════════════════════ */

.hs-shell { position: absolute; top: 0; right: 0; bottom: 0; left: 0; pointer-events: none; font-family: var(--f-ui); font-size: var(--t-small); color: var(--ink); }
/* A window's `width` is its whole box, frame included: the dock and the inset add the same numbers. */
.hs-shell *, .hs-shell *::before, .hs-shell *::after { box-sizing: border-box; }
.hs-glyph { width: 22px; height: 22px; fill: currentColor; flex: none; }

/* ── Layers: world widgets (shifted clear of the dock), screen widgets, windows ── */
.hs-hud { position: absolute; top: 0; left: 0; bottom: 0; right: 0; pointer-events: none; z-index: 2; }
/* The screen widgets' box is the screen: clipped, so a widget wider than a phone (the icon row at 667 px) never
   widens the page into a sideways pan. */
.hs-screen { z-index: 6; overflow: clip; }
.hs-launchers:empty { display: none; }
.hs-layer { position: absolute; top: 0; left: 0; bottom: 0; right: 0; pointer-events: none; z-index: 3; }

/* ── Widgets: placed by src/shell/hud.js; faded per the layout, full on hover ── */
.hs-widget { position: absolute; pointer-events: auto; opacity: var(--hs-op, 1); transition: opacity 120ms ease-out; }
.hs-widget:hover, .hs-widget:focus-within { opacity: 1; }
/* No room beside the docked windows (src/shell/hud.js clearOfDock): the widget waits, never under a window. */
.hs-widget.is-under-dock { visibility: hidden; }
/* MOVE AND FOLD (src/shell/widget-move.js, Tyler 2026-10-25): a title that drags (desktop), a dash at its end that
   folds the widget to that title and a plus that opens it again. Drawn as the coach's fold is: two ink bars, no glyph. */
.hs-widget [data-hs-wdrag] { user-select: none; touch-action: none; }
.hs-widget [data-hs-wdrag]:not(button) { cursor: grab; }
.hs-widget.is-dragging { z-index: 6; opacity: 1; }
.hs-widget.is-dragging [data-hs-wdrag] { cursor: grabbing; }
.hs-wfold { flex: none; display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; background: none; color: var(--ink-3); cursor: pointer; }
.hs-wfold::before, .hs-wfold::after { content: ''; grid-area: 1 / 1; width: 10px; height: 2px; background: currentColor; }
.hs-wfold::after { display: none; }
.hs-wfold[aria-expanded="false"]::after { display: block; width: 2px; height: 10px; }
.hs-wfold:hover { color: var(--ink); }
.hs-wfold:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: -2px; }

/* ── Players online (widgets/online.js): one quiet line on the world, a small green dot, the server's number ── */
.hs-online { display: flex; align-items: center; gap: 6px; margin: 0; padding: 2px 8px; background: var(--hud-fill); font-size: var(--t-micro); color: var(--ink-2); white-space: nowrap; font-variant-numeric: lining-nums tabular-nums; }
.hs-online-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); }

/* ── Tamsin's tally (widgets/tally-hud.js): each held kill task's server count on a notched stick ── */
.hs-tally { display: flex; flex-direction: column; gap: 4px; max-height: var(--hs-room, none); padding: 4px 6px; background: var(--hud-fill);
  border: 6px solid transparent; border-image: url('../../assets/ui/steel-frame-thin.png') 3 / 6px stretch; image-rendering: pixelated; }
.hs-widget.is-tally.is-quiet { display: none; }
.hs-widget.is-boss-bar.is-quiet { display: none; }
.hs-boss { display: flex; flex-direction: column; gap: 4px; width: min(420px, 100%); padding: 4px 8px; background: var(--hud-fill); color: var(--ink); font-size: 13px; }
.hs-boss-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.hs-boss-name { color: var(--gold-2); }
.hs-boss-state { color: var(--ink-2); }
.hs-boss.is-raging .hs-boss-state, .hs-boss.is-wave .hs-boss-state { color: var(--gold-2); font-weight: 700; }
.hs-boss-track { position: relative; height: 12px; background: var(--world-shadow); }
.hs-boss-fill { position: absolute; inset: 0 auto 0 0; background: var(--gold); }
.hs-boss.is-raging .hs-boss-fill { background: var(--red); }
.hs-boss-mark { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--ink); }
.hs-tally-head { display: flex; align-items: flex-start; gap: 4px; }
.hs-tally-open { flex: 1; display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 0; border: 0; background: none; color: var(--ink); font: inherit; text-align: left; cursor: pointer; }
.hs-tally-head .hs-wfold { width: 22px; height: 22px; margin: -2px -2px 0 0; }
.hs-widget.is-folded .hs-tally-none, .hs-widget.is-folded .hs-tally-list { display: none; }
.hs-tally-open b { font-family: var(--f-display); font-weight: 700; font-size: var(--t-micro); letter-spacing: .1em; color: var(--gold-2); }
.hs-tally-open:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: 2px; }
.hs-tally-none { font-size: var(--t-micro); color: var(--ink-2); }
.hs-tally-none:empty { display: none; }
.hs-tally-list { min-height: 0; margin: 0; padding: 0; overflow-y: auto; list-style: none; }
.hs-tally-row { font-size: var(--t-micro); color: var(--ink); white-space: nowrap; font-variant-numeric: lining-nums tabular-nums; }
.hs-tally-wait { display: block; max-width: 28ch; white-space: normal; color: var(--gold-2); }
.hs-tally-wait:empty { display: none; }
/* The split tally stick: a notch every tenth of the count, the server's kills as the cut part; gold once done. */
.ht-stick { display: block; height: 8px; margin: 3px 0 2px; background-color: var(--bg-0); box-shadow: var(--steel-well);
  background-image: repeating-linear-gradient(90deg, transparent 0 calc(10% - 1px), var(--line-soft) calc(10% - 1px) 10%); }
.ht-stick i { display: block; height: 100%; background: var(--ink-2); }
.is-done > .ht-stick i, .is-done .ht-stick i { background: var(--gold); }

/* ── The launcher row: icons only, a tooltip with label and key, the open one lit gold ── */
.hs-launchers { display: flex; align-items: stretch; height: 40px; padding-left: 10px; background: var(--bg-1); border-left: 2px solid var(--steel-sep); }
.hs-launch { position: relative; width: 40px; height: 100%; padding: 0; display: grid; place-items: center; border: 0; background: none; cursor: pointer; color: var(--ink-3); font: inherit; }
.hs-launch:hover, .hs-launch:focus-visible { color: var(--ink); }
/* The first-minute pointer (launcher.js): one line hanging under the Hunts icon, the first in the row. */
.hs-launchers { position: relative; }
/* A Forged Steel popup (the thin steel frame, solid), the gold bar at its left: the game is speaking to you. */
.hs-pointer {
  position: absolute; top: calc(100% + 12px); left: 10px; z-index: 5; width: 260px; margin: 0; padding: 4px 8px 6px 14px;
  background: var(--bg-2); border: 6px solid transparent; border-image: url('../../assets/ui/steel-frame-thin.png') 3 / 6px stretch; image-rendering: pixelated;
  box-shadow: inset 4px 0 0 var(--gold), 0 6px 16px color-mix(in srgb, var(--world-shadow) 50%, transparent);
  font-size: var(--t-micro); line-height: 1.35; color: var(--ink-2); pointer-events: none;
}
/* In a hunt the card hangs under the hunt top bar (hunt-topbar.js), inside that widget's own box. */
.hs-widget > .hs-pointer { left: 0; width: min(260px, 100%); }
.hs-pointer b { display: block; font-family: var(--f-display); font-weight: 700; letter-spacing: .06em; color: var(--gold-2); }
/* The pointer's tip stands on the frame's top edge (the frame is 6 px). */
.hs-pointer::before { content: ''; position: absolute; top: -12px; left: 10px; border: 6px solid transparent; border-top: 0; border-bottom-color: var(--gold); }
/* The coach folds (launcher.js coachFoldOf): a dash at the card's corner folds it to its title, a tab that opens it;
   a new card while folded marks the tab with the bag's "new" square. Only the two controls take presses. */
.hs-pointer:not(.is-min) { padding-right: 34px; }
.hs-coach-min { position: absolute; top: 2px; right: 2px; width: 28px; height: 28px; display: grid; place-items: center; padding: 0; border: 0; background: none; color: var(--ink-3); cursor: pointer; pointer-events: auto; }
.hs-coach-min::after { content: ''; width: 10px; height: 2px; background: currentColor; }
.hs-coach-min:hover, .hs-coach-tab:hover { color: var(--ink); }
.hs-pointer.is-min, .hs-widget > .hs-pointer.is-min { width: auto; padding: 0; }
.hs-coach-tab { display: flex; align-items: center; gap: 6px; min-height: 28px; padding: 0 10px; border: 0; background: none; font-family: var(--f-display); font-weight: 700; font-size: var(--t-micro); color: var(--gold-2); white-space: nowrap; cursor: pointer; pointer-events: auto; }
.hs-coach-new { width: 6px; height: 6px; flex: none; background: var(--gold-2); box-shadow: 0 0 0 1px var(--bg-0); }
.hs-coach-min:focus-visible, .hs-coach-tab:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: -2px; }
/* A card a read finishes (launcher.js coachHtml) carries a close, open or folded: a small cross that records the read.
   Open, it takes the corner and the dash steps left of it; folded, it sits after the tab. */
.hs-pointer.has-close:not(.is-min) { padding-right: 62px; }
.hs-pointer.has-close:not(.is-min) > .hs-coach-min { right: 30px; }
.hs-pointer.is-min.has-close { display: flex; align-items: stretch; }
.hs-coach-close { position: absolute; top: 2px; right: 2px; width: 28px; height: 28px; padding: 0; border: 0; background: none; color: var(--ink-3); cursor: pointer; pointer-events: auto; }
.hs-pointer.is-min > .hs-coach-close { position: relative; top: auto; right: auto; flex: none; }
.hs-coach-close::before, .hs-coach-close::after { content: ''; position: absolute; top: 50%; left: 50%; width: 10px; height: 2px; margin: -1px 0 0 -5px; background: currentColor; transform: rotate(45deg); }
.hs-coach-close::after { transform: rotate(-45deg); }
.hs-coach-close:hover { color: var(--ink); }
.hs-coach-close:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: -2px; }
/* A card that only teaches finishes on its Got it (launcher.js coachHtml): a small quiet press at its foot, right. */
.hs-coach-got { display: block; margin: 6px 0 0 auto; min-height: 28px; padding: 0 12px; border: 1px solid var(--gold); border-radius: 0; background: none; color: var(--gold-2); font-family: var(--f-label); font-weight: 700; font-size: var(--t-micro); cursor: pointer; pointer-events: auto; }
.hs-coach-got:hover { background: var(--bg-3); color: var(--ink); }
.hs-coach-got:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: 2px; }
/* The card inside the window it points into (shell.js .hs-win-coach, hunt-tutorial.js coachWindow): between the title band
   and the body, so it covers no control; the ring shows the control, so it needs no tip. */
.hs-win-coach { flex: none; }
.hs-win-coach:empty { display: none; }
.hs-win-coach > .hs-pointer { position: relative; top: auto; left: auto; z-index: auto; width: auto; margin: 8px 12px 0; box-shadow: inset 4px 0 0 var(--gold); }
.hs-win-coach > .hs-pointer::before { display: none; }
/* One line (launcher.js coachHtml): the title, the words up to the line's end, More when they do not fit, Got it. The window's rows stay. */
.hs-win-coach > .hs-pointer.is-line { display: flex; align-items: center; gap: 8px; padding-block: 0; white-space: nowrap; }
.hs-win-coach > .hs-pointer.is-line > b { flex: none; }
.hs-win-coach > .hs-pointer.is-line > .hs-coach-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.hs-win-coach > .hs-pointer.is-line > .hs-coach-got { flex: none; margin: 0; min-height: 24px; }
.hs-coach-more { flex: none; min-height: 28px; padding: 0 6px; border: 0; background: none; color: var(--gold-2); font-family: var(--f-label); font-weight: 700; font-size: var(--t-micro); cursor: pointer; pointer-events: auto; }
.hs-coach-more:hover { color: var(--ink); }
.hs-coach-more:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: -2px; }
/* What next's other goals, behind More (launcher.js coachHtml): one to a line, under the goal the card names. */
.hs-coach-also { display: block; }
.hs-coach-also[hidden] { display: none; }
.hs-coach-goal { display: block; margin-top: 6px; }
/* The card says what to press on the Hunts window, so its own how-to line stands aside (a running hunt's line stays). */
[data-hs-win="hunts"]:has(.hs-win-coach .hs-pointer) .hw-hunts-now.is-hint { display: none; }
/* THE COACH'S RING (launcher.js ringCoach and renderLaunchers): the one control the card names, ringed in gold,
   a pixel line clear of its edge; it blinks a few times as it lands, then holds still (no glow, never a pulse: the
   press-ack ring below is the one that pulses). A list row or a bar slot rings inside its own edge (a list's scroll
   box would cut an outer ring), inside the picked row's own gold outline. */
[data-coach-ring] { outline: 2px solid var(--gold-2); outline-offset: 3px; animation: hs-coach-ring 400ms steps(1) 6; }
:is(.hw-skill, .hb-slot)[data-coach-ring] { outline-offset: -4px; }
.hw-hunt[data-coach-ring] { outline-offset: -6px; }
@keyframes hs-coach-ring { 50% { outline-color: var(--world-shadow); } }
@media (prefers-reduced-motion: reduce) { [data-coach-ring] { animation: none; } }
.hs-launch:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: -4px; }
.hs-launch.is-open { color: var(--gold-2); }
.hs-launch.is-open::after { content: ''; position: absolute; left: 10px; right: 10px; bottom: 0; height: 2px; background: var(--gold); }
.hs-launch::before {
  content: attr(data-tip) "  " attr(data-key);
  position: absolute; top: calc(100% + 6px); right: 0;
  padding: 3px 8px; white-space: pre;
  font-family: var(--f-ui); font-size: var(--t-micro); font-weight: 700; color: var(--ink);
  background: var(--bg-2); border: 6px solid transparent; border-radius: 0;
  border-image: url('../../assets/ui/steel-frame-thin.png') 3 / 6px stretch; image-rendering: pixelated;
  opacity: 0; pointer-events: none; transition: opacity 120ms ease-out;
}
.hs-launch:hover::before, .hs-launch:focus-visible::before { opacity: 1; }
/* NO KEYS AND NO HOVER ON TOUCH (phone playtest 2026-10-31): no key chip on a sheet's head, no tooltip stuck on the icon a
   tap focused. A held icon says its name instead (launcher-names.js); a press on a grey button says why (off-why.js). */
:root.is-touch .hs-win-head .hs-key, :root.is-touch .hs-launch::before { display: none; }
/* Every key on the Controls page is one a touch screen does not have. */
:root.is-touch .st .st-tab[data-cat="controls"] { display: none; }
.hs-launch-name, .hs-off-why { position: fixed; z-index: 80; box-sizing: border-box; width: max-content; max-width: min(320px, calc(100vw - 8px)); margin: 0; padding: 2px 8px; pointer-events: none;
  background: var(--bg-2); border: 6px solid transparent; border-image: url('../../assets/ui/steel-frame-thin.png') 3 / 6px stretch; image-rendering: pixelated;
  font-family: var(--f-ui); font-size: var(--t-micro); line-height: 1.35; color: var(--ink); }
.hs-launch-name { font-weight: 700; }
.hs-launch { -webkit-touch-callout: none; }

/* ══ FORGED STEEL: the one chrome (Tyler approved the skin on 2026-10-24; ART_DIRECTION §9, §10) ══
   Every window wears one thin steel frame with a square stud in each corner (assets/ui/steel-frame.png, 6 art
   px = 12 px; tools/make-ui-steel-art.mjs). Its fill is a charcoal floor under a steel grain, ONE layer that the
   Window opacity setting fades (--panel-alpha, tokens.css §8); the frame, the rules, the gold and every word stay
   solid. A dark title band ruled in dim gold carries the window's glyph and its name in Cinzel. Inside: section
   heads in Cinzel over a rule running right; the picked row is lifted, outlined in gold with a gold bar at its
   left; fields are sunk wells; the one primary is a raised gold plate (assets/ui/gold-plate.png). The HUD panels
   and the account screens reuse these pieces (the shared selectors below). */

/* ── Windows: one frame for all, docked right by the window manager ── */
.hs-win {
  position: absolute; top: 12px;
  max-height: calc(100% - 24px);
  display: flex; flex-direction: column;
  pointer-events: auto;
  isolation: isolate;
  background: none;
  border: 12px solid transparent;
  border-image: url('../../assets/ui/steel-frame.png') 6 / 12px stretch;
  border-radius: 0;
  image-rendering: pixelated;
  box-shadow: 0 10px 28px color-mix(in srgb, var(--world-shadow) 55%, transparent);
}
/* The floor and its grain: one layer under everything in the window, faded by the opacity setting alone. */
.hs-win::before {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: url('../../assets/ui/steel-grain.png') 0 0 / 64px 64px repeat, var(--bg-1);
  image-rendering: pixelated; opacity: var(--panel-alpha);
}
.hs-win.is-floating { box-shadow: 0 14px 36px color-mix(in srgb, var(--world-shadow) 65%, transparent); }
/* A modal row (Configure action): centred over the World, never docked, never moving the camera. */
.hs-win.is-modal { left: 50%; top: 50%; right: auto; transform: translate(-50%, -50%); max-width: calc(100% - 16px); max-height: calc(100% - 16px); }
/* Dragged by its head (desktop, Tyler 2026-10-27): then it sits at the manager's left/top, no longer centred. */
.hs-win.is-modal.is-moved { transform: none; }
.hs-win.is-modal.is-entering, .hs-win.is-modal.is-leaving { animation: none; }
/* Motion: a 120 ms slide and fade from the dock edge, and back out; never a bounce. */
@keyframes hs-in { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: none; } }
@keyframes hs-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateX(16px); } }
.hs-win.is-entering { animation: hs-in 120ms ease-out both; }
.hs-win.is-leaving { animation: hs-out 120ms ease-in both; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .hs-win.is-entering, .hs-win.is-leaving, .hs-widget, .hs-launch::before { animation: none; transition: none; }
}
/* The title band: darker than the floor, ruled off in dim gold; the window's glyph and name in gold. */
.hs-win-head {
  display: flex; align-items: center; gap: 10px;
  min-height: 44px; padding: 0 6px 0 12px; flex: none;
  background: var(--fill-0);
  border-bottom: 2px solid var(--steel-rule);
  color: var(--gold-2);
  cursor: grab; user-select: none; touch-action: none;
}
.hs-win-head .hs-glyph { width: 22px; height: 22px; }
/* The window's name in Cinzel (Tyler approved Forged Steel 2026-10-24: a characterful heading over a readable body). A long name takes a second
   line inside the band, never an ellipsis ("Gathering and C…", "Too many heroes playi…", Tyler's live play 2026-11-01). */
.hs-win-title { margin: 0; flex: 1; min-width: 0;
  font-family: var(--f-display); font-weight: 700; font-size: var(--t-lead); line-height: 1.1; letter-spacing: .12em; color: var(--gold-2); }
/* Square steel buttons: the window's key and its close; the key style also caps a hotkey in Settings. */
.hs-key { display: inline-grid; place-items: center; min-width: 32px; height: 32px; padding: 0 6px; box-sizing: border-box;
  border: 2px solid var(--steel-btn); border-radius: 0; background: var(--fill-1);
  font-family: var(--f-display); font-weight: 700; font-size: var(--t-micro); line-height: 1; color: var(--ink-2); }
.hs-close { width: 32px; height: 32px; display: grid; place-items: center; padding: 0; cursor: pointer;
  border: 2px solid var(--steel-btn); border-radius: 0; background: var(--fill-1); color: var(--ink-2); }
.hs-close:hover { color: var(--ink); background: var(--bg-3); }
.hs-close:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: -2px; }
.hs-close .hs-glyph { width: 18px; height: 18px; }
.hs-win-body { overflow: auto; padding: 10px 12px 12px; }

/* ── Forged Steel, the shared pieces (windows here; the HUD panels and the account screens add their selectors) ── */
/* A SECTION HEAD: Cinzel in gold, letter-spaced, a dim gold rule running to the right edge. Each head keeps its
   own margins (windows.css, character.css); line-height 1.2 keeps its height where the old face had it. */
.hw-hd-h, .hw-ledger-head.is-on, .hw-slothead, .hw-skill-head, .hs-sub, .hw-cs-head.is-on, .hw-cs-head:first-child {
  display: flex; align-items: center; gap: 12px;
  font-family: var(--f-display); font-weight: 700; font-size: var(--t-micro); line-height: 1.2; letter-spacing: .14em; color: var(--gold);
}
.hw-hd-h::after, .hw-ledger-head.is-on::after, .hw-slothead::after, .hw-skill-head::after, .hs-sub::after, .hw-cs-head.is-on::after, .hw-cs-head:first-child::after {
  content: ''; flex: 1; height: 2px; background: var(--steel-rule);
}
/* A SUNK FIELD: a well one art px deep, never a hairline box (search, selects, amounts, names, the report box). */
.hw-search, .hs-win .hh-select, .hs-win .hh-num, .st-search, .st-select, .hf-name, .hc-field, .rp-text {
  border: 0; border-radius: 0; background: var(--bg-0); box-shadow: var(--steel-well);
}
/* THE ONE PRIMARY: a raised gold plate (pixel art, a lip along its foot), pressed when pressed; its word in Cinzel. */
.hw-primary, .hh-tool.is-primary {
  min-height: 40px; padding: 0 14px;
  border: solid transparent; border-width: 6px 6px 10px; border-radius: 0;
  border-image: url('../../assets/ui/gold-plate.png') 3 3 5 3 fill / 6px 6px 10px stretch; image-rendering: pixelated;
  background: none; color: var(--bg-0);
  font-family: var(--f-display); font-weight: 700; font-size: var(--t-small); letter-spacing: .06em;
}
.hw-primary:hover:not(:disabled), .hh-tool.is-primary:hover:not(:disabled) { background: none; color: var(--bg-0); filter: brightness(1.08); }
.hw-primary:active:not(:disabled), .hh-tool.is-primary:active:not(:disabled) { border-image-source: url('../../assets/ui/gold-plate-down.png'); }
.hw-primary:disabled, .hh-tool.is-primary:disabled { opacity: .5; cursor: default; filter: none; }
/* A POPUP (an item's tooltip, a right-click menu): solid, in the thin steel frame (3 art px = 6 px). */
.hw-tip, .hw-menu {
  background: var(--bg-2); border: 6px solid transparent; border-radius: 0;
  border-image: url('../../assets/ui/steel-frame-thin.png') 3 / 6px stretch; image-rendering: pixelated;
}
/* A SLIDER (the window opacity, the HUD layout's fades): a sunk track and a small gold plate to drag, its value
   written beside it; never the browser's own control. */
.hs-win input[type="range"] { -webkit-appearance: none; appearance: none; height: 14px; margin: 0; padding: 0 1px; background: var(--bg-0); box-shadow: var(--steel-well); cursor: pointer; }
.hs-win input[type="range"]::-webkit-slider-runnable-track { height: 14px; background: none; }
.hs-win input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 14px; border: 0; border-radius: 0;
  background: var(--gold-3); box-shadow: inset 2px 2px 0 var(--gold-2), inset -2px -2px 0 var(--bg-0); }
.hs-win input[type="range"]::-moz-range-track { height: 14px; background: none; }
.hs-win input[type="range"]::-moz-range-thumb { width: 12px; height: 14px; border: 0; border-radius: 0;
  background: var(--gold-3); box-shadow: inset 2px 2px 0 var(--gold-2), inset -2px -2px 0 var(--bg-0); }
.hs-win input[type="range"]:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: 2px; }
.st-val { min-width: 44px; text-align: right; font-weight: 700; color: var(--ink); font-variant-numeric: lining-nums tabular-nums; }
/* A CONFIRMATION in a window, or the answer to a press: a sunk well with the gold bar at its left (the picked row's mark). */
.hw-confirm, .hs-win .hh-intent-msg:not(:empty, .ca-msg), .hw-temper-msg { background: var(--bg-0); box-shadow: var(--steel-well); border-left: 4px solid var(--gold); }
/* A home window (Chat) is held to the World's bottom-left by its bottom edge; a sized one fills its frame.
   Its grip sits on the frame's top-right corner: drag right to widen, up to grow taller (window-sizes.js). */
.hs-win.is-home { top: auto; }
.hs-win.is-sized .hs-win-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.hs-grip { position: absolute; top: -8px; right: -8px; width: 16px; height: 16px; cursor: nesw-resize; touch-action: none; border-top: 2px solid var(--line-strong); border-right: 2px solid var(--line-strong); }
.hs-grip:hover { border-color: var(--gold); }
.hs-note { margin: 0 0 8px; font-size: var(--t-micro); color: var(--ink-2); line-height: 1.4; }

/* ── Rename in place (src/shell/inline-name.js): the name is a quiet button; while naming, the field takes its place ── */
.hs-name { display: block; min-width: 0; max-width: 100%; padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: text; }
.hs-name-t { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; color: var(--ink); border-bottom: 1px dotted transparent; }
.hs-name:hover:not(:disabled) .hs-name-t { border-bottom-color: var(--ink-3); }
.hs-name:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: 1px; }
.hs-name:disabled { cursor: default; }
.hs-name-edit { display: none; width: 100%; min-width: 0; }
.is-naming .hs-name { display: none; }
.is-naming .hs-name-edit { display: block; width: 100%; }

/* ── The HUD layout window: one ruled line per widget, then Reset ── */
.hs-hudset { margin: 0; padding: 0; list-style: none; }
.hs-hudset-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0 10px; min-height: 36px; border-top: 1px solid var(--line-soft); }
.hs-hudset-show { flex: 1 1 150px; font-size: var(--t-small); color: var(--ink-2); }
/* A moved panel's Put back (hud.js): its own line under the row, at the right, never squeezing the name. */
.hs-hudset-put { min-height: 32px; margin: 0 0 8px auto; padding: 0 10px; }
.hs-hudset-show small { font-size: var(--t-micro); color: var(--ink-3); }
.hs-hudset-row input { accent-color: var(--gold); }
.hs-hudset-row input[type="range"] { width: 110px; }
.hs-hudset-pct { width: 44px; text-align: right; font-size: var(--t-micro); font-weight: 700; color: var(--ink); font-variant-numeric: lining-nums tabular-nums; }
.hs-foot { display: flex; justify-content: flex-end; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line-soft); }
.hs-quiet { min-height: 40px; padding: 0 14px; border: 1px solid var(--line-soft); border-radius: var(--r); background: none; color: var(--ink-2); font-family: var(--f-label); font-weight: 700; font-size: var(--t-small); cursor: pointer; }
.hs-quiet:hover { color: var(--ink); border-color: var(--line); }
.hs-quiet:focus-visible { outline: 2px solid var(--aqua-2); outline-offset: 2px; }

.ht-why { display: none; }
/* ── Phone, landscape: a scaled desktop with ONE full-height side sheet ── */
@media (max-width: 540px), (max-height: 540px) and (max-width: 1024px) {
  /* The row gives 60 px back at the left for the strip's one word (app.css): at 667 px each icon gives up its last 4 px,
     and a row of more icons narrows them rather than covering it. */
  .hs-launch { flex: 0 1 auto; width: 44px; min-width: 0; }
  .hs-launchers { padding-left: 4px; max-width: calc(100vw - 60px); }
  .hs-win-head .hs-key, .hs-launch::before { display: none; }
  /* IN TOWN THE CARD STANDS AT THE LEFT (phone playtest 2026-10-31: under the first icon it lay on the hero's name): below
     the place's name, clear of the hero at the centre and of the alerts at the right; the gold ring shows the icon, so
     no tip. A card about the bar keeps its own foot (below). */
  .hs-launchers > .hs-pointer { left: auto; right: calc(100vw - 268px); }
  .hs-launchers > .hs-pointer::before { display: none; }
  /* Open, it runs from the left edge to just short of the hero's column: wide words, few lines. */
  .hs-launchers > .hs-pointer:not(.is-min):not([data-anchor="action_bar"]) { width: min(340px, calc(50vw - 56px)); right: calc(100vw - 8px - min(340px, calc(50vw - 56px))); }
  .hs-launchers > .hs-pointer:not(.is-min) { top: calc(100% + 50px); max-height: calc(var(--coach-room) - 58px); }
  .hs-launchers > .hs-pointer:not(.is-min):not([data-anchor="action_bar"]) > .hs-coach-text { max-height: calc(round(down, var(--coach-room) - 114px - .5lh, 1lh) + .5lh); }
  /* An alert under the card leaves it too little room (shell.js roomForCoach): the card keeps its title until it closes. */
  .hs-widget.is-short-room > .hs-pointer:not(.is-min) > :is(.hs-coach-text, .hs-coach-got) { display: none; }
  .hs-widget.is-no-room > .hs-pointer { display: none; }
  /* While a leave counts down: what the wait is for, in one line under the hunt bar (hunt-topbar.js), the card aside. */
  .ht-why:not([hidden]) { display: block; position: absolute; top: 100%; right: 0; width: max-content; max-width: 100%; margin: 4px 0 0; padding: 2px 8px; background: var(--bg-2); font-size: var(--t-micro); color: var(--ink-2); }
  .hs-widget.is-hunt-topbar:has(.ht-why:not([hidden])) > .hs-pointer { display: none; }
  .hs-coach-close { width: var(--tap); height: var(--tap); }
  .hs-coach-tab { min-height: var(--tap); padding: 0 14px; }
  /* The phone folds a widget but never drags one (widget-move.js): its title is just a title, its fold thumb-sized. */
  .hs-widget [data-hs-wdrag] { touch-action: auto; }
  .hs-widget [data-hs-wdrag]:not(button) { cursor: default; }
  .hs-wfold, .hs-tally-head .hs-wfold { width: var(--tap); height: var(--tap); margin: 0; }
  /* EVERY OPEN CARD ON A PHONE IS ONE ROW OF TITLE AND PRESSES OVER ITS WORDS, AT THE CARD'S FULL WIDTH (phone panel v0.2.83:
     with the presses in a column down its side and Got it on a row of its own, 170 characters needed nine lines and were cut
     mid-sentence). A long card (a high Level's "What next") ends above the action bar: --coach-room is its host's foot to the
     bar's top (shell.js roomForCoach), less the card's 12 px drop and an 8 px gap. The title and the dash stay; the words
     scroll in their own box, which alone takes the thumb (the card lets presses through to the World). That box ends half a
     line in (the room less those 20 px, the frame's 12, the row's 40 and the foot's 4), so a cut line always says there is
     more: a phone's scrollbar shows only while it scrolls. The card's own cap holds where round() or lh is missing
     (tests/phone-touch.mjs PH-6). */
  .hs-pointer:not(.is-min), .hs-pointer.has-close:not(.is-min), .hs-win-coach > .hs-pointer:not(.is-min), .hs-win-coach > .hs-pointer.has-close:not(.is-min) { display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto; grid-template-rows: auto minmax(0, 1fr); align-items: center; padding: 0 0 4px 12px; }
  .hs-pointer:not(.is-min) > b { grid-area: 1 / 1; line-height: 1.2; }
  .hs-pointer:not(.is-min) > .hs-coach-got, .hs-win-coach > .hs-pointer:not(.is-min) > .hs-coach-got { grid-area: 1 / 2; margin: 0; min-height: 40px; padding: 0 8px; }
  .hs-pointer:not(.is-min) > .hs-coach-min { position: static; grid-area: 1 / 3; width: 40px; height: 40px; }
  /* relative, not static: its cross is drawn from its own middle. Static, the cross lay over the words in the middle of the card, and the button was blank (door panel 2026-11-04). */
  .hs-pointer:not(.is-min) > .hs-coach-close { position: relative; top: auto; right: auto; grid-area: 1 / 4; width: 40px; height: 40px; }
  /* Got it and the cross would say the same thing: one press for it, and the row's room for the title. */
  .hs-pointer:has(.hs-coach-got) > .hs-coach-close { display: none; }
  .hs-widget > .hs-pointer:not(.is-min), .hs-launchers > .hs-pointer:not(.is-min) { max-height: calc(var(--coach-room) - 20px); }
  .hs-pointer:not(.is-min) > .hs-coach-text {
    grid-area: 2 / 1 / 3 / -1; padding-right: 14px;
    min-height: 0; max-height: calc(round(down, var(--coach-room) - 76px - .5lh, 1lh) + .5lh); overflow-y: auto; overscroll-behavior: contain;
    pointer-events: auto;
    /* A cut line alone read as cut off, with "no way to read the rest": a gold tip in the box's corner, in the gutter its padding
       keeps, says the words go on. It is the box's own background, so it stays put as they scroll; ::after, one line tall and
       drawn back over the last line (under its words), covers it once that line is in view, so it shows only while more waits. */
    background:
      linear-gradient(to bottom left, var(--gold-2) 50%, transparent 50%) no-repeat right 7px bottom 2px / 5px 6px,
      linear-gradient(to bottom right, var(--gold-2) 50%, transparent 50%) no-repeat right 2px bottom 2px / 5px 6px;
  }
  .hs-pointer:not(.is-min) > .hs-coach-text::after { content: ''; display: block; height: 1lh; margin: -1lh -14px 0 0; background: var(--bg-2); }
  /* A line of room is all a card has to spare: a wide fallback font takes Alegreya's size, so its words end where they do in Alegreya. */
  .hs-pointer:not(.is-min) > .hs-coach-text { font-size-adjust: var(--f-ui-aspect); }
  /* A CARD ABOUT A SHEET STANDS BESIDE THE SHEET (phone panel v0.2.83: inside it, it took 60 px of the 166 a ground's page had,
     and cut its own words at a line and a half): where the town's card stands, in the World's half, so the sheet keeps its whole
     height and the words their room, and the card does not jump when a sheet opens. Its left is the sheet's own (min(400px, 48vw)
     wide, 8 px off the screen's right, its 12 px spine outside this box) brought back to 8 px; its top clears the place's name
     in town and the hunt bar in a hunt. A full-screen window (Configure action) has no World beside it: its card stays in it. */
  .hs-win-coach > .hs-pointer.is-line { gap: 0; white-space: normal; }
  .hs-win-coach > .hs-pointer.is-line > .hs-coach-text { overflow-y: auto; text-overflow: clip; }
  .hs-pointer.is-line > .hs-coach-more { display: none; }
  /* What next's other goals on a phone: a press away, scrolled inside the card's room (QA 2026-11-04: the one goal had no More). */
  .hs-coach-also { max-height: 96px; overflow-y: auto; }
  .hs-win-coach > .hs-pointer { margin: 6px 10px 0; }
  .hs-win-coach > .hs-pointer:not(.is-min) > .hs-coach-text { max-height: calc(round(down, 100vh / 3 - 100px, 1lh) + .5lh); }
  .hs-win:not(.is-modal) > .hs-win-coach > .hs-pointer { position: absolute; margin: 0; top: 50px; left: calc(min(400px, 48vw) + 5px - 100vw); }
  .hs-win:not(.is-modal) > .hs-win-coach > .hs-pointer:not(.is-min) { width: min(340px, calc(50vw - 56px)); max-height: calc(100vh - 200px); }
  .hs-win:not(.is-modal) > .hs-win-coach > .hs-pointer:not(.is-min) > .hs-coach-text { max-height: calc(round(down, 100vh - 256px - .5lh, 1lh) + .5lh); }
  #panel-world[data-scene="hunt"] .hs-win:not(.is-modal) > .hs-win-coach > .hs-pointer { top: 64px; }
  #panel-world[data-scene="hunt"] .hs-win:not(.is-modal) > .hs-win-coach > .hs-pointer:not(.is-min) { max-height: calc(100vh - 216px); }
  #panel-world[data-scene="hunt"] .hs-win:not(.is-modal) > .hs-win-coach > .hs-pointer:not(.is-min) > .hs-coach-text { max-height: calc(round(down, 100vh - 272px - .5lh, 1lh) + .5lh); }
  [data-hs-win="hunts"]:has(.hs-win-coach .hs-pointer) .hw-hunts-now { display: none; }
  /* The town card's own cap, past the general one above (same weight, later wins): its 50 px drop stays inside the room. */
  .hs-launchers > .hs-pointer:not(.is-min):not([data-anchor="action_bar"]) { max-height: calc(var(--coach-room) - 58px); }
  /* The card under the hunt bar stands at the bar's right end, its tip under Leave (first-hour playtest 10-29 #10:
     at the left end it lay over the hero mid-fight); the camera frames the World beside it (shell.js insetNow). */
  .hs-widget.is-hunt-topbar > .hs-pointer { left: auto; right: 0; }
  .hs-widget.is-hunt-topbar > .hs-pointer:not(.is-min) { width: min(320px, 100%); }
  .hs-widget.is-hunt-topbar > .hs-pointer::before { left: auto; right: 10px; }
  .hs-coach-got { min-height: var(--tap); }
  /* A card about the action bar stands just above the bar on a phone (board 2026-10-25 sample s7: under the hunt bar it
     hung far from its slot): its foot 8 px over the bar's top, the host's --coach-room below the host's own foot
     (shell.js roomForCoach). Desktop keeps it under its host. */
  .hs-widget > .hs-pointer[data-anchor="action_bar"]:not(.is-min), .hs-launchers > .hs-pointer[data-anchor="action_bar"]:not(.is-min) {
    top: auto; bottom: calc(8px - var(--coach-room)); max-height: calc(var(--coach-room) - 20px);
  }
  .hs-pointer[data-anchor="action_bar"]:not(.is-min)::before { display: none; }
  /* The sheet keeps the steel frame's spine down its left edge, a stud at each end; a phone dialog the thin frame. */
  .hs-win {
    top: 0; bottom: 0; right: 0; left: auto;
    width: min(400px, 48vw);
    max-height: none;
    border-width: 0 0 0 12px;
    border-image: url('../../assets/ui/steel-frame.png') 6 / 0 0 0 12px stretch;
  }
  /* A SHEET IS FULL HEIGHT IN TOWN TOO: up over the place's row (the 44 px Town and Home, the panel's 2 px, its 4 px gap) to the
     icon row, as it stands in a hunt, where that row is gone. It cost every sheet 50 px of a 340 px screen. The stage's clip lets
     it out while it is open (the panel's own clip still holds the page from panning). */
  #panel-world:not([data-scene="hunt"]) .hs-win:not(.is-modal) { top: calc(-1 * (var(--tap) + 6px)); }
  #panel-world:not([data-scene="hunt"]):has(.hs-win:not(.is-modal)) .world-stage { overflow: visible; }
  .hs-win.is-modal {
    top: 50%; bottom: auto; left: 50%; right: auto;
    max-width: calc(100vw - 16px); max-height: calc(100% - 8px);
    border-width: 6px;
    border-image: url('../../assets/ui/steel-frame-thin.png') 3 / 6px stretch;
  }
  .hs-win-head { min-height: 40px; cursor: default; }
  .hs-grip { display: none; }
  .hs-win-body { padding: 8px 10px 10px; }
  .st .st-tabs { width: 100%; border-right: 0; }
  .st-body { display: block; }
  .st-tab { min-height: 44px; border-left: 0; border-top: 1px solid var(--line-soft); }
  .st[data-view="list"] .st-pages, .st[data-view="page"] .st-tabs { display: none; }
  .st[data-view="page"] .st-back { display: block; min-height: 40px; padding: 0; border: 0; background: none; color: var(--gold); font: inherit; font-weight: 700; cursor: pointer; }
  .st-row .st-help { display: block; }
  .hs-hudset-row { min-height: 32px; gap: 6px; }
  .hs-hudset-show small { display: none; }
  .hs-hudset-row input[type="range"] { width: 72px; }
}

/* EVERY PRESS ANSWERS AT ONCE (src/shell/press-ack.js): a control whose ask is in flight carries a
   pulsing gold ring from the frame it was pressed until the server's answer and the re-read; a refused
   press flashes a red ring (the reason is the component's own message line). Any control, any screen. */
.is-sending { cursor: progress; outline: 2px solid var(--gold-2); outline-offset: 1px; animation: hs-sending 900ms ease-in-out infinite; }
/* The pressed control reads as working, not dead, while its window holds every control off. */
:is(button, select, input).is-sending:disabled { opacity: 1; cursor: progress; }
.is-refused { outline: 2px solid var(--red); outline-offset: 1px; }
@keyframes hs-sending { 50% { outline-color: var(--gold-3); } }
@media (prefers-reduced-motion: reduce) { .is-sending { animation: none; } }
