/* ============================================================
   Base — reset, typography, app shell
   ------------------------------------------------------------
   docs/08-design-system-v2.md §2–§4; file architecture per
   docs/08-design-refresh.md §4, which still stands. Owns the frame
   the application is drawn in: .tm-app / .tm-sidebar / .tm-main /
   .tm-header / .tm-content, plus the global element defaults, the
   .tm-mono utility and the card primitive (.ui.segment — §4 gives
   it no module of its own).

   Every size, weight, colour, radius, shadow and gap here reads a
   --tm-* token; literals are only permitted for structural values
   the token set does not model (1px hairlines, 50% circles,
   scrollbar gutters).

   !important budget: 6, all of them in the margin utilities at the
   bottom, and each one carries the reason. Everything else that used
   to need !important is now beaten on specificity — Fomantic's own
   selectors are quoted in the comment above each such rule, because
   the specificity is the only thing keeping the override alive.
   ============================================================ */

/* ── Fomantic's ink and paper (TM-143 step 2) ─────────────────
   These two are NOT design tokens and deliberately do not live in tokens.css.
   They are the two ends of a wire: scripts/vendor-fomantic.py rewrites every
   `color:rgba(0,0,0,A)` in the vendored sheet as `rgba(var(--tm-fm-ink,0,0,0),A)`
   and every `background:#fff` as `var(--tm-fm-paper,#fff)`, and this is where
   those variables get a value. tokens.css answers "what colour is a warning";
   this answers "what does Fomantic mean by black", which is a different kind of
   question and belongs next to the other Fomantic overrides.

   WHY IT HAD TO BE DONE THIS WAY. Fomantic states its light theme in ~280
   literals — `.ui.list .header` is `rgba(0,0,0,.87)`, `.ui.modal` is `#fff` —
   and a token swap cannot reach any of them. That is exactly how TM-152 put
   near-black text on the new near-black card at 1.10:1 and got the dark theme
   withdrawn. Overriding them from here selector by selector would have been ~100
   specificity guesses against minified selectors, covering only the screens
   somebody thought to look at; rewriting the declaration in place costs two
   variables and covers the components nobody has built a screen for yet.

   THE INK IS A CHANNEL TRIPLET, NOT A COLOUR, because the alpha stays with
   Fomantic. Its own text ramp is .87 body / .6 secondary / .4 disabled, and
   keeping it means the ramp inverts rather than flattening. Composited on
   --tm-surface in the dark theme, those three measure

     .87  13.4:1  (DarkThemeContrastTests reads 13.38 off a real .ui.popup)
     .6    6.9:1
     .4    3.8:1

   against our own 14.4 / 7.3 / 3.6 for --tm-text / --tm-text-secondary /
   --tm-text-muted on the same ground: the two systems agree step for step
   instead of arguing. The .4 step is under AA in both ramps and in both themes,
   because it is what each of them uses for disabled text, which WCAG 1.4.3
   exempts.

   --tm-fm-paper FOLLOWS --tm-surface, not --tm-bg. Every white Fomantic paints
   is a raised thing — modal, popup, dropdown menu, card, table, input, toast —
   and --tm-surface is what the design system calls that plane. Where a component
   wants the other plane, our own component sheet already says so and wins on
   source order.

   The light values restate exactly what the vendored fallbacks already say, so
   the light theme renders byte-identically whether or not this block loads. */
:root {
    --tm-fm-ink: 0, 0, 0;
    --tm-fm-paper: #FFFFFF;
}

:root[data-theme="dark"] {
    --tm-fm-ink: 255, 255, 255;
    --tm-fm-paper: var(--tm-surface);
}

/* The `system` state of the theme switcher (TM-143 step 3). This wire needs the
   same three selectors tokens.css has, and it needs them in the SAME change: a
   media query that flips the palette but leaves the ink at 0,0,0 is TM-152
   exactly — Fomantic's near-black text on our new near-black card, at 1.10:1,
   delivered to every reader whose OS is dark and who never asked for a dark
   theme. The duplication is held to the block above by
   DarkThemeContrastTests.PrefersColorSchemeDark_AndDataThemeDark_MustResolveIdentically,
   which compares every --tm-* property across the two activation paths and so
   covers these two as well. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --tm-fm-ink: 255, 255, 255;
        --tm-fm-paper: var(--tm-surface);
    }
}

/* ── Base Reset & Typography ──────────────────────────────── */
html, body {
    height: 100%;
    margin: 0;
    font-family: var(--tm-font-sans);
    background-color: var(--tm-bg);
    color: var(--tm-text);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* The body default of the type scale, deliberately NOT set on `html`: every
   --tm-text-* token is expressed in rem, so moving the root font size would
   rescale the whole token set recursively. The root stays where Fomantic puts
   it (14px) and only the inherited text size moves.

   --tm-text-md, NOT --tm-text-base. The v2 scale renumbered the steps: `base`
   is now 13px and belongs to dense table content, while `md` (14px) is the
   default for body copy and form text (§2). Both render at the size the old
   `base` did or below, so this is not a size change on screen — it is the same
   14px, read from the token that now means "default".

   --tm-text on --tm-surface measures 18.32:1, on --tm-bg 17.24:1 — WCAG AA for
   normal text needs 4.5:1. */
body {
    font-size: var(--tm-text-md);
    line-height: var(--tm-leading-md);
}

/* Override Fomantic UI's Lato with the interface face.
   No !important: every selector below matches Fomantic's own font-family rule
   at equal specificity (`.ui.button`, `.ui.header`, `.ui.menu` are 0-2-0;
   `.ui.input>input` is 0-2-1) and this sheet is linked after semantic.min.css,
   so the later rule wins on cascade order alone.

   That gap is CLOSED, but not here. Fomantic declares Lato directly on
   `.ui.form input[type=…]` (0-3-1), `.ui.form textarea` and `.ui.modal>.header`.
   A font-family on the *parent* never overrode those — a child's own
   declaration always beats an inherited value, !important or not. Rather than
   chase each selector from this side, TM-145 rewrote all 22 `font-family: Lato,…`
   declarations to `var(--tm-font-sans),…` in the vendored copy of Fomantic
   itself, and cut its 8 Lato @font-face blocks out. There is no Lato left in the
   cascade to override, so the follow-up this comment used to promise in
   forms.css / overlays.css / feedback.css is not needed.

   The rules below are therefore redundant against the vendored sheet, and kept
   deliberately: they cost nothing and they are what keeps the interface face in
   place if the vendored copy is ever replaced with a pristine Fomantic build.

   Worth knowing if that ever happens: Fomantic 2.9.3's dist stylesheet does NOT
   import Lato from Google Fonts — it ships an @font-face pointing at a relative
   `themes/default/assets/fonts/LatoLatin-*.woff2`. Webfonts are fetched lazily,
   on first use, so Lato stops being downloaded the moment the last
   `font-family: Lato` in the cascade is overridden. */
.ui,
.ui.menu,
.ui.header,
.ui.button,
.ui.form,
.ui.table,
.ui.segment,
.ui.message,
.ui.modal,
.ui.dropdown,
.ui.label,
.ui.input input,
.ui.statistic {
    font-family: var(--tm-font-sans);
}

/* HEADINGS NEED THEIR OWN RULE, and the reason is not obvious.
   Fomantic ships `h1,h2,h3,h4,h5 { font-family: Lato,… }` — a declaration on the
   bare *element*, not on a `.ui` class. A font-family set on `body` never beats
   that, no matter its specificity, because an element's own declaration always
   wins over an inherited value. So every page title, card title and section
   heading in the application rendered in Lato while the rest of the interface
   rendered in the interface face, and it did so silently: nothing looks broken,
   the headings are simply set in a different typeface than everything around
   them. Measured in the running app on the Sites page — `<h1>` computed to Lato.

   This matched the previous round's Inter too, so it is not a v2 regression; it
   is a long-standing gap that only became worth fixing when the typeface became
   the point of the change.

   Both rules are 0-0-1 and this sheet loads after semantic.min.css, so source
   order settles it — no !important. Only the family is taken: Fomantic's
   font-weight, line-height and margins on the same rule are left alone, because
   `.tm-page-header` and `.tm-card-header` are what own heading metrics.

   The same mechanism used to keep Lato alive in ~20 other Fomantic rules whose
   selectors outrank the `.ui`-class list above (`.ui.form input[type=…]`,
   `.ui.modal>.header`, `.ui.message .header`, `.ui.card>.content>.header`,
   `.ui.statistic>.value`, `.ui.list .item .header`, `.ui.popup>.header`,
   `.ui.accordion>.title`, `.ui.steps .step .title`, `.ui.search .result .title`,
   `.ui.text.container`). TM-145 settled all of them at the source instead — see
   the note above and the header of the vendored Fomantic stylesheet. */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--tm-font-sans);
}

/* Preserve Fomantic's icon font. `i.icon` (0-1-1) outranks the bare `.ui`
   (0-1-0) above, so an `<i class="ui icon">` keeps its glyphs. */
i.icon {
    font-family: 'Icons';
}

/* ── Monospace utility ────────────────────────────────────────
   THE NAME IS `.tm-mono`. This is the only sanctioned way to reach
   --tm-font-mono; component sheets and pages must not write font-family by
   hand (§2).

   What it is for: identifiers and dates — personal numbers (BRN-018),
   requirement codes, timestamps, expiry dates, anything that appears in a
   column and gets compared down the page. Not for prose, not for names, not as
   decoration.

   `tabular-nums` is the functional half and travels with the class: it forces
   equal-width digits, so figures line up in a column even at different values.
   `font-size: .95em` is a literal on purpose and is not a token: IBM Plex Mono
   runs optically larger than Schibsted Grotesk at the same nominal size, so
   this is an optical correction tied to the pairing of these two faces, not a
   step in the type scale. It is relative (em) so the class can be dropped onto
   any size without pinning one.

   `.tm-mono` is a bare class (0-1-0) so it can sit on a <td> or a <span>
   anywhere; nothing in Fomantic sets font-family on those, so no !important. */
.tm-mono {
    font-family: var(--tm-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.95em;
    letter-spacing: 0;
}

/* ── Application Shell ────────────────────────────────────── */
.tm-app {
    display: flex;
    height: 100vh;
    overflow: hidden;
    background-color: var(--tm-bg);
}

/* ── Sidebar ──────────────────────────────────────────────── */
/* THE RAIL IS LIGHT (§3.4). It was a near-black slab, which is the single most
   dated thing the previous round left standing and which forced a whole parallel
   palette to exist just to stay legible on it. It is now an ordinary surface:
   --tm-sidebar-bg is white, separated from the content by a hairline on the
   right and by nothing else — no gradient, no shadow, no glow overlay.

   The right-hand hairline is the only thing dividing rail from content, so it is
   --tm-sidebar-border (the ordinary rule colour) and not a heavier edge; the
   difference between white and --tm-bg carries the rest.

   --tm-sidebar-item on --tm-sidebar-bg measures 6.18:1, clearing the WCAG AA
   4.5:1 text minimum. Contrast for the active item lives with the active item —
   the nav rows themselves are navigation.css / NavMenu.razor.css business; this
   rule only sets what they inherit and the ground they are measured against. */
.tm-sidebar {
    width: var(--tm-sidebar-width);
    min-width: var(--tm-sidebar-width);
    background: var(--tm-sidebar-bg);
    color: var(--tm-sidebar-item);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
    position: relative;
    z-index: 20;
    border-right: 1px solid var(--tm-sidebar-border);
}

/* Custom scrollbar for sidebar. The gutter width is a structural value with no
   token; the thumb is mixed down from --tm-sidebar-item so it tracks the
   sidebar palette instead of hardcoding an alpha. The mix now resolves to a
   light grey rather than a white veil, because --tm-sidebar-item flipped from a
   white alpha to a solid ink when the rail went light — which is exactly why it
   was written as a mix and not as a literal. */
.tm-sidebar::-webkit-scrollbar {
    width: 4px;
}

.tm-sidebar::-webkit-scrollbar-track {
    background: transparent;
}

.tm-sidebar::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--tm-sidebar-item) 25%, transparent);
    border-radius: var(--tm-radius-sm);
}

.tm-sidebar::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--tm-sidebar-item) 40%, transparent);
}

/* ── Main Content Area ────────────────────────────────────── */
.tm-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-width: 0;
}

/* ── Header ───────────────────────────────────────────────── */
/* White, --tm-header-height tall, and separated from the content by a single
   hairline. The box-shadow it used to carry is gone: with a light rail there is
   no longer a dark mass for the bar to float above, so the shadow only added a
   grey smear under an edge that a 1px rule already states. One divider per
   boundary (§3.4). */
.tm-header {
    height: var(--tm-header-height);
    min-height: var(--tm-header-height);
    background-color: var(--tm-surface);
    border-bottom: 1px solid var(--tm-border);
    padding: 0 var(--tm-space-5);
    display: flex;
    align-items: center;
    z-index: 10;
    position: relative;
}

/* Mobile drawer toggle - hidden on desktop, shown by the responsive rules below.
   Sized to --tm-control-height so it lines up with every other control. */
.tm-nav-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    width: var(--tm-control-height);
    height: var(--tm-control-height);
    margin-right: var(--tm-space-2);
    flex: 0 0 auto;
    padding: 0;
    border: none;
    border-radius: var(--tm-radius-sm);
    background: transparent;
    color: var(--tm-text-secondary);
    cursor: pointer;
    transition: background var(--tm-transition), color var(--tm-transition);
}

.tm-nav-toggle:hover {
    background: var(--tm-border-light);
    color: var(--tm-text);
}

.tm-nav-toggle i.icon {
    margin: 0;
    font-size: var(--tm-text-lg);
}

.tm-header .ui.menu {
    margin: 0;
    border: none;
    box-shadow: none;
    width: 100%;
    background: transparent;
    min-height: auto;
}

/* Header items agree with every other control on height, so the user pill,
   the sign-out link and the drawer toggle sit on one 36px baseline. */
.tm-header .ui.secondary.menu .item {
    min-height: var(--tm-control-height);
    padding: var(--tm-space-2) var(--tm-space-3);
    border-radius: var(--tm-radius-sm);
    color: var(--tm-text-secondary);
    font-size: var(--tm-text-base);
    line-height: var(--tm-leading-base);
    transition: background var(--tm-transition), color var(--tm-transition);
    margin: 0 var(--tm-space-1);
}

.tm-header .ui.secondary.menu .item:hover {
    background: var(--tm-border-light);
    color: var(--tm-text);
}

/* Was --tm-text-muted, which measures 2.55:1 on --tm-surface — under the WCAG
   1.4.11 3:1 floor for a non-text control. The sign-out link has no visible
   label, so its glyph *is* the control and has to clear that floor.
   --tm-text-secondary measures 6.18:1. */
.tm-header .ui.secondary.menu .item i.icon {
    color: var(--tm-text-secondary);
    transition: color var(--tm-transition);
}

/* --tm-primary on --tm-surface: 5.91:1. */
.tm-header .ui.secondary.menu .item:hover i.icon {
    color: var(--tm-primary);
}

/* The gap on .tm-user-pill spaces the avatar from the name; no margin needed. */
.tm-header-text {
    font-weight: var(--tm-weight-medium);
    font-size: var(--tm-text-base);
}

/* User profile pill in header */
.tm-header .right.menu .item {
    font-weight: var(--tm-weight-medium);
}

/* Fomantic's `.ui.menu:not(.vertical) .item` (0-3-0) already lays the item out
   as a vertically centred flex row, so the old `display: flex !important` and
   `align-items: center !important` were restating what was already true. Only
   the gap is genuinely ours. */
.tm-user-pill {
    gap: var(--tm-space-2);
}

/* 32px — one step under --tm-control-height, so the avatar sits inside the
   36px header item without forcing it taller. The background was a
   `linear-gradient(135deg, var(--tm-primary), #7c3aed)`; the violet stop was
   the last colour literal in this file and decorative gradients are retired.
   White on --tm-primary measures 5.91:1 (see tokens.css). */
.tm-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tm-space-6);
    height: var(--tm-space-6);
    border-radius: 50%;
    background: var(--tm-primary);
    color: var(--tm-surface);
    font-weight: var(--tm-weight-semibold);
    font-size: var(--tm-text-xs);
    letter-spacing: 0;
    flex-shrink: 0;
}

/* ── Content Area ─────────────────────────────────────────── */
/* The content column stops growing at --tm-content-max and centres itself.
   Past ~1320px a table row gets wide enough that the eye loses the line it is
   reading on the way back from the right edge. `width: 100%` keeps it filling
   narrower viewports; the auto inline margins do the centring.

   THIS BOX IS THE PAGE'S SCROLLPORT, and since the revert of TM-165 it is the
   only one. Another file used to turn it into a column flex container to hand a
   definite height down to the list panel; that chain is gone and every list now
   scrolls here, the same as /status always did. `flex: 1` inside .tm-main is what
   gives this element a height at all and `overflow-y: auto` is what scrolls it —
   both are load-bearing for every page, not just for lists. */
.tm-content {
    flex: 1;
    overflow-y: auto;
    width: 100%;
    max-width: var(--tm-content-max);
    margin-inline: auto;
    padding: var(--tm-space-6);
    scroll-behavior: smooth;
}

/* Custom scrollbar for content */
.tm-content::-webkit-scrollbar {
    width: 6px;
}

.tm-content::-webkit-scrollbar-track {
    background: transparent;
}

.tm-content::-webkit-scrollbar-thumb {
    background: var(--tm-border);
    border-radius: var(--tm-radius-sm);
}

.tm-content::-webkit-scrollbar-thumb:hover {
    background: var(--tm-border-strong);
}

/* MainLayout wraps @Body in a basic segment; it is a transport, not a surface. */
.tm-content > .ui.segment.basic {
    margin: 0;
    padding: 0;
}

/* ── Page Headers ─────────────────────────────────────────── */
.tm-page-header {
    margin-bottom: var(--tm-space-5);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--tm-space-3);
}

/* One page-title treatment — --tm-text-xl / semibold / --tm-tracking-title.
   §2 raised --tm-text-xl from 22px to 26px, which is the whole point of the
   change: at 22px against 14px body copy there was no typographic difference
   between a page title and the text under it, so the page opened with no
   entry point. 26px against 14px reads as a heading without needing weight
   700, which the scale does not contain — semibold is the ceiling.

   Tracking is negative because it has to be: at 26px the default letter
   spacing reads loose and the title comes apart into words. The value lives in
   --tm-tracking-title so a detail page and a list page cannot drift.

   Beats Fomantic's `h2.ui.header` (0-2-1) and `.ui.header` (0-2-0) on class
   count, so no !important. --tm-text on --tm-bg measures 17.24:1. */
.tm-page-header h1,
.tm-page-header h2,
.tm-page-header .ui.header {
    margin: 0;
    font-size: var(--tm-text-xl);
    line-height: var(--tm-leading-xl);
    font-weight: var(--tm-weight-semibold);
    letter-spacing: var(--tm-tracking-title);
    color: var(--tm-text);
}

/* Optional subtitle — e.g. the personal number under an employee's name, or the
   "53 people at Brno HQ" line under a list title. Fomantic's
   `.ui.header .sub.header` is 0-3-0; this is 0-4-0.
   --tm-text-secondary on --tm-bg measures 5.82:1, clearing AA. */
.tm-page-header .ui.header .sub.header {
    margin-top: var(--tm-space-1);
    font-size: var(--tm-text-base);
    line-height: var(--tm-leading-base);
    font-weight: var(--tm-weight-normal);
    letter-spacing: 0;
    color: var(--tm-text-secondary);
}

/* §5.5: page-title icons are removed from the design system. Every list page
   currently opens with a large black Fomantic glyph (`users`, `calendar`,
   `clipboard list`) that decorates and nothing more.
   ------------------------------------------------------------------
   PHASE 3, READ THIS: the icons are still in the markup — this is the CSS-first
   half of the change, and phase 3 deletes the `<i class="… icon">` elements (and
   the `Icon=` parameters on SemHeader*) from the razor files listed by
   `grep -rn 'tm-page-header' src/TM.Web/Components`. When that lands, delete
   the three rules below; they have no other purpose.
   ------------------------------------------------------------------
   Scope is deliberately narrow: only the page title. Icons inside buttons,
   table cells, messages and the sidebar are untouched.
   The third selector catches Profile.razor, the one page that renders its
   title with a bare `<SemHeader2 Icon="…">` instead of a `.tm-page-header`
   wrapper — MainLayout puts @Body inside a basic segment, so that title is a
   grandchild of .tm-content. Phase 3 should move Profile onto the shared
   page-header markup and this selector disappears with the others. */
.tm-page-header > i.icon,
.tm-page-header .ui.header > i.icon,
.tm-content > .ui.basic.segment > .ui.header > i.icon {
    display: none;
}

/* With the icon gone, Fomantic still reserves the gutter it used to sit in.
   The rule being fought is
   `.ui.header:not(.icon):not(.centered):not(.aligned)>i.icon+.content`
   (0-7-1), so the :not() chain has to be repeated to outrank it — that is the
   whole reason this selector looks the way it does. Deleted in phase 3
   together with the rules above. */
.tm-page-header .ui.header:not(.icon):not(.centered):not(.aligned) > i.icon + .content,
.tm-content > .ui.basic.segment > .ui.header:not(.icon):not(.centered):not(.aligned) > i.icon + .content {
    padding-left: 0;
}

/* §5.7: icons in card headers lose their colour. These are the ones inside a
   card — the red warning triangle on "Top Missing Requirements", the blue
   calendar on "Upcoming Sessions", the orange clock on "Expiring Within 30
   Days", the green Excel glyph on Exports. Each was a different hue for no
   reason a reader could act on, which is what made the headers read as clip art.
   They stay visible, unlike the page-title icons above, because inside a card an
   icon does help scanning; they just stop shouting.

   No phase-2 agent owned this: feedback.css could not reach `.ui.header` and
   navigation.css does not cover cards, so it sat between two owners until now.

   Deliberately scoped to a real (non-basic) card and to a *header*. Icons in
   buttons, table cells, messages and the sidebar keep their colour, and so do
   `.ui.icon.header` blocks, where the glyph is the whole point of the element.
   Fomantic colours these with `i.red.icon` (0-2-1), so `.ui.segment .ui.header >
   i.icon` (0-3-1) takes it without !important.
   --tm-text-muted is correct here: this is decoration next to a text label that
   already carries the meaning, not text and not a lone control. */
.ui.segment:not(.basic) > .ui.header:not(.icon) > i.icon,
.ui.card > .content > .ui.header:not(.icon) > i.icon {
    color: var(--tm-text-muted);
}

/* ── Card headers ─────────────────────────────────────────────
   The title of a card, as opposed to the title of a page.

   Across ~10 files these were written by hand and disagreed with each other: `Overview` was an
   `<h3 class="ui header">`, `Group Assignments` an `<h4 class="ui header" style="margin: 0;">`,
   `Requirements (20)` an `<h3>` again — three heading levels for one rank of heading, and
   nearly every one carrying an inline `margin: 0` or `margin-bottom: 1rem`, i.e. raw values
   where §3.4 spacing tokens exist.

   WHY A CLASS AND NOT A COMPONENT. There is no single shape to wrap. A card header is
   variously bare text, text plus a count, text plus a muted icon, and text plus a bar of one or
   two buttons. A component covering those would take a parameter per variant — which is exactly
   what §5.5.1 declined to build for the tab strip, on the grounds that it "would produce a
   component with one parameter per page, which is a page with extra steps", and it settled that
   case the same way: the markup stays in the pages and is shared through CSS. A class also
   leaves the DOM alone, which matters concretely — a dozen E2E assertions select
   `.ui.segment:has(> h3:has-text('…'))`, so a wrapper element would break them all and buy
   nothing.

   `.tm-page-header` cannot be reused here: it restyles any nested `.ui.header` to
   --tm-text-xl, which would draw a card title at page-title size.

   --tm-text-lg is the token tokens.css annotates "card headers" — it was --tm-text-md, which
   named the same 16px before §2 renumbered the scale; `md` is now the 14px body default, so
   reading it here would draw a card title at body size. --tm-text on --tm-surface measures
   18.32:1.

   Specificity: (0,3,0), which beats Fomantic's `h1.ui.header`…`h4.ui.header` (0,2,1) — class
   count is compared before element count — and `.ui.header` (0,2,0). No !important needed. */
.ui.header.tm-card-header {
    margin: 0 0 var(--tm-space-4);
    font-size: var(--tm-text-lg);
    line-height: var(--tm-leading-lg);
    font-weight: var(--tm-weight-semibold);
    color: var(--tm-text);
}

/* The action bar some card headers carry: the title on the left, one or two buttons on the
   right. It was an inline `display:flex; align-items:center; justify-content:space-between;
   margin-bottom: 1rem` repeated in half a dozen files.

   `flex-wrap` so a long title and its buttons drop onto two lines on a narrow viewport rather
   than squeezing the title into an unreadable column. */
.tm-card-header-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--tm-space-3);
    margin-bottom: var(--tm-space-4);
}

/* Inside the bar the gap below belongs to the bar, not to the heading — otherwise the two
   stack and the card's content starts a row too low. */
.tm-card-header-bar > .ui.header.tm-card-header {
    margin-bottom: 0;
}

/* ── Surfaces (Fomantic segments used as cards) ───────────── */
/* §4 does not name a home for segments; they are the card primitive and are
   global surface styling, so they live with the shell rather than in one of the
   component modules.

   --tm-card-padding is published as a custom property so a rule elsewhere (the
   table-in-card bleed below, and anything tables.css needs) can cancel exactly
   as much padding as the card applies, without either side hardcoding it.

   THE CARD RADIUS IS --tm-radius-md (8px), down from --tm-radius-lg (12px), and
   every segment radius in this file moved with it so attached and stacked
   segments still line up with a plain one. §4: 12px and up reads as a toy, and
   the panel in the approved mock-up is 8px. --tm-radius-lg survives in the token
   set for genuinely large surfaces; a card is not one.

   Fomantic's `.ui.segment` is 0-2-0 for background, border, radius and padding
   alike, and this sheet loads after semantic.min.css — equal specificity, later
   in the cascade, so none of this needs !important. `:not(.basic)` keeps
   Fomantic's own basic-segment reset (0-3-0) intact and leaves the layout
   wrapper alone. */
.ui.segment {
    --tm-card-padding: var(--tm-space-5);
    border: 1px solid var(--tm-border);
    border-radius: var(--tm-radius-md);
    box-shadow: var(--tm-shadow-sm);
    background: var(--tm-surface);
}

.ui.segment:not(.basic) {
    padding: var(--tm-card-padding);
}

/* A raised card is a card, not a control: the old `translateY(-1px)` lift plus
   shadow bloom on hover invited a click that nothing was listening for.
   Removed entirely (§3d) — with it goes the transition that only existed to
   animate it. Fomantic's own rule is `.ui.ui.raised.segment` (0-3-0), which is
   why this one repeats `.ui`. */
.ui.ui.raised.segment {
    box-shadow: var(--tm-shadow-md);
    border: 1px solid var(--tm-border);
}

.ui.stacked.segment {
    border-radius: var(--tm-radius-md);
}

/* §5.2 / §3d: a table inside a card is not a second card. The card side of that
   is the padding — cancelled here so the table can reach the card's edge when
   it is the whole content of the card. Dropping the table's own border, radius
   and shadow is tables.css's half of the rule.
   Fomantic's `.ui.table` margin is 0-2-0; this is 0-4-1. */
.ui.segment:not(.basic) > .ui.table:only-child {
    margin: calc(-1 * var(--tm-card-padding));
    width: calc(100% + 2 * var(--tm-card-padding));
    max-width: none;
}

/* Coloured segments — Fomantic paints a 2px bar across the top in its own
   2014 palette. That is dimmed to the normal card border, with the accent
   surviving only as a hairline indicator on the top edge.

   The `.ui.ui.ui.ui.ui` repetition is not decoration: Fomantic ships these as
   `.ui.ui.ui.ui.ui.red.segment:not(.inverted)` (0-7-0) specifically so nothing
   can override them. Matching that specificity is the only way to win without
   !important, so the selectors are mirrored exactly.

   Contrast of each accent against --tm-surface, as a non-text indicator
   (WCAG 1.4.11 floor 3:1):
     --tm-danger  #B02525 → 6.69:1  ✔
     --tm-success #17795E → 5.34:1  ✔
     --tm-primary #0B6E7F → 5.91:1  ✔
     --tm-warning #9A6206 → 5.09:1  ✔ — this is the one that changed. It used to
       be amber-500 (#f59e0b) at 2.16:1, and the note here recorded that amber
       could not reach 3:1 on white at any usable saturation while the palette
       was frozen. §3.3 unfroze it: the v2 warning is a dark ochre chosen so the
       state can be carried by colour like the other two instead of relying on
       the message text beside it to rescue it. */
.ui.ui.ui.ui.ui.red.segment:not(.inverted),
.ui.ui.ui.ui.ui.yellow.segment:not(.inverted),
.ui.ui.ui.ui.ui.blue.segment:not(.inverted),
.ui.ui.ui.ui.ui.green.segment:not(.inverted) {
    border: 1px solid var(--tm-border);
}

.ui.ui.ui.ui.ui.red.segment:not(.inverted) {
    border-top-color: var(--tm-danger);
}

.ui.ui.ui.ui.ui.yellow.segment:not(.inverted) {
    border-top-color: var(--tm-warning);
}

.ui.ui.ui.ui.ui.blue.segment:not(.inverted) {
    border-top-color: var(--tm-primary);
}

.ui.ui.ui.ui.ui.green.segment:not(.inverted) {
    border-top-color: var(--tm-success);
}

/* Attached segments – preserve visual continuity.
   Fomantic is 0-2-0 for `.ui.attached.segment` and 0-3-0 for
   `.ui.segment[class*="top attached"]`; these tie or beat it and come later.

   `border-color` is here and not only on `.ui.segment` above, and that omission
   is the defect this line fixes. `.ui.segment` sets `border: 1px solid
   var(--tm-border)` at 0-2-0, but Fomantic's `.ui.attached.segment` restates the
   whole shorthand at 0-3-0 — one class higher — so an attached segment quietly
   kept Fomantic's own #D4D4D5 while every other card on the screen was drawn in
   --tm-border (#E3E7EC). That is the "different frame" on /status: its tab pane
   is a bottom-attached segment, so the panel holding the table was outlined a
   step darker and a shade cooler than the panel on /groups beside it, and the
   seam was visible against the tab strip's own --tm-border bottom edge directly
   above it. Nothing about the page was special; it was the only shape on it that
   Fomantic still owned. */
.ui.attached.segment,
.ui.top.attached.segment,
.ui.bottom.attached.segment {
    box-shadow: none;
    border-radius: 0;
    border-color: var(--tm-border);
}

.ui.top.attached.segment {
    border-radius: var(--tm-radius-md) var(--tm-radius-md) 0 0;
}

/* The square top is deliberate and is half of a shape, not an unfinished card
   (TM-162). A bottom-attached segment is Fomantic's tab pane, and Fomantic
   strips its top border outright (`.ui.tabular.menu~.attached:not(.top).segment
   {border-top:none}`) because the tab strip above supplies that edge. The strip
   is flattened to an underlined row in components/navigation.css and carries no
   bottom margin, so its `border-bottom` sits flush on this box: the four sides
   are closed, the bottom two corners are rounded like any card, and the top two
   stay square because the line across them belongs to the strip. Rounding them
   here would pull the panel away from the strip and reopen the seam this fixed.
   Overrides Fomantic's `.ui.segment[class*="bottom attached"]` (0-3-0) on
   specificity plus source order. */
.ui.bottom.attached.segment {
    border-radius: 0 0 var(--tm-radius-md) var(--tm-radius-md);
}

/* Stacked segment – the layered paper edges follow the card radius. */
.ui.stacked.segment::before,
.ui.stacked.segment::after {
    border-radius: var(--tm-radius-md);
}

/* ── Dividers ─────────────────────────────────────────────── */
/* Fomantic's `.ui.divider:not(.vertical):not(.horizontal)` is 0-4-0; repeating
   the :not() chain matches it, so the colour lands without !important. */
.ui.divider:not(.vertical):not(.horizontal) {
    border-top-color: var(--tm-border);
}

/* ── Focus Styles ─────────────────────────────────────────── */
h1:focus {
    outline: none;
}

/* ── Selection & Highlights ───────────────────────────────── */
/* --tm-primary-hover on --tm-primary-light measures 7.17:1. */
::selection {
    background: var(--tm-primary-light);
    color: var(--tm-primary-hover);
}

/* ── First paint ──────────────────────────────────────────── */
/* This used to be `.tm-content > *`, which re-ran the fade on any content
   swap — including a table page change, where the whole page blinked because
   the user clicked "next". It is now pinned to the layout's own wrapper
   segment, which Blazor creates once per circuit, so the fade happens on the
   first render and never again (§3e).

   Opacity only: the old keyframe also translated 4px, which moves the content
   under Playwright's actionability check while it settles.

   --tm-transition-slow carries both the duration and the easing function, so
   the shorthand must not add a second timing function of its own.
   `prefers-reduced-motion` is handled centrally in tokens.css and collapses
   this to 0.01ms; nothing extra is needed here. */
.tm-content > .ui.segment.basic {
    animation: tm-fade-in var(--tm-transition-slow);
}

@keyframes tm-fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

/* ── Utility Classes ──────────────────────────────────────── */
/* The only !important declarations in this file, six of them. A utility class
   is 0-1-0 and the margins it exists to cancel are set by Fomantic component
   rules at 0-2-0 or higher (`.ui.segment { margin: 1rem 0 }`,
   `.ui.header { margin: … }`). A single-class utility cannot outrank those on
   specificity by construction, so !important is what makes it a utility at
   all — this is the second of the two legitimate uses named in §4. */
.tm-mt-0 { margin-top: 0 !important; }
.tm-mb-0 { margin-bottom: 0 !important; }
.tm-mt-1 { margin-top: var(--tm-space-4) !important; }
.tm-mb-1 { margin-bottom: var(--tm-space-4) !important; }
.tm-mt-2 { margin-top: var(--tm-space-6) !important; }
.tm-mb-2 { margin-bottom: var(--tm-space-6) !important; }

/* ── Responsive ───────────────────────────────────────────── */
@media only screen and (max-width: 768px) {
    :root {
        --tm-sidebar-width: 220px;
    }

    .tm-content {
        padding: var(--tm-space-5);
    }

    .tm-header {
        padding: 0 var(--tm-space-4);
    }

    /* Sidebar becomes an off-canvas drawer, toggled by .tm-nav-toggle */
    .tm-nav-toggle {
        display: inline-flex;
    }

    .tm-sidebar {
        position: fixed;
        left: calc(-1 * var(--tm-sidebar-width));
        z-index: 100;
        transition: left var(--tm-transition-slow);
        box-shadow: none;
    }

    .tm-sidebar.visible {
        left: 0;
        box-shadow: var(--tm-shadow-xl);
    }

    .tm-main {
        width: 100%;
    }
}

@media only screen and (max-width: 480px) {
    .tm-content {
        padding: var(--tm-space-4);
    }
}
