/* ============================================================ bridge.css
   Re-points the app's original token names at the tfadmin-ui token set.

   Why a bridge instead of a rewrite: styles.css is 135 KB of rules written
   against --accent, --surface-1, --text-primary and friends. Renaming every
   one of those by hand would be a very large, very risky diff. Aliasing the
   old names to the new ones adopts the whole design language in one file,
   and lets styles.css be cleaned out screen by screen afterwards without the
   UI ever being half-migrated.

   Load LAST — after styles.css — so these definitions win. The dark block
   repeats the same aliases rather than different values: darkness comes from
   tokens.css flipping --tf-*, so the mapping never has to be maintained twice.

   This file is temporary scaffolding. As each screen moves onto the .btn /
   .badge / .card vocabulary in components.css, the styles.css rules it
   replaces get deleted; when styles.css no longer references an old token,
   its alias here can go too.
   ==================================================================== */

:root,
html[data-theme="dark"] {
  /* surfaces */
  --surface-0: var(--tf-bg);
  --surface-1: var(--tf-surface);
  --surface-2: var(--tf-bg);

  /* text — three levels, not five */
  --text-primary: var(--tf-fg);
  --text-secondary: var(--tf-fg-muted);
  --text-muted: var(--tf-fg-subtle);

  /* lines */
  --border: var(--tf-border);
  --border-strong: var(--tf-border-strong);

  /* one accent in the product */
  --accent: var(--tf-accent);
  --accent-bg: var(--tf-accent-weak);
  --accent-contrast: var(--tf-accent-fg);

  /* semantic — status only */
  --danger: var(--tf-danger);   --danger-bg: var(--tf-danger-bg);
  --success: var(--tf-success); --success-bg: var(--tf-success-bg);
  --success-fg: var(--tf-success-fg);
  --warning: var(--tf-warning); --warning-bg: var(--tf-warning-bg);
  --info: var(--tf-info);       --info-bg: var(--tf-info-bg);

  /* shape and elevation */
  --radius: var(--tf-radius-md);
  --radius-lg: var(--tf-radius-lg);
  --shadow-sm: var(--tf-shadow-sm);
  --shadow-md: var(--tf-shadow-md);

  /* type families — one face, per the skill */
  --font-display: var(--tf-font);
  --font-body: var(--tf-font);
  --font-mono: var(--tf-font-mono);
}

/* Base type from the token scale. Individual screens still carry raw px
   sizes in styles.css; those go as each screen is migrated. Setting the base
   here means anything that does NOT override inherits the correct step. */
body {
  font-family: var(--tf-font);
  font-size: var(--tf-text-body);
  line-height: var(--tf-leading-body);
  color: var(--tf-fg);
  background: var(--tf-bg);
}

/* ---------------------------------------------------------------- login screen
   The skill is explicit: "The login screen uses the same --tf-bg family as the
   app — no bespoke gradient." It had its own --tf-navy-* palette and a radial
   gradient, so it read as a different product from the thing behind it.

   Re-pointed here rather than rewritten: fifteen rules reference these tokens,
   and aliasing them keeps the change to six lines and one override. */
:root,
html[data-theme="dark"] {
  --tf-navy-950: var(--tf-bg);
  --tf-navy-900: var(--tf-surface);
  --tf-navy-800: var(--tf-bg);
  --tf-navy-border: var(--tf-border);
  --tf-navy-text: var(--tf-fg);
  --tf-navy-muted: var(--tf-fg-muted);
}

.login-screen {
  background: var(--tf-bg);
}
.login-card {
  border-radius: var(--tf-radius-lg);
  box-shadow: var(--tf-shadow-lg);
}

/* ------------------------------------------------------- collapsed rail fixes
   .identity shrink-wraps to its content and is border-box, so at 76px collapsed
   an 8px padding left an 18px content box holding a 34px avatar — it spilled
   over both edges and read as a clipped circle. Span the rail instead and let
   the avatar centre inside it. */
.sidebar.collapsed .identity{
  width:100%;
  box-sizing:border-box;
  margin:0 0 var(--tf-space-3);
  padding:var(--tf-space-2) 0;
}


/* ------------------------------------------------------------- nav icon size
   18px in a 19px box read small next to 14px body text. The skill's .tf-icon
   default is 20px; the rail takes that, sub-items stay a step down. */
.nav-strip-icon{ width:22px; height:22px; }
.nav-strip-icon svg{ width:20px; height:20px; }
.nav-strip-sub .nav-strip-icon{ width:19px; height:19px; }
.nav-strip-sub .nav-strip-icon svg{ width:17px; height:17px; }

/* ------------------------------------------------- no jump when the rail opens
   .sidebar animates width over 280ms, but the collapsed class flips instantly.
   The collapsed identity used 8px vertical padding against the expanded 12px,
   and a 12px bottom margin against 14px, so the avatar stepped down and then
   settled as the width finished animating. Vertical metrics are now identical
   in both states — only the horizontal ones change, which is the axis that is
   actually animating.

   The 3px accent left border also shifts the centred avatar off-axis on a
   76px rail, so the collapsed state carries an even 1px border instead. */
.sidebar.collapsed .identity{
  width:100%;
  box-sizing:border-box;
  margin:0 0 14px;
  padding:12px 0;
  border-left-width:1px;
}

/* ------------------------------------------------------- brand block height
   The real cause of the jump on expand. The brand is 26+44+22 = 92px expanded
   and 22+28+18 = 68px collapsed, because the full logo (44px) is swapped for
   the icon (28px). The class flips instantly while the sidebar width animates
   over 280ms, so everything below — starting with the avatar — stepped 24px
   and then slid back as the animation finished.

   Pin the height in both states and let align-items:center place whichever
   logo is showing. Nothing below it moves. */
.brand{ box-sizing:border-box; min-height:92px; }
.sidebar.collapsed .brand{ min-height:92px; }

/* ...and the identity itself was the rest of it. Mid-animation the rail is
   ~99px wide, the name and role become visible instantly, and in that narrow
   column they wrap into six or seven lines: the identity goes 60px -> 157px
   -> 108px, and the vertically-centred avatar rides down 25px with it before
   settling.

   Measured, not guessed — the brand and divider stay put throughout; only the
   identity's height moves.

   Keeping the text on one line each holds the height constant at every width
   the animation passes through, and clipping reads better than a column of
   wrapped fragments. */
.identity-row > div:not(.identity-avatar){ min-width:0; }
.identity{ overflow:hidden; }
/* The one-line clamp that used to live here applied at all times and truncated
   the role permanently. It now applies only while the rail animates — see the
   .sidebar.is-animating rules further down. */

/* ----------------------------------------------------------- destructive action
   There was no danger button variant, so "Record pass and strike" — which
   fabricates a mark and issues a disciplinary strike in one irreversible write
   — rendered in the same navy as "Save". Semantic colour is status only per the
   skill, and this is the one place a button legitimately carries it. */
.btn.danger{
  background:var(--tf-danger);
  border-color:var(--tf-danger);
  color:var(--tf-accent-fg);
}
.btn.danger:hover{ filter:brightness(0.92); }
.btn.danger:focus-visible{ outline:2px solid var(--tf-danger); outline-offset:2px; }

/* ------------------------------------------------------------- override control
   The override used to fire from the table cell itself: an unlabelled <td>
   with cursor:help — the cursor that means "this is an annotation, clicking
   does nothing" — and no role, tabindex or accessible name. 28 of 36 cells in
   a live cohort were armed that way, and nothing focusable existed inside the
   matrix at all, so the action was unreachable by keyboard.

   The cell is a read-only summary again. The action is a real button. */
.staff-cell .cell-override{
  display:block; margin-top:var(--tf-space-1);
  font-size:var(--tf-text-micro); line-height:1.2;
  padding:2px 6px; white-space:nowrap;
}
.staff-cell.is-actionable{ cursor:default; }

/* -------------------------------------------------------------- icon slots
   Emoji were standing in for icons in 27 places while a Lucide sprite shipped
   unused. Emoji render in the OS colour font: they ignore currentColor, so
   they stayed full-colour in dark mode, and they sit on a different baseline
   from the text beside them. These slots size the replacements. */
.cbt-icon .tf-icon,
.fh-icon .tf-icon{ width:20px; height:20px; }
.empty-state .ic .tf-icon,
.wiki-empty .ic .tf-icon{ width:28px; height:28px; color:var(--tf-fg-subtle); }
/* Icons that sit inline in button or badge text need the baseline nudge a
   glyph would have given for free. */
.btn .tf-icon,
.badge .tf-icon{ vertical-align:-0.15em; margin-right:0.35em; }

/* ========================================================== responsive repair
   Below 880px the sidebar is meant to become an off-canvas drawer:
   styles.css:973 sets position:fixed inside the media query. But
   styles.css:1215 then sets position:sticky UNCONDITIONALLY — same specificity,
   later in the file, so it wins at every width. The transform survived and the
   position did not, so on a phone the sidebar slid out of view while still
   reserving its 264px in the flex row.

   Result at 390px: 284px of horizontal page scroll, and the participants
   table's scroll container squeezed to 46px around a 643px table.

   Restated inside the same breakpoint so it beats the unconditional rule. */
@media (max-width:880px){
  .sidebar{ position:fixed; }
  /* The rail must not hold a column open once it has left the flow. */
  .sidebar,
  .sidebar.collapsed{ flex-basis:auto; width:var(--tf-sidebar-w); }
  /* The rail floats with a 16px margin on desktop. As a drawer that margin
     offsets it from left:0, so translateX(-100%) left exactly 16px of it —
     and the logo — visible down the edge of every phone screen. */
  /* The rail floats with a 16px margin on desktop; as a drawer that margin
     offset it from left:0, so translateX(-100%) left 16px of it on screen. */
  .sidebar{ margin:0; border-radius:0; }
  .app{ display:block; }
}

/* .app was declared twice with different models — display:flex at
   styles.css:95 and display:grid in components.css, on different breakpoints
   (820 vs 880). Flex wins by load order, so the grid columns were inert.
   Keep the flex model, which is what actually renders, and stop pretending
   otherwise. */
.app{ display:flex; }

/* --------------------------------------------------- folder review clarity
   "No flags" and "Approved" shared .badge.ok, so nine of eleven folders read
   green while two were actually approved — a false all-clear on the screen
   that exists to show what still needs attention. Neutral means "nothing
   found"; green now means a human signed it off. */
.badge.neutral{
  background:var(--tf-bg);
  color:var(--tf-fg-muted);
  border:1px solid var(--tf-border);
}

/* ------------------------------------------------- mass & balance provenance
   The AFM cross-check warning was the smallest text on the page. It is a
   safety notice: give it the weight of one. */
/* components.css ships .alert with -info and -danger; warning was the one
   tone missing, and this notice is precisely a warning. Added as a variant of
   the existing component rather than as a new one — my first attempt invented
   a bespoke notice with a left accent bar, which is the side-tab pattern the
   detector flags and the skill bans. */
.alert-warning{ background:var(--tf-warning-bg); color:var(--tf-warning); }
.alert-warning strong{ color:var(--tf-warning); }

/* ---------------------------------------------------------- my regressions
   1. The sidebar role was permanently truncated to "Chief Theory Knowledge In…"
      because the nowrap/ellipsis fix for a transient 25px avatar jump was
      applied unscoped. Scope it to the collapse animation: the identity holds
      one line while the rail is animating, and wraps normally at rest. */
.identity-name{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.identity-course{ white-space:normal; }
.sidebar.is-animating .identity-name,
.sidebar.is-animating .identity-course{
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* 2. The drawer logo is 212px inside a 232px rail with padding, so it clipped
      by a few pixels once the drawer started clipping horizontally. */
.brand-logo{ max-width:100%; }
/* .alert is display:flex with a gap — it expects an icon and one text child.
   Passing a paragraph containing <strong> made the strong a second flex item
   and split the sentence into columns. Keep the icon from shrinking and let
   the text take the rest. */
.alert > .tf-icon{ flex:none; margin-top:1px; }
.alert > p{ flex:1 1 auto; }

/* ================================================ participants matrix columns
   The columns used to be seven identical "CBT n" headers over six columns of
   em-dashes, which reads as "nobody has done any work". The truth was that
   CBT 2-7 were still drafts nobody could open. In an ATO the axis that decides
   whether anyone needs chasing is the deadline, so the column carries it. */
.staff-matrix th.cbt-col{
  vertical-align:top;
  padding:var(--tf-space-2) var(--tf-space-2);
  min-width:104px;
}
.cbt-col-num{
  display:block; font-size:var(--tf-text-micro); letter-spacing:.05em;
  color:var(--tf-fg-muted);
}
.cbt-col-title{
  display:block; margin:2px 0 3px;
  font-size:var(--tf-text-small); font-weight:var(--tf-weight-medium);
  color:var(--tf-fg); text-transform:none; letter-spacing:0;
  /* Titles were only ever in a tooltip, so the columns were unreadable to
     anyone not hovering — and to anyone on a touch screen. */
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:150px;
}
.cbt-col-state{
  display:block; font-size:var(--tf-text-micro); text-transform:none;
  letter-spacing:0; color:var(--tf-fg-muted); font-weight:var(--tf-weight-normal);
}
.cbt-col-state.is-urgent{ color:var(--tf-warning); font-weight:var(--tf-weight-medium); }
.staff-matrix th.cbt-col.is-draft .cbt-col-title{ color:var(--tf-fg-muted); }
.staff-matrix th.cbt-col.is-closed .cbt-col-state{ color:var(--tf-danger); }

/* An unreleased column is greyed as a band, so its em-dashes read as "nobody
   can sit this yet" rather than "nobody has". */
.staff-cell.is-unreleased{ background:var(--tf-bg); }
.staff-cell.is-unreleased .staff-cell-score,
.staff-cell.is-unreleased span[aria-hidden]{ opacity:.45; }

/* =========================================== matrix must not widen the page
   The participants table sits in a 326px container with overflow-x:auto, and
   still pushed the DOCUMENT to 1171px at a 390px viewport — 781px of
   horizontal page scroll on the Staff Panel. The container was scrolling
   correctly; the table's width was leaking past it into the document's own
   scroll width anyway, which the sticky header and sticky first column make
   worse.

   Isolated by elimination rather than theory: hiding the table dropped the
   page overflow from 781 to 20, and of the candidate fixes (max-width, an
   explicit width, overflow-y:visible, dropping sticky, a grid wrapper) only
   `contain: paint` worked — it stops the subtree contributing its overflow
   to any ancestor's scrollable area.

   My own doing: widening the columns to carry the module title and deadline
   made the table wide enough for this to show. */
.cbt-report-scroll{ contain:paint; }

/* The Staff Panel's own tab bar accounted for the remaining 20px — four tabs
   do not fit 390px. Same treatment the course bar already has: scroll it
   rather than let it widen the page. */
.staff-tabbar{ max-width:100%; overflow-x:auto; scrollbar-width:none; }
.staff-tabbar::-webkit-scrollbar{ display:none; }

/* ------------------------------------------------------ login theme toggle
   The theme control used to exist only in the app topbar, which you can only
   reach by signing in — so someone who works in dark had to cross a bright
   white screen to get to the switch. Same handler and same stored preference
   as the topbar button; this is a second door to it, not a second setting.

   It sits where the topbar's own button sits: page top-right, same 36px
   .icon-btn, same 16px/28px inset (12px/16px on a phone, matching .topbar's
   own media query). Signing in then swaps the login screen for the app
   without the control moving.

   A row rather than absolute positioning: .login-card is vertically centred
   and grows with the demo-account list, so an absolutely-placed corner button
   collides with the card on a short viewport. As a flex row it cannot. */
.login-screen{
  flex-direction:column;
  align-items:stretch;
  justify-content:flex-start;
  padding:16px 28px 24px;
}
.login-topbar{ display:flex; justify-content:flex-end; }
.login-card{ margin:auto; }   /* centres in whatever space the row leaves */

@media (max-width: 720px){
  .login-screen{ padding:12px 16px 20px; }
}

/* ---------------------------------------------------- flight folder lifecycle
   Three things the folder pages gained on 2026-08-30: who was PIC, the
   read-only archive record, and the correction trail shown inline with the
   figure it corrected. */

/* Under the Dual/Solo picker: spells out whose name goes in the commander's
   box, because "Dual"/"Solo" is unambiguous to an instructor and not to a
   first-week student. */
.pic-note{
  margin:4px 0 0; font-size:var(--tf-text-micro); color:var(--tf-fg-muted);
  line-height:1.35;
}

/* The correction trail. Sits under the field label rather than in its own
   column: the reason belongs to the value it changed, and a fifth column of
   free text would have made the row unreadable at any width. */
.ovr-trail{
  margin-top:3px; font-size:var(--tf-text-micro); line-height:1.4;
  color:var(--tf-fg-muted);
}
.ovr-trail.is-old{ opacity:.75; }
.ovr-meta{ font-style:italic; }

/* What the archive says where the instructor's action bar would be. Deliberately
   not styled as a toolbar — there is nothing here to press. */
.review-footer{
  margin-top:var(--tf-space-4); padding-top:var(--tf-space-3);
  border-top:1px solid var(--tf-border);
}
.review-footer p{ margin:0 0 4px; font-size:var(--tf-text-small); }
.review-note{
  display:flex; align-items:flex-start; gap:6px;
  color:var(--tf-fg-muted); font-size:var(--tf-text-micro); line-height:1.45;
}
.review-note .tf-icon{ flex:0 0 auto; margin-top:1px; }

/* The archive filter bar carries ten controls where it used to carry three.
   Wrapping is the whole layout; without a floor the date and time inputs
   collapse to nothing beside the wider selects. */
.staff-filters{ flex-wrap:wrap; align-items:flex-end; gap:var(--tf-space-2) var(--tf-space-3); }
.staff-filters .staff-field{ min-width:118px; }
.staff-filters .staff-field input[type="date"],
.staff-filters .staff-field input[type="time"]{ min-width:0; width:100%; }

/* A disabled Approve button has to read as "not yet", not as broken chrome —
   it is carrying the reason in its title and the count right beside it. */
.save-btn:disabled{ opacity:.55; cursor:not-allowed; }

/* --------------------------------------------------- folder review findings
   Everything the review flags is a problem, so everything it flags is red.
   Amber was reading as "noted" on a screen where the whole point is the rows
   you stop on.

   Two strengths, because two different things are being said:

     DISCREPANCY  these two numbers disagree        light red, 3px rule
     BREACH       the aeroplane is outside limits   deeper red, 4px rule, bold

   The left rule is an inset shadow rather than a border so it does not shift
   the cell by a pixel and does not disappear when the table scrolls sideways. */
/* The rule marks the LEFT EDGE OF THE ROW, so it belongs on the first cell
   only. Put on every > td — as it was — a five-column row drew five vertical
   bars, one at each cell boundary, which is not a margin note but a fence. */
.iff-native .diff-row-warn > td{ background: var(--tf-danger-bg); }
.iff-native .diff-row-warn > td:first-child{
  box-shadow: inset 3px 0 0 0 var(--tf-danger);
}
.iff-native .diff-row-warn > td:first-child{ font-weight: var(--tf-weight-medium); }
/* The status word carries the same colour as the row it labels. */
.iff-native .diff-row-warn .badge.warn{
  background: transparent; color: var(--tf-danger); font-weight: var(--tf-weight-medium);
}

/* One step deeper than --tf-danger-bg (#fee2e2), so a breach reads as more
   than a disagreement without introducing a second hue. */
.iff-native .diff-row-breach > td{
  background: #fcd2d2;
  color: var(--tf-danger);
  font-weight: var(--tf-weight-medium);
}
.iff-native .diff-row-breach > td:first-child{
  box-shadow: inset 4px 0 0 0 var(--tf-danger);
}
html[data-theme="dark"] .iff-native .diff-row-breach > td{ background: #4a1d1d; }

/* The override control on a breach is the same control doing a heavier job. */
.iff-native .override-btn.is-breach{ border-color: var(--tf-danger); color: var(--tf-danger); }
.iff-native .override-btn.is-breach:hover{ background: var(--tf-danger); color: #fff; }
.iff-native .ovr-hint{
  display:block; margin-top:3px;
  font-size: var(--tf-text-micro); color: var(--tf-fg-muted); line-height:1.35;
}

/* ------------------------------------------------ mass & balance, one table
   The entered figure, and the aircraft's own figure directly beneath it. Two
   numbers an inch apart, rather than the same row read twice in two tables. */
.iff-native .mb-table.is-compare td.num{ line-height:1.25; }
.iff-native .mb-entered{ display:block; }
.iff-native .mb-system{
  display:block; margin-top:1px;
  font-size: var(--tf-text-micro); color: var(--tf-fg-muted);
  font-variant-numeric: tabular-nums;
}
/* Where the two disagree, the aircraft's figure is the one to read. */
.iff-native .mb-system.is-off{ color: var(--tf-danger); font-weight: var(--tf-weight-medium); }
.iff-native .mb-system::before{ content:"sys "; opacity:.65; }

/* .mb-table is table-layout:fixed with 34% + 3x22% = 100%, which left exactly
   nothing for the two columns this variant adds — Status and Override came out
   zero-wide and the table overflowed its own scroll box by 112px. Six columns
   need six shares. */
.iff-native .mb-table.is-compare col.c-label{ width:24%; }
.iff-native .mb-table.is-compare col.c-num{ width:16%; }
.iff-native .mb-table.is-compare col:nth-child(5){ width:14%; }
.iff-native .mb-table.is-compare col:nth-child(6){ width:14%; }
.iff-native .mb-table.is-compare th:nth-child(5),
.iff-native .mb-table.is-compare th:nth-child(6){ white-space:nowrap; }
/* The Override button is the row's action; keep it on one line at 14%. */
.iff-native .mb-table.is-compare .override-btn{ white-space:nowrap; }

/* --------------------------------------------------------------- PIC choice
   Two options do not need a dropdown. Segmented, in the same language as the
   Staff Panel's tab bar, and with no explanation of what Dual or Solo mean —
   a student pilot knows. */
.pic-toggle{ display:flex; gap:4px; }
.pic-toggle .pic-opt{
  flex:1 1 auto; padding:7px 10px; cursor:pointer; white-space:nowrap;
  font-size:var(--tf-text-small); font-weight:var(--tf-weight-medium);
  color:var(--tf-fg-muted); background:var(--tf-bg);
  border:1px solid var(--tf-border); border-radius:var(--tf-radius-sm);
  transition:background var(--tf-transition), color var(--tf-transition), border-color var(--tf-transition);
}
.pic-toggle .pic-opt:hover{ border-color:var(--tf-border-strong); color:var(--tf-fg); }
.pic-toggle .pic-opt.active{
  background:var(--tf-accent); border-color:var(--tf-accent); color:var(--tf-accent-fg);
}

/* ------------------------------------------------- kg beside the AFM's unit
   Muted and parenthetical: the aircraft's own unit is the figure of record and
   the kg is the one the school thinks in. */
.iff-native .mb-kg{ color:var(--tf-fg-muted); font-size:var(--tf-text-micro); font-weight:400; }

/* Why a row is outside limits, on the row it is about. */
.iff-native .mb-breach{
  margin-top:3px; font-size:var(--tf-text-micro); line-height:1.35;
  color:var(--tf-danger); font-weight:var(--tf-weight-medium);
}

/* ------------------------------------------------------------- override form
   The cause is the common case pre-selected; the comment is optional and says
   so, because a mandatory sentence on every row produces eight paraphrases of
   "typo" and buries the two that matter. */
.iff-native .ovr-optional{ color:var(--tf-fg-muted); font-weight:400; text-transform:none; letter-spacing:0; }
/* The group action moved out of the section heading and under the table it
   acts on, so the rules that made it sit inline beside a heading — a left
   margin, micro type, vertical-align — are gone. It is deliberately the larger
   of the two override buttons now: it acts on every row at once, and at the
   row buttons' size it read as one of them that had drifted out of place.
   Sized here rather than in iff-native.css because bridge.css loads last. */
.iff-native .override-btn.ovr-all{
  font-size:var(--tf-text-small); font-weight:600; padding:8px 18px;
  color:var(--tf-fg); text-transform:none; letter-spacing:0;
}
.iff-native .override-btn.ovr-all:disabled{ font-weight:500; }
.iff-native .group-ovr{
  margin:0 0 var(--tf-space-3); padding:var(--tf-space-3);
  border:1px solid var(--tf-border-strong); border-radius:var(--tf-radius-md);
  background:var(--tf-bg);
}
.iff-native .group-ovr-count{
  margin:0 0 var(--tf-space-2); font-size:var(--tf-text-small); color:var(--tf-fg-muted);
}

/* --------------------------------------------------------- system error list
   Grouped by field: the same AFM figure flagged three times is one number to
   go and check against the paper, not three separate incidents. */
.syserr-group{
  margin-bottom:var(--tf-space-3); padding:var(--tf-space-3);
  border:1px solid var(--tf-border); border-radius:var(--tf-radius-md);
  background:var(--tf-surface);
}
.syserr-group summary{
  cursor:pointer; display:flex; align-items:center; gap:var(--tf-space-2);
  font-size:var(--tf-text-body);
}
.syserr-group[open] summary{ margin-bottom:var(--tf-space-3); }

/* --------------------------------------------------- the slot-time go/no-go
   A judgement made minutes before the flight, on the weather as it actually
   is — distinct from the planning-time minima tick, which is about a forecast
   from hours earlier. It sits above the legs because it is a fact about the
   flight, not about leg 2. */
/* The left rule ENCODES the state — grey undecided, green going, red not
   going, amber nobody said. It is not an accent rail for decoration, and it is
   the pattern this codebase already uses for exactly that: .identity (3px
   accent), .wiki-blockquote, the info callout at styles.css:1374. The detector
   flags it as a side-tab; noted and kept, because removing it would remove the
   only thing that makes the four states distinguishable at a glance. */
.wx-decision{
  margin:0 0 var(--tf-space-4); padding:var(--tf-space-3) var(--tf-space-4);
  border:1px solid var(--tf-border); border-left:3px solid var(--tf-border-strong);
  border-radius:var(--tf-radius-md); background:var(--tf-surface);
}
.wx-decision.is-go{ border-left-color:var(--tf-success); }
.wx-decision.is-no_go{ border-left-color:var(--tf-danger); }
/* The slot has started and nobody said either way. That is the state an
   instructor most needs to notice, so it is the one that is coloured. */
.wx-decision.is-missing{ border-left-color:var(--tf-warning); background:var(--tf-warning-bg); }

.wx-dec-head{
  display:flex; align-items:center; flex-wrap:wrap; gap:var(--tf-space-2);
  margin:0; font-size:var(--tf-text-small); font-weight:var(--tf-weight-medium);
}
.wx-dec-when{ color:var(--tf-fg-muted); font-weight:400; font-size:var(--tf-text-micro); }
.wx-dec-note{
  margin:var(--tf-space-2) 0 0; font-size:var(--tf-text-micro);
  color:var(--tf-fg-muted); line-height:1.45;
}
.wx-dec-actions{ display:flex; gap:var(--tf-space-2); margin-top:var(--tf-space-3); }
.wx-dec-reports{ margin-top:var(--tf-space-3); }
.wx-dec-reports > summary{
  cursor:pointer; font-size:var(--tf-text-micro); color:var(--tf-fg-muted);
}
.wx-dec-reports[open] > summary{ margin-bottom:var(--tf-space-2); }

/* ------------------------------------------------------------ compose screen
   Writing a folder happens on its own screen. It used to be a card at the top
   of the same page as the folder list, and the form is several screens tall —
   so the list you wanted was always below a half-finished folder. Two jobs on
   one page was the confusion; one job per screen is the fix. */
.compose-head{
  display:flex; align-items:center; gap:var(--tf-space-3);
  margin-bottom:var(--tf-space-4);
}
.compose-sub{ font-size:var(--tf-text-micro); color:var(--tf-fg-muted); }

/* The only way anything leaves this screen: keep it for later, or send it.
   Sticky, because the form is long and the two things you might want to do
   with it should not be a scroll away. */
.compose-bar{
  position:sticky; bottom:0; z-index:5;
  display:flex; align-items:center; justify-content:space-between;
  flex-wrap:wrap; gap:var(--tf-space-3);
  margin-top:var(--tf-space-4); padding:var(--tf-space-3) var(--tf-space-4);
  border:1px solid var(--tf-border); border-radius:var(--tf-radius-md);
  background:var(--tf-surface); box-shadow:var(--tf-shadow-md);
}
/* margin-left:auto keeps the actions hard right — including on the narrow
   widths where the bar wraps them onto their own line, which justify-content on
   the parent does not reach. The send button is last in the group, so it is the
   rightmost thing on the screen: the one action that ends the screen sits where
   the eye finishes. */
.compose-actions{ display:flex; gap:var(--tf-space-2); align-items:center; margin-left:auto; }

/* A draft reads as unfinished, not as a folder that failed something. */
.iff-native .folder-row.is-draft .fh-title{ color:var(--tf-fg-muted); }
.iff-native .folder-row.is-draft .fh-icon{ color:var(--tf-fg-muted); }

/* ------------------------------------------------- controlled documents
   The Documents tab in the Staff Panel. Deliberately plainer than the rest of
   the app: these screens are read by someone reconciling a manual against a
   regulator's copy, and the thing that helps them is a table that looks like
   the table in the manual. */
.wd-list{ display:flex; flex-direction:column; gap:6px; margin-bottom:var(--tf-space-4); }
.wd-row{
  display:flex; align-items:center; justify-content:space-between; gap:var(--tf-space-3);
  width:100%; text-align:left; cursor:pointer;
  padding:11px 14px; border:1px solid var(--tf-border); border-radius:var(--tf-radius-md);
  background:var(--tf-surface); color:inherit;
}
.wd-row:hover{ border-color:var(--tf-border-strong); }
.wd-row.is-open{ border-color:var(--tf-accent); }
/* The row's own glyph carries one fact: in force, or not yet issued. Muted for
   the second, so a list of manuals reads at a glance as what is live and what
   is still being written. */
.wd-row-icon{
  flex:none; display:flex; align-items:center; justify-content:center;
  width:34px; height:34px; border-radius:9px;
  background:var(--tf-bg); color:var(--tf-fg-subtle);
}
.wd-row-icon.is-issued{ color:var(--tf-accent); }
.wd-row-icon .tf-icon{ width:18px; height:18px; }
.wd-row-main{ display:flex; flex-direction:column; gap:2px; min-width:0; flex:1 1 auto; }
.wd-title{ font-weight:var(--tf-weight-medium); }
.wd-sub{ font-size:var(--tf-text-micro); color:var(--tf-fg-muted); }
.wd-row-flags{ display:flex; gap:6px; flex:none; }

.wd-new{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:var(--tf-space-4); }
.wd-new input{ flex:1 1 240px; min-width:0; }

.wd-doc{
  border:1px solid var(--tf-border); border-radius:var(--tf-radius-md);
  padding:var(--tf-space-4); margin-bottom:var(--tf-space-4); background:var(--tf-surface);
}
.wd-body{ margin-top:var(--tf-space-3); }

/* The cover block. Three columns because the manuals have three. */
.wd-sigblock{
  display:flex; gap:var(--tf-space-5); flex-wrap:wrap;
  padding:10px 0 14px; border-bottom:1px solid var(--tf-border); margin-bottom:var(--tf-space-3);
}
.wd-sig{ display:flex; flex-direction:column; gap:1px; }
.wd-sig-h{
  display:flex; align-items:center; gap:5px;
  font-size:var(--tf-text-micro); text-transform:uppercase; letter-spacing:.06em;
  color:var(--tf-fg-muted);
}
.wd-sig-h .tf-icon{ width:13px; height:13px; }
/* The stage glyph sits with its label in a table cell, where a bare svg aligns
   to the baseline — components.css sets svg{display:block} globally. */
.wd-stage .tf-icon{ display:inline-block; vertical-align:-0.16em; margin-right:.35em; width:14px; height:14px; }
.wd-sig-role{ font-size:var(--tf-text-micro); color:var(--tf-fg-muted); }
.wd-sig-who{ font-weight:var(--tf-weight-medium); }
.wd-when{ font-size:var(--tf-text-micro); color:var(--tf-fg-muted); }

/* Markdown, in a monospace box: this is a controlled document being edited, and
   what matters is the exact characters — page markers included. */
.wd-editor{
  width:100%; min-height:340px; resize:vertical;
  font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size:12.5px; line-height:1.55;
  padding:12px; border:1px solid var(--tf-border); border-radius:var(--tf-radius-md);
  background:var(--tf-bg); color:var(--tf-fg);
}
.wd-lep td:first-child{ font-variant-numeric:tabular-nums; }
.wd-routes{ margin-top:var(--tf-space-5); padding-top:var(--tf-space-4); border-top:1px solid var(--tf-border); }
.wd-route-sel{ min-width:170px; }

/* A page mark in a controlled document: the printed page number the text below
   it starts on. Quiet enough to read past, present enough to find. */
.wd-pagemark{
  display:flex; align-items:center; gap:var(--tf-space-3);
  margin:var(--tf-space-5) 0 var(--tf-space-4);
  font-size:var(--tf-text-micro); letter-spacing:.06em; text-transform:uppercase;
  color:var(--tf-fg-subtle);
}
.wd-pagemark::before,
.wd-pagemark::after{ content:''; height:1px; background:var(--tf-border); flex:1 1 auto; }
.wd-pagemark span{ flex:none; }

/* The SOPs carry diagrams scanned from the manual — circuit patterns, signal
   charts — at their natural size, which is 1390px. Unconstrained they push the
   whole Staff Panel sideways by 650px. The manuals view renders the same
   .wiki-doc-body the wiki does, so this belongs to that body wherever it is
   used, not to the documents pane alone. */
.wiki-doc-body img{ max-width:100%; height:auto; display:block; }
.wiki-doc-body pre,
.wiki-doc-body table{ max-width:100%; overflow-x:auto; }
