/* University page layout: the manager portal, the Certification Designer, the Pathway Editor and the rep portal.
   Dan, 2026-10-22: on a 5K monitor the content sat in a 1200 px block in the middle with wide empty sides.
   The rules, wide to narrow:
   - The content column fills the space beside the side menu (or the whole window on the rep portal), with a gutter that
     grows a little with the window. No fixed-width island.
   - Cards and lists of cards flow into more columns as the window grows (auto-fill grids with a minimum card width).
   - Tables take the column up to a cap; every table sits in its own scroll box, so a wide one never scrolls the page.
   - Running text keeps a readable line, at most about 85 characters. Form fields and panels of form fields are capped too.
   - The side menu keeps its fixed width and the page header stays pinned; on a phone the header is made compact so the
     pinned part takes little of the screen.
   Linked after each page's own styles, so these rules win over the old fixed widths. */

/* DM Mono (numerals) is linked from each page's <head> with the other fonts; an @import here blocked rendering. */

/* 64ch is about 85 characters of running text: a "0" is wider than the average letter in Manrope. */
:root { --uni-gutter: clamp(16px, 1.5vw, 48px); --uni-prose: 64ch; --uni-target: 24px; --uni-section: 48px;
  --uni-mono: 'DM Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
  /* The past-due hatch, drawn in the mark's own colour. */
  --uni-hatch: repeating-linear-gradient(45deg, currentColor 0 1.5px, transparent 1.5px 3.5px);
  /* --text-subtle (#8B94A3 on the pages) is 2.7:1 on white; text that used it reads the theme's third ink instead. */
  --text-subtle: var(--th-text-3); }
@media (pointer: coarse) { :root { --uni-target: 44px; } }

/* ---- Content column ---- */
.wrap { max-width: none; padding-left: var(--uni-gutter); padding-right: var(--uni-gutter); }
/* The content column is as tall as the side menu, and its footer (manager portal) sits at the bottom of it. */
.uni-shell > .uni-body { align-self: stretch; display: flex; flex-direction: column; }
.uni-body > main { flex: 1 0 auto; max-width: none; margin: 0; }
/* Designer and Pathway Editor: their <main> is the page grid itself. */
.uni-body > main:not(:has(> .wrap)) { padding: var(--uni-gutter); }
/* Rep portal: the footer stays at the bottom of a short page. auth-gate.js shows and hides #appWrap by writing an
   inline display (block, or none on sign-out and session loss), so this rule needs !important to make it a flex column.
   It keys off that same inline style on purpose: a class set when the page shows would keep the flex (and so the page)
   visible after auth-gate hides it again. If auth-gate ever shows the page another way, change this selector too. */
body > #appWrap:has(> nav.tabs):not([style*="none"]) { display: flex !important; flex-direction: column; min-height: 100vh; }
#appWrap > nav.tabs ~ main { flex: 1 0 auto; }

/* ---- Readable text ---- */
main p, main dd, main .hint, main .sentence { max-width: var(--uni-prose); }
main .result-card p, main [style*="text-align:center"] p, main [style*="text-align: center"] p { margin-left: auto; margin-right: auto; }

/* ---- Form fields: as wide as the column needs, never a field across a 5K screen ---- */
main :is(input:not([type]), input[type=text], input[type=search], input[type=email], input[type=url], input[type=tel], input[type=password], select):not(#view-ramp *) { max-width: min(100%, 48rem); }
main :is(input[type=number], input[type=date], input[type=time], input[type=datetime-local], input[type=month]):not(#view-ramp *) { max-width: min(100%, 16rem); }
/* Lesson text and other long entries are typed as running text. */
main textarea { max-width: min(100%, 100ch); }
/* Panels that are a form or a short callout keep to a readable width instead of stretching with the field stuck left. */
#ap-form, #acc-form, .rep-callout { max-width: 70rem; }
/* Designer form rows (Division, Stage, Card; Add a unit) keep their fields together on a wide screen. */
.uni-body main .row { max-width: min(100%, 70rem); }

/* ---- Small controls: at least 24 px to hit, 44 px on a touch screen ---- */
.uni-body button.why { min-height: var(--uni-target); display: inline-flex; align-items: center; }
.uni-body a.train { display: inline-flex; align-items: center; min-height: var(--uni-target); }
.uni-body main details > summary { min-height: var(--uni-target); padding-top: 2px; padding-bottom: 2px; box-sizing: border-box; }
.uni-body :is(#view-assignments, #view-training) input[type=checkbox] { width: 18px; height: 18px; }
.uni-body #view-training label:has(> input[type=checkbox]) { min-height: var(--uni-target); min-width: var(--uni-target); align-items: center; }
/* The Access form's selects, My path course links and the rep header's title link. */
#acc-form select, #acc-form input:not([type=checkbox]) { min-height: var(--uni-target); }
.mp-course a { display: inline-flex; align-items: center; min-height: var(--uni-target); }
/* The title link is held at 24 px even on touch: 44 px would make the pinned phone header taller for a link that is
   also reachable from the site buttons. */
.page-header a.brand { min-height: 24px; }
/* Touch screens: buttons, filter chips, fields and the rep portal's tabs are all at least 44 px. */
@media (pointer: coarse) {
  /* .btn.btn outranks the Designer and Pathway Editor's own small button sizes (.btn.sm). */
  main :is(.btn.btn, button.chip, select, input:not([type]), input[type=text], input[type=search], input[type=email], input[type=url], input[type=tel], input[type=password], input[type=number], input[type=date]),
  nav.tabs button { min-height: var(--uni-target); }
  main :is(.btn.btn, button.chip), nav.tabs button, .uni-body th button.th-sort { min-width: var(--uni-target); box-sizing: border-box; }
  #acc-form input[type=checkbox] { width: 18px; height: 18px; }
  #acc-form label:has(> input[type=checkbox]) { display: inline-flex; align-items: center; gap: 6px; min-height: var(--uni-target); }
  main a.btn { display: inline-flex; align-items: center; justify-content: center; }
}
/* Sort buttons in table headings, and the small revoke button on an Access role (its inline style sets no height). */
.uni-body th button.th-sort { min-height: var(--uni-target); }
.uni-body button[data-revoke] { min-height: var(--uni-target) !important; }

/* ---- Cards flow into columns ---- */
.uni-body #help-list {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 34rem), 1fr)); gap: var(--s-3, 12px) var(--s-4, 16px); align-items: start;
}
.uni-body #help-list > .queue-item { margin-bottom: 0; }
.uni-body #help-list > .empty { grid-column: 1 / -1; }
/* Pathway Editor: the course and graduation cards, in pathway order reading left to right. */
.pw-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 40rem), 1fr)); gap: 0 var(--s-4, 16px); align-items: start; }
/* Dan, 2026-10-23: the stages ran together once every card and graduation sat in one grid. Each stage is now its own
   full-width section: a header band (number, name, course count, due days, the graduation it ends with), a coloured
   rule down the left and a light tint, its course cards in the grid, and its graduation as a closing banner. The words
   carry the meaning; the colour only helps the eye. Rules are at least 3:1 against the tint, text at least 4.5:1. */
.pw-stage { --pw-rule: #2F6A0C; --pw-tint: #F3F9EC; --pw-band: #E2F0D2;
  margin: 0 0 32px; padding: 0 14px 14px; border: 1px solid var(--pw-band); border-left: 6px solid var(--pw-rule); border-radius: var(--radius-card, 0); background: var(--pw-tint); }
.pw-stage.pw-l2 { --pw-rule: var(--accent-steel); --pw-tint: color-mix(in srgb, var(--accent-steel) 7%, #fff); --pw-band: color-mix(in srgb, var(--accent-steel) 16%, #fff); }
.pw-stage.pw-l3, .pw-stage.pw-adv { --pw-rule: #5A4A96; --pw-tint: #F4F2FA; --pw-band: #E4DFF2; }
.pw-stage.pw-none { --pw-rule: #5B6576; --pw-tint: #F5F6F7; --pw-band: #E6E8EC; border-left-style: dashed; }
/* --pw-ink: the course-code text on a course card, set on the badge's 24% tint of the stage rule. The violet rule reads at
   4.5:1 on its own tint; the green, steel and grey rules do not (4.42, 3.73 and 4.07 on Alabaster), so those stages take
   the matching theme ink instead. Dark sets pale inks in theme-tokens.css. tests/pathway-drag.test.mjs measures every pair. */
.pw-stage { --pw-ink: var(--th-ok, #266406); }
.pw-stage.pw-l2 { --pw-ink: var(--th-info, #245A82); }
.pw-stage.pw-l3, .pw-stage.pw-adv { --pw-ink: var(--pw-rule); }
.pw-stage.pw-none { --pw-ink: var(--th-text-2, #44505F); }
/* The header band sticks under the pinned page header while its stage scrolls by, so a long stage always says which
   stage it is. It stays below the Save bar (z-index 4 under 5) and turns compact while stuck (a scroll-state query;
   browsers without one keep the full band). Below 861 px the University menu bar is pinned under the page header too,
   so the band does not stick there: header, menu bar and band together would take too much of a phone screen. */
.pw-stage-head { margin: 0 -14px 12px; }
.pw-stage-in { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; padding: 12px 14px;
  background: var(--pw-band); border-bottom: 1px solid var(--pw-rule); border-radius: var(--radius-card, 0); }
@media (min-width: 861px) {
  .pw-stage-head { position: sticky; top: var(--ct-hdr-h, 0px); z-index: 4; container-type: scroll-state; }
  /* Fields reached with Tab scroll clear of the page header and the band. */
  html.ct-hdr-pinned:has(.pw-stage-head), html:has(.pw-stage-head) { scroll-padding-top: calc(var(--ct-hdr-h, 0px) + 72px); }
  @container scroll-state(stuck: top) {
    .pw-stage-in { padding: 6px 14px; border-radius: 0; box-shadow: 0 4px 10px rgba(15, 32, 56, .12); }
    .pw-stage-in h3 { font-size: 1.0625rem; }
    .pw-stage-in .pw-stage-num { width: 24px; height: 24px; font-size: 0.875rem; }
  }
}
.pw-stage-head h3 { margin: 0; font-size: 1.25rem; color: var(--ink, #0F2038); }
.pw-stage-num { width: 32px; height: 32px; border-radius: var(--radius-card, 0); background: var(--pw-rule); color: #fff; display: inline-flex; align-items: center; justify-content: center;
  font-family: Fraunces, Georgia, serif; font-size: 1.0625rem; flex-shrink: 0; }
/* Facts with a dot before each. The row is shifted left by one dot's width and clipped, so the dot in front of the
   first fact on any line (the first line or a wrapped one) is hidden, and no line starts with a dot. */
main .pw-stage-meta { margin: 0; max-width: none; overflow: hidden; font-size: 0.875rem; font-weight: 600; color: #3A4352; }
.pw-meta-in { display: flex; flex-wrap: wrap; gap: 2px 0; margin-left: -24px; }
.pw-meta-in > span::before { content: ""; display: inline-block; width: 4px; height: 4px; margin: 0 10px; border-radius: 50%; background: currentColor; vertical-align: middle; }
.pw-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); border: 0; }
/* The stage stripe on a course card stays: it says which stage the course is in (Dan, 2026-10-23). edit-theme.css no
   longer gives a plain card a stripe, so the width is set here as well as the colour. */
.pw-stage .card { border-left: 4px solid var(--pw-rule); }
.pw-stage .pw-items > .card { margin: 0 0 12px; }
/* The graduation that ends a stage: a banner across the whole section, not a tile in the grid, labelled as the close. */
.pw-stage > .card.grad.pw-close { margin: 4px 0 0; padding: 12px 16px; border: 2px solid var(--pw-rule); border-left-width: 6px; background: var(--pw-band); }
.pw-stage > .card.pw-close .cardhead strong { font-size: 1.0625rem; }
main .pw-ends-tag { display: inline-block; margin: 0 0 6px; padding: 2px 10px; border-radius: 0; background: var(--pw-rule); color: #fff;
  font-size: 0.75rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.pw-stage > .pw-empty { margin: 0 0 12px; }
/* Course cards (Dan, 2026-10-23, option A): one card per course, a stripe and a code badge in the stage colour, square
   corners, no help prose. Grab a card anywhere but a field or a control and drag it within its stage or into another; a
   focused card moves with Alt and the arrow keys (Shift as well to cross a stage). Remove is the corner cross. */
.pw-stage .pw-items > .card.pw-course { position: relative; border-left: 4px solid var(--pw-rule); border-radius: 0; cursor: grab;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.pw-course :is(input, textarea) { user-select: text; -webkit-user-select: text; }
.pw-course :is(input, select, textarea, label, summary, a, button, details) { cursor: auto; }
.pw-course:focus-visible { outline: 3px solid var(--th-focus, var(--accent-steel-focus)); outline-offset: 2px; }
.pw-course > .cardhead, .pw-close > .cardhead { padding-right: 28px; }
main .pw-stage .card .code, main .pw-stage .card .code.none { background: color-mix(in srgb, var(--pw-rule) 24%, var(--th-surface, #FFFFFF)); color: var(--pw-ink);
  font-weight: 600; border-radius: 0; }
main .pw-stage .card .code.none { outline: 1px dashed var(--pw-ink); outline-offset: -1px; }
.pw-course .pw-name { color: inherit; text-decoration: none; }
.pw-course a.pw-name:hover { text-decoration: underline; }
main .pw-facts { margin: 4px 0 6px; max-width: none; font-size: 0.8125rem; font-weight: 600; color: var(--th-text-2, #44505F); }
main .pw-facts:empty { display: none; }
.pw-facts .pw-dot { margin: 0 6px; }
/* The corner cross: shown while the card is hovered or holds focus, always on a screen with no hover. */
.pw-stage .card.pw-close { position: relative; }
.pw-x { position: absolute; top: 4px; right: 4px; width: 28px; height: 28px; display: grid; place-items: center; padding: 0; margin: 0;
  border: 1px solid transparent; border-radius: 0; background: transparent; color: var(--th-bad, var(--rose)); font: 700 14px/1 Manrope, system-ui, sans-serif;
  cursor: pointer; opacity: 0; }
.card:is(:hover, :focus-within) > .pw-x { opacity: 1; }
.pw-x:hover, .pw-x:focus-visible { background: var(--th-bad-tint, #F9D3CD); border-color: currentColor; }
.pw-x:focus-visible { outline: 2px solid var(--th-focus, var(--accent-steel-focus)); outline-offset: 1px; }
@media (hover: none) { .pw-x { opacity: 1; } }
@media (pointer: coarse) { .pw-x { top: 0; right: 0; width: 40px; height: 40px; } }
/* Dragging: the card lifts and follows the pointer, a dashed slot in the target stage's colour shows where it will land,
   and the cards around the slot slide to make room (pathway.html animates that, not with reduced motion). */
.pw-stage .pw-items > .card.pw-course.pw-lifted { position: fixed; z-index: 60; margin: 0; pointer-events: none; cursor: grabbing;
  box-shadow: var(--th-shadow-card, var(--shadow-md)); transform: rotate(1deg) scale(1.02); }
.pw-ph { margin: 0 0 12px; border: 2px dashed var(--pw-rule); background: color-mix(in srgb, var(--pw-rule) 10%, transparent); }
html.pw-dragging, html.pw-dragging * { cursor: grabbing !important; }
html.pw-dragging { user-select: none; -webkit-user-select: none; }
.pw-stage.pw-over { outline: 2px solid var(--pw-rule); outline-offset: 2px; }
.pw-items.pw-drop-empty { min-height: 56px; margin: 0 0 12px; border: 1px dashed color-mix(in srgb, var(--pw-rule) 50%, transparent); }
/* Undo after a remove: about six seconds, held while the pointer or focus is on it. */
.pw-toast { position: fixed; left: 50%; bottom: 88px; transform: translateX(-50%); z-index: 70; display: flex; gap: 12px; align-items: center;
  max-width: calc(100vw - 32px); padding: 8px 8px 8px 16px; border-radius: 0; background: var(--th-surface-ink, #1A2332); color: var(--th-on-ink, #FFFFFF);
  box-shadow: var(--th-shadow-card, var(--shadow-md)); font-size: 0.875rem; font-weight: 600; }
.pw-toast[hidden] { display: none; }
.pw-undo { min-height: var(--uni-target, 24px); padding: 8px 12px; border: 1px solid currentColor; border-radius: 0; background: transparent; color: inherit;
  font: 700 0.875rem/1 Manrope, system-ui, sans-serif; cursor: pointer; }
.pw-undo:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
/* A moved card flashes briefly so the eye finds it again; not with reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .pw-stage .card.pw-moved { animation: pw-moved 1.2s ease-out; }
  @keyframes pw-moved { from { box-shadow: 0 0 0 4px var(--pw-rule), 0 4px 14px rgba(15, 32, 56, .18); } to { box-shadow: 0 0 0 0 transparent; } }
}
@media (max-width: 560px) {
  .pw-stage { padding: 0 8px 8px; margin-bottom: 24px; }
  .pw-stage-head { margin: 0 -8px 10px; }
  .pw-stage-in { padding: 10px 8px; }
  /* One fact a line on a phone, with no dots. */
  main .pw-stage-meta { flex-basis: 100%; }
  .pw-meta-in { flex-direction: column; margin-left: 0; }
  .pw-meta-in > span::before { display: none; }
  .pw-stage > .card.grad.pw-close { padding: 10px; }
}
/* Save pathway: docked across the foot of the editor panel, with its own background and a shadow above it. It sits in
   the page flow at the end of the panel, so the last card is never stuck under it, and it stays in view while a long
   pathway scrolls. Fields reached with Tab scroll clear of it. */
.panel > .bar.pw-save { position: sticky; bottom: 0; z-index: 5; margin: 20px -16px -16px; padding: 12px 16px; background: #fff;
  box-shadow: 0 -6px 16px rgba(15, 32, 56, .14); border-radius: var(--radius-card, 0); }
html:has(.bar.pw-save) { scroll-padding-bottom: 96px; }

/* Rep portal, My path: the skills of a stage flow into columns on a wide screen; the stage heading and the
   graduation box span them all, and a skill (heading and courses) never splits across columns. */
.mp-stage { column-width: 30rem; column-gap: var(--s-6, 32px); }
.mp-stage > :not(.mp-group) { column-span: all; }
.mp-group { break-inside: avoid; }
.mp-skill { margin-top: 0; padding-top: var(--s-4, 16px); }
.mp-needs { max-width: 60rem; }
.mp-course { break-inside: avoid; }
.mp-list { margin-bottom: var(--s-2, 8px); }
.dash-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 30rem), 1fr)); }

/* ---- Tables ---- */
/* Every data table sits in a box that scrolls sideways when the table is wider than the column. The small padding
   (taken back by the margin) keeps focus rings on the first and last columns from being cut off. */
.tbl-scroll { overflow-x: auto; max-width: 100%; padding: 3px; margin: -3px; }
/* tbl-scroll.js makes a box that overflows a focusable region, so it can be scrolled from the keyboard. */
.tbl-scroll[data-tbl-region]:focus-visible { outline: 2px solid var(--solar-green-dark, #5FA520); outline-offset: -2px; }
main table.data { max-width: 150rem; }
/* Classes, attendance grid: the rep's name stays in view while the days scroll sideways. overflow: clip keeps the
   rounded corners without making the table a scroll box of its own, which would stop the name column sticking.
   left: -3px covers the scroll box's 3 px focus-ring padding, so no sliver of the days shows beside the names. */
table.att-grid { overflow: clip; }
table.att-grid :is(th, td):first-child { position: sticky; left: -3px; z-index: 1; background: var(--surface-white, #fff); box-shadow: inset -1px 0 0 var(--hairline, #d8dbd4); }
table.att-grid thead th:first-child { background: var(--ink, #0f2038); }
table.att-grid tbody tr:nth-child(even) td:first-child { background: var(--surface, #f3f5ef); }
/* The name column holds a name, whole words only, and never takes most of a phone's width. */
table.att-grid :is(th, td):first-child { min-width: 7rem; }
@media (max-width: 560px) { table.att-grid :is(th, td):first-child { max-width: 9rem; padding-left: 10px; padding-right: 10px; } }
#access-body table.data { width: auto; min-width: min(100%, 40rem); }
/* An email may break, but only once it has a reasonable width, so it is not squeezed one letter per line. */
main table.data td[data-label="Email"] { overflow-wrap: anywhere; min-width: 15ch; }
@media (min-width: 901px) and (max-width: 1500px) {
  main table.data th, main table.data td { padding-left: 10px; padding-right: 10px; }
  main table.data .mini-bar { width: 48px; }
}
/* ---- Phone, and large text: rows of tiles and fields stack rather than push the page sideways ---- */
@media (max-width: 900px) {
  .kpi-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr)); }
  .kpi { min-width: 0; overflow-wrap: anywhere; }
}
@media (max-width: 700px) { .ts-row { grid-template-columns: 1fr !important; } }
@media (max-width: 560px) { .nh-chip { white-space: normal; max-width: 100%; } }

/* Narrow laptop and small tablet in landscape: the progress bar steps aside; the "2/8" next to it carries the number. */
@media (max-width: 1280px) { main table.data .mini-bar { display: none; } }

/* ---- Tablet and phone header: compact so the pinned part stays small ---- */
.h1-role { display: none; }
/* Below 900 px the "Signed in as" row drops under the title and the header grows past the 140 px that site-nav.js
   will pin, so it would scroll away. The account menu at the top right already shows the name and has Sign out
   (and comes first in the tab order), so the row is left out whenever that menu is on the page. */
@media (max-width: 900px) {
  :root:has(#ct-nav) .page-header .user { display: none; }
  .page-header { padding-top: var(--s-4, 16px); padding-bottom: var(--s-3, 12px); }
}
@media (max-width: 560px) {
  .page-header { padding: 0 0 4px; }
  /* site-nav.js reserves a strip at the top for its buttons; keep that and only trim what sits below it.
     "html" puts this ahead of site-nav's own rule of the same shape, which loads later. */
  html .page-header .wrap { padding-top: calc(var(--ct-nav-h, 40px) + 12px) !important; gap: 4px 12px; align-items: center; }
  .page-header .title-block .eyebrow { display: none; }
  /* The eyebrow that says "Manager Access" is hidden here, so the manager title says it instead. */
  .h1-role { display: inline; }
  .page-header .title-block h1 { font-size: 1.0625rem; line-height: 1.2; overflow-wrap: anywhere; }
  .page-header .user { position: static; margin: 0; gap: 8px; font-size: 0.75rem; }
  .page-header .user button { padding: 3px 8px; font-size: 0.75rem; }
  /* Designer and Pathway Editor headers. */
  header.top { padding-top: calc(var(--ct-nav-h, 40px) + 8px); padding-bottom: 6px; }
  header.top .eyebrow { display: none; }
  header.top h1 { font-size: 1.0625rem; line-height: 1.2; overflow-wrap: anywhere; }
  /* Rep portal: the tab row pinned under the header keeps 44 px touch targets but no extra padding. */
  nav.tabs button { padding-top: 0; padding-bottom: 0; }
}
/* Short screens (up to 900 px tall): the rep portal's tab row is a little slimmer, and its header drops the eyebrow and puts the
   signed-in row beside the site buttons, so header and tabs stay under about 15% of the height. */
@media (max-height: 900px) and (min-width: 561px) { nav.tabs button { padding-top: 10px; padding-bottom: 10px; } }
@media (max-height: 900px) and (min-width: 901px) {
  #appWrap:has(> nav.tabs) > .page-header { padding-top: 12px; padding-bottom: 10px; }
  #appWrap:has(> nav.tabs) > .page-header .eyebrow { display: none; }
  #appWrap:has(> nav.tabs) > .page-header .user { top: 10px; right: calc(var(--ct-nav-w, 280px) + 26px); }
}

/* ---- Phone and tablet: the University menu bar stays pinned under the header ---- */
@media (max-width: 860px) {
  /* ".uni-shell >" outranks the menu's own sheet, which loads later. */
  .uni-shell > .uni-nav { position: sticky; top: var(--ct-hdr-h, 0px); z-index: 50; }
  .uni-shell > .uni-nav.open { max-height: calc(100vh - var(--ct-hdr-h, 0px)); overflow-y: auto; }
  /* The open list scrolls under its Menu button, which stays in view so the menu can always be closed. */
  .uni-shell > .uni-nav.open .uni-menu { position: sticky; top: 0; z-index: 1; background: #fafbf7; }
  /* Keep a focused field clear of the header and the menu bar. */
  html:has(.uni-shell > .uni-nav) { scroll-padding-top: calc(var(--ct-hdr-h, 0px) + 72px); }
}
/* A phone on its side (under 420 px tall): site-nav.js lets the header go, and the menu bar scrolls away too, so the
   page keeps its height. */
@media (max-width: 860px) and (max-height: 419px) {
  .uni-shell > .uni-nav { position: static; }
  html:has(.uni-shell > .uni-nav) { scroll-padding-top: 12px; }
}

/* ---- Rep portal tab row: a fade at the right edge while more tabs are hidden off to the side ---- */
@property --uni-tabfade { syntax: '<length>'; inherits: false; initial-value: 0px; }
@keyframes uni-tabfade { from { --uni-tabfade: 40px; } to { --uni-tabfade: 0px; } }
@supports (animation-timeline: scroll()) {
  @media (max-width: 860px) {
    /* The scroll timeline is only active when the row overflows, so a row that fits gets no fade. */
    nav.tabs .wrap { animation: uni-tabfade linear both; animation-timeline: scroll(self inline);
      -webkit-mask-image: linear-gradient(to right, #000 calc(100% - var(--uni-tabfade)), transparent);
      mask-image: linear-gradient(to right, #000 calc(100% - var(--uni-tabfade)), transparent); }
  }
}

/* ---- House marks (page plan D1 to D3, 2026-10-23) ---- */
/* Numerals in DM Mono, the same width for every digit, so counts and days line up. Fraunces stays on hero and strip
   figures; labels stay in Manrope. */
.uni-num, .mp-code, .code, main code, main kbd, .pg-tile-nums b, .pg-count,
table.att-grid :is(tbody, tfoot) td:not(:first-child), #view-cohorts table.data td[data-label="Reps"] {
  font-family: var(--uni-mono); font-variant-numeric: tabular-nums; }

/* Status is shape plus colour, never colour alone: an 8px square before the state word, in the word's own colour.
   Done is filled, open is outlined, past due (or a retake) is hatched, locked is dotted, not started is faint. */
.status-badge, .mp-chip { border-radius: 0; }
:is(.status-badge, .mp-chip:is(.ok, .lock, .late))::before, .mp-grad > strong:first-child::before {
  content: ""; display: inline-block; width: 8px; height: 8px; margin-right: 6px; box-sizing: border-box;
  border: 1.5px solid currentColor; background: transparent; vertical-align: 0; }
:is(.status-approved, .mp-chip.ok)::before, .mp-grad.earned > strong:first-child::before { background: currentColor; }
:is(.status-retake, .mp-chip.late)::before { background: var(--uni-hatch); }
.mp-chip.lock::before { border-style: dotted; }
.status-not-started::before { opacity: .45; }

/* Empty states: one noun phrase, left-aligned on a solid hairline. No dashed box, nothing centred. The Pathway
   Editor's empty calendar track shares the class name and keeps its own look. */
main .empty:not(.cal-track) { text-align: left; border-style: solid; padding: var(--s-3, 12px) var(--s-4, 16px); }

/* A fact kept off the page: a small square mark whose hover, focus and accessible name carry the words. The lock is the
   same mark for a privacy promise. The mark draws at 16px inside a target of --uni-target (24px, 44px on touch); the
   negative margins keep the larger target from pushing the line apart. On focus (a keyboard, or a tap) the words show
   in a box under the mark, as the squares' tips do; a mouse keeps the title. */
.uni-mark { position: relative; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
  width: var(--uni-target); height: var(--uni-target); vertical-align: middle; border: 0; background: none;
  margin: calc((16px - var(--uni-target)) / 2) calc((16px - var(--uni-target)) / 2) calc((16px - var(--uni-target)) / 2) calc(8px + (16px - var(--uni-target)) / 2);
  color: var(--th-text-2); font: 500 0.6875rem/1 var(--uni-mono); cursor: help; }
.uni-mark::before { content: "i"; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
  width: 16px; height: 16px; border: 1.5px solid var(--th-text-3); }
.uni-mark:focus-visible { outline: 2px solid var(--th-focus); outline-offset: 2px; }
.uni-mark:focus { z-index: 30; }
.uni-mark:focus::after { content: attr(aria-label); position: absolute; left: 0; top: calc(100% + 6px); z-index: 30;
  width: max-content; max-width: min(36ch, calc(100vw - 32px)); padding: 6px 8px; box-sizing: border-box;
  background: var(--th-surface); color: var(--th-text); border: 1px solid var(--th-border-strong, var(--th-text));
  border-radius: var(--radius-card, 0); box-shadow: var(--th-shadow-soft, none);
  font: 500 12px/1.35 Manrope, system-ui, sans-serif; letter-spacing: 0; text-transform: none; text-align: left; white-space: normal; pointer-events: none; }
/* The lock: a 12 x 9 body (a centred fill) under a shackle. */
.uni-mark.uni-lock { background: linear-gradient(var(--th-text-2), var(--th-text-2)) center calc(50% + 3px) / 12px 9px no-repeat; }
.uni-mark.uni-lock::before { content: ""; position: absolute; left: calc(50% - 4px); top: calc(50% - 7px); width: 8px; height: 7px;
  border: 2px solid var(--th-text-2); border-bottom: 0; border-radius: 4px 4px 0 0; }
/* A one-word state where a sentence used to be ("None", "No role"). */
.uni-state { font-size: 0.8125rem; font-weight: 600; color: var(--th-text-2); }
/* Classes: the reps in a class, one square chip each. */
.uni-chips { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; padding: 0; }
.uni-chips > li { display: inline-flex; align-items: center; gap: 4px; padding: 2px 2px 2px 8px; border: 1px solid var(--hairline); background: var(--surface-white); }

/* Sections inside a view sit 48px apart, so the parts of a page read as parts. */
:is(.view, #access-body, #archive-body) > h3:not(:first-child) { margin-top: var(--uni-section); }

/* Stripes only where they encode something. My path's Today box, the graduation box and the Reps callout carried a
   coloured left edge that meant nothing; the graduation box says Earned in words and in its mark. */
.mp-today { border-left: 1px solid var(--hairline); }
.mp-grad, .mp-grad.earned { border-left: 0; }
/* The callout's stripe is an inline style, so only !important reaches it. */
.panel.rep-callout { border-left: 1px solid var(--hairline) !important; }
:root[data-theme] .panel.rep-callout { border-left: 1.5px solid var(--th-card-line) !important; }
/* Course codes on My path lean square. */
.mp-code { border-radius: var(--radius-card, 0); }
