:root {
  --bg: #f6f7fb;
  --panel: #ffffff;
  --panel-2: #ffffff;
  --fg: #1f2330;
  --muted: #5b6472;
  --border: #d9dde6;
  --accent: #2563eb;
  --keyword: #1d4ed8;
  --string: #b45309;
  --number: #7c3aed;
  --comment: #6b7280;
  --builtin: #0d9488;
  --def: #b45309;
  --error: #b91c1c;
  --ok: #16a34a;
  /* Semantic status pairs (bg + fg) — formerly repeated as raw hex across
     badges, test summaries, messages, and similarity tints. Same values, named,
     so a future theme/dark-mode is a token swap. */
  --success-bg: #dcfce7; --success-fg: #166534;
  --warning-bg: #fef3c7; --warning-fg: #92400e;
  --danger-bg:  #fee2e2; --danger-fg:  #991b1b;
  --info-bg:    #dbeafe; --info-fg:    #1e40af;
  /* Radius scale — current values, named so a future retune/dark-mode is one
     place. 5px (writeup toolbar button) stays literal as a one-off. */
  --radius-sm: 6px; --radius-md: 8px; --radius-lg: 10px;
  --radius-pill: 999px; --radius-round: 50%;
  /* Spacing scale (2px base; --space-N == N*2 px). Mirrors the existing
     literals EXACTLY — zero visual change. Applied to padding/margin/gap only,
     not to widths/heights/insets/line-heights/border-widths. Odd-pixel one-offs
     (1/3/5/7/9px, used for borders + fine alignment) stay literal; snapping the
     whole UI to a strict 4px grid is a later, intentionally visual slice. */
  --space-1: 2px;  --space-2: 4px;  --space-3: 6px;  --space-4: 8px;
  --space-5: 10px; --space-6: 12px; --space-7: 14px; --space-8: 16px;
  --space-9: 18px; --space-12: 24px; --space-16: 32px; --space-20: 40px;
  /* App shell. --chrome-h is the vertical space the shell takes OUTSIDE the
     page body (just .content's own padding now that the topbar is gone); panes
     that want to fill the viewport subtract it. .has-banner raises it. */
  --sidebar-w: 232px; --rail-narrow-w: 56px; --chrome-h: 32px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  height: 100%;
}

/* `.topbar`, `.brand` and a bare `nav a` rule lived here, from the horizontal
   top navigation the rail replaced. They are gone. The `nav a` rule in
   particular was load-bearing in the wrong direction: written for a row of
   links, it put a 16px LEFT margin on every anchor inside any <nav> in the
   app, which is why the sidebar was first built as an <aside> to dodge it —
   trading a landmark for a workaround. It was also still indenting
   .chapter-nav's links for no reason. Removing it lets the rail be a real
   <nav>, and every surviving nav sets its own spacing. */
.inline-form { display: inline; margin: 0; }

/* Off-screen until focused, then a real button in the top-left. */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -100px;
  z-index: 100;
  padding: var(--space-4) var(--space-8);
  background: var(--panel);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  text-decoration: none;
  transition: top 0.1s;
}
.skip-link:focus { top: var(--space-4); }
/* The target takes focus programmatically; never draw a ring around the page. */
#main:focus { outline: none; }
.linkbutton {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  margin-left: var(--space-8);
  font-size: 14px;
  padding: 0;
}
.linkbutton:hover { color: var(--fg); }

.content { padding: var(--space-8); }

/* ---- App shell -----------------------------------------------------------
   A persistent rail plus the page. `.linkbutton` still carries a 16px left
   margin from the old topbar, hence the reset below. `minmax(0, 1fr)` and
   `min-width: 0` stop a wide child (a code block, a table) from forcing the
   whole grid wider than the viewport. */
.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
}
.app-shell.rail-narrow { grid-template-columns: var(--rail-narrow-w) minmax(0, 1fr); }
.app-shell.has-banner { --chrome-h: 72px; }
.app-main { min-width: 0; }

.app-sidebar {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-8) var(--space-6);
  background: var(--panel);
  border-right: 1px solid var(--border);
}
.app-sidebar a { margin-left: 0; }
.app-sidebar .linkbutton,
.app-sidebar .rail-linkbutton { margin-left: 0; }

.rail-brand {
  font-weight: 700;
  letter-spacing: 0.2px;
  color: var(--fg);
  text-decoration: none;
  font-size: 15px;
}
.rail-heading {
  margin: var(--space-5) 0 var(--space-1);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--muted);
}
.rail-list { list-style: none; margin: 0; padding: 0; }
.rail-link {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  color: var(--muted);
  text-decoration: none;
  font-size: 14px;
  line-height: 1.3;
}
.rail-link:hover { background: var(--surface-hover, rgba(0, 0, 0, 0.04)); color: var(--fg); }
.rail-link.is-current { background: var(--info-bg); color: var(--info-fg); font-weight: 600; }
.rail-icon {
  flex: 0 0 auto;
  width: 18px;
  text-align: center;
  font-size: 12px;
  opacity: 0.85;
}
.rail-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.rail-sublist { list-style: none; margin: var(--space-1) 0 var(--space-3); padding: 0 0 0 30px; }
.rail-sublink {
  display: block;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  color: var(--muted);
  text-decoration: none;
  font-size: 13px;
}
.rail-sublink:hover { color: var(--fg); }
.rail-sublink.is-current { color: var(--fg); font-weight: 600; }
.rail-empty { color: var(--muted); font-size: 13px; padding: var(--space-3) var(--space-4); }

.rail-footer { margin-top: auto; border-top: 1px solid var(--border); padding-top: var(--space-5); }
.rail-account {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4) 0;
}
.rail-linkbutton { font-size: 13px; text-align: left; }

/* The stylesheet had no focus styling at all; a keyboard-only rail would be
   unusable without it. */
.rail-link:focus-visible,
.rail-sublink:focus-visible,
.rail-brand:focus-visible,
.rail-linkbutton:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Collapsed rail (the workbench): icons only, labels gone. */
.rail-narrow .app-sidebar { padding-left: var(--space-3); padding-right: var(--space-3); }
.rail-narrow .rail-label,
.rail-narrow .rail-heading,
.rail-narrow .rail-sublist,
.rail-narrow .rail-empty,
.rail-narrow .rail-account .inline-form { display: none; }
/* The theme toggle survives the collapse — the workbench is exactly where a
   student sits longest, so it is the worst page to lose dark mode on. */
.rail-narrow .rail-account { align-items: center; padding: var(--space-4) 0 0; }
.rail-narrow .rail-brand { font-size: 0; }
.rail-narrow .rail-brand::before { content: "KE"; font-size: 15px; }
.rail-narrow .rail-link { justify-content: center; padding-left: 0; padding-right: 0; }

/* Narrow viewports: the rail becomes a horizontal strip above the page rather
   than eating half the width. The stylesheet's first media query. */
@media (max-width: 900px) {
  .app-shell,
  .app-shell.rail-narrow { grid-template-columns: 1fr; }
  .app-sidebar {
    position: static;
    height: auto;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }
  .app-sidebar .rail-list { display: flex; flex-wrap: wrap; gap: var(--space-2); }
  .rail-heading, .rail-sublist { display: none; }
  .rail-footer { margin-top: 0; border-top: 0; padding-top: 0; }
  .rail-account { flex-direction: row; padding: 0; }
}
/* NOTE: rules that override something defined LATER in this file cannot live
   in the block above — same specificity, so the later rule wins regardless of
   the media query. The workbench overrides are at the end of the file for
   exactly that reason. */

/* ---- Next-up landing ----------------------------------------------------- */
.home-page { max-width: 760px; margin: 0 auto; }
.home-hero {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-12);
}
.home-hero-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-6);
  margin-bottom: var(--space-4);
}
.home-hero-course {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
}
.home-hero-course:hover { color: var(--fg); }
/* The basis label is load-bearing honesty: it says WHY this is next. With no
   due dates in the data it reads "next in the book", never "due soon". */
.home-basis { font-size: 12px; color: var(--muted); white-space: nowrap; }
.home-hero-title { margin: 0 0 var(--space-3); font-size: 20px; line-height: 1.3; }
.home-progress { margin: 0 0 var(--space-8); font-size: 14px; color: var(--muted); }
.home-actions { display: flex; align-items: center; gap: var(--space-12); flex-wrap: wrap; margin: 0; }
.home-alt { font-size: 13px; color: var(--muted); text-decoration: none; }
.home-alt:hover { color: var(--fg); text-decoration: underline; }

.home-then {
  margin: var(--space-16) 0 var(--space-4);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--muted);
}
.home-others { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.home-other {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto;
  gap: var(--space-6);
  align-items: baseline;
  padding: var(--space-6) var(--space-2);
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.home-other-course { color: var(--muted); text-decoration: none; }
.home-other-course:hover { color: var(--fg); }
.home-other-step { color: var(--fg); text-decoration: none; min-width: 0; }
.home-other-step:hover { text-decoration: underline; }
.home-other-state { color: var(--muted); font-size: 13px; white-space: nowrap; }
.home-footnote { margin-top: var(--space-8); font-size: 13px; }

.home-empty {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-16);
}
.home-course-links { list-style: none; margin: var(--space-8) 0 0; padding: 0; }
.home-course-links li { margin-bottom: var(--space-3); }

@media (max-width: 640px) {
  .home-other { grid-template-columns: 1fr; gap: var(--space-1); }
}

/* ---- In-book cross references -------------------------------------------- */
/* Links the book wrote as notebook paths, repointed at our URLs at render time
   (courses/booklinks.py). `.is-missing` is one we could not resolve: shown as
   plain text, because a link that does nothing when clicked reads as the
   reader's mistake. */
.ke-book-link { color: var(--accent); }
.ke-book-link.is-missing {
  color: var(--muted);
  text-decoration: underline dotted;
  cursor: help;
}
.problem-intro {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  padding: var(--space-5) var(--space-8);
  margin-bottom: var(--space-6);
}
.problem-intro > summary { color: var(--muted); font-size: 13px; cursor: pointer; }

/* ---- The book as a spine ------------------------------------------------- */
.course-page { max-width: 900px; }
.course-blurb { color: var(--muted); margin-top: 0; }
.course-next {
  background: var(--info-bg);
  color: var(--info-fg);
  padding: var(--space-5) var(--space-8);
  border-radius: var(--radius-md);
  margin: var(--space-8) 0;
}
.course-next a { color: inherit; font-weight: 600; }
.course-footnote { margin-top: var(--space-12); font-size: 13px; }
.spine-heading, .hw-heading {
  margin: var(--space-16) 0 var(--space-4);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--muted);
}

.spine { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.spine-row { border-bottom: 1px solid var(--border); }
.spine-row.is-next { background: var(--info-bg); }
.spine-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  padding: var(--space-6) var(--space-4);
}
/* The glyph, not colour alone, carries done/skipped — colour-blind readers and
   greyscale printouts both need it. */
.spine-mark { flex: 0 0 auto; width: 14px; color: var(--muted); font-size: 13px; }
.is-done > .spine-head > .spine-mark { color: var(--ok); }
.spine-title { color: var(--fg); text-decoration: none; font-weight: 500; min-width: 0; }
.spine-title:hover { text-decoration: underline; }
.spine-num { color: var(--muted); font-variant-numeric: tabular-nums; }
.is-done .spine-title, .is-skipped .spine-title { color: var(--muted); font-weight: 400; }
.spine-meta { margin-left: auto; color: var(--muted); font-size: 13px; white-space: nowrap; }
.spine-mark-form { display: flex; gap: var(--space-6); margin: 0; flex: 0 0 auto; }
.spine-action { font-size: 12px; margin-left: 0; opacity: 0; transition: opacity 0.1s; }
.spine-row:hover .spine-action,
.spine-action:focus-visible { opacity: 1; }
.badge-next {
  background: var(--accent);
  color: #fff;
  border-radius: var(--radius-pill);
  padding: 1px var(--space-4);
  font-size: 11px;
  font-weight: 600;
}

.spine-details { padding: 0 var(--space-4) var(--space-5) 30px; }
.spine-summary { color: var(--muted); font-size: 12px; cursor: pointer; list-style: revert; }
.spine-summary::marker { color: var(--border); }
.spine-hw, .spine-sub {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  font-size: 14px;
}
.spine-hw.is-next .spine-hw-title { font-weight: 600; }
.spine-hw-title { min-width: 0; }
.spine-parts { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.spine-sub { color: var(--muted); font-size: 13px; }

.grade-list { list-style: none; margin: 0; padding: 0; }
.grade-row {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel);
  padding: var(--space-8);
  margin-bottom: var(--space-6);
}
.grade-row-head { display: flex; justify-content: space-between; gap: var(--space-6); align-items: baseline; }
.grade-where { color: var(--fg); text-decoration: none; font-weight: 600; }
.grade-where:hover { text-decoration: underline; }

@media (max-width: 640px) {
  .spine-head { flex-wrap: wrap; }
  .spine-meta { margin-left: 0; }
  .spine-action { opacity: 1; }
}

/* Editor layout */
.editor-page {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--space-6);
  height: calc(100vh - var(--chrome-h));
}
/* A live Java debug session swaps the split: the stack/heap diagram is much
   wider than a variables table, and at the default 1.4fr/1fr an ordinary 1500px
   desktop left the heap column — every arrow endpoint — off-screen behind a
   horizontal scroll. The editor stays visible and readable; it just stops being
   the larger half while the picture is the thing being looked at. */
.editor-page.java-debugging { grid-template-columns: 1fr 1.6fr; }

.editor-pane, .output-pane {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.pane-header {
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 13px;
  color: var(--muted);
}
.pane-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* Code editor */
.code-wrap {
  position: relative;
  flex: 1;
  min-height: 0;
  background: var(--panel-2);
  display: flex;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 14px;
  line-height: 1.5;
}
.gutter {
  width: 46px;
  flex-shrink: 0;
  background: #f3f5fa;
  border-right: 1px solid var(--border);
  padding: var(--space-6) 0;
  overflow: hidden;
  user-select: none;
  color: #8b95a7;
}
.gutter .ln {
  height: 21px;
  padding: 0 var(--space-4) 0 var(--space-2);
  text-align: right;
  cursor: pointer;
  white-space: nowrap;
}
.gutter .ln:hover { background: #e3e8f1; }
.gutter .ln.bp {
  background: #fee2e2;
  color: #b91c1c;
  font-weight: 700;
  position: relative;
}
.gutter .ln.bp::before {
  content: '●';
  position: absolute;
  left: 4px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 10px;
  color: #dc2626;
}
.code-area {
  position: relative;
  flex: 1;
  min-width: 0;
}
.current-line {
  position: absolute;
  left: 0; right: 0;
  height: 21px;
  background: rgba(250, 204, 21, 0.28);
  border-top: 1px solid rgba(245, 158, 11, 0.6);
  border-bottom: 1px solid rgba(245, 158, 11, 0.6);
  pointer-events: none;
  z-index: 0;
  transition: top 0.08s ease-out;
}
.code-highlight, .code-input {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: var(--space-6);
  border: 0;
  white-space: pre;
  overflow: auto;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  tab-size: 4;
}
.code-highlight {
  pointer-events: none;
  color: var(--fg);
  background: transparent;
  z-index: 1;
}
.code-input {
  color: transparent;
  caret-color: var(--fg);
  background: transparent;
  resize: none;
  outline: none;
  z-index: 2;
}

.hint { font-size: 12px; color: var(--muted); margin-left: auto; align-self: center; }

.debug-panel {
  border-top: 1px solid var(--border);
  padding: var(--space-5) var(--space-6);
  background: #fbfcfe;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.debug-bar { display: flex; gap: var(--space-3); align-items: center; }
.debug-status {
  margin-left: auto;
  font-size: 12px;
  color: var(--muted);
  padding: var(--space-1) var(--space-4);
  border-radius: var(--radius-pill);
  background: #eef1f6;
  border: 1px solid var(--border);
}
.debug-status.running  { background: #dcfce7; border-color: #86efac; color: #14532d; }
.debug-status.paused   { background: #fef3c7; border-color: #fcd34d; color: #78350f; }
.debug-status.done     { background: #e0e7ff; border-color: #a5b4fc; color: #3730a3; }
.debug-status.error    { background: #fee2e2; border-color: #fca5a5; color: #7f1d1d; }
.debug-info { font-size: 13px; color: var(--fg); }
.debug-info b { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.vars-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.vars-table th, .vars-table td {
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}
.vars-table th { color: var(--muted); font-weight: 500; background: #f3f5fa; }
.vars-table td.name  { color: #1d4ed8; font-weight: 600; }
.vars-table td.type  { color: #6b7280; }
.vars-table td.val   { color: var(--fg); white-space: pre-wrap; word-break: break-all; }
.vars-table td.val.changed { background: #fef3c7; }
.vars-table tr.empty td { text-align: center; color: var(--muted); padding: var(--space-5); }
.code-input::selection { background: rgba(37, 99, 235, 0.25); color: transparent; }

/* Python syntax tokens */
.tok-keyword { color: var(--keyword); font-weight: 600; }
.tok-string  { color: var(--string); }
.tok-number  { color: var(--number); }
.tok-comment { color: var(--comment); font-style: italic; }
.tok-builtin { color: var(--builtin); }
.tok-def     { color: var(--def); }

.controls { padding: var(--space-4) var(--space-6); display: flex; gap: var(--space-4); border-top: 1px solid var(--border); }
/* .btn works on <button>, <a>, and <label> (file-upload control), so link- and
   label-buttons render identically — element-qualifying it silently left the
   app's many <a class="btn"> links unstyled. */
.btn {
  display: inline-block;
  background: var(--accent);
  color: #ffffff;
  border: 0;
  padding: var(--space-4) var(--space-7);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}
.btn.secondary { background: #eef1f6; color: var(--fg); border: 1px solid var(--border); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.output {
  flex: 1;
  padding: var(--space-6);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px;
  white-space: pre-wrap;
  overflow: auto;
  color: var(--fg);
}
.output .err { color: var(--error); }

.status-bar {
  font-size: 12px;
  color: var(--muted);
  padding: var(--space-3) var(--space-6);
  border-top: 1px solid var(--border);
  display: flex;
  gap: var(--space-6);
}
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--muted); margin-right: var(--space-3); }
.dot.ok { background: var(--ok); }
.dot.warn { background: #f59e0b; }
.dot.err { background: var(--error); }

/* Auth + listing */
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-9);
  max-width: 420px;
  margin: var(--space-12) auto;
}
.card h1 { margin-top: 0; font-size: 18px; }
.card label { display: block; margin-top: var(--space-5); font-size: 13px; color: var(--muted); }
.card input[type="text"],
.card input[type="password"],
.card input[type="email"] {
  width: 100%;
  padding: var(--space-4) var(--space-5);
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--fg);
  border-radius: var(--radius-sm);
  font-size: 14px;
}
.card .help, .card .errorlist {
  font-size: 12px;
  color: var(--muted);
  margin: var(--space-2) 0 0;
  padding-left: var(--space-8);
}
.card .errorlist { color: var(--error); list-style: disc; }
.card .actions { margin-top: var(--space-7); display: flex; gap: var(--space-4); align-items: center; }

table.sessions {
  width: 100%;
  border-collapse: collapse;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
table.sessions th, table.sessions td {
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--border);
  text-align: left;
  font-size: 13px;
}
table.sessions th { color: var(--muted); font-weight: 500; }
table.sessions a { color: var(--accent); text-decoration: none; }

/* Replay */
.replay-page { display: grid; grid-template-rows: auto 1fr; gap: var(--space-6); height: calc(100vh - var(--chrome-h)); }
.replay-controls { display: flex; gap: var(--space-4); align-items: center; }
.replay-controls input[type=range] { flex: 1; }

/* Phase 1 trace-debugger controls */
.dbg-scrubber { width: 100%; margin: var(--space-4) 0 var(--space-2); display: block; }
.dbg-pos { font-size: 12px; color: #6b7280; margin: 0 var(--space-3); align-self: center; }

/* Phase 2 stack/heap visualizer */
.dbg-viz { margin-top: var(--space-5); overflow: auto; max-height: 360px; border-top: 1px solid #e5e7eb; padding-top: var(--space-4); }
.ke-viz-wrap { position: relative; min-height: 20px; }
.ke-arrows { position: absolute; top: 0; left: 0; pointer-events: none; overflow: visible; z-index: 0; }
.ke-frame, .ke-obj { position: absolute; z-index: 1; background: #fff; border: 1px solid #cbd5e1;
  border-radius: var(--radius-sm); padding: var(--space-2) 7px; font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  box-shadow: 0 1px 2px rgba(0,0,0,.06); }
.ke-frame { background: #f8fafc; }
.ke-obj-title { font-weight: 600; color: #64748b; font-size: 10px; text-transform: uppercase;
  letter-spacing: .03em; margin-bottom: 3px; }
.ke-kv { white-space: nowrap; }
.ke-vname { color: var(--accent); }
.ke-attr { color: #7c3aed; }
.ke-arrow-sep { color: #94a3b8; margin: 0 var(--space-2); }
.ke-prim { color: #0f766e; }
.ke-ref { color: #dc2626; font-size: 10px; vertical-align: middle; }
.ke-cells { display: flex; }
.ke-cell { border: 1px solid #e2e8f0; padding: 1px var(--space-3); text-align: center; min-width: 16px; }
.ke-idx { font-size: 9px; color: #94a3b8; }
.ke-more, .ke-empty { color: #94a3b8; font-style: italic; }
.ke-arrow { fill: none; stroke: #dc2626; stroke-width: 1.5; opacity: .8; }
.ke-arrowhead { fill: #dc2626; }

/* Pyodide debugger: highlight the current (innermost) call-stack frame */
.ke-frame-current { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 1px 2px rgba(0,0,0,.06); }

/* Reference-semantics view: scalar (int/str) heap boxes, garbage objects, toggle */
.ke-scalar { background: #ecfeff; padding: 3px 9px; }
.ke-scalar-val { color: #0f766e; font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }
.ke-garbage { opacity: .5; border-style: dashed; }
.ke-garbage-tag { font-size: 9px; color: #b91c1c; font-style: italic; margin-top: var(--space-1); }
.dbg-toggle { font-size: 12px; color: #6b7280; margin-left: var(--space-7); cursor: pointer; user-select: none; }
.dbg-toggle input { vertical-align: middle; margin-right: var(--space-2); }

/* Pyodide first-load spinner overlay */
.pane-body { position: relative; }
.dbg-loading { position: absolute; inset: 0; z-index: 20; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--space-8); background: rgba(255,255,255,.94); }
.dbg-spinner { width: 44px; height: 44px; border: 4px solid #dbeafe; border-top-color: var(--accent);
  border-radius: var(--radius-round); animation: ke-spin .8s linear infinite; }
@keyframes ke-spin { to { transform: rotate(360deg); } }
.dbg-loading-text { font-size: 14px; font-weight: 600; color: #374151; text-align: center; }
.dbg-loading-text span { display: block; margin-top: 5px; font-weight: 400; font-size: 12px; color: #6b7280; }

/* Heap object collapse/expand */
.ke-caret { display: inline-block; width: 11px; color: #94a3b8; font-size: 10px; }
.ke-clickable { cursor: pointer; user-select: none; }
.ke-clickable:hover { color: var(--accent); }
.ke-collapsed { color: #94a3b8; font-style: italic; font-size: 11px; padding: 3px var(--space-3); white-space: nowrap; }

/* The .dbg-loading display:flex rule above outranks the [hidden] UA rule, so the
   hidden attribute alone would not hide it — restore that. */
.dbg-loading[hidden] { display: none; }

/* ── Java advisories (java-mode.js renderAdvisories) ───────────────────────
   Divergences from a real JVM that the transpiler can name statically — today
   32-bit int overflow. Amber, never red: nothing has failed, the number is
   simply not the one Java would print. Shown on Run as well as Debug, because
   Run is where a student meets it. Uses the same width pinning as .dbg-java so
   a long message scrolls inside itself rather than widening the page. */
.java-advisories {
  width: 0; min-width: 100%;
  padding: var(--space-2) var(--space-4) 0;
  display: flex; flex-direction: column; gap: var(--space-1);
}
.java-advisory {
  display: flex; align-items: baseline; gap: var(--space-2);
  font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: #fffbeb; border: 1px solid #fcd34d; border-radius: var(--radius-sm);
  color: #92400e; padding: var(--space-2) var(--space-3);
}
.java-advisory-tag {
  flex: 0 0 auto; font-weight: 600; text-transform: uppercase;
  font-size: 10px; letter-spacing: .06em; opacity: .8;
}
.java-advisory-msg { min-width: 0; overflow-wrap: anywhere; }
:root[data-theme="dark"] .java-advisory {
  background: #2a2011; border-color: #7c5b12; color: #fcd34d;
}

/* ── Java stack/heap variables view (java-vars.js) ─────────────────────────
   Java problems only; the Python table above is untouched. Every colour goes
   through a --jv-* custom property so the dark theme is a token swap at the
   bottom of this file rather than a parallel set of selectors. */
.dbg-java {
  --jv-surface: var(--panel);
  --jv-surface-2: #f8fafc;
  --jv-line: #cbd5e1;
  --jv-head: var(--muted);
  --jv-type: #7c3aed;      /* declared type — the compiler's half */
  --jv-name: var(--accent);
  --jv-lit: #0f766e;       /* primitive values living in the frame slot */
  --jv-str: #b45309;
  --jv-ref: #dc2626;       /* references and the arrows they draw */
  --jv-cls: #0f766e;
  --jv-null: #94a3b8;
  --jv-faint: #94a3b8;
  margin-top: var(--space-5);
  border-top: 1px solid var(--jv-line);
  padding-top: var(--space-4);
  overflow: auto;
  max-height: 420px;
  /* The workbench sizes .output-pane to its content (a grid item with the
     default min-width:auto, holding column flexboxes all the way down), so a
     wide diagram would push the whole PAGE wider instead of scrolling. Pinning
     the width to zero and re-inflating with a percentage min-width makes this
     box track its parent's width exactly and keeps the overflow inside itself.
     Measured: without this, a 375px viewport grew to 728px of page scroll. */
  width: 0;
  min-width: 100%;
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  /* so .jv-cols can size its gap against THIS box, not the viewport */
  container: dbgjava / inline-size;
}
.jv-wrap { position: relative; width: max-content; }
.jv-arrows { position: absolute; top: 0; left: 0; pointer-events: none; overflow: visible; z-index: 0; }
/* Two columns, side by side at any width. On a narrow viewport the container
   scrolls horizontally rather than reflowing — an arrow that has to wrap to the
   next line stops being a picture of a pointer. */
/* The gap is the horizontal run the arrows are drawn across, so it wants to be
   generous — but not at the price of pushing the heap column, and with it every
   arrowhead, off the right edge of the pane. .dbg-java is an inline-size
   container (above), so the gap can answer to the PANE's width rather than the
   viewport's: full run when the diagram fits, tightened when it is the last
   20px standing between the student and the picture. */
.jv-cols {
  display: flex; align-items: flex-start; position: relative; z-index: 1;
  gap: var(--space-20);
}
@container dbgjava (max-width: 860px) { .jv-cols { gap: var(--space-9); } }
.jv-col { display: flex; flex-direction: column; gap: var(--space-5); min-width: 0; }
.jv-stack { flex: 0 0 auto; }
.jv-heap { flex: 0 0 auto; }
.jv-colhead {
  margin: 0 0 var(--space-1); font: 600 10px/1.4 -apple-system, system-ui, sans-serif;
  letter-spacing: .08em; text-transform: uppercase; color: var(--jv-head);
  border-bottom: 1px solid var(--jv-line); padding-bottom: var(--space-2);
}

.jv-frame, .jv-obj {
  background: var(--jv-surface); border: 1px solid var(--jv-line);
  border-radius: var(--radius-sm); padding: var(--space-3) var(--space-4);
}
.jv-frame { background: var(--jv-surface-2); }
.jv-frame-current { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
/* Class and method names keep their own casing — HashMap is not HASHMAP. Only
   the two column headings are set in small caps. */
.jv-frame-title, .jv-obj-head {
  font: 600 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--jv-head); margin-bottom: var(--space-2); white-space: nowrap;
}
.jv-slots { border-collapse: collapse; }
.jv-slots td { padding: 1px var(--space-2) 1px 0; white-space: nowrap; vertical-align: baseline; }
.jv-type { color: var(--jv-type); }
.jv-name { color: var(--jv-name); font-weight: 600; }
.jv-eq { color: var(--jv-faint); }
.jv-lit { color: var(--jv-lit); }
.jv-lit-str { color: var(--jv-str); }
.jv-null { color: var(--jv-null); font-style: italic; }
.jv-value.jv-changed { background: var(--warning-bg); border-radius: var(--radius-sm); padding: 0 var(--space-2); }
.jv-empty, .jv-elided, .jv-legend { color: var(--jv-faint); font-style: italic; }
.jv-legend {
  max-width: 340px; margin-top: var(--space-2); white-space: normal;
  font: italic 10px/1.5 -apple-system, system-ui, sans-serif;
}

/* A reference: the anchor an arrow leaves from, labelled with the runtime class
   and the identity tag so it stays readable even when the target has scrolled. */
.jv-ref { color: var(--jv-ref); cursor: default; }
.jv-ref-dash { margin-right: var(--space-1); }
.jv-ref-label { text-decoration: underline dotted; text-underline-offset: 2px; }
.jv-arrow { fill: none; stroke: var(--jv-ref); stroke-width: 1.5; opacity: .75; }
.jv-arrowhead { fill: var(--jv-ref); opacity: .75; }
.jv-arrow.jv-hot, .jv-arrowhead.jv-hot { opacity: 1; stroke-width: 2.5; }
.jv-obj.jv-hot { border-color: var(--jv-ref); box-shadow: 0 0 0 1px var(--jv-ref); }

.jv-obj-cls { color: var(--jv-cls); }
.jv-obj-id { color: var(--jv-ref); margin-left: var(--space-1); }
.jv-obj-size { color: var(--jv-faint); margin-left: var(--space-4); font-weight: 400; }
.jv-obj-body { white-space: nowrap; }
.jv-strval { color: var(--jv-str); }
.jv-boxval { color: var(--jv-lit); }
.jv-entry, .jv-field { white-space: nowrap; }
.jv-to { color: var(--jv-faint); margin: 0 var(--space-2); }
.jv-fname { color: var(--jv-name); margin: 0 var(--space-2) 0 var(--space-2); }
.jv-field .jv-eq { margin-right: var(--space-2); }
.jv-cells { display: flex; flex-wrap: wrap; }
.jv-cell {
  border: 1px solid var(--jv-line); padding: 1px var(--space-3);
  text-align: center; min-width: 20px;
}
.jv-idx { font-size: 9px; color: var(--jv-faint); }
.jv-garbage { opacity: .55; border-style: dashed; }
.jv-garbage-tag { font-size: 9px; color: var(--danger-fg); font-style: italic; }

/* An int that has left Java's 32-bit range. Amber, not red: nothing failed —
   real Java would have wrapped silently, and that silence is the lesson. */
.jv-flag {
  margin-left: var(--space-3); padding: 0 var(--space-2); border-radius: var(--radius-sm);
  background: var(--warning-bg); color: var(--warning-fg); font-size: 11px; cursor: help;
}
.jv-badge {
  margin-left: var(--space-3); padding: 0 var(--space-2); border-radius: var(--radius-pill);
  background: var(--info-bg); color: var(--info-fg); font-size: 10px; cursor: help;
}

/* ---- Courses / chapters ---- */
.course-page, .chapter-page { max-width: 820px; margin: var(--space-12) auto; padding: 0 var(--space-9); }
.course-card { border: 1px solid #e5e7eb; border-radius: var(--radius-lg); padding: var(--space-7) var(--space-9); margin: var(--space-6) 0; }
.course-card h2 { margin: 0 0 var(--space-3); font-size: 18px; }
.badge { display: inline-block; font-size: 11px; padding: var(--space-1) var(--space-4); border-radius: var(--radius-pill); }
.badge-pending { background: var(--warning-bg); color: var(--warning-fg); }
.badge-approved { background: var(--success-bg); color: var(--success-fg); }
.badge-rejected { background: var(--danger-bg); color: var(--danger-fg); }
.crumb { font-size: 13px; margin-bottom: var(--space-2); }
.crumb a { color: var(--accent); text-decoration: none; }
.chapter-list { line-height: 1.9; list-style: none; padding-left: 0; }
.enroll-status { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: var(--radius-md); padding: var(--space-5) var(--space-7); }
.muted { color: #6b7280; }
.messages { list-style: none; padding: 0; }
.messages li { padding: var(--space-4) var(--space-6); border-radius: var(--radius-sm); margin: var(--space-3) 0; background: #f1f5f9; }
.messages li.success { background: var(--success-bg); } .messages li.error { background: var(--danger-bg); }
.chapter-body { line-height: 1.65; }
.chapter-body h1, .chapter-body h2, .chapter-body h3 { line-height: 1.25; margin-top: 1.4em; }
.chapter-body .nb-code,
.chapter-body .nb-md pre { background: #0f172a; color: #e2e8f0; padding: var(--space-6) var(--space-7); border-radius: var(--radius-md);
  overflow-x: auto; font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }
.chapter-body .nb-md pre code { background: none; color: inherit; padding: 0; }
.chapter-body blockquote { border-left: 3px solid #cbd5e1; margin: 1em 0; padding: var(--space-1) var(--space-7); color: #475569; }
.chapter-nav { display: flex; justify-content: space-between; gap: var(--space-6); margin: var(--space-16) 0; font-size: 14px; }
.chapter-nav a { color: var(--accent); text-decoration: none; }
/* Non-required "going further" subchapters: visually distinct, clearly optional. */
.subchapter-list { list-style: none; margin: var(--space-1) 0 var(--space-2); padding-left: var(--space-8); }
.subchapter-item { font-size: 14px; color: #475569; }
.subchapter-item a { color: var(--accent); text-decoration: none; }
.subchapters-block { margin-top: var(--space-12); padding-top: var(--space-6); border-top: 1px dashed #cbd5e1; }
.subchapters-block h2 { font-size: 18px; }
.subchapter-page .chapter-body { opacity: 0.98; }
.subchapter-badge { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin: var(--space-3) 0 var(--space-8); }
.badge-optional { display: inline-block; background: #eef2ff; color: #3730a3; border: 1px solid #c7d2fe;
  border-radius: var(--radius-sm); padding: 2px var(--space-4); font-size: 12px; font-weight: 600; }
.badge-difficulty { display: inline-block; border-radius: var(--radius-sm); padding: 2px var(--space-4);
  font-size: 12px; font-weight: 600; text-transform: capitalize; }
.badge-difficulty.small, .badge-optional.small { font-size: 11px; padding: 1px var(--space-3); }
.diff-gentle { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0; }
.diff-moderate { background: #fef9c3; color: #854d0e; border: 1px solid #fde68a; }
.diff-spicy { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }
.ke-subchapter-link { font-weight: 600; }
.subchapter-also { margin: calc(-1 * var(--space-5)) 0 var(--space-8); }
.subchapter-also a { color: var(--accent); text-decoration: none; }
/* Inline answer box embedded at a pause_and_try callout (writeup_inline.js). */
.ke-pat-slot:empty { display: none; }
.writeup-inline { margin: var(--space-4) 0 var(--space-10); padding-left: var(--space-6);
  border-left: 3px solid var(--accent); }
.writeup-inline .writeup-label { margin: 0 0 var(--space-2); font-weight: 600; }

/* ---- Problem page ---- */
.problem-page { max-width: 1100px; margin: var(--space-9) auto; padding: 0 var(--space-9); }
.problem-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-6); }
.problem-head h1 { font-size: 20px; }
.part-nav a { color: var(--accent); text-decoration: none; margin-left: var(--space-6); font-size: 14px; }
.problem-prompt { border: 1px solid #e5e7eb; border-radius: var(--radius-lg); padding: var(--space-3) var(--space-8); margin: var(--space-6) 0; background: #fafafa; }
.problem-prompt > summary { cursor: pointer; font-weight: 600; padding: var(--space-3) 0; }
.prompt-body { line-height: 1.6; }
.prompt-body pre, .prompt-body code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.prompt-body pre { background: #0f172a; color: #e2e8f0; padding: var(--space-5) var(--space-6); border-radius: var(--radius-md); overflow-x: auto; }
.sample-inputs { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); margin: var(--space-5) 0; }
.problem-actions { display: flex; align-items: center; gap: var(--space-6); margin: var(--space-7) 0; }
.assignment-list { list-style: none; padding: 0; }
.assignment-list li { padding: var(--space-4) 0; border-bottom: 1px solid #f1f5f9; }
.assignment-list .parts { display: inline-flex; flex-wrap: wrap; gap: var(--space-3); margin-left: var(--space-4); }
.part-pill { font-size: 12px; padding: var(--space-1) var(--space-5); border: 1px solid #c7d2fe; border-radius: var(--radius-pill);
  color: #3730a3; text-decoration: none; background: #eef2ff; }
.test-results { margin: var(--space-6) 0 var(--space-20); }
.test-summary { font-weight: 700; padding: var(--space-4) var(--space-6); border-radius: var(--radius-md); margin-bottom: var(--space-4); }
.test-summary.all-pass { background: var(--success-bg); color: var(--success-fg); }
.test-summary.some-fail { background: var(--warning-bg); color: var(--warning-fg); }
.test-row { padding: var(--space-2) var(--space-6); border-radius: var(--radius-sm); }
.test-row.test-pass .test-name { color: var(--success-fg); }
.test-row.test-fail .test-name, .test-row.test-error .test-name { color: var(--danger-fg); }
.test-name { font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace; }
.test-msg { background: #fef2f2; color: #7f1d1d; padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm); margin: var(--space-2) 0 var(--space-4);
  font: 12px/1.45 ui-monospace, monospace; white-space: pre-wrap; overflow-x: auto; }
.test-error { background: var(--danger-bg); color: var(--danger-fg); padding: var(--space-4) var(--space-6); border-radius: var(--radius-md); }

/* ---- Submissions (instructor + submit UI) ---- */
.btn.primary { background: var(--ok); color: #fff; }
.submit-status { font-size: 13px; color: var(--success-fg); }
.instructor-bar { margin: var(--space-5) 0; }
.subs-table { width: 100%; border-collapse: collapse; margin: var(--space-7) 0; font-size: 14px; }
.subs-table th, .subs-table td { text-align: left; padding: var(--space-4) var(--space-5); border-bottom: 1px solid #eef2f7; }
.subs-table th { color: #64748b; font-weight: 600; }
.sub-meta { color: #475569; font-size: 14px; }
.result-list { list-style: none; padding: 0; font: 13px/1.7 ui-monospace, monospace; }
.result-list .test-pass { color: var(--success-fg); }
.result-list .test-fail, .result-list .test-error { color: var(--danger-fg); }
.badge-submitted { background: var(--success-bg); color: var(--success-fg); }
.badge-draft { background: var(--warning-bg); color: var(--warning-fg); }
.badge-graded { background: var(--info-bg); color: var(--info-fg); }

/* ---- Guardrailed tutor ---- */
.tutor-panel { border: 1px solid #e5e7eb; border-radius: var(--radius-lg); padding: var(--space-4) var(--space-8); margin: var(--space-7) 0 var(--space-20); background: #fffbeb; }
.tutor-panel > summary { cursor: pointer; font-weight: 600; padding: var(--space-3) 0; }
.tutor-q { width: 100%; box-sizing: border-box; font: 14px/1.5 inherit; padding: var(--space-4); border: 1px solid #d1d5db; border-radius: var(--radius-md); margin: var(--space-3) 0; }
.tutor-reply { white-space: pre-wrap; margin-top: var(--space-5); line-height: 1.55; color: #1f2937; }
.tutor-turn { border-left: 3px solid #fcd34d; padding: var(--space-2) var(--space-6); margin: var(--space-5) 0; }
.tutor-a { white-space: pre-wrap; color: #374151; margin: var(--space-2) 0; }
.small { font-size: 11px; }

/* ---- Similarity (winnowing) ---- */
.subs-table tr.sim-high td { background: var(--danger-bg); }
.subs-table tr.sim-mid td { background: var(--warning-bg); }

/* ---- View-as-student preview ---- */
.student-view-banner { background: #1e293b; color: #fef9c3; text-align: center; padding: var(--space-3) var(--space-6);
  font-size: 13px; display: flex; gap: var(--space-5); align-items: center; justify-content: center; }
.student-view-banner .linkbutton { color: #fde68a; text-decoration: underline; }

/* ---- Teacher self-service ---- */
.manage-section { border-top: 1px solid #e5e7eb; padding: var(--space-7) 0; }
.manage-actions { display: flex; gap: var(--space-4); align-items: center; flex-wrap: wrap; }
.roster-row { display: flex; justify-content: space-between; align-items: center; gap: var(--space-6);
  padding: var(--space-3) 0; border-bottom: 1px solid #f1f5f9; }
.course-edit-form p { margin: var(--space-4) 0; }
.course-edit-form label { display: inline-block; min-width: 150px; font-weight: 600; font-size: 13px; vertical-align: top; }
.course-edit-form input[type=text], .course-edit-form textarea {
  width: 360px; max-width: 100%; padding: var(--space-3) var(--space-4); border: 1px solid #d1d5db; border-radius: var(--radius-sm); }

/* ---- Assignment scheduling ---- */
.assign-row { display: flex; align-items: center; gap: var(--space-5); padding: var(--space-3) 0;
  border-bottom: 1px solid #f1f5f9; flex-wrap: wrap; }
.assign-title { flex: 1; min-width: 200px; }
.assign-row select, .assign-row input[type=date] { padding: var(--space-2) var(--space-3); border: 1px solid #d1d5db; border-radius: var(--radius-sm); }
.due { color: #b45309; font-size: 12px; }

/* ---- Writeup (no-code) problems: rich-text editor ---- */
.writeup-label { display: block; font-weight: 600; margin: var(--space-8) 0 var(--space-2); }
.writeup-prompt { line-height: 1.6; }
.writeup-editor { border: 1px solid #d1d5db; border-radius: var(--radius-md); overflow: hidden; }
.writeup-toolbar { display: flex; flex-wrap: wrap; gap: var(--space-1); align-items: center;
  padding: var(--space-3) var(--space-4); background: #f9fafb; border-bottom: 1px solid #e5e7eb; }
.writeup-toolbar button { min-width: 30px; height: 28px; padding: 0 var(--space-4); cursor: pointer;
  border: 1px solid transparent; background: transparent; border-radius: 5px;
  font-size: 13px; color: #374151; }
.writeup-toolbar button:hover { background: #e5e7eb; }
.writeup-toolbar .sep { width: 1px; height: 18px; background: #d1d5db; margin: 0 var(--space-2); }
.writeup-area { min-height: 220px; padding: var(--space-6) var(--space-7); font: 15px/1.6 inherit; outline: none; }
.writeup-area:empty:before { content: attr(data-placeholder); color: #9ca3af; }
.writeup-area p { margin: 0 0 var(--space-4); }
.writeup-area blockquote { margin: var(--space-4) 0; padding-left: var(--space-6);
  border-left: 3px solid #d1d5db; color: #4b5563; }
.capture-status { font-size: 12px; }
.nb-writeup { line-height: 1.6; }
.rich-replay { height: 100%; box-sizing: border-box; padding: var(--space-7) var(--space-8); overflow: auto;
  background: var(--surface, #fff); line-height: 1.6; }

/* ---- Assignment kind tag ---- */
.badge-kind { background: var(--info-bg); color: var(--info-fg); }

/* ---- Grading: student grade panel + instructor controls ---- */
.grade-panel { border: 1px solid var(--border); border-left: 3px solid var(--ok); border-radius: var(--radius-md);
  padding: 10px 14px; margin: 12px 0; background: var(--surface-subtle, #f9fafb); }
.grade-head { display: flex; align-items: center; gap: 10px; }
.grade-score { font-size: 16px; }
.grade-feedback { margin-top: 8px; line-height: 1.6; }
.grade-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 8px 0 12px; }
.grade-upload-form { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; }
.file-label { position: relative; overflow: hidden; }
.file-label input[type=file] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.grade-form-wrap { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 8px 14px; margin: 14px 0; }
.grade-form-wrap > summary { cursor: pointer; font-weight: 600; padding: 4px 0; }
.grade-form { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; margin-top: 8px; }
.grade-form label.block { display: block; width: 100%; font-size: 13px; color: var(--muted); }
.grade-form input[type=number] { padding: 6px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel-2); color: var(--fg); width: 120px; }

/* ---- Progress + lateness ---- */
.resumed-note { margin: 0 0 var(--space-4); font-size: 13px; }
.badge-done { background: var(--success-bg); color: var(--success-fg); }
.badge-late { background: var(--danger-bg); color: var(--danger-fg); }
.prog { font-size: 13px; color: #374151; margin-left: var(--space-2); }
.due-over { color: #b91c1c; font-weight: 600; }

/* ============================================================================
   Dark theme — append-only. Light-mode rules above are untouched (so light mode
   is byte-for-byte unchanged). Theme is applied by setting data-theme on <html>
   pre-paint (base.html head script): "dark"/"light" from localStorage, else the
   OS prefers-color-scheme. Most of the UI is token-driven, so the bulk is a
   token swap; the override rules below cover raw-hex holdouts that a future
   structural-color tokenization slice can fold back into tokens.
   ========================================================================== */
:root { color-scheme: light; }

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0f1117;
  --panel: #161b22;
  --panel-2: #161b22;
  --fg: #e6edf3;
  --muted: #9198a1;
  --border: #2a3038;
  --accent: #4493f8;
  --keyword: #79c0ff;
  --string: #a5d6ff;
  --number: #d2a8ff;
  --comment: #8b949e;
  --builtin: #56d4bb;
  --def: #d2a8ff;
  --error: #f85149;
  --ok: #3fb950;
  --success-bg: #11281b; --success-fg: #56d364;
  --warning-bg: #2a2410; --warning-fg: #e3b341;
  --danger-bg:  #2d1517; --danger-fg:  #f85149;
  --info-bg:    #122740; --info-fg:    #79c0ff;
  /* dark-only structural helpers (cover the raw-hex holdouts below) */
  --surface-subtle: #1b222c;
  --surface-muted:  #21262d;
  --surface-hover:  #262d38;
  --text-faint:     #6e7681;
}

/* Raw-hex holdouts: borders → token */
:root[data-theme="dark"] .dbg-viz,
:root[data-theme="dark"] .course-card,
:root[data-theme="dark"] .problem-prompt,
:root[data-theme="dark"] .enroll-status,
:root[data-theme="dark"] .tutor-panel,
:root[data-theme="dark"] .tutor-q,
:root[data-theme="dark"] .manage-section,
:root[data-theme="dark"] .roster-row,
:root[data-theme="dark"] .assignment-list li,
:root[data-theme="dark"] .assign-row,
:root[data-theme="dark"] .assign-row select,
:root[data-theme="dark"] .assign-row input[type=date],
:root[data-theme="dark"] .subs-table th,
:root[data-theme="dark"] .subs-table td,
:root[data-theme="dark"] .course-edit-form input[type=text],
:root[data-theme="dark"] .course-edit-form textarea,
:root[data-theme="dark"] .writeup-editor,
:root[data-theme="dark"] .writeup-toolbar,
:root[data-theme="dark"] .writeup-area blockquote,
:root[data-theme="dark"] .chapter-body blockquote,
:root[data-theme="dark"] .ke-cell,
:root[data-theme="dark"] .ke-frame,
:root[data-theme="dark"] .ke-obj { border-color: var(--border); }

:root[data-theme="dark"] .writeup-toolbar .sep { background: var(--border); }

/* Raw-hex holdouts: subtle surfaces → token */
:root[data-theme="dark"] .gutter,
:root[data-theme="dark"] .debug-panel,
:root[data-theme="dark"] .vars-table th,
:root[data-theme="dark"] .enroll-status,
:root[data-theme="dark"] .problem-prompt,
:root[data-theme="dark"] .tutor-panel,
:root[data-theme="dark"] .ke-frame,
:root[data-theme="dark"] .ke-scalar,
:root[data-theme="dark"] .writeup-toolbar { background: var(--surface-subtle); }
:root[data-theme="dark"] .ke-obj { background: var(--panel); }
:root[data-theme="dark"] .debug-status,
:root[data-theme="dark"] .btn.secondary { background: var(--surface-muted); }
:root[data-theme="dark"] .gutter .ln:hover,
:root[data-theme="dark"] .writeup-toolbar button:hover { background: var(--surface-hover); }

/* Messages keep their status tint; only the neutral default becomes dark */
:root[data-theme="dark"] .messages li { background: var(--surface-subtle); }
:root[data-theme="dark"] .messages li.success { background: var(--success-bg); }
:root[data-theme="dark"] .messages li.error { background: var(--danger-bg); }

/* Code blocks were navy on white; on a dark page lift them to a readable block */
:root[data-theme="dark"] .chapter-body .nb-code,
:root[data-theme="dark"] .chapter-body .nb-md pre,
:root[data-theme="dark"] .prompt-body pre { background: var(--surface-subtle); color: var(--fg); }
:root[data-theme="dark"] .rich-replay { background: var(--panel); }

/* Raw-hex holdouts: text greys → token */
:root[data-theme="dark"] .muted,
:root[data-theme="dark"] .dbg-pos,
:root[data-theme="dark"] .dbg-toggle,
:root[data-theme="dark"] .dbg-loading-text span,
:root[data-theme="dark"] .vars-table td.type,
:root[data-theme="dark"] .ke-obj-title,
:root[data-theme="dark"] .sub-meta,
:root[data-theme="dark"] .subs-table th,
:root[data-theme="dark"] .chapter-body blockquote,
:root[data-theme="dark"] .tutor-a,
:root[data-theme="dark"] .prog,
:root[data-theme="dark"] .writeup-area blockquote { color: var(--muted); }

:root[data-theme="dark"] .dbg-loading-text,
:root[data-theme="dark"] .tutor-reply,
:root[data-theme="dark"] .writeup-toolbar button { color: var(--fg); }

:root[data-theme="dark"] .gutter,
:root[data-theme="dark"] .ke-arrow-sep,
:root[data-theme="dark"] .ke-idx,
:root[data-theme="dark"] .ke-more,
:root[data-theme="dark"] .ke-empty,
:root[data-theme="dark"] .ke-caret,
:root[data-theme="dark"] .ke-collapsed { color: var(--text-faint); }
:root[data-theme="dark"] .writeup-area:empty:before { color: var(--text-faint); }

:root[data-theme="dark"] .vars-table td.name { color: var(--keyword); }
:root[data-theme="dark"] .ke-attr { color: var(--number); }
:root[data-theme="dark"] .ke-prim,
:root[data-theme="dark"] .ke-scalar-val { color: var(--builtin); }
:root[data-theme="dark"] .ke-ref,
:root[data-theme="dark"] .ke-garbage-tag { color: var(--danger-fg); }
:root[data-theme="dark"] .ke-arrow { stroke: var(--danger-fg); }
:root[data-theme="dark"] .ke-arrowhead { fill: var(--danger-fg); }

/* Status-bearing holdouts that used their own darker palette */
:root[data-theme="dark"] .vars-table td.val.changed { background: var(--warning-bg); }
:root[data-theme="dark"] .gutter .ln.bp { background: var(--danger-bg); color: var(--danger-fg); }
:root[data-theme="dark"] .gutter .ln.bp::before { color: var(--danger-fg); }
:root[data-theme="dark"] .test-msg { background: var(--danger-bg); color: var(--danger-fg); }
:root[data-theme="dark"] .tutor-turn { border-left-color: var(--warning-fg); }
:root[data-theme="dark"] .due { color: var(--warning-fg); }
:root[data-theme="dark"] .due-over { color: var(--danger-fg); }
:root[data-theme="dark"] .part-pill {
  background: var(--info-bg); color: var(--info-fg); border-color: transparent;
}

/* Debug-status pills: re-point the four states at status tokens */
:root[data-theme="dark"] .debug-status.running { background: var(--success-bg); border-color: var(--success-fg); color: var(--success-fg); }
:root[data-theme="dark"] .debug-status.paused  { background: var(--warning-bg); border-color: var(--warning-fg); color: var(--warning-fg); }
:root[data-theme="dark"] .debug-status.done    { background: var(--info-bg);    border-color: var(--info-fg);    color: var(--info-fg); }
:root[data-theme="dark"] .debug-status.error   { background: var(--danger-bg);  border-color: var(--danger-fg);  color: var(--danger-fg); }

/* Java stack/heap view: one token swap, since java-vars' colours all route
   through --jv-* on .dbg-java. */
:root[data-theme="dark"] .dbg-java {
  --jv-surface: var(--panel);
  --jv-surface-2: var(--surface-subtle);
  --jv-line: var(--border);
  --jv-head: var(--muted);
  --jv-type: var(--number);
  --jv-name: var(--keyword);
  --jv-lit: var(--builtin);
  --jv-str: var(--string);
  --jv-ref: var(--danger-fg);
  --jv-cls: var(--builtin);
  --jv-null: var(--text-faint);
  --jv-faint: var(--text-faint);
}

/* Pyodide first-load overlay + spinner */
:root[data-theme="dark"] .dbg-loading { background: rgba(13, 17, 23, 0.94); }
:root[data-theme="dark"] .dbg-spinner { border-color: var(--border); border-top-color: var(--accent); }

/* Topbar theme toggle */
.theme-toggle { font-size: 15px; line-height: 1; }

/* Bulk scheduling on the manage page — the deployed box has no shell most
   days, so the only way to make a freshly synced 48-assignment course real
   has to be in the browser. */
.bulk-assign {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-6);
  margin-bottom: var(--space-8);
  background: var(--surface-subtle, var(--bg));
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: 13px;
}
.bulk-label { color: var(--muted); }

/* ---- Needs grading ------------------------------------------------------- */
.grading-lede { font-size: 15px; margin-bottom: var(--space-4); }
.grading-actions {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  flex-wrap: wrap;
  margin-bottom: var(--space-16);
}
.grading-clear { font-size: 15px; }
.grading-groups { list-style: none; margin: 0; padding: 0; }
.grading-group { border-top: 1px solid var(--border); padding: var(--space-6) 0; }
.grading-group-head {
  display: flex;
  justify-content: space-between;
  gap: var(--space-6);
  align-items: baseline;
}
.grading-where { font-weight: 600; }
.grading-subs { list-style: none; margin: var(--space-3) 0 0; padding: 0 0 0 var(--space-8); }
.grading-subs li { padding: var(--space-2) 0; display: flex; gap: var(--space-4); align-items: baseline; }
.grading-foot { margin-top: var(--space-16); }
.grade-submit { display: flex; gap: var(--space-4); flex-wrap: wrap; margin-top: var(--space-5); }
.rail-subdivider {
  margin: var(--space-5) 0 var(--space-1);
  padding-left: var(--space-4);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-faint, var(--muted));
}

/* ---- Narrow viewports, part two -----------------------------------------
   These override rules defined further UP the file (.editor-page at ~408,
   .problem-page at ~784), so they have to come after them: a media query does
   not raise specificity, and at equal specificity the later rule wins. */
@media (max-width: 900px) {
  /* The workbench stacks. Side by side at this width the editor pane gets
     about 310px, which is not a code editor. Height goes auto rather than
     splitting one viewport between two panes and giving each half a screen. */
  .editor-page { grid-template-columns: 1fr; height: auto; }
  /* .editor-page.java-debugging (two classes) outranks .editor-page (one), and
     a media query adds no specificity — so the Java debug split survived the
     stack and pushed the debug panel off-screen (measured at 375px: page
     scrollWidth 431 -> 766). Reset it at matching specificity. */
  .editor-page.java-debugging { grid-template-columns: 1fr; }
  .editor-pane { min-height: 60vh; }
  .output-pane { min-height: 40vh; }
  .problem-page { padding: 0 var(--space-5); margin-top: var(--space-5); }
  .problem-head { flex-wrap: wrap; }
  .part-nav a:first-child { margin-left: 0; }
  .course-page, .home-page { max-width: 100%; }
}

/* ---- Sync status --------------------------------------------------------- */
.sync-status {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-8);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  font-size: 14px;
}
.sync-status.is-running { background: var(--info-bg); color: var(--info-fg); }
.sync-status.is-ok { background: var(--success-bg); color: var(--success-fg); }
.sync-status.is-bad { background: var(--danger-bg); color: var(--danger-fg); }
.sync-output {
  display: block;
  width: 100%;
  margin-top: var(--space-3);
  font-size: 12px;
  white-space: pre-wrap;
  opacity: 0.85;
}
.sync-spinner {
  width: 12px;
  height: 12px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: var(--radius-round);
  animation: sync-spin 0.8s linear infinite;
  flex: 0 0 auto;
}
@keyframes sync-spin { to { transform: rotate(360deg); } }
/* Respect a reduced-motion preference — the text already says it is running. */
@media (prefers-reduced-motion: reduce) {
  .sync-spinner { animation: none; }
}

/* ---- Scratch editor language switch -------------------------------------- */
.scratch-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
.scratch-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--muted);
}
.scratch-langs { display: flex; gap: var(--space-2); }
.lang-btn.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.scratch-note { flex: 1 1 auto; min-width: 0; }

/* ---- Book code cells, by language ----------------------------------------
   The OOPS book alternates Java and Kotlin to make the same point twice. A
   Kotlin cell is currently illustrative — nothing in the browser runs Kotlin
   yet — so it is marked as such rather than looking like something that failed
   to run. A left border is the whole treatment: enough to tell the two apart
   while reading, not so much that the page becomes a colour chart. */
.chapter-body .nb-code.lang-kotlin {
  border-left: 3px solid var(--number);
}
.chapter-body .nb-code.lang-java {
  border-left: 3px solid var(--builtin);
}
.nb-lang-note {
  margin: calc(-1 * var(--space-3)) 0 var(--space-8);
  font-size: 12px;
  color: var(--muted);
}
