*, *::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/3d.css. */
  --display: 'Exo 2', 'IBM Plex Sans', system-ui, sans-serif;
  /* Only one display weight ships — pin it so the browser never fakes a bolder cut. */
  --display-weight: 600;
  --text: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --scene-bg: #0a0d22;
  --scene-accent: #7c6bff;
  /* Per-scene menu palette — overridden by JS (applyAdaptiveTheme). */
  --menu-bg: #0a0d22;
  --menu-orb: #7c6bff;
  --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 is tinted with the current scene's bg so sidebar / action-bar /
     nav-arrows pick up the scene palette instead of staying neutral gray. */
  --surface: color-mix(in srgb, var(--scene-bg) 30%, rgba(10, 12, 20, 0.85));
  --surface-solid: color-mix(in srgb, var(--scene-bg) 20%, #111218);
  /* 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;
  /* 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);
  --sidebar-w: 296px;
  --peek: 10px;
  /* Отступ «плавающего острова» сайдбара от краёв вьюпорта. */
  --island-gap: 12px;
  --dock-h: 118px;
  --transition-fast: .18s;
  --transition-med: .28s cubic-bezier(.2, .8, .2, 1);
}

:root[data-mode="light"] {
  --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);
  --shell-bg: color-mix(in srgb, var(--menu-bg) 74%, #ffffff);
  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), background .6s,
    border-width var(--transition-med), border-color var(--transition-med),
    box-shadow var(--transition-med);
}

.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;
  pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(255,255,255,0) 0%, transparent 60%);
  transition: background .3s;
}
.stage-flash.active {
  background: radial-gradient(ellipse at center, rgba(255,255,255,0.04) 0%, transparent 60%);
}

/* ===== SCENE LABEL =====
   A header child, not an overlay: it sits on the shell surface, so it takes
   its contrast from --fg like the rest of the chrome. */
.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),
    color var(--transition-fast), border-color var(--transition-fast),
    background 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);
  color: var(--fg);
  background: var(--fg-a12);
  border-color: var(--fg-a25);
}
@media (prefers-reduced-motion: reduce) {
  .lab-home { transition: color var(--transition-fast); }
}

.scene-label-icon {
  position: relative;
  width: 36px; height: 36px; border-radius: 10px; overflow: hidden;
  background: linear-gradient(135deg, var(--scene-bg), var(--scene-accent));
  border: 1px solid var(--fg-a05);
  display: grid; place-items: center;
  color: #fff; font-size: 13px;
  box-shadow: 0 4px 18px -4px rgba(0,0,0,0.25);
  flex: 0 0 auto;
  transition: background .5s;
}
/* Icon slots: all 16 pre-rendered, stacked. Only the active one is visible. */
.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; }

/* flex-grow, not shrink-to-fit: every slot inside is absolutely positioned, so
   the column has no intrinsic width to size itself by and would collapse to 0. */
.scene-label-text { flex: 1 1 auto; min-width: 0; }

/* Text slots: all 16 pre-rendered, stacked absolutely.
   Non-visible: at -25% above, blurred, invisible. When becoming active → slides to 0.
   When losing active status (.leaving): drops to +25% below while fading out,
   then quietly resets back to rest (-25%) under opacity:0. */
.scene-label-name, .scene-label-meta { position: relative; }
.scene-label-name { min-height: 1.15em; }
.scene-label-meta { min-height: 1.3em; }

/* 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 name just gets hard-clipped mid-letter. */
.scene-label-name .label-slot,
.scene-label-meta .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;
}

.scene-label-name .label-slot { max-width: 34vw; }
.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 { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.15; }
.scene-label-meta {
  font-size: 10px; font-family: var(--mono);
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--fg-a55); margin-top: 3px;
  transition: color .4s;
}
@media screen and (max-width: 760px) {
  .scene-label-meta {
    font-size: 8px;
    margin-top: 1px;
  }
}

/* ===== NAV ARROWS — centred inside the window ===== */
.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);
  display: grid; place-items: center;
  opacity: 0.75;
  transition: all var(--transition-med);
  box-shadow: 0 16px 40px -12px rgba(0,0,0,0.3);
  z-index: 21;
}
.nav-arrow:hover:not(:disabled) {
  opacity: 1;
  background: color-mix(in srgb, var(--scene-accent) 35%, var(--surface));
  border-color: var(--fg-a25);
}
.nav-arrow:disabled { opacity: 0.25; cursor: not-allowed; }
.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-arrow:hover:not(:disabled) { transform: translateY(-50%) scale(1.04); }
/* Tiny visual offset on the chevron so it sits slightly away from the center,
   nudging toward the direction it points. */
.nav-arrow.nav-left  > svg { transform: translateX(-1px); }
.nav-arrow.nav-right > svg { transform: translateX(1px); }

.nav-tip {
  position: absolute; top: 50%; transform: translateY(-50%);
  padding: 8px 12px; border-radius: 10px;
  background: rgba(20, 22, 30, 0.92);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--fg-a12);
  color: var(--fg);
  font-size: 12px; font-weight: 500;
  white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity .18s;
  display: flex; align-items: center; gap: 8px;
  box-shadow: 0 12px 32px -10px rgba(0,0,0,0.4);
}
:root[data-mode="light"] .nav-tip { background: rgba(250, 248, 244, 0.95); }
.nav-left .nav-tip { left: calc(100% + 10px); }
.nav-right .nav-tip { right: calc(100% + 10px); }
.nav-arrow:hover:not(:disabled) .nav-tip { opacity: 1; }
:root.nav-hints-dismissed .nav-tip { opacity: 0 !important; }

.nav-tip kbd {
  font-family: var(--mono);
  font-size: 10px;
  padding: 2px 6px; border-radius: 4px;
  background: var(--fg-a12);
  border: 1px solid var(--fg-a12);
}

/* ===== HEADER BUTTONS =====
   .action-btn, .scene-help-btn and .scene-open-btn are the same control —
   one bordered square that reads as part of the header row. */
.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);
}

/* ===== LANGUAGE DROPDOWN ===== */
.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: 10px;
  width: 100%; padding: 8px 10px;
  border-radius: 8px;
  background: transparent;
  color: var(--fg);
  font-size: 13px;
  text-align: left;
  transition: background .15s;
}
.lang-option:hover { background: var(--fg-a08); }
.lang-option.active {
  background: var(--fg-a08);
  font-weight: 600;
}
.lang-option.active::after {
  content: '✓';
  margin-left: auto;
  color: var(--scene-accent);
  font-weight: 700;
}
.lang-option .flag-icon {
  width: 22px; height: 16px; border-radius: 2px; object-fit: cover;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.15);
  flex: 0 0 auto;
}

/* ===== SIDEBAR ===== */
.sidebar-hover-zone {
  position: fixed; top: 0; bottom: 0;
  right: 0;
  width: 28px;
  z-index: 19;
  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.
   Язычок — это торчащий край самой панели, а не отдельная плашка, поэтому он
   берёт её фон и её рамку, а не нейтральный --surface. */
.sidebar-handle {
  --size: 30px;
  position: fixed;
  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: fixed; top: 0; bottom: 0;
  right: calc(-1 * (var(--sidebar-w) - var(--peek)));
  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;
  z-index: 20;
  color: var(--fg);
  overflow: hidden;
  isolation: isolate;
  transition:
    top var(--transition-med), bottom var(--transition-med),
    right var(--transition-med), border-radius var(--transition-med),
    border-color var(--transition-med), box-shadow var(--transition-med),
    background .35s, backdrop-filter .35s;
}
/* Открытый (по ховеру или закреплённый) — плавающий «остров»: отступы со всех
   сторон, скругление как у окна сцены и приподнятая тень. В свёрнутом виде
   панель по-прежнему прижата к краям (углы прямые, виден только peek). */
.sidebar.open .sidebar-panel {
  top: var(--island-gap);
  bottom: var(--island-gap);
  right: var(--island-gap);
  border-color: var(--fg-a05);
  border-radius: var(--window-radius);
  box-shadow: 0 30px 70px -30px rgba(0,0,0,.7);
}
/* 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) .sidebar-panel {
  z-index: 35;
  backdrop-filter: blur(32px) saturate(1.5);
  -webkit-backdrop-filter: blur(32px) saturate(1.5);
}

/* Decorative blur orbs — soft color wash matching the current scene */
.sidebar-panel::before,
.sidebar-panel::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  filter: blur(60px);
  opacity: 0.45;
  pointer-events: none;
  z-index: -1;
  transition: background .6s ease;
}
/* Both orbs share the same color (--menu-orb) so they read as a unified glow. */
.sidebar-panel::before {
  top: -45%;
  right: -180px;
  width: 290px;
  height: 100vh;
  background: radial-gradient(circle, var(--menu-orb) 0%, transparent 50%);
  background: radial-gradient(circle, var(--menu-orb) 0%, transparent 70%);
}
.sidebar-panel::after {
  bottom: -20%;
  left: -230px;
  width: 300px;
  height: 100vh;
  background: radial-gradient(circle, var(--menu-orb) 0%, transparent 70%);
  opacity: 0.55;
}
:root[data-mode="light"] .sidebar-panel::before { opacity: 0.35; }
:root[data-mode="light"] .sidebar-panel::after  { opacity: 0.4; }

/* Ensure content sits above the orbs */
.sidebar-head, .sidebar-meta, .sidebar-list, .sidebar-footer { position: relative; z-index: 1; }

.sidebar-footer {
  padding: 10px 12px;
  border-top: 1px solid var(--fg-a05);
  flex: 0 0 auto;
  z-index: 5;
}
/* Футер без панели действий (открепившись, она уезжает в шапку) — не рисуем
   лишний разделитель (: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); }

.sidebar-head {
  padding: 20px 18px 16px;
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  border-bottom: 1px solid var(--fg-a05);
}
.sidebar-brand {
  display: flex; align-items: center; gap: 10px; min-width: 0;
}
.sidebar-logo {
  width: 30px; height: 30px; border-radius: 7px; flex: 0 0 auto;
  background: var(--fg-a08);
  border: 1px solid var(--fg-a12);
  display: grid; place-items: center;
  color: var(--fg); font-size: 14px;
  position: relative;
  overflow: hidden;
}
.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: 600; letter-spacing: -0.01em;
  color: var(--fg);
}
.sidebar-subtitle {
  font-size: 10px; font-family: var(--mono);
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--fg-a40); margin-top: 2px;
}
.sidebar-pin {
  width: 32px; height: 32px; border-radius: 8px;
  border: 1px solid var(--fg-a12);
  background: transparent;
  color: var(--fg);
  display: grid; place-items: center;
  transition: background .18s;
}
.sidebar-pin:hover { background: var(--fg-a08); }

.sidebar-pin .icon-collapse { display: none; }
.sidebar.pinned .sidebar-pin .icon-pin { display: none; }
.sidebar.pinned .sidebar-pin .icon-collapse { display: inline; }

/* Category filter — segmented control with a sliding active indicator.
   The indicator is positioned absolutely and moves between tabs on change. */
.sidebar-filter {
  margin: 14px 14px 4px;
  display: flex;
  padding: 3px;
  background: var(--fg-a05);
  border: 1px solid var(--fg-a05);
  border-radius: 10px;
  position: relative;
  z-index: 2;
}
.filter-indicator {
  position: absolute;
  top: 3px;
  left: 0;
  width: 0;
  height: calc(100% - 6px);
  background: var(--menu-orb);
  border-radius: 7px;
  box-shadow: 0 2px 8px rgba(0,0,0,.18);
  transition:
    left .32s cubic-bezier(.2,.8,.2,1),
    width .32s cubic-bezier(.2,.8,.2,1),
    background .35s;
  z-index: 0;
  pointer-events: none;
}
.filter-indicator.no-anim { transition: none; }
.sidebar-filter .filter-btn {
  flex: 1;
  padding: 7px 8px;
  font-size: 11px; font-weight: 500;
  letter-spacing: 0.04em;
  border-radius: 7px;
  color: var(--fg-a75);
  background: transparent;
  position: relative;
  z-index: 1;
  transition: color .2s;
}
.sidebar-filter .filter-btn:hover { color: var(--fg); }
.sidebar-filter .filter-btn.active {
  color: var(--menu-orb-fg);
  font-weight: 600;
}

.sidebar-meta {
  padding: 12px 20px 10px;
  font-size: 10px; font-family: var(--mono);
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--fg-a40);
  display: flex; justify-content: space-between;
}

/* Hidden by filter: removed from layout so there's no empty gap.
   Items that become visible animate in with a small fade-up. */
.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(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.sidebar-list {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: 0 12px 20px;
  display: flex; flex-direction: column; gap: 3px;
}

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

.scene-thumb {
  position: relative; flex: 0 0 auto;
  width: 82px; height: 54px;
  border-radius: 8px; overflow: hidden;
  background: var(--scene-bg);
  outline: 1px solid var(--fg-a08);
  transition: outline .2s;
}
.scene-item.active .scene-thumb {
  outline: 1.5px solid var(--fg-a55);
  outline-offset: 1px;
}
.scene-thumb img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.scene-thumb-fallback {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  color: #fff; font-size: 14px;
}
.scene-thumb-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 45%, rgba(0,0,0,0.55) 100%);
  pointer-events: none;
}
.scene-thumb[data-mode="light"] .scene-thumb-overlay {
  background: linear-gradient(180deg, transparent 45%, rgba(0,0,0,0.3) 100%);
}
.scene-thumb.intro {
  background: linear-gradient(135deg, var(--sc-bg, #0a0d22), var(--sc-accent, #7c6bff));
  display: grid; place-items: center;
  color: #fff;
}
.scene-thumb.intro .intro-label {
  text-align: center; line-height: 1.1;
  font-family: var(--mono);
  font-size: 9px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 500;
}
.scene-thumb.intro .intro-mark { font-size: 14px; margin-bottom: 2px; }

.scene-info { min-width: 0; flex: 1; }
.scene-name {
  font-size: 13px; font-weight: 500; letter-spacing: -0.005em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--fg-a75);
}
.scene-item.active .scene-name { color: var(--fg); font-weight: 600; }
.scene-num {
  font-size: 10px; font-family: var(--mono);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--fg-a40); margin-top: 3px;
}

/* ===== MOBILE DOCK (hidden on desktop) ===== */
.mobile-dock {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0;
  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;
  color: var(--fg);
}

.mobile-dock-track {
  display: flex; gap: 10px; padding: 4px 12px;
  overflow-x: auto; overflow-y: hidden;
}
.mobile-dock-track::-webkit-scrollbar { display: none; }
.mobile-dock-track { scrollbar-width: none; }

.dock-item {
  flex: 0 0 auto; width: 76px;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  color: var(--fg);
  font-family: inherit;
  background: transparent;
}
.dock-thumb {
  position: relative; width: 64px; height: 64px;
  border-radius: 10px; overflow: hidden;
  background: var(--scene-bg);
  outline: 1px solid var(--fg-a12);
  transition: outline .18s;
}
.dock-item.active .dock-thumb { outline: 2px solid var(--fg); }
.dock-thumb img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.dock-thumb.intro {
  background: linear-gradient(135deg, var(--sc-bg, #0a0d22), var(--sc-accent, #7c6bff));
  display: grid; place-items: center;
  color: #fff; font-size: 18px;
}
.dock-name {
  font-size: 10px; font-weight: 500;
  color: var(--fg-a75);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 72px; text-align: center;
}
.dock-item.active .dock-name { color: var(--fg); font-weight: 600; }

/* ===== MODAL (shortcuts) ===== */
.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 {
  width: 460px; max-width: 90vw;
  background: var(--surface-solid);
  color: var(--fg);
  border-radius: 18px;
  padding: 28px;
  border: 1px solid var(--fg-a12);
  box-shadow: 0 40px 100px -20px rgba(0,0,0,0.5);
}
.modal-head {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 20px;
}
.modal-eyebrow {
  font-size: 10px; font-family: var(--mono);
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--fg-a55); margin-bottom: 4px;
}
.modal-title {
  font-size: 22px; font-weight: 600; letter-spacing: -0.02em;
}
.modal-close {
  width: 34px; height: 34px; border-radius: 10px;
  border: 1px solid var(--fg-a12);
  color: var(--fg);
  display: grid; place-items: center;
}

.shortcut-list {
  display: flex; flex-direction: column; gap: 2px;
}
.shortcut-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 0;
  border-bottom: 1px solid var(--fg-a05);
  font-size: 14px;
}
.shortcut-row span { color: var(--fg-a75); }
.shortcut-row kbd {
  font-family: var(--mono);
  font-size: 12px;
  padding: 4px 10px; border-radius: 6px;
  background: var(--fg-a05);
  border: 1px solid var(--fg-a12);
  color: var(--fg);
}

/* ===== SCENE HELP =====
   The button is hidden unless the current scene has help tips defined.
   Clicking drops a small panel with scene-specific hints under the button. */
.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; align-items: center; justify-content: space-between;
  margin-bottom: 10px;
}
.scene-help-title {
  font-size: 10px; font-family: var(--mono);
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--fg-a55);
}
.scene-help-close {
  width: 24px; height: 24px; border-radius: 7px;
  border: 1px solid var(--fg-a08);
  background: transparent; color: var(--fg);
  display: grid; place-items: center;
  transition: background .18s;
}
.scene-help-close:hover { background: var(--fg-a08); }

.scene-help-list {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.scene-help-list li {
  position: relative;
  padding: 10px 12px 10px 34px;
  border-radius: 10px;
  background: var(--fg-a05);
  border: 1px solid var(--fg-a08);
  font-size: 13px; 
  line-height: 1.2;
  color: var(--fg);
}
.scene-help-list li::before {
  content: '💡';
  position: absolute; left: 10px; top: 50%;
  transform: translateY(-50%);
  font-size: 14px;
}

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

/* ===== КАСТОМНЫЕ ПОДСКАЗКИ =====
   Единый плавающий тултип вместо нативных title: стилизованный, локализуемый,
   не вылезает за вьюпорт. Позиционируется из JS (fixed, left/top). */
.ui-tip {
  position: fixed;
  left: 0; top: 0;
  z-index: 90;
  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); }

/* ===== SPEC FRAME ===== */
#spec-frame {
  position: absolute;
  width: 0; height: 0;
  opacity: 0; pointer-events: none;
  overflow: hidden;
}

/* ===== 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; }
}

/* 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; }

/* ===== HIDE THE INTERFACE =====
   One switch clears the chrome around the scene, because a scene's own controls
   frequently live under ours. The window goes full-bleed and only a faint toggle
   is left floating in the corner. The iframe is never touched. */
.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));
  border-color: var(--fg-a25);
}

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

/* Шапка убирается целиком — путь назад держат плавающая кнопка .ui-restore
   (строится в JS) и ромбик сайдбара. */
:root.ui-hidden .app-header { display: none; }

/* Со скрытой обвязкой сайдбар всегда работает по наведению — закреплять нечего,
   и кнопка закрепления только сбивала бы с толку. */
:root.ui-hidden .sidebar-pin { display: none; }

/* Плавающая кнопка возврата интерфейса: прижата вплотную к правому нижнему углу
   и одета в цвета сайдбара — так она читается его продолжением, а не наклейкой
   поверх сцены. Существует всегда, но видима только при скрытом интерфейсе.
   corner-shape поддержан не везде: где его нет, скос останется скруглением —
   деградация допустимая. */
.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 =====
   Exo 2 carries the headline voice, IBM Plex Sans the running copy and
   Geist Mono the readouts. Faces are declared in shared/fonts/css/3d.css. */
.sidebar-title,
.modal-title,
.modal-eyebrow,
.scene-help-title,
.toast-label,
.scene-label-name .label-slot,
.action-btn.action-pill {
  font-family: var(--display);
  font-weight: var(--display-weight);
}

/* Exo 2 is drawn narrow and tall — it carries wider tracking than Plex,
   and the modal headline needs a point back to hold its weight. */
.sidebar-title { letter-spacing: .01em; }
.modal-title { font-size: 21px; letter-spacing: .005em; }
.modal-eyebrow { letter-spacing: .16em; }
