/* ============================================================
   Forms — inputs, dropdowns, form fields, validation
   docs/08-design-system-v2.md §2 / §4 / §6
   ------------------------------------------------------------
   TYPOGRAPHY, READ BEFORE EDITING. §2 renumbered the scale without
   renaming it. In this file:
     --tm-text-sm (12px) field labels, helper and error text
     --tm-text-md (14px) THE VALUE THE USER TYPES — inputs,
                         textareas, selects, dropdowns, menu items
   --tm-text-md used to mean 16px and --tm-text-base used to mean
   body; both moved. Every control in this file reads --tm-text-md,
   and a rule that still says --tm-text-base renders the typed value
   a step smaller than the label it belongs to.

   §6 also makes forms and tables one language: the focus treatment
   here is the same declaration buttons and clickable rows use, and
   the control height is the same token the row actions size to.
   ------------------------------------------------------------

   Fomantic ships from a CDN and is linked before this file (App.razor), so a
   selector of *equal* specificity written here already wins on source order.
   That is why almost nothing below needs !important: where Fomantic outranks us
   it is because it uses a longer selector, and the fix is to match that selector,
   not to escalate. Each remaining !important names the rule it is fighting.
   ============================================================ */

/* ── Field labels ─────────────────────────────────────────── */
/* §6: the label sits above its field at --tm-text-sm / medium /
   --tm-text-secondary. Fomantic's own .ui.form .field > label is
   bold at 0.92857143em, which made every label compete with the
   value underneath it. */
.ui.form .field > label {
    font-size: var(--tm-text-sm);
    line-height: var(--tm-leading-sm);
    font-weight: var(--tm-weight-medium);
    margin-bottom: var(--tm-space-2);
}

/* Fomantic paints the label colour from
   .ui.form:not(.inverted) .field > label:not(.button) — five classes, which
   outruns .ui.form .field > label above. Matching that selector keeps the colour
   on a token without escalating to !important. */
.ui.form:not(.inverted) .field > label:not(.button) {
    color: var(--tm-text-secondary);
}

/* ── Required is not marked; optional is (§6) ──────────────── */
/* Fomantic hangs a red `*` off the label of every `.required.field`.
   §6 retires it, and the reasoning is worth keeping: on these forms
   the great majority of fields are required, so the asterisk was
   noise on almost every row while carrying real signal on almost
   none — and it spent the danger colour, the loudest thing in the
   palette, on a fact that is not an error. Naming the *few* optional
   fields inverts the ratio and says the same thing in words.

   Every selector Fomantic uses is matched, or the marker survives on
   whichever shape is missed. The `.checkbox` variants exist because a
   required field whose control is a checkbox gets the asterisk on the
   control rather than on the label. Content is emptied as well as
   hidden: `display: none` alone leaves the `*` in the accessibility
   tree of some engines, and it must not be announced either — the
   field's own `required` attribute is what carries that to assistive
   technology, and it is untouched here. */
.ui.form .required.field > label::after,
.ui.form .required.fields:not(.grouped) > .field > label::after,
.ui.form .required.fields.grouped > label::after,
.ui.form .required.field > .checkbox::after,
.ui.form .required.fields:not(.grouped) > .field > .checkbox::after {
    display: none;
    content: none;
}

/* The replacement: a quiet "Optional" riding inside the label.
   Normal weight against the label's medium is the whole distinction —
   it must read as an aside, not as a second label. --tm-text-secondary
   and not --tm-text-muted, because this is text (muted is 2.55:1 and
   tokens.css bans it for exactly this).

   Markup: <label>Cost centre <span class="tm-field-optional">Optional</span></label> */
.tm-field-optional {
    margin-left: var(--tm-space-1);
    font-size: var(--tm-text-sm);
    line-height: var(--tm-leading-sm);
    font-weight: var(--tm-weight-normal);
    color: var(--tm-text-secondary);
}

/* ── Text inputs, textareas, selects, selection dropdowns ─── */
/* §6: every control agrees on one height (--tm-control-height), one border
   weight, one radius and one type size. The border was 1.5px here, which is
   exactly why inputs read heavier than the buttons standing next to them.

   WCAG 1.4.11 — THIS NOTE USED TO RECORD A FAILURE AND NO LONGER DOES, so it is
   rewritten rather than deleted: the finding it raised was acted on. The border
   is the whole of the "visual information required to identify a user interface
   component" here, because the fill (--tm-surface) sits on --tm-bg at ~1.02:1
   and says nothing on its own. --tm-border-strong was #C9D0D9 at 1.55:1, under
   the 3:1 floor; tokens.css now ships #8A93A0 at 3.11:1 and carries the full
   argument for why a focus ring could not have rescued it (focus is a state;
   1.4.11 is about identifying the control before you touch it).

   Nothing here hardcodes that value — the point of the fix was that it moves in
   the token and every control follows. */
.ui.input input,
.ui.form input[type="text"],
.ui.form input[type="email"],
.ui.form input[type="password"],
.ui.form input[type="number"],
.ui.form input[type="search"],
.ui.form input[type="tel"],
.ui.form input[type="url"],
.ui.form input[type="date"],
.ui.form input[type="time"],
.ui.form input[type="datetime-local"],
.ui.form textarea,
.ui.form select,
.ui.selection.dropdown {
    border: 1px solid var(--tm-border-strong);
    border-radius: var(--tm-radius-sm);
    background: var(--tm-surface);
    color: var(--tm-text);
    font-size: var(--tm-text-md);
    transition: border-color var(--tm-transition), box-shadow var(--tm-transition);
}

/* Placeholder text. Fomantic paints it rgba(191,191,191,.87) — 1.9:1 on white,
   an untokenised literal and unreadable besides. --tm-text-secondary is the
   quietest colour in the ramp that is still a text colour; the distinction from
   a real value is carried by the value being --tm-text at 18.32:1 above it.
   --tm-text-muted is not available here for the reason tokens.css states. */
.ui.input input::placeholder,
.ui.form input::placeholder,
.ui.form textarea::placeholder {
    color: var(--tm-text-secondary);
    opacity: 1;
}

/* Height and padding are set apart from the block above because the three kinds
   of control reach --tm-control-height differently. Everything is border-box
   (Fomantic's reset puts box-sizing: border-box on html), so the declared height
   already includes padding and border.
   - a single-line <input> gets an exact height and lets the browser centre the
     text in it, which is why no line-height is needed;
   - a <textarea> is multi-line, so only its padding is unified;
   - the selection dropdown is a <div> and has to be told the line box that
     lands it on the same 36px, hence the calc. It also needs room on the right
     for its caret; see the caret rule below. */
.ui.input input,
.ui.form input[type="text"],
.ui.form input[type="email"],
.ui.form input[type="password"],
.ui.form input[type="number"],
.ui.form input[type="search"],
.ui.form input[type="tel"],
.ui.form input[type="url"],
.ui.form input[type="date"],
.ui.form input[type="time"],
.ui.form input[type="datetime-local"],
.ui.form select {
    height: var(--tm-control-height);
    padding: 0 var(--tm-space-3);
}

.ui.form textarea {
    padding: var(--tm-space-3);
    line-height: var(--tm-leading-md);
}

/* ── Fields sharing a line (§6) ───────────────────────────── */
/* "Pole v jednom řádku sdílejí výšku i svislé zarovnání."

   The shared *height* is the control height above: every control in this
   file now resolves to --tm-control-height, which is the half of this
   that had actually been broken (the dropdowns, fixed below).

   The shared *alignment* is already right for two labelled fields —
   Fomantic's `.ui.form .fields` is a flex row on the default
   `align-items: stretch`, so both boxes are equally tall and both keep
   their label and control at the top. Bottom-aligning them instead
   would have been worse, not better: the moment one field showed a
   validation message, every other control on the line would jump up by
   the height of that message.

   The case that genuinely misaligns is a field with no label sitting
   next to one that has a label — a filter bar next to a labelled
   select, say. The unlabelled control starts a label's height too high.
   Reserving exactly the space the label rule above occupies
   (--tm-leading-sm plus its margin-bottom) puts the two controls on one
   line without any markup change and without an empty <label> that a
   screen reader would announce.

   `:has()` degrades safely: a browser without it drops the rule and the
   controls sit where they do today. */
.ui.form .fields > .field:not(:has(> label))::before {
    content: "";
    display: block;
    height: calc(var(--tm-leading-sm) + var(--tm-space-2));
}

/* THE DROPDOWN HAS TO LAND ON --tm-control-height TO THE PIXEL (§6). It sits
   next to real <input>s in every filter bar and every modal row, and a 1–2px
   disagreement between two controls on one line is the single most visible way
   a form looks unfinished.

   It is a <div>, so it cannot be given a height and left to centre its own text
   the way an input does; the line box has to be computed. Everything is
   border-box, so:  space-2 + line-height + space-2 + 2px of border = 36px,
   which is what the calc solves for. Change the padding and the line-height
   follows automatically — that is why the calc references the same token rather
   than repeating a number.

   `min-height` rather than `height`, because a multiple-selection dropdown
   legitimately grows as chips are added; the calc fixes the *empty* height,
   which is the one that has to match the input beside it. */
.ui.selection.dropdown {
    min-height: var(--tm-control-height);
    padding: var(--tm-space-2) var(--tm-space-6) var(--tm-space-2) var(--tm-space-3);
    line-height: calc(var(--tm-control-height) - 2 * var(--tm-space-2) - 2px);
}

/* The multiple-selection variant is the one that actually diverged, and it is
   worth being precise about why, because the obvious fix makes it worse.

   Its vertical padding comes from `.ui.multiple.selection.dropdown` — four
   classes, so it outruns the three-class rule above and keeps Fomantic's
   geometry. That is FINE on its own: the padding is small, so the `min-height`
   above is what decides the resting height, and an empty multiple dropdown
   lands on --tm-control-height like everything else.

   What broke it was overriding the padding here *and* giving the inner .text a
   margin: the two stacked, the content box grew past min-height, and the control
   rendered 39px against the 36px of the input beside it. So the padding is
   deliberately NOT overridden — min-height already does the work, and adding a
   second opinion is what caused the 3px.

   Only the chips are restyled, and only so that one row of them still fits
   inside those 36px instead of pushing the control taller the moment a single
   value is picked. Fomantic sizes them off its own font scale
   (`margin: .14285714rem .28571429rem .14285714rem 0`) and they came out too
   tall once the control's font-size moved to --tm-text-md. */
.ui.multiple.selection.dropdown > .ui.label {
    margin: 0 var(--tm-space-1) 0 0;
    padding: calc(var(--tm-space-1) / 2) var(--tm-space-2);
    font-size: var(--tm-text-sm);
    line-height: var(--tm-leading-sm);
}

/* The placeholder line of an EMPTY multiple dropdown, which is the state that
   sits next to an empty input and therefore the one that has to match it.
   Fomantic gives `.ui.multiple.dropdown > .text` a top margin
   (`.45238095em`) plus its own line-height; together they made the content box
   ~30px, which is taller than the 36px min-height minus padding and border, so
   the control resolved to 38px — the 2px that made every filter bar look
   slightly off. Zeroing the margin lets min-height decide, which is the whole
   design: one token, one height.

   `.default.text` and `input.search` are the same line in the search variant
   (`ui multiple search selection dropdown`, which is what SemBlazor renders for
   a multi-select), so they are reset in step or the search variant keeps the
   2px on its own. Four classes each, which is what it takes to outrank
   Fomantic's three. */
.ui.multiple.selection.dropdown > .text,
.ui.multiple.selection.dropdown > .default.text,
.ui.multiple.selection.dropdown > input.search {
    margin: 0;
    padding: 0;
    line-height: calc(var(--tm-control-height) - 2 * var(--tm-space-2) - 2px);
}

/* Fomantic anchors the caret with top: 0.78571429em plus a negative margin, a
   geometry derived from its own padding — which the rule above changes. Centring
   it explicitly makes the caret independent of whatever padding the control ends
   up with. Same selector shape as Fomantic's, so no !important is needed. */
.ui.selection.dropdown > .search.icon,
.ui.selection.dropdown > .delete.icon,
.ui.selection.dropdown > .dropdown.icon {
    top: 50%;
    right: var(--tm-space-3);
    margin: 0;
    padding: 0;
    transform: translateY(-50%);
    line-height: 1;
    color: var(--tm-text-secondary);
    opacity: 1;
}

/* Hover. Fomantic's .ui.selection.dropdown:hover paints rgba(34,36,38,.35);
   .ui.selection.dropdown alone cannot beat it, so the hover selector is
   matched directly. */
.ui.input input:hover,
.ui.form input:hover,
.ui.form textarea:hover,
.ui.selection.dropdown:hover {
    border-color: var(--tm-text-muted);
}

/* ── Focus ───────────────────────────────────────────────── */
/* THE FOCUS TREATMENT OF THE WHOLE APPLICATION (§6), written out exactly as the
   spec states it so that buttons.css, the clickable table row and everything
   else can be compared against it character for character:

       border-color: var(--tm-primary);
       box-shadow: 0 0 0 3px var(--tm-primary-light);

   §6 pins the ring to --tm-primary-light rather than to an alpha of
   --tm-primary, which is what this rule used to derive with color-mix. Both
   render a pale halo, but only one of them is the same *value* everywhere: a
   color-mix ring composites against whatever is behind it, so the identical
   declaration produced visibly different rings on a white modal and on the grey
   page ground, and "the focus looks the same everywhere" was the requirement.
   An opaque tint from the palette is the same colour on both.

   The ring is never removed. `outline: none` here is paid for by the box-shadow
   in the same declaration — that is the only reason it is allowed.

   EVERY TYPED INPUT IS SPELLED OUT, AND THAT IS THE BUG THIS RULE ONCE HAD.
   It used to say a bare `.ui.form input:focus`, which is (0,3,1). Fomantic
   writes its own focus state as a list of attribute selectors —
   `.ui.form input[type="text"]:focus`, `[type="email"]`, `[type="password"]`
   and the rest — and every one of those is (0,4,1). One step higher, so
   Fomantic won and a focused text field showed no ring and no border change at
   all: keyboard users could not see where they were (WCAG 2.4.7).

   It was invisible to a specificity reading of this file alone, because the
   losing rule is not beaten by anything *here* — the resting rule below it is
   also (0,4,1) but sets no box-shadow, so the file looks self-consistent. The
   competitor is in semantic.min.css, which is served from a CDN: it is
   cross-origin, so `sheet.cssRules` throws on it and any probe walking
   document.styleSheets silently skips it and reports "nothing else matches".
   Match Fomantic's selector shape, and the tie goes to whoever loads last,
   which is this file. No !important needed anywhere.

   .ui.selection.dropdown:focus / .active.dropdown / .active.dropdown:hover are
   all listed because Fomantic paints each of them #96c8da at up to five classes;
   a shorter selector here would simply lose to them. */
.ui.input input:focus,
.ui.input input[type="text"]:focus,
.ui.form input[type="text"]:focus,
.ui.form input[type="email"]:focus,
.ui.form input[type="password"]:focus,
.ui.form input[type="number"]:focus,
.ui.form input[type="search"]:focus,
.ui.form input[type="tel"]:focus,
.ui.form input[type="url"]:focus,
.ui.form input[type="date"]:focus,
.ui.form input[type="time"]:focus,
.ui.form input[type="datetime-local"]:focus,
.ui.form input[type="file"]:focus,
.ui.form input:not([type]):focus,
.ui.form textarea:focus,
.ui.form select:focus,
.ui.selection.dropdown:focus,
.ui.selection.active.dropdown,
.ui.selection.active.dropdown:hover,
.ui.selection.simple.dropdown:hover {
    border-color: var(--tm-primary);
    box-shadow: 0 0 0 3px var(--tm-primary-light);
    outline: none;
}

/* Checkboxes and radios take the same ring, on the box Fomantic draws with a
   ::before (the real <input> is transparent and stretched over it). Without
   this they were the one control in the system whose focus was Fomantic's
   #96c8da — a blue that belongs to no token and appears nowhere else.

   `:focus` and not `:focus-visible`: the visible control is a pseudo-element of
   a *sibling*, so the state has to be read off the input, and a checkbox
   reached by pointer still benefits from the box being marked. Fomantic's own
   selector is `.ui.checkbox input:focus ~ label:before` — matched exactly, so
   source order decides it and no !important is needed. */
.ui.checkbox input:focus ~ .box::before,
.ui.checkbox input:focus ~ label::before,
.ui.radio.checkbox input:focus ~ .box::before,
.ui.radio.checkbox input:focus ~ label::before {
    border-color: var(--tm-primary);
    box-shadow: 0 0 0 3px var(--tm-primary-light);
}

/* The resting box gets the input border too, so a checkbox is as identifiable
   as a text field (WCAG 1.4.11 — see the --tm-border-strong note above).

   `:not(.radio):not(.toggle)` because the shape is the thing being set and those
   two are not squares: a radio is a circle and a toggle is a pill track. Both
   define their own radius at three classes and would win anyway, but relying on
   that is relying on Fomantic's selector lengths not changing — naming the
   exclusion states the intent instead. */
.ui.checkbox:not(.radio):not(.toggle) input ~ .box::before,
.ui.checkbox:not(.radio):not(.toggle) input ~ label::before {
    border-color: var(--tm-border-strong);
    border-radius: var(--tm-radius-sm);
}

/* A ticked box is filled with the accent instead of Fomantic's white box with a
   near-black check, so "on" is readable at a glance down a column of them —
   the permission matrices are the case that needs it. White on --tm-primary
   measures 5.91:1 (tokens.css).

   Radios are excluded again, and here it is load-bearing rather than defensive:
   Fomantic draws a radio's dot with `background-color` on the ::after, so a
   `color` set here would not tint it, but filling its ::before would put a dark
   dot on a petrol disc. A radio keeps the white disc and the dark dot. */
.ui.checkbox:not(.radio):not(.toggle) input:checked ~ .box::before,
.ui.checkbox:not(.radio):not(.toggle) input:checked ~ label::before {
    background: var(--tm-primary);
    border-color: var(--tm-primary);
}

.ui.checkbox:not(.radio):not(.toggle) input:checked ~ .box::after,
.ui.checkbox:not(.radio):not(.toggle) input:checked ~ label::after {
    color: var(--tm-surface);
}

/* The open menu is a continuation of the control, so it takes the same edge.
   Fomantic gives it #96c8da from .ui.selection.active.dropdown .menu (4 classes). */
.ui.selection.dropdown:focus .menu,
.ui.selection.active.dropdown .menu,
.ui.selection.active.dropdown:hover .menu {
    border-color: var(--tm-border);
}

/* ── Action Input (search field + button joined) ─────────── */
/* Only the outer corners are set. Fomantic already zeroes the inner ones from
   .ui.action.input:not([class*="left action"]) > input, which outranks this rule
   and agrees with it. */
.ui.action.input > input {
    border-top-left-radius: var(--tm-radius-sm);
    border-bottom-left-radius: var(--tm-radius-sm);
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.ui.action.input > .button:last-child,
.ui.action.input > .buttons:last-child > .button {
    border-top-right-radius: var(--tm-radius-sm);
    border-bottom-right-radius: var(--tm-radius-sm);
}

/* THE BUTTON IS THE RIGHT-HAND END OF ONE CONTROL, so it takes the field's border and not
   the lighter one buttons.css gives a button standing on its own.

   That difference is deliberate there and wrong here. buttons.css argues the case at
   length: an input's fill is --tm-surface on --tm-bg (1.02:1), so its border carries the
   whole of WCAG 1.4.11 and has to be --tm-border-strong (3.11:1), while a button is
   identified by the label inside it and can afford the quieter --tm-border. A search field
   joined to a magnifier is not two components, though — it is one shape — and drawing its
   left half at 3.11:1 and its right half at 1.30:1 made the outline appear to fade out
   halfway across. Same height, same radius, two different edges: the halves read as two
   objects that happen to touch.

   Fomantic zeroes the input's own right border for exactly this join
   (`border-right-color: transparent !important` on `.ui.action.input:not([class*="left
   action"]) > input`), so this border is also the divider between the two halves, and it
   has to be the same weight as the outline it continues.

   (0,3,0) beats `.ui.button` (0,2,0), so the rest state needs no !important. The hover and
   focus states are left alone on purpose: `.ui.button:hover` already steps up to
   --tm-border-strong, so it now agrees with the resting state instead of changing it. */
.ui.action.input > .button,
.ui.action.input > .buttons > .button {
    border: 1px solid var(--tm-border-strong);
}

/* The clear button sits between the field and the magnifier, so it must not take
   the rounded end-cap that Fomantic gives the last button of an action input.
   Spelled out to six classes so it also survives whatever radius buttons.css
   gives .ui.button — .tm-search-input sits on the .ui.action.input container. */
.ui.action.input.tm-search-input > .button.tm-search-clear {
    border-radius: 0;
}

/* ── Native date inputs (interim — real fix is phase 3) ──── */
/* §5.4 bans <input type="date"> from the visual system; the replacement with
   SemDateInput is a razor change and is scheduled in phase 3 (§9). Until then the
   survivors are at least made to agree with the controls beside them.

   The filter bar has none left — TM-173 folded the two on /sessions into one Date
   chip with a range popover. What is still matched here is `datetime-local` in the
   session forms: Sessions/Create.razor, EditSessionModal.razor and
   SessionProposalModal.razor, where a start and an end carry a time as well as a
   date and SemDateInput alone does not cover it.

   Note the markup in Create.razor is <input type="datetime-local" class="ui input">
   — the class lands on the input itself, so Fomantic's .ui.input turns the element
   into an inline-flex container and none of the .ui.input input rules above can
   reach it. */
input[type="date"].ui.input,
input[type="datetime-local"].ui.input,
input[type="time"].ui.input {
    display: inline-block;
    width: 100%;
    height: var(--tm-control-height);
    padding: 0 var(--tm-space-3);
    border: 1px solid var(--tm-border-strong);
    border-radius: var(--tm-radius-sm);
    background: var(--tm-surface);
    color: var(--tm-text);
    font-family: inherit;
    font-size: var(--tm-text-md);
    line-height: var(--tm-leading-md);
    transition: border-color var(--tm-transition), box-shadow var(--tm-transition);
}

input[type="date"].ui.input:hover,
input[type="datetime-local"].ui.input:hover,
input[type="time"].ui.input:hover {
    border-color: var(--tm-text-muted);
}

input[type="date"].ui.input:focus,
input[type="datetime-local"].ui.input:focus,
input[type="time"].ui.input:focus {
    border-color: var(--tm-primary);
    box-shadow: 0 0 0 3px var(--tm-primary-light);
    outline: none;
}

/* ── Dropdown menus ───────────────────────────────────────── */
.ui.dropdown .menu {
    border-radius: var(--tm-radius-sm);
    border: 1px solid var(--tm-border);
    background: var(--tm-surface);
    box-shadow: var(--tm-shadow-lg);
    margin-top: var(--tm-space-1);
}

/* A selection dropdown's menu is attached to the control, so it keeps its top
   edge flush. Fomantic's .ui.selection.dropdown .menu (3 classes) sets the
   margin that would otherwise detach it. */
.ui.selection.dropdown .menu {
    margin-top: 0;
    box-shadow: var(--tm-shadow-lg);
}

/* THE MENU IS ALLOWED TO BE WIDER THAN THE CONTROL IT HANGS OFF.

   Fomantic pins it to the control (`width: calc(100% + 2px)` on
   `.ui.selection.dropdown .menu`) and lets the options inside wrap
   (`white-space: normal` on `.ui.dropdown .menu > .item`). Both are fine for a
   full-width field in a modal and wrong for a filter bar, which is where most
   of these live: /status sizes "All types" to a `four wide field` — about 96px
   — so the open menu broke "Zákonná školení" and "Self improvement" over two
   lines each. An option is a value, not prose. A value that wraps stops looking
   like one thing you can pick and starts looking like two.

   The three declarations are one idea. `min-width` keeps the menu at least as
   wide as the control so it never reads as detached from it; `width: auto` lets
   it grow to whatever its longest option needs; the cap stops a pathological
   value walking off the right of the page. `min-width` wins over `max-width` in
   CSS, so a control already wider than the cap keeps its own width — the cap
   only ever bounds the *growth*.

   Past the cap the item ellipses rather than wrapping (the rule below), which is
   the same trade the table cells make: one line always, and the full value on
   hover, beats a ragged block that changes height as you scroll the list. */
.ui.selection.dropdown .menu {
    width: auto;
    min-width: calc(100% + 2px);
    max-width: min(24rem, 80vw);
}

/* .ui.selection.dropdown .menu > .item (4 classes) is what actually sets the
   padding and the hairline between items, so it has to be matched here.

   --tm-text-md, matching the control: an option in the list and the value once
   chosen are the same words, and they must not change size when picked.

   `white-space: nowrap` is the half of the width rule above that lives on the
   item: the menu can only grow to its longest option if the option asks for the
   room in the first place. Fomantic sets `white-space: normal; word-wrap:
   normal` here, which is what actually broke the two-word type names over two
   lines. The overflow pair is the fallback for an option longer than the menu's
   cap — one line with an ellipsis, never a second line. */
.ui.dropdown .menu > .item,
.ui.selection.dropdown .menu > .item {
    border-radius: 0;
    border-top: none;
    padding: var(--tm-space-2) var(--tm-space-3);
    font-size: var(--tm-text-md);
    line-height: var(--tm-leading-md);
    color: var(--tm-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: background var(--tm-transition), color var(--tm-transition);
}

/* The chosen value. --tm-primary on --tm-primary-light measures 5.17:1 (see the
   palette analysis in tokens.css), so it clears WCAG AA for normal text. */
.ui.dropdown .menu > .active.item,
.ui.dropdown .menu .active.item {
    background: var(--tm-primary-light);
    color: var(--tm-primary);
    font-weight: var(--tm-weight-medium);
}

/* Hover and keyboard highlight are the same state to the reader. Fomantic marks
   the keyboard-highlighted row with .selected (added by its JS, not by :hover)
   and paints it #f7f7f7 from .ui.dropdown .menu .selected.item.
   Deliberately declared *after* .active.item: pointing at a row is a live
   response to the user and has to read over "this is the current value", and
   .selected only ties .active on specificity, so source order decides it. */
.ui.dropdown .menu > .item:hover,
.ui.dropdown .menu .selected.item {
    background: var(--tm-surface-2);
    color: var(--tm-text);
}

/* ── Validation ───────────────────────────────────────────── */
/* §6, and it is a WCAG 1.4.1 requirement rather than a preference: an error is
   NEVER signalled by colour alone. The red border below is the attention-getter;
   the message underneath (--tm-danger, further down this section) is what
   actually says what is wrong, and it is not optional decoration that a page may
   leave out. A red outline with no text is a defect, not a terse design.

   Fomantic's error styling is written as
   .ui.form .field.error input[type="text"] and friends — the [type] attribute
   pushes it to five classes. `input[type]` matches that count with one selector
   instead of fifteen, so the tokenised version wins on source order. The bare
   .field.error selectors cover fields rendered outside a .ui.form.

   The tint is derived from --tm-danger rather than written as a literal, so it
   follows the token (and the dark theme's --tm-surface) automatically. It lands
   on #faf2f2 in the v2 light theme: --tm-danger on it measures 6.07:1 and
   --tm-text on it measures 16.62:1, so the tint costs the field's own text
   nothing and the border clears the 3:1 non-text floor several times over. */
.ui.form .field.error input[type],
.ui.form .field.error input:not([type]),
.ui.form .field.error textarea,
.ui.form .field.error select,
.field.error input,
.field.error textarea,
.field.error select {
    background: color-mix(in srgb, var(--tm-danger) 6%, var(--tm-surface));
    border-color: var(--tm-danger);
    color: var(--tm-text);
}

.ui.form .field.error input[type]:focus,
.ui.form .field.error input:not([type]):focus,
.ui.form .field.error textarea:focus,
.field.error input:focus,
.field.error textarea:focus {
    border-color: var(--tm-danger);
    box-shadow: 0 0 0 3px var(--tm-danger-light);
    outline: none;
}

/* Fomantic tints the label of an errored field #9f3a38 from
   .ui.ui.form .field.error label (five classes). Doubling .ui the same way keeps
   the signal but reads it from the token: --tm-danger on --tm-surface is 6.69:1,
   clearing WCAG AA for normal text. */
.ui.ui.form .field.error > label {
    color: var(--tm-danger);
}

/* Fomantic's own !important, on .ui.form .field .prompt.label, is the reason the
   three declarations below carry one too — it cannot be beaten on specificity.
   The prompt label is Fomantic's validation balloon; the app renders Blazor's
   .validation-message instead, but both are normalised to the same quiet
   one-line treatment. */
.ui.form .field.error .ui.prompt.label {
    background: transparent !important;   /* beats .ui.form .field .prompt.label { background: #fff !important } */
    border: none !important;              /* beats .ui.form .field .prompt.label { border: 1px solid #e0b4b4 !important } */
    color: var(--tm-danger) !important;   /* beats .ui.form .field .prompt.label { color: #9f3a38 !important } */
    box-shadow: none;
    padding: var(--tm-space-1) 0 0 0;
    font-size: var(--tm-text-sm);
    line-height: var(--tm-leading-sm);
    font-weight: var(--tm-weight-medium);
}

/* Blazor's ValidationMessage — the text half of the error state, the half
   WCAG 1.4.1 makes mandatory. --tm-danger measures 6.69:1 on --tm-surface;
   this is normal-sized text, so it needs the full WCAG AA 4.5:1 bar and
   ProfileValidationContrastTests measures exactly that (TM-36). Nothing in
   Fomantic targets a plain div inside .field, so no !important is required. */
.ui.form .field .validation-message,
.validation-message {
    background: transparent;
    border: none;
    box-shadow: none;
    color: var(--tm-danger);
    font-size: var(--tm-text-sm);
    line-height: var(--tm-leading-sm);
    font-weight: var(--tm-weight-medium);
    padding: var(--tm-space-1) 0 0 0;
}

/* ── Filter bar and filter chips (TM-164, stage 1) ────────── */
/* THE ROW IS ONE FLEX LINE WITH ONE GAP, and that single fact is the fix for the
   defect the user described as a spilled-tea layout. The bars used to be
   Fomantic's sixteen-column grid: `inline fields` holding
   `three/four/five/six wide field`. A column is a percentage of the card, but the
   `SemDropdownSelection` inside it shrank to its own content, so the space a
   reader saw between two controls was *column width minus control width* — a
   different number for every control, on every page, and NOT CONSTANT IN TIME.
   On /status the "All types" control measured 102px while empty and 151px once a
   long type name was chosen, so the gap beside it collapsed by 49px as a side
   effect of picking a value.

   With `gap` the distance between neighbours is one token everywhere and cannot
   move when a value changes. FilterBarChipTests measures exactly that.

   `align-items: center` rather than `stretch`: the row deliberately holds two
   weights of control (see the height note below), and a shared centre line is
   what makes two differently sized things read as one row.

   `flex-wrap: wrap` because /sessions carries six controls. Wrapping changes the
   bar's height, and the bar is one of the `flex-shrink: 0` siblings in the list
   panel's height chain (TM-165) — a taller bar therefore gives the table less
   room rather than overflowing the card, which is the behaviour that chain was
   built for. */
.tm-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tm-space-2);
    margin-bottom: var(--tm-space-4);
}

/* The margin is the space between the bar and the table it filters. On /exports the bar is
   a card of its own with nothing under it, so that space would be read as the card's own
   padding — and the card would sit 16px deeper than every other card on the page. */
.tm-filter-bar:last-child {
    margin-bottom: 0;
}

/* The search field keeps the full control treatment — its own outline, its own
   --tm-control-height, the magnifier joined to it. It is the only control in the
   row that takes typing, and the heavier shape is reserved for that.

   It grows, so the row's slack lands *inside* the field instead of between the
   chips; the gaps stay at one token whatever the window width. The cap is a
   judgement, not a token: past ~30rem the field is wider than anything anyone
   types into it and the row starts reading as one long empty box. Beyond the cap
   the slack simply sits at the end of the row, which leaves the gaps constant
   just the same. */
.tm-filter-bar__search {
    flex: 1 1 240px;
    min-width: 0;
    max-width: 30rem;
}

/* THE MAGNIFIER USED TO HANG OUTSIDE ITS OWN FIELD, and the grid was hiding it.

   An `.ui.action.input` is an inline-flex box holding the input and the button. Fomantic
   gives the input `flex: 1 0 auto` and the button `flex: 0 0 auto` — neither may shrink —
   while its form module sets `width: 100%` on every text input inside a `.ui.form`. The
   input's flex base is therefore the *whole* box and the button is laid out past its right
   edge: the control has always overflowed itself by exactly one button.

   Nobody saw it while the field sat in a `four wide field`, because the column was wider
   than the control and the button spilled into the leftover space beside it. Take the
   leftover space away — which is the entire point of this bar — and the button lands on top
   of the chip next to it.

   Basing the input on its content instead, and letting it shrink, puts the button back
   inside: content + button now fit, and the input takes whatever the row has left.
   FilterBar_SearchField_KeepsItsMagnifierInsideTheField measures it.

   THE SELECTOR IS LONG ON PURPOSE, for the reason the focus rule above spells out. The
   competitor is `.ui.form input[type="text"]` at (0,3,1); a plain `.tm-filter-bar__search >
   input` is (0,2,1) and loses silently, which is exactly what the first attempt at this fix
   did. `.ui.form` is on the bar itself, so naming it here costs nothing and puts this rule
   one step above. */
.ui.form .tm-filter-bar__search > input[type="text"] {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
}

/* THE SEARCH FIELD MATCHES THE CHIPS EXACTLY (rozhodnutí uživatele, 2026-08-26).

   It did not, and the reasoning for that is kept below because it was sound when it
   was written: search is the one control in this bar you type into, so it carried the
   heavier weight — --tm-control-height, --tm-border-strong, --tm-text-md — while the
   chips were made lighter in all three dimensions at once.

   What that argument missed is that it was made while everything around the search
   field was equally heavy. Once TM-164 turned the four dropdowns into chips, the field
   was the only heavy object left, and the 4px step plus the joined magnifier read as
   two different design systems sitting next to each other rather than as a hierarchy.
   Reported by the reader looking at /sessions.

   So all three numbers come down to the chip's: 32px, --tm-border, --tm-text-sm. The
   field keeps the one thing that still distinguishes it — it is the only member of the
   row that grows (`flex: 1 1 240px` above).

   THE MAGNIFIER AND THE CLEAR BUTTON COME WITH IT. They are the right-hand end of the
   same shape, and the long note further up this file explains why their border has to
   equal the field's: drawn at two different weights the outline appears to fade out
   halfway across. That argument does not care WHICH weight, only that it is one — so
   they move to --tm-border together with the input.

   Specificity: the competitor for the input is `.ui.form input[type="text"]` at (0,3,1)
   and for the buttons `.ui.action.input > .button` at (0,3,0). Naming the container's
   own classes puts these at (0,6,1) and (0,5,0), so neither depends on source order —
   which the first attempt at the magnifier fix, recorded above, learned the hard way. */
.ui.form .tm-filter-bar__search.ui.action.input > input[type="text"] {
    height: 32px;
    font-size: var(--tm-text-sm);
    border-color: var(--tm-border);
}

.tm-filter-bar__search.ui.action.input > .button {
    height: 32px;
    min-height: 32px;
    font-size: var(--tm-text-sm);
    border-color: var(--tm-border);
    /* Fomantic fills a button with its own grey. Left alone it was the last thing in
       the row that did not match — one grey square at the end of an otherwise white
       field, which is the same "two objects that happen to touch" the border note
       above describes, only in fill instead of outline. The glyph keeps its own
       colour, so the control is still legible as a button. */
    background: var(--tm-surface);
}

.tm-filter-bar__search.ui.action.input > .button:hover {
    background: var(--tm-border-light);
}

/* Focus still steps the outline up, the same as it does on a chip — the quieter resting
   border is about weight in the row, not about hiding where the caret is. */
.ui.form .tm-filter-bar__search.ui.action.input > input[type="text"]:focus {
    border-color: var(--tm-primary);
}

/* THE CHIP IS 32px WHILE THE SEARCH FIELD BESIDE IT IS --tm-control-height (36px),
   and that difference is deliberate — it is the one place the "fields sharing a
   line share a height" rule above is answered with an alignment instead of a
   number.

   That rule exists to stop *accidental* disagreement: two controls that mean to be
   the same thing, differing by a pixel or two, which is the single most visible way
   a form looks unfinished. A chip does not mean to be the same thing. The defect it
   answers was that five identically weighted boxes weighed as much as the table
   under them, and the chip answers it by being lighter in every dimension at once —
   --tm-border instead of --tm-border-strong, --tm-text-sm instead of --tm-text-md,
   32px instead of 36px. Matching the height back to 36px would undo a third of that
   while buying nothing: the row's height is set by its tallest member either way.

   What must not happen is the two drifting apart optically, and `align-items:
   center` on the bar is what prevents it: both controls share a centre line, so the
   2px above and below the chip read as breathing room rather than as a
   misalignment. Change one of these three numbers and you have to change the
   others. */
.tm-filter-chip,
.tm-filter-clear {
    position: relative;
    display: inline-flex;
    align-items: center;
    height: 32px;
    border: 1px solid var(--tm-border);
    border-radius: var(--tm-radius-sm);
    background: var(--tm-surface);
    color: var(--tm-text-secondary);
    font-family: inherit;
    font-size: var(--tm-text-sm);
    line-height: var(--tm-leading-sm);
    white-space: nowrap;
    transition: border-color var(--tm-transition),
                background var(--tm-transition),
                color var(--tm-transition);
}

/* THE ACTIVE STATE IS THE HALF THAT MATTERS. A resting dropdown drew its text in
   --tm-text-secondary — the placeholder colour — so a filter that was simply not in
   use looked like a control that could not be used. The chip keeps that quiet
   resting colour, which is honest, and then makes "in use" unmistakable: the accent
   border, the accent tint and the accent text together, none of which any disabled
   control in this application ever wears.

   --tm-primary on --tm-primary-light measures 5.17:1 (the palette analysis is in
   tokens.css), so the active chip clears WCAG AA for normal text — and it is
   --tm-text-sm here, which is smaller than normal text, so the margin matters. */
.tm-filter-chip--active {
    border-color: var(--tm-primary);
    background: var(--tm-primary-light);
    color: var(--tm-primary);
    font-weight: var(--tm-weight-medium);
}

.tm-filter-chip:hover,
.tm-filter-clear:hover {
    border-color: var(--tm-border-strong);
}

.tm-filter-chip--active:hover {
    border-color: var(--tm-primary);
}

/* The pill holds two controls when it is active — the trigger and the ✕ — and the
   ring has to describe the pill, not whichever half has focus. `:has()` is already
   relied on in this file (the unlabelled-field rule above) and degrades the same
   way: a browser without it shows no ring on the wrapper, and the item/clear rules
   below keep their own `:focus-visible` treatment as the fallback.

   The values are the application-wide focus treatment written out at the top of
   this file, character for character. */
.tm-filter-chip:has(:focus-visible),
.tm-filter-clear:focus-visible {
    border-color: var(--tm-primary);
    box-shadow: 0 0 0 3px var(--tm-primary-light);
    outline: none;
}

.tm-filter-chip__trigger,
.tm-filter-chip__clear {
    display: inline-flex;
    align-items: center;
    height: 100%;
    margin: 0;
    padding: 0;
    border: none;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.tm-filter-chip__trigger {
    padding: 0 var(--tm-space-3);
    gap: var(--tm-space-1);
}

/* The ✕ takes over the pill's right-hand padding, so trigger plus ✕ come to the
   width the trigger alone would have had plus one glyph — a chip must not jump
   sideways by more than its own ✕ when a value arrives. */
.tm-filter-chip--active .tm-filter-chip__trigger {
    padding-right: var(--tm-space-1);
}

.tm-filter-chip__clear {
    padding: 0 var(--tm-space-2) 0 0;
}

/* Fomantic's `.icon` carries `margin: 0 .25rem 0 0` and a 1.18em box, sized for an
   icon standing beside a word in a button, not for a caret riding at the end of a
   chip. */
.tm-filter-chip__caret,
.tm-filter-chip__clear > .icon {
    margin: 0;
    font-size: 0.85em;
    opacity: 0.75;
}

.tm-filter-chip__clear:hover > .icon {
    opacity: 1;
}

/* THE CHIP'S OWN TWO BUTTONS STAY ABOVE ITS OWN SCRIM WHILE IT IS OPEN (TM-173).
   Without this they are dead controls: the scrim is a sibling covering the whole
   viewport at z-index 59, and the trigger and the ✕ come earlier in the chip with no
   z-index of their own, so an open chip has the scrim lying over its own head.

   It went unnoticed through stage 1 because a menu chip closes itself the moment an
   option is chosen, so its ✕ is only ever exposed with the menu already shut. The
   range popover deliberately stays open after a date is picked — setting a range is
   two acts, not one — so on that chip the ✕ is reachable only by clicking twice: once
   to have the scrim swallow it, once to hit the button. One filter, one way out, and
   the way out has to work on the first click.

   The state is read off `aria-expanded`, which the trigger already publishes, rather
   than off a class added for styling — one source of truth for "this chip is open",
   and the CSS cannot fall out of step with the C# that drives it.

   61 rather than 59: the scrim has to stay above everything else on the page while
   sitting below these two. Both are under the row-actions panel (100, overlays.css)
   and the modal layer (1000). */
.tm-filter-chip:has(.tm-filter-chip__trigger[aria-expanded="true"]) .tm-filter-chip__trigger,
.tm-filter-chip:has(.tm-filter-chip__trigger[aria-expanded="true"]) .tm-filter-chip__clear {
    position: relative;
    z-index: 61;
}

/* "Clear filters" is the quiet one: it undoes work rather than doing any, and it is
   the last thing in the row, so it must not compete with the chips that carry state.
   A transparent edge at rest, the ordinary chip edge on hover — the shape is there
   the whole time, it just does not announce itself. */
.tm-filter-clear {
    padding: 0 var(--tm-space-3);
    border-color: transparent;
    background: none;
    cursor: pointer;
}

/* ── The chip's menu ──────────────────────────────────────── */
/* Shares the box with the dropdown menus above so the two cannot drift: same edge,
   same radius, same surface, same shadow. Only what genuinely differs is restated.
   The date-range popover (TM-173) hangs off a chip in exactly the same way and takes
   the same box for the same reason — a reader must not be able to tell which kind of
   chip they opened by the shape of what came out of it.

   `position: absolute` is safe here for the reason tables.css records: the page card
   is `overflow: visible` precisely so a filter menu can hang below the bar. The
   z-index clears the sticky table header (2, tables.css) and stays under the row
   actions panel (100, overlays.css) and the modal layer (1000). */
.tm-filter-chip__menu,
.tm-filter-chip__popover {
    position: absolute;
    top: calc(100% + var(--tm-space-1));
    left: 0;
    z-index: 60;
    border: 1px solid var(--tm-border);
    border-radius: var(--tm-radius-sm);
    background: var(--tm-surface);
    box-shadow: var(--tm-shadow-lg);
}

.tm-filter-chip__menu {
    padding: var(--tm-space-1) 0;
    max-height: 20rem;
    overflow-y: auto;
}

/* THE WIDTH RULE HAD TO BE RE-DERIVED FOR THE CHIP. `.ui.selection.dropdown .menu`
   above says `width: auto; min-width: calc(100% + 2px); max-width: min(24rem,
   80vw)`, and the middle declaration was doing real work there: the anchor was a
   grid column, so "at least as wide as the control" meant "at least as wide as
   three sixteenths of the card" — roughly 96px on /status, and wider on the
   six-column bars.

   Under a chip that floor collapses, because the chip is sized by its own label:
   "Type" is about 60px. `min-width: 100%` alone would therefore let a menu of short
   options render 60px wide, which reads as a fragment torn off the chip rather than
   as a list. The absolute floor replaces what the column width used to supply. The
   growth and the cap are unchanged and still correct — the option holds one line and
   the menu grows to its longest one, ellipsing only past the cap. */
.tm-filter-chip__menu {
    width: auto;
    min-width: max(100%, 11rem);
    max-width: min(24rem, 80vw);
}

/* Matches `.ui.dropdown .menu > .item` above, including the reason for
   `white-space: nowrap`: the menu can only grow to its longest option if the option
   asks for the room. The extra declarations are the <button> reset — the item is a
   real button here rather than a div with a JS click handler, which is what lets
   the keyboard reach it without any roving-tabindex machinery. */
.tm-filter-chip__item {
    display: block;
    width: 100%;
    margin: 0;
    border: none;
    background: none;
    text-align: left;
    font-family: inherit;
    cursor: pointer;
    padding: var(--tm-space-2) var(--tm-space-3);
    font-size: var(--tm-text-md);
    line-height: var(--tm-leading-md);
    color: var(--tm-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: background var(--tm-transition), color var(--tm-transition);
}

/* The chosen value, same treatment the dropdown menus give `.active.item`. */
.tm-filter-chip__item--selected {
    background: var(--tm-primary-light);
    color: var(--tm-primary);
    font-weight: var(--tm-weight-medium);
}

/* Declared after the selected state on purpose, for the reason the dropdown menu
   rules give: pointing at a row is a live response to the user and has to read over
   "this is the current value". */
.tm-filter-chip__item:hover,
.tm-filter-chip__item:focus-visible {
    background: var(--tm-surface-2);
    color: var(--tm-text);
    outline: none;
}

/* ── The chip's date-range popover (TM-173) ──────────────── */
/* THE POPOVER DOES NOT TAKE THE MENU'S WIDTH RULE, and this is the one place the two
   deliberately part company. Read the menu rule above first: its floor
   `min-width: max(100%, 11rem)` exists because a *menu* is sized by its content, and
   a menu of short options under a 60px chip would render 60px wide and read as a
   fragment torn off the chip. The floor supplies a minimum the content cannot.

   This popover has no such problem — its content is two date fields, which have a
   width of their own, so `min-width` would never be the binding constraint. What it
   needs instead is the opposite: an explicit width, because a `.ui.calendar.input`
   has no intrinsic width at all (it sized itself from the grid column it used to sit
   in, which is why the two loose fields in the bar needed a `flex-basis` before this
   replaced them).

   14rem is what "31.12.2026" plus the clear icon asks for, plus the padding. Applying
   the menu's floor here would have been harmless but meaningless; applying its cap
   matters and is kept — `min(24rem, 80vw)` is what stops the popover running off a
   narrow screen, and 14rem is comfortably under it at every width down to 320px.

   THE FIELDS ARE STACKED, NOT SIDE BY SIDE. Two 10.5rem fields plus a gap come to
   22.5rem, which fits the 24rem cap on a desktop and collides with the 80vw half of
   it below about 470px — the popover would then have to reflow to a stack anyway,
   at exactly the widths where a filter bar has already wrapped. One column at 14rem
   is the same shape at every width, and reads as "from … to …" top to bottom. */
.tm-filter-chip__popover {
    padding: var(--tm-space-3);
    width: 14rem;
    max-width: min(24rem, 80vw);
}

/* The popover is hidden rather than removed — FilterDateRangeChip.razor says why
   (SemDateInput initialises its calendar once, on first render). `[hidden]` is only
   `display: none` by way of the UA sheet, which any `display` here would beat. */
.tm-filter-chip__popover[hidden] {
    display: none;
}

.tm-filter-range__label {
    display: block;
    margin-bottom: var(--tm-space-1);
    color: var(--tm-text-secondary);
    font-size: var(--tm-text-sm);
    line-height: var(--tm-leading-sm);
    font-weight: var(--tm-weight-medium);
}

/* The gap between the two ends of the range. The label above "To" needs more air than
   the one above "From", or the popover reads as one block of four rows rather than as
   two fields. */
.tm-filter-range__field + .tm-filter-range__label {
    margin-top: var(--tm-space-3);
}

/* `.ui.calendar.input` is an inline-flex box with no width of its own, so it has to be
   told to fill the popover; the inner `.ui.input` and the `<input>` are the two boxes
   nested inside it. Same three-selector shape the loose fields in the bar used before
   TM-173 replaced them, for the same reason. */
.tm-filter-range__field .ui.calendar.input,
.tm-filter-range__field .ui.input,
.tm-filter-range__field input {
    width: 100%;
}

/* The outside-click catcher. Transparent and viewport-sized; it sits directly under
   the menu so a click anywhere else closes it. `position: fixed` keeps it out of the
   card's scrolling and out of the height chain.

   Fomantic's own calendar popup, which the range popover raises, is painted in the
   popup z-index range and therefore *above* this — so picking a day is not mistaken
   for a click outside the chip. */
.tm-filter-chip__scrim {
    position: fixed;
    inset: 0;
    z-index: 59;
    background: transparent;
}
