/* ═══════════════════════════════════════════════════════════════
   The Content tab of the session workspace, /editor/session/:id?tab=content
   (src/views/editor/EditorContentTab.vue).

   Renamed from `admin-session-management.css` when the screen it styles moved
   out of /admin and into the editor (run 1, track 4a). Its `csm-` selectors
   still carry the old screen's prefix, deliberately: renaming those too would
   have made a 3879-line MOVE read as a rewrite, and the move had to stay
   reviewable.

   The `admin` prefix is load-bearing and is not a naming preference.
   scripts/bundle-css.mjs partitions the public and admin CSS bundles on
   `href.startsWith('/css/admin')` alone, so a name without it would ship this
   whole sheet to every marketing visitor - silently, with a green build.

   Two guards stand either side of the <link> in index.html, and between them
   they refuse every half-way state: bundle-css.mjs throws
   `bundle-css: missing <file>` for a linked sheet that does not exist, and
   tests/cssBraceBalance.spec.ts refuses a sheet with no rules in it. So this
   file cannot be emptied into a pointer stub and cannot be deleted while the
   link stands. Move the link and the file together, or neither.

   The teaching-plan screen: a course content pane beside the list of four-hour
   session cards.

   DIRECTION: this sheet declares none, and that is deliberate.

   Every physical rule it used to carry is a LOGICAL one - `padding-inline-start`,
   `inset-inline-*`, `margin-inline-end`, `text-align: start` - so the whole
   sheet lays out correctly in either direction. While the screen's copy was
   Hebrew it carried four `direction: rtl` declarations marked `THE RTL ISLAND`,
   because Hebrew laid out left-to-right is not a degraded screen, it is a broken
   one. The copy is English now (decision F10) and all four are gone, along with
   the `dir="rtl"` on the toast: the tab inherits the direction of its host.

   THAT HOST IS `.edt-shell` (public/css/admin-editor.css), which declares
   `direction: ltr; text-align: left` - and it has to, because `direction: rtl`
   is GLOBAL on this site (public/css/global.css). So this screen is LTR because
   the editor page is, and rendering `.csm-page` anywhere outside that shell
   would put an English screen back into the site's RTL default. It has one
   route, `/editor/session/:id`, and that route is inside the shell.

   Everything here is prefixed `csm-`. The shared kit from admin.css
   (`.admin-page-header`, `.admin-toolbar`, `.admin-stat*`, `.empty-state`,
   `.form-*`, `.btn*`, `.badge`, `.modal*`, `.toast`) is reused rather than
   restated; only what is genuinely new to this screen lives below. The editor
   shell links the same admin bundle the panel does, so that kit is here too.
   ═══════════════════════════════════════════════════════════════ */

.csm-page {
  /* The three states of a session against its budget. They are read in the
     bar, in the meter line, in the picker and in the proposal, so they are
     declared once here rather than five times below. */
  --csm-under: var(--primary-color, #0071e3);
  --csm-exact: #2e7d32;
  --csm-over: var(--alert-color, #be123c);
  --csm-warm: #b26a00;

  --csm-surface: #fff;
  --csm-surface-sunken: #f7f9fc;
  --csm-border: #e4eaf3;
  --csm-border-strong: #cfdcee;
  --csm-ink: #1f2430;
  --csm-muted: #6b7280;
  --csm-radius: 14px;
}

/* THE BORROWED KIT BRINGS ITS OWN DIRECTION WITH IT, and this is the whole of
   what removing the RTL island exposed.

   `admin.css` was written for `/admin`, which is RTL top to bottom
   (`.admin-layout { direction: rtl }`), so several pieces of the shared kit
   declare `direction: rtl` for THEMSELVES rather than inheriting it. Inside
   `.edt-shell` those pieces are the only things left laying out right-to-left,
   and the two below carry nothing but English chrome: the summary strip
   reversed its six stats and moved the full stop of its sub-label to the wrong
   end, and the status selects right-aligned `Draft / In progress / Ready`.

   NOT the text inputs, deliberately. `.form-input` / `.form-textarea` are
   RTL in admin.css too, and they stay that way here: what a reader types into
   the search box, the session title and the prep notes is HEBREW, because that
   is the language the curriculum is written in. Chrome is English; content is
   whatever the course is. The same line this screen's copy is drawn along.

   Compound with `.csm-page` on purpose - never bare. Every admin stylesheet is
   concatenated into one bundle that every admin page loads whole, so a bare
   `.admin-stats-banner` rule here would re-lay-out the stats strip of every
   screen in the panel. That is exactly the defect review round 1 found in
   `.edt-page`. */
.csm-page .admin-stats-banner,
.csm-page .form-select {
  direction: ltr;
  /* `text-align` does NOT follow `direction`, and the shared kit sets both:
     admin.css gives `.form-input, .form-select, .form-textarea` a matching
     `text-align: right` for the RTL panel it was written for. Resetting the
     direction alone leaves that alignment behind, so the UA moves the select's
     chevron to the right while the label is still pushed hard against it -
     "Draft" / "In progress" / "Ready" crammed under the arrow with the left
     half of a 6.5rem control empty. That is the same mis-alignment this pair of
     rules exists to remove, mirrored. `start` rather than `left` so the two
     declarations cannot disagree if this screen is ever hosted RTL again. */
  text-align: start;
}

/* Seven `flex: 1` stats spread over a 2200px workspace put a metre of screen
   between the first number and the last, with the divider hairlines floating in
   the middle of nothing. Capped and left-anchored instead. Compound with
   `.csm-page` for the reason spelled out above: the bare class belongs to every
   screen in the panel. */
.csm-page .admin-stats-banner {
  max-inline-size: 1100px;
}

.csm-header {
  flex-wrap: wrap;
  gap: 0.75rem;
}

.csm-toolbar {
  flex-wrap: wrap;
  justify-content: flex-start;
}

/* ── Course selector ─────────────────────────────────────────
   The two course names are the brand logo components, so the tab is sized
   from its content rather than from a text length. */

.csm-course-tabs {
  display: inline-flex;
  align-items: stretch;
  background: #f3f4f6;
  border: 1px solid #e2e4e8;
  border-radius: 10px;
  padding: 3px;
  gap: 2px;
}

.csm-course-tab {
  border: none;
  background: transparent;
  padding: 0.45rem 0.9rem;
  border-radius: 7px;
  font-size: 0.9rem;
  font-weight: 400;
  color: #555;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  transition: background 0.15s, box-shadow 0.15s, color 0.15s;
}

.csm-course-tab:hover {
  background: rgba(255, 255, 255, 0.6);
  color: #1a1a1a;
}

.csm-course-tab.is-active {
  background: #fff;
  color: #1a1a1a;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}

.csm-course-tab:focus-visible {
  outline: 2px solid var(--primary-color, #0071e3);
  outline-offset: 1px;
}

/* Held while a proposal is being computed. That answer belongs to the course
   that asked for it, so the strip says it is busy rather than letting a click
   land on another track while a proposal for this one is still on its way. */
.csm-course-tabs.is-pending {
  cursor: progress;
}

.csm-course-tab:disabled {
  cursor: progress;
  opacity: 0.55;
}

.csm-course-tab:disabled:hover {
  background: transparent;
  color: #555;
}

.csm-course-tab.is-active:disabled:hover {
  background: #fff;
  color: #1a1a1a;
}

/* The logo marks sit on their own baseline; nudge them onto the tab's. */
.csm-course-tab .logo-10x-inline svg,
.csm-course-tab .logo-10x-architect-inline svg {
  vertical-align: -0.18em;
}

/* ── The target figure, edited in place inside the stat strip ── */

.csm-stat-edit {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-inline-start: 0.4rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--csm-border-strong);
  border-radius: 999px;
  background: #fff;
  color: var(--csm-muted);
  font-size: 0.66rem;
  cursor: pointer;
}

.csm-stat-edit:hover {
  color: var(--primary-color, #0071e3);
  border-color: var(--primary-color, #0071e3);
}

.csm-target-input {
  width: 104px;
  text-align: center;
  font-size: 1.4rem;
  padding: 0.2rem 0.4rem;
}

.csm-target-actions {
  display: inline-flex;
  gap: 0.15rem;
  margin-top: 0.35rem;
}

/* A reading that has left the comfortable range stops being decorative. */
.admin-stat.csm-stat--alert .admin-stat-label {
  color: var(--csm-over);
}

/* Content another track is still holding: a warning, not yet a refusal. The
   `csm-stat--alert` variant is used instead the moment one of those meetings is
   marked ready, because then the assignment is refused rather than merely
   costly. */
.admin-stat.csm-stat--held .admin-stat-label {
  color: var(--csm-warm);
}

/* ── Page body: content pane + session list ─────────────────── */

.csm-body {
  display: grid;
  grid-template-columns: minmax(315px, 405px) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}

.csm-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  color: var(--csm-over);
}

/* ── Content pane ───────────────────────────────────────────── */

.csm-pane {
  min-width: 0;
  background: var(--csm-surface);
  border: 1px solid var(--csm-border);
  border-radius: var(--csm-radius);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

/* Dropping an item back on the pane unassigns it, so the pane says so. */
.csm-pane.is-drop-target {
  border-color: var(--primary-color, #0071e3);
  box-shadow: 0 0 0 2px rgba(0, 113, 227, 0.15);
}

.csm-pane-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.8rem 1rem;
  border-bottom: 1px solid var(--csm-border);
  background: var(--csm-surface-sunken);
}

.csm-pane-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--csm-ink);
}

.csm-pane-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border: none;
  background: transparent;
  padding: 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--csm-ink);
  cursor: pointer;
}

/* Two readings, side by side: what is free to place, and what another track is
   still holding. They were one number, and that number was wrong whenever a
   module had moved tracks without its placements. */
.csm-pane-badges {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.3rem;
}

.csm-backlog-badge {
  background: rgba(190, 18, 60, 0.09);
  color: var(--csm-over);
  white-space: nowrap;
}

.csm-backlog-badge.is-clear {
  background: #e8f5e9;
  color: var(--csm-exact);
}

.csm-held-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: rgba(178, 106, 0, 0.12);
  color: var(--csm-warm);
  white-space: nowrap;
}

/* A holder marked ready refuses the write outright, so it does not share the
   colour of one that merely loses the content. */
.csm-held-badge.is-ready {
  background: rgba(190, 18, 60, 0.09);
  color: var(--csm-over);
  box-shadow: inset 0 0 0 1px rgba(190, 18, 60, 0.3);
}

.csm-pane-body {
  padding: 0.85rem 1rem 1rem;
}

.csm-pane-controls {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.csm-search {
  width: 100%;
}

.csm-search input {
  width: 100%;
  /* Mirrors the shared `.admin-search` geometry for this RTL island: the
     magnifier belongs at the START of the field, which here is the right. */
  padding-inline-start: 30px;
  padding-inline-end: 28px;
}

.csm-search .fa-search {
  inset-inline-start: 10px;
  inset-inline-end: auto;
}

.csm-search .search-clear {
  inset-inline-start: auto;
  inset-inline-end: 6px;
}

.csm-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.82rem;
  color: var(--csm-muted);
  cursor: pointer;
}

.csm-pane-buttons {
  display: flex;
  gap: 0.4rem;
}

.csm-pane-hint {
  margin: 0.75rem 0 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--csm-muted);
}

.csm-pane-empty {
  padding: 2rem 0.5rem;
  font-size: 0.85rem;
}

.csm-tree {
  margin-top: 0.75rem;
}

.csm-module + .csm-module {
  margin-top: 0.5rem;
}

.csm-module-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.45rem 0.5rem;
  border: none;
  border-radius: 8px;
  background: var(--csm-surface-sunken);
  color: var(--csm-ink);
  font-size: 0.85rem;
  font-weight: 600;
  text-align: start;
  cursor: pointer;
}

.csm-module-head:hover {
  background: #eef3fa;
}

.csm-module-num {
  flex: 0 0 auto;
  min-width: 1.4rem;
  height: 1.4rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  background: #fff;
  border: 1px solid var(--csm-border-strong);
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--csm-muted);
}

.csm-module-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* A collapsed caret points RIGHT, the direction an LTR panel opens towards,
   and `.is-open` turns it down. Both halves flipped when the RTL island went;
   the glyph is chosen in the template, the rotation here. */
.csm-caret {
  flex: 0 0 auto;
  width: 1rem;
  color: var(--csm-muted);
  font-size: 0.7rem;
  transition: transform 0.15s;
}

.csm-caret.is-open {
  transform: rotate(90deg);
}

/* The deck panel is BORROWED from admin-class-room.css, which draws its caret
   for an RTL host (`rotate(-90deg)` over a chevron pointing inline-start). That
   sheet is shared with the cohorts screen, so the override lives here, in the
   sheet that owns this page. Compound with `.csm-page` on purpose: a bare
   `.acr-deck-caret` rule in an editor sheet is GLOBAL over every admin page,
   which is exactly the defect review round 1 found in `.edt-page`. */
.csm-page .acr-deck-caret.is-open {
  transform: rotate(90deg);
}

.csm-caret-btn {
  border: none;
  background: transparent;
  padding: 0.15rem;
  cursor: pointer;
  line-height: 1;
}

.csm-chapters {
  padding-inline-start: 0.75rem;
  margin-top: 0.35rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.csm-chapter {
  min-width: 0;
}

.csm-chapter-head {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  padding: 0.38rem 0.45rem;
  border: 1px solid var(--csm-border);
  border-radius: 8px;
  background: #fff;
  font-size: 0.82rem;
  color: var(--csm-ink);
}

.csm-chapter-head[draggable='true'] {
  cursor: grab;
}

.csm-chapter-head:hover {
  border-color: var(--csm-border-strong);
}

.csm-chapter-head.is-dragging,
.csm-subject.is-dragging,
.csm-item.is-dragging {
  opacity: 0.45;
}

.csm-chapter-title {
  flex: 1 1 8rem;
  min-width: 0;
  font-weight: 500;
  overflow-wrap: anywhere;
}

.csm-grip {
  flex: 0 0 auto;
  color: #c3cbd8;
  font-size: 0.75rem;
  cursor: grab;
}

.csm-subjects {
  list-style: none;
  margin: 0.3rem 0 0.2rem;
  /* Logical, like every other indent in this sheet. As a physical
     `padding: 0 1rem 0 0` this landed on the RIGHT, which was the LEADING edge
     while the screen was an RTL island - so the subject rows read as nested
     under their chapter. With the island gone the page is LTR, the same rule
     puts the indent on the TRAILING edge, and the tree loses its hierarchy:
     every subject sits flush with its chapter heading and the gap hangs off the
     empty right. The sheet's own header claims every physical rule here is a
     logical one; this was the rule that was not. */
  padding-block: 0;
  padding-inline: 0 0;
  padding-inline-start: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.csm-subject {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin: 0;
  padding: 0.3rem 0.45rem;
  border: 1px solid transparent;
  border-radius: 7px;
  font-size: 0.79rem;
  background: #fff;
}

.csm-subject[draggable='true'] {
  cursor: grab;
}

/* Placed content recedes; unplaced content is the work still to do, so it
   is the thing that catches the eye. */
.csm-subject.is-placed {
  color: #98a2b3;
  background: #fbfcfe;
}

.csm-subject.is-open {
  border-color: rgba(190, 18, 60, 0.22);
  background: rgba(190, 18, 60, 0.035);
}

.csm-subject-title {
  flex: 1 1 7rem;
  min-width: 0;
  overflow-wrap: anywhere;
}

.csm-tree-foot {
  margin: 0.85rem 0 0;
  text-align: center;
  font-size: 0.72rem;
  color: var(--csm-muted);
}

/* ── Hour chips, and the inline editor behind them ──────────── */

.csm-hours-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: #eef3fa;
  border: 1px solid transparent;
  color: #46536b;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.csm-hours-chip.is-over {
  background: rgba(190, 18, 60, 0.08);
  color: var(--csm-over);
}

.csm-hours-chip.is-empty {
  background: rgba(178, 106, 0, 0.1);
  color: var(--csm-warm);
}

.csm-fit-icon {
  font-size: 0.62rem;
  opacity: 0.75;
}

.csm-hours-btn {
  cursor: pointer;
  font-family: inherit;
}

.csm-hours-btn:hover:not(:disabled) {
  border-color: var(--primary-color, #0071e3);
  color: var(--primary-color, #0071e3);
}

.csm-hours-btn:disabled {
  cursor: default;
}

.csm-hours-edit {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
}

.csm-hours-input {
  width: 84px;
  padding: 0.15rem 0.35rem;
  font-size: 0.75rem;
}

/* ── Small status pills, shared by the pane and the cards ───── */

.csm-pill {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.08rem 0.45rem;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 500;
  white-space: nowrap;
}

.csm-pill--open {
  background: rgba(190, 18, 60, 0.08);
  color: var(--csm-over);
}

.csm-pill--done {
  background: #e8f5e9;
  color: var(--csm-exact);
}

.csm-pill--part {
  background: rgba(178, 106, 0, 0.1);
  color: var(--csm-warm);
}

.csm-pill--placed {
  background: #eef3fa;
  color: #5b6b85;
}

.csm-pill--warn {
  background: rgba(178, 106, 0, 0.1);
  color: var(--csm-warm);
}

/* ── Held by another track ───────────────────────────────────
   The marker that replaces a false "לא משובץ". Two states, and they must not
   read alike: a DRAFT holder will simply lose the subject when it is placed
   here, while a READY holder makes the placement fail. One is a cost, the other
   is a wall, so the wall carries the alert colour, a ring and a lock. */

.csm-pill--held {
  background: rgba(178, 106, 0, 0.12);
  color: var(--csm-warm);
  box-shadow: inset 0 0 0 1px rgba(178, 106, 0, 0.24);
}

.csm-pill--held-ready {
  background: rgba(190, 18, 60, 0.1);
  color: var(--csm-over);
  font-weight: 600;
  box-shadow: inset 0 0 0 1px rgba(190, 18, 60, 0.32);
}

/* The mirror image, read from the HOLDING side: a placement sitting in one of
   THIS course's meetings whose subject now belongs to another track. Same
   vocabulary as the held pills above, because it is the same fact seen from the
   other end - but neutral rather than alarmed: from here it is a leftover to
   release, not a wall. */
.csm-pill--stale {
  background: rgba(107, 114, 128, 0.1);
  color: #4b5563;
  box-shadow: inset 0 0 0 1px rgba(107, 114, 128, 0.24);
}

.csm-assign-btn {
  flex: 0 0 auto;
  margin-inline-start: auto;
}

/* ── Session list ───────────────────────────────────────────── */

/* TWO-UP ONCE THERE IS ROOM, rather than one card stretched the width of the
   pane. A card's actions carry `margin-inline-start: auto`, so on a 27" the
   status select ended up roughly a metre of screen from the lecture title it
   belongs to - the pair reads as unrelated at that distance.

   `auto-fit`, NOT `auto-fill`: `auto-fill` keeps an empty track reserved, which
   holds a lone card at half width beside a gap. `auto-fit` collapses the empty
   track and lets one card fill the row. The drop-target geometry is measured
   per card and is unaffected by either. */
.csm-sessions {
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(520px, 1fr));
  gap: 0.9rem;
}

.csm-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 3rem 1.5rem;
  background: var(--csm-surface);
  border: 1px dashed var(--csm-border-strong);
  border-radius: var(--csm-radius);
  color: var(--csm-muted);
}

.csm-empty-icon {
  font-size: 1.8rem;
  color: #c3cbd8;
}

.csm-empty-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 500;
  color: var(--csm-ink);
}

.csm-empty-body {
  margin: 0;
  max-width: 34rem;
  font-size: 0.85rem;
  line-height: 1.6;
}

.csm-empty-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 0.6rem;
}

.csm-session {
  position: relative;
  min-width: 0;
  padding: 0.85rem 1rem 0.95rem;
  background: var(--csm-surface);
  border: 1px solid var(--csm-border);
  border-inline-start: 3px solid var(--csm-under);
  border-radius: var(--csm-radius);
  box-shadow: var(--shadow-sm);
}

.csm-session.is-exact {
  border-inline-start-color: var(--csm-exact);
}

.csm-session.is-over {
  border-inline-start-color: var(--csm-over);
}

.csm-session.is-overflow {
  background: linear-gradient(180deg, rgba(190, 18, 60, 0.035) 0%, #fff 40%);
}

.csm-session.is-drop-target {
  border-color: var(--primary-color, #0071e3);
  box-shadow: 0 0 0 2px rgba(0, 113, 227, 0.14);
}

/* The card this tab was opened through (`aria-current` in the template).
   A quiet wash and a firmer hairline, NOT the drop-target ring and not a fill
   colour: the whole point of this screen is dragging between cards, and a
   permanent marker that looks like the transient "you are about to drop here"
   one would make the drag unreadable. Both the fill state (the inline-start
   bar) and the drop ring still read over it. */
.csm-session.is-current {
  background: linear-gradient(180deg, rgba(0, 113, 227, 0.05) 0%, var(--csm-surface) 55%);
  border-color: var(--csm-border-strong);
}

/* A marked card that is ALSO in overflow keeps the overflow wash, and one being
   dropped onto keeps the drop ring: a warning and a live gesture both outrank a
   bookmark. Declared after it at equal specificity, in that order, on purpose. */
.csm-session.is-current.is-overflow {
  background: linear-gradient(180deg, rgba(190, 18, 60, 0.035) 0%, #fff 40%);
}

.csm-session.is-current.is-drop-target {
  border-color: var(--primary-color, #0071e3);
}

.csm-session-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.csm-session-num {
  flex: 0 0 auto;
  width: 1.9rem;
  height: 1.9rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 9px;
  background: var(--csm-surface-sunken);
  border: 1px solid var(--csm-border-strong);
  /* Solid ink, never the brand gradient: this numeral is a weight-500
     figure and the gradient only reads cleanly through light letterforms. */
  color: var(--primary-color, #0071e3);
  font-size: 0.85rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.csm-session-title {
  flex: 1 1 10rem;
  min-width: 0;
  margin: 0;
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--csm-ink);
  overflow-wrap: anywhere;
}

.csm-badge-overflow {
  background: rgba(190, 18, 60, 0.1);
  color: var(--csm-over);
}

.csm-status--draft {
  background: #eef1f6;
  color: #5b6b85;
}

.csm-status--in_progress {
  background: rgba(178, 106, 0, 0.12);
  color: var(--csm-warm);
}

.csm-status--ready {
  background: #e8f5e9;
  color: var(--csm-exact);
}

.csm-session-actions {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  margin-inline-start: auto;
}

.csm-status-select {
  width: auto;
  min-width: 6.5rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.75rem;
}

/* ── The four-hour meter ────────────────────────────────────── */

.csm-meter {
  margin-top: 0.7rem;
}

.csm-bar {
  height: 8px;
  border-radius: 999px;
  background: #eef1f6;
  overflow: hidden;
}

.csm-bar-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--csm-under);
  transition: width 0.2s ease;
}

.csm-bar.is-exact .csm-bar-fill {
  background: var(--csm-exact);
}

.csm-bar.is-over .csm-bar-fill {
  /* A full bar cannot show how far past the budget the session is, so the
     stripes say "this ran out" and the meter line says by how much. */
  background: repeating-linear-gradient(
    135deg,
    var(--csm-over) 0 6px,
    rgba(190, 18, 60, 0.65) 6px 12px
  );
}

.csm-meter-read {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  margin-top: 0.35rem;
  font-size: 0.76rem;
  color: var(--csm-muted);
}

.csm-meter-num {
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--csm-ink);
  font-variant-numeric: tabular-nums;
}

.csm-meter-of {
  white-space: nowrap;
}

.csm-meter-flag {
  margin-inline-start: auto;
  font-weight: 500;
}

.csm-meter-flag.is-over {
  color: var(--csm-over);
}

.csm-meter-flag.is-exact {
  color: var(--csm-exact);
}

.csm-flags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.55rem;
}

.csm-session-empty {
  margin-top: 0.7rem;
  padding: 1.1rem;
  border: 1px dashed var(--csm-border-strong);
  border-radius: 10px;
  text-align: center;
  font-size: 0.8rem;
  color: var(--csm-muted);
}

/* A meeting with no content OF THIS COURSE that is still holding another
   track's placements. It is not the empty state and must not read as one: the
   rows it holds are listed immediately below, so the box points down rather
   than inviting a drop. */
.csm-session-empty.is-stale-only {
  border-style: solid;
  border-color: rgba(178, 106, 0, 0.28);
  background: rgba(178, 106, 0, 0.05);
  color: #6b5220;
  text-align: start;
}

/* ── A session's content, in stored order ─────────────────────
   The boxes are RUNS: consecutive rows that teach the same chapter. Reading a
   card top to bottom gives the order the server holds, which is the order the
   drop indicator promised. A chapter taught in two stretches gets two boxes,
   and the second one says so - the card never re-sorts the plan to look tidy. */

.csm-groups {
  margin-top: 0.7rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.csm-group {
  border: 1px solid var(--csm-border);
  border-radius: 10px;
  overflow: hidden;
}

/* A chapter resuming after another one was taught in between. */
.csm-group.is-continued {
  border-style: dashed;
}

.csm-group.is-continued .csm-group-title {
  font-weight: 400;
  color: var(--csm-muted);
}

.csm-group-head {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
  padding: 0.35rem 0.6rem;
  background: var(--csm-surface-sunken);
  font-size: 0.76rem;
}

.csm-group-title {
  flex: 1 1 8rem;
  min-width: 0;
  font-weight: 600;
  color: var(--csm-ink);
  overflow-wrap: anywhere;
}

.csm-group-hours {
  color: var(--csm-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.csm-items {
  list-style: none;
  margin: 0;
  padding: 0;
}

.csm-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin: 0;
  padding: 0.35rem 0.6rem;
  font-size: 0.8rem;
  border-top: 1px solid var(--csm-border);
}

.csm-item:first-child {
  border-top: none;
}

.csm-item[draggable='true'] {
  cursor: grab;
}

.csm-item:hover {
  background: #fbfcfe;
}

/* The drop indicator. Drawn on dragover, acted on only in the drop event. */
.csm-item.is-drop-before::before,
.csm-item.is-drop-after::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  height: 2px;
  background: var(--primary-color, #0071e3);
}

.csm-item.is-drop-before::before {
  top: -1px;
}

.csm-item.is-drop-after::after {
  bottom: -1px;
}

.csm-item-title {
  flex: 1 1 8rem;
  min-width: 0;
  overflow-wrap: anywhere;
}

.csm-item-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.1rem;
  margin-inline-start: auto;
}

/* ── Placements whose subject moved to another track ──────────
   NOT this course's content, and the card has to say so without a sentence.
   So the block sits apart from the chapter groups, wears a warm dashed frame
   rather than the groups' solid one, and its rows carry no hours chip, no grip
   and no reorder arrows - nothing that would suggest they take part in the
   plan or in the hours bar above. The only control on them is release. */

.csm-stale {
  margin-top: 0.7rem;
  border: 1px dashed rgba(178, 106, 0, 0.42);
  border-radius: 10px;
  background: rgba(178, 106, 0, 0.04);
  overflow: hidden;
}

.csm-stale-head {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
  padding: 0.4rem 0.6rem;
  background: rgba(178, 106, 0, 0.08);
  font-size: 0.76rem;
}

.csm-stale-icon {
  color: var(--csm-warm);
}

.csm-stale-title {
  flex: 1 1 8rem;
  min-width: 0;
  font-weight: 600;
  color: #6b5220;
  overflow-wrap: anywhere;
}

.csm-stale-release-all {
  flex: 0 0 auto;
  padding: 0.2rem 0.55rem;
  font-size: 0.72rem;
}

.csm-stale-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.csm-stale-item {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin: 0;
  padding: 0.35rem 0.6rem;
  font-size: 0.8rem;
  border-top: 1px dashed rgba(178, 106, 0, 0.28);
}

.csm-stale-item-title {
  flex: 1 1 8rem;
  min-width: 0;
  color: var(--csm-muted);
  overflow-wrap: anywhere;
}

.csm-stale-release {
  margin-inline-start: auto;
}

.csm-stale-note {
  margin: 0;
  padding: 0.4rem 0.6rem 0.5rem;
  border-top: 1px dashed rgba(178, 106, 0, 0.28);
  font-size: 0.74rem;
  line-height: 1.6;
  color: #6b5220;
}

.csm-notes {
  display: flex;
  align-items: flex-start;
  gap: 0.45rem;
  margin: 0.7rem 0 0;
  padding: 0.5rem 0.65rem;
  background: rgba(178, 106, 0, 0.06);
  border-radius: 9px;
  font-size: 0.78rem;
  line-height: 1.6;
  color: #5c4a25;
  white-space: pre-wrap;
}

.csm-materials {
  list-style: none;
  margin: 0.55rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.csm-materials li {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  font-size: 0.76rem;
  color: var(--csm-muted);
}

.csm-materials a {
  color: var(--primary-color, #0071e3);
  text-decoration: none;
  word-break: break-all;
}

.csm-materials a:hover {
  text-decoration: underline;
}

.csm-add-tail {
  align-self: flex-start;
  border-style: dashed;
}

/* ── Modals: editor, picker, proposal ───────────────────────── */

/* The panel takes focus itself only as a fallback, when it holds no control of
   its own to hand the keyboard to. It is a landing place, not a control, so it
   is not outlined for a mouse - but a keyboard reader still gets the ring. */
.csm-modal:focus {
  outline: none;
}

.csm-modal:focus-visible {
  outline: 2px solid var(--primary-color, #0071e3);
  outline-offset: 2px;
}

.csm-material-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 0.4rem;
}

.csm-material-label {
  flex: 1 1 40%;
  min-width: 0;
}

.csm-material-url {
  flex: 1 1 60%;
  min-width: 0;
}

.csm-picker-lede {
  margin: 0 0 0.9rem;
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--csm-muted);
}

.csm-picker-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.csm-picker-list li {
  margin: 0;
}

.csm-picker-option {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--csm-border);
  border-radius: 10px;
  background: #fff;
  text-align: start;
  font-size: 0.85rem;
  cursor: pointer;
}

.csm-picker-option:hover {
  border-color: var(--primary-color, #0071e3);
  background: rgba(0, 113, 227, 0.04);
}

.csm-picker-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

.csm-picker-hours {
  flex: 0 0 auto;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--csm-under);
  white-space: nowrap;
}

.csm-picker-hours.is-exact {
  color: var(--csm-exact);
}

.csm-picker-hours.is-over {
  color: var(--csm-over);
}

.csm-picker-footer {
  flex-wrap: wrap;
  gap: 0.4rem;
}

.csm-proposal-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  padding: 0.6rem 0.8rem;
  margin-bottom: 0.8rem;
  background: var(--csm-surface-sunken);
  border-radius: 10px;
  font-size: 0.8rem;
  color: var(--csm-muted);
}

.csm-proposal-stats strong {
  font-weight: 600;
  color: var(--csm-ink);
  font-variant-numeric: tabular-nums;
}

.csm-proposal-stats .is-alert strong {
  color: var(--csm-over);
}

.csm-proposal-note {
  margin: 0 0 0.5rem;
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--csm-muted);
}

.csm-proposal-note.is-warn {
  color: var(--csm-warm);
}

/* The un-confirmable refusal: another track's prepared meeting holds this
   content. Read louder than a warning because no button on this screen can
   answer it - the fix is in the curriculum editor. */
.csm-proposal-block {
  margin: 0 0 0.7rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid rgba(190, 18, 60, 0.28);
  border-radius: 10px;
  background: rgba(190, 18, 60, 0.04);
}

.csm-proposal-block-lede {
  margin: 0;
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--csm-over);
}

.csm-proposal-block-lede i {
  margin-inline-end: 0.35rem;
}

.csm-proposal-block-list {
  margin: 0.4rem 0 0;
  padding-inline-start: 1.1rem;
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--csm-ink);
}

/* The way out, set apart from the refusal above it: the refusal is what
   happened, this is what to do about it. */
.csm-proposal-block-remedy {
  margin: 0.5rem 0 0;
  padding-top: 0.45rem;
  border-top: 1px solid rgba(190, 18, 60, 0.18);
  font-size: 0.78rem;
  line-height: 1.6;
  font-weight: 600;
  color: var(--csm-ink);
}

.csm-proposal-list {
  list-style: none;
  margin: 0.6rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.csm-proposal-session {
  margin: 0;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--csm-border);
  border-radius: 10px;
}

.csm-proposal-session.is-overflow {
  border-color: rgba(190, 18, 60, 0.28);
  background: rgba(190, 18, 60, 0.03);
}

.csm-proposal-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 0.35rem;
}

.csm-proposal-items {
  list-style: none;
  margin: 0;
  /* Logical, for the same reason as `.csm-subjects` above: as a physical
     right-padding this indent was on the leading edge under RTL and moved to
     the trailing one when the island went. */
  padding: 0;
  padding-inline-start: 0.3rem;
  font-size: 0.78rem;
  color: var(--csm-ink);
}

.csm-proposal-items li {
  margin: 0 0 0.15rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.csm-proposal-chapter {
  color: var(--csm-muted);
  font-size: 0.72rem;
}

.csm-toast {
  text-align: start;
}

/* ── Tablet: the two panes stack, content first ─────────────── */

@media (min-width: 1025px) {
  /* Only worth pinning when there is a column to pin it beside. The admin
     shell scrolls `.admin-main`, not the document, which is what makes a
     sticky offset inside it behave. */
  .csm-pane {
    position: sticky;
    top: 0.5rem;
    max-height: calc(100vh - 7rem);
    overflow-y: auto;
  }
}

@media (max-width: 1024px) {
  .csm-body {
    grid-template-columns: minmax(0, 1fr);
  }

  .csm-header {
    align-items: flex-start;
  }
}

/* ── Phone: the content pane becomes a collapsible sheet ────── */

@media (max-width: 767px) {
  .csm-pane.is-collapsed .csm-pane-body {
    display: none;
  }

  .csm-pane-head {
    padding: 0.7rem 0.8rem;
  }

  .csm-toolbar {
    width: 100%;
  }

  .csm-course-tabs {
    width: 100%;
  }

  .csm-course-tab {
    flex: 1 1 0;
    justify-content: center;
    padding-inline: 0.4rem;
  }

  .csm-session {
    padding: 0.75rem 0.7rem 0.85rem;
  }

  .csm-session-actions {
    width: 100%;
    margin-inline-start: 0;
    justify-content: flex-end;
    flex-wrap: wrap;
  }

  .csm-status-select {
    flex: 1 1 100%;
    min-width: 0;
  }

  .csm-item-actions {
    width: 100%;
    margin-inline-start: 0;
    justify-content: flex-end;
  }

  .csm-target-input {
    width: 88px;
  }

  /* Nothing on this screen is a table, but a long untranslated identifier
     could still push the body sideways. Anything that overflows scrolls
     inside its own card, never the page. */
  .csm-groups,
  .csm-stale-list,
  .csm-proposal-list {
    overflow-x: auto;
  }
}
