/* Training engine.
 *
 * Uses the host's --shy-* tokens DIRECTLY -- no :root alias block. dye and
 * inventory have one only because they arrived from standalone apps with
 * established variable names of their own; a from-scratch engine has none
 * and a second set of names would just be something else to keep in sync.
 *
 * Every stylesheet on this box loads on every page (stylesheet_link_tag
 * :all), so everything here is prefixed `trn-` or scoped under
 * `.engine-training`. No bare element selectors outside that scope.
 */

.trn-filter {
  color: var(--shy-text-muted);
  font-size: 0.875rem;
  margin: 0 0 1rem;
}

.trn-muted { color: var(--shy-text-muted); }

/* ---- task table ---------------------------------------------------- */

.trn-table {
  width: 100%;
  border-collapse: collapse;
}

.trn-table th,
.trn-table td {
  text-align: left;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--shy-border);
  vertical-align: middle;
}

.trn-table th {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--shy-text-muted);
  font-weight: 600;
}

.trn-table tr:last-child td { border-bottom: none; }

.trn-row--inactive td { opacity: 0.55; }

.trn-actions { white-space: nowrap; text-align: right; }
.trn-actions > * { margin-left: 0.5rem; }

/* ---- SOP presence pills --------------------------------------------
 * The nag. A task with no SOP is the thing to fix next, so an empty one
 * reads as an outline rather than quietly looking the same as a filled one.
 */

/* The little "has an SOP written" pill on a skill's step list. Named
   .trn-sopflag, NOT .trn-sop: that class is the SOP reading article itself
   (see further down), and the two collided -- every SOP body was picking up
   `display: inline-block`, semibold and letter-spacing from here. The
   inline-block was the worse half: an inline-block is an atomic box, so a
   printed SOP couldn't split across pages and jumped whole to page 2,
   leaving the first sheet blank under the heading. */
.trn-sopflag {
  display: inline-block;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 0.1rem 0.4rem;
  border-radius: var(--shy-radius-sm);
  margin-right: 0.25rem;
}

.trn-sopflag--yes {
  background: var(--shy-success-tint);
  color: var(--shy-success);
}

.trn-sopflag--no {
  background: transparent;
  color: var(--shy-text-faint);
  border: 1px dashed var(--shy-border-strong);
}

.trn-tag {
  display: inline-block;
  font-size: 0.6875rem;
  padding: 0.1rem 0.4rem;
  border-radius: var(--shy-radius-sm);
  background: var(--shy-surface-alt);
  color: var(--shy-text-muted);
  margin-left: 0.35rem;
}

.trn-tag--linked {
  background: var(--shy-brand-tint);
  color: var(--shy-brand);
}

/* ---- empty state ---------------------------------------------------- */

.trn-empty {
  background: var(--shy-surface);
  border: 1px dashed var(--shy-border-strong);
  border-radius: var(--shy-radius-lg);
  padding: 2rem;
  text-align: center;
}

.trn-empty__title { font-weight: 600; margin: 0 0 0.5rem; }
.trn-empty__hint  { color: var(--shy-text-muted); margin: 0 auto 1rem; max-width: 34rem; }

.trn-import { margin-top: 1rem; }

/* ---- notices -------------------------------------------------------- */

.trn-notice {
  padding: 0.6rem 0.9rem;
  border-radius: var(--shy-radius);
  background: var(--shy-warning-tint);
  color: var(--shy-warning);
  font-size: 0.875rem;
  margin: 0 0 1rem;
}

.trn-notice--trainer {
  background: var(--shy-brand-tint);
  color: var(--shy-brand);
}

/* ---- forms ---------------------------------------------------------- */

.trn-form { max-width: 60rem; }

.trn-field { margin-bottom: 1.25rem; }
.trn-field--grow { flex: 1; }

.trn-field-row {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  align-items: flex-start;
}

.trn-field-row .trn-field { flex: 0 0 12rem; }
.trn-field-row .trn-field--grow { flex: 1 1 16rem; }

.trn-field label {
  display: block;
  font-weight: 600;
  font-size: 0.875rem;
  margin-bottom: 0.25rem;
  color: var(--shy-text);
}

.trn-field--check { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.trn-field--check label { margin: 0; }
.trn-field--check .trn-hint { flex-basis: 100%; }

.trn-field input[type="text"],
.trn-field input[type="number"],
.trn-field select,
.trn-field textarea {
  width: 100%;
  padding: 0.45rem 0.6rem;
  font: inherit;
  color: var(--shy-text);
  background: var(--shy-surface);
  border: 1px solid var(--shy-border-strong);
  border-radius: var(--shy-radius);
}

.trn-field input:focus-visible,
.trn-field select:focus-visible,
.trn-field textarea:focus-visible {
  outline: 2px solid var(--shy-brand-ring);
  outline-offset: 1px;
  border-color: var(--shy-brand);
}

.trn-hint {
  display: block;
  font-size: 0.8125rem;
  color: var(--shy-text-muted);
  margin-top: 0.25rem;
}

.trn-errors {
  background: var(--shy-danger-tint);
  color: var(--shy-danger);
  border-radius: var(--shy-radius);
  padding: 0.75rem 1rem;
  margin-bottom: 1.25rem;
}

.trn-errors p  { margin: 0 0 0.4rem; font-weight: 600; }
.trn-errors ul { margin: 0; padding-left: 1.2rem; }

.trn-form-actions {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  margin-top: 1.5rem;
}

.trn-btn {
  display: inline-block;
  padding: 0.45rem 1rem;
  font: inherit;
  font-weight: 600;
  color: var(--shy-brand-contrast);
  background: var(--shy-brand);
  border: 1px solid transparent;
  border-radius: var(--shy-radius);
  cursor: pointer;
  text-decoration: none;
}

.trn-btn:hover { background: var(--shy-brand-hover); }

.trn-btn-ghost {
  display: inline-block;
  padding: 0.45rem 1rem;
  font: inherit;
  color: var(--shy-text-muted);
  background: transparent;
  border: 1px solid var(--shy-border-strong);
  border-radius: var(--shy-radius);
  cursor: pointer;
  text-decoration: none;
}

.trn-btn-ghost:hover { color: var(--shy-text); border-color: var(--shy-text-muted); }

/* button_to wraps in a <form>; keep it inline with its neighbours. */
.trn-linkbtn {
  font: inherit;
  font-size: 0.875rem;
  color: var(--shy-brand);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
}

.engine-training form.button_to { display: inline; }

/* ---- SOP reading view -----------------------------------------------
 * This is the page someone reads with wet hands halfway through washing a
 * batch of yarn, so it's tuned for reading, not for density: a narrow
 * measure, generous line height, and headings that actually separate steps.
 *
 * The generic ActionText chrome (editor, toolbar, attachments, light/dark)
 * is themed HOST-side in application.css -- it's a framework concern and
 * dye's technique editor needs it too. What's here is only the reading
 * typography, which is Training's own.
 */

.trn-sop-page { max-width: 46rem; }

.engine-training .trn-sop .trix-content {
  font-size: 1.0625rem;
  line-height: 1.7;
  color: var(--shy-text);
}

.engine-training .trn-sop .trix-content > *:first-child { margin-top: 0; }

.engine-training .trn-sop .trix-content h1 {
  font-size: 1.375rem;
  line-height: 1.3;
  margin: 2rem 0 0.75rem;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--shy-border);
}

.engine-training .trn-sop .trix-content ul,
.engine-training .trn-sop .trix-content ol { padding-left: 1.5rem; margin: 0.75rem 0; }
.engine-training .trn-sop .trix-content li { margin-bottom: 0.4rem; }

/* Trix nests <ul> inside <li> for indented lists; keep those tighter. */
.engine-training .trn-sop .trix-content li ul,
.engine-training .trn-sop .trix-content li ol { margin: 0.4rem 0; }

.engine-training .trn-sop .trix-content blockquote {
  border-left: 3px solid var(--shy-brand);
  margin: 1.25rem 0;
  padding: 0.4rem 0 0.4rem 1rem;
  color: var(--shy-text-muted);
  background: var(--shy-brand-tint);
  border-radius: 0 var(--shy-radius) var(--shy-radius) 0;
}

.engine-training .trn-sop .trix-content pre {
  background: var(--shy-surface-sunken);
  border: 1px solid var(--shy-border);
  border-radius: var(--shy-radius);
  padding: 0.75rem 1rem;
  font-family: var(--shy-font-mono);
  font-size: 0.9375rem;
  overflow-x: auto;
  white-space: pre-wrap;
}

.engine-training .trn-sop .trix-content a {
  color: var(--shy-brand);
  text-decoration: underline;
}

/* An SOP photo is the point of the photo, so let it be big -- but keep it
   inside the measure and off the page edge. */
.engine-training .trn-sop .trix-content img {
  max-width: 100%;
  height: auto;
  border-radius: var(--shy-radius);
  border: 1px solid var(--shy-border);
  background: var(--shy-surface-alt);
}

.engine-training .trn-sop .trix-content figure { margin: 1.5rem 0; }

.engine-training .trn-sop .trix-content figcaption,
.engine-training .trn-sop .trix-content .attachment__caption {
  font-size: 0.875rem;
  color: var(--shy-text-muted);
  margin-top: 0.4rem;
}

.trn-sop__revised {
  margin-top: 2.5rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--shy-border);
  font-size: 0.8125rem;
  color: var(--shy-text-faint);
}

.trn-sop__empty {
  background: var(--shy-surface);
  border: 1px dashed var(--shy-border-strong);
  border-radius: var(--shy-radius-lg);
  padding: 2rem;
  text-align: center;
}

.trn-sop__empty p { margin: 0 0 0.75rem; }
.trn-sop__empty p:last-child { margin-bottom: 0; }

/* The editor itself needs a readable height -- an SOP is long. */
.engine-training trix-editor.trn-richtext {
  min-height: 20rem;
}

/* ---- video embeds ---------------------------------------------------
 * Written into the SOP body by the Video toolbar button
 * (sop_editor_controller.js). The markup is stored in the rich text, so
 * these rules have to work both in the editor and on the reading page --
 * hence no .trn-sop ancestor, only the .engine-training scope.
 */

.engine-training .trn-video {
  margin: 1.5rem 0;
}

/* aspect-ratio rather than the old padding-top hack; every browser this app
   supports has it, and it keeps the iframe absolute-positioning out of it. */
.engine-training .trn-video__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
  background: var(--shy-surface-sunken);
  border: 1px solid var(--shy-border);
  border-radius: var(--shy-radius);
  overflow: hidden;
}

.engine-training .trn-video__frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.engine-training .trn-video__caption {
  font-size: 0.8125rem;
  margin-top: 0.4rem;
  color: var(--shy-text-muted);
}

.engine-training .trn-video__caption a { color: var(--shy-brand); }

/* The toolbar button. Trix's own buttons are icon-only squares sized by
   .trix-button--icon; this one is text, so it needs its own width. */
.engine-training .trix-button.trn-video-button {
  width: auto;
  padding: 0 0.6rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-indent: 0;
  color: var(--shy-text-muted);
}

.engine-training .trix-button.trn-video-button:hover {
  color: var(--shy-text);
}

/* ---- the matrix ------------------------------------------------------
 * Tasks down, people across. It gets wide with more people, so the table
 * scrolls horizontally inside its own container (never the page body) and
 * the task column stays stuck to the left edge — reading a row is useless
 * if you can't see which task it is.
 */

.trn-matrix-scroll {
  overflow-x: auto;
  border: 1px solid var(--shy-border);
  border-radius: var(--shy-radius-lg);
  background: var(--shy-surface);
}

.trn-matrix {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
}

.trn-matrix th,
.trn-matrix td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--shy-border);
  text-align: left;
  white-space: nowrap;
}

.trn-matrix thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--shy-surface-alt);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--shy-text-muted);
}

/* The task column pins left. The corner cell has to out-rank both the
   sticky header row and the sticky column, hence the higher z-index. */
.trn-matrix__corner,
.trn-matrix__task,
.trn-matrix__group th {
  position: sticky;
  left: 0;
  background: var(--shy-surface);
  z-index: 1;
}

.trn-matrix__corner { z-index: 3; background: var(--shy-surface-alt); }

.trn-matrix__task {
  font-weight: 500;
  min-width: 12rem;
}

.trn-matrix__person { min-width: 7rem; }

.trn-matrix__person-pct {
  display: block;
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--shy-text-faint);
}

.trn-matrix__group th {
  background: var(--shy-surface-alt);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--shy-text-muted);
  padding-top: 0.6rem;
  padding-bottom: 0.6rem;
}

.trn-matrix__foot {
  margin-top: 0.75rem;
  font-size: 0.8125rem;
  color: var(--shy-text-muted);
}

/* ---- pips ------------------------------------------------------------
 * Three per cell by default (task.required_rounds). Filled = that round
 * happened. All filled = certified, which gets the green treatment so a
 * finished row is readable at a glance across a wide grid.
 */

.trn-cell {
  display: inline-flex;
  gap: 0.25rem;
  align-items: center;
  margin: 0;
}

.trn-pip {
  width: 1.6rem;
  height: 1.6rem;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--shy-border-strong);
  background: var(--shy-surface);
  color: var(--shy-text-faint);
  font: inherit;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Only the button form is interactive; the static version must not pretend. */
button.trn-pip { cursor: pointer; }

button.trn-pip:hover {
  border-color: var(--shy-brand);
  color: var(--shy-brand);
}

button.trn-pip:focus-visible {
  outline: 2px solid var(--shy-brand-ring);
  outline-offset: 1px;
}

.trn-pip--on {
  background: var(--shy-brand);
  border-color: var(--shy-brand);
  color: var(--shy-brand-contrast);
}

button.trn-pip--on:hover {
  background: var(--shy-brand-hover);
  color: var(--shy-brand-contrast);
}

.trn-cell--certified .trn-pip--on {
  background: var(--shy-success);
  border-color: var(--shy-success);
  color: #fff;
}

.trn-cell--certified button.trn-pip--on:hover {
  filter: brightness(1.08);
  color: #fff;
}

/* ---- status + roster -------------------------------------------------- */

.trn-status {
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.1rem 0.45rem;
  border-radius: var(--shy-radius-sm);
}

.trn-status--certified   { background: var(--shy-success-tint); color: var(--shy-success); }
.trn-status--in-progress { background: var(--shy-warning-tint); color: var(--shy-warning); }
.trn-status--not-started { background: var(--shy-surface-alt);  color: var(--shy-text-faint); }

.trn-tag--risk { background: var(--shy-warning-tint); color: var(--shy-warning); }

.trn-roster { list-style: none; margin: 0; padding: 0; }

.trn-roster__row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--shy-border);
}

.trn-roster__row:last-child { border-bottom: none; }
.trn-roster__row > a:first-child { min-width: 8rem; }

/* ---- person page ------------------------------------------------------ */

.trn-person-page { max-width: 60rem; }

.trn-progress-bar {
  height: 0.5rem;
  border-radius: 999px;
  background: var(--shy-surface-sunken);
  overflow: hidden;
  margin-bottom: 1.25rem;
}

.trn-progress-bar__fill {
  height: 100%;
  background: var(--shy-success);
  border-radius: 999px;
}

.trn-nextup {
  background: var(--shy-brand-tint);
  border-radius: var(--shy-radius);
  padding: 0.7rem 1rem;
  margin-bottom: 1.25rem;
  font-size: 0.9375rem;
}

.trn-nextup--done { background: var(--shy-success-tint); color: var(--shy-success); }

.trn-nextup__label {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--shy-text-muted);
  margin-right: 0.5rem;
}

.trn-matrix-page { max-width: none; }

/* ---- binder (on screen) ---------------------------------------------- */

.trn-binder { max-width: 46rem; }

.trn-binder__cover { display: none; }   /* print only -- see @media print */

.trn-binder__contents {
  border: 1px solid var(--shy-border);
  border-radius: var(--shy-radius-lg);
  background: var(--shy-surface);
  padding: 1.25rem 1.5rem;
  margin-bottom: 2rem;
}

.trn-binder__contents h2 {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--shy-text-muted);
  margin: 0 0 0.75rem;
}

.trn-binder__contents ol { margin: 0; padding-left: 1.4rem; }
.trn-binder__contents li { margin-bottom: 0.3rem; }

.trn-binder__task { margin-bottom: 3rem; }

.trn-binder__task-head {
  border-bottom: 2px solid var(--shy-border-strong);
  padding-bottom: 0.5rem;
  margin-bottom: 1.25rem;
}

.trn-binder__task-head h2 { margin: 0 0 0.25rem; font-size: 1.375rem; }

.trn-binder__meta {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--shy-text-muted);
}

.trn-binder__warning {
  color: var(--shy-warning);
  font-weight: 600;
}

.trn-binder__gaps {
  margin-top: 3rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--shy-border);
}

.trn-binder__gaps h2 { font-size: 1rem; margin: 0 0 0.25rem; }

/* ======================================================================
 * PRINT
 *
 * The host layout already strips the app shell (sidebar, top bar) in
 * @media print. What's left is Training's own job: drop the interactive
 * furniture, keep SOP steps from splitting across pages, and — the one
 * that actually matters — make video embeds survive onto paper, since an
 * iframe prints as a blank rectangle.
 * ====================================================================== */

@media print {
  /* Anything that only exists to be clicked. */
  .trn-noprint,
  .trn-btn,
  .trn-btn-ghost,
  .trn-linkbtn,
  .shy-page-header__actions,
  .trn-filter,
  .trn-import,
  .trn-form-actions,
  .trn-sop__empty,
  .engine-training form.button_to {
    display: none !important;
  }

  /* The roster and "who's trained on this" belong on screen, not stapled to
     an SOP someone is following at a sink. */
  .trn-sop-page .shy-card { display: none; }

  .trn-binder { max-width: none; }

  .trn-binder__cover {
    display: block;
    text-align: center;
    padding: 6rem 0 2rem;
    break-after: page;
  }

  .trn-binder__cover h1 { font-size: 2rem; margin: 0 0 0.5rem; }

  .trn-binder__contents { break-after: page; border: none; padding: 0; }

  /* Every SOP starts its own sheet, which is the whole point of a binder --
     you pull one page out and take it to the machine. */
  .trn-binder__task { break-before: page; margin-bottom: 0; }
  .trn-binder__task:first-of-type { break-before: auto; }

  .trn-binder__gaps { break-before: page; }

  /* --- the SOP sheet itself -------------------------------------------
   * tasks/show and tasks/trainer_sop zero the @page margin (see their
   * _print_page partial) to suppress the browser's own header/footer, so
   * the sheet's margin has to come back as padding here. NOTE this lands
   * once at the start and once at the end of the block, not on every sheet,
   * so a multi-page SOP runs closer to the top edge from page 2 on --
   * which is why the TOP is deliberately tighter than the sides: it buys
   * back space on page 1 for the steps, and page 2 on already reads at
   * roughly this offset. 0.6cm stays clear of a typical laser printer's
   * ~0.4cm unprintable edge. */
  .trn-sop-page { padding: 0.6cm 1.2cm 1.2cm; }

  /* One SOP, one title, centred -- there is no navigation on paper for it
     to sit opposite, and the subtitle repeats what the binder/context
     already says. */
  .trn-sop-page .shy-page-header { display: block; text-align: center; }
  .trn-sop-page .shy-page-header__subtitle { display: none; }

  /* The retired-task and trainer-copy banners stay -- a printed trainer
     SOP is otherwise indistinguishable from the staff one, and a sheet off
     a retired task should say so. But their tint prints as nothing and
     their colour isn't in the ink list below, so on paper the padding is
     just a pale gap: flatten them to one compact line. */
  .trn-sop-page .trn-notice {
    padding: 0;
    background: none;
    color: #000;
    font-size: 9pt;
    font-style: italic;
    margin: 0 0 0.4rem;
  }

  /* Reading greys are set for a backlit screen. On paper they print washed
     out -- and printed from a dark-mode browser these tokens resolve to
     near-WHITE, since nothing in the app's print rules resets colour. Pin
     the whole sheet to ink. */
  .trn-sop-page,
  .trn-sop-page .shy-page-header h1,
  .engine-training .trn-sop,
  .engine-training .trn-sop .trix-content,
  .engine-training .trn-sop .trix-content * {
    color: #000;
  }

  /* These four set their own colour with more specificity than the `*`
     above, so a bare universal rule would leave them grey. */
  .engine-training .trn-sop .trix-content a,
  .engine-training .trn-sop .trix-content blockquote,
  .engine-training .trn-sop .trix-content figcaption,
  .engine-training .trn-sop .trix-content .attachment__caption,
  .engine-training .trn-sop .trn-sop__revised {
    color: #000;
  }

  /* Screen sizes are set for reading one step at a time on a phone at a
     sink; on paper that wastes most of a sheet. Drop to a print-native
     scale so a short SOP fits one page. */
  .engine-training .trn-sop .trix-content {
    font-size: 10.5pt;
    line-height: 1.45;
  }

  .engine-training .trn-sop .trix-content h1 {
    font-size: 13pt;
    margin: 1rem 0 0.4rem;
  }

  .engine-training .trn-sop .trix-content ul,
  .engine-training .trn-sop .trix-content ol { margin: 0.5rem 0; }
  .engine-training .trn-sop .trix-content li { margin-bottom: 0.2rem; }
  .engine-training .trn-sop .trix-content figure { margin: 0.75rem 0; }

  /* The page heading is a screen-scale h1 too. */
  .engine-training .shy-page-header { margin-bottom: 0.75rem; }
  .engine-training .shy-page-header h1 { font-size: 16pt; }
  .engine-training .shy-page-header__subtitle { font-size: 9.5pt; margin-top: 0.1rem; }

  /* Keep a step and its photo together rather than orphaning either. */
  .engine-training .trn-sop .trix-content li,
  .engine-training .trn-sop .trix-content figure,
  .engine-training .trn-sop .trix-content blockquote,
  .engine-training .trn-sop .trix-content pre,
  .engine-training .trn-video {
    break-inside: avoid;
  }

  .engine-training .trn-sop .trix-content h1 { break-after: avoid; }

  /* Photos at screen size waste a whole sheet each. */
  .engine-training .trn-sop .trix-content img {
    max-height: 9cm;
    width: auto;
    border: 1px solid #999;
  }

  /* --- video embeds ---------------------------------------------------
   * An iframe prints as an empty box, so the player is dropped entirely and
   * the caption link becomes the printed artefact: its own href is written
   * out beside it, since a link is useless on paper without its address. */
  .engine-training .trn-video__frame { display: none !important; }

  .engine-training .trn-video__caption {
    font-size: 10pt;
    border: 1px solid #999;
    border-radius: 3px;
    padding: 0.4em 0.6em;
    color: #000;
  }

  .engine-training .trn-video__caption::before {
    content: "▶ Video — ";
    font-weight: bold;
  }

  .engine-training .trn-video__caption a { text-decoration: none; }

  .engine-training .trn-video__caption a::after {
    content: ": " attr(href);
    font-family: var(--shy-font-mono);
    word-break: break-all;
  }

  /* Ordinary links in an SOP get the same treatment, for the same reason.
     Anchors within the binder itself are excluded -- printing "#sop-x" next
     to every contents entry would be noise. */
  .engine-training .trn-sop .trix-content a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    word-break: break-all;
  }

  /* The matrix is genuinely useful on the wall, so make it printable rather
     than letting it spill off the sheet. */
  .trn-matrix-scroll { overflow: visible; border: none; }
  .trn-matrix { font-size: 9pt; }
  .trn-matrix thead th { position: static; }
  .trn-matrix__corner,
  .trn-matrix__task,
  .trn-matrix__group th { position: static; }
  .trn-matrix th,
  .trn-matrix td { padding: 0.2rem 0.35rem; }
  /* Filled pips are a background colour, which browsers drop by default. */
  .trn-pip { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  .trn-matrix tr { break-inside: avoid; }
}

/* ---- roadmap ---------------------------------------------------------- */

.trn-roadmap-page { max-width: 56rem; }

.trn-figures {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 2.5rem;
  margin-bottom: 1rem;
}

.trn-figure { display: flex; flex-direction: column; }

.trn-figure__n {
  font-size: 2rem;
  font-weight: 600;
  line-height: 1.1;
  color: var(--shy-text);
}

.trn-figure--lead .trn-figure__n { color: var(--shy-brand); }

.trn-figure__label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--shy-text-muted);
  margin-top: 0.2rem;
}

.trn-note {
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--shy-text-muted);
  margin: 0;
}

.trn-note strong { color: var(--shy-text); }

/* ---- skill cells ------------------------------------------------------
 * A multi-step skill can't be three pips (twenty steps is sixty rounds), so
 * it shows steps-certified plus a progress bar and links through to its own
 * grid. Single-step skills render their pips inline instead, which is most
 * of the catalogue and keeps logging simple work to one click.
 */

.trn-skillcell {
  display: inline-flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 3.4rem;
  text-decoration: none;
  color: var(--shy-text-muted);
}

.trn-skillcell:hover { color: var(--shy-brand); }

.trn-skillcell__count { font-size: 0.8125rem; font-weight: 600; }
.trn-skillcell__of    { color: var(--shy-text-faint); font-weight: 400; }

.trn-skillcell__bar {
  display: block;
  height: 0.3rem;
  border-radius: 999px;
  background: var(--shy-surface-sunken);
  overflow: hidden;
}

.trn-skillcell__fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--shy-brand);
}

.trn-skillcell--certified .trn-skillcell__count { color: var(--shy-success); }
.trn-skillcell--certified .trn-skillcell__fill  { background: var(--shy-success); }
.trn-skillcell--not-started .trn-skillcell__count { color: var(--shy-text-faint); }

/* ---- skill page ------------------------------------------------------- */

.trn-skill-page { max-width: none; }
.trn-skill-page .trn-sop { max-width: 46rem; }

.trn-binder__skill { margin-bottom: 3rem; }

.trn-binder__skill-head {
  border-bottom: 3px solid var(--shy-brand);
  padding-bottom: 0.5rem;
  margin-bottom: 1.25rem;
}

.trn-binder__skill-head h2 { margin: 0 0 0.25rem; font-size: 1.625rem; }

.trn-binder__overview {
  margin-bottom: 2rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--shy-border);
}

.trn-binder__substeps {
  list-style: none;
  padding-left: 1rem;
  margin: 0.25rem 0 0.5rem;
  font-size: 0.875rem;
  color: var(--shy-text-muted);
}

.trn-binder__task-head h3 { margin: 0 0 0.25rem; font-size: 1.125rem; }

/* Printed-only step checklist on a skill page. */
.trn-printonly { display: none; }

.trn-steplist { padding-left: 1.4rem; }
.trn-steplist li { margin-bottom: 0.4rem; }

@media print {
  .trn-printonly { display: block; }

  /* Each SKILL starts a sheet; its steps flow after it rather than each
     burning a page of their own. */
  .trn-binder__skill { break-before: page; }
  .trn-binder__skill:first-of-type { break-before: auto; }
  .trn-binder__task { break-before: auto; margin-bottom: 2rem; }
  .trn-binder__task-head { break-after: avoid; }
  .trn-skillcell__bar { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
}

/* Per-step controls on a skill page. Muted and small: the grid is the
   point of that page, these are maintenance. */
.trn-steprow__actions {
  margin-left: 0.5rem;
  font-size: 0.75rem;
  font-weight: 400;
  white-space: nowrap;
}

.trn-steprow__actions > * { margin-left: 0.4rem; }
.trn-steprow__actions a { color: var(--shy-text-muted); }
.trn-steprow__actions a:hover { color: var(--shy-brand); }

.trn-retired {
  margin-top: 2rem;
  padding-top: 1rem;
  border-top: 1px solid var(--shy-border);
  max-width: 32rem;
}

.trn-retired h2 { font-size: 1rem; margin: 0 0 0.25rem; }
.trn-retired .trn-muted { font-size: 0.875rem; margin: 0 0 0.75rem; }
