/* AI Russian Lecture Assistant — hand-authored, no build step.
   Design grounded in an annotated bilingual manuscript, not a SaaS
   dashboard: interlinear RU/EN text, marginalia for glossary/insights,
   rule-divided lists instead of cards. See docs/DEVELOPMENT_PLAN.md. */

@import url('https://fonts.googleapis.com/css2?family=PT+Serif:ital,wght@0,400;0,700;1,400&family=Golos+Text:wght@400;500;600;700&display=swap');

:root {
  --paper: #EEEFE8;
  --paper-raised: #E5E6DD;
  --ink: #1E2320;
  --ink-ru: #1E2320;
  --ink-en: #52645D;
  --label: #6B6E64;
  --rule: #D3D4C9;
  --rule-strong: #B7B9AB;
  --accent: #2F5D4A;
  --accent-ink: #FFFFFF;
  --live: #8C3A2B;
  --live-tint: #F3E4DF;
  --amber: #8A6D2F;
  --amber-tint: #F1E9D8;

  --insight-definition: #3D6E8C;
  --insight-key-concept: #2F5D4A;
  --insight-example: #8A6D2F;
  --insight-instruction: #5B4E8C;
  --insight-exam: #8C3A2B;
  --insight-formula: #3D7A6E;
  --insight-uncertain: #767A6E;

  /* Liquid Glass materials (docs/UI_REDESIGN_RESEARCH.md Section 2/3) —
     reserved for chrome (nav, floating controls, sheets), never for
     surfaces holding transcript/glossary/summary text. Light values here;
     dark values redefined below, hand-tuned per theme, not inverted. */
  --glass-fill: rgba(255, 255, 255, 0.55);
  --glass-border: rgba(255, 255, 255, 0.6);
  --glass-shadow: 0 10px 30px rgba(30, 35, 32, 0.14);
  --scrim: rgba(20, 25, 22, 0.4);

  --serif: 'PT Serif', Georgia, 'Times New Roman', serif;
  --sans: -apple-system, BlinkMacSystemFont, 'Golos Text', 'Inter', system-ui, sans-serif;

  --gutter: 1.25rem;
  --measure: 46rem;
  --radius: 12px;
  --radius-lg: 20px;
  --spring: cubic-bezier(0.22, 1, 0.36, 1);
}

/* Dark theme — a hand-tuned pair, not an inverted filter (same tokens,
   different values), so every existing rule that already reads var(--x)
   gets a correct dark appearance with no further changes.

   Two selectors both apply these values, kept in sync by hand (no build
   step in this project — see the file banner above):
   - the media query: system-level dark, unless the Settings page's theme
     picker (docs/UI_REDESIGN_RESEARCH.md Section 3) has explicitly forced
     light via [data-theme="light"]
   - the attribute selector: explicit "Dark" chosen on the Settings page,
     regardless of the system preference
   public/js/theme.js sets [data-theme] before first paint (no-flash, same
   section) by reading the viewer's stored choice. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #15181A;
    --paper-raised: #202426;
    --ink: #F1EFE9;
    --ink-ru: #F1EFE9;
    --ink-en: #A7ADA6;
    --label: #9BA29D;
    --rule: rgba(255, 255, 255, 0.12);
    --rule-strong: rgba(255, 255, 255, 0.22);
    --accent: #74C4A4;
    --accent-ink: #0D1210;
    --live: #FF6F65;
    --live-tint: rgba(255, 111, 101, 0.16);
    --amber: #FFB13D;
    --amber-tint: rgba(255, 177, 61, 0.16);

    --insight-definition: #6FA8CB;
    --insight-key-concept: #74C4A4;
    --insight-example: #D1A652;
    --insight-instruction: #9C8FD1;
    --insight-exam: #FF6F65;
    --insight-formula: #6FD1BE;
    --insight-uncertain: #A7ADA6;

    --glass-fill: rgba(40, 44, 42, 0.55);
    --glass-border: rgba(255, 255, 255, 0.1);
    --glass-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
    --scrim: rgba(0, 0, 0, 0.55);
  }
}
:root[data-theme="dark"] {
  --paper: #15181A;
  --paper-raised: #202426;
  --ink: #F1EFE9;
  --ink-ru: #F1EFE9;
  --ink-en: #A7ADA6;
  --label: #9BA29D;
  --rule: rgba(255, 255, 255, 0.12);
  --rule-strong: rgba(255, 255, 255, 0.22);
  --accent: #74C4A4;
  --accent-ink: #0D1210;
  --live: #FF6F65;
  --live-tint: rgba(255, 111, 101, 0.16);
  --amber: #FFB13D;
  --amber-tint: rgba(255, 177, 61, 0.16);

  --insight-definition: #6FA8CB;
  --insight-key-concept: #74C4A4;
  --insight-example: #D1A652;
  --insight-instruction: #9C8FD1;
  --insight-exam: #FF6F65;
  --insight-formula: #6FD1BE;
  --insight-uncertain: #A7ADA6;

  --glass-fill: rgba(40, 44, 42, 0.55);
  --glass-border: rgba(255, 255, 255, 0.1);
  --glass-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
  --scrim: rgba(0, 0, 0, 0.55);
}

/* Runtime theme switch (Settings page) transitions smoothly instead of
   snapping — public/js/theme.js adds this class for ~250ms around the
   change only, never during first paint (that's what the no-flash inline
   script in <head> is for). */
.theme-transition,
.theme-transition * {
  transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease, box-shadow 0.25s ease !important;
}

*, *::before, *::after { box-sizing: border-box; }

html, body { background: var(--paper); }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 15px;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}
/* .masthead is position:fixed (floating bar, below) — completely out of
   flow, so this reserves the room it would otherwise have occupied.
   :has() scopes it to pages that actually render one — the guest layout
   (login/register/forgot/reset) has no masthead and needs none of this.
   Recalibrated down after the masthead became a single row (was 7rem/
   5rem, sized for the old two-row bar) — it's noticeably shorter now, so
   this reserves less, giving that space back to actual content. */
body:has(.masthead) { padding-top: 5.5rem; }
@media (max-width: 56rem) {
  body:has(.masthead) { padding-top: 4.5rem; }
}

a { color: inherit; text-decoration: none; }
a[href]:hover { color: var(--accent); }
img { max-width: 100%; }

::selection { background: var(--accent); color: var(--accent-ink); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

@keyframes lecture-capture-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ---------------------------------------------------------------- */
/* Liquid Glass — chrome only: nav, floating controls, sheets.       */
/* Never applied to a surface holding transcript/glossary/summary    */
/* text (docs/UI_REDESIGN_RESEARCH.md Section 0/2) — those stay on   */
/* solid --paper-raised so dense reading content stays fully legible.*/
/* ---------------------------------------------------------------- */

.glass {
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
  position: relative;
}
.glass::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0) 45%);
  opacity: 0.5;
  pointer-events: none;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .glass::before {
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0) 45%);
  }
}
:root[data-theme="dark"] .glass::before {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0) 45%);
}
/* Reduce Transparency fallback — every glass surface keeps the same
   information hierarchy as a flat, static surface, never just disabled. */
@media (prefers-reduced-transparency: reduce) {
  .glass {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--paper-raised);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
    border-color: var(--rule-strong);
  }
  .glass::before { display: none; }
}

/* ---------------------------------------------------------------- */
/* Masthead / navigation                                            */
/* ---------------------------------------------------------------- */

/* A genuinely floating, detached bar — not a full-width strip merely
   pinned to the top (that was the earlier attempt; per direct feedback
   it still read as "the same" bar regardless of how much its padding
   shrank, because the floating/detached *shape* was the actual ask, not
   just less height). Same standard as the mobile dock/live-capture
   island: margin on all sides, rounded on every corner, its own glass
   surface — just anchored to the top instead of the bottom.
   body's padding-top (below) reserves room for it, since position:fixed
   removes it from normal flow entirely. */
.masthead {
  position: fixed;
  top: 1rem;
  left: 1rem;
  right: 1rem;
  max-width: 56rem;
  margin: 0 auto;
  z-index: 50;
  padding: 0 var(--gutter);
  border-radius: var(--radius-lg);
  transition: max-width 0.2s ease;
}

/* Single row — wordmark, tabs, and account chrome all in line. A 3-track
   grid (not flex) so the tabs sit truly centered regardless of how the
   wordmark and account-chrome widths compare, rather than fighting with
   justify-content over unequal side content. */
.masthead-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
  padding: 0.55rem 0;
  transition: padding 0.2s ease;
}
.masthead-row .wordmark { justify-self: start; }
.masthead-row .tabs { justify-self: center; }
.masthead-row .masthead-meta { justify-self: end; }

/* Shrinks further once scrolled, on every axis — public/js/
   masthead-scroll.js toggles this past a small threshold, reclaiming a
   little more of the screen for actual content everywhere, desktop
   included (where there's no dock/island already doing that job).
   Expanded (default): full wordmark, full text tab labels — the normal,
   spacious reading. Compact (scrolled): wordmark shrinks to a short
   mark (same idiom mobile already uses), and tabs collapse to icon-only
   — except the active tab, which keeps its label so "where am I" stays
   answered even in the compact state; hovering or focusing any other
   tab reveals its label too, same mechanic as the mobile dock. Desktop
   still has no fallback nav the way mobile's dock is one, so nothing
   here ever actually disappears, only relabels. */
.masthead.is-compact { max-width: 44rem; }
.masthead.is-compact .masthead-row { padding: 0.3rem 0; }
.masthead.is-compact .wordmark { font-size: 0.9rem; }
.masthead.is-compact .wordmark em { display: none; }

.masthead.is-compact .tabs a { padding: 0.4rem; gap: 0; }
.masthead.is-compact .tabs .tab-icon { display: flex; }
.masthead.is-compact .tabs .tab-label {
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-width 0.2s var(--spring), opacity 0.15s ease, margin-left 0.2s var(--spring);
}
.masthead.is-compact .tabs a.active .tab-label,
.masthead.is-compact .tabs a:hover .tab-label,
.masthead.is-compact .tabs a:focus-visible .tab-label {
  max-width: 6rem;
  opacity: 1;
  margin-left: 0.4rem;
}

/* Mobile already has a bottom nav dock (.tabs-dock) and, on the live
   screen, its own control island — a second full top bar is redundant
   chrome on top of that, per direct feedback. Keeps only what earns its
   permanent place (identity + theme + account): the wordmark shrinks to
   a short mark, the theme picker and avatar stay, and Log out moves to
   the Settings page (reachable via the avatar) instead of sitting in
   always-visible chrome. */
@media (max-width: 56rem) {
  .masthead { max-width: 20rem; padding: 0 0.85rem; }
  /* .tabs is display:none at this width (it's .tabs-dock's job here
     instead) — with only 2 of the row's 3 grid items left, the 3-column
     1fr/auto/1fr track from the single-row desktop layout auto-placed
     both remaining items into the first two tracks, stranding the third
     (empty) one instead of pushing the account chrome to the true right
     edge. A plain 2-item flex row doesn't have that assumption. */
  .masthead-row { display: flex; justify-content: space-between; padding: 0.5rem 0; gap: 0.5rem; }
  .wordmark { font-size: 0.85rem; }
  .wordmark em { display: none; }
  .masthead-logout { display: none; }
}

.wordmark {
  font-family: var(--serif);
  font-size: 1.1rem;
  font-weight: 700;
  white-space: nowrap;
  transition: font-size 0.2s ease;
}
.wordmark em { font-style: italic; color: var(--accent); }
.wordmark a:hover { color: inherit; }

.masthead-meta {
  font-size: 0.8rem;
  color: var(--label);
  display: flex;
  align-items: center;
  gap: 1.1rem;
  white-space: nowrap;
}

.rec-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--label);
  display: inline-block;
  margin-right: 0.4rem;
  vertical-align: middle;
}
.rec-dot--live {
  background: var(--live);
  animation: lecture-capture-pulse 1.5s ease-in-out infinite;
}

.masthead-avatar {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.masthead-meta .masthead-avatar:hover { color: var(--accent-ink); opacity: 0.88; }

.masthead-icon-btn {
  all: unset;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  color: var(--label);
  cursor: pointer;
}
.masthead-icon-btn:hover { color: var(--ink); background: var(--paper); }

.tabs {
  /* Positioned by .masthead-row .tabs's justify-self: center now that
     this sits inline with the wordmark and account chrome, not centering
     itself within a full-width row of its own. */
  display: flex;
  justify-content: center;
  gap: 1.5rem;
  overflow-x: auto;
}
.tabs a {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--label);
  padding: 0.5rem 0.05rem 0.6rem;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: padding 0.2s ease;
}
.tabs a.active,
.tabs a:hover { color: var(--ink); border-bottom-color: var(--accent); }
.tabs .tab-icon { display: none; }

/* Mobile: .tabs (desktop's top row, nested inside the masthead) hides
   entirely, replaced by .tabs-dock — a floating bottom dock, same visual
   standard as the live-capture island (icon-only, label reveals on
   hover/focus/press), so navigation scales by adding a dock icon rather
   than crowding a row of text tabs as the app grows past three or four
   destinations. .tabs-dock is a SEPARATE element rendered outside
   .masthead on purpose: backdrop-filter on an ancestor (the masthead's
   own .glass) creates a new containing block for position:fixed
   descendants, the same way transform does — a dock nested inside it
   ends up fixed relative to the masthead box, not the viewport, and
   renders pinned near the top instead of the bottom. */
@media (max-width: 56rem) {
  .tabs { display: none; }

  .tabs-dock {
    position: fixed;
    left: 50%;
    bottom: 0.6rem;
    transform: translateX(-50%);
    z-index: 45;
    display: flex;
    gap: 0.2rem;
    padding: 0.35rem;
    border-radius: 999px;
    background: var(--glass-fill);
    -webkit-backdrop-filter: blur(22px) saturate(160%);
    backdrop-filter: blur(22px) saturate(160%);
    border: 1px solid var(--glass-border);
    box-shadow: var(--glass-shadow);
  }
  .tabs-dock a {
    display: flex;
    align-items: center;
    padding: 0.7rem;
    border-radius: 999px;
    color: var(--label);
    transition: background-color 0.15s ease;
  }
  .tabs-dock a.active,
  .tabs-dock a:hover,
  .tabs-dock a:focus-visible { background: var(--paper); color: var(--ink); }

  .tabs-dock .tab-icon { display: flex; flex-shrink: 0; }
  .tabs-dock .tab-label {
    display: inline-block;
    max-width: 0;
    opacity: 0;
    overflow: hidden;
    font-family: var(--sans);
    font-size: 0.85rem;
    font-weight: 500;
    white-space: nowrap;
    transition: max-width 0.25s var(--spring), opacity 0.2s ease, margin-left 0.25s var(--spring);
  }
  .tabs-dock a:hover .tab-label,
  .tabs-dock a:focus-visible .tab-label,
  .tabs-dock a:active .tab-label,
  .tabs-dock a.active .tab-label {
    max-width: 6rem;
    opacity: 1;
    margin-left: 0.5rem;
  }
}
@media (min-width: 56.01rem) {
  .tabs-dock { display: none; }
}
@media (max-width: 56rem) and (prefers-reduced-transparency: reduce) {
  .tabs-dock {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--paper-raised);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
    border-color: var(--rule-strong);
  }
}

/* ---------------------------------------------------------------- */
/* Page frame + shared type                                         */
/* ---------------------------------------------------------------- */

.frame {
  max-width: 78rem;
  margin: 0 auto;
  /* Top padding is small on purpose — body's own padding-top (above)
     already reserves the room the floating masthead needs; this is just
     the extra breathing gap between it and the page title. */
  padding: 1rem var(--gutter) 4rem;
}
.frame--narrow { max-width: 30rem; }
.frame--reading { max-width: 54rem; }

/* A single-purpose form page (start a lecture, settings, add a course) —
   centers the heading and form together as one narrow, intentional column
   instead of leaving it pinned to the left edge of the full-width .frame
   with acres of dead space on large screens. */
.form-panel { max-width: 30rem; margin: 0 auto; }

.page-kicker {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--label);
  margin: 0 0 0.5rem;
}
.page-title {
  font-family: var(--serif);
  font-weight: 700;
  font-size: 1.6rem;
  margin: 0 0 0.35rem;
  text-wrap: balance;
}
.lede { color: var(--label); font-size: 0.95rem; margin: 0 0 2rem; max-width: 42rem; }

.page-actions {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 1.25rem 0 0.5rem;
}

.text-muted { color: var(--label); }

/* ---------------------------------------------------------------- */
/* Forms                                                             */
/* ---------------------------------------------------------------- */

.field { margin-bottom: 1.35rem; }
.field label,
label.field-label {
  display: block;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--label);
  margin-bottom: 0.4rem;
}
.field input,
.field select,
.field textarea,
input.underline,
select.underline {
  width: 100%;
  border: none;
  border-bottom: 1px solid var(--rule-strong);
  background: transparent;
  font-family: var(--sans);
  font-size: 1rem;
  color: var(--ink);
  padding: 0.45rem 0.1rem;
  outline: none;
  border-radius: 0;
}
.field input:focus,
.field select:focus,
.field textarea:focus { border-bottom-color: var(--accent); }
.field input:disabled { color: var(--label); cursor: not-allowed; }
.field-hint { font-size: 0.78rem; color: var(--label); margin-top: 0.3rem; }

/* Password show/hide toggle — injected at runtime by password-toggle.js
   around every input[type=password], so no view markup needs to know
   about it (see public/js/password-toggle.js). */
.field-input-wrap { position: relative; }
.field-input-wrap input { padding-right: 2.1rem; }
.field-toggle-visibility {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  background: none;
  border: none;
  padding: 0 0.15rem;
  margin: 0;
  color: var(--label);
  cursor: pointer;
}
.field-toggle-visibility:hover { color: var(--ink); }

/* Searchable combobox — progressively enhances a plain <select
   data-combobox> at runtime (public/js/combobox.js); the select keeps
   working with no JS. Panel is glass per Section 2's contextual-menu
   guidance (not the flat/solid rule the danger-zone confirm sheet uses,
   since choosing a language isn't a consequential action). */
.combobox { position: relative; }
.combobox-input {
  width: 100%;
  border: none;
  border-bottom: 1px solid var(--rule-strong);
  background: transparent;
  font-family: var(--sans);
  font-size: 1rem;
  color: var(--ink);
  padding: 0.45rem 1.5rem 0.45rem 0.1rem;
  outline: none;
  border-radius: 0;
  cursor: pointer;
}
.combobox-input:focus { border-bottom-color: var(--accent); }
.combobox-chevron {
  position: absolute;
  right: 0.1rem;
  top: 0.55rem;
  color: var(--label);
  pointer-events: none;
}
.combobox-panel {
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
  top: calc(100% + 0.4rem);
  border-radius: var(--radius);
  max-height: 15rem;
  overflow-y: auto;
  padding: 0.4rem;
}
.combobox-panel[hidden] { display: none; }
.combobox-option {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: calc(var(--radius) - 4px);
  padding: 0.55rem 0.7rem;
  font-family: var(--sans);
  font-size: 0.9rem;
  color: var(--ink);
  cursor: pointer;
}
.combobox-option-native { font-family: var(--serif); }
.combobox-option-en { font-size: 0.78rem; color: var(--label); flex-shrink: 0; }
.combobox-option:hover,
.combobox-option.is-active { background: var(--paper-raised); }
.combobox-option[aria-selected="true"] .combobox-option-native { color: var(--accent); font-weight: 700; }
.combobox-empty { padding: 0.6rem 0.7rem; font-size: 0.82rem; color: var(--label); margin: 0; }

.btn {
  display: inline-block;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 0.85rem;
  padding: 0.7rem 1.3rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  text-align: center;
  transition: transform 0.15s var(--spring), opacity 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
}
.btn:active { transform: scale(0.97); }
.btn--primary { background: var(--accent); color: var(--accent-ink); }
/* .btn.btn--primary (two classes), not .btn--primary alone — the base
   a[href]:hover rule above is `a` + `[href]` + `:hover`, which out-
   specifies a single-class hover rule and was winning on any <a
   class="btn btn--primary">, repainting the label the same green as the
   button's own background (i.e. invisible on hover). Same fix applied to
   the other two variants below. */
.btn.btn--primary:hover { color: var(--accent-ink); opacity: 0.92; }
.btn--ghost { background: transparent; border-color: var(--rule-strong); color: var(--ink); }
.btn.btn--ghost:hover { color: var(--ink); border-color: var(--ink); }
.btn--danger { background: transparent; border-color: var(--live); color: var(--live); }
.btn.btn--danger:hover { background: var(--live-tint); color: var(--live); }
.btn--block { width: 100%; }
.btn--text {
  all: unset;
  cursor: pointer;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--accent);
}

/* Segmented control (Automatic/Light/Dark theme picker today; the RU/EN
   live-focus toggle and similar tri-state choices can reuse this later —
   docs/UI_REDESIGN_RESEARCH.md Section 2). */
.segmented {
  display: inline-flex;
  gap: 0.2rem;
  padding: 0.25rem;
  border-radius: 999px;
  background: var(--paper-raised);
}
.segmented-option {
  border: none;
  background: none;
  padding: 0.5rem 1.1rem;
  border-radius: 999px;
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--label);
  cursor: pointer;
  transition: background-color 0.15s var(--spring), color 0.15s ease;
}
.segmented-option.is-active {
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}

/* Compact icon-only variant — masthead quick theme toggle, where there's
   no room for the Settings page's labelled version. */
.segmented--compact { padding: 0.2rem; gap: 0.1rem; }
.segmented--compact .segmented-option {
  padding: 0.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.form-errors {
  border-left: 3px solid var(--live);
  padding: 0.6rem 0.9rem;
  margin-bottom: 1.5rem;
  background: var(--live-tint);
  color: var(--live);
  font-size: 0.85rem;
}
.form-errors div + div { margin-top: 0.25rem; }

.flash-message {
  padding: 0.7rem 0.9rem;
  margin-bottom: 1.5rem;
  border-left: 3px solid var(--accent);
  background: var(--paper-raised);
  font-size: 0.88rem;
}
.flash-message.error {
  border-left-color: var(--live);
  background: var(--live-tint);
  color: var(--live);
}

/* ---------------------------------------------------------------- */
/* Entry screen (login / register / forgot / reset)                 */
/* ---------------------------------------------------------------- */

body.entry-backdrop {
  min-height: 100vh;
  background:
    radial-gradient(60% 50% at 50% -8%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 70%),
    var(--paper);
}

.entry {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.25rem;
}
.entry-box {
  width: 100%;
  max-width: 24rem;
  text-align: center;
  padding: 2.5rem 2rem;
  border-radius: var(--radius-lg);
}
.entry-mark {
  font-family: var(--serif);
  font-size: 1.8rem;
  font-weight: 700;
  margin: 0 0 0.15rem;
}
.entry-mark em { font-style: italic; color: var(--accent); }
.entry-sub { font-size: 0.85rem; color: var(--label); margin: 0 0 2.25rem; }
.entry-form { text-align: left; }
.entry-links {
  margin-top: 1.5rem;
  font-size: 0.8rem;
  color: var(--label);
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

/* ---------------------------------------------------------------- */
/* List toolbar — search + filters, glass (chrome), atop a solid list */
/* (docs/UI_REDESIGN_RESEARCH.md Section 2's list-pattern requirement) */
/* ---------------------------------------------------------------- */

.list-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-bottom: 1.75rem;
}
.search-field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 1rem;
  border-radius: 999px;
  flex: 1 1 14rem;
}
.search-field svg { color: var(--label); flex-shrink: 0; }
.search-field input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  font-family: var(--sans);
  font-size: 0.9rem;
  color: var(--ink);
  outline: none;
}
.filter-chip {
  padding: 0.55rem 1.1rem;
  border-radius: 999px;
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--ink);
  border: none;
  outline: none;
  cursor: pointer;
}

/* ---------------------------------------------------------------- */
/* Catalog rows (lectures list, roster, course list)                 */
/* ---------------------------------------------------------------- */

.catalog { border-top: 1px solid var(--rule-strong); }
.catalog-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.75rem 1.25rem;
  align-items: baseline;
  padding: 1.1rem 0;
  border-bottom: 1px solid var(--rule);
}
.catalog-row:hover .catalog-title { color: var(--accent); }
.catalog-title { font-family: var(--serif); font-size: 1.1rem; display: block; }
.catalog-sub { font-size: 0.8rem; color: var(--label); margin-top: 0.25rem; }
.catalog-status { font-size: 0.75rem; white-space: nowrap; text-align: right; }
.status-ended { color: var(--label); }
.status-live { color: var(--live); font-weight: 600; }
.status-paused { color: var(--insight-example); font-weight: 600; }
.status-created { color: var(--label); }

.empty-state {
  padding: 3rem 0;
  text-align: center;
  color: var(--label);
  border-top: 1px solid var(--rule-strong);
}

@media (max-width: 34rem) {
  .catalog-row { grid-template-columns: 1fr; }
  .catalog-status { text-align: left; }
}

/* Roster (admin users) — same rule-divided language as catalog, plus a role label. */
.roster { border-top: 1px solid var(--rule-strong); }
.roster-row {
  display: grid;
  grid-template-columns: 1.4fr 1.6fr auto;
  gap: 1rem;
  align-items: baseline;
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.9rem;
}
.roster-head {
  display: grid;
  grid-template-columns: 1.4fr 1.6fr auto;
  gap: 1rem;
  padding-bottom: 0.6rem;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--label);
  border-bottom: 1px solid var(--rule-strong);
}
.roster-name { font-family: var(--serif); }
.roster-email { color: var(--label); }
.role-tag {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--label);
  white-space: nowrap;
  justify-self: end;
}
.role-tag--admin { color: var(--accent); font-weight: 600; }
@media (max-width: 40rem) {
  .roster-row, .roster-head { grid-template-columns: 1fr; gap: 0.2rem; }
  .roster-head { display: none; }
  .roster-row { padding: 1rem 0; }
  .role-tag { justify-self: start; margin-top: 0.15rem; }
}

/* ---------------------------------------------------------------- */
/* Admin section — sub-nav, provider settings, usage, languages     */
/* ---------------------------------------------------------------- */

.admin-subnav { margin: 0.25rem 0 2rem; }
.admin-subnav.segmented .segmented-option { text-decoration: none; }

/* Roster variants: users gets a 4th (role+actions) column, languages a
   4-column status/actions layout — same rule-divided list language as the
   base .roster, just a different column count per admin list. */
.roster--admin .roster-row,
.roster--admin .roster-head { grid-template-columns: 1.1fr 1.5fr 9rem auto; }
.roster--languages .roster-row,
.roster--languages .roster-head { grid-template-columns: 1.4fr 1.4fr 6rem auto; }
.roster-role-form { display: flex; align-items: center; gap: 0.5rem; }
.roster-role-form select { font-size: 0.82rem; padding: 0.3rem 0; }
.roster-actions { display: flex; gap: 0.9rem; justify-self: end; align-items: center; }
.btn--text-danger { color: var(--live); }
@media (max-width: 40rem) {
  .roster--admin .roster-row, .roster--admin .roster-head,
  .roster--languages .roster-row, .roster--languages .roster-head { grid-template-columns: 1fr; }
  .roster-actions { justify-self: start; margin-top: 0.3rem; }
}

/* Provider health badge — deliberately its own class rather than reusing
   .status-pill (that one carries a page-level margin meant for a single
   pill under an h1, wrong for an inline badge inside a list row). */
.health-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--label);
  white-space: nowrap;
}
.health-badge .dot { width: 0.5rem; height: 0.5rem; border-radius: 999px; background: var(--label); display: block; flex-shrink: 0; }
.health-badge[data-health="ok"] { color: var(--accent); }
.health-badge[data-health="ok"] .dot { background: var(--accent); }
.health-badge[data-health="cooling"] { color: var(--amber); }
.health-badge[data-health="cooling"] .dot { background: var(--amber); }

.provider-category { margin-bottom: 2.5rem; }
.provider-category h2 { font-family: var(--serif); font-size: 1.15rem; margin: 0 0 0.75rem; }
.provider-list { display: flex; flex-direction: column; gap: 0.6rem; margin-bottom: 1rem; }
.provider-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-size: 0.88rem;
  cursor: pointer;
}
.provider-option:has(input:checked) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, transparent); }
.provider-option.is-unavailable { opacity: 0.55; cursor: not-allowed; }
.provider-option-main { display: flex; align-items: center; gap: 0.6rem; }
.provider-option input[type="radio"] { accent-color: var(--accent); }

/* Append-only event lists (failover log, usage log) — same rule-divided
   grammar as .roster, column count set per list via the modifier class. */
.log-list { border-top: 1px solid var(--rule-strong); font-size: 0.85rem; }
.log-row, .log-head { display: grid; gap: 1rem; align-items: baseline; }
.log-list--failover .log-row, .log-list--failover .log-head { grid-template-columns: 11rem 6rem 8rem 8rem 1fr; }
.log-list--usage .log-row, .log-list--usage .log-head { grid-template-columns: 11rem 8rem 7rem 8rem 5rem 6rem; }
.log-row { padding: 0.6rem 0; border-bottom: 1px solid var(--rule); }
.log-head { padding-bottom: 0.5rem; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--label); border-bottom: 1px solid var(--rule-strong); }
.log-outcome--fallback_used { color: var(--amber); }
.log-outcome--attempt_failed, .log-outcome--exhausted { color: var(--live); }
@media (max-width: 48rem) {
  .log-row, .log-head { grid-template-columns: 1fr !important; gap: 0.2rem; }
  .log-head { display: none; }
  .log-row { padding: 0.85rem 0; }
}

.usage-bars { margin-bottom: 1rem; }
.usage-bar-row { display: grid; grid-template-columns: 11rem 1fr 6.5rem; gap: 0.75rem; align-items: center; padding: 0.4rem 0; font-size: 0.85rem; }
.usage-bar-track { height: 0.5rem; border-radius: 999px; background: var(--paper-raised); overflow: hidden; }
.usage-bar-fill { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.usage-bar-cost { text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 40rem) {
  .usage-bar-row { grid-template-columns: 1fr; gap: 0.3rem; }
  .usage-bar-cost { text-align: left; }
}

.coverage-table { font-size: 0.82rem; border-top: 1px solid var(--rule-strong); }
.coverage-row { display: grid; grid-template-columns: 10rem repeat(auto-fit, minmax(4rem, 1fr)); gap: 0.5rem; padding: 0.5rem 0; border-bottom: 1px solid var(--rule); align-items: center; }
.coverage-row--head { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--label); border-bottom: 1px solid var(--rule-strong); }
.coverage-cell { text-align: center; color: var(--accent); font-weight: 700; }
.coverage-cell--unverified { color: var(--label); font-weight: 400; }
.coverage-cell--none { color: var(--rule-strong); }

/* ---------------------------------------------------------------- */
/* Live capture: manuscript column + marginalia                     */
/* ---------------------------------------------------------------- */

/* Capture status pill — floating glass chrome (docs/UI_REDESIGN_RESEARCH.md
   Section 4.4b). Color/animation are driven entirely by the data-capture
   attribute, never inline JS styles, so dark mode and Reduce Transparency
   both apply automatically — see lecture-capture.js's setStatus(). */
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem 0.5rem 0.8rem;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 1.75rem;
}
.status-pill .dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 999px;
  background: var(--label);
  display: block;
  flex-shrink: 0;
}
.status-pill[data-capture="live"] .dot {
  background: var(--live);
  animation: lecture-capture-pulse 1.5s ease-in-out infinite;
}
.status-pill[data-capture="pending"] .dot { background: var(--amber); animation: lecture-capture-pulse 1.5s ease-in-out infinite; }
.status-pill[data-capture="stopped"] .dot { background: var(--label); }

/* Mobile-only floating control island, styled after iOS's Dynamic Island —
   icon-only Pause/Stop that reclaims the vertical space the in-flow
   .page-actions bar would otherwise cost (hidden at this breakpoint via
   .page-actions--live below), so the live-capture accordion panels get
   more of the screen. Each label is hidden by default and reveals on
   hover, keyboard focus, or press — not a permanent label, since that
   would cost back the space this exists to save; the icons alone are
   unambiguous for exactly two actions.
   Anchored to the top, not the bottom — per direct feedback, controls
   for something you're actively monitoring belong near the "Capturing…"
   status they act on, not a thumb-reach away at the opposite edge of the
   screen. Sits top-right, clear of the masthead and opposite the
   in-flow status pill below the page title, rather than stacked over it. */
@media (max-width: 56rem) {
  .page-actions--live { display: none; }

  .live-island {
    position: fixed;
    top: 4.7rem;
    right: 0.75rem;
    z-index: 46;
    display: flex;
    gap: 0.3rem;
    padding: 0.35rem;
    border-radius: 999px;
  }
  .live-island form { margin: 0; }
  .live-island-btn {
    all: unset;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    padding: 0.7rem;
    border-radius: 999px;
    cursor: pointer;
    color: var(--ink);
    transition: background-color 0.15s ease, padding-right 0.2s var(--spring);
  }
  .live-island-btn--danger { color: var(--live); }
  .live-island-btn svg { flex-shrink: 0; }
  .live-island-label {
    display: inline-block;
    max-width: 0;
    opacity: 0;
    white-space: nowrap;
    font-family: var(--sans);
    font-size: 0.82rem;
    font-weight: 600;
    overflow: hidden;
    transition: max-width 0.25s var(--spring), opacity 0.2s ease, margin-left 0.25s var(--spring);
  }
  .live-island-btn:hover,
  .live-island-btn:focus-visible,
  .live-island-btn:active {
    background: var(--paper);
  }
  .live-island-btn:hover .live-island-label,
  .live-island-btn:focus-visible .live-island-label,
  .live-island-btn:active .live-island-label {
    max-width: 5rem;
    opacity: 1;
    margin-left: 0.5rem;
  }
}
@media (min-width: 56.01rem) {
  .live-island { display: none; }
}

.manuscript-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 18rem;
  gap: 3rem;
  align-items: start;
}

.transcript-streams {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
}
.transcript-streams .stream-panel {
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
}

.ru-line { font-family: var(--serif); font-size: 1.1rem; line-height: 1.6; color: var(--ink-ru); }
.ru-line-partial { color: var(--label); }
.ru-line-partial::after {
  content: '';
  display: inline-block;
  width: 0.4em;
  height: 1em;
  margin-left: 0.15em;
  background: var(--live);
  vertical-align: text-bottom;
  animation: lecture-capture-pulse 1s steps(2) infinite;
}

.margin-col { position: sticky; top: 6rem; }
.margin-section + .margin-section { margin-top: 2rem; }

/* Section toggle — shared by all three live-capture panels (transcript,
   translation, glossary/insights/summaries). Desktop (here): an inert
   static heading, chevron hidden — every panel's body is always shown in
   full, same as before this became a shared component. The mobile
   collapsible-pill behavior is further below. */
.stream-panel-toggle {
  all: unset;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  margin-bottom: 0.9rem;
  pointer-events: none;
}
.stream-panel-label {
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--label);
}
.stream-panel-chevron { display: none; flex-shrink: 0; color: var(--label); }
#drawer-toggle { display: none; }

/* Compact/mobile: all three panels become collapsible pills instead of
   permanently-expanded blocks the reader has to scroll past — tap one to
   focus on exactly it (docs/UI_REDESIGN_RESEARCH.md — mobile live-capture
   redesign; public/js/live-panels.js drives the open/close state, one
   panel open at a time). The open panel gets ~65% of the viewport with
   an internal scroll both the reader (manual) and newly streamed content
   (auto-follow-if-already-near-bottom, public/js/scroll-follow.js) can move. */
@media (max-width: 56rem) {
  .manuscript-layout { grid-template-columns: 1fr; gap: 0.9rem; }
  .transcript-streams { display: flex; flex-direction: column; gap: 0.9rem; }

  .stream-panel {
    padding: 0;
    overflow: hidden;
    background: var(--paper-raised);
    border: 1px solid var(--rule);
    border-radius: 999px;
    transition: border-radius 0.3s var(--spring);
  }
  .stream-panel.is-open { border-radius: var(--radius-lg); }

  .stream-panel-toggle {
    pointer-events: auto;
    cursor: pointer;
    margin-bottom: 0;
    padding: 0.95rem 1.1rem;
  }
  .stream-panel-chevron { display: block; transition: transform 0.25s var(--spring); }
  .stream-panel.is-open .stream-panel-chevron { transform: rotate(180deg); }

  .stream-panel-body {
    max-height: 0;
    opacity: 0;
    overflow-y: hidden;
    padding: 0 1.1rem;
    transition: max-height 0.3s var(--spring), opacity 0.2s ease, padding 0.3s var(--spring);
  }
  .stream-panel.is-open .stream-panel-body {
    max-height: min(65vh, 40rem);
    opacity: 1;
    overflow-y: auto;
    padding: 0 1.1rem 1.1rem;
  }

  .margin-col { position: static; }
  #drawer-toggle { display: flex; }
}
.margin-heading {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--label);
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 0.4rem;
  margin-bottom: 0.7rem;
}
.margin-note { font-size: 0.83rem; line-height: 1.5; color: var(--label); margin: 0; }
.margin-list { list-style: none; margin: 0; padding: 0; }
.margin-list li { padding: 0.45rem 0; border-bottom: 1px solid var(--rule); font-size: 0.83rem; line-height: 1.5; color: var(--label); }
.margin-list li:last-child { border-bottom: none; }

.margin-list .term-ru { font-family: var(--serif); font-weight: 700; font-style: normal; color: var(--ink-ru); }
.margin-list .term-en { font-style: italic; }

/* Insight type color-coding — same small-caps-tag idiom as .role-tag--admin:
   only the tag takes the type color, the body stays muted so a dense list
   of hints still reads as one column, not a wall of colored text. */
.insight-tag {
  display: inline-block;
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--label);
  margin-right: 0.5rem;
}
.insight--definition .insight-tag { color: var(--insight-definition); }
.insight--key_concept .insight-tag { color: var(--insight-key-concept); }
.insight--example .insight-tag { color: var(--insight-example); }
.insight--instruction .insight-tag { color: var(--insight-instruction); }
.insight--exam_relevant .insight-tag { color: var(--insight-exam); }
.insight--formula_technical .insight-tag { color: var(--insight-formula); }
.insight--question_uncertainty .insight-tag { color: var(--insight-uncertain); }

.disclaimer { font-size: 0.72rem; color: var(--label); font-style: italic; margin: 0.6rem 0 0; }

/* Dynamic containers JS appends plain <p>/<li> into (public/js/lecture-capture.js). */
#live-transcript-final p { font-family: var(--serif); font-size: 1.1rem; line-height: 1.6; color: var(--ink-ru); margin: 0 0 0.85rem; }
#live-translation p { font-family: var(--serif); font-style: italic; font-size: 0.98rem; line-height: 1.6; color: var(--ink-en); margin: 0 0 0.85rem; }
/* Pending-translation placeholder (public/js/lecture-capture.js) — a
   subtle pulse signals "working," not "stuck", while the classify/
   translate call (a real several-second round trip server-side) is in
   flight. Falls back to a flat, non-pulsing "unavailable" state if
   nothing arrives within a reasonable window. */
#live-translation p.translation-pending { color: var(--label); animation: lecture-capture-pulse 1.6s ease-in-out infinite; }
#live-translation p.translation-pending--stale { animation: none; opacity: 0.7; }
/* Same pending-placeholder treatment, reused on the ended-lecture page while
   the final summary generates in the background (LectureController::applyTransition()). */
#summaries-page p.translation-pending { color: var(--label); animation: lecture-capture-pulse 1.6s ease-in-out infinite; }
#live-summaries p { font-family: var(--serif); font-size: 0.95rem; line-height: 1.6; margin: 0 0 0.85rem; }
#live-summaries p strong { font-family: var(--sans); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--label); display: block; margin-bottom: 0.2rem; }

/* ---------------------------------------------------------------- */
/* Notes page (post-lecture)                                        */
/* ---------------------------------------------------------------- */

.notes-page { font-family: var(--serif); font-size: 1.02rem; line-height: 1.75; color: var(--ink); }
.notes-page h2 {
  font-family: var(--sans);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--label);
  font-weight: 600;
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 0.5rem;
  margin: 2.5rem 0 1.2rem;
}
.notes-page h2:first-child { margin-top: 0; }
.notes-page p { margin: 0 0 1rem; }

/* Insights specifically: many short, discrete tagged hints, not prose —
   the generic manuscript line-height/paragraph-gap above (tuned for
   Summaries, which genuinely is prose) read as far too airy for a list
   of one- or two-line items, per direct feedback. Divider rules and a
   tighter rhythm make it read as one list, the same idiom the live
   sidebar's .margin-list already uses for the same content. */
.notes-page p.insight {
  font-size: 0.95rem;
  line-height: 1.5;
  margin: 0;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--rule);
}
.notes-page p.insight:last-of-type { border-bottom: none; }

/* .glossary-entry, not the generic .entry — that class was already taken
   by the auth screen's full-page centering wrapper (min-height: 100vh;
   display: flex; align-items/justify-content: center — see .entry
   above), a bare unscoped selector that matches ANY element carrying
   class="entry" regardless of ancestor. Every glossary term div was
   silently inheriting that: forced to a full *viewport height* box with
   its term and definition flexed side by side, which is the actual
   cause of the reported "huge gap between glossary items" — three terms
   meant three full-screen-tall boxes, not three compact rows. Scoping
   .glossary-page .entry only added padding/border on top of that; since
   it never redeclared display/min-height/align-items, none of those
   inherited properties were ever actually overridden.
   Block, not inline, dt/dd for the same reason as before: a compound
   term (e.g. a Russian patronymic with a space in it) plus a long
   inline definition right after it could wrap mid-term, with the
   definition's tail appearing to float between the term's own two
   halves — term on its own line, definition below it, sidesteps that
   entirely, same list rhythm as .notes-page p.insight below. */
.glossary-page { margin: 0; }
.glossary-page .glossary-entry {
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--rule);
}
.glossary-page .glossary-entry:last-child { border-bottom: none; padding-bottom: 0; }
.glossary-page .glossary-entry:first-child { padding-top: 0; }
.glossary-page dt { display: block; font-weight: 700; color: var(--ink-ru); margin-bottom: 0.2rem; }
.glossary-page dd { display: block; margin: 0; color: var(--label); font-style: italic; line-height: 1.5; }

/* Chat-bubble treatment — the question as a sent message (right-aligned,
   tinted), the answer as a received one (left-aligned, plain) — reads as
   an actual conversation rather than a Q, then an A, then a rule, per
   feedback that the flat version didn't read as a real chat area. */
.qa-block { display: flex; flex-direction: column; gap: 0.4rem; }
.qa-block + .qa-block { margin-top: 1.25rem; }
.qa-q {
  align-self: flex-end;
  max-width: 88%;
  margin: 0;
  background: color-mix(in srgb, var(--accent) 14%, var(--paper-raised));
  color: var(--ink);
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 500;
  line-height: 1.4;
  padding: 0.55rem 0.85rem;
  border-radius: 14px 14px 3px 14px;
}
.qa-a {
  align-self: flex-start;
  max-width: 100%;
  margin: 0;
  font-family: var(--serif);
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--ink);
  padding: 0 0.15rem;
}
.qa-a.not-covered { color: var(--label); font-style: italic; }
/* Lightweight Markdown rendering (public/js/qa-panel.js's renderMarkdown())
   — quiz-style multi-part answers were coming back as one unbroken block
   of text with no visible list structure, per direct feedback. */
.qa-a p { margin: 0 0 0.6rem; }
.qa-a p:last-child { margin-bottom: 0; }
.qa-a ol, .qa-a ul { margin: 0 0 0.6rem; padding-left: 1.3rem; }
.qa-a ol:last-child, .qa-a ul:last-child { margin-bottom: 0; }
.qa-a li { margin-bottom: 0.35rem; }
.qa-a li:last-child { margin-bottom: 0; }
.qa-a hr { border: none; border-top: 1px solid var(--rule); margin: 0.75rem 0; }
.qa-a strong { font-weight: 700; }
.qa-cite { align-self: flex-start; font-family: var(--sans); font-size: 0.72rem; color: var(--label); margin: -0.15rem 0 0 0.15rem; }

.ask-row {
  display: flex;
  gap: 0.75rem;
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule-strong);
  flex-wrap: wrap;
}
.ask-row input {
  flex: 1;
  min-width: 12rem;
  border: none;
  border-bottom: 1px solid var(--rule-strong);
  background: transparent;
  font-family: var(--sans);
  font-size: 0.9rem;
  color: var(--ink);
  padding: 0.4rem 0.1rem;
  outline: none;
}

.export-links { font-size: 0.85rem; }
.export-links a { color: var(--accent); font-weight: 600; }
.export-links span { color: var(--rule-strong); margin: 0 0.5rem; }

/* ---------------------------------------------------------------- */
/* Ask-about-this-lecture chat panel                                 */
/* Default (this block): a canvas overlay, hidden until opened via    */
/* .qa-fab — not a bottom sheet, that idiom stays reserved for         */
/* confirmations, per direct feedback. Covers LIVE on every device     */
/* (that screen has no spare room, already busy with the transcript/   */
/* translation/glossary panels) and ENDED on mobile. ENDED on desktop  */
/* is the one exception: .qa-panel--persistent, further below,         */
/* restores the original always-visible sidebar there — that design    */
/* was working well on the summary page specifically, per direct       */
/* feedback, and the canvas treatment only exists to solve the live    */
/* screen's actual space problem, not to replace it everywhere.        */
/* Closable via the header button, the scrim, or Escape                */
/* (public/js/qa-panel.js).                                            */
/* ---------------------------------------------------------------- */

/* .lecture-notes--with-qa reserves room on the right so reading content
   never flows under .qa-panel--persistent (ended, desktop only — the
   canvas overlay everywhere else floats over content instead, so it
   needs no reservation). Set only when that sidebar actually renders
   (status === ended), never unconditionally, so a paused lecture
   showing periodic summaries with no QA panel yet doesn't lose width it
   doesn't need to give up. */
@media (min-width: 56.01rem) {
  .lecture-notes--with-qa { padding-right: 32rem; }
}

/* Always position:fixed, on every breakpoint — deliberately never a
   participant in any grid/flow layout (see the comment on this panel in
   lectures/show.php for why: it used to be a CSS Grid sidebar column, and
   a tall chat history inflated that row's height, leaving a gap under
   short main content once the row outlived it — a real CSS Grid quirk,
   not something align-self can fix, since row tracks size to the
   tallest item's own height before alignment is even applied). Hidden
   by default everywhere; each breakpoint below sets its own size/position
   plus the closed transform, layered under this shared fade+scale. */
.qa-panel {
  position: fixed;
  z-index: 70;
  display: flex;
  flex-direction: column;
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--glass-shadow);
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s var(--spring);
}
.qa-panel.is-open { opacity: 1; pointer-events: auto; }

.qa-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 1rem 1.1rem 0.85rem;
  border-bottom: 1px solid var(--rule);
  flex-shrink: 0;
}
.qa-panel-close {
  all: unset;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--label);
  padding: 0.3rem;
  border-radius: 999px;
}
.qa-panel-close:hover { color: var(--ink); background: var(--paper); }

.qa-panel-history { flex: 1; overflow-y: auto; padding: 1rem 1.1rem; min-height: 0; }
.qa-empty { font-size: 0.85rem; color: var(--label); font-style: italic; margin: 0; }

.qa-panel .ask-row {
  margin: 0;
  padding: 0.85rem 1.1rem;
  border-top: 1px solid var(--rule);
  flex-shrink: 0;
}
.qa-a--pending { color: var(--label); font-style: italic; animation: lecture-capture-pulse 1.6s ease-in-out infinite; }

.qa-fab {
  display: flex;
  align-items: center;
  justify-content: center;
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  width: 50px;
  height: 50px;
  padding: 0;
  border: none;
  border-radius: 999px;
  color: var(--ink);
  z-index: 46;
}
.qa-panel.is-open ~ .qa-fab { display: none; }

.qa-scrim {
  display: block;
  position: fixed;
  inset: 0;
  background: var(--scrim);
  z-index: 65;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.qa-scrim.is-open { opacity: 1; pointer-events: auto; }

/* Desktop, LIVE lecture: a generous centered canvas — same overlay
   pattern mobile uses, just sized for the extra room. min()'d against
   the viewport so it still behaves on a smaller "desktop-width" window,
   not just a large monitor. */
@media (min-width: 56.01rem) {
  .qa-panel {
    top: 50%;
    left: 50%;
    width: min(42rem, 90vw);
    height: min(46rem, 82vh);
    transform: translate(-50%, -50%) scale(0.96);
  }
  .qa-panel.is-open { transform: translate(-50%, -50%) scale(1); }

  /* Desktop, ENDED lecture: the original always-visible sidebar,
     restored — right-anchored, no open/close state, no FAB or scrim
     needed since there's nothing to toggle. Overrides every canvas
     property the .qa-panel rule above set; the compound selector
     (both classes, not just .qa-panel--persistent alone) guarantees
     that regardless of source order, the same mistake as .entry's
     collision earlier isn't worth risking twice. */
  .qa-panel.qa-panel--persistent {
    top: 5.5rem;
    left: auto;
    right: 1.5rem;
    bottom: 1.5rem;
    width: 30rem;
    height: auto;
    opacity: 1;
    pointer-events: auto;
    transform: none;
  }
  .qa-panel--persistent .qa-panel-close { display: none; }
  .qa-panel--persistent ~ .qa-fab,
  .qa-panel--persistent ~ .qa-scrim { display: none; }
}

/* Mobile: a near-fullscreen canvas — fades and scales in rather than
   sliding up, so it doesn't read as the same sheet idiom as
   confirmations. */
@media (max-width: 56rem) {
  .qa-panel {
    top: 3vh;
    left: 0.75rem;
    right: 0.75rem;
    bottom: 3vh;
    transform: scale(0.96);
  }
  .qa-panel.is-open { transform: scale(1); }

  .qa-fab { right: 1rem; bottom: 0.6rem; width: 48px; height: 48px; }
}

.danger-zone {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule-strong);
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

/* ---------------------------------------------------------------- */
/* Confirmation sheet — replaces native confirm() everywhere         */
/* (public/js/confirm-sheet.js). Deliberately solid, not glass: a    */
/* destructive/consequential action should read as heavier and more  */
/* serious, per docs/UI_REDESIGN_RESEARCH.md's danger-zone guidance.  */
/* ---------------------------------------------------------------- */

.confirm-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--scrim);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
/* An author rule of equal specificity to the UA stylesheet's own
   [hidden] { display: none } wins by cascade order regardless — without
   this, the class rule above silently overrides `hidden` in every
   browser, not just here (caught via a headless-screenshot render vs.
   a DOM-only dump disagreeing on this exact element). */
.confirm-overlay[hidden] { display: none; }
@media (min-width: 40rem) {
  .confirm-overlay { align-items: center; }
}
.confirm-sheet {
  width: 100%;
  max-width: 26rem;
  background: var(--paper-raised);
  padding: 1.75rem 1.5rem;
  box-sizing: border-box;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.2);
  animation: confirm-sheet-up 0.25s var(--spring);
}
@media (min-width: 40rem) {
  .confirm-sheet { border-radius: var(--radius-lg); margin: 0 1rem; }
}
@keyframes confirm-sheet-up {
  from { transform: translateY(16px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}
.confirm-title { font-family: var(--serif); font-size: 1.15rem; margin: 0 0 0.6rem; }
.confirm-body { font-size: 0.88rem; line-height: 1.5; color: var(--label); margin: 0 0 1.5rem; }
.confirm-actions { display: flex; gap: 0.75rem; }
.confirm-actions .btn { flex: 1; }

/* ---------------------------------------------------------------- */
/* Search                                                            */
/* ---------------------------------------------------------------- */

.search-form { display: flex; gap: 0.75rem; max-width: 32rem; margin-bottom: 2rem; }
.search-form .search-field { flex: 1; }

.result-count { font-size: 0.8rem; color: var(--label); margin: 0 0 1.5rem; }
.result-row { padding: 1.25rem 0; border-bottom: 1px solid var(--rule); }
.result-row:first-of-type { border-top: 1px solid var(--rule-strong); }
.result-title { font-family: var(--serif); font-size: 1.05rem; }
.result-meta { font-size: 0.78rem; color: var(--label); margin: 0.15rem 0 0.75rem; }
.result-text { font-family: var(--serif); font-size: 0.98rem; line-height: 1.6; margin: 0 0 0.3rem; }
.result-text .lang-tag { font-family: var(--sans); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--label); margin-right: 0.5rem; }

.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 2rem;
}
.pagination .is-disabled { opacity: 0.4; pointer-events: none; }
.pagination-status { font-size: 0.8rem; color: var(--label); }
