:root {
  --border: #d8d8d8;
  --bg: #ffffff;
  --bg-alt: #f6f6f7;
  --text: #1c1c1e;
  --text-muted: #6b6b70;
  --accent: #8e0000; /* TU/e red-ish, no brand assets, just a distinguishing color */
  --accent-contrast: #ffffff;
  --ok: #1a7a3c;
  --ok-bg: #e9f7ee;
  --warn: #9a5b00;
  --warn-bg: #fdf3e0;
  --error: #a3231f;
  --error-bg: #fbeceb;
}

* { box-sizing: border-box; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  margin: 0;
  color: var(--text);
  background: var(--bg-alt);
}

nav {
  background: var(--accent);
  color: var(--accent-contrast);
  padding: 0.75rem 1.5rem;
  display: flex;
  gap: 1.25rem;
  align-items: center;
  flex-wrap: wrap;
}

nav a {
  color: var(--accent-contrast);
  text-decoration: none;
  font-weight: 500;
}

nav a:hover { text-decoration: underline; }

nav .brand { font-weight: 700; margin-right: 0.5rem; }

nav .nav-spacer { flex: 1; }

nav .nav-user { color: var(--accent-contrast); opacity: 0.85; font-size: 0.9rem; }

nav button.link-button {
  background: none;
  border: none;
  color: var(--accent-contrast);
  font-weight: 500;
  font-size: 0.95rem;
  padding: 0;
  cursor: pointer;
  text-decoration: none;
}

nav button.link-button:hover { text-decoration: underline; opacity: 1; }

/* A <button> styled to read as a plain inline link -- for a toggle that
   needs to submit its enclosing form (see course_catalogue.html's Dutch-
   versions toggle) rather than just navigate. `color: LinkText` is a CSS
   system-color keyword resolving to whatever the browser's own default
   link color is, so this matches plain <a> text in the page exactly
   without guessing a hex value; more specific than the nav-scoped rule
   above, so it's unaffected by it. */
button.link-button {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: LinkText;
  text-decoration: underline;
  cursor: pointer;
}

main {
  max-width: 1140px;
  margin: 0 auto;
  padding: 1.5rem;
}

.card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1.25rem;
  margin-bottom: 1.5rem;
}

h1 { font-size: 1.5rem; margin-top: 0; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }

table {
  border-collapse: collapse;
  width: 100%;
  margin-bottom: 1rem;
  display: block;
  overflow-x: auto;
}

th, td {
  border: 1px solid var(--border);
  padding: 0.4rem 0.6rem;
  text-align: left;
  vertical-align: top;
  font-size: 0.92rem;
}

th { background: var(--bg-alt); }

/* Label/value rows for a two-column read-only table, e.g. the delete
   confirmation page's "what's about to go" listing -- the label column is
   sized to fit rather than splitting the width evenly, since the values are
   mostly short. */
.detail-table th {
  width: 34%;
  min-width: 11rem;
  font-weight: 600;
  vertical-align: middle;
}

.detail-table td { vertical-align: middle; }

.detail-table th .hint {
  display: block;
  font-weight: 400;
  color: var(--text-muted);
  font-size: 0.8rem;
  white-space: normal;
}

form.inline { display: inline; }

.version-layout {
  display: flex;
  align-items: flex-start;
  gap: 2rem;
}

.version-nav {
  flex: 0 0 190px;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  position: sticky;
  top: 1.5rem;
}

.version-nav a {
  padding: 0.5rem 0.75rem;
  border-radius: 6px;
  color: var(--text);
  text-decoration: none;
  font-size: 0.88rem;
  line-height: 1.3;
}

.version-nav a:hover { background: var(--bg-alt); }

.version-nav a.active {
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: 600;
}

.version-content { flex: 1; min-width: 0; }

.scheme-exports {
  display: flex;
  gap: 0.6rem;
  margin-top: 1rem;
}

.filter-bar { margin-bottom: 1rem; }

.filter-bar-top {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.6rem;
}

.filter-bar-groups {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  margin-bottom: 0.6rem;
}

/* Pushes one .field in a .filter-bar-groups row (e.g. a lone submit
   button) to the row's right-hand edge, with the rest staying left-aligned
   -- e.g. collection_round_new.html's own "Show" button next to Search/
   Hide courses. */
.field-push-right {
  margin-left: auto;
}

.inline-form {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.inline-form input[type="text"] {
  width: 10rem;
}

.inline-form input[type="number"] {
  width: 6rem;
}

/* Each Visualize checkbox group (colors/tags/other) as its own vertical
   list rather than one long wrapping row -- a row of varying-length
   labels (e.g. "Foundational - Algorithms and Theory" next to "F")
   wraps raggedly and reads as a jumbled paragraph once there are more
   than a handful of options, which is exactly what a long tag-mode
   collection list runs into. Vertical stacking keeps every option
   scannable regardless of how many/how long they are; the groups
   themselves still sit side by side (via .filter-bar-groups above) when
   there's room, so this isn't a strict return to one filter per row. */
.filter-group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.2rem;
}

.filter-group .hint {
  white-space: nowrap;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 0.15rem;
}

.field { margin-bottom: 0.85rem; }

.field label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.25rem;
}

.field .hint {
  display: block;
  font-weight: 400;
  color: var(--text-muted);
  font-size: 0.8rem;
}

/* A field's own collapsed-by-default explanation, for one genuinely long
   template footnote (docs/Assessment Plan (template).docx's AI-related
   measures guidance, with its own examples table) that would dominate the
   form if shown inline like every other field's plain .hint. Native
   <details>/<summary> -- no JavaScript, this app has none. */
.hint-details {
  margin-top: 0.3rem;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.hint-details summary {
  cursor: pointer;
  color: var(--accent);
  font-weight: 600;
}
.hint-details p { margin: 0.4rem 0; }
.hint-details table { font-size: 0.8rem; margin: 0.4rem 0; }

input[type=text], input[type=number], input[type=file], input[type=search], select, textarea {
  width: 100%;
  max-width: 420px;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 0.95rem;
}

/* The search box static/select-filter.js puts above a <select data-filter>,
   and its "N of M match" counter below it. */
input.select-filter { display: block; margin-bottom: 0.3rem; }
.select-filter-count { display: block; margin-top: 0.2rem; }

button, input[type=submit], a.button {
  display: inline-block;
  background: var(--accent);
  color: var(--accent-contrast);
  border: none;
  border-radius: 4px;
  padding: 0.5rem 1rem;
  font-size: 0.95rem;
  cursor: pointer;
  text-decoration: none;
  line-height: 1.2;
}

button:hover, input[type=submit]:hover, a.button:hover { opacity: 0.9; text-decoration: none; }

/* Column headers of a <table class="sortable">, turned into buttons by
   static/table-sort.js -- styled to read as the header text itself, with
   an arrow for the active column (aria-sort, set by the script). */
button.sort-button {
  background: none;
  color: inherit;
  border: none;
  border-radius: 0;
  padding: 0;
  font: inherit;
  text-align: inherit;
  cursor: pointer;
}
button.sort-button:hover { opacity: 1; text-decoration: underline; }
button.sort-button::after { content: " \2195"; color: var(--text-muted); font-size: 0.8em; }
th[aria-sort="ascending"] button.sort-button::after { content: " \25B2"; color: inherit; }
th[aria-sort="descending"] button.sort-button::after { content: " \25BC"; color: inherit; }

button.secondary, a.button.secondary {
  background: var(--bg-alt);
  color: var(--text);
  border: 1px solid var(--border);
}

button.danger {
  background: var(--error);
}

.row-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

/* Stacks row actions vertically instead of side-by-side -- for tables
   with several other wide columns already, where two buttons side by
   side would force the actions column (sized to fit unwrapped content
   under default table auto-layout) wider than the space actually
   reserved for the table. */
.row-actions-stack {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.3rem;
}

.row-actions-stack .button,
.row-actions-stack form,
.row-actions-stack button {
  width: 100%;
  text-align: center;
}

.row-actions-stack .button,
.row-actions-stack button {
  padding: 0.4rem 0.35rem;
  font-size: 0.82rem;
}

.banner {
  padding: 0.75rem 1rem;
  border-radius: 6px;
  margin-bottom: 1rem;
}

.banner.error { background: var(--error-bg); color: var(--error); }
.banner.ok { background: var(--ok-bg); color: var(--ok); }
.banner.warning { background: var(--warn-bg); color: var(--warn); }

.overlap-clash { background: var(--error-bg); color: var(--error); }
.overlap-clash td { color: var(--error); }

.muted { color: var(--text-muted); font-size: 0.85rem; }

.scheme-header {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.4rem;
  margin-bottom: 0.4rem;
}

.scheme-header div {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.3rem 0.5rem;
  font-weight: 600;
  font-size: 0.95rem;
  text-align: center;
}

.scheme-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.4rem;
  margin-bottom: 1.5rem;
}

.scheme-block {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.4rem 1.6rem 0.4rem 0.5rem;
  font-size: 0.95rem;
  background: var(--bg);
}

.scheme-block a {
  color: #1155cc;
  text-decoration: none;
}

.scheme-block a:hover { text-decoration: underline; }

.scheme-extra-lines {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-top: 0.15rem;
}

.scheme-tags {
  margin-top: 0.25rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

.tag-badge {
  display: inline-block;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0.05rem 0.35rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-muted);
  line-height: 1.4;
}

.scheme-timeslot {
  position: absolute;
  bottom: 0.3rem;
  right: 0.4rem;
  color: var(--text-muted);
  font-size: 0.82rem;
}

.scheme-block.proposal-preview {
  border: 2px dashed var(--accent);
  box-shadow: 0 0 0 2px var(--accent) inset;
}

/* The one proposal a per-proposal Preview was opened from, among the
   other open proposals for the same curriculum applied alongside it. */
.scheme-block.proposal-preview.proposal-focus {
  border-style: solid;
  box-shadow: 0 0 0 3px var(--accent) inset;
}

.scheme-block.proposal-needs-attention {
  border-color: var(--warn);
}

.proposal-warning {
  position: absolute;
  top: 0.2rem;
  right: 0.4rem;
  color: var(--warn);
  font-size: 0.9rem;
}

.scheme-block.slot {
  border-style: dashed;
  background: var(--bg-alt);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.scheme-footnotes { margin-top: -1rem; margin-bottom: 1.5rem; }
.scheme-footnotes p { margin: 0.2rem 0; }

.scheme-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.25rem;
  font-size: 0.9rem;
  color: var(--text-muted);
}

.legend-item { display: inline-flex; align-items: center; gap: 0.4rem; }

.legend-swatch {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 3px;
  border: 1px solid var(--border);
}

/* Weekly test/feedback Gantt charts -- assessment_plan.html's own
   "Weekly schedule test and feedback" card (one test-form label column)
   and curriculum_version_assessment_plan_overlap.html (one grid per
   year+quarter, a full-width .gantt-course-header row above each
   course's own test-form rows, since a course there can span several
   rows -- one per its own test forms, see _build_rows() in
   app/routes/assessment_plans.py). Same explicit grid-column/grid-row
   placement technique as .scheme-grid in _scheme_grid.html, just with a
   variable week count instead of a fixed 4 quarters, and a variable row
   count per course instead of a fixed one row each. */
.gantt-grid {
  display: grid;
  gap: 0.3rem;
  margin-bottom: 1rem;
  align-items: stretch;
}

.gantt-header-cell {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.3rem 0.4rem;
  font-weight: 600;
  font-size: 0.8rem;
  text-align: center;
}

.gantt-header-cell.gantt-exam { background: var(--warn-bg); color: var(--warn); }

.gantt-label-cell {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.85rem;
  padding: 0.2rem 0.4rem;
  min-width: 0;
}

/* A full-width row naming a course, above its own test-form rows rather
   than a column beside them -- spans every week column via
   grid-column: 1 / span 11 (see curriculum_version_assessment_plan_
   overlap.html). Plain heading styling, not a bordered box like the
   week-header cells, so it reads as a section break rather than one
   more data cell. */
.gantt-course-header {
  font-weight: 600;
  font-size: 0.85rem;
  padding: 0.4rem 0.4rem 0.15rem;
  border-top: 2px solid var(--border);
}

/* One populated week's cell: an unstyled layout container -- no border,
   no background -- that just centers whatever marker(s) it holds, so an
   empty week (nothing scheduled there for this row) renders nothing at
   all rather than a visible box. Not rendered at all by the template
   when a week has no entries, but kept borderless either way in case
   that ever changes. min-width: 0 keeps a grid column able to actually
   shrink to its `1fr` share instead of a marker's content forcing it
   wider (the default min-width: auto on a grid/flex item ignores its
   own track size). */
.gantt-cell {
  display: flex;
  flex-wrap: wrap;
  align-content: center;
  justify-content: center;
  gap: 0.15rem;
  min-width: 0;
}

/* A linked weekly item's own mark in the grid: a small filled diamond,
   colored by Formative/Summative, no position number -- the row it's in
   (see .gantt-marker below) already says which test form it is, so
   repeating the number on every single week would just be noise. Full
   detail is still one hover away via the diamond's own title attribute. */
.gantt-diamond {
  width: 0.6rem;
  height: 0.6rem;
  transform: rotate(45deg);
  border: 1px solid var(--border);
  background: var(--bg-alt);
  flex-shrink: 0;
}

.gantt-diamond-formative { background: var(--ok); border-color: var(--ok); }
.gantt-diamond-both { background: var(--warn); border-color: var(--warn); }
.gantt-diamond-summative { background: var(--error); border-color: var(--error); }

/* The row's own identifying marker, in .gantt-label-cell -- unlike
   .gantt-diamond above, this one keeps its position number, since this
   is the one place that number actually needs to be introduced. */
.gantt-marker {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 50%;
  font-size: 0.68rem;
  font-weight: 700;
  border: 1px solid var(--border);
  background: var(--bg-alt);
  color: var(--text);
  flex-shrink: 0;
}

/* Color, not just position, mirrors the marker's own text (verbatim as
   recorded) so the signal survives for anyone who can't rely on color
   alone. */
.gantt-marker-formative { background: var(--ok-bg); color: var(--ok); border-color: var(--ok); }
.gantt-marker-both { background: var(--warn-bg); color: var(--warn); border-color: var(--warn); }
.gantt-marker-summative { background: var(--error-bg); color: var(--error); border-color: var(--error); }

/* A legacy/unlinked weekly item -- its own free-text activity verbatim,
   since there's no test form (and so no position number, and so no row
   of its own beyond the one shared "not linked" row -- see
   _build_rows()) to show a diamond for instead. Rare after
   scripts/backfill_assessment_plan_weekly_item_test_forms.py, but not
   nothing, so still needs its own readable fallback. */
.gantt-chip {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0.05rem 0.3rem;
  font-size: 0.72rem;
  background: var(--bg-alt);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A row's own "what this row is" label, next to its marker in
   .gantt-label-cell -- the test form's name (plus weight, when
   recorded). */
.gantt-legend-label { min-width: 0; }

.grid-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.75rem 1.25rem;
  align-items: end;
}

.actions { margin-top: 1rem; }

/* Save on the left, Submit on the right -- the course guide page's two
   distinct actions (button[form] pointing at two different <form>s), kept
   apart so they don't read as one choice between equally-weighted options. */
.actions-split { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.75rem; }

.list-links a { margin-right: 1rem; }

/* Any "Academic year" column, across every curriculum table (the curricula
   list, the all-versions list, a curriculum's own versions table): keep a
   program's name on one line (e.g. "Pre-Master CSE") and each academic year
   uncut (e.g. "2023-2024" never breaking at its own hyphen), letting other
   columns (Description, Source, Notes) give up the width instead. The
   column is sized to fit its own header, "Academic year", on one line --
   the widest single line it ever needs, since a row listing more than one
   academic year (the curricula list) stacks them one per line rather than
   spreading out, so no stacked line is ever as wide as the header itself. */
.col-nowrap { white-space: nowrap; }
.col-academic-year { white-space: nowrap; }
.col-academic-year a {
  display: block;
  white-space: nowrap;
  margin-right: 0;
}

/* An inline <select> sitting inside running text (e.g. "ALL/ONE of the
   following collections apply:") shouldn't stretch to the input[type=text]
   default of width:100%/max-width:420px -- it should size to its own
   options, like any other inline form control. */
.select-inline { width: auto; max-width: none; }

/* Preserve course_entry_collections.courses' own newlines (one course per
   line) in the table cell, instead of collapsing them per normal HTML
   whitespace rules. */
.multiline { white-space: pre-wrap; }

/* Read-only display for HTML_DETAIL_FIELDS (app/html_text.py) -- real,
   sanitized HTML (see catalogue_field_input()'s own is_html branch in
   course_catalogue.html), not plain text, so no white-space:pre-wrap here
   -- the markup does its own layout. Just enough spacing between block
   elements to read as prose rather than a run-on block, since this app
   otherwise has no rich-content styling at all. */
.rich-text p { margin: 0 0 0.6em; }
.rich-text p:last-child { margin-bottom: 0; }
.rich-text ul, .rich-text ol { margin: 0 0 0.6em; padding-left: 1.4em; }
.rich-text li { margin-bottom: 0.2em; }
.rich-text blockquote {
  margin: 0 0 0.6em;
  padding-left: 0.75em;
  border-left: 3px solid var(--border);
  color: var(--text-muted);
}

.mismatch-row {
  border-bottom: 1px solid var(--border);
  padding: 0.75rem 0;
}

.mismatch-row .choices { display: flex; gap: 1.5rem; margin-top: 0.4rem; }
.mismatch-row label { font-weight: 400; }

.radio-choices { display: flex; gap: 1.5rem; margin: 0.3rem 0 0.75rem; }
.radio-choices label { font-weight: 400; }

.approval-badge {
  display: inline-block;
  background: var(--ok-bg);
  color: var(--ok);
  border: 1px solid var(--ok);
  border-radius: 3px;
  padding: 0.05rem 0.4rem;
  font-size: 0.7rem;
  font-weight: 600;
  vertical-align: middle;
}

.legend-key {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-bottom: 1rem;
}

.legend-key-item { display: flex; align-items: center; gap: 0.5rem; }

/* Groups a card's "facts + its own add-form" cluster into a visibly
   bounded box -- Course enrolment and Lecturers each pack several such
   clusters (entrance requirements, excluded programmes, entry rules;
   responsible lecturer, co-lecturers, instructors) into one card, where
   plain paragraph spacing alone left it unclear which add-form's button
   belonged to which table above it. */
.enrolment-block {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem;
  margin-bottom: 1.25rem;
  background: var(--bg-alt);
}

.enrolment-block:last-child { margin-bottom: 0; }

/* A lecturer's note to the course catalogue coordinator (Educational
   methods'/Assessment's own comment field) isn't part of the OSIRIS record
   itself -- framed as a distinct callout, in the same warning-amber the
   review round's own banners already use for "flag this for someone", so
   it doesn't read as just another data field on the card. */
.coordinator-note {
  border: 1px solid var(--warn);
  background: var(--warn-bg);
  border-radius: 6px;
  padding: 0.75rem 0.75rem 1rem;
  /* Extra room above, on top of .field's own margin-bottom -- this box
     follows directly after the add-form's own submit button, and without
     it the two buttons (Add X above, Save comment now inside this box)
     read as one run of controls instead of two separate actions. */
  margin-top: 2rem;
}

.coordinator-note label { color: var(--warn); }

.coordinator-note .actions { margin-top: 0.75rem; }

/* One posted message in a discussion thread (course guide comments,
   director-review messages) -- same bordered-callout shape as .diff-entry,
   just left-bordered in the neutral border colour rather than a diff kind,
   since every message here is equally "just posted", nothing to highlight
   over anything else. */
.comment-thread { margin-bottom: 0.75rem; }

.comment {
  border-left: 3px solid var(--border);
  padding: 0.1rem 0 0.1rem 0.75rem;
  margin-bottom: 0.75rem;
}

.comment-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.15rem;
  font-size: 0.9rem;
}

/* Opts one field out of the global 420px input cap -- for the course
   guide's prose fields (learning objectives, content, promotional
   descriptions), which are paragraphs rather than the short values that
   cap is sized for. */
.field.wide textarea { max-width: 100%; }

/* Stored-vs-OSIRIS text in the course guide comparison. <pre> so the line
   breaks in an Objectives or Content field -- which are most of what
   differs between the two -- stay visible instead of being collapsed into
   one paragraph, but wrapped and size-capped so a long field doesn't push
   the table off-screen or bury the rows below it. */
/* The comparison's Field column holds a name plus two lines of context, so
   under default table auto-layout -- which hands most of the width to the
   two prose columns -- it collapses to a two-word-per-line sliver. */
/* Label/value rows for a card whose fields are read-only -- the course
   guide's Course details. A two-column table rather than the .grid-form
   layout the editable cards use: laying read-only values out as though they
   were inputs invites someone to try typing into them. The label column is
   sized to fit rather than splitting the width evenly, since the values are
   mostly short codes. */
/* A proportional split rather than a shrink-to-fit label column: now that
   both sides hold full terms ("Bachelor College or Graduate School" against
   "Mathematics and Computer Science") a nowrap label column takes whatever
   its longest label needs and leaves the values a sliver to wrap in. The
   min-width keeps the labels readable once the percentage gets small. */
.detail-table th {
  width: 34%;
  min-width: 11rem;
  font-weight: 600;
  vertical-align: middle;
}

.detail-table td { vertical-align: middle; }

.detail-table th .hint {
  display: block;
  font-weight: 400;
  color: var(--text-muted);
  font-size: 0.8rem;
  white-space: normal;
}

.compare-field { min-width: 11rem; }

.compare-text {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: inherit;
  font-size: 0.85rem;
  max-height: 16rem;
  overflow-y: auto;
  min-width: 18rem;
}

/* Lets a <fieldset disabled> wrap a read-only form without also drawing
   the browser's default fieldset chrome around it -- same use as the
   inline style course_detail.html applies for its own read-only case. */
.bare-fieldset { border: none; padding: 0; margin: 0; }

code {
  background: var(--bg-alt);
  padding: 0.1rem 0.35rem;
  border-radius: 4px;
  font-size: 0.85em;
}

/* Course guide review rounds: the reviewer's diff view
   (catalogue_round.html) and the round banner on the lecturer's own form. */

.status-badge {
  display: inline-block;
  border-radius: 3px;
  padding: 0.05rem 0.45rem;
  font-size: 0.72rem;
  font-weight: 600;
  vertical-align: middle;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.status-open { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn); }
.status-submitted, .status-awaiting-director, .status-committee_pending { background: var(--bg-alt); color: var(--text); border: 1px solid var(--border); }
.status-accepted, .status-applied, .status-committee_approved { background: var(--ok-bg); color: var(--ok); border: 1px solid var(--ok); }
.status-rejected, .status-declined { background: var(--error-bg); color: var(--error); border: 1px solid var(--error); }
.status-withdrawn { background: var(--bg-alt); color: var(--text-muted); border: 1px solid var(--border); }

/* A per-department feature toggle's own "off" state (course guide/
   assessment plan settings) -- neutral, not the alarm-red .status-rejected
   above: a department not having a workflow turned on is a normal,
   expected state, not a failure. */
.status-off { background: var(--bg-alt); color: var(--text-muted); border: 1px solid var(--border); }

.diff-summary { font-size: 1rem; margin: 0 0 0.75rem; }

/* One changed thing. The left rule is what makes a page of them scannable --
   a reviewer counting changes reads the rules, not the prose. */
.diff-entry {
  border-left: 3px solid var(--border);
  padding: 0.1rem 0 0.1rem 0.75rem;
  margin-bottom: 0.9rem;
}

/* The full-record view (?all=1) shows every field/row, changed or not --
   an unchanged one is dimmed and loses its left border so a long record
   still reads calmly and the real changes (still full-strength ins/del
   colour, no dimming) are what actually draws the eye. */
.diff-entry-unchanged {
  border-left-color: transparent;
  opacity: 0.65;
}

.diff-entry-head {
  font-weight: 600;
  font-size: 0.9rem;
  margin-bottom: 0.2rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.diff-inline, .diff-prose { margin: 0.15rem 0; font-size: 0.9rem; }

/* Prose diffs keep their line breaks: a numbered list of learning
   objectives has to still read as a list, or the reviewer loses the
   structure the change sits in. */
.diff-prose { white-space: pre-wrap; }

/* Deliberately the same ins/del the tracked-changes Word document uses --
   these readers already know what struck-through and underlined mean here,
   and inventing a different vocabulary would cost them that. */
.diff-entry ins {
  background: var(--ok-bg);
  color: var(--ok);
  text-decoration: none;
  border-bottom: 1px solid var(--ok);
}

.diff-entry del {
  background: var(--error-bg);
  color: var(--error);
}

/* A repeatable section's row deleted since the round opened, shown inline
   in the table rather than only in the collapsed diff below it -- so a
   reviewer sees it struck through right where it used to sit, instead of
   the row just quietly disappearing. text-decoration on the cell already
   draws the line through any nested markup, so no per-value <del> needed. */
tr.row-removed td:not(:last-child) { text-decoration: line-through; color: var(--error); }

/* Student assistant requests (see docs/STUDENT_ASSISTANTS.md) reuse the
   .status-badge shape above with their own statuses. Deliberately the same
   colour vocabulary the review-round statuses already established on this
   site: amber for "somebody still has to act", neutral for "in someone
   else's hands", green for settled-yes, red for settled-no. */
.status-draft { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn); }
.status-pending_director { background: var(--bg-alt); color: var(--text); border: 1px solid var(--border); }
.status-approved { background: var(--ok-bg); color: var(--ok); border: 1px solid var(--ok); }
.status-not_approved { background: var(--error-bg); color: var(--error); border: 1px solid var(--error); }
/* A closed recruitment round is finished, not refused -- neutral rather
   than the red .status-rejected would give it. */
.status-closed { background: var(--bg-alt); color: var(--text-muted); border: 1px solid var(--border); }

/* Stored prose shown read-only in a .detail-table cell -- an SA request's
   justification and vacancy text are whole paragraphs typed into a
   textarea, so their line breaks have to survive being displayed, same
   reasoning as .diff-prose above. */
.detail-table td.prose { white-space: pre-wrap; }

/* Help pages (help.html): the user guide rendered from docs/user-guide/. */
.help-nav-heading {
  margin-top: 0.75rem;
  padding: 0 0.75rem;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.help-content { line-height: 1.55; }
.help-content details { margin: 1rem 0; }
.help-content summary { cursor: pointer; font-weight: 600; color: var(--accent); }
.help-guide-list { list-style: none; padding-left: 0; }
.help-guide-list li { margin: 0.6rem 0; }
.help-content h1 { margin-bottom: 1rem; }
.help-content h2 { margin-top: 2rem; }
.help-content img { max-width: 100%; height: auto; border: 1px solid var(--border); border-radius: 6px; }
.help-content pre { background: var(--bg-alt); padding: 0.75rem 1rem; border-radius: 6px; overflow-x: auto; }
.help-content pre code { background: none; padding: 0; }
.help-content blockquote {
  margin: 1rem 0;
  padding: 0.5rem 1rem;
  border-left: 4px solid var(--warn);
  background: var(--warn-bg);
  border-radius: 0 6px 6px 0;
}
.help-content blockquote p { margin: 0.4rem 0; }
