*, *::before, *::after {
  margin: 0; padding: 0; box-sizing: border-box;
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
}

html, body {
  width: 100%; height: 100%; overflow: hidden;
  font-family: var(--text);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  background: var(--shell-bg); color: #f4f2ff;
  transition: background .45s;
}

:root {
  /* Section typography — faces declared in shared/fonts/css/interactive.css. */
  --display: 'Unbounded', 'Nunito', system-ui, sans-serif;
  /* Only one display weight ships — pin it so the browser never fakes a bolder cut. */
  --display-weight: 600;
  --text: 'Nunito', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --mono: 'Roboto Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* App shell geometry */
  --shell-gap: 16px;      /* gutter between the window and the shell edges */
  --header-h: 74px;       /* header row: also the window's top inset */
  --window-radius: 16px;
  --island-gap: 12px;     /* отступ раскрытого сайдбара-«острова» от краёв экрана */
  /* The shell sits a touch under the sidebar so the panel still reads as raised. */
  --shell-bg: color-mix(in srgb, var(--menu-bg) 78%, #05050a);

  --scene-bg: #0a0a14;
  --scene-accent: #6366f1;
  --menu-bg: #0a0a14;
  --menu-orb: #6366f1;
  --menu-orb-fg: #fff;
  --fg: #f4f2ff;
  --fg-a05: rgba(244, 242, 255, 0.05);
  --fg-a08: rgba(244, 242, 255, 0.08);
  --fg-a12: rgba(244, 242, 255, 0.12);
  --fg-a25: rgba(244, 242, 255, 0.25);
  --fg-a40: rgba(244, 242, 255, 0.4);
  --fg-a55: rgba(244, 242, 255, 0.55);
  --fg-a75: rgba(244, 242, 255, 0.75);
  --surface: color-mix(in srgb, var(--scene-bg) 30%, rgba(10, 10, 20, 0.85));
  --surface-solid: color-mix(in srgb, var(--scene-bg) 20%, #111218);
  --sidebar-w: 296px;
  --peek: 10px;
  --dock-h: 118px;
  --transition-fast: .18s;
  --transition-med: .28s cubic-bezier(.2, .8, .2, 1);
}

:root[data-mode="light"] {
  --shell-bg: color-mix(in srgb, var(--menu-bg) 74%, #ffffff);
  --fg: #15151a;
  --fg-a05: rgba(21, 21, 26, 0.05);
  --fg-a08: rgba(21, 21, 26, 0.08);
  --fg-a12: rgba(21, 21, 26, 0.12);
  --fg-a25: rgba(21, 21, 26, 0.25);
  --fg-a40: rgba(21, 21, 26, 0.4);
  --fg-a55: rgba(21, 21, 26, 0.55);
  --fg-a75: rgba(21, 21, 26, 0.75);
  --surface: color-mix(in srgb, var(--scene-bg) 25%, rgba(252, 252, 250, 0.82));
  --surface-solid: color-mix(in srgb, var(--scene-bg) 15%, #fefefe);
  color: var(--fg);
}

.scroll::-webkit-scrollbar { width: 6px; height: 6px; }
.scroll::-webkit-scrollbar-track { background: transparent; }
.scroll::-webkit-scrollbar-thumb { background: var(--fg-a12); border-radius: 3px; }
.scroll::-webkit-scrollbar-thumb:hover { background: var(--fg-a25); }

button { font-family: inherit; color: inherit; border: none; background: none; cursor: pointer; }
button:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
iframe { color-scheme: normal; background-color: inherit; border: none; }

/* ===== APP HEADER =====
   The shell's title row: identity on the left, every action on the right.
   It stops at the sidebar's edge, so pinning the list narrows the header and
   the window together and the two right edges stay flush. */
.app-header {
  position: fixed;
  top: 0; left: 0;
  right: var(--stage-right, 0px);
  height: var(--header-h);
  display: flex; align-items: center; gap: 14px;
  padding: 0 var(--shell-gap);
  z-index: 30;
  color: var(--fg);
  /* The row is transparent and reaches over the sidebar's peek strip, so only
     its real contents may take the pointer — otherwise the empty middle of the
     header swallows the hover and the clicks meant for the diamond handle. */
  pointer-events: none;
  transition: right var(--transition-med), height var(--transition-med), opacity .25s;
}
.app-header > * { pointer-events: auto; }

/* A hover-opened sidebar is raised above the header so the action cluster does
   not paint over its own head row — which would then bury a dropdown opened
   from that same cluster. While one is open the header takes the top layer back. */
.app-header:has(.lang-selector.open),
.app-header:has(.scene-help.open) { z-index: 40; }

.header-actions {
  margin-left: auto;
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 6px;
}

/* ===== STAGE — the inset window =====
   The experiment is not full-bleed any more: it is a separate rounded surface
   sitting inside our chrome, which is what makes the whole thing read as one
   application with an app loaded into it. */
.stage {
  position: fixed;
  top: var(--header-h);
  left:   calc(var(--stage-left,   0px) + var(--shell-gap));
  right:  calc(var(--stage-right,  0px) + var(--shell-gap));
  bottom: calc(var(--stage-bottom, 0px) + var(--shell-gap));
  padding: 0;
  background: var(--scene-bg);
  border: 1px solid var(--fg-a05);
  border-radius: var(--window-radius);
  overflow: hidden;
  isolation: isolate;
  box-shadow: 0 30px 70px -30px rgba(0,0,0,.7), 0 2px 12px -6px rgba(0,0,0,.45);
  transition:
    top var(--transition-med), left var(--transition-med),
    right var(--transition-med), bottom var(--transition-med),
    border-radius var(--transition-med),
    border-width var(--transition-med), border-color var(--transition-med),
    box-shadow var(--transition-med), background .6s;
}

.stage-slot {
  position: absolute; inset: 0;
  border-radius: inherit; overflow: hidden;
  opacity: 0;
  transform: scale(1.04);
  filter: blur(12px);
  transition:
    opacity 450ms cubic-bezier(.4, 0, .2, 1),
    transform 600ms cubic-bezier(.4, 0, .2, 1),
    filter 450ms cubic-bezier(.4, 0, .2, 1);
  pointer-events: none;
}
.stage-slot.visible {
  opacity: 1; transform: scale(1); filter: blur(0);
  pointer-events: auto;
}
.stage-slot iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  background: var(--scene-bg);
}

.stage-flash {
  position: absolute; inset: 0;
  background: var(--scene-bg);
  opacity: 0; pointer-events: none;
  transition: opacity 180ms ease-out;
  z-index: 5;
}
.stage-flash.active { opacity: 0.35; }

#spec-frame {
  position: fixed; width: 0; height: 0;
  top: -9999px; left: -9999px; pointer-events: none;
}

/* ===== SCENE LABEL ===== */
.scene-label {
  position: relative; top: auto; left: auto; z-index: auto;
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 12px;
  padding: 0;
  color: var(--fg);
  text-shadow: none;
  transition: opacity .25s;
}

/* ===== BACK TO THE LAB =====
   Sits immediately left of the current scene's icon and copies its size and shape,
   so the two read as one control: "this scene · leave it". The label unfurls on
   hover (0fr -> 1fr) instead of permanently stealing width from the scene title —
   and because the column is content-sized, it fits every one of the 12 locales. */
.lab-home {
  flex: 0 0 auto;
  height: 36px;
  border-radius: 10px;
  background: var(--fg-a05);
  border: 1px solid var(--fg-a05);
  color: var(--fg-a75);
  text-decoration: none;
  overflow: hidden;
  display: inline-grid;
  grid-template-columns: 36px minmax(0, 0fr);
  align-items: center;
  transition: grid-template-columns var(--transition-med),
    background var(--transition-fast),
    color var(--transition-fast), border-color var(--transition-fast);
}
.lab-home > svg { grid-column: 1; justify-self: center; }
.lab-home-label {
  grid-column: 2;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  padding-right: 12px;
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
}
.lab-home:hover,
.lab-home:focus-visible {
  grid-template-columns: 36px minmax(0, 1fr);
  background: var(--fg-a12);
  color: var(--fg);
  border-color: var(--fg-a25);
}
@media (prefers-reduced-motion: reduce) {
  .lab-home { transition: color var(--transition-fast); }
}

.scene-label-icon {
  flex: 0 0 auto;
  width: 36px; height: 36px; border-radius: 10px;
  background: var(--surface);
  backdrop-filter: blur(20px) saturate(1.2);
  -webkit-backdrop-filter: blur(20px) saturate(1.2);
  border: 1px solid var(--fg-a05);
  display: grid; place-items: center;
  font-size: 17px; overflow: hidden;
  position: relative;
}
.scene-label-icon .ico-slot {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  opacity: 0; transform: scale(0.82); filter: blur(4px);
  transition:
    opacity .45s cubic-bezier(.4,0,.2,1),
    transform .5s cubic-bezier(.4,0,.2,1),
    filter .45s cubic-bezier(.4,0,.2,1);
  pointer-events: none;
}
.scene-label-icon .ico-slot.visible { opacity: 1; transform: scale(1); filter: blur(0); }
.scene-label-icon .ico-slot img { width: 100%; height: 100%; object-fit: cover; }

/* The slots inside are absolutely positioned, so the column has no intrinsic
   width of its own — it takes the header's leftover room instead. */
.scene-label-text {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; gap: 1px;
}

.scene-label-name,
.scene-label-meta {
  overflow: hidden; position: relative; white-space: nowrap;
}
.scene-label-name { height: 22px; }
.scene-label-meta { height: 16px; }

/* left+right rather than shrink-to-fit: the slot has to inherit the column's
   width, otherwise it sizes itself to the text, never overflows, and the
   ellipsis never fires — the parent just hard-clips the name mid-letter. */
.label-slot {
  position: absolute; left: 0; right: 0; top: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0;
  transform: translateY(-25%);
  filter: blur(4px);
  transition:
    opacity .45s cubic-bezier(.4,0,.2,1),
    transform .45s cubic-bezier(.4,0,.2,1),
    filter .45s cubic-bezier(.4,0,.2,1);
  pointer-events: none;
}
.label-slot.visible {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
}
.label-slot.leaving {
  opacity: 0;
  transform: translateY(25%);
  filter: blur(4px);
}

.scene-label-name .label-slot {
  font-size: 14px; font-weight: 600;
  color: var(--fg); letter-spacing: -.01em;
  max-width: 34vw;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.scene-label-meta .label-slot {
  font-size: 11px; font-family: var(--mono);
  color: var(--fg-a55); letter-spacing: .04em;
}

/* ===== NAV ARROWS =====
   Centred inside the window, not the viewport: the header eats the top inset and
   the dock (on mobile) the bottom one. The duplicated `top` is a deliberate dvh
   progressive enhancement — the vh line is the fallback. */
.nav-arrow {
  position: fixed;
  top: calc((var(--header-h) + 100vh  - var(--stage-bottom, 0px) - var(--shell-gap)) / 2);
  top: calc((var(--header-h) + 100dvh - var(--stage-bottom, 0px) - var(--shell-gap)) / 2);
  transform: translateY(-50%);
  width: 52px; height: 52px; border-radius: 50%;
  background: var(--surface);
  backdrop-filter: blur(20px) saturate(1.2);
  -webkit-backdrop-filter: blur(20px) saturate(1.2);
  border: 1px solid var(--fg-a05);
  color: var(--fg);
  z-index: 20; display: grid; place-items: center;
  transition: background var(--transition-fast), border-color var(--transition-fast),
              color var(--transition-fast), opacity var(--transition-fast),
              transform var(--transition-fast),
              top var(--transition-med), left var(--transition-med), right var(--transition-med);
}
.nav-arrow:hover { background: var(--scene-accent); border-color: var(--scene-accent); color: #fff; }
.nav-arrow:hover:not(:disabled) { transform: translateY(-50%) scale(1.04); }
.nav-arrow:disabled { opacity: 0.25; pointer-events: none; }

.nav-left  { left:  calc(var(--stage-left,  0px) + var(--shell-gap) + 14px); }
.nav-right { right: calc(var(--stage-right, 0px) + var(--shell-gap) + 14px); }

/* Nav hints tooltip */
.nav-tip {
  position: absolute;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 7px 10px; border-radius: 8px;
  background: rgba(20, 22, 30, 0.92);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--fg-a12);
  color: var(--fg); font-size: 12px; font-weight: 500;
  white-space: nowrap; pointer-events: none;
  opacity: 0; transition: opacity .2s;
  z-index: 1;
}
.nav-left  .nav-tip { left: calc(100% + 10px); }
.nav-right .nav-tip { right: calc(100% + 10px); }
.nav-tip kbd {
  font-family: var(--mono);
  font-size: 10px; color: var(--fg-a55);
  letter-spacing: .04em;
}
:root:not(.nav-hints-dismissed) .nav-arrow:hover .nav-tip { opacity: 1; }

/* ===== HEADER BUTTONS =====
   The action buttons, the tips button and "open in a new tab" are the same
   control — one bordered square, one hover state. */
.action-btn,
.scene-help-btn,
.scene-open-btn {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  width: 36px; height: 36px; padding: 0;
  border-radius: 10px;
  border: 1px solid var(--fg-a05);
  background: var(--fg-a05);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 11px; letter-spacing: .1em;
  box-shadow: none; opacity: 1;
  transition: background .2s, border-color .2s, color .2s;
}
.action-btn:hover,
.scene-help-btn:hover,
.scene-open-btn:hover { background: var(--fg-a12); }
.action-pill { width: auto; padding: 0 10px; }

.action-bar { display: flex; align-items: center; gap: 6px; color: var(--fg); }
.scene-help    { position: relative; }
.scene-open-btn{ position: relative; }
.scene-help[hidden], .scene-open-btn[hidden] { display: none; }
.scene-help.open .scene-help-btn {
  background: color-mix(in srgb, var(--menu-orb) 26%, var(--fg-a08));
  border-color: var(--fg-a25);
}

/* Lang selector */
.lang-selector { position: relative; }
.lang-dropdown {
  position: absolute;
  top: calc(100% + 10px); right: 0; left: auto; bottom: auto;
  width: 240px; max-height: min(420px, 60vh); overflow-y: auto;
  display: flex; flex-direction: column; gap: 1px;
  background: var(--surface-solid);
  border: 1px solid var(--fg-a12);
  border-radius: 14px;
  padding: 6px;
  box-shadow: 0 24px 60px -18px rgba(0,0,0,.55);
  transform-origin: top right;
  opacity: 0; visibility: hidden; transform: translateY(-6px) scale(.98);
  z-index: 40;
  scrollbar-width: thin;
  transition: opacity var(--transition-fast), visibility var(--transition-fast), transform var(--transition-fast);
}
.lang-selector.open .lang-dropdown { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
.lang-option {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px; border-radius: 8px;
  font-size: 13px; color: var(--fg-a75);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.lang-option:hover { background: var(--fg-a08); color: var(--fg); }
.lang-option.active { color: var(--fg); font-weight: 600; }
.lang-option.active::after {
  content: '✓';
  margin-left: auto;
  color: var(--scene-accent);
  font-weight: 700;
}
.flag-icon { width: 18px; height: 14px; object-fit: cover; border-radius: 2px; }

/* ===== SIDEBAR ===== */
.sidebar {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: var(--sidebar-w);
  z-index: 25;
  pointer-events: none;
}
/* Hovered open, the panel slides out across the header row, so it has to win the
   stacking order — otherwise the header's action cluster paints over the
   sidebar's own head. Pinned, the header already stops at the panel's edge, and
   collapsed it must stay under the header so the top-right corner keeps its
   clicks. */
.sidebar.open:not(.pinned) { z-index: 35; }
.sidebar-hover-zone,
.sidebar-handle,
.sidebar-panel { pointer-events: auto; }
.sidebar-hover-zone {
  position: absolute; top: 0; bottom: 0;
  right: 0;
  width: 28px;
  z-index: 0; /* stay under the panel/handle so it never intercepts their clicks */
  transition: width var(--transition-med);
}
.sidebar.open .sidebar-hover-zone { width: calc(var(--sidebar-w) + 22px); }

/* Reveal handle — attached to the left edge of the peek strip when collapsed,
   vertically centred on the header row so it lines up with the chrome.
   Язычок — торчащий край самой панели, а не отдельная плашка, поэтому берёт её
   цвет и её рамку. */
.sidebar-handle {
  --size: 30px;
  position: absolute;
  top: calc((var(--header-h) - var(--size)) / 2);
  right: calc(var(--peek) - (var(--size) / 2));
  width: var(--size);
  height: var(--size);
  background: var(--menu-bg);
  border-bottom-left-radius: 4px;
  backdrop-filter: blur(20px) saturate(1.2);
  -webkit-backdrop-filter: blur(20px) saturate(1.2);
  border: 1px solid var(--fg-a08);
  border-right: none;
  color: var(--fg);
  display: grid; place-items: center;
  z-index: 20;
  opacity: 1;
  box-shadow: -4px 8px 20px -6px rgba(0,0,0,0.25);
  transform: rotate(45deg);
  transition:
    opacity .25s,
    top var(--transition-med),
    right var(--transition-med),
    transform var(--transition-med),
    background .35s;
}
.sidebar-handle > svg {
  transform: rotate(-45deg) translateX(-6px);
}
.sidebar-handle:hover { opacity: 0; scale: .95; }
.sidebar.open .sidebar-handle {
  opacity: 0;
  pointer-events: none;
  scale: 0.95;
}
.sidebar.pinned .sidebar-handle { display: none; }

.sidebar-panel {
  position: absolute; top: 0; right: calc(-1 * (var(--sidebar-w) - var(--peek))); bottom: 0;
  width: var(--sidebar-w);
  background: var(--menu-bg);
  backdrop-filter: blur(24px) saturate(1.2);
  -webkit-backdrop-filter: blur(24px) saturate(1.2);
  /* В свёрнутом виде видна только левая кромка; рамка задана целиком, но
     прозрачной, чтобы при раскрытии в «остров» анимировался лишь цвет. */
  border: 1px solid transparent;
  border-left-color: var(--fg-a08);
  border-radius: 0;
  display: flex; flex-direction: column;
  gap: 0;
  overflow: hidden;
  isolation: isolate;
  transition: top var(--transition-med), right var(--transition-med),
    bottom var(--transition-med), border-radius var(--transition-med),
    border-color var(--transition-med), box-shadow var(--transition-med),
    background .35s, backdrop-filter .35s;
}
/* Раскрытая панель — плавающий «остров»: отступы от краёв, скругление и
   приподнятая тень, как у окна сцены. */
.sidebar.open .sidebar-panel {
  top: var(--island-gap); right: var(--island-gap); bottom: var(--island-gap);
  border-radius: var(--window-radius);
  border-color: var(--fg-a05);
  box-shadow: 0 30px 70px -30px rgba(0,0,0,.7);
}
.sidebar.open:not(.pinned) .sidebar-panel {
  backdrop-filter: blur(32px) saturate(1.5);
  -webkit-backdrop-filter: blur(32px) saturate(1.5);
}

/* Orb blobs */
.sidebar-panel::before,
.sidebar-panel::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  filter: blur(60px);
  opacity: 0.45;
  pointer-events: none;
  z-index: -1;
  background: var(--menu-orb);
  transition: background .6s;
}
.sidebar-panel::before { width: 200px; height: 200px; top: -60px; right: -60px; }
.sidebar-panel::after  { width: 160px; height: 160px; bottom: 60px; left: -40px; opacity: 0.3; }

.sidebar-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 16px 14px;
  flex-shrink: 0;
}
.sidebar-brand { display: flex; align-items: center; gap: 10px; min-width: 0; }

.sidebar-logo {
  width: 36px; height: 36px; border-radius: 9px;
  background: var(--menu-orb);
  color: var(--menu-orb-fg);
  display: grid; place-items: center;
  font-size: 16px; font-weight: 700;
  flex-shrink: 0; overflow: hidden;
  position: relative;
  transition: background .6s, color .3s;
}
.sidebar-logo .ico-slot {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  opacity: 0; transform: scale(0.82); filter: blur(4px);
  transition:
    opacity .45s cubic-bezier(.4,0,.2,1),
    transform .5s cubic-bezier(.4,0,.2,1),
    filter .45s cubic-bezier(.4,0,.2,1);
  pointer-events: none;
}
.sidebar-logo .ico-slot.visible { opacity: 1; transform: scale(1); filter: blur(0); }
.sidebar-logo .ico-slot img { width: 100%; height: 100%; object-fit: cover; }
.sidebar-brand-text { min-width: 0; }
.sidebar-title {
  font-size: 14px; font-weight: 700;
  color: var(--fg); letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidebar-subtitle {
  font-size: 11px; color: var(--fg-a55);
  white-space: nowrap; margin-top: 1px;
}

.sidebar-pin {
  width: 30px; height: 30px; border-radius: 7px;
  display: grid; place-items: center;
  color: var(--fg-a40); flex-shrink: 0;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.sidebar-pin:hover { background: var(--fg-a08); color: var(--fg); }
.sidebar-pin .icon-collapse { display: none; }
.sidebar.pinned .sidebar-pin .icon-pin     { display: none; }
.sidebar.pinned .sidebar-pin .icon-collapse { display: inline; }

/* ===== TAG CLOUD ===== */
.tag-cloud-wrap {
  padding: 4px 14px 8px;
  flex-shrink: 0;
}

.tag-cloud {
  position: relative;
  display: flex; flex-wrap: wrap; gap: 5px;
  padding: 3px;
}

/* Sliding indicator for the "All" chip. JS sets top/left/width/height to the
   active chip's own box, so it tracks a single chip even when the chips wrap
   onto a second row (otherwise it would span the full cloud height and bleed
   over the row below). Opaque, like the segmented control in the 3d section —
   the chip label rides on top of it. */
.filter-indicator {
  position: absolute;
  top: 3px; height: 24px;
  border-radius: 7px;
  background: var(--menu-orb);
  opacity: 1;
  pointer-events: none;
  transition: left .2s cubic-bezier(.2,.8,.2,1), top .2s cubic-bezier(.2,.8,.2,1),
              width .2s cubic-bezier(.2,.8,.2,1), height .2s cubic-bezier(.2,.8,.2,1),
              opacity .2s, background .35s;
  z-index: 0;
}

.tag-chip {
  position: relative; z-index: 1;
  padding: 4px 9px;
  border-radius: 7px;
  font-size: 11px; font-weight: 500;
  color: var(--fg-a75);
  background: var(--fg-a05);
  border: 1px solid var(--fg-a05);
  transition: background var(--transition-fast), color var(--transition-fast),
              border-color var(--transition-fast);
  cursor: pointer;
}
.tag-chip:hover {
  background: var(--fg-a08);
  color: var(--fg);
}
.tag-chip.active {
  background: transparent;
  border-color: transparent;
  color: var(--menu-orb-fg);
  font-weight: 600;
}
/* The sliding pill only ever tracks the "all" chip (the filter is multi-select,
   one pill cannot cover several chips), so every other active tag paints the
   same pill itself — without it the label would sit on bare sidebar and turn
   unreadable on light scenes. */
.tag-chip.active:not([data-tag="all"]) {
  background: var(--menu-orb);
  border-color: var(--menu-orb);
}

/* ===== SIDEBAR CONTENT ===== */
.sidebar-meta {
  display: flex; justify-content: space-between; align-items: center;
  padding: 4px 16px 6px;
  font-size: 11px; color: var(--fg-a40);
  letter-spacing: .04em; text-transform: uppercase;
  flex-shrink: 0;
}
.sidebar-list {
  flex: 1;
  overflow-y: auto; overflow-x: hidden;
  padding: 4px 8px 8px;
  display: flex; flex-direction: column; gap: 2px;
}
/* Подвал острова: слева — действия, языковая пилюля прижата к правому краю. */
.sidebar-footer {
  flex-shrink: 0;
  padding: 10px 12px;
  border-top: 1px solid var(--fg-a05);
}
/* На мобильном панель действий переезжает в шапку — подвал без неё не должен
   оставлять полосу (:empty не сработал бы из-за пробельных узлов). */
.sidebar-footer:not(:has(.action-bar)) { display: none; }
.sidebar-footer .action-bar { display: flex; width: 100%; gap: 6px; }
.sidebar-footer .lang-selector { margin-left: auto; }
/* Панель обрезается overflow:hidden, поэтому список языков открывается вверх
   и остаётся внутри острова; закрытым он спрятан вниз, а не вверх. */
/* In the sidebar this is not a small floating card. It fills the band the scene
   list occupies and sits flush on the footer, edge to edge, so its bottom corners
   have nothing left to round and only the top two are cut. --lang-top and
   --lang-bottom are measured off the list rather than assumed: the tag row wraps,
   the meta row comes and goes, and the notch insets the whole panel. Measuring is
   what keeps twelve languages out of a scrollbar wherever the panel is tall
   enough to hold them.

   position: static on the selector is the point of the whole rule — it hands the
   containing block up to the panel, which is what lets the dropdown be as wide as
   the sidebar instead of as wide as the little pill it hangs off. */
.sidebar-footer .lang-selector { position: static; }
.sidebar-footer .lang-dropdown {
  top: auto;
  bottom: var(--lang-bottom, 56px);
  /* left/right against the containing block resolve to the panel width in
     both engines: whichever ancestor turns out to be positioned, its padding
     box spans the panel, because nothing between them has a side border. */
  left: 0; right: 0; width: auto;
  /* An absolute, not a percentage. The two engines disagree on which ancestor
     is the containing block here — the panel in the shared viewer, the footer
     in the legacy ones — and a percentage of the footer is 57px of room. */
  max-height: var(--lang-band, 60vh);
  padding: 8px;
  border-radius: 14px 14px 0 0;
  border-bottom: 0;
  background: color-mix(in srgb, var(--menu-bg) 74%, transparent);
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  box-shadow: 0 -22px 50px -20px rgba(0, 0, 0, .6);
  /* Revealed upward out of the footer: the clip starts closed against the bottom
     edge and opens towards the top, so the list grows rather than slides. */
  transform: none;
  clip-path: inset(100% 0 0 0);
  transition: opacity var(--transition-fast), visibility var(--transition-fast),
              clip-path var(--transition-med);
}
.sidebar-footer .lang-selector.open .lang-dropdown { clip-path: inset(0 0 0 0); }

/* Scene list items */
.scene-item {
  display: flex; align-items: center; gap: 14px;
  padding: 10px;
  border-radius: 12px;
  color: var(--fg-a75);
  width: 100%;
  text-align: left;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.scene-item:hover { background: var(--fg-a05); color: var(--fg); }
.scene-item.active {
  background: color-mix(in srgb, var(--menu-orb) 14%, var(--fg-a05));
  color: var(--fg);
}

.scene-thumb {
  width: 82px; height: 54px;
  border-radius: 8px;
  background: var(--sc-bg, #0a0a14);
  outline: 1px solid var(--fg-a08);
  flex-shrink: 0; overflow: hidden;
  position: relative;
  transition: outline .2s;
}
.scene-item.active .scene-thumb {
  outline: 1.5px solid var(--fg-a55);
  outline-offset: 1px;
}
.scene-thumb img {
  width: 100%; height: 100%; object-fit: cover;
  display: block;
}
.scene-thumb-fallback {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-size: 22px;
}
.scene-thumb-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 40%, rgba(0,0,0,.35) 100%);
}
.scene-thumb.intro {
  background: linear-gradient(135deg, var(--sc-bg, #0a0a14), color-mix(in srgb, var(--sc-accent, #6366f1) 30%, var(--sc-bg, #0a0a14)));
}
.intro-label {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; font-size: 10px; font-weight: 600;
  color: var(--fg-a55); letter-spacing: .1em; text-transform: uppercase;
}
.intro-mark { font-size: 18px; }

.scene-info { min-width: 0; flex: 1; }
.scene-name {
  font-size: 12px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: inherit;
}
.scene-num {
  font-size: 10px;
  font-family: var(--mono);
  color: var(--fg-a40); letter-spacing: .04em;
  margin-top: 2px;
}

/* Hidden by filter */
.scene-item.hidden, .dock-item.hidden { display: none; }
.scene-item, .dock-item {
  animation: filter-in .28s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes filter-in {
  from { opacity: 0; transform: translateY(5px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ===== MOBILE DOCK ===== */
.mobile-dock {
  display: none;
  position: fixed; bottom: 0; left: 0; right: 0;
  height: var(--dock-h, 118px);
  z-index: 20;
  background: var(--surface);
  backdrop-filter: blur(20px) saturate(1.2);
  -webkit-backdrop-filter: blur(20px) saturate(1.2);
  border-top: 1px solid var(--fg-a08);
  padding: 10px 0 14px;
}

.mobile-dock-track {
  display: flex; align-items: flex-start; gap: 8px;
  overflow-x: auto; overflow-y: hidden;
  padding: 0 12px;
  height: 100%;
  scroll-snap-type: x mandatory;
}
.dock-item {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  flex-shrink: 0;
  scroll-snap-align: center;
  padding: 2px;
  border-radius: 10px;
  transition: background var(--transition-fast);
}
.dock-item:hover { background: var(--fg-a05); }
.dock-item.active { background: color-mix(in srgb, var(--menu-orb) 14%, var(--fg-a05)); }

.dock-thumb {
  width: 56px; height: 56px; border-radius: 10px;
  background: var(--sc-bg, #0a0a14);
  border: 1px solid var(--fg-a08);
  overflow: hidden; flex-shrink: 0;
  display: grid; place-items: center;
  font-size: 20px;
}
.dock-thumb.intro {
  background: linear-gradient(135deg, var(--sc-bg, #0a0a14), color-mix(in srgb, var(--sc-accent, #6366f1) 30%, var(--sc-bg, #0a0a14)));
}
.dock-thumb img { width: 100%; height: 100%; object-fit: cover; }
.dock-name {
  font-size: 9px; font-weight: 500; color: var(--fg-a55);
  max-width: 60px; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ===== SHORTCUTS MODAL ===== */
.modal-backdrop {
  position: fixed; inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(8px);
  display: grid;
  place-items: center;
  opacity: 0;
  visibility: hidden;
  scale: 1.1;
  filter: blur(8px);
  transition: opacity .18s, visibility .18s, transform .25s, scale .25s, filter .25s;
}
.modal-backdrop.open {
  opacity: 1;
  visibility: visible;
  scale: 1;
  filter: blur(0);
}

.modal {
  background: var(--surface-solid);
  border: 1px solid var(--fg-a12);
  border-radius: 18px;
  padding: 24px;
  width: min(400px, calc(100vw - 32px));
  box-shadow: 0 24px 64px rgba(0,0,0,.5);
}
.modal-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  margin-bottom: 20px;
}
.modal-eyebrow {
  font-size: 10px; font-weight: 600; letter-spacing: .15em;
  text-transform: uppercase; color: var(--fg-a40);
  margin-bottom: 4px;
}
.modal-title { font-size: 20px; font-weight: 700; color: var(--fg); }
.modal-close {
  width: 30px; height: 30px; border-radius: 8px;
  display: grid; place-items: center;
  color: var(--fg-a55);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.modal-close:hover { background: var(--fg-a08); color: var(--fg); }

.shortcut-list { display: flex; flex-direction: column; gap: 6px; }
.shortcut-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 10px; border-radius: 8px;
  background: var(--fg-a05);
  font-size: 13px; color: var(--fg-a75);
}
.shortcut-row kbd {
  font-family: var(--mono);
  font-size: 11px; color: var(--fg-a55);
  background: var(--fg-a08);
  border: 1px solid var(--fg-a12);
  border-radius: 5px;
  padding: 2px 7px;
}

/* ===== SCENE HELP PANEL =====
   Anchored under the tips button in the header, so it opens downward. */
.scene-help-panel {
  position: absolute;
  top: calc(100% + 10px); right: 0; left: auto; bottom: auto;
  width: 320px; max-width: calc(100vw - 2 * var(--shell-gap));
  background: var(--surface-solid);
  border: 1px solid var(--fg-a12);
  border-radius: 14px;
  padding: 14px 16px 16px;
  box-shadow: 0 24px 60px -18px rgba(0,0,0,.55);
  transform-origin: top right;
  transform: scale(.92); opacity: 0; pointer-events: none;
  display: block;
  z-index: 40;
  transition: opacity .2s cubic-bezier(.2,.8,.2,1), transform .22s cubic-bezier(.2,.8,.2,1);
}
.scene-help.open .scene-help-panel { opacity: 1; transform: scale(1); pointer-events: auto; }

.scene-help-head {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 10px;
}
.scene-help-title { font-size: 13px; font-weight: 600; color: var(--fg); }
.scene-help-close {
  width: 24px; height: 24px; border-radius: 6px;
  display: grid; place-items: center;
  color: var(--fg-a40);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.scene-help-close:hover { background: var(--fg-a08); color: var(--fg); }

.scene-help-list {
  list-style: none; display: flex; flex-direction: column; gap: 6px;
}
.scene-help-list li {
  font-size: 12px; line-height: 1.55; color: var(--fg-a75);
  padding-left: 12px; position: relative;
}
.scene-help-list li::before {
  content: '→';
  position: absolute; left: 0;
  color: var(--scene-accent);
  font-size: 10px;
}

/* ===== TOAST ===== */
.toast {
  position: fixed;
  bottom: calc(var(--stage-bottom, 0px) + var(--shell-gap) + 18px);
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--surface-solid);
  border: 1px solid var(--fg-a12);
  border-radius: 12px;
  padding: 10px 16px;
  font-size: 13px; font-weight: 500;
  color: var(--fg);
  z-index: 100;
  opacity: 0; pointer-events: none;
  transition: opacity .22s, transform .22s cubic-bezier(.2,.8,.2,1);
  box-shadow: 0 8px 24px rgba(0,0,0,.3);
  max-width: calc(100vw - 32px);
}
.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
}
.toast--copy {
  display: flex; align-items: flex-start; gap: 10px;
}
.toast-icon { flex-shrink: 0; margin-top: 1px; }
.toast-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.toast-label { font-weight: 600; }
.toast-url {
  font-size: 11px;
  font-family: var(--mono);
  color: var(--fg-a55);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 240px;
}

/* Scene credit in the help panel */
.scene-help-list li.scene-help-credit {
  margin-top: 8px; padding-top: 8px; padding-left: 0;
  border-top: 1px solid var(--fg-a08);
  color: var(--fg-a40); font-size: 11px;
}
.scene-help-list li.scene-help-credit::before { content: none; }

/* ===== КАСТОМНАЯ ПОДСКАЗКА =====
   Единственный элемент на весь документ; позиционируется из JS по
   getBoundingClientRect цели и никогда не выходит за края экрана. */
.ui-tip {
  position: fixed;
  left: 0; top: 0;
  z-index: 120;
  max-width: min(280px, calc(100vw - 16px));
  background: var(--surface-solid);
  border: 1px solid var(--fg-a08);
  border-radius: 8px;
  padding: 6px 10px;
  font-size: 11px; font-weight: 500;
  color: var(--fg);
  box-shadow: 0 12px 32px -14px rgba(0,0,0,.5);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px) scale(.97);
  transition: opacity .18s cubic-bezier(.2,.8,.2,1), transform .18s cubic-bezier(.2,.8,.2,1);
}
/* Подсказка, перевёрнутая выше цели, въезжает сверху вниз. */
.ui-tip.above { transform: translateY(-4px) scale(.97); }
.ui-tip.visible { opacity: 1; transform: translateY(0) scale(1); }

/* ===== MOBILE ===== */
@media (max-width: 760px) {
  :root { --shell-gap: 10px; --header-h: 58px; --window-radius: 12px; }
  .app-header { right: 0; gap: 8px; padding: 0 var(--shell-gap); }
  .scene-label { gap: 8px; }
  .lab-home, .scene-label-icon { width: 34px; height: 34px; }
  .lab-home { grid-template-columns: 34px minmax(0, 0fr); }
  .lab-home:hover, .lab-home:focus-visible { grid-template-columns: 34px minmax(0, 1fr); }
  .scene-label-name .label-slot { font-size: 12px; max-width: 40vw; }
  .action-btn, .scene-help-btn, .scene-open-btn { width: 32px; height: 32px; border-radius: 9px; }
  /* The language pill is text-sized, not a square. */
  .action-pill { width: auto; }
  #actFullscreen, #actShortcuts { display: none; }
  .sidebar { display: none; }
  .mobile-dock { display: block; }
  .nav-arrow { width: 44px; height: 44px; }
  .nav-tip { display: none; }
}

/* ===== HIDE THE INTERFACE =====
   Один переключатель убирает все слои вокруг сцены, потому что её собственные
   элементы управления часто живут под нашими: окно становится полноэкранным,
   шапка прячется целиком, а в углу остаётся только плавающая кнопка возврата.
   Сам iframe не трогаем. */
.ui-toggle .icon-show { display: none; }
.ui-toggle[aria-pressed="true"] .icon-hide { display: none; }
.ui-toggle[aria-pressed="true"] .icon-show { display: block; }
.ui-toggle[aria-pressed="true"] {
  background: color-mix(in srgb, var(--menu-orb) 20%, var(--fg-a08));
  color: var(--fg);
}

:root.ui-hidden { --header-h: 0px; --shell-gap: 0px; --window-radius: 0px; }
/* Прозрачной рамки мало: сквозь неё по всему периметру просвечивает фон
   оболочки, и вокруг сцены остаётся волосяная линия — рамка схлопывается в ноль. */
:root.ui-hidden .stage { border-width: 0; border-color: transparent; box-shadow: none; }
:root.ui-hidden .app-header { display: none; }
/* Обнулённый --window-radius нужен полноэкранной сцене; плавающий «остров»
   сохраняет своё скругление. */
:root.ui-hidden .sidebar.open .sidebar-panel { border-radius: 16px; }
/* Пока обвязка скрыта, сайдбар работает только по наведению — закреплять нечего,
   и кнопка закрепления уходит вместе с остальным хромом. */
:root.ui-hidden .sidebar-pin { display: none; }

/* Плавающая кнопка возврата интерфейса: шапки больше нет, поэтому нужен
   отдельный элемент поверх сцены. Строится из JS. Прижата вплотную к правому
   нижнему углу и одета в цвета сайдбара — так она читается как его продолжение,
   а не как чужая плашка на сцене. `corner-shape: bevel` поддержан не везде: без
   него угол просто останется скруглённым, это допустимая деградация. */
.ui-restore {
  display: none;
  position: fixed; right: 0; bottom: 0; top: auto; left: auto;
  width: 46px; height: 46px;
  z-index: 50;
  border-radius: 16px 0 0 0;
  corner-shape: bevel;
  background: var(--menu-bg);
  border: 1px solid var(--fg-a08);
  border-right: none; border-bottom: none;
  color: var(--fg);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  opacity: .85;
  transform: translate(0, 0);
  transition: opacity var(--transition-fast), transform var(--transition-med),
              background .35s, border-color .2s;
}
.ui-restore:hover,
.ui-restore:focus-visible { opacity: 1; background: color-mix(in srgb, var(--fg) 8%, var(--menu-bg)); }
:root.ui-hidden .ui-restore { display: grid; place-items: center; }

/* Наведение на правый край выдвигает сайдбар прямо поверх этого угла — кнопка
   уходит тем же путём, которым её накрыло бы: наружу через свой угол. */
:root.ui-hidden .sidebar.open ~ .ui-restore {
  opacity: 0;
  pointer-events: none;
  transform: translate(100%, 100%);
}

/* Ромб-ручка центрируется по строке шапки — при --header-h: 0 он вылез бы за
   верхний край. Кнопка возврата теперь внизу, верх свободен, поэтому ручке
   хватает небольшого отступа от края. */
:root.ui-hidden .sidebar-handle { top: 18px; }

/* visibility, not opacity alone: a fully transparent overlay would still eat
   the clicks meant for the scene underneath. */
:root.ui-hidden .nav-arrow,
:root.ui-hidden .mobile-dock {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--transition-fast) ease, visibility 0s var(--transition-fast);
}

/* ===== SECTION TYPOGRAPHY =====
   Unbounded shouts the headlines, Nunito keeps the copy friendly and
   Roboto Mono handles the readouts. Declared in shared/fonts/css/interactive.css. */
.sidebar-title,
.modal-title,
.modal-eyebrow,
.scene-help-title,
.toast-label,
.intro-label,
.scene-label-name .label-slot,
.action-btn.action-pill {
  font-family: var(--display);
  font-weight: var(--display-weight);
}

/* Unbounded is extremely wide: every headline needs a size step down and
   negative tracking, or the single-line labels ellipsis out. */
.sidebar-title { font-size: 12px; letter-spacing: -.02em; }
.scene-label-name .label-slot { font-size: 13px; letter-spacing: -.02em; }
.modal-title { font-size: 17px; letter-spacing: -.03em; }
.modal-eyebrow { font-size: 9px; letter-spacing: .1em; }
.intro-label { letter-spacing: .06em; }

/* Nunito's rounded terminals make it read a shade smaller than Inter did. */
.sidebar-subtitle,
.tag-chip { font-size: 12px; }
