/* The shared patterns, one implementation each. Ported from the canonical page named
   for each in plan/component-library.md, which is also where every value that holds
   sitewide is recorded. A page consumes these rather than carrying its own copy: the
   mockup's pages each held their own, which is the drift Phase 6 spent itself removing.

   Nothing page-specific belongs here. The palette is tokens.css and the page shell is
   base.css. */

/* ---- the card ----
   Canonical: any of the 11 pages carrying .form-box, which are identical. */

.form-box {
  background: rgba(241,231,219,0.04);
  border: 1px solid rgba(241,231,219,0.12);
  border-radius: 4px;
  padding: 26px 28px;
  margin-bottom: 22px;
}

/* ---- the selector box ----
   Two behaviours, both deliberate. A builder box stretches the full column width
   (sections, preptests, the RC drill page, the dashboard's Viewing as selector); a
   filter box shrinks to fit its own controls (course progress, the three analytics
   pages, notes, the wrong answer journal). The padding differs with the behaviour,
   which is how the canonical pages have it. */

.selector-box {
  background: #313d56;
  border: 1px solid rgba(241,231,219,0.12);
  border-radius: 4px;
  margin-bottom: 22px;
}
.selector-box-builder { padding: 26px 28px; }
.selector-box-filter { display: inline-block; padding: 16px 18px; }

/* The field label above a control inside either box. */
.col-label {
  font-variant: small-caps;
  font-size: 13px;
  letter-spacing: 0.06em;
  opacity: 0.55;
  margin-bottom: 12px;
}

/* ---- the tab bar ----
   Canonical: course-progress. Identical on all 8 pages that carry one. */

.tab-bar {
  display: flex;
  justify-content: center;
  gap: 6px;
  border-bottom: 1px solid rgba(241,231,219,0.2);
  margin-bottom: 32px;
}
.tab-btn {
  background: transparent;
  border: none;
  color: var(--color-text);
  opacity: 0.6;
  font-family: inherit;
  font-variant: small-caps;
  font-size: 16px;
  letter-spacing: 0.03em;
  padding: 12px 22px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.tab-btn:hover { opacity: 0.85; }
.tab-btn.active {
  opacity: 1;
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
  font-weight: 700;
}
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* ---- the message bubble ----
   Canonical: messages.html, which reuses the sitewide chat widget's own .chat-msg rules
   so a thread looks the same opened from either place. The instructor's message is
   washed and sits left; the student's is bubbled and sits right. Both surfaces here are
   light, which is what the messages page and the chat panel are. */

.chat-msg { font-size: 13px; line-height: 1.5; max-width: 78%; }
.chat-msg .chat-msg-from {
  font-variant: small-caps;
  font-size: 10.5px;
  opacity: 0.55;
  margin-bottom: 2px;
  letter-spacing: 0.03em;
}
.chat-msg.from-instructor {
  background: rgba(93,159,209,0.18);
  border-radius: 4px;
  padding: 6px 8px;
  margin: 0 -2px;
  align-self: flex-start;
}
.chat-msg.from-instructor .chat-msg-from {
  color: var(--color-icon-blue-light);
  opacity: 1;
}
.chat-msg.from-student {
  align-self: flex-end;
  text-align: right;
  background: #eef0f5;
  border-radius: 4px;
  padding: 6px 8px;
  margin: 0 -2px;
}
.chat-msg.from-student .chat-msg-from { text-align: right; }

/* The notes page draws a note's own thread plain black on white. A deliberate
   difference, applied on the thread container rather than by a second component. */
.chat-msg-plain .chat-msg.from-instructor,
.chat-msg-plain .chat-msg.from-student {
  background: transparent;
  color: #000;
}
.chat-msg-plain .chat-msg .chat-msg-from { color: inherit; }

/* A thread stacks its bubbles and lets each one take its own side. */
.chat-thread {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* ---- anything the browser draws itself ----
   The page's palette is cream on navy. A native dropdown's open list is drawn by the
   browser on its own white surface, and the page's colours do not reach it, so cream
   text lands on white and is unreadable. Every mockup page fixes this on its own select
   under its own class name — six names for one rule, and component-library.md never
   indexed it — so here it is written once, unscoped, and cannot be forgotten by a page
   that introduces a new dropdown.

   The same holds for any other control the browser renders itself. If one is added and
   its open state is drawn outside the page, give it explicit dark-on-white colours. */

select option {
  color: #000;
  background: #fff;
}

/* The select control itself, in the page's own treatment. The mockup calls this
   picker-select, control-select, viewing-as-select, type-select, qty-select and
   setup-field select; they are one control. */
select.form-select {
  background: transparent;
  color: var(--color-text);
  border: 1px solid rgba(241,231,219,0.35);
  border-radius: 2px;
  padding: 9px 10px;
  font-family: inherit;
  font-variant: small-caps;
  letter-spacing: 0.03em;
  font-size: 13.5px;
  opacity: 0.8;
}
select.form-select:hover { opacity: 1; }

/* ---- the formatting and attachment row ----
   One row, four buttons. The mockup carries three class families and six colour variants
   of the same markup; plan/component-library.md's finding is that only the surface
   colour, opacity and padding ever needed to differ, so those three are variables the
   component sets and everything else is written once here. */

.format-row {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: var(--format-pad, 8px 12px 0 14px);
  flex-shrink: 0;
}
.format-btn {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--format-colour, var(--color-text));
  opacity: var(--format-opacity, 0.4);
  padding: 5px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 3px;
}
.format-btn:hover { opacity: 1; color: var(--color-accent); }
.format-btn svg { width: var(--format-icon, 15px); height: var(--format-icon, 15px); }

/* ---- the attachment strip ----
   One chip per file, currentColor throughout so it reads correctly wherever it sits — the
   journal panel today, the chat widget and notes reply box once items 3 and 4 build them —
   with no per-surface variables to set. A pending chip (picked but not yet uploaded) has no
   link to give, so it prints as plain text rather than as an anchor. */

.attachment-strip { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
/* :empty never matches: the hidden file input is always a child, chips or not. :has() is what
   actually reads "no chip in here" and keeps working as pending chips are added and removed by
   components.js, not just at first render. Found in review, 2026-09-04. */
.attachment-strip:not(:has(.attachment-chip)) { display: none; margin-top: 0; }
.attachment-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 1px solid currentColor;
  border-radius: 12px;
  padding: 2px 8px;
  font-size: 11px;
  opacity: 0.85;
  max-width: 220px;
}
.attachment-chip a {
  color: inherit;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attachment-chip a:hover { text-decoration: underline; }
.attachment-chip.pending { font-style: italic; }
.attachment-chip button {
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  padding: 0;
  font-size: 12px;
  line-height: 1;
  opacity: 0.7;
  flex-shrink: 0;
}
.attachment-chip button:hover { opacity: 1; }

/* ---- the bookmark filter pill ----
   Canonical: sections, where the mockup calls it .drill-filter-btn. Renamed here because
   it also sits on course progress and preptests, which are not drilling pages. */

.bookmark-filter-btn {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 1px solid rgba(255,128,88,0.5);
  border-radius: 2px;
  color: var(--color-accent);
  opacity: 0.85;
  font-variant: small-caps;
  font-size: 11.5px;
  letter-spacing: 0.03em;
  padding: 7px 12px;
  cursor: pointer;
  font-family: inherit;
}
.bookmark-filter-btn svg { width: 13px; height: 13px; display: block; }
.bookmark-filter-btn:hover:not(:disabled) { opacity: 1; background: rgba(255,128,88,0.12); }
.bookmark-filter-btn:disabled { opacity: 0.3; cursor: default; }
.bookmark-filter-btn.active {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-accent-text);
  opacity: 1;
}
.bookmark-filter-btn.active svg { fill: var(--color-accent-text); }

/* ---- the written time dropdown ----
   Written rather than a native <select>: a browser draws its own option list and will not
   take the light background, dark option text and grey-bar-with-white-text rollover this
   needs. Two kinds from one implementation, a plain one that picks from its list and an
   editable one that is a text field also offering a list. Rendered by the time-dropdown Blade
   component and driven by delegated handlers in components.js, so the server can redraw the
   form it sits in without losing what the student typed. */

.time-dd { position: relative; flex: 1; min-width: 0; }
.time-dd-control {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  width: 100%;
  box-sizing: border-box;
  background: rgba(43,41,49,0.06);
  border: 1px solid rgba(43,41,49,0.25);
  border-radius: 3px;
  padding: 4px 6px;
  font-family: inherit;
  font-size: 12px;
  text-align: left;
  color: var(--color-accent-text);
  cursor: pointer;
}
/* The minutes control is typed into as well as picked from, so it is a real text field. */
input.time-dd-control { cursor: text; }
.time-dd-caret { font-size: 9px; opacity: 0.55; flex-shrink: 0; }
.time-dd.open .time-dd-control { border-color: var(--color-accent); }

/* Inert until the row above it has a value. The control itself also carries the disabled
   attribute, which is what keeps it out of the tab order: opacity and pointer-events only stop
   the mouse, and tabbing into a greyed field and typing into it was possible without it. */
.time-dd.disabled { opacity: 0.45; }
.time-dd.disabled .time-dd-control { cursor: default; }

.time-dd-list {
  position: absolute;
  z-index: 40;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  max-height: 190px;
  overflow-y: auto;
  background: var(--color-text);
  border: 1px solid rgba(43,41,49,0.3);
  border-radius: 3px;
  box-shadow: 0 6px 16px rgba(0,0,0,0.4);
}
.time-dd-list[hidden] { display: none; }
.time-dd-opt {
  padding: 4px 8px;
  font-size: 12px;
  color: var(--color-accent-text);
  cursor: pointer;
  white-space: nowrap;
}
/* A grey bar with white text over the list's dark-on-light. The value the control holds
   wears the same bar, but only until the pointer enters the list: two bars at once reads
   as two selections. */
.time-dd-opt:hover,
.time-dd-list:not(:hover) .time-dd-opt.active {
  background: var(--color-icon-blue-dark);
  color: #fff;
}

/* ---- the results table ----
   Canonical: sections, where the mockup calls it .questions-table (and .passages-table on
   rc-passages, which is the same implementation under a second name). Fixed column widths
   as explicit percentages, cells wrapping rather than the table scrolling sideways, small
   caps throughout, a darker header row.

   The scroll container is what makes the one case that does need it work: at 955px on
   lr-questions a cell cannot compress further, and only then does the scroll engage. */

.table-scroll { overflow-x: auto; }

table.results-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
  table-layout: fixed;
}
/* lr-questions and rc-questions run a step smaller. A recorded difference, not a fix. */
table.results-table-dense { font-size: 13px; }

table.results-table th,
table.results-table td {
  text-align: left;
  padding: 10px 12px;
  white-space: normal;
  overflow-wrap: break-word;
  vertical-align: top;
  border-bottom: 1px solid rgba(241,231,219,0.1);
  font-variant: small-caps;
}
table.results-table thead th {
  font-variant: small-caps;
  font-size: 12.5px;
  letter-spacing: 0.03em;
  opacity: 0.85;
  background: var(--color-modal-background);
}
table.results-table tbody tr:hover { background: rgba(241,231,219,0.03); }
table.results-table tbody tr[data-row-link] { cursor: pointer; }
table.results-table input[type="checkbox"] {
  accent-color: var(--color-accent);
  width: 14px;
  height: 14px;
  cursor: pointer;
}
table.results-table .results-table-empty td { opacity: 0.55; font-variant: normal; }

/* The 'stacked' cell type: a primary value with a smaller secondary line beneath it — a
   drill-set question row's own position in the set, with the question's real code
   underneath (stage 6 item 5). */
table.results-table .cell-sub { font-variant: normal; font-size: 11px; opacity: 0.5; margin-top: 2px; }

/* The bookmark column's own toggle button, one implementation for whatever table carries the
   column: a section's questions, a passage's, a PrepTest's — none built yet besides sections. */
.bookmark-toggle-btn {
  background: transparent;
  border: 1px solid rgba(255,128,88,0.5);
  border-radius: 2px;
  color: var(--color-accent);
  opacity: 0.7;
  padding: 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.bookmark-toggle-btn:hover { opacity: 1; background: rgba(255,128,88,0.12); }
.bookmark-toggle-btn.bookmarked { opacity: 1; background: rgba(255,128,88,0.18); }
.bookmark-toggle-btn.bookmarked svg { fill: var(--color-accent); }
.bookmark-toggle-btn svg { width: 13px; height: 13px; display: block; }
.bookmark-toggle-btn:disabled { opacity: 0.4; cursor: default; }

/* ---- the sort control ----
   A header button with an arrow. First click sorts descending. Also used on a plain
   Sort by row above a non-tabular list, which is why it is not scoped to a table. */

.col-sort-btn {
  background: transparent;
  border: none;
  color: var(--color-text);
  font-family: inherit;
  font-variant: small-caps;
  font-size: 12.5px;
  letter-spacing: 0.03em;
  opacity: 0.8;
  cursor: pointer;
  padding: 0;
}
.col-sort-btn:hover { opacity: 1; }
.col-sort-btn.sort-active { opacity: 1; color: var(--color-accent); }
.sort-arrow { font-size: 10px; }

/* ---- the table column filter ----
   A plain text button with a chevron in a column heading, over one shared dark checklist
   panel with an explicit apply step. Canonical: lr-questions. */

.col-filter { position: relative; }
.col-filter-btn {
  background: transparent;
  border: none;
  color: var(--color-text);
  font-family: inherit;
  font-variant: small-caps;
  font-size: 12.5px;
  letter-spacing: 0.03em;
  opacity: 0.8;
  cursor: pointer;
  padding: 0;
}
.col-filter-btn:hover { opacity: 1; }
.col-filter-btn.filter-active { opacity: 1; color: var(--color-accent); }

.col-filter-dropdown {
  display: none;
  position: fixed;
  background: var(--color-modal-background);
  border-radius: 3px;
  padding: 10px 14px;
  min-width: 190px;
  max-width: 280px;
  box-shadow: 0 8px 20px rgba(0,0,0,0.4);
  z-index: 50;
}
.col-filter-dropdown.open { display: block; }
.col-filter-dropdown-list {
  max-height: 240px;
  overflow-y: auto;
  white-space: normal;
}
.col-filter-dropdown label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-variant: normal;
  font-size: 13px;
  letter-spacing: normal;
  opacity: 1;
  padding: 4px 0;
  cursor: pointer;
  white-space: nowrap;
}
.col-filter-dropdown input[type="checkbox"] {
  accent-color: var(--color-accent);
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  cursor: pointer;
}

/* Shared by both panels: a footer row holding Cancel and OK side by side. Checking a
   box edits the draft only; OK is what applies it, Cancel discards it. */
.filter-panel-footer, .field-panel-footer {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
.filter-apply-btn {
  flex: 1;
  background: var(--color-accent);
  color: var(--color-accent-text);
  border: none;
  border-radius: 2px;
  padding: 8px 14px;
  font-family: inherit;
  font-variant: small-caps;
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.03em;
  cursor: pointer;
}
.filter-apply-btn:hover { opacity: 0.92; }
.filter-cancel-btn {
  flex: 1;
  background: transparent;
  color: var(--color-text);
  border: 1px solid rgba(241,231,219,0.35);
  border-radius: 2px;
  padding: 8px 14px;
  font-family: inherit;
  font-variant: small-caps;
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.03em;
  cursor: pointer;
}
.filter-cancel-btn:hover { opacity: 0.8; }
.field-panel-footer .filter-cancel-btn {
  color: #232830;
  border-color: rgba(35,40,48,0.25);
}

/* ---- the page-level filter field ----
   A bordered box showing the current selection, over a white checklist panel. Canonical:
   wrong-answer-journal. Milan preferred this treatment to the borderless text-and-chevron
   link it replaced. */

.field-select { position: relative; }
.field-select-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  background: transparent;
  color: var(--color-text);
  border: 1px solid rgba(241,231,219,0.35);
  border-radius: 2px;
  padding: 9px 10px;
  font-family: inherit;
  font-variant: small-caps;
  font-size: 13.5px;
  letter-spacing: 0.03em;
  opacity: 0.8;
  cursor: pointer;
}
.field-select-btn:hover { opacity: 1; }
.field-select-btn.filter-active {
  opacity: 1;
  color: var(--color-accent);
  border-color: var(--color-accent);
}
.field-select-btn .field-select-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.field-select-btn .nav-chevron { flex-shrink: 0; opacity: 0.7; }

.field-dropdown {
  display: none;
  position: fixed;
  background: #fff;
  border-radius: 3px;
  padding: 10px 14px;
  min-width: 200px;
  max-width: 300px;
  box-shadow: 0 8px 20px rgba(0,0,0,0.4);
  z-index: 50;
}
.field-dropdown.open { display: block; }
.field-dropdown-list { max-height: 260px; overflow-y: auto; white-space: normal; }
.field-dropdown label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  padding: 4px 0;
  cursor: pointer;
  white-space: nowrap;
  color: #232830;
}
.field-dropdown input[type="checkbox"] {
  accent-color: var(--color-accent);
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  cursor: pointer;
}
.field-dropdown .sub-group-label {
  font-size: 11px;
  letter-spacing: 0.03em;
  color: #232830;
  opacity: 0.6;
  font-variant: small-caps;
  padding: 6px 0 2px;
}
/* The optional "All" row (ResultsPanels.open's opts.allOption) and the "Select All" row
   (opts.selectAllOption): bold, separated by a rule, mutually exclusive with the
   individual checkboxes below it, which grey out and stop accepting clicks while it's
   checked. Both rows share the same .field-all-row class regardless of which panel they
   render in, so this needs a dark-panel copy alongside the white one below. */
.field-dropdown .field-all-row, .col-filter-dropdown .field-all-row {
  font-weight: 600;
  padding-bottom: 8px;
  margin-bottom: 4px;
}
.field-dropdown .field-all-row { border-bottom: 1px solid rgba(35,40,48,0.15); }
.col-filter-dropdown .field-all-row { border-bottom: 1px solid rgba(241,231,219,0.15); }
.field-dropdown .field-check-disabled, .col-filter-dropdown .field-check-disabled { opacity: 0.45; }
.field-dropdown .field-check-disabled input[type="checkbox"],
.col-filter-dropdown .field-check-disabled input[type="checkbox"] { pointer-events: none; }

/* ---- pagination ---- */

.table-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 14px;
  font-size: 13px;
  opacity: 0.85;
}
.table-pagination select {
  background: var(--color-modal-background);
  color: var(--color-text);
  border: 1px solid rgba(241,231,219,0.3);
  border-radius: 2px;
  padding: 5px 8px;
  font-family: inherit;
}
.pagination-controls { display: flex; align-items: center; gap: 10px; }
.pagination-controls button {
  background: transparent;
  border: 1px solid rgba(241,231,219,0.3);
  color: var(--color-text);
  border-radius: 2px;
  padding: 5px 10px;
  font-family: inherit;
  font-variant: small-caps;
  cursor: pointer;
}
.pagination-controls button:disabled { opacity: 0.3; cursor: not-allowed; }

/* ---- correct and incorrect ----
   A green check and a red cross as icons rather than words, in a row cell and in the
   Result column's own heading, which carries both and no text label. */

.correct-icon { display: inline-block; vertical-align: middle; }
.col-filter-btn[data-filter-icons] { display: inline-flex; align-items: center; gap: 2px; }

/* ---- the Progress chart pane ----
   Nine charts, one specification. The header carries the title left and any per-chart
   controls right; preptests' version is not a flex row, because its header has only one
   child and making it one moved the chart. A recorded difference, not a fix.

   The canvas height is fixed at every width, and 500px for the efficiency matrix. Only
   the width follows the column the pane sits in. */

.chart-pane-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 14px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}
.chart-pane-title-block {
  /* Reserves the breadcrumb's own space on every page, not only where one is currently
     showing, so the header doesn't change height across a drill (Milan's decision, stage 7
     item 1, D4 — matches lr-questions.html's own value; the two RC design pages omit this and
     visibly shift by it). */
  flex: 1 1 auto;
  min-width: 0;
  min-height: 62px;
}
.chart-pane-title {
  font-variant: small-caps;
  font-size: 15px;
  letter-spacing: 0.03em;
  opacity: 0.85;
  margin-bottom: 6px;
}
.chart-pane-controls { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

/* The optional breadcrumb under the title — preptests' own, once a chart point has opened that
   attempt's own section breakdown. Sits inside the title block rather than beside it, so a page
   with no breadcrumb (every page but preptests) renders exactly as it did before this existed. */
.breadcrumb {
  font-variant: small-caps;
  font-size: 13px;
  letter-spacing: 0.03em;
  opacity: 0.7;
  margin-top: 6px;
  min-height: 16px;
}
.breadcrumb:empty { margin-top: 0; min-height: 0; }
.breadcrumb .crumb-link { color: var(--color-accent); text-decoration: none; cursor: pointer; }
.breadcrumb .crumb-link:hover { text-decoration: underline; }
.breadcrumb .crumb-sep { opacity: 0.4; margin: 0 6px; }
.breadcrumb .crumb-current { color: var(--color-text); }

.chart-canvas-wrap { position: relative; height: 400px; }

/* ---- the averages row ----
   Below the chart, never beside it. */

.progress-averages-row { display: flex; justify-content: center; flex-wrap: wrap; gap: 70px; }
.progress-averages-row.multi-col { gap: 36px; }
.avg-block { text-align: center; }
.avg-label {
  font-variant: small-caps;
  font-size: 13px;
  letter-spacing: 0.06em;
  opacity: 0.55;
  margin-bottom: 8px;
}
.avg-value { font-variant: small-caps; font-size: 32px; }

/* The optional name/date sub-line preptests' Low and Top Accuracy carry (<x-averages-row>'s
   own [value, name, date] shape). No other page passes one, so this sits empty everywhere
   else. */
.avg-sub-name { font-size: 11px; opacity: 0.6; margin-top: 4px; }
.avg-sub-date { font-size: 11px; opacity: 0.5; margin-top: 1px; }

/* The metric toggle some chart headers carry. */
.toggle-group {
  display: flex;
  flex-shrink: 0;
  border: 1px solid rgba(241,231,219,0.3);
  border-radius: 3px;
  overflow: hidden;
  align-self: flex-start;
}
.toggle-btn {
  background: transparent;
  color: var(--color-text);
  border: none;
  border-right: 1px solid rgba(241,231,219,0.2);
  padding: 7px 14px;
  font-family: inherit;
  font-variant: small-caps;
  font-size: 12.5px;
  letter-spacing: 0.03em;
  opacity: 0.7;
  cursor: pointer;
}
.toggle-btn:last-child { border-right: none; }
.toggle-btn:hover { opacity: 1; }
.toggle-btn.active { background: var(--color-accent); color: var(--color-accent-text); opacity: 1; }

/* ---- the tab bar, inline ----
   A second behaviour of the same control, the way the selector box has two. The schedule
   puts its tabs at the left of a row it shares with the date nav, so the centring and the
   full-width rule underneath both come off; the indicator under the active tab stays. */

.tab-bar-inline {
  justify-content: flex-start;
  gap: 28px;
  border-bottom: none;
  margin-bottom: 0;
}
.tab-bar-inline .tab-btn { padding: 4px 2px 8px; margin-bottom: 0; }

/* ---- the item block ----
   Course content on a calendar or in a day panel, carrying its subject stripe. Two patterns
   plan/component-library.md indexes under Colour — the stripe, and the dashed amber-brown
   exercise treatment — on one block, since every block that takes the second also takes the
   first. Canonical: schedule.html; the dashboard's day panel uses the same block. */

.item-block {
  background: var(--color-modal-background);
  border: 1px solid rgba(241,231,219,0.2);
  border-radius: 3px;
  padding: 6px 8px;
  font-size: 12px;
  line-height: 1.35;
}
.item-block .name { display: block; }
.item-block .meta { display: block; font-size: 10.5px; opacity: 0.6; margin-top: 1px; }

/* The amber-brown exists because the sitewide coral is also the selected state on these
   pages, so a coral exercise block read as already selected. */
.item-block.exercise {
  border-style: dashed;
  border-color: rgba(156,122,74,0.75);
  background: rgba(156,122,74,0.18);
}

/* Qualified with .item-block rather than left as a bare .cat-X, to match .exercise's own
   specificity: both set a border shorthand, so a lower-specificity stripe loses the left
   edge to .exercise's border-color whichever comes later in the file. */
.item-block.cat-langlogic { border-left: 4px solid var(--color-category-langlogic); }
.item-block.cat-rc { border-left: 4px solid var(--color-category-rc); }
.item-block.cat-lr { border-left: 4px solid var(--color-category-lr); }
.item-block.cat-unknown { border-left: 4px solid rgba(241,231,219,0.35); }

/* Locked (stage 10 item 3) — a whole block, chapter or day row whose content sits outside
   the student's tier. Own look rather than a subject stripe or the exercise treatment,
   which is why item-block.blade.php drops both classes when locked rather than layering
   this on top of them. */
.item-block.is-locked { border-left: 4px solid var(--color-icon-blue-dark); color: var(--color-icon-blue-dark); }
.item-block.is-locked .meta { opacity: 1; }

/* A block standing for content in two subjects splits its stripe. A border cannot show two
   colours, so the stripe becomes a child element and the two colours are set inline from the
   subjects the block actually holds. */
.item-block.split { display: flex; padding: 0; overflow: hidden; }
.item-block.split .split-bar {
  width: 4px;
  flex-shrink: 0;
  background: linear-gradient(to bottom, var(--stripe-top) 50%, var(--stripe-bottom) 50%);
}
.item-block.split .split-content { padding: 6px 8px; min-width: 0; }

/* The small-caps header above a group of blocks, and the muted "1.2 - " numbering prefix
   that sits inside a heading rather than beside it. */
.eyebrow {
  font-variant: small-caps;
  font-size: 10.5px;
  letter-spacing: 0.06em;
  opacity: 0.5;
  margin: 8px 0 3px;
}
.eyebrow:first-child { margin-top: 0; }
.item-num { opacity: 0.55; }

/* ---- the day panel ----
   One day's work in full. Shared by the schedule and the dashboard; the schedule's own copy
   carries a minimum height so an empty day does not collapse, which is a deliberate
   difference recorded in plan/component-library.md. */

.day-panel {
  background: rgba(241,231,219,0.04);
  border: 1px solid rgba(241,231,219,0.35);
  border-radius: 4px;
  overflow: hidden;
}
.day-panel .col-head {
  padding: 10px 4px;
  font-variant: small-caps;
  font-size: 13px;
  text-align: center;
  opacity: 0.7;
  border-bottom: 1px solid rgba(241,231,219,0.35);
}
.day-panel .day-body { padding: 16px 16px 20px; }
.day-panel .chapter-group { margin-bottom: 20px; }
.day-panel .chapter-group:last-child { margin-bottom: 0; }
.day-panel .chapter-name { font-variant: small-caps; font-size: 15px; margin-bottom: 8px; }
.day-panel .rest-label {
  font-size: 13px;
  font-style: italic;
  opacity: 0.55;
  text-align: center;
  padding: 26px 0;
}

.day-row {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 6px;
  padding: 6px 8px;
}
.day-row:last-child { margin-bottom: 0; }
.day-row input[type="checkbox"] {
  accent-color: var(--color-accent);
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  cursor: pointer;
}
.day-row input[type="checkbox"]:disabled { cursor: default; opacity: 0.5; }
.day-row input[type="checkbox"].is-locked { cursor: not-allowed; opacity: 1; accent-color: var(--color-icon-blue-dark); }
.day-row .row-label {
  font-size: 12.5px;
  line-height: 1.35;
  color: inherit;
  text-decoration: none;
}
.day-row .row-label:hover { color: var(--color-accent); text-decoration: underline; }
.day-row .row-label.is-locked,
.day-row .row-label.is-locked:hover { color: var(--color-icon-blue-dark); text-decoration: none; cursor: not-allowed; }

/* ---- A STUDENT'S OWN EVENT ----
   plan/component-library.md, "A student's own event". The one block on these pages that
   inverts: the palette's cream with its near-black text, where every other block is dark on
   navy, plus a 4px accent-coral left edge in place of one of the three category stripes and no
   checkbox, since an event has nothing to complete. Milan's call, 2026-08-19, on both.

   What an inversion does not carry with it, and why the numbers below differ from the same
   labels elsewhere: an opacity that reads as muted against navy is not muted against cream but
   unreadable, so the 0.65 and 0.7 these labels wear on the dark surface are held at 0.75 here;
   and the accent coral every hover on these pages uses is 2:1 on cream, so Edit and Delete
   hover to full strength and an underline instead. */

.event-group { margin-bottom: 20px; }
.event-block {
  background: var(--color-text);
  color: var(--color-accent-text);
  border: 1px solid rgba(43,41,49,0.18);
  border-left: 4px solid var(--color-accent);
  border-radius: 3px;
  padding: 6px 8px;
  font-size: 12px;
  line-height: 1.35;
}

/* In the day panel: the time range, the title, the body and the two controls.
   Every block carries the same gap below it rather than the last one being zeroed, because
   what follows the last block is either the add control or the open form and both want the
   gap. A :last-of-type rule would stop matching the moment the form opens, the form being a
   div too. */
.day-panel .event-block { margin-bottom: 6px; }
.event-head { display: flex; align-items: baseline; gap: 8px; }
.event-time { font-size: 11.5px; opacity: 0.75; white-space: nowrap; flex-shrink: 0; }
.event-title { font-size: 12.5px; flex: 1; overflow-wrap: anywhere; }
.event-body { font-size: 11.5px; opacity: 0.75; margin-top: 3px; overflow-wrap: anywhere; }
.event-actions { display: flex; gap: 8px; flex-shrink: 0; }
.event-actions button {
  background: none;
  border: none;
  padding: 0;
  font-family: inherit;
  font-size: 10.5px;
  font-variant: small-caps;
  letter-spacing: 0.04em;
  color: var(--color-accent-text);
  opacity: 0.7;
  cursor: pointer;
}
.event-actions button:hover { opacity: 1; text-decoration: underline; }

/* The add control. Always present, on every day, since an event can go on any of them. It sits
   on the panel's own dark surface rather than on a block, so it stays light on navy. */
.event-add {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 5px 8px;
  background: none;
  border: 1px dashed rgba(241,231,219,0.3);
  border-radius: 3px;
  font-family: inherit;
  font-size: 11.5px;
  color: var(--color-text);
  opacity: 0.6;
  cursor: pointer;
}
.event-add:hover { opacity: 1; border-color: var(--color-accent); color: var(--color-accent); }

/* ---- the add/edit form, inline in the day panel ----
   Light with dark text, matching the blocks above it rather than the panel behind it, so the
   whole of what belongs to the student reads as one thing. */
.event-form {
  margin-top: 8px;
  padding: 10px;
  background: var(--color-text);
  color: var(--color-accent-text);
  border: 1px solid rgba(43,41,49,0.2);
  border-radius: 3px;
}
.event-form label {
  display: block;
  font-variant: small-caps;
  font-size: 10.5px;
  letter-spacing: 0.06em;
  opacity: 0.72;
  margin-bottom: 3px;
}
/* Named rather than matching every text input in the form: the minutes control is a text input
   too, and it is styled as a dropdown control below. */
.event-form input.event-input-title,
.event-form textarea {
  width: 100%;
  box-sizing: border-box;
  background: rgba(43,41,49,0.06);
  border: 1px solid rgba(43,41,49,0.25);
  border-radius: 3px;
  padding: 4px 6px;
  font-family: inherit;
  font-size: 12px;
  color: var(--color-accent-text);
}
.event-form input.event-input-title::placeholder,
.event-form textarea::placeholder { color: rgba(43,41,49,0.55); }
.event-form textarea { resize: vertical; min-height: 44px; }
.event-form .field { margin-bottom: 8px; }
/* Shown only after Save has been pressed with the title empty, so the button never just
   appears dead. */
.event-form .field.missing input { border-color: var(--color-accent); }
.event-form .missing-note { font-size: 10.5px; color: var(--color-accent-text); opacity: 0.8; margin-top: 3px; }

/* ---- the time row ----
   The three controls in it are the shared written time dropdown above; this is only how the
   row holds them. Capped in width because below 965px the day panel drops under the calendar
   and goes full width, where three controls holding an hour, a quarter-hour and AM would
   stretch to about 260px each. */
.event-time-fields { display: flex; gap: 6px; max-width: 340px; }

.event-form-actions { display: flex; gap: 8px; justify-content: flex-end; }
.event-form-actions button {
  font-family: inherit;
  font-size: 11.5px;
  padding: 4px 12px;
  border-radius: 3px;
  cursor: pointer;
}
.event-form-actions .save { background: var(--color-accent); color: var(--color-accent-text); border: 1px solid var(--color-accent); }
.event-form-actions .cancel { background: none; color: var(--color-accent-text); border: 1px solid rgba(43,41,49,0.3); opacity: 0.7; }
.event-form-actions .cancel:hover { opacity: 1; }

/* ---- the repeating "type info" row, and its single-panel role ----
   Values were unified in Phase 6 and are shared by both roles: 18px name, 20px content gap,
   22px between figures, 10px between action buttons. The two roles do NOT share padding,
   though component-library.md's one-sentence summary reads as if they did — the three
   analytics pages' own corrected mockups carry no .type-info-box rule at all, so their single
   summary panel is a plain .form-box (26px/28px padding, 22px margin-bottom); only the
   repeating-row role (sections, course progress, preptests, the two drill pages) wants it
   tight. So the tight values are scoped to .type-info-row, added alongside .type-info-box on
   a row's own wrapper (.section-row, .exercise-set-row, an RC .passage-row, and so on) rather
   than left on the bare class. Milan's decision, stage 7 item 1 planning, 2026-09-03. */
.type-info-row.type-info-box { padding: 14px 20px; margin-bottom: 10px; }
.type-info-row.type-info-box:last-child { margin-bottom: 0; }
.type-info-content { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.type-info-main { flex: 1 1 auto; min-width: 0; }
.type-info-eyebrow { font-variant: small-caps; font-size: 12.5px; opacity: 0.6; letter-spacing: 0.03em; margin-bottom: 4px; }
.type-info-name { font-variant: small-caps; font-size: 18px; overflow-wrap: break-word; }
.type-info-date {
  display: block;
  font-variant: normal;
  font-size: 12px;
  opacity: 0.55;
  margin-top: 3px;
  white-space: nowrap;
}
.type-info-stats { display: flex; gap: 22px; flex-shrink: 0; }
.type-info-stat { font-size: 13px; opacity: 0.75; text-align: right; }
.type-info-actions { display: flex; gap: 10px; flex-shrink: 0; align-items: center; }
.info-action-btn {
  background: transparent;
  border: 1px solid rgba(255,128,88,0.5);
  border-radius: 2px;
  color: var(--color-accent);
  opacity: 0.85;
  font-variant: small-caps;
  font-size: 11.5px;
  letter-spacing: 0.03em;
  padding: 7px 12px;
  text-decoration: none;
  font-family: inherit;
  cursor: pointer;
}
.info-action-btn:hover { opacity: 1; background: rgba(255,128,88,0.12); }

/* ---- the start button ----
   Sections and preptests' own Start (and the Continue Section/Continue Preptest anchor
   beside it) carried no rule at all — a bare, unstyled control next to every other page's
   fully-styled Generate Question Set. Matched to .generate-btn (lr-question-set.css,
   rc-question-set.css) here rather than duplicated per page, since sections.css and
   preptests.css both reach it. */
.start-btn {
  display: inline-block;
  background: var(--color-accent);
  color: var(--color-accent-text);
  border: none;
  border-radius: 2px;
  padding: 13px 26px;
  font-family: inherit;
  font-variant: small-caps;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.04em;
  text-decoration: none;
  cursor: pointer;
}
.start-btn:hover:not(:disabled) { opacity: 0.92; }
.start-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* The single-panel role's own two additions, stage 7 item 1: the empty-state line before a bar
   is clicked, and the clicked bar's own colour swatch. Wording is the generic line two of the
   three design pages carry (rc-passages.html, rc-questions.html) rather than lr-questions.html's
   own, which names a chart title that stops being true after the first drill — Milan's decision,
   same planning pass. */
.type-info-empty { font-size: 14px; opacity: 0.55; }
.type-info-swatch { width: 14px; height: 14px; border-radius: 3px; flex-shrink: 0; }

/* ---- the efficiency matrix ----
   A bubble chart with its legend beside it rather than through Chart.js's own legend, so the
   legend can carry a layout Chart.js doesn't offer. 500px canvas height at every width, where
   every other chart is 400px (component-library.md, "Layout at narrow widths"). Canonical:
   lr-questions.html. The legend's own width is the one deliberate LR-vs-RC difference —
   .matrix-legend-wide is LR's two-column 340px block; the bare class is RC's single column,
   sized for RC's own shorter label lists. */
.matrix-row { display: flex; gap: 30px; align-items: flex-start; margin-top: 20px; }
.matrix-legend-item { display: flex; align-items: center; gap: 9px; font-size: 12.5px; line-height: 1.3; }
.matrix-legend-swatch { width: 12px; height: 12px; border-radius: 2px; flex-shrink: 0; }
.matrix-chart-wrap { flex: 1 1 auto; min-width: 0; position: relative; height: 500px; }
.matrix-legend {
  flex: 0 0 200px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 24px;
}
.matrix-legend-wide {
  flex: 0 0 340px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 16px;
  padding-top: 24px;
}

/* ---- shared across the three analytics pages ----
   Moved out of lr-questions.css into the shared layer, stage 7 item 5, on Milan's
   authorization — the third exception this stage to "components.css is item 1's alone", after
   chart-pane.blade.php and components.js in item 3. Item 3's own docblock assumed .intro-text
   differed page to page (RC centred, LR not); reading the actual file found both already
   identical (centred, 780px, same margin), so nothing page-local survives for it at all. */
.intro-text {
  font-size: 14px;
  line-height: 1.65;
  opacity: 0.75;
  max-width: 780px;
  margin: 0 auto 34px;
  text-align: center;
}

/* The Questions tab's own summary box: "Filtered to <a>Clear</a>" once a filter is applied. */
.clear-link {
  color: var(--color-accent);
  text-decoration: none;
  cursor: pointer;
  margin-left: 8px;
}

.clear-link:hover {
  text-decoration: underline;
}

.questions-box {
  padding: 0;
  overflow: hidden;
}

/* The mockups keep .table-pagination inside the same card as the table, giving it a
   border-top separator instead of the shared component's own default margin-top gap. */
.questions-box .table-pagination {
  margin-top: 0;
  padding: 14px 16px;
  border-top: 1px solid rgba(241, 231, 219, 0.12);
}

/* ---- an RC passage row nested inside an expanded section ----
   Shared by sections._passage-row.blade.php, which stage 5 item 5 reuses directly for a
   PrepTest's own section-inside-a-test nesting rather than a third copy — moved here from
   sections.css, which preptests' own page never loaded. Recoloured to the RC category colour;
   everything else it needs (.type-info-box, .type-info-actions, .bookmark-toggle-btn) is already
   above. */
.passage-list-indent { margin-left: 18px; }
.passage-row-wrap { margin-bottom: 10px; }
.passage-row-wrap:last-child { margin-bottom: 0; }
.passage-row {
  cursor: pointer;
  border-color: rgba(95,174,158,0.3);
  transition: background 0.15s ease, border-color 0.15s ease;
}
.passage-row:hover,
.passage-row-wrap.expanded .passage-row {
  background: rgba(95,174,158,0.1);
  border-color: rgba(95,174,158,0.4);
}
.passage-row .type-info-name { color: var(--color-category-rc); }
.passage-row .info-action-btn {
  border-color: rgba(95,174,158,0.5);
  color: var(--color-category-rc);
}
.passage-row .info-action-btn:hover { background: rgba(95,174,158,0.12); }
.passage-questions { display: none; margin: -4px 0 14px; }
.passage-row-wrap.expanded .passage-questions { display: block; }

/* ---- the viewing-as bar ----
   Stage 9 item 1. One control, not two: the same "Viewing as" selector the dashboard's
   own mockup carries, moved up under the header so it renders identically on the
   dashboard and on every other headered page instead of being redrawn down in the
   content column (Milan, 2026-09-06). Shows on the dashboard always, since that is
   still the way in; everywhere else only while he is actually viewing a student
   (plan/feature-logic.md, "Viewing as a student"). The left accent border is the same
   "this part is Milan's" signal the Admin nav item already carries. */
.viewing-as-bar {
  background: #313d56;
  border-left: 3px solid var(--color-accent);
  padding: 12px 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}
.viewing-as-bar .viewing-as-label {
  font-variant: small-caps;
  font-size: 13.5px;
  letter-spacing: 0.03em;
  opacity: 0.85;
}
.viewing-as-bar .viewing-as-label strong { color: var(--color-text); opacity: 1; }
.viewing-as-bar select.form-select { width: 230px; }

/* ---- the register bar ----
   Stage 10 item 1. Same position and shape as the viewing-as bar above — a full-bleed
   strip directly under the header — but for a visitor: no accent-as-ownership border
   (that signal means "this part is Milan's", which a register prompt is not), and a
   call to action rather than a dropdown. The accent colour still appears, on the
   Register link itself, so the bar doesn't lose it entirely. */
.register-bar {
  background: #313d56;
  padding: 12px 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}
.register-bar-label {
  font-variant: small-caps;
  font-size: 13.5px;
  letter-spacing: 0.03em;
  opacity: 0.85;
}
.register-bar-cta {
  color: var(--color-accent);
  font-variant: small-caps;
  letter-spacing: 0.03em;
  font-size: 13.5px;
  font-weight: 600;
}
.register-bar-login {
  color: var(--color-text);
  opacity: 0.7;
  font-variant: small-caps;
  letter-spacing: 0.03em;
  font-size: 13.5px;
}
.register-bar-cta:hover, .register-bar-login:hover { opacity: 1; }

/* ---- the disabled-write treatment ----
   Stage 9 item 1 builds this once; item 3 applies it to every write control on all
   fifteen pages once Milan is viewing a student (plan/feature-logic.md, "Viewing as a
   student": "disabled rather than hidden"). Stage 10 item 1 broadens the condition that
   switches it on to ActingStudent::writesDisabled(), so the same class now also covers a
   visitor with no account (plan/access-tiers.md, "No account") — the class itself is
   unchanged. 0.45 opacity and a "no" cursor, decision 6,
   Milan 2026-09-06 — the value already used for the site's two multi-part controls
   (.time-dd.disabled, .field-check-disabled), rather than the fainter 0.3 the smaller
   single buttons use, which disappears against the dark ground on a bigger control.
   The title attribute carries decision 7's tooltip.

   This is presentation only, per progress.md's own stage 9 work list: "a disabled
   control is presentation and not access control" — item 3's own server-side refusal
   on every write route is the real boundary, not this class. It therefore carries no
   pointer-events rule: an earlier version set pointer-events: none on the element and
   its children, which also stops the element from ever receiving hover, so neither the
   cursor nor the title tooltip decision 7 asks for ever rendered — found in review.
   Whatever actually stops the click (the native disabled attribute, or a server-side
   refusal alone) is item 3's own choice per control; this class only has to look right
   next to it. */
/* !important on both: this is a state override that has to win regardless of whatever
   else a specific control's own stylesheet sets, including a rule with higher
   specificity than this class's own single class — found stage 9 item 5's own closing
   check, where `.sticky-note .note-bell-btn`'s own two-class base rule silently beat
   this on both properties, so the bell looked and behaved like an ordinary control
   while scoped even though its `disabled` attribute genuinely stopped the click. */
.is-view-disabled {
  opacity: 0.45 !important;
  cursor: not-allowed !important;
}

/* ---- the scoped-write refusal notice ----
   Item 3's own server-side refusal (RefuseScopedWrite) redirects an ordinary form post
   back to its page with this flash message, for the case where a control was reached
   through the browser's own tools rather than through .is-view-disabled above. It
   reuses questions.css's own .toast look (same fixed-bottom placement, dark background,
   fade transition) rather than inventing a second visual language for one message, but
   lives here rather than there since this one has to render on all fifteen pages and
   that one is scoped to the question interface alone. Shown already faded in — a form
   post is a full page load, so there is no JS moment to animate into, unlike the
   fetch-triggered version. */
.scoped-write-notice {
  position: fixed;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%);
  background: #1f2226;
  color: #fff;
  padding: 10px 18px;
  border-radius: 6px;
  font-size: 13px;
  z-index: 95;
  transition: opacity .25s ease;
}
