/* ── css/tokens.css */
/* Avatar Workbench design tokens (spec §15). Features use these tokens only, never literal colours. */
:root {
  color-scheme: light;
  /* surfaces */
  --bg: #F4F4F5;            --panel: #FFFFFF;          --panel-raised: #FFFFFF;   --panel-sunken: #EDEDF0;
  --line: #E3E3E6;          --line-strong: #CFCFD4;    --scrim: rgba(24,24,27,.32);
  /* text */
  --text: #18181B;          --text-dim: #5B5D66;       --text-mute: #6E7079;      --text-on-accent: #FFFFFF;
  /* accent & status */
  --accent: #4B5BEA;        --accent-hover: #3F4FDB;   --accent-weak: rgba(75,91,234,.10); --focus: #4B5BEA;
  --ok: #17804F;            --ok-weak: rgba(23,128,79,.10);
  --warn: #9A6212;          --warn-weak: rgba(154,98,18,.12);
  --err: #C53030;           --err-weak: rgba(197,48,48,.10);
  /* stage (theme-independent values, listed here for one place; the dark theme does not override them): a mid-light
     checker, like the thumbnail tile, so black hair, tails and wings and white shirts or particles all read on it in
     both themes (QA P9: the dark checker hid black items) */
  --stage-checker-a: #DCDEE2; --stage-checker-b: #CBCED3; --stage-checker-size: 16px;
  --stage-light: #F2F2F2;   --stage-grey: #808080;     --stage-dark: #141414;
  --stage-dim-outside: rgba(0,0,0,.55);                /* export framing */
  /* item thumbnails shown on their own (outfit rows, rail, Inspector): one neutral mid-light tile in both themes, so
     black hair and brows and white shirts or particles all stay readable, and it never glares in the dark theme */
  --thumb-tile: #C6C9CF;
  /* elevation */
  --shadow-1: 0 1px 2px rgba(0,0,0,.06), 0 1px 1px rgba(0,0,0,.04);
  --shadow-2: 0 4px 12px rgba(0,0,0,.10), 0 1px 3px rgba(0,0,0,.06);
  --shadow-3: 0 12px 32px rgba(0,0,0,.16), 0 2px 6px rgba(0,0,0,.08);
  /* type */
  --font-ui: 'Inter Variable', Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'JetBrains Mono Variable', 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --fs-11: 11px; --fs-12: 12px; --fs-13: 13px; --fs-15: 15px; --fs-18: 18px;
  --lh-tight: 1.25; --lh: 1.45;
  --fw-regular: 400; --fw-medium: 500; --fw-semibold: 600;
  --tracking-label: .04em;
  /* spacing (4 px base, 8 px grid) */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 20px; --sp-6: 24px; --sp-8: 32px;
  /* radii */
  --r-xs: 4px; --r-sm: 6px; --r-md: 8px; --r-lg: 10px; --r-pill: 999px;
  /* sizes. Panels (design 40 §B.3, §B.7): every right-side panel is one of two widths, and every panel header and
     footer is one 44 px bar (with its hairline), so the bottom edge is one band across the Outfit panel, the Pose bar
     and any right panel. --panel-inspector-w is the Inspector's default; a width the user drags wins (resize.js). */
  --topbar-h: 48px; --bar-h: 44px; --head-h: 44px; --posebar-h: calc(var(--bar-h) - 1px); --timeline-h: 112px;
  --row-h: 32px; --control-h: 28px;
  --panel-w: 360px; --panel-wide-w: 420px;
  --panel-outfit-w: 260px; --panel-inspector-w: var(--panel-w); --rail-w: 44px;
  --drawer-catalog-w: var(--panel-wide-w); --drawer-export-w: var(--panel-w); --drawer-library-w: var(--panel-w);
  --tabbar-h: 56px; --touch: 44px;
  /* motion (design 40 §B.2; mirrored in js/core/motion.js). Nothing appears, leaves, resizes or moves in one frame
     unless the pointer drives it; prefers-reduced-motion makes every move instant (base.css). */
  --dur-1: 100ms;          /* micro: hover/press colours, toggles, icon state, checkmarks, leaving popovers */
  --dur-2: 160ms;          /* small: popovers, menus, tooltips, toasts, tab content, inline reveals */
  --dur-3: 240ms;          /* panels: sidebars, drawers, docks, sheets, takeovers */
  --dur-3-exit: 200ms;     /* the same surfaces leaving */
  --ease-standard: cubic-bezier(.2, 0, 0, 1);   /* entering, moving, resizing */
  --ease-exit: cubic-bezier(.3, 0, 1, 1);       /* leaving */
  --shift-1: 4px;          /* popover, menu and tooltip entry offset */
  --shift-2: 8px;          /* tab content, toast and new-row entry offset */
  /* the earlier names, kept as aliases */
  --dur-fast: var(--dur-1); --dur: var(--dur-2); --dur-drawer: var(--dur-3); --ease-out: var(--ease-standard);
  /* layers */
  --z-stage-ui: 10; --z-panel: 20; --z-drawer: 30; --z-popover: 40; --z-modal: 50; --z-toast: 60;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #16171A;            --panel: #1D1E22;          --panel-raised: #25272C;   --panel-sunken: #131417;
    --line: #2E3036;          --line-strong: #3A3D44;    --scrim: rgba(0,0,0,.5);
    --text: #E8E8EA;          --text-dim: #9A9CA3;       --text-mute: #8A8D95;      --text-on-accent: #0E0F12;
    --accent: #7C8CFF;        --accent-hover: #91A0FF;   --accent-weak: rgba(124,140,255,.16); --focus: #7C8CFF;
    --ok: #3FB27F;            --ok-weak: rgba(63,178,127,.14);
    --warn: #E5A93B;          --warn-weak: rgba(229,169,59,.14);
    --err: #F06A6A;           --err-weak: rgba(240,106,106,.14);
    --shadow-1: 0 1px 2px rgba(0,0,0,.4);
    --shadow-2: 0 4px 12px rgba(0,0,0,.45), 0 1px 3px rgba(0,0,0,.3);
    --shadow-3: 0 12px 32px rgba(0,0,0,.55), 0 2px 6px rgba(0,0,0,.35);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #16171A;            --panel: #1D1E22;          --panel-raised: #25272C;   --panel-sunken: #131417;
  --line: #2E3036;          --line-strong: #3A3D44;    --scrim: rgba(0,0,0,.5);
  --text: #E8E8EA;          --text-dim: #9A9CA3;       --text-mute: #8A8D95;      --text-on-accent: #0E0F12;
  --accent: #7C8CFF;        --accent-hover: #91A0FF;   --accent-weak: rgba(124,140,255,.16); --focus: #7C8CFF;
  --ok: #3FB27F;            --ok-weak: rgba(63,178,127,.14);
  --warn: #E5A93B;          --warn-weak: rgba(229,169,59,.14);
  --err: #F06A6A;           --err-weak: rgba(240,106,106,.14);
  --shadow-1: 0 1px 2px rgba(0,0,0,.4);
  --shadow-2: 0 4px 12px rgba(0,0,0,.45), 0 1px 3px rgba(0,0,0,.3);
  --shadow-3: 0 12px 32px rgba(0,0,0,.55), 0 2px 6px rgba(0,0,0,.35);
}
@media (pointer: coarse) { :root { --row-h: 52px; --control-h: 44px; --fs-13: 15px; --bar-h: 52px; } }
/* Phones: a sheet's footer sits on the tab bar, so it is the tab bar's height (two bands of one height, U3). */
@media (pointer: coarse) and (max-width: 767px) { :root { --bar-h: var(--tabbar-h); } }

body { background: var(--bg); color: var(--text); }

/* ── css/base.css */
/* Reset, typography, focus ring and reduced motion (spec §15, §16). */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  font-family: var(--font-ui);
  font-size: var(--fs-13);
  line-height: var(--lh);
  font-weight: var(--fw-regular);
  font-feature-settings: 'cv11', 'ss01';
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow: hidden;
  overscroll-behavior: none;
}
h1, h2, h3, h4, h5, h6, p, ul, ol, dl, dd, figure { margin: 0; padding: 0; }
h1, h2, h3, h4 { font-size: inherit; font-weight: var(--fw-semibold); line-height: var(--lh-tight); }
ul, ol { list-style: none; }
img, svg, canvas, video { display: block; max-width: none; }
button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
button:disabled { cursor: default; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code, kbd, samp, pre, .wb-mono { font-family: var(--font-mono); font-size: var(--fs-12); }
kbd {
  display: inline-block; min-width: 18px; padding: 0 5px; border-radius: var(--r-xs);
  border: 1px solid var(--line-strong); background: var(--panel-sunken); color: var(--text-dim);
  font-size: var(--fs-11); line-height: 18px; text-align: center;
}
hr { border: 0; border-top: 1px solid var(--line); margin: var(--sp-2) 0; }
[hidden] { display: none !important; }
.wb-num, .wb-tabular { font-variant-numeric: tabular-nums; }
.wb-label {
  font-size: var(--fs-11); font-weight: var(--fw-semibold); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--text-mute);
}
.wb-dim { color: var(--text-dim); }
.wb-mute { color: var(--text-mute); }
.wb-sr-only {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Focus: a visible 2 px ring with a 2 px offset (spec §16). */
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--r-xs); }
input:focus-visible, textarea:focus-visible, select:focus-visible { outline-offset: 0; }

::selection { background: var(--accent-weak); }

/* Scrollbars stay quiet in both themes. */
* { scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; }

/* Reduced motion = instant (design 40 §B.1): every move, slide, fade and size change lands in its final state in the
   frame it happens (0s, not 1 ms: a 1 ms transition still paints its first frame at the start value). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0s !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0s !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
}

/* ── css/layout.css */
/* App grid, panels, stage frame, drawers and sheets (spec §4).
   Breakpoints: desktop ≥ 1280 · laptop 1024–1279 · tablet 768–1023 · mobile < 768.
   The shell mirrors state onto #app as data attributes:
     data-outfit="open|rail|hidden"   data-inspector="open|rail|hidden"   data-focus
     data-overlay-outfit / data-overlay-inspector (tablet overlays)        data-drawer="<id>"
     data-embed                                                          data-sheet="peek|half|full" (mobile) */

#app {
  --outfit-col: var(--panel-outfit-w);
  --insp-col: var(--rail-w);
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  position: relative;
  display: grid;
  height: 100vh;
  height: 100dvh;
  width: 100%;
  overflow: hidden;
  /* Not a scroll container at all: a focus or scrollIntoView inside a panel still entering from off-screen can never
     slide the whole app sideways (the first Catalog open did, design 40 §B.1). */
  overflow: clip;
  grid-template-columns: var(--outfit-col) minmax(0, 1fr) var(--insp-col);
  grid-template-rows: calc(var(--topbar-h) + var(--safe-top)) minmax(0, 1fr) auto;
  grid-template-areas:
    "top    top   top"
    "outfit stage insp"
    "outfit pose  insp";
  background: var(--bg);
}
#app[data-outfit="rail"]   { --outfit-col: var(--rail-w); }
#app[data-outfit="hidden"] { --outfit-col: 0px; }
#app[data-inspector="open"]   { --insp-col: var(--panel-inspector-w); }
#app[data-inspector="hidden"] { --insp-col: 0px; }
#app[data-focus] { --outfit-col: 0px; --insp-col: 0px; }
#app[data-focus] #panel-outfit,
#app[data-focus] #panel-inspector { display: none; }

#topbar {
  grid-area: top;
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--safe-top) calc(var(--sp-3) + var(--safe-right)) 0 calc(var(--sp-3) + var(--safe-left));
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  min-width: 0;
  z-index: var(--z-panel);
}
#topbar > [data-region="topbar.look"]    { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; flex: 0 1 auto; }
#topbar > [data-region="topbar.omnibox"] { display: flex; align-items: center; justify-content: center; flex: 1 1 auto; min-width: 0; }
#topbar > [data-region="topbar.actions"] { display: flex; align-items: center; gap: var(--sp-1); flex: 0 0 auto; margin-left: auto; }

#panel-outfit {
  grid-area: outfit;
  min-width: 0; min-height: 0;
  display: flex; flex-direction: column;
  background: var(--panel);
  border-right: 1px solid var(--line);
  overflow: hidden;
  z-index: calc(var(--z-panel) - 1);
}
#panel-outfit > [data-region="panel.outfit"] { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
#app[data-outfit="hidden"] #panel-outfit { display: none; }

/* The stage insets (the part of the stage a panel, sheet, the Timeline track or a banner covers) are animatable, so
   the toolbar, the HUD and the banner move with the panel that set them, in step with the camera (design 40 §B.2,
   §B.5; stage.js). A panel leaving uses the exit timing; a layout commit changes them at once (data-inset-instant). */
@property --stage-inset-top { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --stage-inset-right { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --stage-inset-bottom { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --stage-inset-left { syntax: '<length>'; inherits: true; initial-value: 0px; }
#stage {
  transition: --stage-inset-top var(--dur-3) var(--ease-standard), --stage-inset-right var(--dur-3) var(--ease-standard),
    --stage-inset-bottom var(--dur-3) var(--ease-standard), --stage-inset-left var(--dur-3) var(--ease-standard);
}
#stage[data-inset-exit] { transition-duration: var(--dur-3-exit); transition-timing-function: var(--ease-exit); }
#stage[data-inset-instant] { transition: none; }
#stage {
  grid-area: stage;
  position: relative;
  min-width: 0; min-height: 0;
  overflow: hidden;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

#posebar {
  grid-area: pose;
  min-width: 0;
  background: var(--panel);
  border-top: 1px solid var(--line);
  position: relative;
  z-index: calc(var(--z-panel) - 2);
}
#posebar:empty { display: none; }
/* A desktop drawer over the right end of the Pose bar: the bar keeps to the part it leaves (drawers.js, QA2 N6). */
#posebar { padding-right: max(var(--drawer-cover, 0px), var(--insp-cover, 0px)); }

#panel-inspector {
  grid-area: insp;
  min-width: 0; min-height: 0;
  display: flex; flex-direction: column;
  background: var(--panel);
  border-left: 1px solid var(--line);
  overflow: hidden;
  z-index: var(--z-panel);
}
#app[data-inspector="hidden"] #panel-inspector { display: none; }

/* ── Inspector frame (rail ↔ open) ───────────────────────────────────────── */
.wb-insp-rail { display: none; flex-direction: column; align-items: center; gap: var(--sp-1); padding: var(--sp-2) 0; }
.wb-insp-frame { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
#panel-inspector[data-state="rail"] .wb-insp-rail { display: flex; }
#panel-inspector[data-state="rail"] .wb-insp-frame { display: none; }
.wb-insp-head {
  display: flex; align-items: center; gap: var(--sp-1);
  height: var(--head-h); padding: 0 var(--sp-2) 0 var(--sp-4);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.wb-insp-head .wb-insp-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--fw-semibold); }
/* A selection change updates the Inspector's body in place: the old body dissolves over it (panels.js, an opaque
   ghost) while the new one settles in (design 40 §B.4). */
.wb-insp-body[data-swap] { animation: wb-swap-in var(--dur-2) var(--ease-standard); }
.wb-insp-body { flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; }
.wb-insp-foot--id .wb-foot-start { font-family: var(--font-mono); font-size: var(--fs-11); }
.wb-insp-empty { padding: var(--sp-6) var(--sp-4); color: var(--text-mute); text-align: center; display: grid; gap: var(--sp-3); justify-items: center; }

/* ── Panel resize handles (desktop and laptop; js/shell/resize.js): the inner edge of each open panel ─────── */
/* Both panels anchor their handle (below 1280 px the open Inspector is position: absolute instead, which anchors it too). */
#panel-outfit, #panel-inspector { position: relative; }
.wb-resize {
  position: absolute; top: 0; bottom: 0; width: 6px; z-index: 6;
  cursor: col-resize; touch-action: none; outline: none;
}
.wb-resize--outfit { right: -6px; }   /* over the panel's border and the stage edge, clear of the list's scrollbar */
.wb-resize--inspector { left: 0; }
.wb-resize::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: 2px;
  background: transparent; transition: background-color var(--dur) var(--ease-out);
}
.wb-resize--outfit::after { left: 0; }
.wb-resize--inspector::after { left: 0; }
/* The Outfit handle reaches past the panel's edge, so the panel doesn't clip (its content region clips itself). */
@media (min-width: 1024px) { #panel-outfit { overflow: visible; } }
.wb-resize:hover::after,
.wb-resize:focus-visible::after,
#app[data-resizing="outfit"] .wb-resize--outfit::after,
#app[data-resizing="inspector"] .wb-resize--inspector::after { background: var(--accent); }
#app[data-resizing], #app[data-resizing] * { cursor: col-resize !important; user-select: none; -webkit-user-select: none; }
#app[data-outfit="rail"] .wb-resize--outfit,
#panel-inspector:not([data-state="open"]) .wb-resize--inspector { display: none; }
@media (max-width: 1023px) { .wb-resize { display: none; } }

/* ── Drawers (desktop/tablet: right side; mobile: bottom sheet) ──────────── */
#drawers { position: absolute; inset: 0; pointer-events: none; z-index: var(--z-drawer); }
.wb-drawer {
  position: absolute;
  top: calc(var(--topbar-h) + var(--safe-top)); right: 0; bottom: 0;
  width: var(--drawer-w, var(--drawer-catalog-w));
  max-width: 100%;
  display: flex; flex-direction: column;
  background: var(--panel);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow-3);
  pointer-events: auto;
  transform: translateX(0);
  /* Right panels (design 40 §B.3, §B.4): slide in from the right edge over --dur-3; leave over --dur-3-exit. Switching
     panels keeps the slot: its width tweens and the content cross-fades (drawers.js). */
  transition: transform var(--dur-3) var(--ease-standard), height var(--dur-3) var(--ease-standard), width var(--dur-3) var(--ease-standard);
  border-top-left-radius: 0;
}
.wb-drawer[data-entering] { transform: translateX(100%); }
.wb-drawer[data-leaving] { transform: translateX(100%); transition-duration: var(--dur-3-exit); transition-timing-function: var(--ease-exit); pointer-events: none; }
/* The panel switching to another one: its old content fades out over the new (drawers.js morph). */
.wb-drawer-body { position: relative; }
.wb-drawer-body[data-swapping] > :not(.wb-swap-ghost) { animation: wb-swap-in var(--dur-2) var(--ease-standard) both; }
/* Over an open Inspector a drawer is at least as wide as it, so it covers the Inspector whole instead of leaving a strip
   of it cut mid-word beside the drawer (a widened Inspector, QA2 N6). Phones and tablets (sheets, overlays) are below. */
@media (min-width: 1024px) {
  #app[data-inspector="open"] .wb-drawer { width: max(var(--drawer-w, var(--drawer-catalog-w)), var(--panel-inspector-w)); }
}
/* The drawer itself takes the focus only when it has no control to focus (drawers.js): no ring around the whole panel. */
.wb-drawer:focus-visible { outline: none; }
.wb-drawer-head {
  display: flex; align-items: center; gap: var(--sp-2);
  height: var(--head-h); padding: 0 var(--sp-2) 0 var(--sp-4);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.wb-drawer-head .wb-drawer-actions { display: flex; align-items: center; gap: var(--sp-1); flex: 0 0 auto; }
.wb-drawer-head .wb-drawer-actions:empty { display: none; }
.wb-drawer-head .wb-drawer-title { flex: 1 1 auto; min-width: 0; font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-drawer-grab { display: none; }
.wb-drawer-body { flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; display: flex; flex-direction: column; }

/* ── Tab bar (mobile only) ───────────────────────────────────────────────── */
#tabbar { display: none; }

/* ── Overlay layers ─────────────────────────────────────────────────────── */
#popovers { position: fixed; inset: 0; pointer-events: none; z-index: var(--z-popover); }
#popovers > * { pointer-events: auto; }
#modals { position: fixed; inset: 0; pointer-events: none; z-index: var(--z-modal); }
#modals > * { pointer-events: auto; }
/* One stack holds the two live regions (toasts.js places it: centred over the free stage, above the bottom chrome and the
   HUD's reserved row). The newest toast is on top; when the chrome it avoids moves, the stack glides (the script
   switches the transition off for a toast's first placement and for a window resize). */
#toast-stack {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--sp-4) + var(--safe-bottom));
  display: flex; flex-direction: column-reverse; align-items: center;
  z-index: var(--z-toast); pointer-events: none;
  width: max-content; max-width: calc(100vw - 2 * var(--sp-4));
  transition: left var(--dur-3) var(--ease-standard), bottom var(--dur-3) var(--ease-standard), max-width var(--dur-3) var(--ease-standard);
}
#toasts, #toasts-assertive { display: flex; flex-direction: column-reverse; align-items: center; gap: var(--sp-2); max-width: 100%; }
/* (the live regions stay in the tree when empty: a region that is not rendered is not announced) */
#toasts:not(:empty) ~ #toasts-assertive:not(:empty) { margin-bottom: var(--sp-2); }
#toasts > *, #toasts-assertive > * { pointer-events: auto; }

/* Absolutely positioned panels below keep their grid-area, so their containing block is that
   grid area (rows 2–3), which already starts under the top bar. Anchor at top: 0; adding the
   top-bar height again left a blank strip above the panel. */

/* ── Laptop (1024–1279): outfit 240 docked, inspector overlays the stage edge ─ */
@media (max-width: 1279px) {
  #app { --panel-outfit-w: 240px; }
}

/* ── The open Inspector (≥ 1024 px): a right panel over the stage at every desktop and laptop width (design 40 §B.3).
      The 44 px rail is the only docked column on the right, so the stage never resizes when the Inspector opens:
      the frame slides in over it from the right edge (--dur-3) and out (--dur-3-exit), and reports its width as the
      stage's right inset (panels.js). The rail stays under it, out of reach until the frame has left. ── */
@media (min-width: 1024px) {
  #app[data-inspector="open"] { --insp-col: var(--rail-w); }
  #panel-inspector { overflow: visible; }
  #panel-inspector .wb-insp-rail { display: flex; }
  #panel-inspector[data-state="open"] .wb-insp-rail { visibility: hidden; transition: visibility 0s linear var(--dur-3); }
  #panel-inspector .wb-insp-frame {
    position: absolute; top: 0; right: 0; bottom: 0; width: var(--panel-inspector-w); max-width: calc(100vw - var(--rail-w));
    background: var(--panel); border-left: 1px solid var(--line); box-shadow: var(--shadow-3);
    display: none; transform: translateX(100%);
    transition: transform var(--dur-3-exit) var(--ease-exit), display var(--dur-3-exit) allow-discrete;
  }
  #panel-inspector[data-state="open"] .wb-insp-frame {
    display: flex; transform: none;
    transition: transform var(--dur-3) var(--ease-standard), display var(--dur-3) allow-discrete;
  }
  @starting-style { #panel-inspector[data-state="open"] .wb-insp-frame { transform: translateX(100%); } }
  /* The resize handle sits on the frame's inner edge. */
  .wb-resize--inspector { left: calc(var(--rail-w) - var(--panel-inspector-w)); }
  #app[data-resizing="inspector"] #panel-inspector .wb-insp-frame { transition: none; }

  /* The Outfit dock between open and rail (dock-motion.js, design 40 §B.3): the panel overlays the stage while it
     slides, then the grid column commits with the stage inset dropping in the same frame (nothing visibly moves). */
  #panel-outfit[data-dock] {
    position: absolute; left: 0; top: 0; bottom: 0; width: var(--dock-w, var(--panel-outfit-w));
    box-shadow: var(--shadow-3); z-index: calc(var(--z-panel) + 1);
    transition: transform var(--dur-3) var(--ease-standard);
  }
  #panel-outfit[data-dock="out"] { transform: translateX(var(--dock-shift, 0px)); }
  /* The JSON view widening or narrowing the panel (dock-motion.js setView): its width tweens over the stage. */
  #panel-outfit[data-dock="wide"] { transition: width var(--dur-3) var(--ease-standard); }
  #panel-outfit[data-dock="leaving"] { transform: translateX(var(--dock-shift, 0px)); transition-duration: var(--dur-3-exit); transition-timing-function: var(--ease-exit); }
  #panel-outfit[data-dock] .wb-resize--outfit { display: none; }
}

/* ── Tablet (768–1023): both panels are overlay drawers; stage full width ─── */
@media (max-width: 1023px) {
  #app, #app[data-outfit="rail"], #app[data-inspector="open"], #app[data-outfit="open"][data-outfit-view="json"] { --outfit-col: 0px; --insp-col: 0px; }
  #panel-outfit, #panel-inspector { display: none; }
  #app[data-overlay-outfit] #panel-outfit {
    display: flex; position: absolute; left: 0;
    top: 0; bottom: 0;
    width: min(320px, 90vw);
    box-shadow: var(--shadow-3); z-index: var(--z-drawer);
  }
  #app[data-overlay-inspector] #panel-inspector {
    display: flex; position: absolute; right: 0;
    top: 0; bottom: 0;
    width: min(var(--panel-inspector-w), 90vw);
    box-shadow: var(--shadow-3); z-index: var(--z-drawer);
  }
  #app[data-overlay-inspector] #panel-inspector .wb-insp-rail { display: none; }
  #app[data-overlay-inspector] #panel-inspector .wb-insp-frame { display: flex; }
}
/* Tablet overlays slide from their edge (design 40 §B.4): in over --dur-3, out over --dur-3-exit (the panel keeps its
   place until it has left: display changes at the end, allow-discrete). */
@media (min-width: 768px) and (max-width: 1023px) {
  #panel-outfit, #panel-inspector {
    position: absolute; top: 0; bottom: 0; box-shadow: var(--shadow-3); z-index: var(--z-drawer);
    transition: transform var(--dur-3-exit) var(--ease-exit), display var(--dur-3-exit) allow-discrete;
  }
  #panel-outfit { left: 0; width: min(320px, 90vw); transform: translateX(-100%); }
  #panel-inspector { right: 0; width: min(var(--panel-inspector-w), 90vw); transform: translateX(100%); }
  #app[data-overlay-outfit] #panel-outfit, #app[data-overlay-inspector] #panel-inspector {
    transform: none; transition: transform var(--dur-3) var(--ease-standard), display var(--dur-3) allow-discrete;
  }
  @starting-style {
    #app[data-overlay-outfit] #panel-outfit { transform: translateX(-100%); }
    #app[data-overlay-inspector] #panel-inspector { transform: translateX(100%); }
  }
}
/* Tablet top bar: the look name keeps its width (at most 180 px, omnibox.css) and the omnibox takes what is left; at
   900 px and below the omnibox is an icon (QA P15: at 900 px the name was squeezed to "M…"). */
@media (min-width: 768px) and (max-width: 1023px) {
  #topbar > [data-region="topbar.look"] { flex-shrink: 0; }
}

/* ── Mobile (< 768): 44 px top bar, stage, tab bar; drawers become sheets ── */
@media (max-width: 767px) {
  #app {
    --topbar-h: 44px;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: calc(var(--topbar-h) + var(--safe-top)) minmax(0, 1fr) auto calc(var(--tabbar-h) + var(--safe-bottom));
    grid-template-areas: "top" "stage" "pose" "tabs";
  }
  #app[data-overlay-outfit] #panel-outfit, #app[data-overlay-inspector] #panel-inspector { display: none; }
  #topbar { padding-left: calc(var(--sp-2) + var(--safe-left)); padding-right: calc(var(--sp-2) + var(--safe-right)); gap: var(--sp-1); }
  #topbar > [data-region="topbar.omnibox"] { flex: 0 0 auto; }
  #topbar > [data-region="topbar.look"] { flex: 1 1 auto; }
  /* The bar omnibox is desktop-only (phones open it as a sheet from the source pill): drop its empty slot and gap. */
  html[data-booted] #topbar > [data-region="topbar.omnibox"] { display: none; }
  #topbar > [data-region="topbar.actions"] > .wb-row > .wb-iconbtn { width: var(--touch); }   /* Undo / Redo: a full 44 px target (was 40: a tap 21 px from the centre missed) */

  #tabbar {
    grid-area: tabs;
    display: flex; align-items: stretch;
    padding: 0 var(--safe-right) var(--safe-bottom) var(--safe-left);
    background: var(--panel);
    border-top: 1px solid var(--line);
    z-index: calc(var(--z-drawer) + 1);
  }

  /* Sheets: peek 168 px · half 50dvh · full 100dvh − 44 */
  .wb-drawer {
    top: auto; left: 0; right: 0;
    bottom: calc(var(--tabbar-h) + var(--safe-bottom));
    width: 100%;
    height: var(--sheet-h, 50dvh);
    border-left: 0;
    border-top: 1px solid var(--line);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    box-shadow: var(--shadow-3);
    transform: translateY(0);
  }
  .wb-drawer[data-entering] { transform: translateY(100%); }
  .wb-drawer[data-leaving] { transform: translateY(100%); }
  .wb-drawer[data-detent="peek"] { --sheet-h: 168px; }
  .wb-drawer[data-detent="half"] { --sheet-h: 50vh; --sheet-h: 50dvh; }
  .wb-drawer[data-detent="tall"] { --sheet-h: 62vh; --sheet-h: 62dvh; }
  .wb-drawer[data-detent="full"] {
    --sheet-h: calc(100vh - 44px - var(--safe-top)); --sheet-h: calc(100dvh - 44px - var(--safe-top));
    bottom: 0; z-index: 2; border-radius: 0;
    padding-bottom: var(--safe-bottom);
  }
  #app[data-sheet="full"] #drawers { z-index: calc(var(--z-drawer) + 2); }
  .wb-drawer[data-dragging] { transition: none; }
  .wb-drawer-grab {
    display: flex; justify-content: center; align-items: center;
    height: 20px; flex: 0 0 auto; cursor: grab; touch-action: none;
  }
  .wb-drawer-grab::before { content: ''; width: 36px; height: 4px; border-radius: var(--r-pill); background: var(--line-strong); }
  .wb-drawer-head { height: var(--head-h); touch-action: none; }
  /* A re-parented panel fills its sheet. */
  .wb-drawer-body > #panel-outfit,
  .wb-drawer-body > #panel-inspector {
    display: flex !important; position: static !important; width: auto !important;
    border: 0; box-shadow: none; flex: 1 1 auto; min-height: 0;
  }
  .wb-drawer-body > #panel-inspector .wb-insp-rail { display: none; }
  .wb-drawer-body > #panel-inspector .wb-insp-frame { display: flex; }
  .wb-drawer-body > #panel-inspector .wb-insp-head { display: none; }   /* the sheet header carries the title and ✕ */
  /* One footer rule for phone sheets: a footer is there when it carries an action (Export's buttons, Library's, an item's
     copy-id); a status-only line (the Outfit count, the Character summary) is not chrome stacked on the tab bar — the
     Outfit count is in the sheet's title, and the sheet's list gets the 56 px back. */
  .wb-drawer-body .wb-insp-foot--status { display: none; }
  /* The Outfit footer keeps what the title does not say: "Filled by default: eyes, mouth" and the catalog warning (it has
     data-note while either is there). Without one it is closed, and it opens and closes by gliding, not by a jump. */
  .wb-drawer-body .wb-out-foot {
    height: 0; min-height: 0; max-height: 0; border-top-width: 0; opacity: 0; visibility: hidden;
    transition: height var(--dur-2) var(--ease-standard), min-height var(--dur-2) var(--ease-standard), max-height var(--dur-2) var(--ease-standard),
      opacity var(--dur-2) var(--ease-standard), border-top-width var(--dur-2) var(--ease-standard), visibility 0s linear var(--dur-2);
  }
  .wb-drawer-body .wb-out-foot[data-note] {
    height: var(--bar-h); min-height: var(--bar-h); max-height: var(--bar-h); border-top-width: 1px; opacity: 1; visibility: visible;
    transition-delay: 0s;
  }
  /* the count is in the sheet's title: the footer says only the notes, one after the other */
  .wb-drawer-body .wb-out-foot[data-note] > .wb-out-foot-count { display: none; }
  .wb-drawer-body .wb-out-foot[data-note] > span::before { content: none; }
  .wb-drawer-body .wb-out-foot[data-note] > span:not([hidden]) ~ span:not([hidden])::before { content: ' · '; white-space: pre; color: var(--text-mute); }
  #toast-stack { bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--sp-3)); }
  #app[data-sheet] #toast-stack { bottom: auto; top: calc(var(--topbar-h) + var(--safe-top) + var(--sp-2)); }
}

/* Embed (?embed=1): condensed chrome; follows the iframe's own width. */
#app[data-embed] .wb-hide-embed { display: none !important; }

/* Visibility helpers per breakpoint */
@media (max-width: 767px) { .wb-hide-mobile { display: none !important; } }
@media (min-width: 768px) { .wb-only-mobile { display: none !important; } }
@media (min-width: 1024px) { .wb-only-tablet { display: none !important; } }
@media (max-width: 767px) { .wb-only-tablet { display: none !important; } }
@media (pointer: coarse) and (hover: none) { .wb-hide-touch { display: none !important; } }

/* ── Boot skeleton (spec §14.1): until a region gets its content, it shows a quiet placeholder of the same size,
      so the chrome does not pop in after the first avatar pixels. Pure CSS; each disappears as its region fills. ── */
html:not([data-booted]) #topbar > [data-region]:empty::before {
  content: ''; display: block; height: 28px; border-radius: var(--r-sm); background: var(--panel-sunken);
}
html:not([data-booted]) #topbar > [data-region="topbar.look"]:empty::before { width: 220px; }
html:not([data-booted]) #topbar > [data-region="topbar.omnibox"]:empty::before { width: min(480px, 100%); height: 32px; border-radius: var(--r-md); }
html:not([data-booted]) #topbar > [data-region="topbar.actions"]:empty::before { width: 300px; }
html:not([data-booted]) [data-region="panel.outfit"]:empty {
  margin: 52px var(--sp-3) 0;
  background: repeating-linear-gradient(to bottom, var(--panel-sunken) 0 22px, transparent 22px 36px);
  opacity: .7;
}
@media (min-width: 768px) {
  html:not([data-booted]) #posebar:empty { display: block; height: var(--bar-h); }
}
@media (max-width: 767px) {
  html:not([data-booted]) #topbar > [data-region="topbar.look"]:empty::before { width: 140px; }
  html:not([data-booted]) #topbar > [data-region="topbar.omnibox"]:empty::before,
  html:not([data-booted]) #topbar > [data-region="topbar.actions"]:empty::before { width: 32px; }
}

/* ── Loading silhouette (spec F1, design 40 §D.1, §C.15, U4): while html[data-boot-scene] — the veil: from the first
      paint until the live avatar's first frame, a look loaded wholesale, a rig switch, the live avatar restarting — the
      stage shows the rig's body-flesh silhouette where the avatar will appear and the loading indicator below, never a
      picture of the avatar (owner, 2026-10-13: "I'd rather have a loading screen"). js/shell/silhouette.js places each
      rig's own through the stage camera once the stage is up, and fades it out as the avatar fades in. The
      masks are body-flesh rendered by the engine at Unity's large frame, so the shape is as crisp as an avatar; dark on
      the light stages (owner, 2026-10-14: "use the body-flesh in dark"), light on dark and grey, breathing between 60
      and 100 % (static under reduced motion). ── */
.wb-stage-view { --sil-tint: rgba(24, 24, 27, .52); }
.wb-stage-view[data-bg="grey"] { --sil-tint: rgba(24, 24, 27, .45); }
.wb-stage-view[data-bg="dark"] { --sil-tint: rgba(255, 255, 255, .22); }
/* The veil fades the stills out (--dur-2) as the silhouette fades in, never a blank frame. Stills are shown at all only
   where the live stage cannot draw (stage.js data-no-stills: a look only Unity draws, no WebGL2, Export's hold), and
   then only at Large or more. */
html[data-boot-scene] .wb-stage-view > img.wb-stage-media {
  visibility: hidden; opacity: 0;
  transition: opacity var(--dur-2) var(--ease-standard), visibility 0s linear var(--dur-2);
}
.wb-stage-view[data-no-stills] > :is(img.wb-stage-media, canvas.wb-stage-detail) { visibility: hidden !important; opacity: 0 !important; }
/* No silhouette at the first paint (owner, 2026-10-14: it was placed by CSS Fit rules, not the stage camera, so it sat
   off the floor grid and jumped to the session's body size when the app ran): the stage's own one (silhouette.js, placed
   by the stage transform the floor and the avatar use) fades in as soon as the stage is up. */
/* The saved session's stage background at the first paint (index.html html[data-boot-bg] from wb.boot; stage.js removes
   it once its own data-bg is set), so a dark session never flashes the checkerboard first. */
html[data-boot-bg="light"] .wb-stage-bg { background: var(--stage-light); }
html[data-boot-bg="grey"] .wb-stage-bg { background: var(--stage-grey); }
html[data-boot-bg="dark"] .wb-stage-bg { background: var(--stage-dark); }
html[data-boot-bg="custom"] .wb-stage-bg { background: var(--boot-stage-custom, var(--stage-grey)); }
/* A rig without a silhouette (no default outfit: pet_neopet, the purrlings rigs): the neutral veil. */
html[data-boot-scene][data-sil-none] .wb-stage-view::after {
  content: ''; position: absolute; left: 50%; top: 50%; pointer-events: none;
  height: min(72%, 640px); aspect-ratio: 1 / 2; transform: translate(-50%, -50%);
  background:
    radial-gradient(circle at 50% 20%, var(--line-strong) 0 18%, transparent 18.5%),
    radial-gradient(ellipse 28% 40% at 50% 64%, var(--line-strong) 0 97%, transparent 100%);
  opacity: .55;
  animation: wb-veil 1.2s var(--ease-standard) infinite alternate;
}
.wb-stage-silhouette {
  position: absolute; left: 0; top: 0; width: 0; height: 0; z-index: 2; pointer-events: none;
  background: var(--sil-tint);
  -webkit-mask: var(--sil-mask) center / 100% 100% no-repeat; mask: var(--sil-mask) center / 100% 100% no-repeat;
  opacity: 0; transition: opacity var(--dur-2) var(--ease-standard), background-color var(--dur-2) var(--ease-standard);
}
.wb-stage-silhouette[data-mirror] { transform: scaleX(-1); }
/* It fades in (--dur-2) as the old avatar fades out, then breathes; taking over from the first paint's CSS silhouette
   it carries on breathing in the same phase instead (silhouette.js: data-instant, --sil-phase). */
.wb-stage-silhouette[data-on] { opacity: 1; animation: wb-sil-in var(--dur-2) var(--ease-standard) both, wb-sil-breathe 1.2s ease-in-out var(--dur-2) infinite alternate; }
.wb-stage-silhouette[data-on][data-instant] { animation: wb-sil-breathe 1.2s ease-in-out var(--sil-phase, 0s) infinite alternate; }
@keyframes wb-sil-in { from { opacity: 0; } to { opacity: .6; } }
@keyframes wb-sil-breathe { from { opacity: .6; } to { opacity: 1; } }
@keyframes wb-veil { from { opacity: .35; } to { opacity: .65; } }
@media (prefers-reduced-motion: reduce) {
  html[data-boot-scene] .wb-stage-view::after, .wb-stage-silhouette[data-on] { animation: none; }
}

/* ── Loading indicator (design 40 §C.15): a ring and one line of text in the middle of the stage, over the silhouette,
      and the seconds since it started once that is 5 s or more. Part of the first paint (index.html #stage-loading, CSS
      only: it shows before the app's bundle has loaded, its seconds counted by a registered custom property); stage.js
      then drives it (data-js): the phase the start-up is in, "Loading @name…", "Restarting the live avatar…", an error
      with Retry. It shows while the veil is up and no card in the middle of the stage says why instead (an empty look,
      one that needs Unity, new items being fetched). It fades in after 300 ms, so a fast load never flashes it, and
      fades out (--dur-2) as the live avatar fades in. Through one wait it never narrows and widens smoothly (stage.js
      sizeLoading sets its width), its content centred: no jump at each new phase or when the seconds appear. ── */
@property --wb-load-s { syntax: '<integer>'; inherits: false; initial-value: 0; }
.wb-stage-loading {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 4;
  display: flex; align-items: center; justify-content: center; gap: 10px; max-width: calc(100% - 32px); min-height: 36px; box-sizing: border-box;
  padding: 6px 14px; border-radius: var(--r-pill);
  background: var(--panel-raised); border: 1px solid var(--line); box-shadow: var(--shadow-2);
  color: var(--text); font-size: var(--fs-13); line-height: 18px; white-space: nowrap; font-variant-numeric: tabular-nums;
  pointer-events: none; opacity: 0; visibility: hidden;
  transition: opacity var(--dur-2) var(--ease-standard), visibility 0s linear var(--dur-2), width 200ms var(--ease-standard);
}
@media (max-width: 767px) { .wb-stage-loading { left: calc((100% - 66px) / 2); max-width: calc(100% - 66px - 24px); } }
.wb-stage-loading-ring {
  flex: none; width: 14px; height: 14px; box-sizing: border-box; border-radius: 50%;
  border: 2px solid var(--line-strong); border-top-color: var(--accent); animation: wb-spin .9s linear infinite;
}
.wb-stage-loading-text { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.wb-stage-loading-time { flex: none; color: var(--text-dim); }
.wb-stage-loading-time:empty { display: none; }
.wb-stage-loading[data-error] { color: var(--err); border-color: var(--err); }
.wb-stage-loading[data-error] .wb-stage-loading-ring { display: none; }
.wb-stage-loading .wb-stage-loading-act { pointer-events: auto; color: var(--accent); font-weight: var(--fw-semibold); }
.wb-stage-loading .wb-stage-loading-act:hover { text-decoration: underline; }
/* first paint: the CSS alone — in after 300 ms, the seconds from 5 s on */
html[data-boot-scene] .wb-stage-loading:not([data-js]) { visibility: visible; animation: wb-load-in 200ms var(--ease-standard) 300ms both; }
html[data-boot-scene] .wb-stage-loading:not([data-js]) > .wb-stage-loading-time {
  display: inline; counter-reset: wb-load-s var(--wb-load-s);
  animation: wb-load-count 600s steps(600, end) both, wb-load-in 1ms linear 5s both;
}
html[data-boot-scene] .wb-stage-loading:not([data-js]) > .wb-stage-loading-time::after { content: '· ' counter(wb-load-s) ' s'; }
/* the app's: data-on while it shows (stage.js: once the wait's first 300 ms are over); taking over from the CSS one
   above when that shows already, at once (data-instant: no blink out and back in) */
.wb-stage-loading[data-js][data-on] { opacity: 1; visibility: visible; transition: opacity 200ms var(--ease-standard), visibility 0s linear 0s, width 200ms var(--ease-standard); }
.wb-stage-loading[data-js][data-on][data-instant] { transition: width 200ms var(--ease-standard); }
@keyframes wb-load-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes wb-load-count { from { --wb-load-s: 0; } to { --wb-load-s: 600; } }
@media (prefers-reduced-motion: reduce) {
  /* No fades and a still ring, but the waits stay: 300 ms before it shows (a fast load never flashes it) and the
     seconds from 5 s on. base.css zeroes every delay and duration under reduced motion; a wait is not motion, so
     these put the first paint's back (!important over its !important, by specificity), with no fade. (The app's
     waits are timers: stage.js.) */
  .wb-stage-loading-ring { animation: none !important; border-color: var(--accent); }
  html[data-boot-scene] .wb-stage-loading:not([data-js]) { animation: wb-load-in 1ms linear 300ms both !important; }
  html[data-boot-scene] .wb-stage-loading:not([data-js]) > .wb-stage-loading-time {
    animation: wb-load-count 600s steps(600, end) both, wb-load-in 1ms linear 5s both !important;
  }
}

/* ── css/components.css */
/* Shell components (spec §6, §15). Tokens only; classes prefixed .wb- */

/* ── Icons ──────────────────────────────────────────────────────────────── */
.wb-icon { width: 20px; height: 20px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.wb-icon[data-size="16"] { width: 16px; height: 16px; }
.wb-icon[data-size="14"] { width: 14px; height: 14px; }

/* ── Buttons ────────────────────────────────────────────────────────────── */
.wb-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--control-h); padding: 0 var(--sp-3);
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  background: var(--panel-raised); color: var(--text);
  font-size: var(--fs-13); font-weight: var(--fw-medium); line-height: 1;
  white-space: nowrap; user-select: none;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.wb-btn:hover:not(:disabled) { background: var(--panel-sunken); }
.wb-btn:active:not(:disabled) { background: var(--line); }
.wb-btn:disabled, .wb-btn[aria-disabled="true"] { opacity: .45; }
.wb-btn[aria-pressed="true"] { background: var(--accent-weak); border-color: var(--accent); color: var(--accent); }
.wb-btn--primary { background: var(--accent); border-color: var(--accent); color: var(--text-on-accent); }
.wb-btn--primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }
.wb-btn--primary:active:not(:disabled) { background: var(--accent-hover); }
.wb-btn--ghost { background: transparent; border-color: transparent; color: var(--text-dim); }
.wb-btn--ghost:hover:not(:disabled) { background: var(--panel-sunken); color: var(--text); }
.wb-btn--danger { color: var(--err); border-color: var(--err); background: transparent; }
.wb-btn--danger:hover:not(:disabled) { background: var(--err-weak); }
.wb-btn--danger.wb-btn--solid { background: var(--err); color: var(--text-on-accent); }
.wb-btn--sm { height: 24px; padding: 0 var(--sp-2); font-size: var(--fs-12); }
.wb-btn--block { width: 100%; }
.wb-btn .wb-btn-kbd { color: var(--text-mute); font-size: var(--fs-11); font-weight: var(--fw-regular); }
.wb-btn--icon { width: var(--control-h); padding: 0; }
.wb-iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: var(--r-sm);
  color: var(--text-dim); background: transparent; position: relative;
  transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.wb-iconbtn:hover:not(:disabled) { background: var(--panel-sunken); color: var(--text); }
.wb-iconbtn:disabled { opacity: .35; }
.wb-iconbtn[aria-pressed="true"], .wb-iconbtn[aria-expanded="true"] { background: var(--accent-weak); color: var(--accent); }
.wb-iconbtn--sm { width: 24px; height: 24px; }
.wb-iconbtn--sm .wb-icon { width: 16px; height: 16px; }
.wb-badge {
  position: absolute; top: 1px; right: 1px; min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: var(--r-pill); background: var(--accent); color: var(--text-on-accent);
  font-size: 10px; font-weight: var(--fw-semibold); line-height: 16px; text-align: center;
  font-variant-numeric: tabular-nums;
}
@media (pointer: coarse) {
  .wb-iconbtn { width: var(--touch); height: var(--touch); }
  .wb-btn { min-height: var(--touch); }
}

/* ── Segmented control ─────────────────────────────────────────────────── */
.wb-seg {
  display: inline-flex; padding: 2px; gap: 2px; position: relative;
  border-radius: var(--r-sm); background: var(--panel-sunken); border: 1px solid var(--line);
}
/* The selection slides between options (design 40 §B.6): one thumb under the buttons, moved by ui.segmented. */
.wb-seg-thumb {
  position: absolute; top: 2px; bottom: 2px; left: 0; width: 0; z-index: 0; pointer-events: none;
  border-radius: var(--r-xs); background: var(--panel-raised); box-shadow: var(--shadow-1);
  transition: transform var(--dur-2) var(--ease-standard), width var(--dur-2) var(--ease-standard);
}
.wb-seg-thumb[data-still] { transition: none; }
.wb-seg-thumb[hidden] { display: none; }
.wb-seg > button { position: relative; z-index: 1; transition: color var(--dur-1) var(--ease-standard); }
.wb-seg[data-thumb] > button[aria-checked="true"] { background: transparent; box-shadow: none; }
.wb-seg > button {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  height: calc(var(--control-h) - 6px); padding: 0 var(--sp-2);
  border-radius: var(--r-xs); color: var(--text-dim);
  font-size: var(--fs-12); font-weight: var(--fw-medium); white-space: nowrap;
}
.wb-seg > button:hover:not(:disabled, [aria-disabled="true"]) { color: var(--text); }
.wb-seg > button[aria-checked="true"] { background: var(--panel-raised); color: var(--text); box-shadow: var(--shadow-1); }
.wb-seg > button:disabled { opacity: .4; }
/* Unavailable but still hoverable / focusable, so its tooltip can say why (ui.segmented). */
.wb-seg > button[aria-disabled="true"] { opacity: .4; cursor: not-allowed; }
.wb-seg--block { display: flex; }
.wb-seg--block > button { flex: 1 1 0; }

/* ── Stepper ‹ n › ─────────────────────────────────────────────────────── */
.wb-stepper { display: inline-flex; align-items: center; border: 1px solid var(--line-strong); border-radius: var(--r-sm); height: var(--control-h); background: var(--panel-raised); }
.wb-stepper > button { width: 24px; height: 100%; display: inline-flex; align-items: center; justify-content: center; color: var(--text-dim); }
.wb-stepper > button:hover:not(:disabled) { color: var(--text); background: var(--panel-sunken); }
.wb-stepper > button:disabled { opacity: .35; }
.wb-stepper > output { min-width: 40px; padding: 0 var(--sp-1); text-align: center; font-variant-numeric: tabular-nums; font-size: var(--fs-12); }

/* ── Toggle switch ─────────────────────────────────────────────────────── */
.wb-toggle { display: inline-flex; align-items: center; gap: var(--sp-2); cursor: pointer; user-select: none; min-height: var(--control-h); }
.wb-toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.wb-toggle .wb-toggle-track {
  position: relative; width: 28px; height: 16px; border-radius: var(--r-pill);
  background: var(--line-strong); transition: background var(--dur) var(--ease-out); flex: 0 0 auto;
}
.wb-toggle .wb-toggle-track::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%;
  background: var(--panel-raised); box-shadow: var(--shadow-1); transition: transform var(--dur) var(--ease-out);
}
.wb-toggle input:checked + .wb-toggle-track { background: var(--accent); }
.wb-toggle input:checked + .wb-toggle-track::after { transform: translateX(12px); }
.wb-toggle input:focus-visible + .wb-toggle-track { outline: 2px solid var(--focus); outline-offset: 2px; }
.wb-toggle input:disabled + .wb-toggle-track { opacity: .45; }

/* ── Chips ─────────────────────────────────────────────────────────────── */
.wb-chip {
  display: inline-flex; align-items: center; gap: 4px; max-width: 100%;
  height: 24px; padding: 0 var(--sp-2); border-radius: var(--r-pill);
  background: var(--panel-sunken); border: 1px solid var(--line); color: var(--text);
  font-size: var(--fs-12); white-space: nowrap;
}
.wb-chip > .wb-chip-label { overflow: hidden; text-overflow: ellipsis; }
.wb-chip[aria-pressed="true"] { background: var(--accent-weak); border-color: var(--accent); color: var(--accent); }
.wb-chip--accent { background: var(--accent-weak); border-color: transparent; color: var(--accent); }
button.wb-chip:hover { border-color: var(--line-strong); }
.wb-chip > .wb-chip-x { display: inline-flex; margin-right: -4px; padding: 2px; border-radius: 50%; color: var(--text-mute); }
.wb-chip > .wb-chip-x:hover { color: var(--text); background: var(--line); }
.wb-chip > .wb-chip-x .wb-icon { width: 12px; height: 12px; }

/* ── Inputs ────────────────────────────────────────────────────────────── */
.wb-input, .wb-select, .wb-textarea {
  height: var(--control-h); padding: 0 var(--sp-2);
  border-radius: var(--r-sm); border: 1px solid var(--line-strong);
  background: var(--panel-raised); color: var(--text);
  font-size: var(--fs-13); min-width: 0;
}
.wb-textarea { height: auto; padding: var(--sp-2); font-family: var(--font-mono); font-size: var(--fs-12); line-height: 1.5; resize: vertical; }
.wb-input::placeholder, .wb-textarea::placeholder { color: var(--text-mute); }
.wb-input:focus, .wb-select:focus, .wb-textarea:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 3px var(--accent-weak); }
.wb-input[aria-invalid="true"] { border-color: var(--err); }
.wb-field { display: grid; gap: 4px; }
.wb-field-error { color: var(--err); font-size: var(--fs-12); }
.wb-search { position: relative; display: flex; align-items: center; }
.wb-search > .wb-icon { position: absolute; left: 8px; color: var(--text-mute); width: 16px; height: 16px; pointer-events: none; }
.wb-search > .wb-input { padding-left: 30px; width: 100%; }
@media (max-width: 767px), (pointer: coarse) {
  .wb-input, .wb-select, .wb-textarea { font-size: 16px; }
}

/* ── Floating surfaces: popover, menu, tooltip ──────────────────────────── */
.wb-popover {
  position: fixed; top: 0; left: 0;
  min-width: 160px; max-width: calc(100vw - 16px); max-height: calc(100vh - 16px);
  overflow: auto; overscroll-behavior: contain;
  background: var(--panel-raised); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  animation: wb-pop-in var(--dur-2) var(--ease-standard);
}
/* Floating surfaces enter with a fade and a --shift-1 offset, and leave with a --dur-1 fade (design 40 §B.4). */
@keyframes wb-pop-in { from { opacity: 0; transform: translateY(calc(-1 * var(--shift-1))); } to { opacity: 1; transform: none; } }
.wb-popover[data-side="top"] { animation-name: wb-pop-in-up; }
@keyframes wb-pop-in-up { from { opacity: 0; transform: translateY(var(--shift-1)); } to { opacity: 1; transform: none; } }
.wb-popover[data-leaving], .wb-tooltip[data-leaving], .wb-hint[data-leaving] { animation: wb-fade-out var(--dur-1) var(--ease-exit) forwards; pointer-events: none; }
@keyframes wb-fade-out { to { opacity: 0; } }
.wb-popover-pad { padding: var(--sp-3); }
.wb-menu { padding: 4px; min-width: 200px; }
.wb-menu-item {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%;
  min-height: 30px; padding: 0 var(--sp-2); border-radius: var(--r-xs);
  color: var(--text); text-align: left; font-size: var(--fs-13);
}
.wb-menu-item:hover:not(:disabled), .wb-menu-item:focus-visible { background: var(--panel-sunken); outline: none; }
.wb-menu-item:disabled { color: var(--text-mute); }
.wb-menu-item > .wb-menu-check { width: 16px; display: inline-flex; justify-content: center; color: var(--accent); }
.wb-menu-item > .wb-menu-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-menu-item > .wb-menu-kbd { color: var(--text-mute); font-size: var(--fs-11); font-variant-numeric: tabular-nums; }
.wb-menu-sep { height: 1px; margin: 4px 0; background: var(--line); }
.wb-menu-head { padding: 6px var(--sp-2) 2px; }
@media (pointer: coarse) { .wb-menu-item { min-height: var(--touch); } }

.wb-tooltip {
  position: fixed; top: 0; left: 0; z-index: var(--z-toast);
  max-width: 280px; padding: 4px 8px; border-radius: var(--r-xs);
  background: var(--text); color: var(--bg);
  font-size: var(--fs-12); line-height: 1.35; pointer-events: none;
  box-shadow: var(--shadow-2);
  animation: wb-pop-in var(--dur-2) var(--ease-standard);
}
.wb-tooltip kbd { background: transparent; color: inherit; border-color: currentColor; opacity: .8; }

/* ── First-visit hint (F1) ─────────────────────────────────────────────── */
.wb-hint {
  position: fixed; top: 0; left: 0; z-index: var(--z-popover);
  display: flex; align-items: flex-start; gap: var(--sp-2);
  max-width: 260px; padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-3);
  border-radius: var(--r-md); background: var(--accent); color: var(--text-on-accent);
  font-size: var(--fs-12); line-height: 1.4; box-shadow: var(--shadow-3);
  animation: wb-fade-in var(--dur) var(--ease-out);
}
.wb-hint[hidden] { display: none; }
.wb-hint-text { flex: 1 1 auto; padding-top: 2px; }
.wb-hint-close { flex: 0 0 auto; width: 22px; height: 22px; border-radius: var(--r-xs); display: inline-flex; align-items: center; justify-content: center; color: inherit; opacity: .85; }
.wb-hint-close:hover { opacity: 1; background: color-mix(in srgb, currentColor 16%, transparent); }

/* ── Modal ─────────────────────────────────────────────────────────────── */
.wb-modal-scrim {
  position: fixed; inset: 0; background: var(--scrim);
  display: flex; align-items: center; justify-content: center; padding: var(--sp-4);
  animation: wb-fade-in var(--dur-2) var(--ease-standard);
}
@keyframes wb-fade-in { from { opacity: 0; } to { opacity: 1; } }
.wb-modal-scrim > .wb-modal { animation: wb-modal-in var(--dur-2) var(--ease-standard); }
@keyframes wb-modal-in { from { opacity: 0; transform: scale(.98); } to { opacity: 1; transform: none; } }
.wb-modal-scrim[data-leaving] { animation: wb-fade-out var(--dur-1) var(--ease-exit) forwards; pointer-events: none; }
.wb-modal {
  width: min(520px, 100%); max-height: calc(100dvh - 2 * var(--sp-4));
  display: flex; flex-direction: column;
  background: var(--panel-raised); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-3);
}
.wb-modal--wide { width: min(760px, 100%); }
.wb-modal-head { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-2) var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line); }
.wb-modal-head h2 { flex: 1 1 auto; font-size: var(--fs-15); }
.wb-modal-body { padding: var(--sp-4); overflow: auto; min-height: 0; display: grid; gap: var(--sp-3); align-content: start; }
.wb-modal-foot { display: flex; justify-content: flex-end; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--line); }

/* ── Toasts ────────────────────────────────────────────────────────────── */
/* A toast never outgrows the screen: the text wraps (a long file name breaks after _ - . ×, else anywhere), and on a
   narrow screen the action moves under the text (QA2 deploy note: "Saved <file name> · Show in Jobs" at 375 px). */
.wb-toast {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: 40px; max-width: min(560px, 100%); box-sizing: border-box; padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--text); color: var(--bg);
  box-shadow: var(--shadow-3);
  font-size: var(--fs-13);
  animation: wb-toast-in var(--dur-2) var(--ease-standard);
}
@keyframes wb-toast-in { from { opacity: 0; transform: translateY(var(--shift-2)); } to { opacity: 1; transform: none; } }
.wb-toast[data-leaving] {
  opacity: 0; transform: translateY(var(--shift-1)); pointer-events: none;
  transition: opacity var(--dur-1) var(--ease-exit), transform var(--dur-1) var(--ease-exit);
}
.wb-toast > .wb-toast-body { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; flex-wrap: wrap; column-gap: var(--sp-3); }
.wb-toast .wb-toast-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; padding: 3px 0; }
.wb-toast .wb-toast-text em { font-style: italic; font-weight: var(--fw-semibold); }
/* margin-left −8 px: beside the text the gap stays 12 px; wrapped under it, the label lines up with the text */
.wb-toast-body > .wb-toast-action { margin-left: -8px; color: inherit; font-weight: var(--fw-semibold); padding: 4px 8px; border-radius: var(--r-xs); text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; }
.wb-toast-body > .wb-toast-action:hover { background: color-mix(in srgb, var(--bg) 16%, transparent); }
.wb-toast > .wb-toast-close { color: inherit; opacity: .7; display: inline-flex; padding: 4px; border-radius: var(--r-xs); }
.wb-toast > .wb-toast-close:hover { opacity: 1; }
.wb-toast > .wb-toast-icon { display: inline-flex; }
.wb-toast--error { background: var(--err); color: var(--text-on-accent); }
.wb-toast--warn > .wb-toast-icon { color: var(--warn); }
.wb-toast--success > .wb-toast-icon { color: var(--ok); }

/* ── Skeleton shimmer ──────────────────────────────────────────────────── */
.wb-skel {
  display: block; border-radius: var(--r-xs);
  background: linear-gradient(90deg, var(--panel-sunken) 0%, var(--line) 50%, var(--panel-sunken) 100%);
  background-size: 200% 100%;
  animation: wb-shimmer 1.2s linear infinite;
}
.wb-skel--text { height: 10px; margin: 6px 0; }
.wb-skel--row { height: var(--row-h); }
.wb-skel--circle { border-radius: 50%; }
@keyframes wb-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }

/* ── Contextual lock line + Engine tag (spec §12.6) ─────────────────────── */
.wb-lock {
  display: flex; align-items: center; gap: 6px;
  color: var(--text-dim); font-size: var(--fs-12); line-height: var(--lh-tight);
  padding: 4px 0;
}
.wb-lock > .wb-icon { width: 14px; height: 14px; color: var(--text-mute); }
.wb-engine-tag {
  display: inline-flex; align-items: center; gap: 3px;
  height: 18px; padding: 0 6px; border-radius: var(--r-pill);
  border: 1px solid var(--line-strong); background: var(--panel-sunken); color: var(--text-dim);
  font-size: var(--fs-11); font-weight: var(--fw-medium); white-space: nowrap;
}

/* ── Ids: monospace with a middle ellipsis (applied by dom.middleEllipsis) ─ */
.mono-id, .wb-id {
  font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: clip; min-width: 0;
}
/* dom.monoIdFit sizes the middle ellipsis to the row; the end ellipsis is only a safety net for rounding. */
.mono-id--fit { text-overflow: ellipsis; }

/* ── Image placeholder (core/img.js, F3): a failed <img> keeps its box and shows a quiet glyph (a mid-grey that reads
      on both themes), never the browser's broken icon. data-ph picks the glyph; .wb-ph shows it in a box with no image. ── */
:root {
  --ph-item: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23808080' fill-opacity='.65'%3E%3Cpath d='M8.5 3 3 6.2l2.1 4 2.4-1.2V21h9V9l2.4 1.2 2.1-4L15.5 3c-.6 1.6-1.9 2.6-3.5 2.6S9.1 4.6 8.5 3z'/%3E%3C/svg%3E");
  --ph-look: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23808080' fill-opacity='.65'%3E%3Ccircle cx='12' cy='6.5' r='3.8'/%3E%3Cpath d='M12 11.5c-3.6 0-6 2.4-6 5.6V22h12v-4.9c0-3.2-2.4-5.6-6-5.6z'/%3E%3C/svg%3E");
  --ph-move: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23808080' fill-opacity='.65'%3E%3Cpath d='M12 2l2.3 6.7L21 11l-6.7 2.3L12 20l-2.3-6.7L3 11l6.7-2.3z'/%3E%3C/svg%3E");
  --ph-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23808080' fill-opacity='.65'%3E%3Cpath fill-rule='evenodd' d='M3 5h18v14H3zm2 2v8.6l4.2-4.2 3.3 3.3 2.7-2.7L19 15.8V7zm10.5 1.2a1.6 1.6 0 1 1 0 3.2 1.6 1.6 0 0 1 0-3.2z'/%3E%3C/svg%3E");
}
img[data-img]:not(.wb-stage-media), .wb-ph[data-ph] {
  background-color: var(--panel-sunken); background-image: var(--ph-image);
  background-position: center; background-size: min(62%, 40px) auto; background-repeat: no-repeat;
}
img[data-img][data-ph="item"]:not(.wb-stage-media), .wb-ph[data-ph="item"] { background-image: var(--ph-item); }
img[data-img][data-ph="look"]:not(.wb-stage-media), .wb-ph[data-ph="look"] { background-image: var(--ph-look); }
img[data-img][data-ph="move"]:not(.wb-stage-media), .wb-ph[data-ph="move"] { background-image: var(--ph-move); }
img[data-img="retrying"]:not(.wb-stage-media) { opacity: .6; }

/* ── Status dot ────────────────────────────────────────────────────────── */
.wb-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--text-mute); flex: 0 0 auto; }
.wb-dot[data-state="ok"] { background: var(--ok); }
.wb-dot[data-state="warming"], .wb-dot[data-state="busy"], .wb-dot[data-state="degraded"] { background: var(--warn); }
.wb-dot[data-state="down"] { background: var(--err); }

/* ── Section label / misc ──────────────────────────────────────────────── */
.wb-section { padding: var(--sp-3); border-bottom: 1px solid var(--line); display: grid; gap: var(--sp-2); }
.wb-row { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.wb-spacer { flex: 1 1 auto; }
.wb-note { color: var(--text-dim); font-size: var(--fs-12); }
.wb-note--warn { color: var(--warn); }
.wb-note--err { color: var(--err); }
.wb-spinner {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--line-strong); border-top-color: var(--accent);
  animation: wb-spin .8s linear infinite; flex: 0 0 auto;
}
@keyframes wb-spin { to { transform: rotate(360deg); } }

/* ── Top bar bits ──────────────────────────────────────────────────────── */
.wb-topbar-sep { width: 1px; height: 20px; background: var(--line); margin: 0 var(--sp-1); }

/* ── Inspector rail ────────────────────────────────────────────────────── */
.wb-insp-rail .wb-iconbtn { width: 32px; height: 32px; }
.wb-insp-rail .wb-iconbtn[aria-current="true"] { color: var(--accent); }

/* ── Tab bar (mobile) ──────────────────────────────────────────────────── */
.wb-tab {
  flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  color: var(--text-dim); font-size: var(--fs-11); font-weight: var(--fw-medium); min-width: 0;
}
.wb-tab[aria-selected="true"] { color: var(--accent); }
.wb-tab .wb-icon { width: 22px; height: 22px; }

/* ── Stage ─────────────────────────────────────────────────────────────── */
.wb-stage-view { position: absolute; inset: 0; overflow: hidden; }
.wb-stage-bg[data-bg="checker"] {
  background-color: var(--stage-checker-a);
  background-image:
    linear-gradient(45deg, var(--stage-checker-b) 25%, transparent 25%, transparent 75%, var(--stage-checker-b) 75%),
    linear-gradient(45deg, var(--stage-checker-b) 25%, transparent 25%, transparent 75%, var(--stage-checker-b) 75%);
  background-size: var(--stage-checker-size) var(--stage-checker-size);
  background-position: 0 0, calc(var(--stage-checker-size) / 2) calc(var(--stage-checker-size) / 2);
}
.wb-stage-bg[data-bg="light"] { background: var(--stage-light); }
.wb-stage-bg[data-bg="grey"]  { background: var(--stage-grey); }
.wb-stage-bg[data-bg="dark"]  { background: var(--stage-dark); }
.wb-stage-bg[data-bg="custom"] { background: var(--stage-custom, var(--stage-grey)); }

.wb-stage-media {
  position: absolute; left: 0; top: 0; transform-origin: 0 0;
  will-change: transform;
}
/* Never a native image drag or iOS long-press callout: a drag on the stage pans (⌥-drag exports, stage.js). */
img.wb-stage-media { -webkit-user-drag: none; -webkit-touch-callout: none; user-select: none; -webkit-user-select: none; }
img.wb-stage-media[draggable="true"] { -webkit-user-drag: element; }   /* armed by ⌥/Alt for one gesture */
.wb-stage-media[data-pixelated] { image-rendering: pixelated; }
.wb-stage-media[data-dim] { opacity: .85; transition: opacity var(--dur) var(--ease-out); }
/* Stills cross-fade over --dur-2 (design 40 §B.4), the avatar replacing the loading silhouette too. */
.wb-stage-fade { transition: opacity var(--dur-2) var(--ease-standard); }
.wb-stage-media { z-index: 0; }
canvas.wb-stage-media { z-index: 2; }
.wb-stage-overlay-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 3; }
/* The room floor grid (stage.js drawFloor): under every avatar layer, never a pointer target; not under a takeover. */
.wb-stage-floor { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; }
.wb-stage-view[data-takeover] .wb-stage-floor { display: none; }
.wb-stage-dom { position: absolute; inset: 0; pointer-events: none; }
.wb-stage-dom > * { pointer-events: auto; }
.wb-stage-dom > [data-passive] { pointer-events: none; }
.wb-stage-takeover { position: absolute; inset: 0; overflow: auto; background: var(--bg); z-index: 5; }
.wb-stage-view[data-pannable] { cursor: grab; }
.wb-stage-view[data-panning] { cursor: grabbing; }
.wb-stage-view > .wb-stage-takeover, .wb-stage-empty-box { cursor: auto; }   /* not part of the pan surface */

/* The toolbar sits in the visible part of the stage: below the banner (its measured height, --stage-banner-h), right
   of an overlaying Outfit panel, and never wider than what a drawer leaves. stage.js fits it: data-fit="full" (labels),
   "icons" (no labels) or "fold" (body size, background and overlays move into ⋯) (QA2 m1, m2). */
.wb-stage-toolbar {
  position: absolute; top: calc(var(--sp-3) + var(--stage-banner-h, 0px)); left: calc(var(--sp-3) + var(--stage-inset-left, 0px));
  z-index: var(--z-stage-ui);
  display: flex; align-items: center; gap: 2px;
  height: 36px; padding: 0 4px;
  background: var(--panel-raised); border: 1px solid var(--line);
  border-radius: var(--r-pill); box-shadow: var(--shadow-2);
  max-width: calc(100% - 2 * var(--sp-3) - var(--stage-inset-left, 0px) - var(--stage-inset-right, 0px));
}
.wb-stage-toolbar:is([data-fit="icons"], [data-fit="fold"]) .wb-tb-text { display: none; }
.wb-stage-toolbar[data-fit="fold"] .wb-tb-fold { display: none; }
.wb-stage-toolbar:is([data-fit="icons"], [data-fit="fold"]) .wb-tb-btn { padding: 0 7px; }
.wb-stage-toolbar:empty { display: none; }
.wb-stage-toolbar .wb-tb-btn {
  display: inline-flex; align-items: center; gap: 4px; height: 28px; padding: 0 var(--sp-2);
  border-radius: var(--r-pill); color: var(--text-dim); font-size: var(--fs-12); font-weight: var(--fw-medium);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.wb-stage-toolbar .wb-tb-btn:hover:not(:disabled) { background: var(--panel-sunken); color: var(--text); }
.wb-stage-toolbar .wb-tb-btn[aria-expanded="true"], .wb-stage-toolbar .wb-tb-btn[aria-pressed="true"] { background: var(--accent-weak); color: var(--accent); }
.wb-stage-toolbar .wb-tb-btn .wb-icon { width: 16px; height: 16px; }
.wb-stage-toolbar .wb-tb-sep { width: 1px; height: 16px; background: var(--line); margin: 0 2px; }
.wb-tb-swatch { width: 12px; height: 12px; border-radius: 3px; border: 1px solid var(--line-strong); }

.wb-hud { position: absolute; z-index: var(--z-stage-ui); display: flex; align-items: center; gap: var(--sp-2); pointer-events: none; }
.wb-hud > * { pointer-events: auto; }
.wb-hud-bl { left: var(--sp-3); bottom: calc(var(--sp-3) + var(--stage-inset-bottom, 0px)); max-width: calc(100% - 2 * var(--sp-3) - var(--stage-inset-right, 0px)); flex-wrap: wrap; }
.wb-hud-br { right: calc(var(--sp-3) + var(--stage-inset-right, 0px)); bottom: calc(var(--sp-3) + var(--stage-inset-bottom, 0px)); }
.wb-hud-tr { right: calc(var(--sp-3) + var(--stage-inset-right, 0px)); top: calc(var(--sp-3) + var(--stage-banner-h, 0px)); flex-direction: column; align-items: flex-end; }
.wb-hud-label {
  font-size: var(--fs-11); color: var(--text-dim); font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--panel) 82%, transparent); padding: 2px 8px; border-radius: var(--r-pill);
  border: 1px solid var(--line);
}
/* Per-request fallback (spec §12.7): "Rendered by Unity — reason" on the stage, Compare panes and exports. */
/* The reason is the short form (js/render/fallback-copy.js) and may wrap to a second line (QA P6); the full reason is
   in the tooltip. */
.wb-hud-fallback {
  display: inline-flex; align-items: center; gap: 5px; min-width: 0; max-width: min(48ch, 64vw);
  font-size: var(--fs-11); line-height: 1.3; color: var(--warn); font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--panel) 88%, transparent); padding: 2px 8px; border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--warn) 55%, transparent);
}
.wb-hud-fallback .wb-icon { width: 12px; height: 12px; flex: none; }
.wb-hud-fallback-text {
  min-width: 0; overflow: hidden; white-space: normal; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
}
.wb-hud-br { max-width: calc(100% - 2 * var(--sp-3) - var(--stage-inset-right, 0px)); flex-wrap: wrap; justify-content: flex-end; }
.wb-pill {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  height: 26px; padding: 0 10px; border-radius: var(--r-pill);
  background: var(--panel-raised); border: 1px solid var(--line); box-shadow: var(--shadow-1);
  font-size: var(--fs-12); color: var(--text-dim); font-variant-numeric: tabular-nums;
}
.wb-pill > .wb-pill-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.wb-pill--error { color: var(--err); border-color: var(--err); background: var(--panel-raised); height: auto; min-height: 26px; padding: 3px 10px; }
.wb-pill--error .wb-pill-text { white-space: normal; }
.wb-pill--stale { color: var(--warn); border-color: var(--warn); }
/* A note about the picture (items the game has no files for: shell/stage.js skippedNote): it wraps rather than clips on a phone. */
.wb-pill--note { height: auto; min-height: 26px; padding: 3px 10px; }
.wb-pill--note .wb-pill-text { white-space: normal; }
.wb-pill .wb-pill-act { color: var(--accent); font-weight: var(--fw-semibold); }
.wb-pill .wb-pill-act:hover { text-decoration: underline; }
.wb-pill-ring {
  width: 10px; height: 10px; border-radius: 50%; border: 1.5px solid var(--text-mute); border-top-color: transparent;
  animation: wb-spin 1s linear infinite;
}
.wb-chip-bodysize {
  font-size: var(--fs-12); font-weight: var(--fw-semibold); padding: 4px 10px; border-radius: var(--r-pill);
  background: var(--panel-raised); border: 1px solid var(--line); box-shadow: var(--shadow-2);
  animation: wb-fade-in var(--dur) var(--ease-out);
}
/* Warming / down / degraded (F12). An opaque surface (the panel, tinted with the status colour), a status-coloured
   edge and icon, and the theme's own text: readable over every stage background in both themes (QA2 M2; the stage
   checker is the same mid-light grey in both). It spans the visible stage (not under a drawer or an overlaying panel);
   its measured height moves the toolbar down and is an inset of the Fit area (stage.js, QA2 m1). */
.wb-stage-banner {
  --banner-tone: var(--warn);
  position: absolute; top: 0; left: var(--stage-inset-left, 0px); right: var(--stage-inset-right, 0px);
  z-index: calc(var(--z-stage-ui) + 1);
  display: flex; align-items: flex-start; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-3);
  background: color-mix(in srgb, var(--banner-tone) 9%, var(--panel-raised)); color: var(--text);
  border-bottom: 1px solid color-mix(in srgb, var(--banner-tone) 45%, var(--line));
  box-shadow: inset 3px 0 0 var(--banner-tone), var(--shadow-1);
  font-size: var(--fs-12); line-height: var(--lh-tight);
}
.wb-stage-banner[data-kind="down"] { --banner-tone: var(--err); }
.wb-stage-banner .wb-banner-icon { flex: none; display: inline-flex; color: var(--banner-tone); padding: 5px 0; }
.wb-stage-banner .wb-banner-body { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; flex-wrap: wrap; column-gap: var(--sp-3); row-gap: var(--sp-1); }
.wb-stage-banner .wb-banner-text { flex: 1 1 260px; min-width: 0; padding: 5px 0; }
.wb-stage-banner .wb-banner-acts { flex: none; display: inline-flex; align-items: center; gap: var(--sp-1); }
.wb-stage-banner > .wb-iconbtn { flex: none; }
.wb-stage-empty {
  position: absolute; inset: 0; z-index: 4; display: flex; align-items: center; justify-content: center; text-align: center;
  padding: var(--sp-6); color: var(--text-dim); pointer-events: none;
}
.wb-stage-empty > div { max-width: 340px; background: var(--panel-raised); padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md); border: 1px solid var(--line); }
.wb-stage-empty-box { display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); pointer-events: auto; }
.wb-stage-empty-box p { margin: 0; }
.wb-stage-empty-title { color: var(--text); font-weight: 600; }
/* A look that needs Unity while Unity is off or removed (spec §12.8): the card replaces the picture. */
.wb-stage-needs > div { max-width: 380px; border-color: color-mix(in srgb, var(--warn) 45%, var(--line)); box-shadow: inset 3px 0 0 var(--warn), var(--shadow-1); }
.wb-stage-needs-acts { justify-content: center; flex-wrap: wrap; gap: var(--sp-2); }
.wb-stage-media[data-needs] { opacity: .12 !important; filter: grayscale(1); }   /* over the cross-fade's inline opacity */
/* A new item being fetched by the asset service (render/fresh-items.js): not a warning, so the accent, and a step bar. */
.wb-stage-fresh[data-state="fetching"] > div { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); box-shadow: inset 3px 0 0 var(--accent), var(--shadow-1); }
.wb-fresh-bar { width: 220px; max-width: 100%; height: 4px; border-radius: 2px; background: var(--accent-weak); overflow: hidden; position: relative; }
.wb-fresh-bar > span { position: absolute; inset: 0 auto 0 0; width: 35%; background: var(--accent); border-radius: 2px; animation: wb-fresh-slide 1.4s ease-in-out infinite; }
.wb-fresh-bar[data-determinate] > span { animation: none; transition: width .4s ease; }
.wb-fresh-step { margin-top: -4px; }
.wb-fresh-chips { justify-content: center; flex-wrap: wrap; gap: var(--sp-1); }
@keyframes wb-fresh-slide { 0% { left: -35%; } 100% { left: 100%; } }
@media (prefers-reduced-motion: reduce) { .wb-fresh-bar > span { animation: none; left: 0; width: 100%; opacity: .5; } }
.wb-drop-target {
  position: fixed; inset: 8px; z-index: var(--z-modal);
  border: 2px dashed var(--accent); border-radius: var(--r-lg);
  background: var(--accent-weak);
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
}
.wb-drop-target > span {
  background: var(--panel-raised); color: var(--accent); font-weight: var(--fw-semibold);
  padding: var(--sp-2) var(--sp-4); border-radius: var(--r-pill); box-shadow: var(--shadow-2);
}

/* Mobile floating stage buttons (Flip, body size, compare) */
@media (max-width: 767px) {
  .wb-stage-toolbar {
    left: auto; right: calc(var(--sp-2) + var(--stage-inset-right, 0px)); top: calc(var(--sp-2) + var(--stage-banner-h, 0px));
    flex-direction: column; height: auto; padding: 4px; border-radius: var(--r-lg); gap: 2px; max-width: none;
  }
  .wb-stage-toolbar:is([data-fit="icons"], [data-fit="fold"]) .wb-tb-btn { padding: 0 6px; }
  .wb-stage-toolbar .wb-tb-btn { height: var(--touch); min-width: var(--touch); justify-content: center; padding: 0 6px; }
  .wb-stage-toolbar .wb-tb-sep { width: 20px; height: 1px; margin: 2px 0; }
  .wb-stage-toolbar .wb-tb-text { display: none; }
  .wb-hud-tr { top: calc(var(--sp-2) + var(--stage-banner-h, 0px)); right: calc(var(--touch) + var(--sp-4)); }
  /* A sheet leaves a short stage: the toolbar collapses into ⋯ so it doesn't cover a third of it (R2-12). */
  #app[data-sheet] .wb-stage-toolbar > :not(.wb-tb-more) { display: none; }
}

/* ── Shortcut sheet ───────────────────────────────────────────────────── */
.wb-keys { columns: 2 280px; column-gap: var(--sp-6); }
.wb-keys-group { break-inside: avoid; margin-bottom: var(--sp-4); }
.wb-keys-group h3 { margin-bottom: var(--sp-1); }
.wb-keys-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: 3px 0; border-bottom: 1px solid var(--line); }
.wb-keys-row > span:first-child { color: var(--text); }
.wb-keys-combo { display: inline-flex; gap: 3px; flex-wrap: wrap; justify-content: flex-end; }

/* ── History list ─────────────────────────────────────────────────────── */
.wb-hist { padding: 4px; width: 300px; max-height: 420px; overflow: auto; }
.wb-hist-item { display: flex; gap: var(--sp-2); align-items: baseline; width: 100%; padding: 6px var(--sp-2); border-radius: var(--r-xs); text-align: left; }
.wb-hist-item:hover { background: var(--panel-sunken); }
.wb-hist-item[aria-current="true"] { background: var(--accent-weak); color: var(--accent); }
.wb-hist-item[data-future] { color: var(--text-mute); }
.wb-hist-item > .wb-hist-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-hist-item > .wb-hist-time { color: var(--text-mute); font-size: var(--fs-11); font-variant-numeric: tabular-nums; }

/* Stage toolbar background swatch: a visible chip (fine checker, strong border), also when the label is hidden. */
.wb-stage-toolbar .wb-tb-swatch { --stage-checker-size: 6px; border: 1.5px solid var(--text-mute); flex: 0 0 auto; }
@media (max-width: 767px) { .wb-stage-toolbar .wb-tb-swatch { width: 18px; height: 18px; border-radius: 4px; } }

/* A touch phone has no keyboard: shortcut letters in menus, buttons and the Inspector are clutter there (QA P14). */
@media (hover: none) and (pointer: coarse) {
  /* html: above the features' own display rules (feature CSS loads after this file) */
  html .wb-menu-item > .wb-menu-kbd, html .wb-btn .wb-btn-kbd, html .wb-ins-keys, html .wb-ins-toggle > kbd { display: none; }
}

/* Touch targets of at least 44 × 44 px (§16, QA P14, QA2 m8), after the base rules above. Where the control looks
   better smaller, a transparent ::before carries the rest of the hit area (it counts as the control for taps). */
@media (pointer: coarse) {
  .wb-seg > button { min-width: var(--touch); position: relative; }
  .wb-seg > button::before { content: ''; position: absolute; inset: -3px 0; }   /* 38 px + the segment's padding */
  .wb-stepper > button { width: var(--touch); position: relative; }
  .wb-stepper > button::before { content: ''; position: absolute; inset: -1px 0; }   /* over the stepper's border */
  button.wb-chip { height: var(--touch); padding: 0 var(--sp-3); }
}

/* A touch tablet (wider than a phone, whose toolbar is the column above): the stage toolbar's buttons are 44 px targets
   too (QA TABLET-TARGETS: 28 px at 820 × 1180). */
@media (pointer: coarse) and (min-width: 768px) {
  .wb-stage-toolbar { height: 52px; }
  .wb-stage-toolbar .wb-tb-btn { height: var(--touch); min-width: var(--touch); justify-content: center; }
}

/* ── Motion helpers (design 40 §B; js/core/motion.js) ────────────────────── */
/* motion.swap: the old content leaves as a ghost over the host while the new one fades in from --shift-2 toward the
   direction of travel (tabs, the Inspector body, a panel switching to another). */
.wb-swap-ghost { position: absolute; left: 0; top: 0; z-index: 2; overflow: hidden; pointer-events: none; box-sizing: border-box; background: var(--panel); }
/* The ghost is the old content frozen as it was painted: moving it into the ghost must not replay its entry
   animations (a tab's own fade-in restarted from opacity 0 and blanked the panel, then came back over the new tab). */
.wb-swap-ghost * { animation: none !important; transition: none !important; }
/* Leaving content dissolves from its first frame (a fast-start curve): the switch shows at once, not after a pause. */
.wb-swap-out { animation: wb-fade-out var(--dur-1) var(--ease-standard) forwards; }
.wb-swap-in { animation: wb-swap-in var(--dur-2) var(--ease-standard) both; }
/* The incoming content is already there under the opaque leaving content, which dissolves into it: one clean
   cross-dissolve (never both views faint at once), the new one settling from --shift-2 toward the direction of travel. */
@keyframes wb-swap-in { from { opacity: .5; transform: translateX(calc(var(--swap-dir, 0) * var(--shift-2))); } 40% { opacity: 1; } to { opacity: 1; transform: none; } }

/* ── Panel footer (design 40 §B.7; ui.footer) ───────────────────────────── */
/* One line, --bar-h with its top hairline (44 px; 52 px on coarse pointers): the Outfit panel, the Pose bar and every
   right panel share the bottom edge. start ellipsizes (its tooltip has the whole text); end holds at most two
   buttons; progress is a 2 px bar on the top edge. */
.wb-foot {
  position: relative; flex: 0 0 auto; display: flex; align-items: center; gap: var(--sp-2);
  height: var(--bar-h); min-height: var(--bar-h); max-height: var(--bar-h); min-width: 0;
  padding: 0 var(--sp-2) 0 var(--sp-3); border-top: 1px solid var(--line); background: var(--panel);
  font-size: var(--fs-12); color: var(--text-dim); white-space: nowrap; overflow: hidden;
}
.wb-foot-start { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-foot-end { flex: 0 0 auto; display: flex; align-items: center; gap: var(--sp-1); }
.wb-foot-progress { position: absolute; left: 0; right: 0; top: -1px; height: 2px; background: transparent; overflow: hidden; }
.wb-foot-progress[hidden] { display: none; }
.wb-foot-progress > span { display: block; height: 100%; width: 0; background: var(--accent); transition: width var(--dur-2) linear; }
.wb-foot-progress[data-indeterminate] > span { width: 35%; position: absolute; animation: wb-exp-indet 1.2s var(--ease-standard) infinite; }

/* ── Items left out because they don't fit the rig (core/left-out.js) ────── */
.wb-leftout-list { display: grid; gap: 2px; max-height: 50vh; overflow: auto; margin: 0; padding: 0; }
.wb-leftout-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--sp-3); padding: 6px 0; border-bottom: 1px solid var(--line); }
.wb-leftout-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-leftout-id { grid-column: 1; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-leftout-rig { grid-column: 2; grid-row: 1 / span 2; align-self: center; font-size: var(--fs-12); }

/* ── css/features/omnibox.css */
/* Feature styles: omnibox. Owned by the omnibox feature; tokens only, classes prefixed .wb-omni- (spec §17.9).
   Parts: the top-bar field and its floating dropdown, the full-screen mobile dialog, Look name + source chip,
   the mobile source pill, the Look menu and the Share popover. */

/* ── Field (shared by the bar and the dialog) ─────────────────────────────── */
.wb-omni { position: relative; min-width: 0; }
.wb-omni--bar { width: min(480px, 100%); }
.wb-omni .wb-omni-trigger { display: none; }

.wb-omni-box {
  display: flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 6px 0 8px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--panel-sunken); color: var(--text-mute);
  transition: border-color var(--dur) var(--ease-out), background var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.wb-omni-box:hover { border-color: var(--line-strong); }
.wb-omni-box:focus-within { border-color: var(--accent); background: var(--panel-raised); box-shadow: 0 0 0 3px var(--accent-weak); }
.wb-omni[data-busy] .wb-omni-box { border-color: var(--accent); }
.wb-omni-glass { display: inline-flex; color: var(--text-mute); flex: 0 0 auto; }
.wb-omni-input {
  flex: 1 1 auto; min-width: 0; height: 100%;
  border: 0; background: transparent; color: var(--text);
  font: inherit; font-size: var(--fs-13);
}
.wb-omni-input::placeholder { color: var(--text-mute); }
.wb-omni-input:focus, .wb-omni-input:focus-visible { outline: none; }
.wb-omni-input[aria-invalid="true"] { color: var(--text); }
.wb-omni-kbd { flex: 0 0 auto; }
.wb-omni[data-focused] .wb-omni-kbd { display: none; }
.wb-omni-spin { width: 14px; height: 14px; }
.wb-omni-clear {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 20px; height: 20px; border-radius: 50%; color: var(--text-mute);
}
.wb-omni-clear:hover { color: var(--text); background: var(--line); }
.wb-omni-clear .wb-icon { width: 12px; height: 12px; }

/* ── Dropdown (bar: floats in #popovers; sheet: fills the dialog) ─────────── */
.wb-omni-panel {
  display: flex; flex-direction: column;
  background: var(--panel-raised); color: var(--text);
}
.wb-omni-panel--float {
  position: fixed; top: 0; left: 0;
  max-height: min(70vh, 560px); overflow: auto; overscroll-behavior: contain;
  border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-3);
  padding: 4px;
  animation: wb-omni-in var(--dur) var(--ease-out);
}
@keyframes wb-omni-in { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } }

.wb-omni-msg {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  margin: 2px 2px 4px; padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm); font-size: var(--fs-13); line-height: var(--lh-tight);
  background: var(--panel-sunken); color: var(--text-dim);
}
.wb-omni-msg[data-kind="error"] { background: var(--err-weak); color: var(--err); }
.wb-omni-msg-text { display: inline-flex; align-items: flex-start; gap: 6px; flex: 1 1 220px; min-width: 0; }
.wb-omni-msg-text > span { overflow-wrap: anywhere; }
.wb-omni-msg-actions { display: inline-flex; gap: 6px; }

/* The list scrolls inside the panel so the footer hints stay pinned below it (R2-04). */
.wb-omni-list { display: flex; flex-direction: column; min-height: 0; flex: 0 1 auto; overflow: auto; overscroll-behavior: contain; }
.wb-omni-panel--float { overflow: hidden; }
.wb-omni-foot { flex: none; }
.wb-omni-list:empty { display: none; }
.wb-omni-group { display: flex; flex-direction: column; }
.wb-omni-group + .wb-omni-group { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--line); }
.wb-omni-ghead { padding: 6px var(--sp-2) 2px; }

.wb-omni-opt {
  display: flex; align-items: center; gap: var(--sp-2);
  min-height: 40px; padding: 4px var(--sp-2);
  border-radius: var(--r-sm); cursor: pointer; user-select: none;
}
.wb-omni-opt[aria-selected="true"] { background: var(--accent-weak); }
.wb-omni-opt[aria-disabled="true"] { opacity: .5; cursor: default; }
.wb-omni-lead {
  width: 32px; height: 32px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm); background: var(--panel-sunken); color: var(--text-dim); overflow: hidden;
}
.wb-omni-lead:empty { background: transparent; }
.wb-omni-opt[aria-selected="true"] .wb-omni-lead { color: var(--accent); }
.wb-omni-glyph { display: inline-flex; }
.wb-omni-thumb { width: 32px; height: 32px; object-fit: contain; display: block; }
/* A head crop (owner task 7; thumbs.js sets left, top and width in percent of the 32 px square; the lead clips the rest). */
.wb-omni-lead { position: relative; }
.wb-omni-thumb[data-crop] { position: absolute; height: auto; max-width: none; }
.wb-omni-thumb[data-thumb="loading"] { background: linear-gradient(90deg, var(--panel-sunken), var(--line), var(--panel-sunken)); background-size: 200% 100%; animation: wb-omni-shimmer 1.2s linear infinite; }
@keyframes wb-omni-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.wb-omni-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.wb-omni-label, .wb-omni-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wb-omni-label { font-size: var(--fs-13); color: var(--text); }
.wb-omni-sub { font-size: var(--fs-12); color: var(--text-dim); }
.wb-omni-sub .mono-id { font-size: var(--fs-11); }
.wb-omni-hint { flex: 0 0 auto; opacity: .55; }
.wb-omni-opt:not([aria-selected="true"]):not(.wb-omni-opt--keep) .wb-omni-hint { visibility: hidden; }
.wb-omni-opt[aria-selected="true"] .wb-omni-hint { opacity: 1; border-color: var(--accent); color: var(--accent); }
.wb-omni-opt--more .wb-omni-label { color: var(--accent); }
.wb-omni-placeholder { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2); color: var(--text-mute); font-size: var(--fs-12); }

.wb-omni-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  margin-top: 4px; padding: 6px var(--sp-2) 2px; border-top: 1px solid var(--line);
}
.wb-omni-list:empty + .wb-omni-foot { border-top: 0; margin-top: 0; }
.wb-omni-foot kbd { margin: 0 1px; }

/* ── At 900 px and below the bar collapses to an icon button that expands over the top bar ── */
@media (max-width: 900px) {
  .wb-omni--bar { width: auto; }
  .wb-omni--bar .wb-omni-trigger { display: inline-flex; }
  .wb-omni--bar:not([data-expanded]) .wb-omni-box { display: none; }
  .wb-omni--bar[data-expanded] .wb-omni-trigger { visibility: hidden; }
  .wb-omni--bar[data-expanded] .wb-omni-box {
    position: fixed; z-index: var(--z-popover);
    top: calc(var(--safe-top) + (var(--topbar-h) - 36px) / 2); left: calc(var(--sp-2) + var(--safe-left)); right: calc(var(--sp-2) + var(--safe-right));
    height: 36px; background: var(--panel-raised); box-shadow: var(--shadow-2);
  }
}

/* ── Full-screen dialog (mobile) ─────────────────────────────────────────── */
.wb-modal-scrim:has(> .wb-omni-dialog) { padding: 0; align-items: stretch; }
.wb-modal.wb-omni-dialog {
  width: 100%; max-width: none; height: 100%; max-height: none;
  border: 0; border-radius: 0; box-shadow: none;
  padding: var(--safe-top) var(--safe-right) var(--safe-bottom) var(--safe-left);
}
.wb-omni-dialog .wb-modal-head { display: none; }
.wb-omni-dialog .wb-modal-body { flex: 1 1 auto; display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.wb-omni--sheet { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.wb-omni--sheet .wb-omni-box {
  flex: 0 0 auto; height: 52px; gap: var(--sp-2); padding: 0 var(--sp-2) 0 var(--sp-1);
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: var(--panel-raised);
}
.wb-omni--sheet .wb-omni-box:focus-within { box-shadow: none; border-color: var(--line); }
.wb-omni--sheet .wb-omni-input { font-size: 16px; }
.wb-omni-go {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  min-width: var(--touch); height: 36px; padding: 0 var(--sp-3); border-radius: var(--r-sm);
  background: var(--accent); color: var(--text-on-accent); font-size: var(--fs-15); font-weight: var(--fw-semibold);
}
.wb-omni--sheet .wb-omni-panel { flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; padding: 4px var(--sp-2); }
.wb-omni--sheet .wb-omni-opt { min-height: 52px; }
.wb-omni--sheet .wb-omni-label { font-size: var(--fs-15); }
.wb-omni--sheet .wb-omni-foot { margin-top: auto; padding: var(--sp-3) var(--sp-2); position: sticky; bottom: 0; background: var(--panel-raised); }

/* ── Look name · source chip · ⟳ (topbar.look) ───────────────────────────── */
.wb-omni-look { display: flex; align-items: center; gap: 2px; min-width: 0; }
.wb-omni-name {
  display: inline-flex; align-items: center; gap: 4px; min-width: 0; max-width: 240px;
  height: 32px; padding: 0 6px 0 var(--sp-2); border-radius: var(--r-sm);
  font-size: var(--fs-13); font-weight: var(--fw-semibold); color: var(--text);
}
.wb-omni-name:hover, .wb-omni-name[aria-expanded="true"] { background: var(--panel-sunken); }
@media (pointer: coarse) { .wb-omni-name { height: var(--touch); } }   /* a 44 px target on touch (QA TABLET-TARGETS) */
.wb-omni-name-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-omni-name-edited { display: none; color: var(--text-mute); font-weight: var(--fw-regular); white-space: nowrap; }
.wb-omni-name .wb-icon { color: var(--text-mute); }
.wb-omni-rename { width: 160px; height: 30px; font-weight: var(--fw-semibold); }
.wb-omni-sep { color: var(--text-mute); padding: 0 2px; }
.wb-omni-src {
  display: inline-flex; align-items: center; min-width: 0; max-width: 360px;
  height: 26px; padding: 0 var(--sp-2); border-radius: var(--r-pill);
  border: 1px solid transparent; color: var(--text-dim); font-size: var(--fs-12);
}
.wb-omni-src:hover, .wb-omni-src[aria-expanded="true"] { background: var(--panel-sunken); border-color: var(--line); color: var(--text); }
/* The italic "Edited" ends the line: room for its overhang, or overflow: hidden cuts the d's ascender (QA2 m4). */
.wb-omni-src-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-right: 3px; }
.wb-omni-src-edited em { font-style: italic; color: var(--text); }
.wb-omni-src-saved em { font-style: normal; color: var(--ok); }
.wb-omni-refresh.wb-omni-spinning .wb-icon { animation: wb-spin .8s linear infinite; }
@media (max-width: 1279px) {
  .wb-omni-src-time { display: none; }
  .wb-omni-src { max-width: 220px; }
}
@media (max-width: 1023px) {
  .wb-omni-src, .wb-omni-sep, .wb-omni-refresh { display: none; }
  .wb-omni-name-edited { display: inline; }
  .wb-omni-name { max-width: 180px; }
}

/* ── Mobile source pill ──────────────────────────────────────────────────── */
.wb-omni-pill {
  display: flex; align-items: stretch; flex: 1 1 auto; min-width: 0; max-width: 260px;
  height: 36px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--panel-sunken);
}
.wb-omni-pill-main {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px;
  padding: 0 var(--sp-2) 0 var(--sp-3); border-radius: var(--r-pill) 0 0 var(--r-pill);
  color: var(--text); font-size: var(--fs-13); font-weight: var(--fw-semibold);
}
.wb-omni-pill-main .wb-icon { color: var(--text-mute); }
.wb-omni-pill-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-omni-pill-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex: 0 0 auto; }
.wb-omni-pill-more {
  flex: 0 0 auto; width: 40px; display: inline-flex; align-items: center; justify-content: center;
  border-left: 1px solid var(--line); border-radius: 0 var(--r-pill) var(--r-pill) 0; color: var(--text-dim);
}
.wb-omni-pill-main:active, .wb-omni-pill-more:active { background: var(--line); }
@media (max-width: 767px) {
  /* Give the source name the room (it was cut to "@Moc…" / "Pasted …" with space to spare). */
  .wb-omni-pill { max-width: none; }
  .wb-omni-pill-main { padding-right: 4px; gap: 5px; }
  .wb-omni-pill-more { width: 32px; }
}
/* Touch: both halves of the pill are 44 px targets (the Look menu ▾ 44 px wide; 44 px tall through a transparent
   ::before over the pill, the whole height of the 44 px top bar; QA2 N7). */
@media (pointer: coarse) {
  .wb-omni-pill-main, .wb-omni-pill-more { position: relative; }
  .wb-omni-pill-main::before, .wb-omni-pill-more::before { content: ''; position: absolute; inset: -5px 0; }
  .wb-omni-pill-more { width: var(--touch); }
}

/* ── Look menu ───────────────────────────────────────────────────────────── */
.wb-omni-lookmenu { padding: 4px; }
.wb-omni-mi { min-height: 34px; padding-top: 3px; padding-bottom: 3px; }
.wb-omni-mi > .wb-menu-check { width: auto; min-width: 16px; color: var(--text-dim); }
.wb-omni-mi > .wb-menu-label { display: flex; flex-direction: column; }
.wb-omni-mi-sub { font-size: var(--fs-11); color: var(--text-mute); overflow: hidden; text-overflow: ellipsis; }
.wb-omni-lm-thumb { display: block; width: 28px; height: 28px; object-fit: contain; border-radius: var(--r-xs); background: var(--panel-sunken); }
.wb-omni-lm-src { padding: 6px var(--sp-2) var(--sp-2); display: grid; gap: 2px; }
.wb-omni-lm-name { font-weight: var(--fw-semibold); font-size: var(--fs-13); }
.wb-omni-lm-empty { padding: 2px var(--sp-2) var(--sp-2); }

/* ── Share ───────────────────────────────────────────────────────────────── */
.wb-omni-share { padding: var(--sp-3); display: grid; gap: var(--sp-3); }
.wb-omni-share-sec { display: grid; gap: 6px; min-width: 0; }
.wb-omni-share-sec + .wb-omni-share-sec { padding-top: var(--sp-3); border-top: 1px solid var(--line); }
.wb-omni-share-url { width: 100%; font-family: var(--font-mono); font-size: var(--fs-11); color: var(--text-dim); }
.wb-omni-share-size { font-weight: var(--fw-regular); opacity: .85; }
.wb-omni-share-fmts { display: flex; flex-wrap: wrap; gap: 6px; }
.wb-omni-share-warn {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  padding: 6px var(--sp-2); border-radius: var(--r-sm);
  background: var(--warn-weak); color: var(--warn); font-size: var(--fs-12);
}
.wb-omni-share-status:empty { display: none; }
.wb-omni-share-status { color: var(--ok); }
@media (max-width: 1279px) {
  .wb-omni-sharebtn > span { display: none; }
  .wb-omni-sharebtn { width: 32px; padding: 0; }
}
@media (max-width: 767px), (pointer: coarse) {
  .wb-omni-share-url { font-size: 16px; }
}

/* ── css/features/outfit.css */
/* Feature styles: outfit. Owned by the outfit feature; use tokens only, classes prefixed per §17.9. */

.wb-out { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.wb-out-open { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.wb-out-rail { display: none; }

/* ── Header ─────────────────────────────────────────────────────────────── */
.wb-out-head { flex: 0 0 auto; display: grid; gap: var(--sp-2); padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line); }
.wb-out-head-row { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.wb-out-title {
  font-size: var(--fs-11); font-weight: var(--fw-semibold); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--text-mute); flex: 0 0 auto;
}
.wb-out-views { flex: 0 0 auto; }
.wb-out-views > button { padding: 0 var(--sp-2); font-size: var(--fs-12); }
.wb-out-search { flex: 1 1 auto; min-width: 0; }
.wb-out-filter { height: var(--control-h); font-size: var(--fs-13); }
.wb-out-filter::-webkit-search-cancel-button { cursor: pointer; }
.wb-out-add { flex: 0 0 auto; height: var(--control-h); }

/* ── Character row (pinned) ─────────────────────────────────────────────── */
.wb-out-char {
  flex: 0 0 auto; position: relative;
  display: flex; align-items: center; gap: var(--sp-2);
  min-height: 44px; padding: var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-3);
  border-bottom: 1px solid var(--line); cursor: pointer; user-select: none; -webkit-user-select: none;
}
.wb-out-char:hover { background: var(--panel-sunken); }
.wb-out-char[aria-pressed="true"] { background: var(--accent-weak); box-shadow: inset 2px 0 0 var(--accent); }
.wb-out-char:focus-visible { outline-offset: -2px; }
.wb-out-char-icon {
  width: 28px; height: 28px; flex: 0 0 auto; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--panel-sunken); color: var(--text-dim);
}
.wb-out-char-label { font-weight: var(--fw-semibold); font-size: var(--fs-13); }
.wb-out-char-meta { color: var(--text-dim); font-size: var(--fs-12); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.wb-out-chev { color: var(--text-mute); display: inline-flex; flex: 0 0 auto; }
.wb-out-dots { display: inline-flex; gap: 4px; align-items: center; }
.wb-out-dot {
  width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text) 18%, transparent);
}
.wb-out-dot[data-kind="none"] { background: transparent; box-shadow: inset 0 0 0 1.5px var(--line-strong); }

/* ── Body and views ─────────────────────────────────────────────────────── */
.wb-out-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; position: relative; }
.wb-out-view { display: none; flex: 1 1 auto; min-height: 0; flex-direction: column; }
/* The view being left fades out over the new one (outfit/index.js swapView). */
.wb-out-view[data-leaving] { display: flex !important; position: absolute; inset: 0; z-index: 2; background: var(--panel); pointer-events: none; animation: wb-fade-out var(--dur-1) var(--ease-standard) forwards; }
.wb-out[data-view="items"] .wb-out-view[data-view="items"],
.wb-out[data-view="layers"] .wb-out-view[data-view="layers"],
.wb-out[data-view="json"] .wb-out-view[data-view="json"] { display: flex; }
.wb-out[data-view="json"] .wb-out-char { display: none; }

.wb-out-list {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  padding: var(--sp-1) 0; position: relative; overscroll-behavior: contain;
}
.wb-out-list:empty { flex: 0 0 auto; padding: 0; }
.wb-out-state { flex: 1 1 auto; min-height: 0; overflow: auto; }

/* ── Item row ───────────────────────────────────────────────────────────── */
.wb-out-row {
  position: relative; overflow: hidden;
  user-select: none; -webkit-user-select: none;
  touch-action: pan-y;
}
.wb-out-row:focus-visible { outline-offset: -2px; }
.wb-out-main {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 6px;
  min-height: 40px; padding: 2px var(--sp-1) 3px 2px;
  background: var(--panel);
  cursor: default;
  transition: transform var(--dur) var(--ease-out);
}
.wb-out-row:hover > .wb-out-main { background: color-mix(in srgb, var(--panel-sunken) 60%, var(--panel)); }
.wb-out-row[aria-selected="true"] > .wb-out-main { background: color-mix(in srgb, var(--accent) 10%, var(--panel)); box-shadow: inset 2px 0 0 var(--accent); }
.wb-out-row[data-hidden] .wb-out-thumb,
.wb-out-row[data-hidden] .wb-out-text { opacity: .5; }
.wb-out-list[data-soloing] .wb-out-row[data-dimmed] .wb-out-thumb,
.wb-out-list[data-soloing] .wb-out-row[data-dimmed] .wb-out-text,
.wb-out-row--layer[data-dimmed] .wb-out-text { opacity: .4; }

.wb-out-grip {
  width: 12px; flex: 0 0 auto; display: inline-flex; justify-content: center;
  color: var(--text-mute); opacity: 0; cursor: grab; touch-action: none;
}
.wb-out-row:hover .wb-out-grip, .wb-out-row:focus-within .wb-out-grip { opacity: 1; }
.wb-out-row[data-nodrag] .wb-out-grip { visibility: hidden; }

.wb-out-thumb {
  position: relative; flex: 0 0 auto;
  width: 28px; height: 28px; border-radius: var(--r-xs); overflow: hidden;
  background: var(--panel-sunken);
  display: inline-flex; align-items: center; justify-content: center;
}
/* The item alone (thumbs 'isolated'), cropped to its pixels: a 2 px margin keeps it off the rounded edge. It sits on the
   neutral thumbnail tile in both themes (dark hair and white items both read on it). */
.wb-out-thumb > img { position: absolute; inset: 2px; width: calc(100% - 4px); height: calc(100% - 4px); object-fit: contain; }
.wb-out-thumb > img:not([data-thumb="ok"]):not([data-thumb="stale"]):not([data-thumb="interim"]) { opacity: 0; }
.wb-out-thumb > img[data-thumb="stale"] { opacity: .55; }
.wb-out-thumb:has(> img[data-thumb="ok"]), .wb-out-thumb:has(> img[data-thumb="stale"]), .wb-out-thumb:has(> img[data-thumb="interim"]) { background: var(--thumb-tile); }
/* The catalog's picture of a hat, hair or face item is the whole avatar: it is cropped to the head or the face (thumbs.js
   placeCrop: left, top and width inline, in percent of this square tile). While the item render is not there yet it stands
   in (data-thumb="interim", thumbs `interim`) and the render fades in over it (data-swap). */
.wb-out-thumb > img[data-crop] { inset: auto; height: auto; max-width: none; }
.wb-out-thumb > img[data-swap] { animation: wb-fade-in var(--dur-2) var(--ease-standard); }
/* Catalog art standing in for a render (an effect with no still picture of its own): particle and glow art is often
   faint white, which vanishes on the light tile; a soft dark edge keeps it readable (QA2 m9). */
.wb-out-thumb > img[data-thumb-src="catalog"]:not([data-crop]), .wb-out-rail-item img[data-thumb-src="catalog"]:not([data-crop]) { filter: drop-shadow(0 0 1px rgb(0 0 0 / .55)) drop-shadow(0 0 2px rgb(0 0 0 / .3)); }
/* No still picture of its own (an effect, an animated item) and no catalog art: a sparkle instead of the type letters. */
.wb-out-thumb-none { display: none; color: var(--text-mute); }
.wb-out-thumb:has(> img[data-thumb="none"]) .wb-out-thumb-glyph { display: none; }
.wb-out-thumb:has(> img[data-thumb="none"]) .wb-out-thumb-none { display: inline-flex; }
.wb-out-thumb:has(> img[data-thumb="loading"]) {
  background: linear-gradient(90deg, var(--panel-sunken) 0%, var(--line) 50%, var(--panel-sunken) 100%);
  background-size: 200% 100%; animation: wb-shimmer 1.2s linear infinite;
}
.wb-out-thumb-glyph {
  font-size: var(--fs-11); font-weight: var(--fw-semibold); color: var(--text-mute); letter-spacing: .02em;
}
.wb-out-thumb:has(> img[data-thumb="ok"]) .wb-out-thumb-glyph,
.wb-out-thumb:has(> img[data-thumb="stale"]) .wb-out-thumb-glyph,
.wb-out-thumb:has(> img[data-thumb="interim"]) .wb-out-thumb-glyph,
.wb-out-thumb:has(> img[data-thumb="loading"]) .wb-out-thumb-glyph { visibility: hidden; }
/* No image: the one shared placeholder glyph (core/img.js), as in the Catalog and the Inspector (Q5). As specific as the
   item-alone rule above (which hides an <img> that isn't "ok" or "stale"), and later, so the placeholder shows. */
.wb-out-thumb > img[data-img]:not([data-thumb="none"]) { opacity: 1; }
.wb-out-thumb:has(> img[data-img]) .wb-out-thumb-glyph { visibility: hidden; }

.wb-out-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 0; }
.wb-out-line1, .wb-out-line2 { display: flex; align-items: center; gap: 6px; min-width: 0; }
.wb-out-line1 { min-height: 22px; }
.wb-out-line1 > .wb-spacer { min-width: 0; }
.wb-out-acts { display: inline-flex; align-items: center; flex: 0 0 auto; margin-right: -2px; }
.wb-out-name {
  font-size: var(--fs-13); line-height: 16px; color: var(--text); font-weight: var(--fw-medium);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.wb-out-name--id { font-style: italic; font-weight: var(--fw-regular); color: var(--text-dim); }
.wb-out-name--loading {
  color: transparent; border-radius: var(--r-xs);
  background: linear-gradient(90deg, var(--panel-sunken) 0%, var(--line) 50%, var(--panel-sunken) 100%);
  background-size: 200% 100%; animation: wb-shimmer 1.2s linear infinite;
}
.wb-out-line2 { font-size: var(--fs-11); line-height: 14px; color: var(--text-mute); }
.wb-out-id {
  font-size: var(--fs-11); flex: 1 1 auto; min-width: 0; color: var(--text-mute);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Line 2 reads id … palette · style · type: the type sits at the right edge so a list can be scanned by it, and the
   id gives way (ellipsis) before it would touch it (owner, 2026-10-14). */
.wb-out-line2 > .wb-out-pal { order: 1; }
.wb-out-line2 > .wb-out-style { order: 2; }
.wb-out-line2 > .wb-out-tag { order: 3; margin-left: auto; }
.wb-out-line2 > .wb-out-tag::before, .wb-out-line2 > .wb-out-pal::before { content: none; }
.wb-out-tag { flex: 0 0 auto; white-space: nowrap; color: var(--text-dim); }
.wb-out-tag[data-kind="warn"] { color: var(--warn); font-weight: var(--fw-medium); }
/* A hair back worn with another hair's front (docs/50 §5.4) is said in words ("Mixed back") and by a dashed connector,
   in the row's own quiet colours: the Studio base opens that way, so it must not look like something wrong. */

/* ── Hair pair: one Hair row, its back an indented child joined by a connector (docs/50 §5.4) ── */
.wb-out-pairline { display: none; }
.wb-out-row[data-child] > .wb-out-main { padding-left: 32px; }
.wb-out-row[data-child] .wb-out-grip { display: none; }
.wb-out-row[data-child] .wb-out-pairline {
  display: block; position: absolute; left: 22px; top: -2px; width: 7px; height: calc(50% + 2px);
  border-left: 1.5px solid var(--line-strong, var(--line)); border-bottom: 1.5px solid var(--line-strong, var(--line));
  border-bottom-left-radius: 4px; pointer-events: none;
}
.wb-out-row[data-child][data-mixed] .wb-out-pairline { border-left-style: dashed; border-bottom-style: dashed; }
.wb-out-pal { display: inline-flex; align-items: center; gap: 3px; flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.wb-out-paldot { width: 8px; height: 8px; border-radius: 50%; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text) 25%, transparent); }
.wb-out-paldot[data-kind="index"] { background: var(--text-mute); box-shadow: none; }
.wb-out-style {
  flex: 0 0 auto; padding: 0 4px; border-radius: var(--r-xs); line-height: 14px;
  background: var(--panel-sunken); color: var(--text-dim); font-weight: var(--fw-semibold);
}
.wb-out-pill {
  flex: 0 0 auto; padding: 0 6px; border-radius: var(--r-pill); line-height: 16px;
  background: var(--accent); color: var(--text-on-accent); font-size: var(--fs-11); font-weight: var(--fw-semibold);
  animation: wb-out-pop var(--dur-drawer) var(--ease-out);
}
@keyframes wb-out-pop { from { transform: scale(.6); opacity: 0; } to { transform: none; opacity: 1; } }
.wb-out-warn-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--warn); flex: 0 0 auto; }
.wb-out-lock { display: inline-flex; color: var(--text-mute); flex: 0 0 auto; }

.wb-out-act {
  flex: 0 0 auto; width: 24px; height: 22px; border-radius: var(--r-xs);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-mute);
}
.wb-out-act:hover { background: var(--panel-sunken); color: var(--text); }
.wb-out-act .wb-icon { width: 16px; height: 16px; }
.wb-out-eye[aria-pressed="true"] { color: var(--text); }
.wb-out-solo[aria-pressed="true"] { color: var(--accent); background: var(--accent-weak); }
.wb-out-solo[aria-pressed="true"] .wb-icon { stroke-width: 2.5; }
.wb-out-more[aria-expanded="true"] { background: var(--accent-weak); color: var(--accent); }

/* new + warning states */
.wb-out-row[data-new] > .wb-out-main { background: color-mix(in srgb, var(--accent) 6%, var(--panel)); transition: background var(--dur-2) var(--ease-standard); }
/* A newly equipped row comes in with a fade and a --shift-2 drop (design 40 §B.4), and its tint fades out later. */
.wb-out-row[data-new], .wb-out-row[data-back] { animation: wb-out-row-in var(--dur-2) var(--ease-standard); }   /* (data-back: undo or redo brought it back: no tint, no pill) */
@keyframes wb-out-row-in { from { opacity: 0; transform: translateY(calc(-1 * var(--shift-2))); } to { opacity: 1; transform: none; } }
.wb-out-row[data-new][aria-selected="true"] > .wb-out-main { background: color-mix(in srgb, var(--accent) 12%, var(--panel)); }

/* Ghost row (evicted, 4 s) */
.wb-out-ghost > .wb-out-main {
  background: transparent; min-height: 32px; padding-left: var(--sp-3);
  border: 1px dashed var(--line-strong); border-radius: var(--r-sm); margin: 2px var(--sp-2);
  animation: wb-out-ghost 4s linear forwards;
}
.wb-out-ghost .wb-out-name { color: var(--text-dim); text-decoration: line-through; font-weight: var(--fw-regular); }
.wb-out-ghost-icon { color: var(--text-mute); display: inline-flex; }
.wb-out-ghost-by { color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wb-out-ghost-undo {
  flex: 0 0 auto; font-size: var(--fs-12); font-weight: var(--fw-semibold); color: var(--accent);
  padding: 2px 8px; border-radius: var(--r-xs);
}
.wb-out-ghost-undo:hover { background: var(--accent-weak); }
@keyframes wb-out-ghost { 0%, 80% { opacity: 1; } 100% { opacity: .35; } }

/* Swipe actions (touch): Hide and Remove behind the row */
.wb-out-swipe {
  position: absolute; inset: 0 0 0 auto; width: 144px; z-index: 0;
  display: flex;
}
.wb-out-swipe-btn { flex: 1 1 0; font-size: var(--fs-12); font-weight: var(--fw-semibold); color: var(--text-on-accent); }
.wb-out-swipe-btn[data-kind="hide"] { background: var(--text-dim); }
.wb-out-swipe-btn[data-kind="remove"] { background: var(--err); }
.wb-out-swipe-btn:disabled { opacity: .5; }
.wb-out-row:not([data-swiped]):not([data-swiping]) .wb-out-swipe { visibility: hidden; }
.wb-out-row[data-swiping] > .wb-out-main { transition: none; }
.wb-out-row[data-swiped] > .wb-out-main { transform: translateX(-144px); }

/* Drag to reorder */
.wb-out[data-dragging] { cursor: grabbing; }
.wb-out-list[data-dragging] .wb-out-ghost { display: none; }
.wb-out-list[data-dragging] .wb-out-row { transition: transform var(--dur) var(--ease-out); }
.wb-out-list[data-dragging] .wb-out-row[data-drag-row],
.wb-out-list[data-dragging] .wb-out-row[data-drag-follow] {
  transition: none; z-index: 3; overflow: visible;
}
.wb-out-list[data-dragging] .wb-out-row[data-drag-row] > .wb-out-main,
.wb-out-list[data-dragging] .wb-out-row[data-drag-follow] > .wb-out-main {
  box-shadow: var(--shadow-2), inset 2px 0 0 var(--accent); border-radius: var(--r-sm);
  background: var(--panel-raised);
}
.wb-out-list[data-drop-settle] .wb-out-row { transition: none; }

/* ── Layers view ────────────────────────────────────────────────────────── */
.wb-out-layers-top { flex: 0 0 auto; padding: var(--sp-2) var(--sp-2) 0; }
.wb-out-banner {
  display: flex; gap: 6px; align-items: flex-start;
  padding: var(--sp-2); border-radius: var(--r-sm);
  background: var(--panel-sunken); color: var(--text-dim); font-size: var(--fs-12); line-height: var(--lh-tight);
}
.wb-out-banner > .wb-icon { margin-top: 1px; flex: 0 0 auto; color: var(--text-mute); }
.wb-out-card { display: grid; gap: 4px; padding: var(--sp-2); border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--panel-raised); }
.wb-out-layer-num {
  flex: 0 0 auto; min-width: 22px; text-align: right; padding-right: 2px;
  font-family: var(--font-mono); font-size: var(--fs-11); color: var(--text-mute); font-variant-numeric: tabular-nums;
}
.wb-out-row--layer > .wb-out-main { min-height: 32px; }

/* ── Empty / loading / no match ─────────────────────────────────────────── */
.wb-out-empty { display: grid; gap: var(--sp-2); padding: var(--sp-4) var(--sp-3); text-align: center; justify-items: center; }
.wb-out-empty p { margin: 0; }
.wb-out-empty-title { font-weight: var(--fw-semibold); }
.wb-out-empty-actions { display: grid; gap: var(--sp-2); width: 100%; max-width: 220px; margin-top: var(--sp-2); }
.wb-out-loading { display: grid; padding: var(--sp-1) 0; }
.wb-out-skelrow { display: flex; align-items: center; gap: var(--sp-2); height: 36px; padding: 0 var(--sp-3) 0 var(--sp-5); }
.wb-out-skelrow .wb-skel:first-child { flex: 0 0 auto; }
.wb-out-skeltext { flex: 1 1 auto; display: grid; gap: 0; }
.wb-out-skeltext .wb-skel { margin: 3px 0; height: 8px; }

/* ── Footer ─────────────────────────────────────────────────────────────── */
/* .wb-foot (components.css): one 44 px line; the parts after the count give way first (ellipsis, whole text in the
   footer's tooltip). */
.wb-out-foot { gap: 0; }
.wb-out-foot > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; }
.wb-out-foot > span + span::before { content: ' · '; white-space: pre; color: var(--text-mute); }
.wb-out-foot-count { font-variant-numeric: tabular-nums; flex: 0 0 auto !important; }
.wb-out-foot-fill { color: var(--text-mute); }
.wb-out-foot-fill-label { color: var(--text-dim); }
.wb-out-foot-cat { color: var(--text-mute); }
.wb-out[data-view="json"] .wb-out-foot { display: none; }

/* ── JSON view ──────────────────────────────────────────────────────────── */
.wb-out-json { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.wb-out-json-bar {
  flex: 0 0 auto; display: flex; align-items: center; gap: var(--sp-1);
  min-height: 36px; padding: var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-3);
  border-bottom: 1px solid var(--line); font-size: var(--fs-12); color: var(--text-dim);
}
.wb-out-json-state { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-out-json[data-dirty] .wb-out-json-bar { background: var(--warn-weak); color: var(--text); }
.wb-out-json-edit {
  flex: 1 1 auto; min-height: 120px; display: flex; overflow: hidden;
  background: var(--panel-sunken);
  font-family: var(--font-mono); font-size: var(--fs-12); line-height: 18px;
}
.wb-out-json-gutter {
  flex: 0 0 auto; overflow: hidden; padding: var(--sp-2) 6px var(--sp-2) var(--sp-1);
  min-width: 30px; text-align: right; color: var(--text-mute); user-select: none; -webkit-user-select: none;
  border-right: 1px solid var(--line); background: var(--panel);
}
.wb-out-json-ln { height: 18px; font-variant-numeric: tabular-nums; }
.wb-out-json-ln[data-state="err"] { color: var(--err); font-weight: var(--fw-semibold); background: var(--err-weak); box-shadow: inset 2px 0 0 var(--err); }
.wb-out-json-ln[data-state="warn"] { color: var(--warn); background: var(--warn-weak); }
.wb-out-json-ta {
  flex: 1 1 auto; min-width: 0; resize: none; border: 0; margin: 0;
  padding: var(--sp-2); background: transparent; color: var(--text);
  font: inherit; line-height: inherit; tab-size: 2; white-space: pre; overflow: auto;
}
.wb-out-json-ta:focus { outline: none; box-shadow: inset 0 0 0 2px var(--focus); }
/* Wrap lines (default): the text is laid out for the column (json.js), so only an id wider than the column itself
   still wraps: it fills its line and goes on below (never split at its hyphen as if the id ended there), indented
   under its line where the browser supports a hanging indent. */
.wb-out-json-edit { position: relative; }
.wb-out-json[data-wrap] .wb-out-json-ta { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; overflow-x: hidden; text-indent: 6ch hanging each-line; }
.wb-out-json-measure {
  position: absolute; left: 0; top: 0; visibility: hidden; pointer-events: none;
  white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all; tab-size: 2;
}
.wb-out-json-ta[aria-invalid="true"]:focus { box-shadow: inset 0 0 0 2px var(--err); }
.wb-out-json-msgs { flex: 0 0 auto; display: grid; max-height: 35%; overflow: auto; border-top: 1px solid var(--line); }
.wb-out-json-msgs:empty { display: none; }
.wb-out-json-msg {
  display: flex; gap: 6px; align-items: flex-start; text-align: left;
  padding: 6px var(--sp-3); font-size: var(--fs-12); line-height: var(--lh-tight); color: var(--text);
}
.wb-out-json-msg:hover { background: var(--panel-sunken); }
.wb-out-json-msg > .wb-icon { flex: 0 0 auto; margin-top: 1px; }
.wb-out-json-msg[data-kind="err"] > .wb-icon { color: var(--err); }
.wb-out-json-msg[data-kind="warn"] > .wb-icon { color: var(--warn); }
.wb-out-json-msg strong { font-weight: var(--fw-semibold); }

/* ── Rail (44 px, ⇧O) — desktop and laptop only ─────────────────────────── */
@media (min-width: 1024px) {
  #panel-outfit[data-mode="rail"] .wb-out-open { display: none; }
  #panel-outfit[data-mode="rail"] .wb-out-rail { animation: wb-fade-in var(--dur-2) var(--ease-standard); }
  #panel-outfit[data-mode="rail"] .wb-out-rail {
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; align-items: center; gap: 4px;
    padding: var(--sp-2) 0;
  }
}
.wb-out-rail .wb-iconbtn { width: 32px; height: 32px; flex: 0 0 auto; }
.wb-out-rail .wb-iconbtn--sm { width: 28px; height: 28px; }
.wb-out-rail-sep { width: 24px; height: 1px; background: var(--line); margin: 2px 0; flex: 0 0 auto; }
.wb-out-rail-list {
  flex: 1 1 auto; min-height: 0; width: 100%; overflow-y: auto; overflow-x: hidden; scrollbar-width: none;
  display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 2px 0;
}
.wb-out-rail-list::-webkit-scrollbar { display: none; }
.wb-out-rail-item {
  position: relative; flex: 0 0 auto; width: 34px; height: 34px; padding: 1px;
  border-radius: var(--r-sm); display: inline-flex; align-items: center; justify-content: center;
}
.wb-out-rail-item:hover { background: var(--panel-sunken); }
.wb-out-rail-item[aria-pressed="true"] { box-shadow: inset 0 0 0 2px var(--accent); background: var(--accent-weak); }
.wb-out-rail-item[data-hidden] .wb-out-thumb { opacity: .4; }
.wb-out-rail-item[data-dimmed] .wb-out-thumb { opacity: .4; }
.wb-out-rail-item[data-new]::after {
  content: ''; position: absolute; top: 1px; right: 1px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 2px var(--panel);
}
.wb-out-rail-item[data-warn]::before {
  content: ''; position: absolute; bottom: 1px; right: 1px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--warn); box-shadow: 0 0 0 2px var(--panel);
}
.wb-out-rail-thumb { width: 30px; height: 30px; }
.wb-out-rail-count { font-size: var(--fs-11); color: var(--text-mute); font-variant-numeric: tabular-nums; }

/* The collapse button only makes sense where the panel is docked. */
@media (max-width: 1023px) { .wb-out-collapse { display: none; } }

/* ── Touch and mobile: 52 px rows, 44 px targets, no hover-only affordances ─ */
@media (pointer: coarse), (max-width: 767px) {
  .wb-out-main { min-height: 52px; gap: var(--sp-2); padding: 2px var(--sp-1) 2px 4px; }
  .wb-out-row--layer > .wb-out-main { min-height: 52px; }
  .wb-out-thumb { width: 40px; height: 40px; }
  .wb-out-name { font-size: 15px; line-height: 20px; }
  .wb-out-line2 { font-size: var(--fs-12); line-height: 16px; }
  .wb-out-id { font-size: var(--fs-12); }
  .wb-out-grip { opacity: 1; width: 20px; }
  .wb-out-row[data-child] > .wb-out-main { padding-left: 46px; }
  .wb-out-row[data-child] .wb-out-pairline { left: 36px; width: 8px; }
  .wb-out-act { width: 40px; height: 32px; }
  .wb-out-line1 { min-height: 32px; }
  .wb-out-act .wb-icon { width: 20px; height: 20px; }
  .wb-out-char { min-height: 52px; }
  .wb-out-skelrow { height: 52px; }
  .wb-out-add, .wb-out-filter { height: 36px; }
}
@media (max-width: 767px) {
  /* More room for the equipped list (owner task 6). The pinned Character row is the tab bar's Character tab; the item
     count (and how many are hidden) is in the sheet's title, so the footer opens only for a note the title does not say
     ("Filled by default", the catalog loading: data-note, layout.css). */
  .wb-out-char { display: none; }
  .wb-out-head { padding: var(--sp-2) var(--sp-3); }
  .wb-out-head-row > .wb-spacer { display: none; }
  .wb-out-views { flex: 1 1 auto; display: flex; }
  .wb-out-views > button { flex: 1 1 0; min-height: 36px; font-size: var(--fs-13); }
  .wb-out-search-toggle { width: 40px !important; height: 40px !important; flex: 0 0 auto; }
  .wb-out-filter-row { display: none; }
  .wb-out[data-search-open] .wb-out-filter-row, .wb-out[data-filtering] .wb-out-filter-row { display: flex; }
  .wb-out[data-view="json"] .wb-out-search-toggle { display: none; }
  .wb-out-foot { padding: 0 var(--sp-3); }
  .wb-out-char { min-height: 48px; }
  .wb-out-swipe { width: 144px; }
  .wb-out-json-edit { font-size: 13px; line-height: 20px; }
  .wb-out-json-ln { height: 20px; }
  .wb-out-json-ta { font-size: 16px; line-height: 20px; }
}
/* Touch: Hide, Solo and ⋯ are full 44 px targets — 44 px wide, and 44 px tall through a transparent ::before over the
   row's id line (the button itself stays 32 px, so the row keeps its two lines). The row is 56 px with its name line
   6 px down, so the whole 44 px target is inside the row, never over the row above (QA2 N7). */
@media (pointer: coarse) {
  .wb-out-row:not(.wb-out-row--layer) > .wb-out-main { min-height: 56px; padding-top: 6px; padding-bottom: 2px; }
  .wb-out-act { width: var(--touch); position: relative; }
  .wb-out-act::before { content: ''; position: absolute; inset: -6px 0; }
}
/* On narrow phones the solo button moves into ⋯ (still mirrored there) so names keep room. */
@media (max-width: 359px) { .wb-out-solo { display: none; } }

/* Draw list (Browser engine, caps.layers.inspect): the resolved paint order, back to front. */
.wb-out-drawcard { margin-bottom: var(--sp-2); }
.wb-out-drawcard .wb-out-draw { transition: opacity var(--dur-2) var(--ease-standard); }
.wb-out-drawcard[data-stale] .wb-out-draw { opacity: .5; }
.wb-out-draw { list-style: none; margin: 0; padding: 0; max-height: 260px; overflow: auto; display: grid; gap: 1px; }
.wb-out-draw-row {
  display: flex; align-items: center; gap: 6px; min-height: 24px; padding: 0 4px; border-radius: var(--r-xs, 4px);
  font-size: var(--fs-12); color: var(--text); cursor: default;
}
.wb-out-draw-row[tabindex] { cursor: pointer; }
.wb-out-draw-row[tabindex]:hover { background: var(--panel-sunken); }
.wb-out-draw-row[aria-current="true"] { background: var(--accent-weak); }
.wb-out-draw-name { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-out-draw-slot { flex: 0 0 auto; max-width: 48%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-11); color: var(--text-mute); }

/* ── Items view sections: Avatar items, then Clothing items (owner, 2026-10-14) ── */
.wb-out-section {
  display: flex; align-items: baseline; gap: 6px; padding: 10px 12px 4px;
  font-size: var(--fs-11); font-weight: var(--fw-medium); letter-spacing: .02em; color: var(--text-dim);
  user-select: none;
}
.wb-out-section:first-child { padding-top: 4px; }
.wb-out-section-count { color: var(--text-mute); font-weight: var(--fw-regular); font-variant-numeric: tabular-nums; }

/* ── Not on the avatar yet (owner, 2026-10-14): a row whose item the live avatar does not show yet pulses until it does
   (live-stage.js 'live:pending'); reduced motion: a steady tint instead. ── */
@keyframes wb-out-pending { 0%, 100% { background-color: transparent; } 50% { background-color: var(--accent-weak); } }
.wb-out-row[data-pending] > .wb-out-main { animation: wb-out-pending 1.1s ease-in-out infinite; border-radius: var(--radius-sm, 6px); }
.wb-out-row[data-pending] .wb-out-thumb { animation: wb-out-pending-thumb 1.1s ease-in-out infinite; }
@keyframes wb-out-pending-thumb { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) {
  .wb-out-row[data-pending] > .wb-out-main { background-color: var(--accent-weak); }
}

/* ── css/features/inspector.css */
/* Feature styles: inspector. Owned by the inspector feature; tokens only, classes prefixed .wb-ins- (spec §17.9). */

.wb-ins { font-size: var(--fs-13); }
.wb-ins kbd {
  display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 3px;
  border: 1px solid var(--line); border-radius: var(--r-xs); background: var(--panel-sunken);
  font: var(--fw-medium) 10px/1 var(--font-ui); color: var(--text-mute);
}
.wb-ins-keys { display: inline-flex; gap: 2px; align-items: center; color: var(--text-mute); font-size: var(--fs-11); }
.wb-ins-fine { font-size: var(--fs-11); color: var(--text-mute); margin: 0; }
.wb-ins-link {
  color: var(--accent); font-size: inherit; text-decoration: underline; text-underline-offset: 2px; padding: 0;
}
.wb-ins-link:hover { color: var(--accent-hover); }
.wb-ins-badge {
  display: inline-flex; align-items: center; height: 18px; padding: 0 6px; margin-left: 4px;
  border-radius: var(--r-pill); background: var(--panel-sunken); border: 1px solid var(--line);
  color: var(--text-dim); font-size: var(--fs-11); font-weight: var(--fw-medium); white-space: nowrap;
}

/* ── Sections ──────────────────────────────────────────────────────────── */
.wb-ins-sec { border-bottom: 1px solid var(--line); }
.wb-ins-sec-bar { display: flex; align-items: center; gap: var(--sp-2); padding: 0 var(--sp-3) 0 var(--sp-2); min-height: 36px; }
.wb-ins-sec-head {
  display: inline-flex; align-items: center; gap: 4px; flex: 1 1 auto; min-width: 0; height: 36px; padding: 0 4px;
  color: var(--text-mute); text-align: left; border-radius: var(--r-xs);
}
.wb-ins-sec-head:hover { color: var(--text); }
.wb-ins-sec-head > .wb-icon { transition: transform var(--dur) var(--ease-out); }
.wb-ins-sec-head[aria-expanded="false"] > .wb-icon { transform: rotate(-90deg); }
.wb-ins-sec-head:hover .wb-label { color: var(--text-dim); }
.wb-ins-sec-extra { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.wb-ins-sec-value { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; }
.wb-ins-sec-body { padding: 0 var(--sp-3) var(--sp-3); display: grid; gap: var(--sp-2); min-width: 0; }

/* ── Item header ───────────────────────────────────────────────────────── */
.wb-ins-head { display: flex; gap: var(--sp-3); padding: var(--sp-3); border-bottom: 1px solid var(--line); align-items: center; }
.wb-ins-thumb {
  flex: 0 0 auto; width: 72px; height: 72px; border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--line); display: grid; place-items: center; padding: 3px;
  /* One fixed cell: an auto row would size to the image, so a portrait (trimmed) item render would be drawn at the
     tile's width and cut off at the bottom (QA P1-inspector-thumb-crop). */
  grid-template: minmax(0, 1fr) / minmax(0, 1fr);
  /* As the outfit rows: a plain tile while there is no picture (loading, ✦, failed), the neutral thumbnail tile under
     a picture; never the stage checker (QA P8). */
  background: var(--panel-sunken);
}
.wb-ins-thumb:has(.wb-ins-thumb-img[data-thumb="ok"]), .wb-ins-thumb:has(.wb-ins-thumb-img[data-thumb="stale"]) { background: var(--thumb-tile); }
.wb-ins-thumb-img { width: 100%; height: 100%; min-width: 0; min-height: 0; object-fit: contain; grid-area: 1 / 1; }
.wb-ins-thumb-img:not([src]) { visibility: hidden; }
/* While a new palette or style renders, the previous picture stays, dimmed (§6.6). */
.wb-ins-thumb-img[data-thumb="stale"] { opacity: .5; }
.wb-ins-thumb-img[data-thumb-src="catalog"] { filter: drop-shadow(0 0 1px rgb(0 0 0 / .55)) drop-shadow(0 0 3px rgb(0 0 0 / .3)); }   /* QA2 m9, as the rows */
/* A sparkle when the item has no still picture of its own. */
.wb-ins-thumb-none { display: none; grid-area: 1 / 1; color: var(--text-mute); }
.wb-ins-thumb:has(.wb-ins-thumb-img[data-thumb="none"]) .wb-ins-thumb-none { display: inline-flex; }
.wb-ins-thumb:has(.wb-ins-thumb-img[data-thumb="loading"]) { animation: wb-ins-pulse 1.2s ease-in-out infinite; }
.wb-ins-ident { display: grid; gap: 2px; min-width: 0; flex: 1 1 auto; }
.wb-ins-namerow { display: flex; align-items: center; gap: 2px; min-width: 0; }
.wb-ins-fav { flex: 0 0 auto; margin: -6px -4px -6px 0; }
.wb-ins-name { flex: 0 1 auto; min-width: 0; font-size: var(--fs-15); font-weight: var(--fw-semibold); line-height: var(--lh-tight); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-ins-idrow { display: flex; align-items: center; gap: 2px; min-width: 0; }
.wb-ins-idrow .mono-id { flex: 0 1 auto; }
.wb-ins-type { white-space: nowrap; }

/* ── Appearance ────────────────────────────────────────────────────────── */
.wb-ins-field { display: grid; gap: 6px; }
.wb-ins-field-head { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.wb-ins-field-label { font-weight: var(--fw-medium); }
.wb-ins-style { display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: var(--sp-2); align-items: center; }
.wb-ins-style-thumb {
  position: relative; margin: 0; width: 56px; height: 56px; border-radius: var(--r-sm); overflow: hidden;
  border: 1px solid var(--line); display: grid; place-items: center; padding: 2px;
  grid-template: minmax(0, 1fr) / minmax(0, 1fr);   /* one fixed cell, so a portrait render fits whole (QA P1) */
  /* As the header and the outfit rows: the item alone on the neutral thumbnail tile, a plain tile without a picture. */
  background: var(--panel-sunken);
}
.wb-ins-style-thumb[data-state="ok"] { background: var(--thumb-tile); }
.wb-ins-style-img {
  display: grid; place-items: center; grid-template: minmax(0, 1fr) / minmax(0, 1fr);
  width: 100%; height: 100%; min-width: 0; min-height: 0; grid-area: 1 / 1;
}
.wb-ins-style-img img { width: 100%; height: 100%; min-width: 0; min-height: 0; object-fit: contain; grid-area: 1 / 1; }
.wb-ins-style-img img:not([src]) { visibility: hidden; }
.wb-ins-style-img::after { grid-area: 1 / 1; }
.wb-ins-style-thumb[data-state="loading"] .wb-ins-style-img { animation: wb-ins-pulse 1.2s ease-in-out infinite; }
.wb-ins-style-thumb[data-state="failed"] .wb-ins-style-img::after { content: '!'; color: var(--text-mute); font-weight: var(--fw-semibold); }
/* No still picture of its own (an effect, an animated item: renderItemAlone 'empty' | 'body'): a sparkle, as in the rows. */
.wb-ins-style-none { display: none; grid-area: 1 / 1; color: var(--text-mute); }
.wb-ins-style-thumb[data-state="empty"] .wb-ins-style-none { display: inline-flex; }
/* A style the item does not have (the render is fully transparent). */
.wb-ins-style-thumb[data-state="missing"] { border-style: dashed; }
.wb-ins-style-thumb[data-state="missing"] .wb-ins-style-img::after { content: 'none'; color: var(--text-mute); font-size: var(--fs-11); }
/* An item the renderer leaves out for want of a file (plan.skipped): an empty dashed tile, not a failure's '!'. */
.wb-ins-style-thumb[data-state="skipped"] { border-style: dashed; }
.wb-ins-thumb { position: relative; }
/* A style the item does not have: the header keeps the last real picture, dimmed, under a "No style n" badge (QA P8). */
.wb-ins-thumb[data-missing] .wb-ins-thumb-img, .wb-ins-thumb[data-fallback] .wb-ins-thumb-img { opacity: .5; }
.wb-ins-thumb[data-missing]::after {
  content: attr(data-missing); position: absolute; inset: auto 4px 4px; padding: 0 4px; border-radius: var(--r-xs);
  background: var(--panel-raised); color: var(--text-mute); font-size: 10px; line-height: 16px; text-align: center; white-space: nowrap;
}
.wb-ins-style-same {
  display: none; position: absolute; left: 2px; right: 2px; bottom: 2px; padding: 0 2px; border-radius: var(--r-xs);
  background: var(--panel-raised); color: var(--text-mute); font-size: 9px; line-height: 14px; text-align: center; white-space: nowrap;
}
.wb-ins-style-thumb[data-same] .wb-ins-style-same { display: block; }
.wb-ins-style-thumb[data-same] .wb-ins-style-img { opacity: .45; }
.wb-ins-style-main { display: grid; gap: 2px; min-width: 0; }
.wb-ins-style-note:empty { display: none; }
.wb-ins-follows { display: flex; align-items: baseline; gap: 6px; margin: 0; line-height: 1.5; }
.wb-ins-follows > .wb-icon { flex: 0 0 auto; align-self: center; color: var(--text-mute); }
.wb-ins-colourline { display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--sp-2); min-width: 0; }
/* The text wraps (a pet part's "Body colour · row 0 of 31 · shared with 3 other parts" is wider than the Inspector, QA P10). */
.wb-ins-colour-text { color: var(--text-dim); flex: 0 1 auto; min-width: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.wb-ins-colour-change { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.wb-ins-colourline[hidden] { display: none; }
@keyframes wb-ins-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) {
  .wb-ins-style-thumb[data-state="loading"] .wb-ins-style-img, .wb-ins-thumb:has(.wb-ins-thumb-img[data-thumb="loading"]) { animation: none; }
}

/* ── Check / actions ───────────────────────────────────────────────────── */
.wb-ins-toggles { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
.wb-ins-toggle { display: inline-flex; align-items: center; gap: 6px; }
.wb-ins-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.wb-ins-actions > .wb-btn { flex: 1 1 0; }

/* ── Key/value lists ───────────────────────────────────────────────────── */
.wb-ins-kvs { display: grid; gap: 6px; margin: 0; }
.wb-ins-kv { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: var(--sp-2); align-items: baseline; }
.wb-ins-kv > dt { color: var(--text-mute); font-size: var(--fs-12); }
.wb-ins-kv > dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.wb-ins-kv code { font-family: var(--font-mono); font-size: var(--fs-12); }

/* ── Thumbnail diagnostics ─────────────────────────────────────────────── */
.wb-ins-diag { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-2); }
.wb-ins-diag-fig { margin: 0; display: grid; gap: 4px; }
.wb-ins-diag-img {
  aspect-ratio: 3 / 4; border-radius: var(--r-sm); overflow: hidden; border: 1px solid var(--line); display: grid; place-items: center;
  background: repeating-conic-gradient(var(--stage-checker-a) 0 25%, var(--stage-checker-b) 0 50%) 0 0 / 12px 12px;
}
.wb-ins-diag-img img { width: 100%; height: 100%; object-fit: contain; }
.wb-ins-diag-img img:not([src]) { visibility: hidden; }
.wb-ins-diag-fig[data-state="loading"] .wb-ins-diag-img { animation: wb-ins-pulse 1.2s ease-in-out infinite; }
.wb-ins-diag-fig figcaption { font-size: var(--fs-11); color: var(--text-dim); }

/* ── Rig picker ────────────────────────────────────────────────────────── */
.wb-ins-rig-search { flex: 1 1 auto; height: 26px; }
.wb-ins-rigs {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px;
  max-height: 252px; overflow: auto; overscroll-behavior: contain; padding: 1px;
}
.wb-ins-rig {
  display: grid; justify-items: center; gap: 2px; padding: 6px 4px; min-width: 0;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--panel-raised);
}
.wb-ins-rig:hover { border-color: var(--line-strong); }
.wb-ins-rig[aria-checked="true"] { border-color: var(--accent); background: var(--accent-weak); }
.wb-ins-rig[hidden] { display: none; }
/* A rig whose only fitting items are its default outfit (design 40 §A.6). */
.wb-ins-rig { position: relative; }
/* In the tile's own column, under the id: never over the picture. */
.wb-ins-rig-tag {
  padding: 0 4px; border-radius: var(--r-xs); max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  background: var(--panel-sunken); color: var(--text-mute); font-size: 10px; line-height: 15px; white-space: nowrap;
}
/* a tile whose shape is on its way: its place, quietly pulsing (not an empty square that fills in with a jump) */
.wb-ins-rig-ph { width: 100%; height: 100%; background: var(--line); opacity: .25; animation: wb-ins-ph 1.2s ease-in-out infinite alternate; }
@keyframes wb-ins-ph { to { opacity: .5; } }
@media (prefers-reduced-motion: reduce) { .wb-ins-rig-ph { animation: none; } }
.wb-ins-rig-sil {
  width: 100%; height: 100%; background: currentColor; opacity: .55; animation: wb-ins-sil-in var(--dur-2) var(--ease-standard);
  -webkit-mask: var(--m) center / contain no-repeat; mask: var(--m) center / contain no-repeat;
}
.wb-ins-rig[aria-checked="true"] .wb-ins-rig-sil { opacity: 1; }
.wb-ins-rig-icon { animation: wb-ins-sil-in var(--dur-2) var(--ease-standard); }
@keyframes wb-ins-sil-in { from { opacity: 0; } }
.wb-ins-rig-foot { margin: var(--sp-1) 0 0; }
.wb-ins-rig-foot .wb-ins-link { font-size: inherit; }
.wb-ins-rig-art {
  width: 40px; height: 40px; display: grid; place-items: center; border-radius: var(--r-sm);
  background: var(--panel-sunken); color: var(--text-dim); overflow: hidden;
}
.wb-ins-rigs--previews .wb-ins-rig-art { width: 100%; height: 56px; background: transparent; }
.wb-ins-rig-art img { width: 100%; height: 100%; object-fit: contain; }
/* The whole picture in the tile (a grid cell doesn't give the image a definite height to fill). */
.wb-ins-rigs--previews .wb-ins-rig-art > img { height: 56px; min-height: 0; }
/* A white rig (Boo-Bud) is a white ghost on the light tile with only its eyes showing: the soft dark edge of the faint
   catalog art (QA2 m9, as the rows) keeps its outline readable (QA N2). */
.wb-ins-rigs--previews .wb-ins-rig-art > img { filter: drop-shadow(0 0 1px rgb(0 0 0 / .55)) drop-shadow(0 0 2px rgb(0 0 0 / .3)); }
.wb-ins-rig-art--loading { animation: wb-ins-pulse 1.2s ease-in-out infinite; }
/* Previews on (QA2 m10): a tile waiting for its preview shimmers; the one rendering pulses with a spinner; the progress
   line above the grid says how many are ready and which one is rendering. */
.wb-ins-rig[data-preview="queued"] .wb-ins-rig-art,
.wb-ins-rig[data-preview="loading"] .wb-ins-rig-art {
  position: relative;
  background: linear-gradient(90deg, var(--panel-sunken) 0%, var(--line) 50%, var(--panel-sunken) 100%);
  background-size: 200% 100%; animation: wb-shimmer 1.4s linear infinite;
}
.wb-ins-rig[data-preview="queued"] .wb-ins-rig-art > *, .wb-ins-rig[data-preview="loading"] .wb-ins-rig-art > * { opacity: .45; }
.wb-ins-rig[data-preview="loading"] .wb-ins-rig-art::after {
  content: ''; position: absolute; right: 5px; bottom: 5px; width: 10px; height: 10px; border-radius: 50%;
  border: 1.5px solid var(--accent); border-top-color: transparent; animation: wb-spin .8s linear infinite;
}
/* No preview (no known parts, or a starter the renderer draws nothing for, QA2 N2): the letters stay, quieter. */
.wb-ins-rig[data-preview="none"] .wb-ins-rig-art, .wb-ins-rig[data-preview="failed"] .wb-ins-rig-art { color: var(--text-mute); }
.wb-ins-rig-progress { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 2px 0 0; font-variant-numeric: tabular-nums; }
.wb-ins-rig-progress .wb-spinner { width: 10px; height: 10px; border-width: 1.5px; flex: none; }
.wb-ins-rig-progress-hint { color: var(--text-mute); }
@media (prefers-reduced-motion: reduce) {
  .wb-ins-rig[data-preview] .wb-ins-rig-art, .wb-ins-rig[data-preview="loading"] .wb-ins-rig-art::after { animation: none; }
}
.wb-ins-rig-glyph { font-weight: var(--fw-semibold); font-size: var(--fs-15); letter-spacing: .02em; }
.wb-ins-rig[aria-checked="true"] .wb-ins-rig-art { color: var(--accent); }
.wb-ins-rig-name { font-size: var(--fs-12); font-weight: var(--fw-medium); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-ins-rig .mono-id { font-size: 10px; max-width: 100%; text-overflow: ellipsis; }

/* ── Body size ─────────────────────────────────────────────────────────── */
.wb-ins-sizes > button { height: auto; padding: 4px 2px; }
.wb-ins-size { display: grid; justify-items: center; gap: 2px; font-size: var(--fs-11); }
.wb-ins-sil { fill: currentColor; opacity: .7; }
.wb-ins-sizes > button[aria-checked="true"] .wb-ins-sil { opacity: 1; color: var(--accent); }
.wb-ins-size-hint { display: flex; align-items: center; gap: 4px; margin: 0; }
.wb-ins-size-hint .wb-ins-keys { margin-left: auto; }

/* ── Character colours ─────────────────────────────────────────────────── */
.wb-ins-cats { display: grid; gap: 2px; }
.wb-ins-cats[hidden] { display: none; }
.wb-ins-cat { display: grid; gap: 6px; padding-bottom: 6px; scroll-margin-top: var(--sp-2); }
.wb-ins-cat + .wb-ins-cat { border-top: 1px solid var(--line); padding-top: 4px; }
.wb-ins-cat-head {
  display: flex; align-items: center; gap: 6px; min-height: 28px; padding: 0 2px; text-align: left; border-radius: var(--r-xs);
}
.wb-ins-cat-head > .wb-icon { color: var(--text-mute); transition: transform var(--dur) var(--ease-out); }
.wb-ins-cat-head[aria-expanded="false"] > .wb-icon { transform: rotate(-90deg); }
.wb-ins-cat-label { font-weight: var(--fw-medium); }
.wb-ins-cat[data-empty] .wb-ins-cat-label { color: var(--text-dim); }
.wb-ins-cat-readout { color: var(--text-dim); font-size: var(--fs-12); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.wb-ins-cat-count { color: var(--text-mute); font-size: var(--fs-11); white-space: nowrap; }
.wb-ins-cat-note { margin: 0; padding-left: 22px; }
.wb-ins-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(22px, 1fr)); gap: 3px; }
.wb-ins-swatches[hidden] { display: none; }
.wb-ins-swatch {
  position: relative; display: flex; height: 22px; border-radius: var(--r-xs); overflow: hidden;
  border: 1px solid var(--line-strong); padding: 0; min-width: 0;
}
.wb-ins-swatch > span { flex: 1 1 0; min-width: 0; align-self: stretch; height: 100%; }
.wb-ins-swatch:hover { border-color: var(--text-dim); }
.wb-ins-swatch[aria-checked="true"] { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); z-index: 1; }
.wb-ins-swatch:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; z-index: 2; }
.wb-ins-star {
  position: absolute; right: 0; top: -1px; font-style: normal; font-size: 9px; line-height: 1; color: var(--text-on-accent);
  text-shadow: 0 0 2px var(--scrim), 0 0 1px var(--text);
}
.wb-ins-swatch--static { display: inline-flex; width: 28px; height: 14px; flex: 0 0 auto; border: 1px solid var(--line-strong); border-radius: var(--r-xs); overflow: hidden; }
.wb-ins-swatch--static[hidden] { display: none; }
.wb-ins-swatch--static > span { flex: 1 1 0; }
/* A row that follows another colour (Mouth −1 draws the lips in the skin colour): a corner mark. */
.wb-ins-swatch--follows::after {
  content: ''; position: absolute; left: 2px; bottom: 2px; width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-mute); box-shadow: 0 0 0 1px var(--panel-raised);
}
/* "Item" colour rigs: one colour per part type (a stepper), with the parts it colours listed below. */
.wb-ins-pgroup { display: grid; gap: 2px; padding-bottom: 6px; }
.wb-ins-pgroup + .wb-ins-pgroup { border-top: 1px solid var(--line); padding-top: 4px; }
.wb-ins-part { display: flex; align-items: center; gap: var(--sp-2); min-height: 30px; min-width: 0; }
.wb-ins-part-name {
  min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left;
  font-weight: var(--fw-medium); padding: 0;
}
.wb-ins-part-of { white-space: nowrap; font-size: var(--fs-11); }
.wb-ins-pgroup-parts, .wb-ins-pgroup-warn, .wb-ins-pgroup-others { margin: 0; font-size: var(--fs-12); line-height: 1.45; }
.wb-ins-pgroup-warn[hidden] { display: none; }
.wb-ins-pgroup-others { margin-top: 4px; }
.wb-ins-part-link { font-size: inherit; text-align: left; }

/* ── Look ──────────────────────────────────────────────────────────────── */
.wb-ins-coverage { margin: 0; }
.wb-ins-notes { width: 100%; min-height: 84px; font-family: var(--font-ui); font-size: var(--fs-13); }

/* ── Multi-select ──────────────────────────────────────────────────────── */
.wb-ins-multi-summary { margin: 0; font-weight: var(--fw-medium); }
.wb-ins-multi-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; }
.wb-ins-multi-item {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%; min-height: 26px; padding: 0 6px;
  border-radius: var(--r-xs); text-align: left;
}
.wb-ins-multi-item:hover { background: var(--panel-sunken); }
.wb-ins-multi-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-ins-multi-list > li[data-hidden] .wb-ins-multi-name { color: var(--text-mute); text-decoration: line-through; }

/* ── Touch ─────────────────────────────────────────────────────────────── */
@media (pointer: coarse) {
  .wb-ins-sec-bar, .wb-ins-sec-head { min-height: var(--touch); height: auto; }
  /* Touch targets of at least 44 px (§16, QA P14): fewer, larger swatches. */
  .wb-ins-swatches { grid-template-columns: repeat(auto-fill, minmax(var(--touch), 1fr)); gap: 4px; }
  .wb-ins-swatches > .wb-ins-swatch { height: var(--touch); }   /* the grid's; the header's static chip stays 28×14 */
  .wb-ins-cat-head { min-height: var(--touch); }
}
.wb-ins-sec[data-section="rig"] .wb-toggle { white-space: nowrap; }
@media (max-width: 767px) { .wb-ins-rigs { max-height: 216px; } }


/* ── css/features/catalog.css */
/* Feature styles: catalog (spec §4.3, §4.7, §6.8, §14.5). Owned by the catalog feature; tokens only, classes .wb-cat-*. */

.wb-cat { position: relative; display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.wb-cat-flex { flex: 1 1 auto; }
.wb-cat-link {
  color: var(--accent); font-size: var(--fs-12); font-weight: var(--fw-medium); white-space: nowrap;
  padding: 2px 4px; border-radius: var(--r-xs);
}
.wb-cat-link:hover { text-decoration: underline; }

/* ── Replace mode ───────────────────────────────────────────────────────── */
.wb-cat-replace {
  display: flex; align-items: center; gap: var(--sp-2); flex: 0 0 auto;
  padding: 6px var(--sp-2) 6px var(--sp-3);
  background: var(--accent-weak); color: var(--accent); font-size: var(--fs-13);
  box-shadow: inset 0 -1px 0 var(--line);   /* not a border: the bar opens and closes its height (motion.reveal) */
}
.wb-cat-replace[hidden] { display: none; }
.wb-cat-replace-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.wb-cat-replace-text em { font-style: normal; font-weight: var(--fw-semibold); }

/* ── Header: search · Filters · chips · meta ────────────────────────────── */
/* The rows are not spaced by a grid gap: the rows that open and close (the category tabs, the chips) carry their space as
   their own top padding, which motion.reveal animates with their height, so nothing jumps when one of them goes. */
.wb-cat-head { --cat-gap: var(--sp-2); display: grid; padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line); flex: 0 0 auto; }
.wb-cat-meta { margin-top: var(--cat-gap); }
.wb-cat-searchrow { display: flex; gap: var(--sp-2); align-items: center; min-width: 0; }
.wb-cat-search { flex: 1 1 auto; min-width: 0; }
.wb-cat-search > .wb-input.wb-cat-input { height: 32px; padding-right: 30px; }
.wb-cat-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.wb-cat-clearq { position: absolute; right: 4px; }
.wb-cat-clearq[hidden] { display: none; }
.wb-cat-filters { height: 32px; gap: 6px; padding: 0 var(--sp-2) 0 10px; flex: 0 0 auto; }
.wb-cat-filters[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.wb-cat-badge {
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--text-on-accent);
  font-size: var(--fs-11); font-weight: var(--fw-semibold); line-height: 18px; text-align: center;
  font-variant-numeric: tabular-nums;
}
.wb-cat-badge[hidden] { display: none; }
/* ★ Favourites filter (beside Filters) */
.wb-cat-favfilter { width: 32px; height: 32px; flex: 0 0 auto; border: 1px solid var(--line); border-radius: var(--r-md); }
.wb-cat-favfilter[aria-pressed="true"], .wb-cat-favmini[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-weak); }

/* ── Category switcher (owner task 3; tabs.js) ─────────────────────────── */
.wb-cat-cats-wrap { display: grid; min-width: 0; padding-top: var(--cat-gap); }
.wb-cat-cats-wrap[hidden] { display: none; }
.wb-cat-cats-host { position: relative; min-width: 0; }
.wb-cat-cats {
  position: relative; min-width: 0; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; overscroll-behavior-x: contain;
  border-radius: var(--r-sm); background: var(--panel-sunken); border: 1px solid var(--line);
}
.wb-cat-cats::-webkit-scrollbar { display: none; }
/* A fade at the edge that has more tabs behind it. */
.wb-cat-cats[data-more-end], .wb-cat-subs[data-more-end] { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); }
.wb-cat-cats[data-more-start], .wb-cat-subs[data-more-start] { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 28px), transparent); }
.wb-cat-cats[data-more-start][data-more-end], .wb-cat-subs[data-more-start][data-more-end] {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
}
.wb-cat-cats-in { position: relative; display: flex; gap: 2px; padding: 2px; width: max-content; min-width: 100%; }
/* Arrows at the ends of the strip when it does not fit (a mouse; touch swipes). */
.wb-cat-cats-nav {
  position: absolute; top: 1px; bottom: 1px; z-index: 2; width: 28px; display: inline-flex; align-items: center; color: var(--text-dim);
  opacity: 0; pointer-events: none; transition: opacity var(--dur-1) var(--ease-standard), color var(--dur-1) var(--ease-standard);
}
.wb-cat-cats-nav[data-on] { opacity: 1; pointer-events: auto; }
.wb-cat-cats-nav:hover { color: var(--text); }
.wb-cat-cats-nav[data-dir="end"] { right: 1px; justify-content: flex-end; padding-right: 5px; border-radius: 0 var(--r-sm) var(--r-sm) 0; background: linear-gradient(to left, var(--panel-sunken) 45%, transparent); }
.wb-cat-cats-nav[data-dir="start"] { left: 1px; justify-content: flex-start; padding-left: 5px; border-radius: var(--r-sm) 0 0 var(--r-sm); background: linear-gradient(to right, var(--panel-sunken) 45%, transparent); }
@media (pointer: coarse) { .wb-cat-cats-nav { display: none; } }
/* The open tab: one raised thumb that slides between tabs, like the segmented controls (design 40 §B.6). */
.wb-cat-cats-thumb {
  position: absolute; top: 2px; bottom: 2px; left: 0; width: 0; z-index: 0; pointer-events: none;
  border-radius: var(--r-xs); background: var(--panel-raised); box-shadow: var(--shadow-1);
  transition: transform var(--dur-2) var(--ease-standard), width var(--dur-2) var(--ease-standard);
}
.wb-cat-cats-thumb[data-still] { transition: none; }
.wb-cat-cats-thumb[hidden], .wb-cat-cats[data-mixed] .wb-cat-cats-thumb { display: none; }
.wb-cat-cat {
  position: relative; z-index: 1; flex: 0 0 auto; display: inline-flex; align-items: center;
  height: 28px; padding: 0 9px; border-radius: var(--r-xs);
  color: var(--text-dim); font-size: var(--fs-12); font-weight: var(--fw-medium); white-space: nowrap;
  transition: color var(--dur-1) var(--ease-standard), opacity var(--dur-2) var(--ease-standard);
}
.wb-cat-cat[hidden] { display: none; }
.wb-cat-cat:hover { color: var(--text); }
.wb-cat-cat[aria-selected="true"] { color: var(--text); }
.wb-cat-cat:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.wb-cat-cat[data-zero] { opacity: .45; }
.wb-cat-cat-n { color: var(--text-mute); font-size: var(--fs-11); font-variant-numeric: tabular-nums; }
/* A count (on a tab, on a type pill) opens and closes its own width, so a search that brings the counts in widens each
   tab smoothly instead of moving the whole strip in one frame (tabs.js counter(); the mark follows the tabs). */
.wb-cat-n { display: inline-grid; grid-template-columns: 0fr; opacity: 0; transition: grid-template-columns var(--dur-2) var(--ease-standard), opacity var(--dur-2) var(--ease-standard); }
.wb-cat-n[data-on] { grid-template-columns: 1fr; opacity: 1; }
.wb-cat-n-m { min-width: 0; overflow: hidden; }
.wb-cat-n-t { display: block; padding-left: 5px; white-space: nowrap; }
/* The types of the open group: the Filters sheet's pills, in one scrolling row. */
.wb-cat-subs {
  display: flex; gap: 6px; align-items: center; min-width: 0; overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain;
  margin: 0 calc(-1 * var(--sp-3)); padding: 6px var(--sp-3) 0;
}
.wb-cat-subs::-webkit-scrollbar { display: none; }
.wb-cat-subs[hidden] { display: none; }
.wb-cat-pill.wb-cat-sub { flex: 0 0 auto; height: 28px; gap: 0; }
.wb-cat-sub .wb-cat-n-t { padding-left: 6px; }

.wb-cat-chips {
  display: flex; gap: 6px; align-items: center; min-width: 0;
  overflow-x: auto; scrollbar-width: none; margin: 0 calc(-1 * var(--sp-3)); padding: var(--cat-gap) var(--sp-3) 0;
}
.wb-cat-chips::-webkit-scrollbar { display: none; }
.wb-cat-chips[hidden] { display: none; }
.wb-cat-chips[data-leaving] { pointer-events: none; }
.wb-cat-chips > .wb-chip { flex: 0 0 auto; height: 26px; padding-left: 10px; }
.wb-cat-chip--mono .wb-chip-label { font-family: var(--font-mono); font-size: var(--fs-11); }
.wb-cat-clearall { flex: 0 0 auto; margin-left: 2px; }

.wb-cat-meta { display: flex; align-items: center; gap: 4px; min-width: 0; min-height: 28px; font-size: var(--fs-12); color: var(--text-dim); }
.wb-cat-meta[hidden] { display: none; }
.wb-cat-count { font-variant-numeric: tabular-nums; color: var(--text); font-weight: var(--fw-medium); white-space: nowrap; }
.wb-cat-dot { color: var(--text-mute); }
.wb-cat-sortbtn {
  display: inline-flex; align-items: center; gap: 2px; height: 24px; padding: 0 4px 0 6px;
  border-radius: var(--r-xs); color: var(--text-dim); font-size: var(--fs-12); white-space: nowrap;
}
.wb-cat-sortbtn:hover { background: var(--panel-sunken); color: var(--text); }
.wb-cat-sortbtn .wb-icon { width: 14px; height: 14px; }
.wb-cat-outside[hidden] { display: none; }
/* "+N outside filters" stays in view however many chips there are (QA P2): it sticks to the row's right edge and the
   chips scroll under it, behind a short fade. */
.wb-cat-chips > .wb-cat-outside {
  flex: 0 0 auto; margin-left: auto; white-space: nowrap;
  position: sticky; right: 0; z-index: 1; padding-left: 14px;
  background: linear-gradient(to right, transparent, var(--panel) 12px);
}
.wb-cat-meta .wb-seg > button { height: 22px; padding: 0 6px; }

/* ── First-load progress ────────────────────────────────────────────────── */
.wb-cat-progress { display: grid; gap: 6px; padding: var(--sp-2) var(--sp-3); font-size: var(--fs-12); color: var(--text-dim); flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.wb-cat-progress[hidden] { display: none; }
.wb-cat-progress-track { display: block; height: 4px; border-radius: var(--r-pill); background: var(--panel-sunken); overflow: hidden; }
.wb-cat-progress-fill { display: block; height: 100%; width: 0; background: var(--accent); border-radius: inherit; transition: width var(--dur) linear; }

/* ── Results ────────────────────────────────────────────────────────────── */
.wb-cat-results { position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.wb-cat-scroll {
  position: relative; flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  contain: strict;
}
.wb-cat-scroll[hidden] { display: none; }
.wb-cat-scroll:focus { outline: none; }
.wb-cat-spacer { width: 1px; pointer-events: none; }
.wb-cat-row {
  position: absolute; top: 0; left: 8px; right: 8px;
  height: var(--wb-cat-row, 126px);
  display: grid; grid-template-columns: repeat(var(--wb-cat-cols, 4), minmax(0, 1fr)); column-gap: 6px;
  will-change: transform; contain: layout style;
}
.wb-cat-row[hidden] { display: none; }

/* Card (grid mode) */
.wb-cat-cell {
  position: relative; display: flex; flex-direction: column; gap: 3px; min-width: 0;
  border-radius: var(--r-md); cursor: pointer; user-select: none; -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.wb-cat-cell[hidden] { display: none; }
.wb-cat-cell:focus { outline: none; }
.wb-cat-cell:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.wb-cat-thumb {
  position: relative; flex: 0 0 auto; width: 100%; height: var(--wb-cat-cell, 94px);
  border-radius: var(--r-md); background: var(--panel-sunken); border: 1px solid var(--line); overflow: hidden;
  transition: border-color var(--dur) var(--ease-out);
}
.wb-cat-cell:hover .wb-cat-thumb { border-color: var(--line-strong); }
.wb-cat-cell:active .wb-cat-thumb { background: var(--line); }
.wb-cat-img {
  position: absolute; inset: 5%; width: 90%; height: 90%; object-fit: contain;
  opacity: 0; transition: opacity var(--dur) var(--ease-out); pointer-events: none;
}
.wb-cat-img[data-thumb="ok"] { opacity: 1; }
/* A head crop (owner task 7): the avatar picture of a hat, hair or face item is placed so the tile shows the head. thumbs.js
   sets left, top and width inline (percent of the square tile; the height follows); the tile clips the rest. */
.wb-cat-img[data-crop] { inset: auto; height: auto; max-width: none; }
.wb-cat-glyph {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 4px;
  font-size: var(--fs-11); color: var(--text-mute); text-align: center; line-height: var(--lh-tight);
}
.wb-cat-img[data-thumb="ok"] + .wb-cat-glyph { display: none; }
/* No image (every source failed, or a loaded one failed later): the one shared placeholder glyph (core/img.js), as in
   the Outfit rows, the Inspector and the omnibox (Q5). .wb-cat-glyph only carries the loading shimmer. */
.wb-cat-img[data-img] { opacity: 1; }
.wb-cat-img[data-img] + .wb-cat-glyph { display: none; }
.wb-cat-img[data-thumb="loading"] + .wb-cat-glyph,
.wb-cat-cell[data-state="pending"] .wb-cat-glyph {
  color: transparent;
  background: linear-gradient(90deg, var(--panel-sunken) 0%, var(--line) 50%, var(--panel-sunken) 100%);
  background-size: 200% 100%; animation: wb-shimmer 1.2s linear infinite;
}
.wb-cat-check {
  position: absolute; top: 4px; right: 4px; display: none; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%; background: var(--accent); color: var(--text-on-accent);
  box-shadow: var(--shadow-1);
}
.wb-cat-check .wb-icon { width: 12px; height: 12px; }
.wb-cat-cell[data-equipped] .wb-cat-check { display: flex; }
/* "Change back": the hair's own back, listed first (docs/50 §5.4). */
.wb-cat-own {
  position: absolute; left: 4px; bottom: 4px; z-index: 1; padding: 0 6px; border-radius: var(--r-pill);
  background: var(--panel-raised); color: var(--accent); box-shadow: var(--shadow-1);
  font-size: var(--fs-11); font-weight: var(--fw-medium); line-height: 16px; white-space: nowrap; pointer-events: none;
}
.wb-cat-own[hidden] { display: none; }
.wb-cat-scroll[data-mode="list"] .wb-cat-own { display: none; }
.wb-cat-scroll[data-mode="list"] .wb-cat-type[data-own] { color: var(--accent); font-weight: var(--fw-medium); }
/* ★ favourite: on hover and focus (and on touch, the card last tapped); always shown when on */
.wb-cat-fav {
  position: absolute; top: 3px; left: 3px; z-index: 1;
  display: flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%;
  color: var(--text-dim); background: var(--panel-raised); box-shadow: var(--shadow-1);
  opacity: 0; pointer-events: none; transition: opacity var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.wb-cat-fav .wb-icon { width: 14px; height: 14px; }
.wb-cat-fav:hover { color: var(--accent); }
.wb-cat-cell:hover .wb-cat-fav,
.wb-cat-cell:focus-visible .wb-cat-fav,
.wb-cat-scroll[data-touched] .wb-cat-cell[data-active] .wb-cat-fav,
.wb-cat-cell[data-fav] .wb-cat-fav { opacity: 1; pointer-events: auto; }
.wb-cat-cell[data-fav] .wb-cat-fav { color: var(--accent); }
@media (hover: none) { .wb-cat-cell:hover:not([data-fav]):not([data-active]) .wb-cat-fav { opacity: 0; pointer-events: none; } }
/* Touch: the 22 px star keeps a 44 px hit area (QA2 m8). */
@media (pointer: coarse) { .wb-cat-fav::before { content: ''; position: absolute; inset: -11px; } }
.wb-cat-cell[data-equipped] .wb-cat-thumb { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
/* ↵ target (F5): only while the search field has focus AND the keyboard is in use (index.js sets data-kbd on typing or
   arrowing; a pointer press clears it), with a label saying what ↵ does — never a bare outline on open. */
.wb-cat[data-kbd]:has(.wb-cat-input:focus) .wb-cat-cell[data-active] .wb-cat-thumb { outline: 2px solid var(--accent); outline-offset: 1px; }
.wb-cat[data-kbd]:has(.wb-cat-input:focus) .wb-cat-cell[data-active] .wb-cat-thumb::after {
  content: '↵ Add'; position: absolute; left: 50%; bottom: 4px; transform: translateX(-50%); z-index: 1;
  padding: 1px 6px; border-radius: var(--r-pill); background: var(--accent); color: var(--text-on-accent);
  font-size: var(--fs-11); font-weight: var(--fw-medium); line-height: 16px; white-space: nowrap; pointer-events: none;
  box-shadow: var(--shadow-1);
}
.wb-cat[data-kbd]:has(.wb-cat-input:focus) .wb-cat-cell[data-active][data-equipped] .wb-cat-thumb::after { content: '↵ Remove'; }
.wb-cat[data-kbd][data-replace]:has(.wb-cat-input:focus) .wb-cat-cell[data-active] .wb-cat-thumb::after { content: '↵ Replace'; }

.wb-cat-text { display: block; min-width: 0; }
.wb-cat-name {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  font-size: var(--fs-12); line-height: 14px; color: var(--text); word-break: break-word;
}
.wb-cat-name--id { font-family: var(--font-mono); font-style: italic; font-size: var(--fs-11); color: var(--text-dim); word-break: break-all; }
.wb-cat-cell[data-state="pending"] .wb-cat-name { height: 10px; width: 70%; border-radius: var(--r-xs); background: var(--panel-sunken); }
.wb-cat-id, .wb-cat-type { display: none; }
/* A hair front's card names the back it brings when that back is not named after it (docs/50 §5.4). */
.wb-cat-pair { color: var(--text-dim); }

/* Compact list mode (32 px rows; 44 px on touch) */
.wb-cat-scroll[data-mode="list"] .wb-cat-row { left: 0; right: 0; column-gap: 0; }
.wb-cat-scroll[data-mode="list"] .wb-cat-cell {
  flex-direction: row; align-items: center; gap: var(--sp-2); height: 100%; padding: 0 var(--sp-3); border-radius: 0;
}
.wb-cat-scroll[data-mode="list"] .wb-cat-cell:hover { background: var(--panel-sunken); }
.wb-cat-scroll[data-mode="list"] .wb-cat-cell:focus-visible { outline-offset: -2px; }
.wb-cat-scroll[data-mode="list"] .wb-cat-thumb { width: 24px; height: 24px; border-radius: var(--r-xs); }
.wb-cat-scroll[data-mode="list"] .wb-cat-glyph { font-size: 0; }
.wb-cat-scroll[data-mode="list"] .wb-cat-cell[data-equipped] .wb-cat-thumb { box-shadow: none; }
.wb-cat-scroll[data-mode="list"] .wb-cat-check { position: static; order: 3; flex: 0 0 auto; width: 16px; height: 16px; }
.wb-cat-scroll[data-mode="list"] .wb-cat-text { flex: 1 1 auto; display: flex; align-items: baseline; gap: var(--sp-2); overflow: hidden; }
/* The name keeps the larger share (whole, up to 65 %); the id takes the rest with a middle ellipsis (grid.js, QA2 m6). */
.wb-cat-scroll[data-mode="list"] .wb-cat-name { display: block; flex: 0 0 auto; max-width: 65%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: var(--fs-13); line-height: var(--lh-tight); }
.wb-cat-scroll[data-mode="list"] .wb-cat-name--id { flex: 1 1 auto; max-width: none; font-size: var(--fs-12); }
.wb-cat-scroll[data-mode="list"] .wb-cat-id { display: block; flex: 1 1 0; min-width: 0; font-size: var(--fs-11); overflow: hidden; white-space: nowrap; }
.wb-cat-scroll[data-mode="list"] .wb-cat-name--id + .wb-cat-id { display: none; }
.wb-cat-scroll[data-mode="list"] .wb-cat-type { display: block; flex: 0 0 auto; font-size: var(--fs-11); color: var(--text-mute); white-space: nowrap; }
.wb-cat-scroll[data-mode="list"] .wb-cat-cell[data-state="pending"] .wb-cat-name { height: 10px; width: 40%; }
.wb-cat-scroll[data-mode="list"] .wb-cat-fav { position: relative; top: auto; left: auto; flex: 0 0 auto; width: 22px; height: 22px; background: none; box-shadow: none; }
.wb-cat[data-kbd]:has(.wb-cat-input:focus) .wb-cat-scroll[data-mode="list"] .wb-cat-cell[data-active] { background: var(--accent-weak); }
.wb-cat[data-kbd]:has(.wb-cat-input:focus) .wb-cat-scroll[data-mode="list"] .wb-cat-cell[data-active] .wb-cat-thumb { outline: none; }
.wb-cat[data-kbd]:has(.wb-cat-input:focus) .wb-cat-scroll[data-mode="list"] .wb-cat-cell[data-active] .wb-cat-thumb::after { content: none; }
.wb-cat[data-kbd]:has(.wb-cat-input:focus) .wb-cat-scroll[data-mode="list"] .wb-cat-cell[data-active]::after {
  content: '↵ Add'; flex: 0 0 auto; order: 4; padding: 0 6px; border-radius: var(--r-pill); background: var(--accent); color: var(--text-on-accent);
  font-size: var(--fs-11); font-weight: var(--fw-medium); line-height: 16px; white-space: nowrap;
}
.wb-cat[data-kbd]:has(.wb-cat-input:focus) .wb-cat-scroll[data-mode="list"] .wb-cat-cell[data-active][data-equipped]::after { content: '↵ Remove'; }
.wb-cat[data-kbd][data-replace]:has(.wb-cat-input:focus) .wb-cat-scroll[data-mode="list"] .wb-cat-cell[data-active]::after { content: '↵ Replace'; }

/* Sort options say what they order by (sort menu and Filters sheet). */
.wb-cat-sort-opt { display: grid; gap: 1px; min-width: 0; text-align: left; }
.wb-menu-item:has(.wb-cat-sort-opt) { padding-top: 5px; padding-bottom: 5px; align-items: flex-start; }
.wb-menu-item:has(.wb-cat-sort-opt) > .wb-menu-check { margin-top: 1px; }
.wb-cat-radio:has(.wb-cat-sort-opt) { height: auto; min-height: 30px; padding-top: 4px; padding-bottom: 4px; align-items: flex-start; }
.wb-cat-radio:has(.wb-cat-sort-opt) > .wb-cat-radio-dot { margin-top: 2px; }
.wb-cat-sort-sub { font-size: var(--fs-11); color: var(--text-mute); line-height: var(--lh-tight); white-space: normal; }
.wb-cat-radio:disabled { color: var(--text-mute); cursor: default; }   /* Relevance with no search text (R7) */
.wb-cat-radio:disabled:hover { background: none; }

/* ── States: first load · error · no results ────────────────────────────── */
.wb-cat-state { position: absolute; inset: 0; overflow: auto; background: var(--panel); }
.wb-cat-state[hidden] { display: none; }
.wb-cat-loading { padding: var(--sp-2) var(--sp-3) var(--sp-4); display: grid; gap: var(--sp-3); align-content: start; }
.wb-cat-skeletons { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: var(--sp-3) 6px; }
.wb-cat-skelcard { display: grid; gap: 4px; }
.wb-cat-skelcard > .wb-skel:first-child { aspect-ratio: 1 / 1; height: auto !important; border-radius: var(--r-md); }
.wb-cat-msg { display: grid; gap: var(--sp-2); justify-items: center; text-align: center; padding: var(--sp-8) var(--sp-5); color: var(--text-dim); }
.wb-cat-msg > .wb-icon { color: var(--text-mute); }
.wb-cat-msg[role="alert"] > .wb-icon { color: var(--err); }
.wb-cat-msg-title { margin: 0; color: var(--text); font-size: var(--fs-13); font-weight: var(--fw-medium); overflow-wrap: anywhere; }
.wb-cat-msg-title em { font-style: normal; font-family: var(--font-mono); font-size: var(--fs-12); }
.wb-cat-msg .wb-note { margin: 0; max-width: 34ch; overflow-wrap: anywhere; }
.wb-cat-state-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: center; }
.wb-cat-state-row .mono-id { color: inherit; max-width: 22ch; text-overflow: ellipsis; }
.wb-cat-loading .wb-cat-state-row { justify-content: flex-start; }
.wb-cat-loading > .wb-note { margin: 0; }

/* ── Footer ─────────────────────────────────────────────────────────────── */
.wb-cat-foot { color: var(--text-mute); }   /* .wb-foot: one 44 px line (design 40 §B.7) */
.wb-cat-hints { flex: 0 0 auto; }
.wb-cat-updated { flex: 0 1 auto; min-width: 0; overflow: hidden; }
.wb-cat-updated[hidden] { display: none; }
.wb-cat-foot .wb-iconbtn[hidden] { display: none; }
.wb-cat-foot .wb-iconbtn[data-busy] .wb-icon { animation: wb-spin .8s linear infinite; }

/* ── Hover card ─────────────────────────────────────────────────────────── */
.wb-cat-hover {
  position: fixed; top: 0; left: 0; z-index: var(--z-toast);
  display: grid; gap: 2px; max-width: 280px; padding: var(--sp-2) 10px;
  background: var(--panel-raised); color: var(--text); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--shadow-2); font-size: var(--fs-12); line-height: var(--lh-tight); pointer-events: none;
  animation: wb-cat-fade var(--dur) var(--ease-out);
}
.wb-cat-hover-name { font-size: var(--fs-13); font-weight: var(--fw-semibold); overflow-wrap: anywhere; }
.wb-cat-hover-name.wb-cat-name--id { font-size: var(--fs-12); font-weight: var(--fw-regular); }
.wb-cat-hover-id { white-space: normal; overflow-wrap: anywhere; }
.wb-cat-hover-meta { color: var(--text-dim); }
.wb-cat-hover-flag { color: var(--warn); }
.wb-cat-hover-pair { color: var(--text-dim); }
.wb-cat-hover-pair em { font-style: normal; color: var(--text); }
.wb-cat-hover-hint { color: var(--text-mute); font-size: var(--fs-11); margin-top: 2px; }
@keyframes wb-cat-fade { from { opacity: 0; } to { opacity: 1; } }

/* ── Drop target (card dragged onto the stage) ──────────────────────────── */
[data-wb-cat-drop] { outline: 2px dashed var(--accent); outline-offset: -8px; }

/* ── Filters sheet ──────────────────────────────────────────────────────── */
.wb-cat-sheet { display: flex; flex-direction: column; background: var(--panel); min-height: 0; }
.wb-cat-sheet--panel {
  position: absolute; inset: 0; z-index: 3;
  transform: translateX(0); transition: transform var(--dur-drawer) var(--ease-out);
}
.wb-cat-sheet--panel[data-entering] { transform: translateX(100%); }
.wb-cat-sheet--mobile {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: calc(var(--z-modal) - 1);
  max-height: 85vh; max-height: 85dvh;
  border-radius: var(--r-lg) var(--r-lg) 0 0; border-top: 1px solid var(--line); box-shadow: var(--shadow-3);
  transform: translateY(0); transition: transform var(--dur-drawer) var(--ease-out);
}
.wb-cat-sheet--mobile[data-entering] { transform: translateY(100%); }
.wb-cat-scrim {
  position: fixed; inset: 0; z-index: calc(var(--z-modal) - 2); background: var(--scrim);
  opacity: 1; transition: opacity var(--dur-drawer) var(--ease-out);
}
.wb-cat-scrim[data-entering] { opacity: 0; }
.wb-cat-sheet-grab { display: flex; justify-content: center; align-items: center; height: 16px; flex: 0 0 auto; }
.wb-cat-sheet-grab::before { content: ''; width: 36px; height: 4px; border-radius: var(--r-pill); background: var(--line-strong); }
.wb-cat-sheet-head {
  display: flex; align-items: center; gap: var(--sp-2); height: 44px; flex: 0 0 auto;
  padding: 0 var(--sp-2) 0 var(--sp-4); border-bottom: 1px solid var(--line);
}
.wb-cat-sheet-title { flex: 1 1 auto; margin: 0; font-size: var(--fs-15); font-weight: var(--fw-semibold); }
.wb-cat-sheet-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 var(--sp-4) var(--sp-4); }
.wb-cat-sheet-foot { padding: 0 var(--sp-2); }   /* .wb-foot (design 40 §B.7) */
.wb-cat-sheet-foot .wb-cat-show { flex: 1 1 auto; height: 32px; font-variant-numeric: tabular-nums; }
.wb-cat-sheet-foot .wb-cat-show[aria-busy="true"] { opacity: .8; }
.wb-cat-sec { padding: var(--sp-3) 0; border-top: 1px solid var(--line); }
.wb-cat-sec:first-child { border-top: 0; }
.wb-cat-sec > .wb-label { margin: 0 0 var(--sp-2); }
.wb-cat-sec > .wb-note { margin-top: 4px; }

.wb-cat-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.wb-cat-pill {
  display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px; max-width: 100%;
  border-radius: var(--r-pill); border: 1px solid var(--line-strong); background: var(--panel-raised);
  font-size: var(--fs-12); color: var(--text); white-space: nowrap;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.wb-cat-pill:hover { background: var(--panel-sunken); }
.wb-cat-pill[aria-pressed="true"] { background: var(--accent-weak); border-color: var(--accent); color: var(--accent); }
.wb-cat-pill[data-zero] { opacity: .5; }
.wb-cat-pill-label { overflow: hidden; text-overflow: ellipsis; }
.wb-cat-pill--mono .wb-cat-pill-label { font-family: var(--font-mono); font-size: var(--fs-11); }
.wb-cat-pill-n { color: var(--text-mute); font-size: var(--fs-11); font-variant-numeric: tabular-nums; }
.wb-cat-pill-n:empty { display: none; }
.wb-cat-pill[aria-pressed="true"] .wb-cat-pill-n { color: inherit; }
.wb-cat-pill[aria-pressed="true"] .wb-cat-pill-n:not(.wb-cat-n) { opacity: .8; }
.wb-cat-pill[aria-pressed="true"] .wb-cat-n-t { opacity: .8; }

/* Groups wrap as pills; an expanded group's cts take a full-width line right after it. */
.wb-cat-types { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.wb-cat-tg { display: contents; }
.wb-cat-tg-head { display: inline-flex; align-items: center; gap: 0; }
.wb-cat-tg-more { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 28px; border-radius: var(--r-sm); color: var(--text-dim); }
.wb-cat-tg[data-open] .wb-cat-tg-more { color: var(--accent); }
.wb-cat-sheet:focus { outline: none; }
.wb-cat-tg-more:hover { background: var(--panel-sunken); color: var(--text); }
.wb-cat-tg-more .wb-icon { transition: transform var(--dur) var(--ease-out); }
.wb-cat-tg-more[aria-expanded="true"] .wb-icon { transform: rotate(180deg); }
.wb-cat-tg-cts { display: none; flex: 1 0 100%; flex-wrap: wrap; gap: 6px; margin: 0 0 4px 4px; padding: 2px 0 2px 10px; border-left: 2px solid var(--line); }
.wb-cat-tg[data-open] .wb-cat-tg-cts { display: flex; }

.wb-cat-coll-input { width: 100%; margin-bottom: var(--sp-2); }
.wb-cat-coll-note { margin-top: 6px; }
.wb-cat-coll-note:empty { display: none; }
.wb-cat-toggles { display: grid; gap: 4px; }

.wb-cat-radios { display: grid; gap: 2px; }
.wb-cat-radio { display: flex; align-items: center; gap: var(--sp-2); height: 30px; padding: 0 6px; border-radius: var(--r-sm); font-size: var(--fs-13); color: var(--text); text-align: left; }
.wb-cat-radio:hover { background: var(--panel-sunken); }
.wb-cat-radio-dot { width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid var(--line-strong); flex: 0 0 auto; background: var(--panel-raised); }
.wb-cat-radio[aria-checked="true"] .wb-cat-radio-dot { border-color: var(--accent); background: var(--accent); box-shadow: inset 0 0 0 3px var(--panel-raised); }

/* ── Touch & mobile ─────────────────────────────────────────────────────── */
@media (pointer: coarse) {
  .wb-cat-search > .wb-input.wb-cat-input, .wb-cat-filters { height: var(--touch); }
  .wb-cat-cat { height: 38px; padding: 0 14px; }
  .wb-cat-pill.wb-cat-sub { height: 36px; }
  .wb-cat-pill { height: 36px; padding: 0 12px; }
  .wb-cat-tg-more { width: 32px; height: 36px; }
  .wb-cat-radio { height: 40px; }
  .wb-cat-radio:has(.wb-cat-sort-opt) { min-height: 40px; }
  .wb-cat-sheet-foot .wb-cat-show { height: var(--touch); }
}
@media (max-width: 767px) {
  /* The switcher is two rows on a phone and the sheet is short: tabs and types a little lower than a 44 px target. */
  .wb-cat-cat { height: 36px; padding: 0 12px; }
  .wb-cat-pill.wb-cat-sub { height: 32px; }
  .wb-cat-hints { display: none; }
  .wb-cat-head { --cat-gap: 6px; padding: 6px var(--sp-3) var(--sp-2); }
  /* Phones: no footer (its refresh moved into the count row), so the grid gets the room (R2-12). */
  .wb-cat-foot { display: none; }
}
/* The rig scope chip in the count row (R2-11). When the row is short (phones, long pet names) the chip gives way with
   an ellipsis (its tooltip has the full scope); the sort button never shrinks (Q6). */
.wb-cat-scope { display: inline-flex; flex: 0 1 auto; min-width: 0; }
.wb-cat-scope .wb-chip { min-width: 0; }
.wb-cat-scope .wb-chip-label { min-width: 0; }
.wb-cat-meta .wb-cat-sortbtn { flex: none; }
/* Phones: "Humanoid items" reads "Humanoid" as a plain label (the rig scope is changed in Filters, "Compatible with
   current rig"; a 16 px ✕ is no touch target), and the chip itself separates the count from the sort (no "·"). */
@media (max-width: 767px) {
  .wb-cat-scope-tail, .wb-cat-scope .wb-chip-x { display: none; }
  .wb-cat-scope:not(:empty) + .wb-cat-dot { display: none; }
  .wb-cat-scope .wb-chip { margin: 0 0 0 2px; padding: 0 6px; }
  .wb-cat-meta { gap: 3px; }
  /* ★ filter and the layout switch share the row with the count, scope and sort as full 44 px touch squares; ⟳ moved
     into the sort menu so "Humanoid" still fits from 360 px (Q6 with favourites, QA2 N7). */
  .wb-cat-meta .wb-iconbtn { width: var(--touch); flex: none; }
}
/* The sort button stays a compact text button; on touch its hit area is 44 px tall (QA2 N7). */
@media (pointer: coarse) {
  .wb-cat-sortbtn { position: relative; }
  .wb-cat-sortbtn::before { content: ''; position: absolute; inset: -10px 0; }
}
.wb-cat-scope:empty { display: none; }
.wb-cat-scope .wb-chip { height: 22px; font-size: var(--fs-11); margin-left: 4px; }
.wb-cat-refresh-mini[hidden] { display: none; }
.wb-cat-refresh-mini[data-busy] .wb-icon { animation: wb-spin .8s linear infinite; }
@media (prefers-reduced-motion: reduce) {
  .wb-cat-sheet--panel, .wb-cat-sheet--mobile, .wb-cat-scrim, .wb-cat-img, .wb-cat-progress-fill, .wb-cat-cats-thumb, .wb-cat-cat, .wb-cat-n, .wb-cat-cats-nav { transition: none; }
  .wb-cat-hover { animation: none; }
}

/* A card whose item the live avatar does not show yet pulses until it does (live-stage.js 'live:pending'). */
@keyframes wb-cat-pending { 0%, 100% { box-shadow: 0 0 0 2px var(--accent); opacity: 1; } 50% { box-shadow: 0 0 0 2px transparent; opacity: .55; } }
.wb-cat-cell[data-pending] { animation: wb-cat-pending 1.1s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .wb-cat-cell[data-pending] { box-shadow: 0 0 0 2px var(--accent); } }

/* ── css/features/pose.css */
/* Feature styles: pose. Owned by the pose feature; use tokens only, classes prefixed per §17.9.
   Pose bar 44 px → Timeline 112 px (head 40 · scrubber 40 · footer 32); emote picker; mobile transport strip. */

.wb-pose { container-type: inline-size; container-name: pose; }
.wb-pose-bar, .wb-pose-tl, .wb-pose-strip { display: none; }
.wb-pose[data-mode="bar"] .wb-pose-bar { display: flex; }
.wb-pose[data-mode="timeline"] .wb-pose-tl { display: grid; }

.wb-pose-sep { width: 1px; height: 20px; background: var(--line); flex: 0 0 auto; margin: 0 2px; }
.wb-pose kbd, .wb-pose-picker kbd {
  display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 4px;
  border: 1px solid var(--line-strong); border-bottom-width: 2px; border-radius: var(--r-xs);
  font-family: var(--font-mono); font-size: var(--fs-11); color: var(--text-dim); background: var(--panel-raised);
}

/* ── Pose bar (idle, 44 px) ──────────────────────────────────────────────── */
.wb-pose-bar {
  height: var(--posebar-h); align-items: center; gap: var(--sp-2);
  padding: 0 var(--sp-3) 0 var(--sp-2); min-width: 0; overflow: hidden;
}
.wb-pose-trigger { gap: 6px; max-width: 240px; padding: 0 var(--sp-2); color: var(--text); font-weight: var(--fw-semibold); }
.wb-pose-trigger .wb-icon[data-size="16"] { color: var(--accent); }
.wb-pose-trigger-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.wb-pose-cm-label { flex: 0 0 auto; }
.wb-pose-cm { display: flex; gap: 6px; min-width: 0; overflow: hidden; flex: 0 1 auto; }
.wb-pose-cm-note { align-self: center; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wb-pose-chip { cursor: pointer; }
.wb-pose-hint { display: inline-flex; align-items: center; gap: 3px; color: var(--text-mute); font-size: var(--fs-12); white-space: nowrap; flex: 0 0 auto; }

/* ── Timeline (112 px) ───────────────────────────────────────────────────── */
.wb-pose-tl { height: var(--timeline-h); grid-template-rows: 40px 40px minmax(0, 1fr); min-width: 0; }
.wb-pose-head {
  display: flex; align-items: center; gap: var(--sp-1); min-width: 0;
  padding: 0 var(--sp-2); overflow: hidden;
}
.wb-pose-head > * { flex: 0 0 auto; }
.wb-pose-head > .wb-spacer { flex: 1 1 auto; }
.wb-pose-head .wb-pose-trigger { flex: 0 1 auto; min-width: 72px; }
.wb-pose-transport { display: inline-flex; align-items: center; gap: 2px; }
.wb-pose-transport .wb-pose-play { width: 30px; height: 30px; color: var(--text); background: var(--panel-sunken); border-radius: 50%; }
.wb-pose-transport .wb-pose-play[aria-pressed="true"] { background: var(--accent-weak); color: var(--accent); }
.wb-pose-menubtn { gap: 4px; color: var(--text-dim); }
.wb-pose-menubtn .wb-icon[data-size="14"] { width: 14px; height: 14px; }
.wb-pose-bakedonly { display: inline-flex; align-items: center; }
.wb-pose[data-kind="realtime"] .wb-pose-bakedonly { display: none; }
.wb-pose-qbox { display: inline-flex; align-items: center; gap: 6px; padding: 0 var(--sp-1); }
.wb-pose-quality { font-size: var(--fs-12); color: var(--text-dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
.wb-pose-hd { font-weight: var(--fw-semibold); letter-spacing: .02em; }
.wb-pose-rangebox { display: inline-flex; align-items: center; gap: 0; }
.wb-pose-in, .wb-pose-out { min-width: 28px; padding: 0 6px; }
.wb-pose-readout {
  font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap; padding: 0 var(--sp-2);
}

/* Scrubber */
.wb-pose-scrub { padding: 4px var(--sp-3); min-width: 0; }
.wb-pose-track {
  position: relative; height: 32px; border-radius: var(--r-sm);
  background: var(--panel-sunken); border: 1px solid var(--line); overflow: hidden;
  cursor: pointer; touch-action: none; user-select: none;
}
.wb-pose-track:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.wb-pose-track[aria-disabled="true"] { cursor: default; }
.wb-pose-ticks { position: absolute; inset: 0; width: 100%; height: 100%; }
.wb-pose-playhead {
  position: absolute; top: 0; bottom: 0; left: 0; width: 2px;
  background: color-mix(in srgb, var(--accent) 35%, transparent);
  border-left: 2px solid var(--accent); box-sizing: border-box; pointer-events: none;
}
.wb-pose-handle {
  position: absolute; top: 0; bottom: 0; left: -5px; width: 10px; cursor: ew-resize; z-index: 2;
}
.wb-pose-handle::after {
  content: ''; position: absolute; top: 3px; bottom: 3px; left: 3px; width: 4px;
  border-radius: 2px; background: var(--accent);
}
.wb-pose-fade-label {
  position: absolute; left: 0; top: 2px; text-align: center; pointer-events: none;
  font-size: 10px; color: var(--warn); text-transform: uppercase; letter-spacing: var(--tracking-label);
  white-space: nowrap; overflow: hidden;
}
.wb-pose-stripe {
  position: absolute; inset: 0; display: none; pointer-events: none;
  background-image: repeating-linear-gradient(-45deg, var(--accent-weak) 0 10px, transparent 10px 20px);
  background-size: 28px 28px;
  animation: wb-pose-stripe .9s linear infinite;
}
.wb-pose[data-rendering] .wb-pose-stripe { display: block; }
@keyframes wb-pose-stripe { from { background-position: 0 0; } to { background-position: 28px 0; } }
@media (prefers-reduced-motion: reduce) { .wb-pose-stripe { animation: none; } }

/* Footer */
.wb-pose-foot {
  display: flex; align-items: center; gap: var(--sp-3); min-width: 0;
  padding: 0 var(--sp-3); font-size: var(--fs-12); color: var(--text-dim); overflow: hidden;
}
.wb-pose-status { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; flex: 1 1 auto; overflow: hidden; white-space: nowrap; }
.wb-pose-status > * { flex: 0 0 auto; }
.wb-pose-status .wb-lock { padding: 0; }
.wb-pose-jobtext { overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 0 1 auto; font-variant-numeric: tabular-nums; }
.wb-pose-jobtext em, .wb-pose-state em { font-style: normal; font-weight: var(--fw-semibold); color: var(--text); }
.wb-pose-err { display: inline-flex; align-items: center; gap: 6px; color: var(--err); min-width: 0; overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto; }
.wb-pose-missing { display: inline-flex; align-items: center; gap: 4px; color: var(--warn); }
.wb-pose-guide { overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 0 1 auto !important; color: var(--text-mute); }
.wb-pose-foot-right { display: flex; align-items: center; gap: var(--sp-3); flex: 0 0 auto; }
.wb-pose-foot-right .wb-toggle { font-size: var(--fs-12); min-height: 24px; }
.wb-pose-rerender { cursor: pointer; font-weight: var(--fw-medium); }

/* Narrow docks: drop labels before controls. */
@container pose (max-width: 1100px) {
  .wb-pose-onion > span:not(.wb-btn-kbd) { display: none; }
  .wb-pose-onion { width: 28px; padding: 0; }
  .wb-pose-head .wb-pose-trigger { max-width: 160px; }
}
@container pose (max-width: 920px) {
  .wb-pose-quality { display: none; }
  .wb-pose-loop > span { display: none; }
  .wb-pose-hint { display: none; }
  .wb-pose-cm-label { display: none; }
}
@container pose (max-width: 780px) {
  .wb-pose-rangebox { display: none; }
  .wb-pose-transport .wb-iconbtn:first-child, .wb-pose-transport .wb-iconbtn:last-child { display: none; }
}

/* ── Mobile: the transport strip (44 px above the tabs) or the sheet header ── */
.wb-pose-mini { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; width: 100%; }
.wb-pose-mmid { display: grid; gap: 0; flex: 1 1 auto; min-width: 0; }
.wb-pose-mtop { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2); min-width: 0; }
.wb-pose-mname {
  font-size: var(--fs-12); font-weight: var(--fw-semibold); color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; text-align: left; padding: 0;
}
.wb-pose-mini .wb-pose-readout { font-size: var(--fs-11); padding: 0; color: var(--text-dim); }
.wb-pose-mrange { width: 100%; height: 20px; margin: 0; accent-color: var(--accent); touch-action: pan-x; }
/* Rendering a move: a short job line replaces the frame readout, over an indeterminate stripe on the scrubber. */
.wb-pose-mtrack { position: relative; }
.wb-pose-mstripe {
  position: absolute; left: 0; right: 0; top: 50%; height: 6px; transform: translateY(-50%); border-radius: var(--r-pill);
  display: none; pointer-events: none;
  background-image: repeating-linear-gradient(-45deg, var(--accent) 0 6px, var(--accent-weak) 6px 12px);
  background-size: 17px 17px; opacity: .7;
  animation: wb-pose-stripe .9s linear infinite;
}
.wb-pose-mjob { display: none; font-size: var(--fs-11); color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; font-variant-numeric: tabular-nums; }
.wb-pose-mini[data-rendering] .wb-pose-mjob { display: inline; }
.wb-pose-mini[data-rendering] .wb-pose-readout { display: none; }
.wb-pose-mini[data-rendering] .wb-pose-mstripe { display: block; }
@media (prefers-reduced-motion: reduce) { .wb-pose-mstripe { animation: none; } }
.wb-drawer-head > .wb-pose-mini { flex: 1 1 auto; width: auto; }
.wb-drawer-head:has(> .wb-pose-mini) .wb-drawer-title { flex: 0 1 auto; max-width: 28%; }
.wb-drawer-head > .wb-pose-mini > .wb-pose-mstop { display: none; }   /* the sheet's own ✕ sits next to it */
/* A full-height sheet hides the stage, so the transport would only crowd its header. */
.wb-drawer[data-detent="full"] .wb-drawer-head > .wb-pose-mini { display: none; }
.wb-drawer[data-detent="full"] .wb-drawer-head .wb-drawer-title { flex: 1 1 auto; max-width: none; }

/* ── The Timeline over the stage (design 40 §B.3, ≥ 768 px) ──────────────────────────────────────────────────
   The Pose bar keeps its 44 px: in timeline mode the Timeline's head is the bar's content (it cross-fades in), and
   its track (scrubber 40 + status 32) rises above the bar over the stage, reported as the stage's bottom inset
   (timeline.js), instead of the bar growing to 112 px in one frame. Leaving, the track sinks back behind the bar. */
@media (min-width: 768px) {
  .wb-pose-tl { height: var(--posebar-h); grid-template-rows: 100%; }
  .wb-pose-bar, .wb-pose-head { position: relative; z-index: 1; background: var(--panel); }
  .wb-pose[data-mode="timeline"] .wb-pose-head, .wb-pose[data-mode="bar"]:not([data-booting]) .wb-pose-bar { animation: wb-fade-in var(--dur-2) var(--ease-standard); }
  .wb-pose-scrub, .wb-pose-foot { position: absolute; left: 0; right: 0; z-index: 0; background: var(--panel); }
  .wb-pose-scrub { bottom: calc(100% + 32px); height: 41px; border-top: 1px solid var(--line); box-shadow: 0 -1px 0 var(--line); }
  .wb-pose-foot { bottom: 100%; height: 32px; }
  .wb-pose[data-mode="timeline"] .wb-pose-scrub, .wb-pose[data-mode="timeline"] .wb-pose-foot { animation: wb-pose-rise var(--dur-3) var(--ease-standard); }
  @keyframes wb-pose-rise { from { transform: translateY(73px); } to { transform: none; } }
  .wb-pose[data-tl-leaving] .wb-pose-tl { display: grid; position: absolute; inset: 0; pointer-events: none; }
  .wb-pose[data-tl-leaving] .wb-pose-head { visibility: hidden; }
  .wb-pose[data-tl-leaving] .wb-pose-scrub, .wb-pose[data-tl-leaving] .wb-pose-foot { animation: wb-pose-sink var(--dur-3-exit) var(--ease-exit) forwards; }
  @keyframes wb-pose-sink { to { transform: translateY(73px); } }
}

@media (max-width: 767px) {
  .wb-pose[data-mode] .wb-pose-bar, .wb-pose[data-mode] .wb-pose-tl { display: none; }
  .wb-pose[data-active="true"] .wb-pose-strip {
    display: flex; align-items: center; height: var(--posebar-h);
    padding: 0 calc(var(--sp-2) + var(--safe-right, 0px)) 0 calc(var(--sp-2) + var(--safe-left, 0px));
  }
  #app[data-sheet] .wb-pose-strip, .wb-pose-strip:empty { display: none !important; }
  #posebar:has(> .wb-pose[data-active="false"]) { display: none; }
  #app[data-sheet] #posebar { display: none; }
  .wb-pose-mini .wb-iconbtn { width: 40px; height: 40px; }
}

/* ── Emote picker (popover / Moves sheet) ───────────────────────────────── */
.wb-pose-pop { padding: 0; overflow: hidden; display: flex; flex-direction: column; height: min(560px, 70vh); }
.wb-pose-picker { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
.wb-pose-search { padding: var(--sp-2) var(--sp-2) var(--sp-1); flex: 0 0 auto; }
.wb-pose-search > .wb-icon { left: calc(var(--sp-2) + 8px); }
.wb-pose-search > .wb-input { height: 32px; }
.wb-pose-search > .wb-input::-webkit-search-cancel-button { cursor: pointer; }
.wb-pose-groupnav {
  display: flex; gap: 4px; padding: 2px var(--sp-2) var(--sp-2); overflow-x: auto; flex: 0 0 auto;
  scrollbar-width: none; border-bottom: 1px solid var(--line);
}
.wb-pose-groupnav::-webkit-scrollbar { display: none; }
.wb-pose-navchip { cursor: pointer; flex: 0 0 auto; }
.wb-pose-navchip .wb-pose-count { margin-left: 2px; }
.wb-pose-list { flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; padding: 0 0 var(--sp-2); }
/* Every section: a header, then its content 8 px under it (rows, chips or a note); the header sticks while its rows
   scroll, and what scrolls under it fades out in a short band instead of being cut at the header's edge. */
.wb-pose-sec { padding: var(--sp-1) 0; }
.wb-pose-sec + .wb-pose-sec { border-top: 1px solid var(--line); }
.wb-pose-sec-head {
  display: flex; align-items: center; gap: 6px; padding: var(--sp-3) var(--sp-3) var(--sp-2);
  position: sticky; top: 0; z-index: 1; background: var(--panel-raised);
}
.wb-pose-sec-head::after {
  content: ''; position: absolute; left: 0; right: 0; top: 100%; height: var(--sp-2); pointer-events: none;
  background: linear-gradient(to bottom, var(--panel-raised), transparent);
}
.wb-pose-count { font-weight: var(--fw-regular); color: var(--text-mute); font-variant-numeric: tabular-nums; letter-spacing: 0; }
.wb-pose-sec-note { font-weight: var(--fw-regular); text-transform: none; letter-spacing: 0; color: var(--text-mute); }
.wb-pose-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 var(--sp-3) var(--sp-2); }
.wb-pose-chips .wb-pose-chip.is-active { outline: 2px solid var(--focus); outline-offset: 1px; }
.wb-pose-empty-note { padding: 0 var(--sp-3) var(--sp-2); margin: 0; }
.wb-pose-ao > summary { cursor: pointer; list-style: none; position: static; }
.wb-pose-ao > summary::before { content: '▸'; margin-right: 2px; color: var(--text-mute); }
.wb-pose-ao[open] > summary::before { content: '▾'; }
.wb-pose-ao-note { padding: 0 var(--sp-3) var(--sp-2); margin: 0; }

.wb-pose-row {
  display: grid; grid-template-columns: 36px minmax(0, 1fr) minmax(0, auto) 28px; align-items: center; gap: var(--sp-2);
  min-height: 44px; padding: 2px var(--sp-2) 2px var(--sp-3); cursor: pointer;
}
.wb-pose-row.is-active { background: var(--panel-sunken); }
.wb-pose-row[aria-selected="true"] { background: var(--accent-weak); }
.wb-pose-row[aria-selected="true"] .wb-pose-row-name { color: var(--accent); }
.wb-pose-row.is-dim, .wb-pose-navchip.is-dim { opacity: .5; }
.wb-pose-row.is-dim.is-active { opacity: .8; }
.wb-pose-thumb {
  width: 36px; height: 36px; border-radius: var(--r-sm); overflow: hidden; display: inline-flex;
  align-items: center; justify-content: center; background: var(--panel-sunken); color: var(--text-mute);
}
.wb-pose-thumb > img { width: 100%; height: 100%; object-fit: contain; }
.wb-pose-thumb[data-kind="poster"] { background: var(--stage-checker-b); }
.wb-pose-row-name { font-size: var(--fs-13); color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.wb-pose-unnamed { color: var(--text-mute); font-style: italic; }
.wb-pose-human { color: var(--text-dim); }
.wb-pose-row-id { min-width: 0; overflow: hidden; display: flex; justify-content: flex-end; }
/* An emote that needs Unity while Unity is off or removed (spec §12.8). */
.wb-pose-tag {
  margin-left: var(--sp-2); padding: 0 6px; border-radius: var(--r-pill); border: 1px solid var(--line);
  font-size: var(--fs-11, 11px); color: var(--text-dim); vertical-align: 1px;
}
.wb-pose-row-id.is-strong .mono-id { color: var(--text); font-weight: var(--fw-semibold); }
.wb-pose-star {
  width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm); color: var(--text-mute); opacity: 0;
}
.wb-pose-row:hover .wb-pose-star, .wb-pose-row.is-active .wb-pose-star, .wb-pose-star[aria-pressed="true"], .wb-pose-star:focus-visible { opacity: 1; }
.wb-pose-star[aria-pressed="true"] { color: var(--warn); }
.wb-pose-star[aria-pressed="true"] .wb-icon { fill: currentColor; }
.wb-pose-star:hover { background: var(--line); color: var(--text); }
@media (hover: none) { .wb-pose-star { opacity: 1; } }

.wb-pose-state { padding: var(--sp-4) var(--sp-3); display: grid; gap: var(--sp-2); justify-items: start; color: var(--text-dim); font-size: var(--fs-13); }
.wb-pose-state p { margin: 0; }
.wb-pose-state .wb-btn.is-active { outline: 2px solid var(--focus); outline-offset: 1px; }
.wb-pose-skel { padding: var(--sp-2) var(--sp-3); display: grid; gap: var(--sp-2); }
.wb-pose-skel-row { display: grid; grid-template-columns: 36px 1fr; gap: var(--sp-2); align-items: center; }
.wb-pose-picker-foot {
  display: flex; gap: var(--sp-3); padding: 6px var(--sp-3); border-top: 1px solid var(--line);
  font-size: var(--fs-11); color: var(--text-mute); flex: 0 0 auto;
}
.wb-pose-picker-foot > span { display: inline-flex; align-items: center; gap: 3px; }
.wb-pose-picker--sheet .wb-pose-search > .wb-input { height: 40px; font-size: 16px; }
.wb-pose-picker--sheet .wb-pose-chip { height: 32px; padding: 0 var(--sp-3); font-size: var(--fs-13); }
.wb-pose-picker--sheet .wb-pose-row { min-height: 52px; }

.wb-pose-hudq { color: var(--text); }

/* Which list [ ] is walking (R2-08); shown briefly after a press. */
.wb-pose-audition { font-size: var(--fs-11); white-space: nowrap; pointer-events: none; max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
.wb-pose-audition[hidden] { display: none; }

/* ── css/features/export.css */
/* Feature styles: export (spec §4.4, §6.11, §10). Tokens only; classes prefixed .wb-exp- (§17.9). */

/* ── Top bar button + jobs badge ─────────────────────────────────────────── */
.wb-exp-btnwrap { position: relative; display: inline-flex; }
.wb-exp-badge {
  position: absolute; top: -5px; right: -6px; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--r-pill); border: 2px solid var(--panel);
  background: var(--accent); color: var(--text-on-accent);
  font-size: 10px; font-weight: var(--fw-semibold); line-height: 14px; text-align: center;
  font-variant-numeric: tabular-nums; cursor: pointer; z-index: 1;
}
.wb-exp-badge[data-dot] { min-width: 12px; width: 12px; height: 12px; padding: 0; top: -3px; right: -3px; background: var(--ok); }
.wb-exp-badge:hover { filter: brightness(1.1); }
.wb-exp-badge:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
@media (max-width: 767px) {
  .wb-exp-btn > span { display: none; }
  .wb-exp-btn { width: var(--touch); padding: 0; border-color: transparent; background: transparent; }
}

/* ── Drawer layout ───────────────────────────────────────────────────────── */
.wb-exp-drawer { display: flex; flex-direction: column; min-height: 0; }
.wb-exp-head { padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line); flex: 0 0 auto; }
.wb-exp-head .wb-seg > button { gap: 6px; }
.wb-exp-pane { flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; }
.wb-exp-tab { display: flex; flex-direction: column; }
.wb-exp-tab > .wb-exp-note { margin: var(--sp-2) var(--sp-3) 0; }
.wb-exp-sec { padding: var(--sp-3); border-bottom: 1px solid var(--line); display: grid; gap: var(--sp-2); min-width: 0; }
.wb-exp-sec:last-child { border-bottom: 0; }
.wb-exp-sec-label {
  margin: 0; font-size: var(--fs-11); font-weight: var(--fw-semibold); color: var(--text-mute);
  text-transform: uppercase; letter-spacing: var(--tracking-label);
}
.wb-exp-sub { display: grid; gap: var(--sp-2); min-width: 0; }
.wb-exp-sub[hidden], .wb-exp-tab [hidden] { display: none !important; }
.wb-exp-row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; min-width: 0; }
.wb-exp-stack { display: grid; gap: var(--sp-1); min-width: 0; }
.wb-exp-inline-label { color: var(--text-dim); font-size: var(--fs-12); min-width: 52px; }
.wb-exp-shown { flex: 1 1 0; min-width: 0; max-width: 100%; text-overflow: ellipsis; }
.wb-exp-note { margin: 0; line-height: var(--lh); }
.wb-exp-sec .wb-seg--block { width: 100%; }
.wb-exp-sec .wb-seg > button { white-space: nowrap; }
/* Sections and sub-blocks are grids, whose items stretch to the column: a segmented control that is not --block was
   stretched to the full width while its buttons kept their label width, so the sunken tail after the last button read
   as one huge option (the Format switch's "WebP"). A non-block switch keeps its own width; a block switch splits the
   row into equal columns (grid, not flex: a longer label never takes a bigger share). */
.wb-exp-drawer :is(.wb-exp-sec, .wb-exp-sub, .wb-exp-stack) > .wb-seg:not(.wb-seg--block) { justify-self: start; }
.wb-exp-drawer .wb-seg--block { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
.wb-exp-drawer .wb-seg--block > button { min-width: 0; padding-inline: var(--sp-1); overflow: hidden; }

/* Number fields. */
.wb-exp-numfield { display: inline-flex; align-items: center; gap: 4px; }
.wb-exp-numlabel { color: var(--text-dim); font-size: var(--fs-12); font-variant-numeric: tabular-nums; }
.wb-exp-num { height: var(--control-h); padding: 0 6px; text-align: right; font-variant-numeric: tabular-nums; }
.wb-exp-num:disabled { opacity: .5; }
.wb-exp-suffix { color: var(--text-mute); font-size: var(--fs-12); }
.wb-exp-x { color: var(--text-mute); }
/* Margins: label · T B L R · reset on one grid row; the four fields share the width and never overflow. */
.wb-exp-margins { display: grid; grid-template-columns: auto repeat(4, minmax(0, 1fr)) auto; gap: 6px; }
.wb-exp-margins .wb-exp-numfield { gap: 3px; min-width: 0; }
.wb-exp-margins .wb-exp-num { width: 100%; min-width: 0; }
@media (max-width: 767px) {
  .wb-exp-margins { grid-template-columns: repeat(4, minmax(0, 1fr)) auto; }
  .wb-exp-margins > .wb-exp-inline-label { grid-column: 1 / -1; }
}

/* Framing chip (mirrors the stage tag). */
.wb-exp-chip {
  display: flex; align-items: center; gap: var(--sp-2); min-height: 28px;
  padding: 2px 4px 2px var(--sp-2); border-radius: var(--r-sm);
  background: var(--panel-sunken); color: var(--text-dim); font-size: var(--fs-12);
}
.wb-exp-chip-text { flex: 1 1 auto; min-width: 0; }
.wb-exp-chip[data-kind="adjusted"] { background: var(--accent-weak); color: var(--accent); }
.wb-exp-chip[data-kind="low"] { background: var(--warn-weak); color: var(--warn); }

/* Size summary. */
.wb-exp-summary { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; font-size: var(--fs-12); color: var(--text-dim); font-variant-numeric: tabular-nums; }
.wb-exp-summary-text { flex: 1 1 auto; min-width: 0; }
.wb-exp-summary[data-warn="true"] .wb-exp-summary-text { color: var(--warn); }

/* Colour swatch input. */
.wb-exp-colour {
  width: 32px; height: var(--control-h); padding: 2px; border-radius: var(--r-sm);
  border: 1px solid var(--line-strong); background: var(--panel-raised); cursor: pointer; flex: 0 0 auto;
}

/* Name template. */
.wb-exp-name { flex: 1 1 auto; min-width: 0; font-size: var(--fs-12); }
.wb-exp-name-preview {
  font-family: var(--font-mono); font-size: var(--fs-11); color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wb-exp-mono { font-family: var(--font-mono); }

/* Animation output radios. */
.wb-exp-radios { display: grid; gap: 2px; }
.wb-exp-radio {
  display: flex; align-items: flex-start; gap: var(--sp-2); padding: 6px var(--sp-2);
  border-radius: var(--r-sm); cursor: pointer;
}
.wb-exp-radio:hover { background: var(--panel-sunken); }
.wb-exp-radio:has(input:checked) { background: var(--accent-weak); }
.wb-exp-radio input { margin: 2px 0 0; accent-color: var(--accent); }
.wb-exp-radio-text { display: grid; gap: 1px; min-width: 0; }
.wb-exp-radio-label { font-size: var(--fs-13); font-weight: var(--fw-medium); }
.wb-exp-radio-desc { font-size: var(--fs-12); color: var(--text-dim); }
.wb-exp-radio[data-disabled="true"] { cursor: not-allowed; opacity: .55; }
.wb-exp-emote { margin: var(--sp-3) var(--sp-3) 0; font-size: var(--fs-12); color: var(--text-dim); }

/* Empty states. */
.wb-exp-empty {
  align-items: center; justify-content: center; text-align: center; gap: var(--sp-2);
  padding: var(--sp-8) var(--sp-6); color: var(--text-dim);
}
.wb-exp-empty h3 { margin: 0; font-size: var(--fs-15); color: var(--text); font-weight: var(--fw-semibold); }
.wb-exp-empty p { margin: 0 0 var(--sp-2); max-width: 280px; }

/* Layers → SVG: the Options disclosure (closed by default) between the mode and the notes. */
.wb-exp-sec--flush { padding: 0; }
.wb-exp-more > summary {
  display: flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 var(--sp-3);
  cursor: pointer; list-style: none; user-select: none;
  font-size: var(--fs-11); font-weight: var(--fw-semibold); color: var(--text-mute);
  text-transform: uppercase; letter-spacing: var(--tracking-label);
}
.wb-exp-more > summary::-webkit-details-marker { display: none; }
.wb-exp-more > summary:hover { background: var(--panel-sunken); }
.wb-exp-more > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.wb-exp-more > summary > .wb-icon { transition: transform var(--dur-fast) var(--ease-out); }
.wb-exp-more[open] > summary > .wb-icon { transform: rotate(90deg); }
.wb-exp-more-aside { margin-left: auto; text-transform: none; letter-spacing: 0; font-weight: var(--fw-regular); font-size: var(--fs-12); }
.wb-exp-more-body { display: grid; gap: var(--sp-3); padding: 2px var(--sp-3) var(--sp-3); }
.wb-exp-more-body .wb-seg--block { width: 100%; }
@media (pointer: coarse) { .wb-exp-more > summary { min-height: var(--touch); } }

/* Layers caveats. */
.wb-exp-caveats { margin: 0; padding-left: 18px; display: grid; gap: 4px; font-size: var(--fs-12); color: var(--text-dim); line-height: var(--lh); }
.wb-exp-confirm p { margin: 0 0 var(--sp-2); font-variant-numeric: tabular-nums; }

/* ── Footer ──────────────────────────────────────────────────────────────── */
.wb-exp-foot { gap: var(--sp-2); }   /* .wb-foot: one 44 px line (design 40 §B.7) */
.wb-exp-foot-start { display: flex; align-items: center; min-width: 0; }
.wb-exp-status { color: var(--text-dim); min-width: 0; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.wb-exp-actions { gap: var(--sp-1); }
.wb-exp-jobline { display: flex; align-items: center; gap: 6px; color: var(--text-dim); min-width: 0; }
.wb-exp-jobline[hidden] { display: none; }
.wb-exp-foot-start > .wb-exp-jobline { flex: 1 1 auto; }
.wb-exp-jobline > .wb-btn { flex: 0 0 auto; }
.wb-exp-again { flex: 0 0 auto; padding: 0; color: var(--accent); font-size: inherit; font-weight: var(--fw-medium); }
.wb-exp-again:hover { text-decoration: underline; }
/* An export error: a message above the footer (it reveals its height and fades: motion.reveal). */
.wb-exp-error {
  flex: 0 0 auto; display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-3); border-top: 1px solid var(--line);
  background: var(--err-weak); color: var(--err); font-size: var(--fs-12);
}
.wb-exp-error[hidden] { display: none; }
.wb-exp-error-text { flex: 1 1 auto; min-width: 0; color: var(--text); }
.wb-exp-jobline-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
.wb-exp-jobline[data-state="done"] { color: var(--ok); }
.wb-exp-jobline[data-state="error"] { color: var(--err); }
.wb-exp-jobline[data-state="error"] .wb-exp-jobline-text { white-space: normal; }

/* Progress bar (drawer footer and Jobs rows). */
.wb-exp-progress { height: 4px; border-radius: var(--r-pill); background: var(--panel-sunken); overflow: hidden; position: relative; }
.wb-foot-progress.wb-exp-progress { position: absolute; height: 2px; border-radius: 0; background: transparent; }
.wb-exp-progress[hidden] { display: none; }
.wb-exp-progress > span { display: block; height: 100%; width: 0; background: var(--accent); border-radius: inherit; transition: width var(--dur) linear; }
.wb-exp-progress[data-indeterminate] > span { width: 35%; position: absolute; animation: wb-exp-indet 1.2s var(--ease-out) infinite; }
@keyframes wb-exp-indet { from { left: -35%; } to { left: 100%; } }
@media (prefers-reduced-motion: reduce) { .wb-exp-progress[data-indeterminate] > span { animation: none; width: 100%; opacity: .5; } }

/* ── Jobs tray ───────────────────────────────────────────────────────────── */
.wb-exp-tray { padding: 0; display: flex; flex-direction: column; max-height: min(70vh, 520px); max-width: calc(100vw - 16px); }
.wb-exp-tray-head { display: flex; align-items: center; justify-content: space-between; padding: var(--sp-2) var(--sp-2) var(--sp-1) var(--sp-3); }
.wb-exp-tray-head .wb-label { font-size: var(--fs-11); font-weight: var(--fw-semibold); color: var(--text-mute); text-transform: uppercase; letter-spacing: var(--tracking-label); }
.wb-exp-jobs { list-style: none; margin: 0; padding: 0 var(--sp-1); overflow: auto; display: grid; gap: 2px; }
.wb-exp-tray-empty { padding: var(--sp-4) var(--sp-3); color: var(--text-dim); font-size: var(--fs-12); }
.wb-exp-tray-empty[hidden] { display: none; }
.wb-exp-tray-foot { padding: var(--sp-2) var(--sp-3); border-top: 1px solid var(--line); }
.wb-exp-job { display: flex; align-items: flex-start; gap: var(--sp-2); padding: var(--sp-2); border-radius: var(--r-sm); }
.wb-exp-job:hover { background: var(--panel-sunken); }
.wb-exp-job-icon { color: var(--text-dim); padding-top: 1px; }
.wb-exp-job[data-state="error"] .wb-exp-job-icon { color: var(--err); }
.wb-exp-job[data-state="done"] .wb-exp-job-icon { color: var(--ok); }
.wb-exp-job-main { flex: 1 1 auto; min-width: 0; display: grid; gap: 3px; }
.wb-exp-job-title { font-size: var(--fs-13); font-weight: var(--fw-medium); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-exp-job-meta { font-size: var(--fs-12); color: var(--text-dim); font-variant-numeric: tabular-nums; }
.wb-exp-job-note { font-size: var(--fs-11); color: var(--text-mute); overflow-wrap: anywhere; }
.wb-exp-job-note[hidden] { display: none; }
.wb-exp-job-acts { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
/* What the file's maker has to say (job.notes): one fold per group, the list scrolls inside the tray row. */
.wb-exp-job-notes { display: grid; gap: var(--sp-1); margin-top: 2px; justify-items: start; }
.wb-exp-job-notes[hidden] { display: none; }
.wb-exp-job-notegroup { font-size: var(--fs-11); color: var(--text-dim); min-width: 0; max-width: 100%; }
.wb-exp-job-notegroup > summary { cursor: pointer; padding: 3px 0; min-height: 24px; display: flex; align-items: center; }
.wb-exp-job-notegroup[data-key="standard"] > summary { color: var(--warn); }
.wb-exp-job-notegroup > ul { list-style: none; margin: 2px 0 var(--sp-1); padding: 0 0 0 var(--sp-3); display: grid; gap: 3px; max-height: 160px; overflow: auto; }
.wb-exp-job-notegroup li { overflow-wrap: anywhere; color: var(--text-mute); }

/* ── Stage overlay: frame, handles, tag ──────────────────────────────────── */
.wb-stage-dom > .wb-exp-ov { position: absolute; inset: 0; pointer-events: none; }
.wb-stage-dom > .wb-exp-ov[hidden] { display: none; }
.wb-exp-frame {
  position: absolute; pointer-events: auto; cursor: move; z-index: 4;
  touch-action: none; box-sizing: border-box; border-radius: 1px;
}
.wb-exp-frame[data-passive] { pointer-events: none; }
.wb-exp-frame[data-passive] .wb-exp-handle { display: none; }
.wb-exp-frame:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.wb-exp-handle {
  position: absolute; width: 12px; height: 12px; margin: -6px 0 0 -6px;
  background: var(--panel); border: 1.5px solid var(--accent); border-radius: 2px;
  box-shadow: var(--shadow-1);
}
.wb-exp-handle[data-h="nw"] { left: 0; top: 0; cursor: nwse-resize; }
.wb-exp-handle[data-h="n"]  { left: 50%; top: 0; cursor: ns-resize; }
.wb-exp-handle[data-h="ne"] { left: 100%; top: 0; cursor: nesw-resize; }
.wb-exp-handle[data-h="e"]  { left: 100%; top: 50%; cursor: ew-resize; }
.wb-exp-handle[data-h="se"] { left: 100%; top: 100%; cursor: nwse-resize; }
.wb-exp-handle[data-h="s"]  { left: 50%; top: 100%; cursor: ns-resize; }
.wb-exp-handle[data-h="sw"] { left: 0; top: 100%; cursor: nesw-resize; }
.wb-exp-handle[data-h="w"]  { left: 0; top: 50%; cursor: ew-resize; }
.wb-exp-frame[data-locked] .wb-exp-handle:is([data-h="n"], [data-h="s"], [data-h="e"], [data-h="w"]) { display: none; }
@media (pointer: coarse) {
  .wb-exp-handle { width: 22px; height: 22px; margin: -11px 0 0 -11px; }
}
.wb-exp-tag {
  position: absolute; z-index: 4; pointer-events: auto;
  display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 4px 0 8px;
  border-radius: var(--r-pill); background: var(--panel-raised); color: var(--text);
  box-shadow: var(--shadow-2); font-size: var(--fs-12); white-space: nowrap;
}
.wb-exp-tag-size { font-variant-numeric: tabular-nums; font-weight: var(--fw-medium); font-size: var(--fs-11); }
.wb-exp-tag-state { color: var(--text-dim); padding-left: 6px; border-left: 1px solid var(--line); }
.wb-exp-tag-state[hidden] { display: none; }
.wb-exp-tag[data-kind="adjusted"] .wb-exp-tag-state { color: var(--accent); }
.wb-exp-tag[data-kind="low"] .wb-exp-tag-state { color: var(--warn); }
.wb-exp-tag-reset {
  width: 20px; height: 20px; border-radius: var(--r-pill); color: var(--accent);
  display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-13); line-height: 1;
}
.wb-exp-tag-reset:hover { background: var(--accent-weak); }
.wb-exp-tag-reset[hidden] { display: none; }
.wb-exp-tag:not(:has(.wb-exp-tag-reset:not([hidden]))) { padding-right: 8px; }

/* Mobile: Export is a tab, so the top-bar button only shows while jobs are running (its badge opens the tray). */
@media (max-width: 767px) {
  .wb-exp-btnwrap:not(:has(.wb-exp-badge:not([hidden]))) { display: none; }
}
/* Max sizes locked for a look or emote the browser engine hands to Unity (QA P3, spec §12.6). */
.wb-exp-maxlock { display: flex; align-items: flex-start; gap: var(--sp-2); flex-wrap: wrap; }
.wb-exp-maxlock .wb-lock { flex: 1 1 14em; min-width: 0; align-items: flex-start; }
.wb-exp-maxlock .wb-lock > .wb-icon { margin-top: 1px; flex: none; }
/* [Export 2× without <item>] [Use Large]: wrap under the line when the column is narrow; a long item name is cut with an
   ellipsis (the button's tooltip shows it whole). */
.wb-exp-maxlock-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); min-width: 0; max-width: 100%; }
.wb-exp-maxlock-actions[hidden] { display: none; }
.wb-exp-maxlock .wb-exp-without { max-width: 100%; min-width: 0; }
.wb-exp-maxlock .wb-exp-without > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── css/features/renderer.css */
/* Feature styles: renderer. Owned by the renderer feature; use tokens only, classes prefixed per §17.9. */

/* ── Renderer chip (topbar.actions, order 30) ───────────────────────────── */
.wb-rnd-chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 28px; padding: 0 10px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--panel-raised); color: var(--text-dim);
  font-size: var(--fs-12); font-weight: var(--fw-medium); white-space: nowrap;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.wb-rnd-chip:hover { background: var(--panel-sunken); color: var(--text); }
@media (pointer: coarse) { .wb-rnd-chip { height: var(--touch); } }   /* a 44 px target on touch (QA TABLET-TARGETS) */
.wb-rnd-chip[aria-expanded="true"] { border-color: var(--accent); color: var(--text); }
.wb-rnd-chip[data-state="down"] { border-color: var(--err); color: var(--err); background: var(--err-weak); }
.wb-rnd-chip[data-state="warming"], .wb-rnd-chip[data-state="busy"] { border-color: var(--warn); }
.wb-rnd-chip[data-state="degraded"] { border-color: var(--warn); color: var(--warn); background: var(--warn-weak); }
.wb-rnd-chip[data-state="warming"] .wb-dot, .wb-rnd-chip[data-state="unknown"] .wb-dot { animation: wb-rnd-pulse 1.2s ease-in-out infinite; }
@keyframes wb-rnd-pulse { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .wb-rnd-chip .wb-dot { animation: none !important; } }
@media (max-width: 767px) {
  .wb-rnd-chip { padding: 0; width: var(--touch); height: var(--touch); justify-content: center; border-color: transparent; background: transparent; }
  .wb-rnd-chip-label { display: none; }
  .wb-rnd-chip .wb-dot { width: 10px; height: 10px; }
}

/* ── Popover ───────────────────────────────────────────────────────────── */
.wb-rnd-pop { width: min(380px, calc(100vw - 16px)); padding: 0; font-size: var(--fs-13); }
.wb-rnd-pop-head { padding: var(--sp-3) var(--sp-3) var(--sp-2); display: grid; gap: 6px; }
.wb-rnd-pop-title { color: var(--text-mute); }
.wb-rnd-pop-line { gap: var(--sp-2); }
.wb-rnd-pop-line .wb-dot { width: 10px; height: 10px; }
.wb-rnd-pop-name { font-weight: var(--fw-semibold); color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-rnd-pop-state { color: var(--text-dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
.wb-rnd-pop-state[data-state="ok"] { color: var(--ok); }
.wb-rnd-pop-state[data-state="warming"], .wb-rnd-pop-state[data-state="busy"], .wb-rnd-pop-state[data-state="degraded"] { color: var(--warn); }
.wb-rnd-pop-state[data-state="down"] { color: var(--err); }
.wb-rnd-pop-detail { display: grid; gap: 4px; }
.wb-rnd-pop-checked { min-height: 24px; }
.wb-rnd-pop-checked .wb-btn { height: 24px; padding: 0 var(--sp-2); }
.wb-rnd-pop-meta { padding: 0 var(--sp-3) var(--sp-3); display: grid; gap: 2px; border-bottom: 1px solid var(--line); padding-left: calc(var(--sp-3) + 18px); }
.wb-rnd-pop-sections { padding: 4px 0; }
.wb-rnd-pop-copy { padding: var(--sp-2) var(--sp-3); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }

.wb-rnd-details > summary {
  display: flex; align-items: center; gap: 6px; min-height: 32px; padding: 0 var(--sp-3);
  cursor: pointer; list-style: none; color: var(--text); font-weight: var(--fw-medium); user-select: none;
}
.wb-rnd-details > summary::-webkit-details-marker { display: none; }
.wb-rnd-details > summary:hover { background: var(--panel-sunken); }
.wb-rnd-details > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.wb-rnd-details > summary > .wb-icon { color: var(--text-mute); transition: transform var(--dur-fast) var(--ease-out); }
.wb-rnd-details[open] > summary > .wb-icon { transform: rotate(90deg); }
.wb-rnd-details > :not(summary) { padding: 2px var(--sp-3) var(--sp-3) calc(var(--sp-3) + 20px); }
.wb-rnd-sum-aside {
  margin-left: auto; color: var(--text-mute); font-size: var(--fs-11); font-weight: var(--fw-regular);
  font-variant-numeric: tabular-nums;
}
@media (pointer: coarse) { .wb-rnd-details > summary { min-height: var(--touch); } }

.wb-rnd-caps { display: grid; gap: 4px; }
.wb-rnd-caps-group { color: var(--text-mute); margin-top: var(--sp-2); }
.wb-rnd-caps-group:first-child { margin-top: 0; }
.wb-rnd-kv { display: grid; grid-template-columns: minmax(0, 42%) minmax(0, 1fr); gap: 2px var(--sp-2); margin: 0; font-size: var(--fs-12); }
.wb-rnd-kv dt { color: var(--text-dim); }
.wb-rnd-kv dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }
.wb-rnd-missing { margin: 0; padding-left: 16px; display: grid; gap: 2px; font-size: var(--fs-12); color: var(--text-dim); }

.wb-rnd-table { width: 100%; border-collapse: collapse; font-size: var(--fs-12); font-variant-numeric: tabular-nums; }
.wb-rnd-table th { text-align: left; font-weight: var(--fw-medium); color: var(--text-mute); padding: 2px 4px 4px 0; border-bottom: 1px solid var(--line); }
.wb-rnd-table td { padding: 3px 4px 3px 0; color: var(--text-dim); border-bottom: 1px solid var(--line); white-space: nowrap; }
.wb-rnd-table td.mono { font-family: var(--font-mono); color: var(--text); overflow: hidden; text-overflow: ellipsis; max-width: 140px; }
.wb-rnd-table .num { text-align: right; padding-right: 0; }
.wb-rnd-table tr[data-bad] td { color: var(--err); }
.wb-rnd-lasterr { margin-top: var(--sp-2); overflow-wrap: anywhere; }

.wb-rnd-service { display: grid; gap: var(--sp-3); }
.wb-rnd-svc-row { display: grid; gap: 4px; }
.wb-rnd-svc-row .wb-note--err { overflow-wrap: anywhere; }
.wb-rnd-countdown { font-size: var(--fs-12); color: var(--text-dim); padding: 6px var(--sp-2); border-radius: var(--r-sm); background: var(--warn-weak); }
.wb-rnd-count { font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); color: var(--text); }
.wb-rnd-ok { color: var(--ok); }
.wb-rnd-confirm { display: grid; gap: var(--sp-2); }
.wb-rnd-confirm p { margin: 0; }

/* ── Compare Front | Back (stage takeover) ──────────────────────────────── */
/* Compare covers the visible part of the stage (not what a drawer, a sheet or an overlaying panel covers). */
.wb-rnd-cmp {
  --rnd-safe-top: 60px; --rnd-safe-right: 0px; --rnd-safe-bottom: 40px; --rnd-safe-left: 0px;
  --rnd-split: 50%;
  position: absolute; top: 0; left: var(--stage-inset-left, 0px); right: var(--stage-inset-right, 0px); bottom: var(--stage-inset-bottom, 0px);
  overflow: hidden; background: var(--bg);
  user-select: none; -webkit-user-select: none; touch-action: none;
}
.wb-rnd-cmp-box { position: absolute; inset: 0; display: flex; gap: 1px; background: var(--line); }
.wb-rnd-cmp-pane { position: relative; flex: 1 1 0; min-width: 0; overflow: hidden; }
.wb-rnd-cmp[data-pannable] .wb-rnd-cmp-pane { cursor: grab; }
.wb-rnd-cmp[data-panning] .wb-rnd-cmp-pane { cursor: grabbing; }
.wb-rnd-cmp-media {
  position: absolute; left: 0; top: 0; transform-origin: 0 0; max-width: none;
  pointer-events: none; will-change: transform;
}
.wb-rnd-cmp-media[data-pixelated] { image-rendering: pixelated; }
.wb-rnd-cmp-media[data-dim] { opacity: .6; transition: opacity var(--dur) var(--ease-out); }
.wb-rnd-cmp-pane[data-updating] > img.wb-rnd-cmp-media { opacity: .85; }

/* Swipe: pane B is clipped to the right of the divider; holding shows B alone. */
.wb-rnd-cmp-clip { position: absolute; inset: 0; clip-path: inset(0 0 0 var(--rnd-split)); pointer-events: none; }
.wb-rnd-cmp[data-bonly] .wb-rnd-cmp-clip { clip-path: none; }
.wb-rnd-cmp[data-bonly] .wb-rnd-cmp-divider { opacity: 0; }
.wb-rnd-cmp[data-mode="swipe"] .wb-rnd-cmp-pane { touch-action: none; }
.wb-rnd-cmp-divider {
  position: absolute; top: 0; bottom: 0; left: var(--rnd-split); width: 24px; margin-left: -12px;
  cursor: ew-resize; z-index: 3; display: flex; align-items: center; justify-content: center; touch-action: none;
}
.wb-rnd-cmp-divider::before { content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px; background: var(--accent); box-shadow: 0 0 0 1px color-mix(in srgb, var(--bg) 60%, transparent); }
.wb-rnd-cmp-knob {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%; background: var(--accent); color: var(--text-on-accent); box-shadow: var(--shadow-2);
}
.wb-rnd-cmp-knob .wb-icon { width: 12px; height: 12px; margin: 0 -2px; }
.wb-rnd-cmp-divider:focus-visible { outline: none; }
.wb-rnd-cmp-divider:focus-visible .wb-rnd-cmp-knob { box-shadow: 0 0 0 3px var(--focus); }
@media (pointer: coarse) { .wb-rnd-cmp-divider { width: var(--touch); margin-left: calc(var(--touch) / -2); } .wb-rnd-cmp-knob { width: 36px; height: 36px; } }

.wb-rnd-cmp-cap {
  position: absolute; bottom: var(--sp-3); left: 50%; transform: translateX(-50%); z-index: 4;
  font-size: var(--fs-11); font-weight: var(--fw-medium); color: var(--text-dim); font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--panel) 86%, transparent); border: 1px solid var(--line);
  padding: 2px 8px; border-radius: var(--r-pill); white-space: nowrap; pointer-events: none;
  max-width: calc(100% - 2 * var(--sp-3)); overflow: hidden; text-overflow: ellipsis;
}
.wb-rnd-cmp[data-mode="swipe"] .wb-rnd-cmp-cap[data-pane="a"] { left: var(--sp-3); transform: none; bottom: calc(var(--sp-3) + 30px); }
.wb-rnd-cmp[data-mode="swipe"] .wb-rnd-cmp-cap[data-pane="b"] { left: auto; right: var(--sp-3); transform: none; bottom: calc(var(--sp-3) + 30px); }
.wb-rnd-cmp[data-bonly] .wb-rnd-cmp-cap[data-pane="a"] { opacity: 0; }
.wb-rnd-cmp-hint {
  position: absolute; top: calc(var(--rnd-safe-top) - 8px); left: 50%; transform: translateX(-50%); z-index: 4;
  font-size: var(--fs-11); color: var(--text-mute); pointer-events: none; white-space: nowrap;
}

.wb-rnd-cmp-status { position: absolute; inset: 0; z-index: 2; pointer-events: none; display: flex; align-items: center; justify-content: center; padding: var(--rnd-safe-top) var(--sp-4) var(--rnd-safe-bottom); }
.wb-rnd-cmp-status:empty { display: none; }
.wb-rnd-cmp-status > * { pointer-events: auto; }
.wb-rnd-cmp-status > .wb-skel { opacity: .5; }
.wb-rnd-cmp[data-mode="swipe"] .wb-rnd-cmp-status[data-pane="b"] { left: var(--rnd-split); }
.wb-rnd-cmp[data-mode="swipe"][data-bonly] .wb-rnd-cmp-status[data-pane="b"] { left: 0; }
.wb-rnd-cmp[data-mode="swipe"] .wb-rnd-cmp-status[data-pane="a"] { right: calc(100% - var(--rnd-split)); }
.wb-rnd-cmp-msg {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; text-align: center;
  max-width: 320px; padding: var(--sp-2) var(--sp-3); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--panel-raised) 92%, transparent); border: 1px solid var(--line); box-shadow: var(--shadow-1);
  font-size: var(--fs-12); color: var(--text-dim);
}
.wb-rnd-cmp-msg[data-kind="error"] { border-color: var(--err); color: var(--text); }
.wb-rnd-cmp-msg .wb-lock { padding: 0; }
.wb-rnd-cmp-pill {
  position: absolute; top: calc(var(--rnd-safe-top) - 4px); right: calc(var(--sp-3) + var(--rnd-safe-right, 0px));
  display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; border-radius: var(--r-pill);
  font-size: var(--fs-11); color: var(--text-dim); background: color-mix(in srgb, var(--panel) 86%, transparent); border: 1px solid var(--line);
}
.wb-rnd-cmp-pill .wb-spinner { width: 10px; height: 10px; border-width: 1.5px; }

/* Placed against the stage toolbar by compare.js (QA2 M1): --rnd-cmp-top is the toolbar's row when it made room,
   else the row below it; --rnd-cmp-maxw is what is left beside the toolbar (or the stage width). It wraps when it must. */
.wb-rnd-cmp-controls {
  position: absolute; top: var(--rnd-cmp-top, var(--sp-3)); right: var(--sp-3); z-index: 5;
  display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2);
  max-width: var(--rnd-cmp-maxw, calc(100% - 2 * var(--sp-3)));
  min-height: 36px; padding: 2px 4px 2px var(--sp-3);
  background: var(--panel-raised); border: 1px solid var(--line); border-radius: var(--r-pill); box-shadow: var(--shadow-2);
}
.wb-rnd-cmp-controls[data-wrapped] { border-radius: var(--r-lg); padding: 4px; }
.wb-rnd-cmp-title { font-size: var(--fs-12); font-weight: var(--fw-semibold); color: var(--text); white-space: nowrap; }
/* The whole takeover comes in together: the panes, the controls row and the parity bar fade over --dur-2 (the controls
   alone used to be there from the first frame, the pictures fading in under them). */
.wb-rnd-cmp[data-enter] .wb-rnd-cmp-box, .wb-rnd-cmp[data-enter] .wb-rnd-cmp-controls, .wb-rnd-cmp[data-enter] .wb-rnd-cmp-stats { animation: wb-rnd-fade var(--dur-2) var(--ease-standard); }
@keyframes wb-rnd-fade { from { opacity: 0; } to { opacity: 1; } }
@media (max-width: 767px) {
  .wb-rnd-cmp { --rnd-safe-top: 56px; --rnd-safe-right: calc(var(--touch) + 16px); --rnd-safe-bottom: 44px; }
  .wb-rnd-cmp-controls { left: var(--sp-2); right: auto; top: var(--rnd-cmp-top, var(--sp-2)); padding: 3px; border-radius: var(--r-lg); gap: 2px; justify-content: flex-start; }
  .wb-rnd-cmp-controls .wb-seg { border: 0; }
  .wb-rnd-cmp-title { display: none; }
}

/* ── Fit check (stage takeover) ─────────────────────────────────────────── */
.wb-rnd-fit {
  position: absolute; inset: 0; display: flex; flex-direction: column; gap: var(--sp-2);
  padding: calc(var(--rnd-tb-bottom, 48px) + 10px) var(--sp-4) 44px; background: var(--bg); overflow: auto;
}
.wb-rnd-fit-head { display: flex; align-items: flex-start; gap: var(--sp-3); flex-wrap: wrap; }
.wb-rnd-fit-heading { flex: 1 1 240px; min-width: 0; display: grid; gap: 2px; }
.wb-rnd-fit-title { font-size: var(--fs-15); color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-rnd-fit-name { font-weight: var(--fw-semibold); }
.wb-rnd-fit-item { color: var(--text-dim); }
.wb-rnd-fit-cost { font-size: var(--fs-12); color: var(--text-dim); font-variant-numeric: tabular-nums; }
.wb-rnd-fit-cost[data-kind="error"] { color: var(--err); }
.wb-rnd-fit-cost[data-kind="done"] { color: var(--ok); }
.wb-rnd-fit-actions { flex: 0 0 auto; }
.wb-rnd-fit-grid {
  flex: 1 1 auto; min-height: 0;
  display: grid; gap: var(--sp-2);
  grid-template-columns: auto repeat(var(--rnd-cols, 5), minmax(0, 1fr));
  grid-template-rows: auto repeat(var(--rnd-rows, 2), minmax(120px, 1fr));
}
.wb-rnd-fit-corner { min-width: 0; grid-row: 1; grid-column: 1; }
.wb-rnd-fit-colhead { grid-row: 1; grid-column: calc(var(--s) + 2); }
.wb-rnd-fit-rowhead { grid-row: calc(var(--d) + 2); grid-column: 1; }
.wb-rnd-fit-cell { grid-row: calc(var(--d) + 2); grid-column: calc(var(--s) + 2); }
.wb-rnd-fit-colhead {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  font-size: var(--fs-12); font-weight: var(--fw-semibold); color: var(--text);
}
.wb-rnd-fit-colhead kbd { font-size: var(--fs-11); color: var(--text-mute); }
.wb-rnd-fit-rowhead {
  display: flex; align-items: center; justify-content: center; padding: 0 var(--sp-1);
  font-size: var(--fs-12); font-weight: var(--fw-semibold); color: var(--text);
  writing-mode: vertical-rl; transform: rotate(180deg);
}
.wb-rnd-fit-cell {
  position: relative; min-width: 0; min-height: 0; border-radius: var(--r-md);
  background: var(--stage-light); border: 1px solid var(--line); overflow: hidden;
}
.wb-rnd-fit-cell[data-current] { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.wb-rnd-fit-cell[data-state="error"] { border-color: var(--err); box-shadow: inset 0 0 0 1px var(--err); background: var(--err-weak); }
.wb-rnd-fit-hit {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: stretch; justify-content: flex-end;
  padding: var(--sp-1) var(--sp-1) 0; color: var(--text); text-align: center; border-radius: inherit;
}
.wb-rnd-fit-hit:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.wb-rnd-fit-hit:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.wb-rnd-fit-canvas { flex: 1 1 auto; min-height: 0; width: 100%; height: 100%; object-fit: contain; visibility: hidden; }
.wb-rnd-fit-cell[data-has-image] .wb-rnd-fit-canvas { visibility: visible; }
.wb-rnd-fit-cell[data-state="loading"][data-has-image] .wb-rnd-fit-canvas,
.wb-rnd-fit-cell[data-state="queued"][data-has-image] .wb-rnd-fit-canvas { opacity: .5; }
.wb-rnd-fit-label {
  flex: 0 0 auto; padding: 4px 0 6px; font-size: var(--fs-11); font-weight: var(--fw-medium); color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wb-rnd-fit-skel { position: absolute; left: 22%; right: 22%; top: 12%; bottom: 30px; border-radius: var(--r-md); display: none; pointer-events: none; }
.wb-rnd-fit-cell:not([data-has-image]):is([data-state="queued"], [data-state="loading"]) .wb-rnd-fit-skel { display: block; }
.wb-rnd-fit-cell[data-state="queued"] .wb-rnd-fit-skel { animation: none; opacity: .5; }
.wb-rnd-fit-msg {
  position: absolute; left: var(--sp-1); right: var(--sp-1); top: var(--sp-1); z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 6px; pointer-events: none;
}
.wb-rnd-fit-msg:empty { display: none; }
.wb-rnd-fit-msg > * { pointer-events: auto; }
.wb-rnd-fit-cell[data-state="error"] .wb-rnd-fit-msg { top: 50%; transform: translateY(-60%); }
.wb-rnd-fit-err {
  display: flex; align-items: flex-start; gap: 4px; max-width: 100%; font-size: var(--fs-11); color: var(--err); text-align: left;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
}
.wb-rnd-fit-err .wb-icon { flex: 0 0 auto; vertical-align: -2px; margin-right: 2px; }
.wb-rnd-fit-pill {
  display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; border-radius: var(--r-pill);
  font-size: var(--fs-11); color: var(--text-dim); background: color-mix(in srgb, var(--panel) 86%, transparent); border: 1px solid var(--line);
  pointer-events: none;
}
.wb-rnd-fit-pill .wb-spinner { width: 10px; height: 10px; border-width: 1.5px; }
.wb-rnd-fit-hint { text-align: center; }
@media (max-width: 767px) {
  .wb-rnd-fit { padding: var(--sp-2) calc(var(--rnd-tb-right, 60px) + 8px) 44px var(--sp-2); }
  /* Phones: transpose — body sizes down, facings across; the page scrolls vertically. */
  .wb-rnd-fit-grid {
    flex: 0 0 auto;
    grid-template-columns: auto repeat(var(--rnd-rows, 2), minmax(0, 1fr));
    grid-template-rows: auto repeat(var(--rnd-cols, 5), 240px);
  }
  .wb-rnd-fit-colhead { grid-row: calc(var(--s) + 2); grid-column: 1; writing-mode: vertical-rl; transform: rotate(180deg); }
  .wb-rnd-fit-colhead kbd { display: none; }
  .wb-rnd-fit-rowhead { grid-row: 1; grid-column: calc(var(--d) + 2); writing-mode: horizontal-tb; transform: none; padding: 2px 0; }
  .wb-rnd-fit-cell { grid-row: calc(var(--s) + 2); grid-column: calc(var(--d) + 2); }
  .wb-rnd-fit-hint { display: none; }
}

/* Compare while an emote plays on a front-only renderer: pane B shows the idle still with this note. */
.wb-rnd-cmp-pill--left { right: auto; left: var(--sp-3); max-width: calc(100% - 2 * var(--sp-3)); }
.wb-rnd-cmp-pill--left .wb-lock { padding: 0; font-size: var(--fs-11); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Side by side: lift the stage HUD (size · renderer, draft label) above pane B's caption instead of overlapping it. */
@media (min-width: 768px) {
  #stage:has(.wb-rnd-cmp[data-mode="side"]) .wb-hud-br { bottom: calc(var(--sp-3) + 32px + var(--stage-inset-bottom, 0px)); }
}
/* Phones: Fit check takes the whole stage — the floating toolbar (flip, size, zoom: all moot for the grid) steps
   aside instead of sitting on the Back column; the grid's own ✕ exits. */
@media (max-width: 767px) {
  #stage:has(.wb-rnd-fit) .wb-stage-toolbar { display: none; }
}

/* ── Renderer choice and fallback line (popover) ─────────────────────────── */
.wb-rnd-choice { display: flex; flex-direction: column; gap: var(--sp-1); margin: var(--sp-2) 0; }
.wb-rnd-fallback { gap: var(--sp-2); margin-top: var(--sp-1); }
.wb-rnd-fallback .wb-dot { flex: none; }

/* ── Compare → Renderer: parity bar (tolerance, % pixels differ, report) ─── */
.wb-rnd-cmp-stats {
  position: absolute; top: var(--rnd-stats-top, calc(var(--sp-3) + 44px)); right: var(--sp-3); z-index: 5;
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; justify-content: flex-end;
  max-width: calc(100% - 2 * var(--sp-3)); padding: 3px 4px 3px var(--sp-3);
  background: var(--panel-raised); border: 1px solid var(--line); border-radius: var(--r-pill); box-shadow: var(--shadow-1);
  font-size: var(--fs-12); color: var(--text-dim); font-variant-numeric: tabular-nums;
}
.wb-rnd-cmp-stats-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.wb-rnd-cmp-stats-tol { display: inline-flex; align-items: center; gap: 4px; }
.wb-rnd-cmp-stats .wb-toggle { font-size: var(--fs-12); }
/* Emote time alignment (QA P2): one more line inside the parity bar. */
.wb-rnd-cmp-stats[data-note] { border-radius: var(--r-lg); padding-bottom: 6px; }
.wb-rnd-cmp-align {
  flex: 1 0 100%; display: inline-flex; align-items: center; justify-content: flex-end; gap: 5px;
  font-size: var(--fs-11); color: var(--text-dim); text-align: right; padding-right: var(--sp-2);
}
.wb-rnd-cmp-align .wb-icon { width: 12px; height: 12px; flex: none; }
.wb-rnd-cmp[data-mode="diff"] .wb-rnd-cmp-pane { background: var(--bg); }
.wb-rnd-cmp .wb-rnd-cmp-pill.wb-hud-fallback { color: var(--warn); max-width: calc(100% - 2 * var(--sp-3) - var(--rnd-safe-right, 0px)); border-radius: 10px; }
.wb-rnd-cmp-stats[data-wrapped] { border-radius: var(--r-lg); }
/* Phones: the parity bar sits at the bottom with the captions just above it; compare.js keeps the feet above both
   (--rnd-stats-h is the bar's measured height, QA2 N4). */
@media (max-width: 767px) {
  .wb-rnd-cmp-stats { top: auto; bottom: var(--sp-2); left: var(--sp-2); right: var(--sp-2); border-radius: var(--r-lg); justify-content: flex-start; padding-left: var(--sp-2); }
  /* Two short rows: the numbers, then the tolerance stepper (its value says what it is) and the report. */
  .wb-rnd-cmp-stats-text { flex: 1 0 100%; padding: 2px 0 0 2px; }
  .wb-rnd-cmp-stats-tol > .wb-note { display: none; }
  .wb-rnd-cmp-stats > .wb-btn { margin-left: auto; }
  .wb-rnd-cmp[data-source="renderer"] .wb-rnd-cmp-cap[data-pane] { bottom: calc(var(--sp-2) + var(--rnd-stats-h, 0px) + 6px); }
  /* Swipe: each caption keeps to its own half, so a long one never runs over the other. */
  .wb-rnd-cmp[data-mode="swipe"] .wb-rnd-cmp-cap[data-pane] { max-width: calc(50% - var(--sp-3) - 4px); }
}
/* While Compare is on, the stage's "Rendered by Unity — reason" badge steps aside (it sat on pane B's caption and
   under the phone parity bar); the pane it is about says it (compare.js, QA2 N4). */
#stage:has(.wb-rnd-cmp) .wb-hud-br .wb-hud-fallback { display: none; }
/* In a pane (half the stage in Swipe on a phone) the short reason may take a third line rather than be cut. */
.wb-rnd-cmp .wb-hud-fallback-text { -webkit-line-clamp: 3; line-clamp: 3; }
/* Swipe: the "Rendered by Unity — reason" notes sit in one layer over the whole stage, not in the half a pane's status
   layer is (QA N4: a phone's half was ~100 px, a note cut to three lines). */
.wb-rnd-cmp-notes {
  position: absolute; z-index: 4; left: var(--sp-3); right: calc(var(--sp-3) + var(--rnd-safe-right, 0px));
  top: calc(var(--rnd-safe-top) - 4px); display: flex; flex-direction: column; align-items: flex-start; gap: 4px; pointer-events: none;
}
.wb-rnd-cmp-notes:empty { display: none; }
.wb-rnd-cmp .wb-rnd-cmp-notes > .wb-rnd-cmp-pill.wb-hud-fallback { position: static; max-width: 100%; pointer-events: auto; }
/* Unity's fallback line (spec §12.8): [Check Unity] stays on one line beside the text. */
.wb-rnd-fallback-check { flex: none; white-space: nowrap; align-self: flex-start; }

/* ── css/features/library.css */
/* Feature styles: library (spec §6.16): saved looks and favourites. Owned by the library feature; tokens only,
   classes .wb-lib-*. The drawer is 360 px wide (--drawer-library-w); a bottom sheet on mobile. */

.wb-lib { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

/* ── Save / status ──────────────────────────────────────────────────────── */
.wb-lib-status {
  flex: 0 0 auto; display: grid; gap: var(--sp-2); align-items: center;
  padding: var(--sp-3); border-bottom: 1px solid var(--line);
}
.wb-lib-status[data-kind="saved"] { grid-template-columns: minmax(0, 1fr) auto; }
.wb-lib-state { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: var(--fs-13); color: var(--text-dim); }
.wb-lib-state > .wb-icon { flex: 0 0 auto; width: 16px; height: 16px; }
.wb-lib-state > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-lib-state strong { color: var(--text); font-weight: var(--fw-semibold); }
.wb-lib-status[data-kind="saved"] .wb-lib-state > .wb-icon { color: var(--ok); }
.wb-lib-status[data-kind="edited"] .wb-lib-state > .wb-icon { color: var(--warn); }
.wb-lib-state-acts { display: flex; gap: var(--sp-1); flex-wrap: wrap; }
.wb-lib-save-label { margin: 0; }
.wb-lib-save-row { display: flex; gap: var(--sp-1); align-items: center; min-width: 0; }
.wb-lib-save-row > .wb-input { flex: 1 1 auto; min-width: 0; }

/* ── Filter ─────────────────────────────────────────────────────────────── */
.wb-lib-filter { flex: 0 0 auto; margin: var(--sp-2) var(--sp-3) 0; }
.wb-lib-filter[hidden] { display: none; }
.wb-lib-filter-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

/* ── Saved looks ────────────────────────────────────────────────────────── */
.wb-lib-list { flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; padding: var(--sp-1) 0 var(--sp-2); }
.wb-lib-list:empty { display: none; }
.wb-lib-row { position: relative; display: flex; align-items: center; gap: 2px; padding-right: var(--sp-2); }
.wb-lib-row:hover { background: color-mix(in srgb, var(--panel-sunken) 60%, var(--panel)); }
.wb-lib-row[data-current] { background: color-mix(in srgb, var(--accent) 8%, var(--panel)); box-shadow: inset 2px 0 0 var(--accent); }
.wb-lib-open {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: var(--sp-3);
  padding: 6px var(--sp-1) 6px var(--sp-3); text-align: left; color: inherit; border-radius: 0; cursor: pointer;
}
.wb-lib-open--edit { cursor: default; }
.wb-lib-open:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.wb-lib-thumb {
  position: relative; flex: 0 0 auto; width: 40px; height: 52px; border-radius: var(--r-sm);
  background: var(--panel-sunken); overflow: hidden; display: flex; align-items: center; justify-content: center;
}
.wb-lib-thumb-img { width: 100%; height: 100%; object-fit: contain; }
.wb-lib-thumb:not([data-state="ok"]) .wb-lib-thumb-img { display: none; }
.wb-lib-thumb-glyph { display: flex; color: var(--text-mute); opacity: .7; }
.wb-lib-thumb[data-state="ok"] .wb-lib-thumb-glyph { display: none; }
.wb-lib-thumb[data-state="loading"] {
  background: linear-gradient(90deg, var(--panel-sunken) 0%, var(--line) 50%, var(--panel-sunken) 100%);
  background-size: 200% 100%; animation: wb-shimmer 1.2s linear infinite;
}
.wb-lib-thumb[data-state="loading"] .wb-lib-thumb-glyph { visibility: hidden; }
@media (prefers-reduced-motion: reduce) { .wb-lib-thumb[data-state="loading"] { animation: none; } }
.wb-lib-text { flex: 1 1 auto; min-width: 0; display: grid; gap: 2px; }
.wb-lib-name-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
.wb-lib-name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--fs-13); font-weight: var(--fw-medium); color: var(--text); line-height: var(--lh-tight);
}
.wb-lib-meta { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-12); color: var(--text-dim); }
.wb-lib-pill {
  flex: 0 0 auto; height: 16px; padding: 0 6px; border-radius: var(--r-pill); line-height: 16px;
  font-size: var(--fs-11); font-weight: var(--fw-medium); color: var(--accent); background: var(--accent-weak);
}
.wb-lib-rename { height: 26px; font-size: var(--fs-13); }
.wb-lib-more { flex: 0 0 auto; opacity: 0; }
.wb-lib-row:hover .wb-lib-more, .wb-lib-row:focus-within .wb-lib-more, .wb-lib-more[aria-expanded="true"] { opacity: 1; }
@media (hover: none) { .wb-lib-more { opacity: 1; } }

/* ── Empty ──────────────────────────────────────────────────────────────── */
.wb-lib-empty {
  flex: 1 1 auto; display: grid; gap: var(--sp-2); justify-items: center; align-content: start; text-align: center;
  padding: var(--sp-8) var(--sp-5); color: var(--text-dim);
}
.wb-lib-empty[hidden] { display: none; }
.wb-lib-empty > .wb-icon { color: var(--text-mute); }
.wb-lib-empty p { margin: 0; max-width: 36ch; overflow-wrap: anywhere; }
.wb-lib-empty-title { color: var(--text); font-size: var(--fs-13); font-weight: var(--fw-medium); }

/* ── Footer: where it is kept · Export · Import ─────────────────────────── */
.wb-lib-foot { gap: var(--sp-1); color: var(--text-mute); }   /* .wb-foot: one 44 px line (design 40 §B.7) */
.wb-lib-foot-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wb-lib-link { padding: 0; color: var(--accent); font-size: inherit; font-weight: var(--fw-medium); }
.wb-lib-link:hover { text-decoration: underline; }
.wb-lib-foot .wb-btn:disabled { opacity: .5; }
.wb-lib-warn { flex: 0 0 auto; margin: 0; padding: 6px var(--sp-3); font-size: var(--fs-12); color: var(--warn); background: var(--warn-weak); }
.wb-lib-warn[hidden] { display: none; }

/* Touch: taller rows and controls. */
@media (pointer: coarse) {
  .wb-lib-open { padding-top: 8px; padding-bottom: 8px; }
  .wb-lib-name, .wb-lib-state { font-size: var(--fs-13); }
  .wb-lib-rename { height: 36px; font-size: 16px; }
  .wb-lib-save-row > .wb-input { font-size: 16px; }
}

