/*
 * ── TORVI TRAINING (1 Oct) ────────────────────────────────────────────────────────────────────
 *
 * ONE STYLESHEET FOR SEVEN PAGES, linked rather than copied into each. The shell's chrome (grid,
 * sidebar, .main) comes from shell-chrome-2026-09-28.css; this is Training's own furniture: the
 * tiles, the course rows, the quiz answers, the status chips.
 *
 * EVERY COLOUR IS A TOKEN. The spec names hex values from the design canvas -- #EAF5EF for a
 * correct answer, #FBE0D8 for overdue -- and each of those already exists in the palette as a
 * light/dark PAIR. Writing the hex would give a page that is right in daylight and unreadable at
 * night, which is the one thing the palette file exists to prevent.
 *
 * THE LEARNER'S SCREENS WORK AT 390px, which is where they are actually read: a phone held in one
 * hand in a corridor. Answer rows are 64px minimum because they are tapped, not clicked.
 */

/*
 * ── THE PAGE FURNITURE EVERY OTHER PAGE KEEPS IN ITS OWN <style> BLOCK ───────────────────────
 *
 * .btn, .error, .success, .row-form, .field-hint and the rest are defined inline on each of the
 * 31 existing shell pages. A page that LINKS its CSS instead -- which these do, because they are
 * written for the CSP that is coming -- inherits none of it.
 *
 * FOUND TWICE, THE SECOND TIME BY LOOKING: mobilecheck caught the missing .data-table (the report
 * was 213px wider than a phone), and the learner's Start button was still rendering as an
 * underlined link in the browser afterwards. Nothing was going to catch that one -- an unstyled
 * button is a button -- so the lesson is that linking a stylesheet means bringing ALL of the
 * furniture, not the parts a gate happens to measure.
 *
 * Copied from visitors.html, which is the nearest page in shape, with the deprecated aliases
 * swapped for their replacements.
 */
.btn { padding:9px 14px; font-size:13.5px; font-weight:600; font-family:inherit;
       border:1px solid transparent; border-radius:8px; cursor:pointer; text-decoration:none;
       display:inline-block; }
.btn-primary { background:var(--teal); color:var(--on-teal); }
/*
 * THE SECONDARY BUTTON, and the default (3 Oct). A `.btn` with no variant set only padding, a
 * radius and a transparent border -- so an ANCHOR carrying it kept the browser's own link colour
 * and underline. "View as learner" sat beside Publish as a blue link, and so did "Edit course"
 * and "Replace the file". A class called btn that does not look like a button is a trap for the
 * next person who uses it, so the base class now carries the secondary look itself and
 * .btn-secondary names it for anybody writing new markup.
 */
/*
 * ...AND IT MUST NOT TAKE THE VARIANTS WITH IT (7 Oct, item 22). Written as a bare `.btn` it sat
 * AFTER .btn-primary at the same specificity, so every primary button on these pages lost its teal
 * and took the card's background -- with .btn-primary's own white text still on it. White on white
 * in Light, and dark ink on a dark card in Dark: ten invisible buttons, found by measuring them.
 */
.btn:not(.btn-primary):not(.btn-danger):not(.btn-ghost), .btn-secondary { background:var(--card); color:var(--heading); border-color:var(--line); }
.btn:not(.btn-primary):not(.btn-danger):not(.btn-ghost):hover, .btn-secondary:hover { background:var(--surface-2); }
.btn-primary:hover { background:var(--teal-dark); }
.btn-ghost { background:var(--card); color:var(--heading); border-color:var(--surface-2); }
.btn-sm { padding:6px 11px; font-size:12.5px; }
/*
 * A DISABLED CONTROL IS STILL READ (7 Oct, item 22). Faded to 55% its label measured 1.1:1 on the
 * button it was on; at 75% it was still under AA. Opacity is the wrong tool for "you cannot press
 * this": it says so by dimming the one thing that explains what the button was. A disabled button
 * takes the page's own quiet surface instead -- plainly inert, and still legible.
 */
.btn:disabled { background:var(--surface-2); color:var(--muted); border-color:var(--line); opacity:1; cursor:default; }
/* Archiving somebody off a course, and archiving a course. Outlined rather than filled: it is the
   destructive-looking action on the row and should not out-shout Assign. */
.btn-danger { background:var(--card); color:var(--danger-text); border-color:var(--danger-text); }
.btn-danger:hover { background:var(--danger-bg); }
.error { background:var(--danger-bg); color:var(--danger-text); padding:10px 12px;
         border-radius:8px; font-size:13px; }
.success { background:var(--ok-bg); color:var(--ok-text); padding:10px 12px;
           border-radius:8px; font-size:13px; }
.field-hint { font-size:12px; color:var(--muted); margin:4px 0 8px; line-height:1.5; }
.row-form { display:flex; gap:10px; flex-wrap:wrap; align-items:flex-end; }
.row-form label { display:block; font-size:12px; color:var(--muted); margin-bottom:4px; }
label { display:block; font-size:12px; color:var(--muted); margin-bottom:4px; font-weight:600; }
input, select, textarea { width:100%; padding:9px 11px; font-size:13.5px; font-family:inherit;
                          border:1.5px solid var(--control-edge); border-radius:8px;
                          background:var(--card); color:var(--heading); }
textarea { resize:vertical; min-height:70px; line-height:1.5; }

.t-head { display:flex; align-items:flex-start; justify-content:space-between; gap:16px;
          flex-wrap:wrap; margin-bottom:28px; }
.t-title { font-family:'Sora',sans-serif; font-size:26px; font-weight:700; margin:0 0 6px; }
.t-sub { color:var(--muted); font-size:14px; margin:0; }
.t-actions { display:flex; gap:10px; flex-wrap:wrap; }

/* ── the four tiles ──────────────────────────────────────────────────────────────────────── */
.t-tiles { display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:14px;
           margin-bottom:22px; }
.t-tile { background:var(--card); border:1px solid var(--surface-2); border-radius:12px;
          padding:16px 18px; }
.t-tile-label { font-size:12px; color:var(--muted); font-weight:500; margin-bottom:8px; }
.t-tile-value { font-family:'Sora',sans-serif; font-size:26px; font-weight:700; color:var(--heading);
                line-height:1.1; }
.t-tile-note { font-size:12px; color:var(--muted); margin-top:5px; }
/* A tile that is a link looks like a tile and behaves like a link: no underline, no colour
   change, and the border answers the pointer so it is obvious it can be opened. */
/* ── a course pack's preview ─────────────────────────────────────────────────────────────── */
/* Twenty courses in a panel: one line each, what is wrong with it underneath, and a tick per
   course so somebody can take nineteen of them. */
.t-pack-list { display:grid; gap:10px; margin-top:14px; max-height:50vh; overflow:auto; }
.t-pack-course { display:grid; gap:6px; padding:12px 14px; background:var(--card);
                 border:1px solid var(--surface-2); border-radius:10px; }
.t-pack-pick { display:flex; gap:10px; align-items:flex-start; cursor:pointer; }
.t-pack-pick input { margin-top:3px; }
.t-pack-pick b { display:block; color:var(--heading); }
.t-pack-errors { margin:4px 0 0; padding-left:18px; font-size:13px; color:var(--danger-text); }
.t-pack-errors li { margin:2px 0; }
@media (max-width: 560px) { .t-pack-list { max-height:none; } }

.t-tile-link { display:block; text-decoration:none; color:inherit; }
.t-tile-link:hover { border-color:var(--teal); }
.t-tile-link:focus-visible { outline:3px solid var(--teal); outline-offset:2px; }

.t-cards { display:grid; grid-template-columns:repeat(auto-fit,minmax(320px,1fr)); gap:14px; }
.t-card { background:var(--card); border:1px solid var(--surface-2); border-radius:12px;
          padding:18px 20px; margin-bottom:14px; }
.t-card-head { display:flex; align-items:baseline; justify-content:space-between; gap:12px;
               margin-bottom:14px; }
.t-card-title { font-family:'Sora',sans-serif; font-size:16px; font-weight:700; margin:0; }
.t-link { color:var(--teal); text-decoration:none; font-weight:600; font-size:13px; }

/* ── rows, used by the overdue list, the lesson list and the question list ───────────────── */
.t-row { display:flex; align-items:center; gap:12px; padding:11px 0;
         border-bottom:1px solid var(--line); }
.t-row:last-child { border-bottom:0; }
.t-row-main { flex:1; min-width:0; }
.t-row-title { font-weight:600; font-size:14px; }
.t-row-meta { font-size:12.5px; color:var(--muted); margin-top:2px; }
.t-row-num { width:26px; height:26px; border-radius:50%; background:var(--sand);
             color:var(--heading); font-size:12px; font-weight:700; display:flex;
             align-items:center; justify-content:center; flex-shrink:0; }

/* ── progress ────────────────────────────────────────────────────────────────────────────── */
.t-bar { height:8px; border-radius:20px; background:var(--surface-2); overflow:hidden; }
.t-bar span { display:block; height:100%; background:var(--navy); border-radius:20px; }
/* Under half is the one number somebody has to act on, so it stops looking like the others. */
.t-bar.low span { background:var(--danger-button); }

/* ── status chips, the pairs from the palette ────────────────────────────────────────────── */
.t-chip { display:inline-block; font-size:11px; font-weight:700; padding:3px 9px;
          border-radius:20px; white-space:nowrap; }
.t-chip-overdue, .t-chip-failed { background:var(--danger-bg); color:var(--danger-text); }
.t-chip-passed  { background:var(--ok-bg); color:var(--ok-text); }
.t-chip-progress{ background:var(--status-progress-bg); color:var(--status-progress-text); }
.t-chip-todo    { background:var(--sand); color:var(--sand-text); }
.t-chip-draft   { background:var(--warn-bg); color:var(--warn-text); }

/* ── the builder ─────────────────────────────────────────────────────────────────────────── */
.t-crumb { font-size:12.5px; color:var(--muted); margin-bottom:8px; }
.t-crumb a { color:var(--muted); text-decoration:none; }
.t-add { display:flex; gap:8px; flex-wrap:wrap; margin-top:14px; }
/*
 * A TICKED ANSWER IS GREEN AND SAYS SO. The canvas asks for #EAF5EF on #1B5E44, which is the
 * palette's ok pair -- so the same row is legible in the dark palette without a second rule.
 */
.t-answer { display:flex; align-items:center; gap:10px; padding:10px 12px; border-radius:9px;
            border:1.5px solid var(--control-edge); margin-bottom:8px; background:var(--card); }
.t-answer.correct { background:var(--ok-bg); border-color:var(--ok-text); }
.t-answer-flag { margin-left:auto; font-size:11px; font-weight:700; color:var(--ok-text); }

/* ── assign ──────────────────────────────────────────────────────────────────────────────── */
.t-person { display:flex; align-items:center; gap:12px; padding:10px 12px; border-radius:9px;
            border:1px solid transparent; }
.t-person.picked { background:var(--teal-tint); border-color:var(--teal); }
.t-person.taken { opacity:0.9; }

/* ── the learner, at 390px ───────────────────────────────────────────────────────────────── */
/*
 * 520px is the phone, where it is the whole screen. On a desktop it left the course rendered in a
 * narrow strip down the middle of a wide page -- which is what "View as learner shows half the
 * screen" was: the same column, with eight hundred pixels of empty paper beside it.
 *
 * So the column fills the content area from 900px up, and the PROSE keeps a measure of its own:
 * a lesson set in 130-character lines is harder to read than one in a strip, and widening the
 * card is not an argument for widening the sentence.
 */
.t-learn { max-width:520px; margin:0 auto; }
@media (min-width: 900px) {
  .t-learn { max-width:none; }
  .t-learn .t-rich { max-width:72ch; }
}
.t-course-card { background:var(--card); border:1px solid var(--surface-2); border-radius:12px;
                 padding:16px 18px; margin-bottom:12px; }
.t-course-title { font-family:'Sora',sans-serif; font-size:17px; font-weight:700; margin:8px 0 10px; }
.t-steps { display:flex; gap:4px; margin:14px 0 18px; }
.t-steps i { flex:1; height:5px; border-radius:20px; background:var(--surface-2); }
.t-steps i.done { background:var(--teal); }
.t-steps i.here { background:var(--navy); }
.t-lesson-body { font-size:15px; line-height:1.65; white-space:pre-wrap; }
.t-lesson-media { width:100%; border-radius:10px; margin:12px 0; display:block; }
/* A CEILING FOR VIDEO, which a picture does not need: a portrait clip at width:100% is taller
   than the phone it is played on, so the controls end up below the fold and the lesson looks
   broken. The black fill is the letterboxing a video player is expected to have. */
video.t-lesson-media { max-height:70vh; background:#000; }
.t-lesson-frame { width:100%; aspect-ratio:16/9; border:0; border-radius:10px; margin:12px 0; }
/*
 * 64px MINIMUM, because these are tapped with a thumb. The selected state is a 2px navy border
 * rather than a colour swap alone: a border survives a high-contrast mode that flattens fills.
 */
.t-option { display:flex; align-items:center; gap:12px; width:100%; min-height:64px;
            padding:14px 16px; margin-bottom:10px; border-radius:11px; text-align:left;
            font-size:15px; font-family:inherit; cursor:pointer;
            background:var(--card); border:1.5px solid var(--control-edge); color:var(--heading); }
.t-option.picked { background:var(--status-progress-bg); border:2px solid var(--navy); }
.t-score { font-family:'Sora',sans-serif; font-size:46px; font-weight:800; line-height:1;
           margin:10px 0 6px; }
.t-foot { display:flex; gap:10px; margin-top:22px; }
/*
 * 44px, because these are the buttons a learner taps on a phone -- Continue, Next, Try again. The
 * shared .btn is sized for a mouse; at 390px it came out at 37px, which mobilecheck called too
 * small to tap and it was right. display:flex so an <a> and a <button> are the same height.
 */
.t-foot .btn { flex:1; min-height:44px; display:inline-flex; align-items:center;
               justify-content:center; }

/*
 * ── THE REPORT'S TABLE ───────────────────────────────────────────────────────────────────────
 *
 * The same .data-table every other list in the product uses, and the same .table-scroll around
 * it -- both of which live in each page's own <style> block elsewhere, so a page that links its
 * CSS instead (as the new pages do) had neither. mobilecheck caught it precisely: 213px wider
 * than the phone.
 *
 * A seven-column report is a desktop screen and it stays one; what the wrapper buys is that a
 * manager who opens it on a phone scrolls the TABLE rather than the whole page sideways.
 */
.data-table { width:100%; border-collapse:collapse; font-size:13px; }
.data-table th { text-align:left; font-size:10.5px; text-transform:uppercase; letter-spacing:0.05em;
                 color:var(--muted); padding:8px 10px; border-bottom:1px solid var(--line);
                 white-space:nowrap; }
.data-table td { padding:10px; border-bottom:1px solid var(--surface-2); vertical-align:middle; }
.table-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; }

@media (max-width: 560px) {
  .t-title { font-size:22px; }
  .t-tiles { grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); }
  .t-head { flex-direction:column; }
}

/* ── A LIST ROW IS A TABLE ROW ────────────────────────────────────────────────────────────────
 *
 * The People page was .t-row: a flex row with the name and email stacked in one growing cell and
 * an unshrinkable <select> beside them. At 1280 with a real name and a real work address the
 * select took its width out of the cell, the name wrapped to one word per line, and the email ran
 * under the dropdown. Each fact gets its own column and one baseline, and the columns are declared
 * per table in --t-cols so one component serves People and a course's Assigned-to list.
 *
 * Under 560px a grid of six columns is six columns of nothing, so every row becomes a block: the
 * title, then label/value pairs, then the controls.
 */
.t-grid { display:block; }
.t-grid-head, .t-grid-row {
  display:grid; grid-template-columns:var(--t-cols); gap:12px; align-items:center;
  padding:11px 0; border-bottom:1px solid var(--line);
}
.t-grid-head { font-size:11px; font-weight:700; letter-spacing:0.06em; text-transform:uppercase;
               color:var(--muted); padding:0 0 8px; }
.t-grid-row:last-child { border-bottom:0; }
.t-cell { min-width:0; }
/* One line, cut with an ellipsis: a long address must never push a column out of shape. */
.t-cell-1 { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.t-cell-name { font-weight:600; font-size:14px; }
.t-cell-sub { font-size:12.5px; color:var(--muted); }
.t-cell-label { display:none; font-size:11px; font-weight:700; letter-spacing:0.06em;
                text-transform:uppercase; color:var(--muted); }
.t-grid-row select, .t-grid-row .btn { width:100%; }
.t-rowacts { display:flex; gap:6px; flex-wrap:wrap; align-items:center; }
.t-rowacts .btn { width:auto; }

/* The "More" menu's own rules moved to row-actions-2026-10-09.css on 9 October, beside the
   one implementation that renders it (client/row-actions.js). They were here because the
   Training tables were the first thing to need them; the staff profile needs them too, and a
   second copy of a component's stylesheet is the drift this codebase keeps writing down. */

@media (max-width: 560px) {
  /* A row is a card here, so the menu is as wide as the card rather than hanging off its edge. */
  .t-more { display:flex; flex:1 1 auto; }
  .t-more-btn { width:100%; }
  .t-more-menu { left:0; right:0; min-width:0; }
}

/*
 * A DENSE TABLE KEEPS ITS COLUMNS AND SCROLLS. Seven facts -- person, status, due, best score,
 * attempts, completed, actions -- need about 960px, and the main column at 1280 has nothing like
 * that once the sidebar has taken its share. Squeezed into the space it did not overflow visibly:
 * it OVERLAPPED, and the Person and Status headings printed on top of each other.
 *
 * A media query cannot fix this, because the constraint is the width of the COLUMN the table sits
 * in and a media query only knows the window. So the row keeps a floor and .table-scroll (the same
 * wrapper the report table uses) scrolls the table rather than the page.
 */
/* Wide enough for the widest row each dense table actually has. */
.t-grid-dense { min-width:900px; }
@media (max-width: 560px) {
  /* On a phone it stacks instead, like every other grid here, and the floor comes off. */
  .t-grid-dense { min-width:0; }
}

@media (max-width: 560px) {
  .t-grid-head { display:none; }
  .t-grid-row { grid-template-columns:1fr; gap:6px; padding:14px 0; align-items:start; }
  .t-cell-label { display:block; }
  /* The address is the one thing worth wrapping on a phone rather than cutting. */
  .t-cell-1 { white-space:normal; overflow-wrap:anywhere; }
  .t-grid-row select { width:100%; }
  .t-rowacts { width:100%; }
  .t-rowacts .btn { flex:1 1 auto; }
}

/* ── THE EDITOR PANEL ─────────────────────────────────────────────────────────────────────────
 *
 * A lesson and a question each open in a panel that covers the page, because the thing being
 * edited is a page of prose or a question with six answers on it -- the previous builder asked for
 * both through window.prompt, one line at a time, with the answers typed as a comma-separated list
 * and an asterisk on the correct one.
 *
 * Wide, not a dialog squeezed into the middle: 880px is a readable measure for a lesson and leaves
 * an answer row room for its text, its tick and its controls. On a phone it is the whole screen.
 */
.t-editor { position:fixed; inset:0; z-index:60; background:rgba(11,22,66,0.45);
            display:flex; align-items:flex-start; justify-content:center;
            padding:32px 16px; overflow:auto; }
.t-editor[hidden] { display:none; }
.t-editor-panel { background:var(--card); border-radius:14px; width:100%; max-width:880px;
                  padding:22px 22px 18px; box-shadow:0 18px 50px rgba(11,22,66,0.28); }
.t-editor-head { display:flex; align-items:baseline; justify-content:space-between; gap:12px;
                 margin-bottom:16px; }
.t-editor-title { font-family:'Sora',sans-serif; font-size:19px; font-weight:700; margin:0; }
.t-editor-foot { display:flex; gap:10px; justify-content:flex-end; margin-top:20px;
                 padding-top:16px; border-top:1px solid var(--line); }
/* Resizable, and tall enough that a lesson does not have to be written through a letterbox. */
.t-editor textarea.t-prose { min-height:300px; resize:vertical; line-height:1.55; font-size:14.5px; }

/* ── ANSWER ROWS ──────────────────────────────────────────────────────────────────────────────
 * The tick is the first thing on the row because "which of these is right" is the question being
 * answered while writing them.
 */
.t-answers { display:flex; flex-direction:column; gap:8px; margin-top:8px; }
.t-answer-row { display:grid; grid-template-columns:34px minmax(0,1fr) auto; gap:10px;
                align-items:center; }
.t-answer-row input[type=text] { margin:0; }
.t-answer-tick { display:flex; align-items:center; justify-content:center; }
.t-answer-tick input { width:18px; height:18px; margin:0; accent-color:var(--teal); }
.t-answer-ctl { display:flex; gap:4px; }
.t-answer-ctl button { padding:5px 9px; font-size:12px; line-height:1; }
.t-hint { font-size:12.5px; color:var(--muted); margin:8px 0 0; }

/* The up/down pair on a lesson or question row. */
.t-ord { display:flex; gap:4px; }
.t-ord button { padding:4px 9px; font-size:13px; line-height:1.2; }

@media (max-width: 560px) {
  .t-editor { padding:0; }
  .t-editor-panel { border-radius:0; min-height:100vh; max-width:none; padding:18px 16px 24px; }
  .t-editor-foot { position:sticky; bottom:0; background:var(--card); }
  .t-answer-row { grid-template-columns:34px minmax(0,1fr); grid-template-areas:'tick text' '. ctl'; }
  .t-answer-tick { grid-area:tick; }
  .t-answer-row input[type=text] { grid-area:text; }
  .t-answer-ctl { grid-area:ctl; }
}

/* ── THE EDITOR'S STATUS LINE ─────────────────────────────────────────────────────────────────
 * How many characters there are, and whether they are safe. Both matter while a long document is
 * being pasted: the count says "it all went in" before the save, and the clock says "and it is
 * kept" after it.
 */
.t-editor-status { display:flex; justify-content:space-between; align-items:baseline; gap:12px;
                   margin-top:6px; font-size:12.5px; }
.t-count { color:var(--muted); font-variant-numeric:tabular-nums; }
.t-count.over { color:var(--danger-text); font-weight:700; }
.t-saved { color:var(--muted); }
.t-saved.ok { color:var(--ok-text); font-weight:600; }
.t-saved.bad { color:var(--danger-text); font-weight:600; }
@media (max-width: 560px) {
  .t-editor-status { flex-direction:column; gap:2px; }
}

/* ── THE BUILDER: OUTLINE LEFT, EDITOR RIGHT ──────────────────────────────────────────────────
 *
 * The course was a column of cards with the editor thrown over the top in a modal, so the thing
 * being written and the shape of the course it belongs to were never on screen together. The
 * outline is the course; the pane beside it is whatever is selected.
 *
 * Under 900px the column is not there to be split, so the outline sits above the editor and the
 * page reads top to bottom like every other phone screen in the product.
 */
.t-builder { display:grid; grid-template-columns:270px minmax(0,1fr); gap:18px; align-items:start; }
.t-outline { background:var(--card); border:1px solid var(--surface-2); border-radius:12px;
             padding:12px; position:sticky; top:12px; }
.t-outline-group { font-size:10.5px; font-weight:700; letter-spacing:0.08em; text-transform:uppercase;
                   color:var(--muted); margin:12px 6px 6px; }
.t-outline-group:first-child { margin-top:2px; }
.t-outline-item { display:flex; align-items:center; gap:8px; width:100%; text-align:left;
                  background:none; border:0; border-radius:8px; padding:8px 9px; cursor:pointer;
                  font-family:inherit; font-size:13px; color:var(--ink); }
.t-outline-item:hover { background:var(--surface-2); }
.t-outline-item.on { background:var(--navy); color:#fff; font-weight:600; }
.t-outline-num { flex-shrink:0; width:20px; height:20px; border-radius:50%; background:var(--sand);
                 color:var(--heading); font-size:11px; font-weight:700; display:flex;
                 align-items:center; justify-content:center; }
.t-outline-item.on .t-outline-num { background:rgba(255,255,255,0.22); color:#fff; }
.t-outline-text { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.t-outline-add { width:100%; margin-top:6px; }
.t-pane { background:var(--card); border:1px solid var(--surface-2); border-radius:12px; padding:18px; }

@media (max-width: 900px) {
  .t-builder { grid-template-columns:1fr; }
  .t-outline { position:static; }
}

/* ── THE LESSON TOOLBAR, AND WHAT STRUCTURED CONTENT LOOKS LIKE ───────────────────────────────
 * One set of rules, used by the editor, the preview and the learner, so what an admin types is
 * what the person doing the course reads. .t-rich is that shape.
 */
.t-toolbar { display:flex; flex-wrap:wrap; align-items:center; gap:4px; padding:6px;
             border:1px solid var(--surface-2); border-bottom:0;
             border-radius:9px 9px 0 0; background:var(--surface-2); }
.t-tool { font-family:inherit; font-size:12.5px; font-weight:600; padding:5px 9px; min-width:30px;
          border:1px solid transparent; border-radius:6px; background:var(--card);
          color:var(--heading); cursor:pointer; }
.t-tool:hover { border-color:var(--control-edge); }
.t-tool-sep { width:1px; height:18px; background:var(--line); margin:0 3px; }

div.t-prose[contenteditable] { border-radius:0 0 9px 9px; overflow-y:auto; }
.t-rich { font-size:15px; line-height:1.65; }
.t-rich h2 { font-family:'Sora',sans-serif; font-size:19px; margin:18px 0 8px; }
.t-rich h3 { font-family:'Sora',sans-serif; font-size:16px; margin:16px 0 6px; }
.t-rich p { margin:0 0 12px; }
.t-rich ul, .t-rich ol { margin:0 0 12px; padding-left:22px; }
.t-rich li { margin:0 0 5px; }
.t-rich blockquote { margin:0 0 12px; padding-left:12px; border-left:3px solid var(--line);
                     color:var(--muted); }
.t-rich a { color:var(--teal); }
.t-rich img { max-width:100%; height:auto; border-radius:8px; display:block; margin:12px 0; }
/* A table from a document is whatever width it wants to be; the lesson scrolls it rather than
   the page, which is the same answer the report table came to. */
.t-rich table { border-collapse:collapse; width:100%; margin:0 0 12px; font-size:13.5px; }
.t-rich th, .t-rich td { border:1px solid var(--line); padding:7px 9px; text-align:left;
                         vertical-align:top; }
.t-rich th { background:var(--surface-2); font-weight:700; }
/* A structured body is not pre-wrap: its paragraphs do the spacing. */
.t-lesson-body.t-rich { white-space:normal; }
@media (max-width: 560px) {
  .t-rich { font-size:15.5px; }
  .t-rich table { display:block; overflow-x:auto; }
}

/* ── LINKS INSIDE TRAINING'S OWN FURNITURE ───────────────────────────────────────────────────
 *
 * "1 assigned", "← Courses" and "Edit course" were default browser links: blue, underlined, and
 * nothing like anything else on the page. These are navigation, not buttons, so they are not
 * dressed as buttons -- they take the product's own link colour and lose the permanent underline,
 * which comes back on hover and on focus so they are still obviously links.
 *
 * SCOPED AWAY FROM .t-rich ON PURPOSE. A link inside a LESSON is somebody else's writing and
 * should look like a link in a document, underline and all -- see .t-rich a.
 */
.t-card a:not(.btn):not(.nav-item),
.t-crumb a,
.t-grid a:not(.btn),
.t-head a:not(.btn) {
  color: var(--teal);
  font-weight: 600;
  text-decoration: none;
}
.t-card a:not(.btn):not(.nav-item):hover,
.t-crumb a:hover,
.t-grid a:not(.btn):hover,
.t-head a:not(.btn):hover,
.t-card a:not(.btn):not(.nav-item):focus-visible,
.t-crumb a:focus-visible,
.t-grid a:not(.btn):focus-visible,
.t-head a:not(.btn):focus-visible { text-decoration: underline; }
/* The lesson's own prose keeps document links. */
.t-rich a { text-decoration: underline; }

/* A scrolling table has to be allowed to be NARROWER than its contents, or it pushes the page
   wide instead of scrolling inside its card. A grid or flex child defaults to min-width:auto,
   which means "at least as wide as my content" — the one declaration that makes .table-scroll
   actually scroll. */
.t-card { min-width: 0; }
.table-scroll { max-width: 100%; min-width: 0; }

/* THE GRID COLUMN THE PAGE SITS IN moved to app-shell-2026-09-11.css on 5 Oct, where every page
   gets it. The note that used to be here said a layout change reaching forty other pages was not
   one to make on the way past; the layout pass that followed was exactly that change, and the same
   wide-table-scrolls-the-page fault was waiting on all forty. */

/* ── A NEUTRAL BUTTON NEEDS A BACKGROUND OF ITS OWN ──────────────────────────────────────────
 *
 * The shared .btn base deliberately sets no background, so a <button class="btn"> with no
 * modifier falls back to the browser's own light grey. In dark mode the shell paints its label
 * near-white, and "Duplicate" rendered as an empty pale pill with invisible text. An <a
 * class="btn"> was fine, because an anchor has no default background — so this only showed on the
 * one control that happens to be a real button.
 *
 * Fixed with tokens, so it follows light and dark, and scoped to this stylesheet: only Training's
 * pages load it, and giving every button in the product a background is not a change to make on
 * the way past.
 */
.t-card .btn:not(.btn-primary):not(.btn-danger):not(.btn-ghost),
.t-pane .btn:not(.btn-primary):not(.btn-danger):not(.btn-ghost),
.t-editor .btn:not(.btn-primary):not(.btn-danger):not(.btn-ghost),
.t-rowacts .btn:not(.btn-primary):not(.btn-danger):not(.btn-ghost) {
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--heading);
}

/*
 * ── THE FILE A LESSON HOLDS, IN THE EDITOR ───────────────────────────────────────────────────
 *
 * An admin who had just uploaded a video was shown one line of hint text. The file itself is the
 * proof the upload worked, so it is drawn: the picture, the player, or the PDF in a frame, with
 * the name it was uploaded under, its size, and which of the three states it is in.
 */
.t-file-card { border:1px solid var(--line); border-radius:12px; padding:12px; background:var(--card); }
.t-file-shot { display:block; width:100%; max-height:320px; border-radius:8px;
               background:var(--surface-2); object-fit:contain; border:0; }
.t-file-pdf { height:320px; }
.t-file-meta { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:10px; }
.t-file-name { font-weight:600; font-size:13.5px; word-break:break-word; min-width:0; }
.t-file-size { font-size:12.5px; color:var(--muted); }
/* The state is the answer to "did that work", so it reads as a word and not only as a colour. */
.t-file-state { margin-left:auto; font-size:12px; font-weight:600; border-radius:999px;
                padding:3px 10px; white-space:nowrap; }
.t-file-uploaded { background:var(--ok-bg); color:var(--ok-text); }
.t-file-uploading { background:var(--sand); color:var(--sand-text); }
.t-file-failed, .t-file-missing { background:var(--danger-bg); color:var(--danger-text); }

/* A PDF in a lesson, on a screen wide enough to read one: tall enough for a page, and it
   scrolls itself. Below 560px this is hidden entirely in favour of .t-pdf-phone. */
.t-lesson-pdf { width:100%; height:70vh; min-height:420px; border:1px solid var(--line);
                border-radius:10px; display:block; background:var(--surface-2); }
/*
 * The panel that replaces the frame, for the two cases where there is something true to say: a
 * browser with no PDF viewer, and a file that did not arrive. Hidden until one of them happens --
 * and the [hidden] rule has to be written, because `display:grid` here would otherwise win.
 */
.t-pdf-instead { display:grid; place-items:center; gap:10px; padding:24px; text-align:center;
                 border:1px solid var(--line); border-radius:12px; }
.t-pdf-instead[hidden] { display:none; }
/* An <iframe> is replaced by the panel, not left empty beside it. */
.t-lesson-pdf[hidden] { display:none; }
/* The phone half, swapped in below 560px -- see the comment in training-course.js. */
.t-pdf-phone { display:none; border:1px solid var(--line); border-radius:12px; padding:20px;
               text-align:center; }
.t-pdf-what { margin:0 0 2px; font-weight:600; }
/* The note when the file itself did not arrive — a different thing from a browser that cannot
   show one, and it should not read like ordinary small print. */
.t-pdf-broken { color:var(--danger, #B3261E); font-weight:600; }
@media (max-width: 560px) {
  /* The phone card says all of this better, so neither the frame nor the panel is drawn here. */
  .t-lesson-pdf, .t-pdf-desk, .t-pdf-instead { display:none; }
  .t-pdf-phone { display:block; }
}

/* ── PART 3: WHAT SOMEBODY WATCHED, AND WHAT SOMEBODY ELSE ISSUED ────────────────────────────
 *
 * Written here rather than inline, because a linked stylesheet owns none of a page's furniture
 * until it is told to: a checklist drawn with no rules at all is a column of bare checkboxes with
 * the label a line below the box, which reads as two lists.
 */
.t-check { display:flex; align-items:flex-start; gap:10px; padding:10px 0; font-size:14px;
           cursor:pointer; }
.t-check input { margin:2px 0 0; width:18px; height:18px; flex-shrink:0; accent-color:var(--teal); }
.t-check span { line-height:1.45; }
/* The note under its own item, indented to the label so the pair reads as one thing. */
.t-check-note { margin:0 0 14px 28px; width:calc(100% - 28px); font-size:13px; }
.t-check-note:last-child { margin-bottom:0; }
/* Instructions an admin typed, set apart from the prose around them without shouting. */
.t-note { margin:12px 0 0; padding:11px 13px; border-radius:10px; background:var(--surface-2);
          border-left:3px solid var(--teal); }

/* ── SEATS AND PRICE BANDS ────────────────────────────────────────────────────────────────── */
.t-bands { display:grid; gap:6px; margin:10px 0 0; }
.t-band { display:flex; justify-content:space-between; gap:12px; font-size:13.5px;
          padding:7px 10px; border-radius:8px; background:var(--surface-2); }
.t-band-on { background:var(--teal); color:#fff; font-weight:600; }
.t-seats { display:flex; align-items:flex-end; gap:10px; flex-wrap:wrap; margin-top:12px; }
.t-seats label { margin:0 0 4px; }
.t-seats input { width:110px; }
.t-total { font-size:22px; font-weight:700; font-family:'Sora',system-ui,sans-serif;
           color:var(--heading); }
.t-total-sub { font-size:12.5px; color:var(--muted); margin-top:2px; }


/* ── THE COMPLIANCE MATRIX (Part 4 item 3) ───────────────────────────────────────────────────
 *
 * Staff down, courses across. The grid scrolls sideways INSIDE its own box rather than making the
 * page do it -- a page that scrolls sideways is the one thing the phone gates refuse -- and the
 * name column is sticky, because a cell is meaningless once you cannot see whose row it is on.
 */
.t-mx-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius);
             background: var(--card); }
.t-mx { display: table; width: 100%; border-collapse: collapse; }
.t-mx-row { display: table-row; }
.t-mx-row > * { display: table-cell; vertical-align: middle; border-bottom: 1px solid var(--line);
                padding: 8px 10px; }
.t-mx-head > * { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
                 color: var(--muted); background: var(--surface-2); }
.t-mx-name { position: sticky; left: 0; z-index: 1; background: var(--card); min-width: 190px; }
.t-mx-head .t-mx-name { background: var(--surface-2); }
.t-mx-name .t-row-meta { display: block; }
.t-mx-col, .t-mx-slot { min-width: 108px; max-width: 150px; }
.t-mx-col span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.t-mx-cell { display: block; width: 100%; box-sizing: border-box; border: 0; border-radius: 8px; padding: 6px 8px;
             font: inherit; font-size: 12px; font-weight: 600; text-align: center; }
button.t-mx-cell { cursor: pointer; }
/*
 * NO OPACITY ON THE LETTER. M, R or O sits beside the status word in the same cell, and 75% opacity
 * took it to 2.98:1 on --ok-bg in light and 4.38:1 in dark -- both under AA for text this size, and
 * the letter is the half that says whether the training is mandatory. The smaller size is enough to
 * make it secondary; the colour is the cell's own, which already passes.
 *
 * Caught by the --deep's render sweep, not by palettecheck: a static gate reads the tokens a rule
 * names and cannot see what an opacity does to the pair once it is painted.
 */
.t-mx-cell i { font-style: normal; font-size: 11px; }
.t-cell-current    { background: var(--ok-bg); color: var(--ok-text); }
.t-cell-soon       { background: var(--warn-bg); color: var(--warn-text); }
.t-cell-overdue    { background: var(--danger-bg); color: var(--danger-text); }
.t-cell-progress   { background: var(--status-progress-bg); color: var(--status-progress-text); }
.t-cell-awaiting   { background: var(--teal-tint); color: var(--teal-text); }
.t-cell-notstarted { background: var(--sand); color: var(--sand-text); }
.t-cell-na         { background: none; color: var(--muted); }
/*
 * A PLACEHOLDER LOOKS UNBUILT. Same colour pair as "not required" -- which is the one pair on this
 * screen already proven against AA in both palettes -- and a dashed edge to carry the difference,
 * because the eleventh background/text pair on one grid is a contrast finding waiting to happen.
 */
.t-cell-coming     { background: none; color: var(--muted); border: 1px dashed var(--line);
                     padding: 5px 7px; }
/* An alert is a column of a different kind, and the header says so under the title. */
.t-mx-col.is-alert em { display: block; font-style: normal; font-size: 9.5px; letter-spacing: 0.08em;
                        color: var(--teal-text); }
/*
 * ── THE SETUP GRID'S CELLS ───────────────────────────────────────────────────────────────────
 *
 * Four states, and the letter is the content. The two mandatory ones carry colour because they are
 * the obligations; optional is quiet; blank is a dot, so an empty cell is still a target big enough
 * to hit on a phone rather than a gap you have to aim at.
 */
.t-req-M { background: var(--danger-bg); color: var(--danger-text); }
.t-req-R { background: var(--warn-bg); color: var(--warn-text); }
.t-req-O { background: var(--sand); color: var(--sand-text); }
.t-req-none { background: none; color: var(--muted); border: 1px dashed var(--line); padding: 5px 7px; }
/* A course this organisation's service categories exclude: still a row, visibly not in play. */
.t-mx-row.is-out .t-mx-name b, .t-mx-row.is-out .t-mx-cell { opacity: 0.55; }
.t-mx-col em { display: block; font-style: normal; font-size: 9.5px; letter-spacing: 0.04em;
               color: var(--muted); font-weight: 600; }
.t-mx-name .btn { margin-top: 6px; }
.t-mx-key { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; }
.t-mx-key .t-mx-cell { display: inline-block; width: auto; }
.t-mx-score { text-align: right; }
.t-mx-score b { display: block; font-family: 'Sora', sans-serif; font-size: 30px; line-height: 1; }
.t-mx-score span { font-size: 12px; color: var(--muted); }
.t-mx-score.is-met b { color: var(--ok-text); }
.t-mx-score.is-under b { color: var(--danger-text); }

@media (max-width: 900px) {
  /*
   * ON A PHONE THE GRID IS STILL A GRID, and that is deliberate: this screen is read across and
   * down, and a card per person per course would be four hundred cards. It keeps its own sideways
   * scroll inside the box, with the name column pinned, so the page itself never moves.
   */
  .t-mx-name { min-width: 130px; }
  .t-mx-col, .t-mx-slot { min-width: 92px; }
  .t-mx-score { text-align: left; }
}
