:root {
  --primary: #2d5cf5;
  --primary-hover: #234ad4;
  --success: #10b981;
  --warning: #f59e0b;
  --danger: #ef4444;
  --text: #1a1a1a;
  --text-secondary: #666;
  --border: #e5e7eb;
  --card: #ffffff;
  --bg: #f5f7fa;

  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;
  --space-2xl: 48px;

  --radius-sm: 6px;
  --radius: 12px;
  --radius-lg: 16px;

  --shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  --shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.08);
}

* {
  box-sizing: border-box;
}

.hidden {
  display: none !important;
}

body {
  margin: 0;
  font-family: 'Manrope', system-ui, -apple-system, sans-serif;
  color: var(--text);
  background: var(--bg);
}

.login-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
  animation: fadeIn 400ms ease;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.login-card {
  width: 100%;
  max-width: 380px;
  background: var(--card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2xl) var(--space-xl);
}

.logo {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.5px;
  margin-bottom: var(--space-lg);
  color: var(--text);
  text-decoration: none;
}

.logo span {
  color: var(--primary);
}

h1 {
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.5px;
  margin: 0 0 var(--space-lg);
}

form {
  display: flex;
  flex-direction: column;
}

label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  margin-top: var(--space-sm);
  margin-bottom: var(--space-xs);
}

input,
textarea {
  font-size: 15px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color 150ms, box-shadow 150ms;
  font-family: inherit;
  width: 100%;
}

textarea {
  resize: vertical;
  min-height: 100px;
}

input:focus,
textarea:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(45, 92, 245, 0.15);
}

input.invalid,
textarea.invalid {
  border-color: var(--danger);
}

.password-field-wrap {
  position: relative;
}

.password-field-wrap input {
  padding-right: 40px;
}

.password-toggle-btn {
  position: absolute;
  top: 50%;
  right: 4px;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  border: none;
  background: none;
  cursor: pointer;
  font-size: 16px;
  opacity: 0.6;
  padding: 0;
}

.password-toggle-btn:hover {
  opacity: 1;
}

.field-error {
  display: block;
  font-size: 12px;
  color: var(--danger);
  min-height: 16px;
  margin-top: var(--space-xs);
}

.form-error {
  font-size: 13px;
  color: #991b1b;
  background: #fef2f2;
  border-radius: var(--radius-sm);
  padding: var(--space-sm) var(--space-md);
  margin-top: var(--space-sm);
  display: none;
}

.form-error.visible {
  display: block;
}

.login-card button[type='submit'] {
  margin-top: var(--space-md);
  background: var(--primary);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  padding: 10px 20px;
  font-size: 15px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background 150ms, transform 150ms, box-shadow 150ms;
}

.login-card button[type='submit']:hover:not(:disabled) {
  background: var(--primary-hover);
  transform: translateY(-1px);
  box-shadow: var(--shadow);
}

.login-card button[type='submit']:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.recovery-hint {
  margin: var(--space-lg) 0 0;
  font-size: 13px;
  color: var(--text-secondary);
  text-align: center;
}

.stub-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  text-align: center;
  padding: var(--space-lg);
}

.stub-page p {
  color: var(--text-secondary);
  margin: 0;
}

.stub-page button {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 20px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  color: var(--text);
  cursor: pointer;
  transition: border-color 150ms;
}

.stub-page button:hover {
  border-color: var(--primary);
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 20px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background 150ms, transform 150ms, box-shadow 150ms, border-color 150ms;
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-primary {
  background: var(--primary);
  color: #fff;
}

.btn-primary:hover:not(:disabled) {
  background: var(--primary-hover);
  transform: translateY(-1px);
  box-shadow: var(--shadow);
}

.btn-secondary {
  background: #fff;
  border-color: var(--border);
  color: var(--text);
}

.btn-secondary:hover:not(:disabled) {
  border-color: var(--primary);
}

.btn-danger {
  background: #fff;
  border-color: var(--danger);
  color: var(--danger);
}

.btn-danger:hover:not(:disabled) {
  background: #fef2f2;
}

/* Задача: тимчасовий стан кнопки "Підняти руку" після успіху - та сама
   зелена палітра, що й .badge-active. */
.btn-raised {
  background: #d1fae5;
  border-color: #059669;
  color: #065f46;
}

.btn-sm {
  padding: 6px 12px;
  font-size: 13px;
}

/* Admin layout */
.admin-layout {
  display: flex;
  min-height: 100vh;
  align-items: flex-start;
}

.admin-sidebar {
  width: 240px;
  flex-shrink: 0;
  min-height: 100vh;
  background: var(--card);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: var(--space-lg) var(--space-md);
  gap: var(--space-lg);
}

.admin-nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  flex: 1;
}

.admin-nav-item {
  display: block;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  color: var(--text);
  text-decoration: none;
  font-size: 14px;
  font-weight: 600;
  transition: background 150ms;
}

.admin-nav-item:hover {
  background: var(--bg);
}

.admin-nav-item.active {
  background: var(--primary);
  color: #fff;
}

.admin-logout {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: border-color 150ms;
}

.admin-logout:hover {
  border-color: var(--primary);
}

.admin-content {
  flex: 1;
  padding: var(--space-xl);
  padding-top: calc(var(--space-xl) + 56px);
  min-width: 0;
}

/* Задача: getHeaderIconsContainer (adminUtils.js) монтує дзвіночок/чат/
   глосарій/перемикач ролі сюди на сторінках адмінки (sidebar-layout,
   без .student-topbar) - фіксована смуга праворуч від сайдбару, над
   .admin-content (в якого тому й додано зверху зайвих 56px, щоб
   .admin-header з кнопками під ним не перекривався). */
.admin-header-icons {
  position: fixed;
  top: 0;
  left: 240px;
  right: 0;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-sm);
  padding: 0 var(--space-xl);
  /* Задача 11.5: було 200 - тобто вище за модалку, і затемнення не накривало
     дзвіночок із перемикачем ролі, а клікати по них крізь відкриту модалку
     було можна. 150 ставить цю смугу в один шар зі .student-topbar та
     рештою хедерних віджетів, як і задумано: це той самий хедер, просто в
     sidebar-розкладці. Модалка (160) тепер накриває її, тости (200) -
     лишаються над модалкою. */
  z-index: 150;
  pointer-events: none;
}

.admin-header-icons > * {
  pointer-events: auto;
}

.admin-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-lg);
  flex-wrap: wrap;
  gap: var(--space-md);
}

.admin-header h1 {
  margin: 0;
}

/* Card */
.card {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: var(--space-lg);
  overflow-x: auto;
}

.course-summary-card {
  text-decoration: none;
  color: inherit;
  transition: box-shadow 150ms ease, transform 150ms ease;
}

.course-summary-card:hover {
  box-shadow: var(--shadow-lg);
  transform: translateY(-2px);
}

/* Filters */
.filters-bar {
  display: flex;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
  flex-wrap: wrap;
}

/* Задача: рідна стрілка селекта притиснута впритул до правого краю, і
   padding-right її не відсуває - браузер малює її поверх padding-бокса.
   Єдиний спосіб керувати відступом - вимкнути рідний вигляд і намалювати
   свою стрілку фоном. Data-URI, а не окремий файл: одна іконка на весь
   застосунок, зайвий HTTP-запит заради 200 байт не потрібен. */
select {
  font-size: 14px;
  padding: 10px 36px 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: inherit;
  background-color: #fff;
  color: var(--text);
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23666' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='2.5 4.5 6 8 9.5 4.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 12px 12px;
}

/* Рідна стрілка в IE/старому Edge малюється окремо від background - без
   цього поруч із намальованою була б ще й системна. */
select::-ms-expand {
  display: none;
}

select:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(45, 92, 245, 0.15);
}

/* Table */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

th {
  text-align: left;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  color: var(--text-secondary);
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

td {
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

tr:last-child td {
  border-bottom: none;
}

.table-actions {
  display: flex;
  gap: var(--space-xs);
  justify-content: flex-end;
}

.table-row-clickable {
  cursor: pointer;
}

.table-row-clickable:hover {
  background: var(--bg);
}

.course-thumb {
  width: 48px;
  height: 32px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  display: block;
}

.course-thumb-empty {
  background: var(--bg);
  border: 1px dashed var(--border);
}

.course-image-preview {
  margin-top: var(--space-sm);
  max-width: 240px;
  max-height: 135px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  display: block;
}

/* Drag & drop reordering */
.dragging {
  opacity: 0.4;
}

.drag-handle {
  cursor: grab;
  color: var(--text-secondary);
  user-select: none;
  width: 24px;
  text-align: center;
  letter-spacing: -2px;
  flex-shrink: 0;
}

.text-truncate {
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Course structure (blocks & lessons) */
.back-link {
  display: inline-block;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  text-decoration: none;
  margin-bottom: var(--space-md);
}

.back-link:hover {
  color: var(--primary);
}

.section-block {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: var(--space-md);
}

.section-block-header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border);
}

.section-block-title {
  flex: 1;
  font-size: 16px;
  font-weight: 700;
}

.section-block-description {
  flex-basis: 100%;
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: 2px;
}

.lessons-list {
  padding: var(--space-sm) var(--space-lg);
}

.lesson-row-wrap {
  border-bottom: 1px solid var(--border);
}

.lesson-row-wrap:last-child {
  border-bottom: none;
}

.lesson-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) 0;
  text-decoration: none;
  color: inherit;
}

/* Задача: списки уроків у студента й інструктора - більше повітря між
   рядками і вирівняні колонки праворуч (тип, статус), щоб бейджі стояли
   один під одним, а не «стрибали» слідом за довжиною назви.
   Окремий модифікатор, а не сам .lesson-row: той самий клас
   використовують адмінські списки з перетягуванням і рядом кнопок дій
   (adminCourseDetail/adminQuiz/adminTraining), і там ця розкладка ні до
   чого. */
.lesson-row-learn {
  /* Лівий відступ однаковий у всіх рядків - у ньому ж поточний урок
     малює свою вертикальну смужку (див. .lesson-row-current). */
  padding: var(--space-md) 0 var(--space-md) var(--space-sm);
  gap: var(--space-md);
}

/* Фіксована ширина - саме заради вирівнювання по вертикалі. Тип уроку
   тепер іконка (назва лишилась у title), тож колонка вузька. */
.lesson-type-icon {
  flex: 0 0 24px;
  text-align: center;
  font-size: 16px;
  line-height: 1;
  cursor: default;
}

/* 180px - під найдовший зі статусів ("Потребує доопрацювання", ~175px).
   Вужча колонка не стискається, а розсувається: у flex-елемента
   min-width за замовчуванням auto, а бейдж має white-space: nowrap -
   тож рядок із довгим статусом просто вийшов би з ряду. */
.lesson-status-col {
  flex: 0 0 180px;
  display: flex;
  justify-content: flex-end;
}

.lesson-access-col {
  flex: 0 0 110px;
  text-align: right;
  color: var(--text-secondary);
  font-size: 13px;
}

.lesson-row-clickable {
  cursor: pointer;
}

.lesson-row-clickable:hover .lesson-title {
  color: var(--primary);
}

.lesson-row-locked {
  opacity: 0.55;
}

.lesson-row-completed {
  opacity: 0.6;
}

/* Смужка малюється inset-тінню, а не border-left: рамка + компенсуючий
   padding зсували ВЕСЬ рядок на 3px, і колонки праворуч у поточного
   уроку виходили з ряду - при вирівняних колонках це одразу видно.
   Тінь не займає місця в розкладці взагалі. */
.lesson-row-current {
  box-shadow: inset 3px 0 0 var(--primary);
  background: #eef2ff;
}

.lesson-expand-icon {
  display: inline-block;
  width: 1em;
  color: var(--text-secondary);
  transition: transform 150ms ease;
}

.lesson-row-expanded .lesson-expand-icon {
  transform: rotate(90deg);
}

.lesson-expand {
  padding: var(--space-md);
  margin: 0 0 var(--space-sm);
  background: var(--bg);
  border-radius: var(--radius-sm);
}

.lesson-expand-actions {
  display: flex;
  gap: var(--space-sm);
  flex-wrap: wrap;
}

.training-self-preview-tile {
  width: 100%;
  max-width: 320px;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--radius-sm);
  overflow: hidden;
  margin: var(--space-md) 0;
}

.training-self-preview-tile video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Задача: прокторинг камери на сторінці курсу (тільки teaching_mode='full')
   - постійна бічна панель з власною камерою, заглушкою камери інструктора
   і кнопкою "Підняти руку", поруч зі списком уроків. */
.course-page-layout {
  display: flex;
  align-items: flex-start;
  gap: var(--space-lg);
}

.course-page-layout .student-content {
  flex: 1;
  min-width: 0;
  /* Задача: звичайний .student-content (нижче) обмежує 960px глобально -
     на сторінках навчання (студент/інструктор) список уроків від цього
     був затиснутий незалежно від видимості бічної камера-панелі. */
  max-width: 1200px;
}

.camera-panel {
  width: 260px;
  flex-shrink: 0;
  position: sticky;
  /* Прилипає ПІД хедером, а не під нього: хедер теж sticky, і без цієї
     поправки камера студента заїжджала б йому за спину. Висота хедера не
     константа (залежить від padding+вмісту, а на вузькому екрані
     навігація ще й переноситься в два рядки), тож її міряє JS -
     див. --topbar-height в adminUtils.js. Запасне 0px - на випадок, якщо
     скрипт ще не встиг відпрацювати. */
  top: calc(var(--topbar-height, 0px) + var(--space-lg));
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.camera-panel-box h3 {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  margin: 0 0 var(--space-sm);
}

.camera-panel-box video {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--radius-sm);
  object-fit: cover;
}

.camera-placeholder {
  aspect-ratio: 16 / 9;
  background: #111;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  font-size: 13px;
  text-align: center;
  padding: var(--space-sm);
}

@media (max-width: 900px) {
  .course-page-layout {
    flex-direction: column;
  }

  .camera-panel {
    width: 100%;
    position: static;
  }
}

.access-form,
.lesson-detail {
  padding: var(--space-md);
  margin-bottom: var(--space-sm);
  background: var(--bg);
  border-radius: var(--radius-sm);
}

.lesson-title {
  flex: 1;
  font-size: 14px;
}

.lesson-type-badge {
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  white-space: nowrap;
}

.lesson-type-video {
  background: #a78bfa;
}

.lesson-type-article {
  background: #60a5fa;
}

.lesson-type-quiz {
  background: #34d399;
}

.lesson-type-assignment {
  background: #fbbf24;
}

.lesson-type-training {
  background: #f472b6;
}

.section-footer {
  padding: var(--space-sm) var(--space-lg) var(--space-md);
}

/* Badges */
.badge {
  display: inline-block;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

.badge-active {
  background: #d1fae5;
  color: #065f46;
}

.badge-inactive {
  background: #f3f4f6;
  color: #4b5563;
}

.badge-role {
  background: #eef2ff;
  color: #3730a3;
}

.badge-warning {
  background: #fef3c7;
  color: #92400e;
}

/* Empty / loading states */
.empty-state,
.loading-state {
  text-align: center;
  padding: var(--space-2xl) var(--space-lg);
  color: var(--text-secondary);
  font-size: 14px;
}

/* Modal */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
  /* Задача 11.5 (хвіст UI з Roadmap): було 100 - НИЖЧЕ за все, що стоїть на
     150: закріплений хедер, дзвіночок, кнопки чату й глосарію, перемикач
     ролі, власна камера інструктора. Наслідок був не лише косметичний:
     затемнення не накривало хедер, і поки модалка відкрита, по дзвіночку та
     решті віджетів можна було клікати крізь неї.
     160 - вище за весь шар 150, але нижче за тости (200), віджет активного
     дзвінка (300) і панель дзвінка (400/500): сповіщення й дзвінок мають
     лишатись видимими поверх будь-якої модалки. */
  z-index: 160;
  animation: fadeIn 250ms ease;
}

.modal-overlay.hidden {
  display: none;
}

.modal {
  background: var(--card);
  border-radius: var(--radius-lg);
  max-width: 560px;
  width: 100%;
  max-height: 90vh;
  overflow-y: auto;
  padding: var(--space-xl);
  animation: modalIn 250ms ease;
}

.modal-wide {
  max-width: 720px;
}

@keyframes modalIn {
  from {
    opacity: 0;
    transform: scale(0.96);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.modal h2 {
  font-size: 20px;
  font-weight: 700;
  margin: 0 0 var(--space-lg);
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
  margin-top: var(--space-lg);
}

.modal-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 var(--space-md);
}

.modal-grid .field-full {
  grid-column: 1 / -1;
}

/* Credentials reveal panel */
.credentials-panel {
  background: #ecfdf5;
  border: 1px solid #a7f3d0;
  border-radius: var(--radius-sm);
  padding: var(--space-md);
  margin-top: var(--space-md);
  font-size: 14px;
}

.credentials-panel code {
  background: #fff;
  padding: 2px 6px;
  border-radius: 4px;
  font-family: 'SFMono-Regular', Consolas, monospace;
}

/* Student cabinet */
.student-layout {
  min-height: 100vh;
}

/* Задача: хедер лишається на видноті при прокрутці. sticky, а не fixed:
   елемент лишається в потоці, тож не треба компенсувати його висоту
   відступом у .student-content і немає стрибка розмітки.

   z-index саме 150 - стільки ж, скільки вже мають віджети всередині
   самого хедера (.notification-bell, .chat-widget-btn, .role-switcher-
   widget). Оскільки хедер тепер утворює контекст накладання, це число
   вирішує долю всього хедера разом із їхніми дропдаунами; 150 зберігає
   рівно той порядок, що був: вище за .modal-overlay (100), нижче за
   .toast (200), .active-call-widget (300) і .call-panel (400). */
.student-topbar {
  position: sticky;
  top: 0;
  z-index: 150;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-md) var(--space-xl);
  background: var(--card);
  border-bottom: 1px solid var(--border);
}

/* Задача: хедер у 3 зони - лого зліва (.logo), навігація по центру
   (.topbar-nav), іконки сповіщень/чату/глосарія + "Вийти" праворуч
   (.topbar-actions). */
.topbar-nav {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex: 1;
  justify-content: center;
}

.topbar-nav a,
.nav-dropdown-toggle {
  color: var(--text);
  text-decoration: none;
  font-weight: 600;
  font-size: 14px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
}

.topbar-nav a:hover,
.nav-dropdown-toggle:hover {
  color: var(--primary);
}

.nav-dropdown {
  position: relative;
}

.nav-dropdown-menu {
  position: absolute;
  top: calc(100% + 14px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  padding: var(--space-xs);
  display: flex;
  flex-direction: column;
  min-width: 180px;
  z-index: 20;
}

.nav-dropdown-menu a {
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

.nav-dropdown-menu a:hover {
  background: var(--bg);
  color: var(--primary);
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.header-icons {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

/* Задача: іконки в шапці - просто іконки, без кружечків. Раніше кожна
   була кнопкою 40x40 з рамкою, білим тлом і тінню, і три такі медальйони
   поруч із текстовими кнопками виглядали важче за саму навігацію.
   Розмір 40x40 лишаємо як зону кліку (44px - рекомендований мінімум для
   пальця, тут трохи менше, але це десктоп) - зникло лише оформлення.
   Одне правило на всі три: дзвіночок, чат і глосарій монтуються різними
   віджетами, але в шапці мають бути невідрізними. */
.notification-bell-btn,
.chat-widget-btn-trigger,
.glossary-widget-btn-trigger {
  width: 40px;
  height: 40px;
  padding: 0;
  background: none;
  border: none;
  box-shadow: none;
  border-radius: var(--radius-sm);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms ease;
}

.notification-bell-btn:hover,
.chat-widget-btn-trigger:hover,
.glossary-widget-btn-trigger:hover {
  background: var(--bg);
}

/* Задача: усі текстові кнопки шапки (Камера, Перемикнути роль, Вийти,
   Налаштування) - однакового розміру й стилю. Раніше "Камера"/"Роль"
   були 40px заввишки (.role-switcher-btn), а "Вийти"/"Налаштування" -
   .btn-sm, тобто на 9px нижчі, і ряд виглядав нерівним. Правило
   заскоуплене на шапку, щоб не зачепити .btn-sm по всьому застосунку. */
/* Тільки прямі діти: .header-icons теж лежить у .topbar-actions, а в її
   віджетах є свої .btn всередині випадних панелей (напр. "Прочитати всі"
   у дзвіночку) - їх розтягувати до 40px не треба. */
.topbar-actions > .btn,
.topbar-actions .role-switcher-btn {
  height: 40px;
  padding: 0 var(--space-md);
  font-size: 13px;
  font-weight: 600;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
}

.student-content {
  max-width: 960px;
  margin: 0 auto;
  padding: var(--space-xl);
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-md);
  margin: var(--space-lg) 0;
}

/* Дашборд студента (2026-08-22): картки показників мають бути такої самої
   ширини, як картка навчання в списку "Мої курси" нижче. Тому тут дослівно
   ті самі значення, що й у .courses-grid — і minmax, і gap.

   auto-fill, а НЕ auto-fit: саме в цьому вся суть. auto-fit згорнув би
   порожні колонки й розтягнув три картки на всю ширину — вони стали б
   ширшими за картки курсів. auto-fill лишає порожню колонку, тож ширина
   збігається один в один.

   ⚠️ Якщо колись зміниться minmax у .courses-grid — міняти й тут, інакше
   ряди тихо розʼїдуться. Зв'язок між ними не виражений у коді. */
.stats-grid--course-width {
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-lg);
}

.stat-card {
  text-align: center;
}

.stat-value {
  font-size: 32px;
  font-weight: 800;
  color: var(--primary);
}

.stat-label {
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: var(--space-xs);
}

.course-row {
  display: block;
  padding: var(--space-md) 0;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  color: inherit;
}

.course-row:last-child {
  border-bottom: none;
}

a.course-row:hover .course-row-title {
  color: var(--primary);
}

.course-row-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-xs);
}

.course-row-title {
  font-weight: 600;
}

.course-row-percent {
  font-size: 13px;
  color: var(--text-secondary);
}

/* Задача: дашборд студента - "Мої курси" сіткою карток (банер + назва +
   прогрес + "Доступно до") замість списку рядків .course-row вище. */
.courses-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-lg);
  margin-top: var(--space-md);
}

.courses-grid > .empty-state,
.courses-grid > .loading-state {
  grid-column: 1 / -1;
}

.course-card {
  display: block;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: box-shadow 150ms ease, transform 150ms ease;
}

.course-card:hover {
  box-shadow: var(--shadow-lg);
  transform: translateY(-2px);
}

.course-card-banner {
  aspect-ratio: 16 / 9;
  background-color: var(--bg);
  background-size: cover;
  background-position: center;
}

.course-card-banner-empty {
  background: linear-gradient(135deg, var(--primary), var(--primary-hover));
}

/* Задача: однакові відступи в блоках головної студента. "Продовжити
   навчання" - звичайна .card з padding: var(--space-lg), а картки курсів
   мали на 8px менше, і два блоки один під одним читались як зроблені
   різними людьми. */
.course-card-body {
  padding: var(--space-lg);
}

.course-card-title {
  font-weight: 700;
  margin-bottom: var(--space-sm);
}

.course-card:hover .course-card-title {
  color: var(--primary);
}

.course-card-meta {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: var(--space-xs);
}

.course-card-access {
  font-size: 13px;
  color: var(--warning);
  margin-top: var(--space-sm);
}

.progress-bar {
  background: var(--bg);
  border-radius: 999px;
  height: 8px;
  overflow: hidden;
}

/* Задача: дзвінок у тій же вкладці - закріплена бокова панель справа
   (activeCallWidget.js), UI дзвінка будується динамічно
   public/js/trainingCallUi.js. Розкладка - стек рівних блоків одна під
   одною (власна камера + камера співрозмовника, третій блок для
   секондарі-інструктора-спостерігача), а не "велике відео + self-PIP". */
.call-stage {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.call-tiles-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  flex: 1;
  min-height: 320px;
}

.call-tiles-stack:empty::before {
  content: 'Очікуємо на відео співрозмовника...';
  color: #ccc;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  background: #000;
  border-radius: var(--radius);
}

.call-video-tile {
  position: relative;
  flex: 1;
  min-height: 140px;
  background: #111;
  border-radius: var(--radius);
  overflow: hidden;
}

.call-video-tile.speaking {
  outline: 3px solid var(--success);
  outline-offset: -3px;
}

.call-video-inner {
  position: relative;
  width: 100%;
  height: 100%;
}

.call-video-inner video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.call-video-tile-self .call-video-inner video {
  transform: scaleX(-1);
}

.call-tile-label {
  position: absolute;
  bottom: 6px;
  left: 8px;
  color: #fff;
  font-size: 12px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
  z-index: 1;
}

.call-mic-indicator {
  position: absolute;
  bottom: 6px;
  right: 8px;
  font-size: 14px;
  z-index: 1;
}

.call-controls {
  display: flex;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.call-controls .btn.active {
  background: var(--danger);
  color: #fff;
  border-color: var(--danger);
}

.call-status {
  color: var(--text-secondary);
  font-size: 14px;
  margin-top: var(--space-sm);
}

.progress-bar-fill {
  background: var(--primary);
  height: 100%;
  border-radius: 999px;
  transition: width 250ms ease;
}

.course-row-meta {
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: var(--space-xs);
}

.video-embed-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
}

.video-embed-wrap iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: none;
  border-radius: var(--radius-sm);
}

/* Задача: розділи статті - нативний <details>/<summary>, лише стилізація. */
.article-section {
  border-bottom: 1px solid var(--border);
  padding: var(--space-sm) 0;
}

.article-section:last-child {
  border-bottom: none;
}

.article-section-title {
  cursor: pointer;
  font-weight: 700;
  font-size: 15px;
  list-style: none;
  padding: var(--space-xs) 0;
}

.article-section-title::-webkit-details-marker {
  display: none;
}

.article-section-title::before {
  content: '▸';
  display: inline-block;
  width: 1em;
  color: var(--text-secondary);
  transition: transform 150ms ease;
}

.article-section[open] > .article-section-title::before {
  transform: rotate(90deg);
}

.article-section-title:hover {
  color: var(--primary);
}

.article-section-content {
  padding: var(--space-sm) 0 0 1em;
  white-space: pre-wrap;
}

/* Notes - прості рядки "як від руки", без карток/рамок (Задача:
   нотатки по курсу). .note-group-title лишається для історії спроб
   тесту й історії пошуку глосарія - тут навмисно окремі класи. */
.notes-section-title {
  font-size: 15px;
  font-weight: 700;
  margin: var(--space-md) 0 4px;
}

.notes-lesson-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-secondary);
  margin: var(--space-sm) 0 4px;
}

.note-line {
  padding: 6px 0;
  border-bottom: 1px dashed var(--border);
}

.note-line:last-child {
  border-bottom: none;
}

.note-line-date {
  display: block;
  font-size: 12px;
  color: var(--text-secondary);
}

.note-line-text {
  white-space: pre-wrap;
  margin: 2px 0;
}

.note-line-actions {
  display: flex;
  gap: var(--space-sm);
}

/* Chat - used inside chat-widget-panel as <button> items (picker list) */
.chat-list-item {
  display: block;
  width: 100%;
  padding: var(--space-md);
  border: none;
  border-bottom: 1px solid var(--border);
  background: none;
  text-decoration: none;
  text-align: left;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
}

.chat-list-item:last-child {
  border-bottom: none;
}

.chat-list-item:hover .chat-list-name {
  color: var(--primary);
}

.chat-messages {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  max-height: 500px;
  overflow-y: auto;
  padding: var(--space-sm) 0;
}

.chat-bubble {
  max-width: 70%;
  padding: 10px 14px;
  border-radius: var(--radius);
  font-size: 14px;
  white-space: pre-wrap;
}

.chat-bubble-theirs {
  align-self: flex-start;
  background: var(--bg);
}

.chat-bubble-mine {
  align-self: flex-end;
  background: var(--primary);
  color: #fff;
}

.chat-bubble-time {
  font-size: 11px;
  opacity: 0.7;
  margin-top: 4px;
  display: block;
}

/* Toast */
.toast {
  position: fixed;
  bottom: var(--space-lg);
  right: var(--space-lg);
  background: var(--text);
  color: #fff;
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  box-shadow: var(--shadow-lg);
  z-index: 200;
  animation: fadeIn 250ms ease;
}

/* Задача 10.3 / хедер у 3 зони: дзвіночок монтується всередині
   .header-icons у шапці (не floating на document.body), потрібен лише
   position: relative як якір для випадної панелі. */
.notification-bell {
  position: relative;
  z-index: 150;
}

.notification-bell-btn {
  position: relative;
}

.notification-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--danger);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.notification-badge.hidden {
  display: none;
}

.notification-dropdown {
  position: absolute;
  top: 48px;
  right: 0;
  width: 340px;
  max-height: 420px;
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.notification-dropdown.hidden {
  display: none;
}

.notification-dropdown-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border);
  font-weight: 700;
}

.notification-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-sm) var(--space-md);
  border: none;
  border-bottom: 1px solid var(--border);
  background: none;
  cursor: pointer;
  font-family: inherit;
}

.notification-item:last-child {
  border-bottom: none;
}

.notification-item.unread {
  background: var(--primary-light, #eef2ff);
}

.notification-item-title {
  font-size: 14px;
  font-weight: 600;
}

.notification-item-body {
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: 2px;
}

.notification-item-time {
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 4px;
}

.notification-toast-stack {
  position: fixed;
  bottom: var(--space-lg);
  right: var(--space-lg);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  align-items: flex-end;
}

/* Задача: інструктор бачить власну камеру - знизу-зліва, щоб не
   перетинатись з .notification-toast-stack (знизу-справа). */
.instructor-self-camera {
  position: fixed;
  bottom: var(--space-lg);
  left: var(--space-lg);
  z-index: 150;
  width: 160px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
}

.instructor-self-camera video {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: #000;
  display: block;
}

.instructor-self-camera-label {
  position: absolute;
  bottom: 4px;
  left: 6px;
  color: #fff;
  font-size: 11px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}

/* Задача: плаваюча кнопка "у вас дзвінок" - зверху по центру, щоб не
   перетинатись з камерою інструктора (знизу-зліва) чи тостами (знизу-справа);
   pulse - привертає увагу, бо може означати вхідний дзвінок, що вимагає дії. */
.active-call-widget {
  position: fixed;
  top: var(--space-lg);
  left: 50%;
  transform: translateX(-50%);
  z-index: 300;
  background: var(--danger);
  color: #fff;
  border: none;
  border-radius: 999px;
  padding: 10px 20px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
  animation: active-call-pulse 1.6s ease-in-out infinite;
}

@keyframes active-call-pulse {
  0%, 100% { box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3); }
  50% { box-shadow: 0 2px 20px rgba(220, 38, 38, 0.6); }
}

/* Задача (звуки): вхідний, ще не прийнятий дзвінок - помітніша пульсація
   в такт рингтону (той самий 3-секундний цикл, що й у
   notificationSound.js). Не робимо через animation-name у JS, щоб такт
   не збивався при перемальовуванні індикатора. */
.active-call-widget.incoming {
  animation: incoming-call-pulse 3s ease-in-out infinite;
}

@keyframes incoming-call-pulse {
  0%, 100% { transform: translateX(-50%) scale(1); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3); }
  6% { transform: translateX(-50%) scale(1.06); box-shadow: 0 4px 26px rgba(220, 38, 38, 0.75); }
  12% { transform: translateX(-50%) scale(1); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3); }
  22% { transform: translateX(-50%) scale(1.06); box-shadow: 0 4px 26px rgba(220, 38, 38, 0.75); }
  30% { transform: translateX(-50%) scale(1); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3); }
}

/* Задача (звуки): блок налаштувань звуку. label глобально inline, тож два
   чекбокси поспіль без display:block злиплися б в один рядок. */
.sound-toggle {
  display: block;
}

.sound-volume-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.sound-volume-row label {
  margin: 0;
  white-space: nowrap;
}

/* Глобальне правило для input задає width:100% + padding + рамку - для
   повзунка це виглядало б як текстове поле. */
.sound-volume-row input[type='range'] {
  width: 180px;
  padding: 0;
  border: none;
  box-shadow: none;
}

.sound-hint {
  color: var(--text-secondary);
  font-size: 13px;
  margin-top: var(--space-sm);
}

/* Пояснювальний рядок під заголовком блоку чи полем форми. Знадобився в
   картці компанії: наслідки дій там не очевидні з самої кнопки (курс
   отримають і ті, кого привʼяжуть пізніше), і це треба сказати словами
   поруч, а не в підтвердженні постфактум. */
.hint-text {
  color: var(--text-secondary);
  font-size: 13px;
  margin: 0 0 var(--space-md);
}

/* Задача: дзвінок у тій же вкладці - плаваюча панель поверх сторінки
   (замість окремого popup-вікна), живе поза .student-layout, тож SPA-
   роутер (spaShell.js) її ніколи не чіпає при переходах між сторінками. */
.call-panel {
  position: fixed;
  /* Під хедером, а не поверх нього. Висоту тримає ResizeObserver в
     adminUtils.js; 0px - для адмінських сторінок, де .student-topbar
     немає взагалі (там своя fixed-шапка). */
  top: var(--topbar-height, 0px);
  right: 0;
  bottom: 0;
  width: 360px;
  min-width: 260px;
  max-width: calc(100vw - 80px);
  background: var(--bg, #fff);
  box-shadow: var(--shadow-lg);
  z-index: 400;
  display: flex;
  flex-direction: column;
  border-left: 1px solid var(--border);
}

/* Задача: "розгорнути" - та сама панель на весь екран, а не окреме
   модальне вікно. Саме клас, а не новий контейнер: перенести <video> в
   іншу ноду означало б підморгування чорним, а перемонтувати дзвінок -
   реконект до LiveKit. z-index вище за все інше (хедер 150, дзвіночок і
   тости 200, індикатор дзвінка 300) - на весь екран означає на весь
   екран. Ширину, задану інлайново перетягуванням, перекриваємо !important
   саме тому, що вона інлайнова. */
.call-panel.expanded {
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: auto !important;
  max-width: none;
  z-index: 500;
  border-left: none;
}

.call-panel.expanded .call-panel-resize-handle {
  display: none;
}

/* У боковій панелі камери стоять стовпчиком, на весь екран - поруч:
   auto-fit сам розкладе дві камери в дві колонки, а три (коли підключився
   другий інструктор) - у три на широкому екрані або 2+1 на вужчому. */
.call-panel.expanded .call-stage {
  flex: 1;
  min-height: 0;
}

/* На весь екран комірка сітки майже квадратна, а відео - 16:9. cover
   (як у боковій панелі, де плитка теж широка) обрізав би людині півкадру
   по боках, тому тут contain: кадр показуємо цілком, вільне місце лишається
   темним тлом плитки. */
.call-panel.expanded .call-video-inner video {
  object-fit: contain;
}

.call-panel.expanded .call-tiles-stack {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  /* Рядки тягнуться на всю висоту, а не по вмісту: без цього плитки
     стиснулись би до власної висоти відео і зависли б угорі екрана. */
  grid-auto-rows: 1fr;
  min-height: 0;
}

.call-panel.expanded .call-video-tile {
  min-height: 0;
}

.call-panel.expanded .call-controls {
  justify-content: center;
}

.call-panel-header-actions {
  display: flex;
  align-items: center;
  gap: 2px;
}

.call-panel-expand {
  display: flex;
  align-items: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-secondary);
  padding: 4px 8px;
}

.call-panel-expand:hover {
  color: var(--text);
}

.call-panel-resize-handle {
  position: absolute;
  top: 0;
  left: -4px;
  width: 8px;
  height: 100%;
  cursor: col-resize;
  z-index: 1;
}

.call-panel-resize-handle:hover,
.call-panel.resizing .call-panel-resize-handle {
  background: var(--primary);
  opacity: 0.3;
}

.call-panel.resizing {
  user-select: none;
}

.call-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border);
  cursor: default;
  flex: 0 0 auto;
}

.call-panel-title {
  font-weight: 700;
  font-size: 14px;
}

.call-panel-minimize {
  background: none;
  border: none;
  font-size: 16px;
  cursor: pointer;
  color: var(--text-secondary);
  padding: 4px 8px;
}

.call-panel-minimize:hover {
  color: var(--text);
}

.call-panel-body {
  padding: var(--space-md);
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.call-panel-mount {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.call-panel .call-controls {
  flex: 0 0 auto;
}

.notification-toast {
  background: var(--text);
  color: #fff;
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  box-shadow: var(--shadow-lg);
  cursor: pointer;
  max-width: 320px;
  animation: fadeIn 250ms ease;
}

.notification-toast-title {
  font-weight: 700;
}

/* Задача: чат і глосарій - заякорені модалки (той самий патерн, що й
   дзвіночок сповіщень вище), а не окремі сторінки. Монтуються в один
   ряд усередині .header-icons у шапці - лише position: relative як
   якір для випадної панелі. */
.chat-widget-btn,
.glossary-widget-btn {
  position: relative;
  z-index: 150;
}

.chat-widget-btn-trigger,
.glossary-widget-btn-trigger {
  position: relative;
}

.chat-widget-panel,
.glossary-widget-panel {
  position: absolute;
  top: 48px;
  right: 0;
  width: 360px;
  max-height: 480px;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.chat-widget-panel.hidden,
.glossary-widget-panel.hidden {
  display: none;
}

.widget-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border);
  font-weight: 700;
}

.widget-panel-back-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 14px;
  color: var(--primary);
  padding: 0;
  font-family: inherit;
}

.widget-panel-body {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
}

.widget-panel-search {
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border);
}

.widget-panel-search input {
  width: 100%;
}

.chat-widget-messages {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}

.chat-widget-compose {
  display: flex;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border-top: 1px solid var(--border);
}

.chat-widget-compose textarea {
  flex: 1;
  resize: none;
  min-height: 40px;
}

/* Перемикнути роль (roleSwitcher.js) - у ряд з дзвіночком/чатом/
   глосарієм через .header-icons (той самий контейнер, що й вони). На
   адмінці (sidebar-layout, без topbar) getHeaderIconsContainer падає
   назад на document.body - та сама поведінка, що й у дзвіночка там. */
.role-switcher-widget {
  position: relative;
  z-index: 150;
}

.role-switcher-btn {
  height: 40px;
  padding: 0 var(--space-md);
  border-radius: var(--radius-sm);
  background: var(--card);
  border: 1px solid var(--border);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

.role-switcher-dropdown {
  position: absolute;
  top: 48px;
  right: 0;
  min-width: 200px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  z-index: 150;
}

.role-switcher-dropdown.hidden {
  display: none;
}

.role-switcher-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-sm) var(--space-md);
  border: none;
  background: none;
  font-size: 13px;
  cursor: pointer;
}

.role-switcher-item:hover {
  background: var(--bg);
}

.role-switcher-item.active {
  font-weight: 700;
  background: var(--bg);
}

/* Задача 8.2/8.3: шкала відповідей ЧВД - один сегмент на питання кола.
   Порожній сегмент навмисно не сірий-у-сірому, а з рамкою: інакше на світлому
   тлі не видно, скільки питань ще попереду. */
.answer-scale {
  display: flex;
  gap: 3px;
  margin: var(--space-sm) 0;
}

.answer-scale-cell {
  flex: 1;
  height: 12px;
  border-radius: 3px;
  background: var(--surface, #fff);
  border: 1px solid var(--border);
}

.answer-scale-cell.correct {
  background: var(--success);
  border-color: var(--success);
}

.answer-scale-cell.incorrect {
  background: var(--danger);
  border-color: var(--danger);
}

/* Задача 11.3: вартість уроку в балах у списку уроків адмінки. */
.lesson-points {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* Задача 11.3: прапорець "ціль на день" у рядку уроку в інструктора. */
.goal-flag {
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 4px 8px;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  /* Неактивний прапорець приглушений майже до сірого: він є в КОЖНОМУ рядку,
     і якби всі горіли кольором, список перетворився б на гірлянду. */
  filter: grayscale(1);
  opacity: 0.35;
  transition: opacity 150ms, filter 150ms, border-color 150ms;
}

.goal-flag:hover {
  opacity: 0.8;
  border-color: var(--border);
}

.goal-flag-active {
  filter: none;
  opacity: 1;
  border-color: var(--warning);
  background: #fffbeb;
}

/* Ціль на завершення - поле з датою вгорі курсу в інструктора. */
.completion-goal-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex-wrap: wrap;
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid var(--border);
}

.completion-goal-row label {
  margin: 0;
  white-space: nowrap;
}

.completion-goal-row input {
  width: auto;
}

.completion-goal-row small {
  color: var(--text-secondary);
  font-size: 12px;
}

/* Ціль на завершення очима студента - над прогресом курсу. */
.completion-goal-banner {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
  font-size: 14px;
}

.completion-goal-banner strong {
  color: var(--primary);
}

.completion-goal-banner .goal-overdue {
  color: var(--danger);
}

/* Задача 11.3: верх дашборда студента - показники ліворуч, рейтинг праворуч. */
.dashboard-top {
  display: grid;
  /* Ліва колонка фіксованої ширини (та сама, що в картки курсу нижче), права
     забирає решту. Так рейтинг отримує місце під імена й бали, а стовпчик
     показників не розтягується на пів екрана. */
  grid-template-columns: 260px 1fr;
  gap: var(--space-lg);
  margin: var(--space-lg) 0;
  align-items: start;
}

.dashboard-stats {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

/* На вузькому екрані двоколонкова сітка стає одноколонковою: рейтинг з
   іменами й балами не влазить поруч зі стовпцем на телефоні. */
@media (max-width: 720px) {
  .dashboard-top {
    grid-template-columns: 1fr;
  }
}

.leaderboard-card {
  /* Висота під три картки показників ліворуч - щоб блоки закінчувались на
     одному рівні, а не сходинкою. */
  min-height: 100%;
}

.leaderboard-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-bottom: var(--space-md);
}

.leaderboard-header h2 {
  margin: 0;
}

.leaderboard-tabs {
  display: inline-flex;
  background: var(--bg);
  border-radius: var(--radius-sm);
  padding: 2px;
  gap: 2px;
}

.leaderboard-tab {
  border: none;
  background: none;
  padding: 6px 14px;
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 150ms, color 150ms;
}

.leaderboard-tab.active {
  background: var(--card);
  color: var(--text);
  box-shadow: var(--shadow);
}

.leaderboard-row {
  display: grid;
  /* Місце - фіксовано, імʼя тягнеться, бали притиснуті праворуч. */
  grid-template-columns: 32px 1fr auto;
  align-items: center;
  gap: var(--space-sm);
  padding: 10px var(--space-sm);
  border-radius: var(--radius-sm);
  font-size: 14px;
}

.leaderboard-row + .leaderboard-row {
  border-top: 1px solid var(--border);
}

.leaderboard-place {
  font-weight: 700;
  color: var(--text-secondary);
  text-align: center;
}

.leaderboard-points {
  font-weight: 700;
  color: var(--primary);
  white-space: nowrap;
}

/* Рядок самого глядача. Виділення робить таблицю читабельною з одного
   погляду - інакше своє місце доводиться шукати очима. */
.leaderboard-row-viewer {
  background: rgba(45, 92, 245, 0.08);
  border-top-color: transparent;
}

.leaderboard-row-viewer .leaderboard-place,
.leaderboard-row-viewer .leaderboard-name {
  color: var(--text);
  font-weight: 700;
}

/* Розрив між топом і рядком глядача, коли він поза топом: без нього здається,
   що 12-те місце йде одразу після пʼятого. */
.leaderboard-row-gap {
  text-align: center;
  color: var(--text-secondary);
  font-size: 16px;
  line-height: 1;
  padding: 2px 0;
}

/* Задача 11.1: хронологія уроку за кнопкою «Детальніше» (інструктор). */
.lesson-timeline-wrap {
  margin-bottom: var(--space-md);
}

.lesson-timeline {
  margin-top: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--bg);
  border-radius: var(--radius-sm);
  max-width: 420px;
}

.lesson-timeline-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-md);
  padding: 5px 0;
  font-size: 13px;
  color: var(--text-secondary);
}

.lesson-timeline-row + .lesson-timeline-row {
  border-top: 1px solid var(--border);
}

.lesson-timeline-row strong {
  color: var(--text);
  white-space: nowrap;
}

/* Задача 11.1: блок «Аналітика» в профілі студента. */
.analytics-grid {
  display: grid;
  /* Графіку потрібна ширина, показникам - ні: вони читаються стовпчиком. */
  grid-template-columns: minmax(0, 1fr) 280px;
  gap: var(--space-lg);
  align-items: start;
}

@media (max-width: 860px) {
  .analytics-grid {
    grid-template-columns: 1fr;
  }
}

.analytics-chart-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-md);
  font-size: 13px;
  color: var(--text-secondary);
  margin-bottom: var(--space-sm);
}

.analytics-chart-header strong {
  color: var(--primary);
  font-size: 15px;
}

.chart-wrap {
  position: relative;
}

.points-chart {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}

/* Підказка при наведенні. Зміщення на пів висоти вгору і трохи вбік, щоб не
   накривати саму точку, яку читають. */
.chart-tooltip {
  position: absolute;
  transform: translate(-50%, -160%);
  background: var(--text);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  pointer-events: none;
}

.analytics-metrics {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.analytics-course {
  background: var(--bg);
  border-radius: var(--radius-sm);
  padding: var(--space-md);
}

.analytics-course-title {
  font-weight: 700;
  font-size: 14px;
  margin-bottom: var(--space-sm);
}

.metric-row + .metric-row {
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid var(--border);
}

.metric-label {
  font-size: 12px;
  color: var(--text-secondary);
}

.metric-value {
  font-size: 20px;
  font-weight: 800;
  line-height: 1.2;
}

.metric-extra {
  font-size: 12px;
  color: var(--text-secondary);
}

/* Пояснення, чому показника немає. Свідомо не сірим-непомітним: «не
   відстежується» — це відповідь, а не виноска. */
.metric-hint {
  font-size: 12px;
  color: var(--text-secondary);
  font-style: italic;
  margin-top: 2px;
}

/* Уточнення замовника 2026-08-23: студент бачить ціль на день прапорцем біля
   уроку. На відміну від інструкторського .goal-flag це НЕ кнопка - студент
   ціль не ставить і не знімає, тож ані курсора, ані станів наведення. */
.lesson-goal-flag {
  flex: 0 0 auto;
  font-size: 15px;
  line-height: 1;
  cursor: default;
}
