/* Riwaq component kit: retained markup APIs, entirely new visual treatment. */
/* THE ACTION IS A PILL (owner concept alignment, 2026-09-06). The concept draws every
   pressed thing — the brass primary, the quiet bordered secondary, the segmented options —
   at the full 999px radius; the 12px tile radius belongs to icon tiles and inputs, not to
   actions. The 48px floors and the 1px border are unchanged, so no compensation is owed. */
.ic-btn, .ic-iconbutton, .ic-dialog__close, .ic-drawer__close, .ic-noticeband__dismiss, .ic-rowchoice__option {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--rw-label-gap);
    min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); max-inline-size: 100%;
    padding: var(--rw-label-gap) var(--rw-row-padding-inline); border: 1px solid var(--rw-control-border);
    border-radius: var(--rw-radius-pill); background: var(--rw-surface); color: var(--rw-ink);
    font: inherit; font-weight: 600; line-height: var(--rw-leading-body); text-align: center;
    text-decoration: none; white-space: normal; overflow-wrap: anywhere; cursor: pointer;
    transition: var(--rw-transition-hand);
}
/* The concept's brass pill carries its ink at 800 and no visible border — the fill is the
   boundary (border-color matches so the 1px never shifts layout). Quiet buttons keep the
   drawn line. */
.ic-btn--primary { background: var(--rw-accent-fill); color: var(--rw-accent-ink); border-color: var(--rw-accent-fill); font-weight: 800; }
/* The concept's quiet button draws its boundary with the soft hairline; that line is
   ~1.2:1 against the ground and the border is the control's only boundary, so the
   AA-passing control border stays (recorded adaptation — WCAG 1.4.11 over the literal). */
.ic-btn--secondary, .ic-btn--outline { background: var(--rw-surface); color: var(--rw-ink); }
.ic-btn--quiet, .ic-iconbutton { background: transparent; border-color: transparent; color: var(--rw-accent-text); }
.ic-btn--destructive, .ic-btn--danger { background: var(--rw-danger-bg); color: var(--rw-danger-fg); border-color: var(--rw-danger-fg); }
/* ── HOVER (Task 7F) ─────────────────────────────────────────────────────────────────────
   REPLACES the underline-only hover. Each variant tints its OWN token family and borrows no
   other's, which is what the re-aimed DestructiveWeightTests now asserts rule by rule; the
   shared rule below still adds no pair at all, which is what it always asserted. The underline
   stays on every variant: it is the cue that survives forced colours and a reader who cannot
   separate two warm browns.

   The neutral rule is written by exclusion rather than by naming --secondary and --outline,
   because the kit also ships size-only modifiers (--tiny, --sm, --block) that carry no ground
   of their own; naming the coloured variants meant a plain `.ic-btn ic-btn--tiny` got no hover
   at all. The :not() list makes the four rules mutually exclusive, so none of them has to win a
   specificity argument with another. */
.ic-btn:hover:not(:disabled):not([aria-disabled="true"]) { text-decoration: underline; text-underline-offset: var(--rw-space-1); }
.ic-btn:not(.ic-btn--primary):not(.ic-btn--quiet):not(.ic-btn--destructive):not(.ic-btn--danger):hover:not(:disabled):not([aria-disabled="true"]), .ic-dialog__close:hover:not(:disabled), .ic-drawer__close:hover:not(:disabled), .ic-noticeband__dismiss:hover:not(:disabled) { background: var(--rw-surface-hover); }
.ic-btn--primary:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--rw-accent-fill-hover); border-color: var(--rw-accent-fill-hover); }
.ic-btn--quiet:hover:not(:disabled):not([aria-disabled="true"]), .ic-iconbutton:hover:not(:disabled) { background: var(--rw-surface-hover); border-color: transparent; }
.ic-btn--destructive:hover:not(:disabled):not([aria-disabled="true"]), .ic-btn--danger:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--rw-danger-bg-hover); }

/* ── PRESS (Task 7F · D1) ────────────────────────────────────────────────────────────────
   REPLACES `border-style: double` on a control whose border is 1px. A browser cannot draw
   three lines in one pixel, so it rendered solid: every press in IlmClass — every register
   mark, every save — has given the finger under it no feedback at all since the kit shipped.

   Press is the WITHDRAWAL of the hover tone plus a 1px block-direction shift and a shallow
   inset, so it reads as pushed into the page. It introduces no colour, so no variant's measured
   pair can be broken by it and the destructive control keeps its danger pair through every
   state. The shift is block-direction, which is the same move in both writing directions. */
.ic-btn:active:not(:disabled), .ic-rowchoice__option:active:not(:disabled), .ic-iconbutton:active:not(:disabled), .ic-dialog__close:active:not(:disabled), .ic-drawer__close:active:not(:disabled), .ic-noticeband__dismiss:active:not(:disabled) { translate: 0 1px; box-shadow: var(--rw-press-inset); }
.ic-btn:not(.ic-btn--primary):not(.ic-btn--destructive):not(.ic-btn--danger):active:not(:disabled) { background: var(--rw-surface-press); }
.ic-btn--primary:active:not(:disabled) { background: var(--rw-accent-fill); border-color: var(--rw-accent-fill); }
.ic-btn--destructive:active:not(:disabled), .ic-btn--danger:active:not(:disabled) { background: var(--rw-danger-bg); }
/* A press must never take the focus ring's cream halo away with it. The halo is the half of the
   ring that separates on a dark ground, it is drawn as a box-shadow, and the press above is a
   box-shadow too — so a keyboard reader holding Space would have lost half their ring at exactly
   the moment they were acting. Both, in that order: the inset reads under the halo. */
.ic-btn:active:not(:disabled):focus-visible, .ic-rowchoice__option:active:not(:disabled):focus-visible, .ic-iconbutton:active:not(:disabled):focus-visible, .ic-dialog__close:active:not(:disabled):focus-visible, .ic-drawer__close:active:not(:disabled):focus-visible, .ic-noticeband__dismiss:active:not(:disabled):focus-visible {
    box-shadow: var(--rw-press-inset), 0 0 0 2px var(--rw-focus-light), 0 0 0 6px var(--rw-focus-light);
}

/* DISABLED — the dashes stay: they render correctly at 1px, unlike the double border, and they
   survive forced colours. What changes is that a disabled primary drops its brass. A pill that
   still looks like the button you want invites the press it is going to ignore, and on a phone
   that press costs a real second of confusion. */
.ic-btn:disabled, .ic-btn[aria-disabled="true"], .ic-rowchoice__option:disabled { cursor: not-allowed; border-style: dashed; background: var(--rw-surface); color: var(--rw-ink-muted); border-color: var(--rw-control-border); font-weight: 600; text-decoration: none; }

/* ── BUSY (Task 7F · D2) ─────────────────────────────────────────────────────────────────
   The ring was drawn and never turned: there was no @keyframes for it in any of the twelve
   sheets, so it has always been a static broken circle beside a label. Linear on purpose — a
   spinner that eases is a spinner pretending to know how far along it has left to go.

   Under reduced motion the base sheet's global override stops it dead and NOTHING replaces it,
   because nothing needs to: the state is carried by aria-busy="true", which is announced, and by
   the control refusing the press. The motion was never the meaning. The label never changes
   either — swapping "Save" for "Saving…" changes the button's width mid-press and its accessible
   name mid-action; a wait longer than about a second gets a role="status" line beside it. */
.ic-btn[aria-busy="true"] { cursor: progress; }
.ic-btn[aria-busy="true"]::before { content: ""; inline-size: var(--rw-space-3); block-size: var(--rw-space-3); border: 2px solid currentColor; border-inline-end-color: transparent; border-radius: 50%; animation: rw-spin 900ms linear infinite; }
@keyframes rw-spin { to { rotate: 360deg; } }
.ic-link { color: var(--rw-accent-text); text-decoration: underline; text-underline-offset: var(--rw-space-1); }
.ic-link:hover { text-decoration-thickness: 2px; }
/* Cards float on the courtyard ground now: the concept's one soft shadow, spelled only as
   the token (owner concept alignment, 2026-09-06 — "the current build is the concept's
   skeleton without its light"). Content that is not a card (tables, rows) still casts none. */
/* The raised plane: soft edge, rest shadow, and rows inside tint in the card's own family.
   `.ic-module.is-attention`'s brass top (0,2,0) still wins its edge. */
.ic-surface--card, .ic-surface--panel { background: var(--rw-surface); border: 1px solid var(--rw-edge); border-radius: var(--rw-radius-panel); padding: var(--rw-panel-inset); min-inline-size: 0; box-shadow: var(--rw-shadow-rest); --rw-hover-ground: var(--rw-surface-hover); }
/* The explicit minmax(0,1fr) column matters: the implicit auto column takes the widest
   child's min-content, so one select whose longest option outgrows a phone at 200% text
   stretched every sibling in the section to 510px against a 320px viewport (measured,
   quiz editor's kind select). A zero-floored track keeps children at the section's own
   width and lets each overflow rule inside them do its job. */
.ic-section { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--rw-field-stack); min-inline-size: 0; }
.ic-section + .ic-section { border-block-start: 1px solid var(--rw-line); padding-block-start: var(--rw-panel-inset); }
.ic-module__head, .ic-section__head { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--rw-label-gap) var(--rw-field-stack); }
.ic-module__label, .ic-section__title, .ic-entrance__head, .ic-dialog__title, .ic-drawer__title { margin: 0; font-family: var(--rw-font-body); font-size: var(--rw-text-lead); font-weight: 600; }
/* Page regions speak in Marcellus one step above the card label; module, dialog and drawer labels
   stay Mulish 600 as work labels. A section acting as a card's own head keeps the card rank. The
   :lang list further down (0,2,0) still wins in Arabic and Urdu. */
.ic-section__title:where(:not(:is(.ic-surface--card, .ic-surface--panel, .ic-surface--overlay, .ic-home__attention, .ic-drawer, .ic-dialog, .ic-modal) *)) {
    font-family: var(--rw-font-display); font-weight: 400; font-size: var(--rw-text-title); font-synthesis: none;
}
.ic-module__action, .ic-section__action { margin-inline-start: auto; }
/* AND THEY MAY SHRINK (Task 9, A-6). A flex item's `min-width` is `auto`, so a head's action
   slot could never be narrower than its own content: at 200% text size the calendar's
   Today / prev / next group held the slot at 348px inside a 320px viewport and the whole
   document scrolled sideways, which is WCAG 2.2 AA 1.4.10 failing on a stated bar. The
   buttons inside it already wrap; nothing above them let them. Measured at 320 and 200%:
   scrollWidth 380 -> 320.

   On the kit rather than on the calendar, because the slot is the kit's and every page that
   puts anything wider than a word in it has the same hole. */
.ic-module__action, .ic-section__action { min-inline-size: 0; }
.ic-module__lede, .ic-section__aside { color: var(--rw-ink-muted); }
.ic-module__body { min-inline-size: 0; }
/* `is-attention`, NOT `--attention`, AND THE NAME WAS WRONG FROM THE FIRST DAY OF THE REDESIGN.
   `Module.razor` has emitted `class="ic-module is-attention"` since before the branch point; this
   sheet was written against a BEM name the component never rendered, so `ModuleTone.Attention`
   drew NOTHING for the whole of the Riwaq work. Two slices rationed the tone carefully in the
   meantime — F1's "a page marking more than one module Attention has not prioritised, it has
   shouted", F2 moving it off a permanent destructive card and onto the state that earns it — and
   none of it reached a screen. Found by looking at a photograph of the roles page, where the
   single-administrator card was indistinguishable from the two beside it. The same defect class
   as the 2px invalid border Task 7F found: a rule that lost, silently, and was specified around
   for months. */
.ic-module.is-attention { border-block-start: var(--rw-space-1) solid var(--rw-accent-border); }
.rw-arch { border: 1px solid var(--rw-accent-border); border-start-start-radius: 50% var(--rw-arch-rise); border-start-end-radius: 50% var(--rw-arch-rise); padding: var(--rw-space-12) var(--rw-panel-inset) var(--rw-panel-inset); }
/* The niche is an opt-in moment of pause, never a data surface. */
.rw-niche { background: var(--rw-portico); color: var(--rw-on-green); padding: var(--rw-panel-inset); }
.ic-field, .ic-fieldset, .ic-select { display: grid; gap: var(--rw-label-gap); min-inline-size: 0; }
.ic-fieldset { padding: 0; border: 0; }
/* A field stretched by its row keeps its control at control height; the spare room goes below. */
.ic-field, .ic-select { align-content: start; }
/* `.ic-field__label` JOINS THE SAME RULE. Forty-four fields across eleven screens wrap the
   control in `<label class="ic-field">` and carry the visible text in a
   `<span class="ic-field__label">` inside it — so the label element IS the field, the
   `.ic-field > label` above cannot reach the span, and no sheet in the product carried a rule
   for it. Every FormField label read at 600 and those forty-four read at 400, in the same
   forms. One rule, one weight. */
.ic-field > label, .ic-select > label, .ic-fieldset > legend, .ic-field__label { font-weight: 600; }
.ic-field__hint, .ic-field__optional, .ic-file__meta, .ic-select__more { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-field__error, .validation-message { color: var(--rw-danger-fg); background: var(--rw-danger-bg); padding: var(--rw-label-gap); border-inline-start: var(--rw-space-1) solid currentColor; }
/* THE :where() AROUND THE EXCLUSIONS IS LOAD-BEARING (Task 7F, found by looking at a rendered
   field rather than by reading this rule). It changes nothing about WHICH elements match; it
   drops this selector's specificity from (0,4,1) to (0,1,1).

   Written without it, the three :not()s made the resting rule out-specify every state rule
   underneath it, and `.ic-field [aria-invalid="true"]` (0,2,0), `.ic-input[readonly]` (0,2,0)
   and `.ic-field :disabled` (0,1,1) all lost the cascade to it. Measured in the browser, in
   both themes: an invalid field, a read-only field, a disabled field and an ordinary field
   rendered byte-identical — the 1px control border, parchment ground, full-strength ink and a
   48px box. (The measured values are in task-7f-report.md §1. Naming them here would put a
   colour literal in this sheet, which the token guard forbids — and it reads comments too,
   which is how this one was caught.)
   Every error state on every form in the product was carried by the message alone, and the
   error BORDER — the one Task 7F's brief describes as growing a pixel — never drew at all. */
.ic-input, .ic-textarea, .ic-field input:where(:not([type="checkbox"]):not([type="radio"]):not([type="file"])), .ic-field textarea, .ic-field select {
    inline-size: 100%; min-inline-size: 0; min-block-size: var(--rw-target-min); padding: var(--rw-label-gap) var(--rw-row-padding-inline);
    border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-control); background: var(--rw-surface); color: var(--rw-ink); font: inherit;
    transition: background-color var(--rw-motion-fast) var(--rw-ease-enter), border-color var(--rw-motion-fast) var(--rw-ease-enter);
}
.ic-field textarea, textarea.ic-input { min-block-size: 8rem; resize: vertical; }
/* iOS draws its date and time fields at least as wide as the formatted value and ignores the
   width above: at 200% text a date field ran 66 px past a 375 px phone. Without the native
   appearance it takes the field's own look and width, and a tap still opens the iOS picker.
   Only iOS (the touch-callout query): desktop browsers keep their calendar buttons. */
@supports (-webkit-touch-callout: none) {
    :is(.ic-input, .ic-field input):is([type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]) { -webkit-appearance: none; appearance: none; }
}
/* A chosen value longer than the field (Danish "Afventer gennemgang" at 200% text) is clipped in
   the field, as a native one is, instead of painting past it and widening the page. */
select.ic-input, .ic-field select { overflow: hidden; }
.ic-input::placeholder, .ic-field input::placeholder { color: var(--rw-ink-muted); opacity: 1; }
/* A field warms one step under the pointer, like every other control the hand can reach — but
   not one that refuses to be typed in. (Task 7F §3.) */
.ic-input:hover:not(:disabled):not([readonly]), .ic-field input:where(:not([type="checkbox"]):not([type="radio"]):not([type="file"])):hover:not(:disabled):not([readonly]), .ic-field textarea:hover:not(:disabled):not([readonly]), .ic-field select:hover:not(:disabled) { background: var(--rw-surface-hover); }
/* ── THE INVALID FIELD (Task 7F · D4) ────────────────────────────────────────────────────
   REPLACES a bare `border: 2px solid`, which grew the kit's 1px border and gave nothing back:
   the moment a field went invalid it grew and nudged every neighbour beside it. The product had
   this discipline for its selected states — calc(… - 2px) — and a mechanical guard enforcing it,
   but the guard read only `3px double`, so a 2px solid walked past it for the life of the kit.
   The 1px comes back in padding here, and the guard's predicate now reads any border growth. */
.ic-field [aria-invalid="true"], .ic-input[aria-invalid="true"] {
    border: 2px solid var(--rw-danger-ink);
    padding: calc(var(--rw-label-gap) - 1px) calc(var(--rw-row-padding-inline) - 1px);
}
/* READ-ONLY IS NOT DISABLED. Dashes mean "you cannot", and a read-only value can still be
   focused, read aloud and copied out over a phone — it is a real value somebody may need. So it
   keeps its solid border and its full-strength ink and recedes to the recessed ground instead.
   Disabled is the one that goes muted and dashed. */
.ic-input[readonly], .ic-field input[readonly], .ic-field textarea[readonly] { background: var(--rw-ground-2); color: var(--rw-ink); cursor: default; }
/* The dashes and the muted ink now actually reach the control. `border-style: dashed` had been
   written here since the kit shipped and had never rendered on a field: the resting rule above
   set the whole `border` shorthand at a higher specificity, so a disabled field looked exactly
   like an editable one. */
.ic-field :disabled, .ic-input:disabled, .ic-textarea:disabled { cursor: not-allowed; border-style: dashed; color: var(--rw-ink-muted); background: var(--rw-surface); }
.ic-choice__options { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap) var(--rw-field-stack); }
.ic-choice__option { display: flex; align-items: center; position: relative; min-inline-size: var(--rw-target-min); }
/* No accent-color of its own: the radio inherits `--rw-control-accent` from :root like every other
   control. `--rw-green` is a fill, and at night it drew the chosen radio as a hollow ring beside a
   solid unchosen dot, so the dot said the opposite of the underline. */
.ic-choice__option input { position: absolute; inset-inline-start: var(--rw-label-gap); margin: 0; inline-size: var(--rw-space-4); block-size: var(--rw-space-4); }
.ic-choice__option label { display: flex; align-items: center; min-block-size: var(--rw-target-min); padding: var(--rw-label-gap) var(--rw-row-padding-inline); padding-inline-start: var(--rw-space-8); cursor: pointer; border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-control); }
.ic-choice__option:has(input:checked) label { background: var(--rw-surface); text-decoration: underline; text-underline-offset: var(--rw-space-1); border-color: var(--rw-accent-border); }
.ic-file { display: flex; align-items: center; flex-wrap: wrap; gap: var(--rw-label-gap); padding: var(--rw-row-padding-block); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-control); }
.ic-file__input { inline-size: 100%; min-block-size: var(--rw-target-min); color: var(--rw-ink); }
/* Every file picker's button is a quiet pill like every other secondary action; left to the
   browser it read "Choose File" in the operating system's grey. */
input[type="file"]::file-selector-button, .ic-file__input::file-selector-button { min-block-size: var(--rw-target-min); margin-inline-end: var(--rw-space-3); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-pill); padding-inline: var(--rw-space-4); background: var(--rw-surface); color: var(--rw-ink); font: inherit; font-weight: 600; cursor: pointer; }
input[type="file"]::file-selector-button:hover { background: var(--rw-surface-hover); }
input[type="file"] { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-file__selection { display: grid; gap: var(--rw-space-1); min-inline-size: 0; flex: 1 1 12rem; }
/* The chosen name is a filename — a record that breaks rather than widen the row; the
   none variant is the quiet absence. Spelled by FilePicker and by the library's own
   hand-rolled pickers (LibraryUpload, LibraryResource), which reuse this vocabulary. */
.ic-file__chosen { min-inline-size: 0; overflow-wrap: anywhere; }
.ic-file__chosen--none { color: var(--rw-ink-muted); }
.ic-file__meta { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap); }
.ic-select__list { list-style: none; margin: 0; padding: var(--rw-focus-clearance); max-block-size: 18rem; overflow-y: auto; border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-control); background: var(--rw-surface); scroll-padding-block: var(--rw-focus-clearance); }
.ic-select__option { min-block-size: var(--rw-target-min); padding: var(--rw-row-padding-block) var(--rw-row-padding-inline); cursor: pointer; }
.ic-select__option[aria-selected="true"] { background: var(--rw-green); color: var(--rw-on-green); text-decoration: underline; }
.ic-select__empty:empty { margin: 0; }
.ic-tablewrap { min-inline-size: 0; padding: var(--rw-focus-clearance); }
.ic-table { inline-size: 100%; border-collapse: collapse; text-align: start; }
.ic-table th, .ic-table td { padding: var(--rw-row-padding-block) var(--rw-row-padding-inline); border-block-end: 1px solid var(--rw-line); vertical-align: baseline; overflow-wrap: anywhere; }
/* Baseline, not top: a name is a 48px link target, so top-aligned cells sat half a line above it.
   A heading follows its column's text rather than the browser's centred default. */
.ic-table th { text-align: start; vertical-align: bottom; }
/* A button label never breaks inside a word: the cell's `anywhere` read "Suspen" / "d". */
.ic-table .ic-btn { overflow-wrap: normal; white-space: nowrap; }
/* A COLUMN HEADING IS A WORD (Task 9, A-5). The rule above gives every cell
   `overflow-wrap: anywhere`, which is right for a cell holding an address or a filename and
   wrong for the heading over it: `anywhere` is the one value that also lowers the box's
   MIN-CONTENT width, so an auto table was free to give "Enrolled" 85px and the heading read
   `Enrolle` / `d` on a 1280 desktop. `break-word` breaks a word only when it would otherwise
   overflow and leaves min-content alone, so the column is now at least as wide as its own
   name, and a heading that genuinely cannot fit still breaks rather than spilling. */
.ic-table th { font-weight: 600; background: var(--rw-ground-2); overflow-wrap: break-word; }
.ic-tabular { font-variant-numeric: tabular-nums; }
.ic-records { padding: 0; margin: 0; list-style: none; }
.ic-row, .ic-record, .ic-modulerow { display: flex; align-items: center; flex-wrap: wrap; gap: var(--rw-label-gap) var(--rw-field-stack); padding: var(--rw-row-padding-block) var(--rw-row-padding-inline); border-block-end: 1px solid var(--rw-line); transition: background-color var(--rw-motion-fast) var(--rw-ease-enter), border-color var(--rw-motion-fast) var(--rw-ease-enter); }
.ic-row__main, .ic-record__who, .ic-modulerow__what { flex: 1 1 12rem; min-inline-size: 0; display: grid; gap: var(--rw-space-1); }
.ic-row__name, .ic-record__name, .ic-modulerow__name { font-weight: 600; }
.ic-row__meta, .ic-row__by, .ic-modulerow__meta { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
/* A record's facts under its name (F3: identity, then facts) — the record row's Note slot
   carrying a subject, a count, a date. The same muted small line the module row draws, given
   its own name so a record and a module row can diverge without a shared class dragging both. */
.ic-record__facts { display: block; color: var(--rw-ink-muted); font-size: var(--rw-text-small); min-inline-size: 0; overflow-wrap: anywhere; }
/* Name and meta are spans carrying record values ("Fortgeschrittenenunterricht - حفظ"), and a
   span gets no wrap from the base sheet's p/h/a/label rule: at 200% text on a phone the meta's
   min-content measured 352px against a 320px viewport (quiz settings' Who row) and stretched
   the whole column. Records break rather than widen the page. */
.ic-row__name, .ic-row__meta, .ic-row__by, .ic-record__name, .ic-modulerow__name, .ic-modulerow__meta { min-inline-size: 0; overflow-wrap: anywhere; }
/* The record's Note slot renders bare text and spans straight into __who (an applicant's
   email beside their name, on the platform review queue) — at 200% text on a phone an
   18-character address is a 300px unbreakable run and the whole page scrolled sideways
   (measured, /admin/registrations at 320: scrollWidth 401). The block-level wrap covers
   every inline child at once, the same reasoning as the __name rule above. */
.ic-record__who { overflow-wrap: anywhere; }
.ic-row__side, .ic-modulerow__go, .ic-rowactions, .ic-collection__filters, .ic-rowchoice { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-label-gap); min-inline-size: 0; }
.ic-record__extra { flex-basis: 100%; }
.ic-row--flagged, .ic-modulerow.is-flagged, .ic-record[data-record-state="untouched"] { border-inline-start: var(--rw-space-1) solid var(--rw-accent-border); }
/* A card already insets its content; a row inside one lines up with the card's label rather than
   stepping in a second time. It hangs its hover tint 12px into the card's inset while its text
   stays on the label's line; a flagged or untouched row hangs its 4px brass bar there too, so text
   aligns in every state and a row that changes state does not move sideways. */
.ic-module__body > .ic-modulerow,
.ic-module__body .ic-records > .ic-record { margin-inline: calc(-1 * var(--rw-space-3)); padding-inline: var(--rw-space-3); }
.ic-module__body > .ic-modulerow.is-flagged,
.ic-module__body .ic-records > .ic-record[data-record-state="untouched"] { padding-inline-start: calc(var(--rw-space-3) - var(--rw-space-1)); }
/* THE LAST ROW OF A PANEL DRAWS NO RULE UNDER IT. Found by looking at four screens in this
   cohort: a one-row export module, an age bucket holding one register, an activity log above its
   Show more, and a learner's last session all ended in a line to nothing, which reads as a list
   that failed to finish. The same sentence `.ic-queue__item:last-child` already carries. */
.ic-module__body .ic-modulerow:last-child,
.ic-module__body .ic-records > .ic-record:last-child,
.ic-module__body .ic-attend__row:last-child { border-block-end: 0; }
.ic-rowchoice__option { flex: 1 1 var(--rw-target-min); }
.ic-rowchoice__option[aria-pressed="true"] { background: var(--rw-green); color: var(--rw-on-green); border-color: var(--rw-control-border); text-decoration: underline; text-underline-offset: var(--rw-space-1); }
/* ── CHOOSING (Task 7F §4) ───────────────────────────────────────────────────────────────
   A CHOSEN OPTION DOES NOT HOVER, and the exclusion is the rule rather than an oversight:
   hovering the thing you already picked and watching it change tone reads as "you can pick
   this", which is a lie. Selection is a state, not a gesture — it survives the pointer leaving,
   and it is carried by fill AND underline so it never depends on colour alone. */
.ic-rowchoice__option:hover:not([aria-pressed="true"]):not(:disabled) { background: var(--rw-surface-hover); text-decoration: underline; text-underline-offset: var(--rw-space-1); }
.ic-select__option:hover:not([aria-selected="true"]) { background: var(--rw-surface-hover); }
.ic-choice__option:not(:has(input:checked)):hover label { background: var(--rw-surface-hover); }
/* The search, the count, the records and "show more" each get their own breath (2026-09-25 audit:
   the search box sat flush on the list, or on its empty state, on every collection). */
.ic-collection { min-inline-size: 0; display: grid; gap: var(--rw-space-4); }
.ic-collection__filters, .ic-collection__summary, .ic-collection__more { }
.ic-rowactions__more { min-inline-size: 0; }
.ic-rowactions__morelist { display: grid; gap: var(--rw-label-gap); padding: var(--rw-row-padding-block); border: 1px solid var(--rw-control-border); background: var(--rw-surface); border-radius: var(--rw-radius-control); box-shadow: var(--rw-shadow); --rw-hover-ground: var(--rw-surface-hover); }
.ic-rowactions__morelist a, .ic-rowactions__morelist button { min-block-size: var(--rw-target-min); display: flex; align-items: center; padding-inline: var(--rw-row-padding-inline); }
.ic-tag, .ic-status, .ic-badge { display: inline-flex; align-items: center; gap: var(--rw-space-1); padding: var(--rw-space-1) var(--rw-label-gap); border: 1px solid transparent; border-radius: var(--rw-radius-control); font-size: var(--rw-text-small); font-weight: 600; background: var(--rw-ground-2); color: var(--rw-ink); }
/* A status word is still a word: "Warteschlange" inside the delivery queue's chip is a
   221px run at 200% text and pushed a 320 phone sideways (measured). The chip wraps its
   own text before it ever widens the row. */
.ic-status, .ic-tag, .ic-badge { min-inline-size: 0; overflow-wrap: anywhere; }
.ic-status--ok, .ic-badge--success, .ic-badge--present { background: var(--rw-success-bg); color: var(--rw-success-fg); }
.ic-status--waiting, .ic-badge--warning, .ic-badge--late { background: var(--rw-warning-bg); color: var(--rw-warning-fg); }
.ic-status--stopped, .ic-badge--danger, .ic-badge--absent { background: var(--rw-danger-bg); color: var(--rw-danger-fg); }
.ic-badge--info { background: var(--rw-info-bg); color: var(--rw-info-fg); }
/* The application/registration lifecycle words (approved / pending / rejected) and the
   generic caution chip. These four rendered as the neutral base badge until the Task 5
   sweep — the word always carried the state; the tone pairs now agree with the Status
   component's own mapping (ok / waiting / stopped), so a chip and a Status can sit on one
   screen without calling the same state two colours. */
.ic-badge--approved { background: var(--rw-success-bg); color: var(--rw-success-fg); }
.ic-badge--pending, .ic-badge--warn { background: var(--rw-warning-bg); color: var(--rw-warning-fg); }
.ic-badge--rejected { background: var(--rw-danger-bg); color: var(--rw-danger-fg); }
.ic-fig { display: grid; align-content: start; gap: var(--rw-label-gap); padding: var(--rw-panel-inset); min-inline-size: 0; border-block-start: 1px solid var(--rw-line); }
.ic-fig__link { display: grid; gap: var(--rw-label-gap); min-block-size: var(--rw-target-min); }
/* Figures in the body face, light and large: Marcellus's 1 reads as a capital I and its 0 as an O.
   Never underlined; the label under them carries the link's underline. */
.ic-fig__n { font-family: var(--rw-font-body); font-size: var(--rw-text-heading); line-height: var(--rw-leading-display); font-weight: 300; letter-spacing: -.02em; font-variant-numeric: tabular-nums lining-nums; }
.ic-fig__link { text-decoration: none; color: inherit; }
.ic-fig__link .ic-fig__label { color: var(--rw-accent-text); text-decoration: underline; text-underline-offset: .2em; }
.ic-fig__link:hover .ic-fig__label { text-decoration-thickness: 2px; }
.ic-fig__unit { font-size: var(--rw-text-body); vertical-align: baseline; }
.ic-fig__note, .ic-fig__calc { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-state, .ic-empty, .ic-outage { padding: var(--rw-panel-inset); max-inline-size: var(--rw-reading-measure); }
/* A span, so outside the base rule that lets words break: "Ingen kursusansøgninger" at 200% text
   ran past a 375 px phone. */
.ic-state__label { font-weight: 600; overflow-wrap: anywhere; }
.ic-state__text { color: var(--rw-ink-muted); }
.ic-state__action { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap); }
.ic-alert, .ic-noticeband, .ic-offline { padding: var(--rw-row-padding-block) var(--rw-row-padding-inline); border-inline-start: var(--rw-space-1) solid currentColor; background: var(--rw-info-bg); color: var(--rw-info-fg); }
/* Alert copy is often a long localized sentence rendered straight into the band's own
   element, which the base sheet's p/h/a/label wrap rule never reaches: the staff page's
   last-admin warning carries "Administratorrolle", a 337px unbreakable word at 200% text
   on a 320 phone (measured — it widened the whole content column). Records break rather
   than widen the page. */
.ic-alert, .ic-noticeband, .ic-offline { min-inline-size: 0; overflow-wrap: anywhere; }
.ic-alert--error, .ic-noticeband--outage { background: var(--rw-danger-bg); color: var(--rw-danger-fg); }
.ic-alert--warning, .ic-noticeband--readonly, .ic-offline { background: var(--rw-warning-bg); color: var(--rw-warning-fg); }
.ic-alert--success { background: var(--rw-success-bg); color: var(--rw-success-fg); }
.ic-alert a, .ic-summary a, .ic-noticeband a { color: inherit; }
.ic-alert__actions { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap); }
/* One stripe-panel shape: the stripe side stays square, the open side rounds, and it mirrors in RTL.
   Full-bleed bands (.ic-noticeband, .ic-offline) stay square. No shadow, and status pairs are untouched. */
.ic-alert, .ic-field__error, .validation-message { border-start-end-radius: var(--rw-radius-control); border-end-end-radius: var(--rw-radius-control); }
.ic-noticeband__more summary, .ic-summary a { min-block-size: var(--rw-target-min); display: flex; align-items: center; cursor: pointer; }
.ic-dialog { inline-size: min(36rem, calc(100% - 2 * var(--rw-page-gutter))); max-block-size: calc(100dvh - 2 * var(--rw-page-gutter)); padding: var(--rw-panel-inset); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-panel); background: var(--rw-surface); color: var(--rw-ink); overflow: auto; scroll-padding: var(--rw-focus-clearance); box-shadow: var(--rw-shadow-float); --rw-hover-ground: var(--rw-surface-hover); }
.ic-dialog::backdrop { background: var(--rw-portico); opacity: .8; }
.ic-dialog__head, .ic-drawer__head { display: flex; align-items: start; gap: var(--rw-label-gap); }
.ic-dialog__title, .ic-drawer__title { flex: 1; min-inline-size: 0; }
.ic-dialog__actions { display: flex; flex-wrap: wrap; justify-content: end; gap: var(--rw-label-gap); margin-block-start: var(--rw-field-stack); }
/* A non-modal drawer stays in document flow: it must not cover focus in the live page. */
.ic-drawer { min-inline-size: 0; padding: var(--rw-panel-inset); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-panel); background: var(--rw-surface); color: var(--rw-ink); margin-block: var(--rw-field-stack); box-shadow: var(--rw-shadow); --rw-hover-ground: var(--rw-surface-hover); }
[role="tablist"] { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap); border-block-end: 1px solid var(--rw-line); padding: var(--rw-focus-clearance); }
[role="tab"] { min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); padding: var(--rw-label-gap) var(--rw-row-padding-inline); background: var(--rw-surface); color: var(--rw-ink); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-control); font: inherit; transition: background-color var(--rw-motion-fast); }
/* The chosen tab does not hover either, for the same reason the chosen segment does not. And
   the indicator does not slide: a sliding underline needs one unbroken track, this tablist
   wraps, and a slider would jump lines. The selected tab is a filled pill instead. */
[role="tab"]:hover[aria-selected="false"] { background: var(--rw-surface-hover); }
[role="tab"][aria-selected="true"] { background: var(--rw-green); color: var(--rw-on-green); text-decoration: underline; text-underline-offset: var(--rw-space-1); }
/* SELECTION IS A STATE, NOT A GESTURE. At rest a choosable control transitions only its press
   translate, so choosing swaps fill and ink in the same frame (the measured 1.05:1 blink is gone)
   and the theme crossfade (0,1,1) still outranks this (0,1,0) rule. Hovering an UNCHOSEN option
   adds the hand, so the tint still eases in; hover-out snaps, which is intended. The chosen
   recipes (UXA-074: fill, underline, 700, 3px double) are untouched. */
:where(.ic-btn, .ic-chip, .ic-quizlist__filter, .ic-library__topic, .ic-library__chip)[aria-pressed],
.ic-rowchoice__option,
[role="tab"],
.ic-select__option { transition: translate var(--rw-motion-fast) var(--rw-ease-enter); }
:where(.ic-btn, .ic-chip, .ic-quizlist__filter, .ic-library__topic, .ic-library__chip)[aria-pressed="false"]:hover:not(:disabled),
.ic-rowchoice__option:not([aria-pressed="true"]):hover:not(:disabled),
[role="tab"]:not([aria-selected="true"]):hover,
.ic-select__option:not([aria-selected="true"]):hover { transition: var(--rw-transition-hand); }
@media (max-width: 40rem) {
    .ic-table[data-mobile-transform="labelled-records"], .ic-table[data-mobile-transform="labelled-records"] tbody, .ic-table[data-mobile-transform="labelled-records"] tr, .ic-table[data-mobile-transform="labelled-records"] td { display: block; inline-size: 100%; }
    .ic-table[data-mobile-transform="labelled-records"] thead { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
    .ic-table[data-mobile-transform="labelled-records"] tr { padding-block: var(--rw-row-padding-block); border-block-end: 1px solid var(--rw-control-border); }
    .ic-table[data-mobile-transform="labelled-records"] td { border: 0; padding-block: var(--rw-label-gap); }
    .ic-table[data-mobile-transform="labelled-records"] td[data-label]:not([data-label=""])::before { content: attr(data-label); display: block; font-size: var(--rw-text-small); font-weight: 600; color: var(--rw-ink-muted); margin-block-end: 2px; }
    /* Under its label a value is the reading, not a footnote: ink, not the muted meta tone. */
    .ic-table[data-mobile-transform="labelled-records"] td[data-label]:not([data-label=""]) > .ic-table__meta { color: var(--rw-ink); }
    /* A record is as wide as its sheet, not a column. The name's desktop reading floor (40vw) and
       a button label held on one line both outran it at 320 with 200% text and crossed the sheet's
       edge. The name wraps like any value, and a button gets back the kit's own wrap: the one-line
       label exists for an auto table's column widths, and a record has no columns. `anywhere`
       stays: the label is a flex item, and `break-word` leaves its min-content at the longest word,
       so "nulstillingslink" spilled out of both sides of its pill instead of breaking. So a label
       that fits must be given the room: a tiny press trades its inline padding (the kit's own
       "only the padding is"). At 320 with 200% text the sheet's border left "Rediger" 100px of its
       101.5px and it read "Redige" / "r"; 12px (24px at 200%) gives it 116px. */
    .ic-table[data-mobile-transform="labelled-records"] td > .ic-table__name { min-inline-size: 0; }
    .ic-table[data-mobile-transform="labelled-records"] td .ic-btn { white-space: normal; overflow-wrap: anywhere; }
    .ic-table[data-mobile-transform="labelled-records"] td .ic-btn--tiny { padding-inline: var(--rw-space-3); }
    .ic-rowchoice { flex-basis: 100%; }
    /* THE ACTION ROW BECOMES A COLUMN AND THE CONTROLS TAKE THE WIDTH. Two 48px pills side by
       side at 320px leave about 130px each: a target you can hit and a label you cannot read.
       The note goes last because at this width it is the least urgent thing in the row, and
       between the two controls it would separate a decision from its alternative. */
    .ic-formactions { flex-direction: column; align-items: stretch; }
    .ic-formactions > .ic-btn, .ic-formactions > button { inline-size: 100%; }
    /* A CHILD SELECTOR, SO IT OUTRANKS THE BASE RULE BELOW THIS BLOCK. As a plain class it lost on source
       order to `.ic-formactions__note { flex: 1 1 12rem }`, and in a column a 12rem basis is a height:
       every note became a 192px band under its button at 320 (found by the B1 rig). */
    .ic-formactions > .ic-formactions__note { order: 1; flex: none; }
}
/* A BARE CONTROL IN A STACKED CONTAINER IS A ROW, NOT A BAR (B1 fix round, B-7). A module body, a
   section body, a fieldset and a form stack are grids, and a grid's default justify-items is stretch:
   a <button> or an <a class="ic-btn"> placed straight in one filled the column, so the widest and
   loudest control on Course details was "End this class" at ~385px above two sentences of
   reassurance, and a quiet "Add a programme" read as a heading. F2 draws an action ROW, and
   .ic-rowactions / .ic-formactions already get it right where they are used. Above the one-handed
   width the bare control takes its own width at the line's start; at 320 the stretch is wanted — a
   full-width target under a thumb — and nothing here touches it, which is the same choice
   .ic-formactions makes with its column. Any page with a bare control directly in one of these
   containers gets the row, because the rule is the family's rather than a page's. The emergency
   reveal's gate (.ic-reveal, a grid too) joined the list when its Show / Try again was read at 1280
   filling the whole panel. */
@media (min-width: 40.0625rem) {
    .ic-module__body > .ic-btn, .ic-section > .ic-btn, .ic-section__body > .ic-btn, .ic-fieldset > .ic-btn, .ic-form-stack > .ic-btn, .ic-reveal > .ic-btn { justify-self: start; }
}
@media (forced-colors: active) { .ic-status, .ic-tag, .ic-badge { border-color: CanvasText; } .ic-rowchoice__option[aria-pressed="true"], [role="tab"][aria-selected="true"] { border: 3px double Highlight; } }
/* Native picker remains keyboard reachable; its localized label is the visible target. */
.ic-file { position: relative; }
/* THE PICKER IS THE BUTTON (2026-09-25 audit). With the native input hidden behind its label,
   the field's border drew an empty box around one pill button. */
.ic-file:has(.ic-file__pick) { border: 0; padding: 0; }
.ic-file__input { position: absolute; inline-size: 1px; block-size: 1px; min-block-size: 0; padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); }
.ic-file:has(.ic-file__input:focus-visible) .ic-file__pick { outline: 2px solid var(--rw-focus-dark); outline-offset: 2px; box-shadow: 0 0 0 2px var(--rw-focus-light), 0 0 0 6px var(--rw-focus-light); }
.ic-alert--ok { background: var(--rw-success-bg); color: var(--rw-success-fg); }
.ic-alert--warn { background: var(--rw-warning-bg); color: var(--rw-warning-fg); }
.ic-modal { inline-size: min(36rem, calc(100% - 2 * var(--rw-page-gutter))); max-block-size: calc(100dvh - 2 * var(--rw-page-gutter)); padding: var(--rw-panel-inset); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-panel); background: var(--rw-surface); color: var(--rw-ink); overflow: auto; scroll-padding: var(--rw-focus-clearance); box-shadow: var(--rw-shadow-float); --rw-hover-ground: var(--rw-surface-hover); }
.ic-modal::backdrop { background: var(--rw-portico); opacity: .8; }
.ic-modal__head { display: flex; align-items: start; gap: var(--rw-label-gap); }
.ic-modal__title { flex: 1; min-inline-size: 0; margin: 0; font-family: var(--rw-font-body); font-size: var(--rw-text-lead); font-weight: 600; }
.ic-modal__close { min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-control); background: var(--rw-surface); color: var(--rw-ink); font: inherit; cursor: pointer; }
/* `Modal` HAS RENDERED `.ic-modal__body` SINCE IT WAS WRITTEN AND NOTHING HAS EVER STYLED IT —
   markup with no rule, which is the orphaned-rule defect arriving from the other side. Its five
   parts therefore stacked with no space at all: on the homework marking pass the position, the
   hand-in, the grade row, the sentence about who sees the mark and the feedback box ran together
   into one block. The field stack is what every other form region in the product uses, and this
   is the product's ONLY `Modal` consumer, so the rule can only add rhythm where there was none. */
.ic-modal__body { display: grid; gap: var(--rw-field-stack); min-inline-size: 0; }
.ic-modal__actions { display: flex; flex-wrap: wrap; justify-content: end; gap: var(--rw-label-gap); margin-block-start: var(--rw-field-stack); }
:lang(ar) :is(.ic-module__label, .ic-reflection__title, .ic-reflection__source, .ic-getapp__title, .ic-outage__title, .ic-section__title, .ic-entrance__head, .ic-dialog__title, .ic-drawer__title, .ic-modal__title), :lang(ur) :is(.ic-module__label, .ic-reflection__title, .ic-reflection__source, .ic-getapp__title, .ic-outage__title, .ic-section__title, .ic-entrance__head, .ic-dialog__title, .ic-drawer__title, .ic-modal__title) { font-family: var(--rw-font-arabic); }
.ic-field__opt { color: var(--rw-ink-muted); font-size: var(--rw-text-small); font-weight: 400; }
.ic-state__label--sub { font-weight: 400; }

/* Named measures and nesting: parents own spacing, children never add a second gutter. */
.ic-form-stack { inline-size: 100%; max-inline-size: var(--rw-form-measure); display: grid; gap: var(--rw-field-stack); }
/* Cards grouped in one form are separated like cards anywhere else; a block form stacked them edge to edge. */
form:has(> .ic-module) { display: grid; gap: var(--rw-space-6); align-content: start; }
.ic-field, .ic-fieldset, .ic-select { inline-size: 100%; max-inline-size: var(--rw-field-measure); }
.ic-field:has(input[type="date"]), .ic-field:has(input[type="time"]), .ic-field:has(input[type="number"]) { max-inline-size: var(--rw-short-field-measure); }

/* THE FORM FAMILY'S OWN TWO BLOCKS (F2 · docs/redesign/families/F2-form.html).

   `__actions` is declared 45 separate times in this product, six of them on the seven sibling
   forms of one masjid's profile. A form family in which every page invents its own action row
   is a family in the plan and nowhere else, so the row is written once here.

   The row ends where the fields end rather than running the page's width: a commit that sits
   under nothing is a commit whose scope the reader has to infer. */
.ic-formactions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-3); inline-size: 100%; max-inline-size: var(--rw-form-measure); padding-block-start: var(--rw-space-4); border-block-start: 1px solid var(--rw-line); }
.ic-formactions__note { flex: 1 1 12rem; min-inline-size: 0; color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
/* Nested in a group that draws its own boundary, the row's own rule would be a second line a
   few pixels inside the first. */
.ic-fieldset--bounded > .ic-formactions { padding-block-start: 0; border-block-start: 0; }

/* A GROUP NAME IS NOT A FIELD LABEL, and until this rule they rendered identically: the kit gives
   `.ic-field > label` and `.ic-fieldset > legend` the same weight, and an unbordered group has no
   other cue at all — so "Your name" above "Full name" on /account/profile read as two labels for
   one box, and the grouping the split exists for was invisible. Photographed at 1280 before the
   rule and after it. The group name takes the lead size; a border, where a group earns one, is a
   second cue rather than the only one. */
.ic-form-stack > .ic-fieldset > legend { font-size: var(--rw-text-lead); }
.ic-fieldset { margin: 0; }
/* A rendered legend is not a grid item, so the group's gap never reached it and the name sat on
   its first label. */
.ic-fieldset > legend { padding-inline: 0; margin-block-end: var(--rw-label-gap); }
.ic-fieldset:has(> .ic-field) > legend { margin-block-end: var(--rw-space-4); }
/* A group of FIELDS breathes like the stack it sits in. The tight label gap this inherits is
   right for a group of radios, where the options belong to one question and crowding them is
   what makes them read as one; it is wrong between two labelled inputs. */
.ic-fieldset:has(> .ic-field) { gap: var(--rw-field-stack); }

/* A GROUP EARNS A BORDER WHEN IT CARRIES ITS OWN ACTION, or a consequence different from the
   rest of the form's. Not when it is merely long: the legend and the stack gap already group
   fields nobody was going to confuse, and a box drawn around those is furniture. What a border
   says, and a legend alone cannot, is WHICH FIELDS THIS BUTTON COMMITS — which is unanswerable
   on a page carrying two commits, and /account/security carries two. */
.ic-fieldset--bounded { max-inline-size: var(--rw-form-measure); gap: var(--rw-field-stack); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); padding: var(--rw-panel-inset); }
.ic-fieldset--bounded > legend { padding-inline: var(--rw-space-2); font-size: var(--rw-text-lead); }
/* A LEGEND DOES NOT WRAP UNLESS IT IS TOLD ITS LIMIT, and a legend is not a grid item however
   the fieldset is laid out — the rendered legend sits in the border box and takes its width from
   its own content. Photographed at 320px under the 200% zoom stress on the curriculum editor:
   "Set an expectation" ran 65px past the viewport and the page scrolled sideways, while every
   field under it wrapped correctly. Its containing block is the fieldset, so 100% is the box it
   must stay inside. */
.ic-fieldset > legend { max-inline-size: 100%; min-inline-size: 0; overflow-wrap: anywhere; }
/* A GROUP INSIDE A NAMED GROUP IS A QUESTION, NOT A SECTION. Photographed on
   /account/notifications with two masajid: the masjid's name sat on the border while the
   `ChoiceField` legend inside it read larger, so the box was named by the smaller of the two
   words on it. Nesting is what decides the rank here — the same question is the page's own when
   there is one masjid and nothing wrapping it, and it keeps the lead size there. */
.ic-fieldset--bounded .ic-form-stack > .ic-fieldset > legend { font-size: var(--rw-text-body); }
/* The destructive group is bounded in the danger ink rather than filled with it: a filled
   panel would put every control inside it on a ground none of their pairs were measured on. */
.ic-fieldset--danger { border-color: var(--rw-danger-ink); }
.ic-module, .ic-module__body, .ic-section__body { display: grid; gap: var(--rw-field-stack); }
.ic-module__lede, .ic-state__text, .ic-section__aside { max-inline-size: var(--rw-reading-measure); }
.ic-state, .ic-empty, .ic-outage { display: grid; gap: var(--rw-space-3); }
/* The state family's two loud members are the concept's stroked-arch cards now (owner
   concept alignment, 2026-09-06 — section 06, "Nothing yet, or something wrong"): a calm
   centred card, an arch drawn in outline above the words, and the ERROR arch is the dashed
   one. No red banner — "honest, never alarming" is the concept's own sentence; the danger
   pair stays with true alerts (.ic-alert--error), which keep it. The arch is drawn with
   borders rather than an embedded image so it takes the accent token in both themes. */
.ic-state--error, .ic-state--empty {
    justify-items: center; text-align: center;
    padding: var(--rw-panel-inset);
    background: var(--rw-surface); border: 1px solid var(--rw-edge);
    border-radius: var(--rw-radius-panel); box-shadow: var(--rw-shadow);
}
.ic-state--error::before, .ic-state--empty::before {
    content: ""; inline-size: 3rem; block-size: 3.375rem;
    border: 2px solid var(--rw-accent-border); border-block-end: 0;
    border-start-start-radius: 50% 78%; border-start-end-radius: 50% 78%;
}
.ic-state--error::before { border-style: dashed; border-block-end: 0; }
.ic-state--error .ic-state__label, .ic-state--empty .ic-state__label {
    font-family: var(--rw-font-display); font-weight: 400; font-size: var(--rw-text-lead);
}
.ic-state--empty { color: var(--rw-ink-muted); }
.ic-state--empty .ic-state__label { color: var(--rw-ink); }
.ic-state--empty .ic-state__action, .ic-state--error .ic-state__action { justify-content: center; }
/* Inside a card the state sheds the card clothes it would otherwise double. The Home figures
   well already gives the panel inset, so a state in it sheds the inset as a card's does. */
.ic-surface--card .ic-state, .ic-surface--panel .ic-state, .ic-home__figures .ic-state, .ic-module .ic-empty { padding: 0; }
.ic-surface--card .ic-state--empty, .ic-surface--panel .ic-state--empty,
.ic-surface--card .ic-state--error, .ic-surface--panel .ic-state--error {
    background: transparent; border: 0; box-shadow: none;
}
/* PageShell's refusals and the kit's Refusal/Unresolved say the same kind of thing as the empty and
   error cards, so they wear the same raised card. CSS only; SignInHere and every return link are untouched. */
.ic-state:is([data-state="unauthenticated"], [data-state="no-organization"], [data-state="forbidden"], [data-state="refused"], [data-state="unresolved"]) {
    justify-items: center; text-align: center;
    background: var(--rw-surface); border: 1px solid var(--rw-edge);
    border-radius: var(--rw-radius-panel); box-shadow: var(--rw-shadow-rest);
}
/* No card inside a card: every state sheds its clothes inside ANY bounded plane, not only card and
   panel (the attention callout, drawer and dialogs were doubling it). (0,3,0) outranks both rules above.
   The Home figures well is a bounded plane too: a learner whose attendance read failed, or an admin
   whose term read failed, got Unresolved's raised card sunk inside the recessed well. */
:is(.ic-surface--card, .ic-surface--panel, .ic-surface--overlay, .ic-home__attention, .ic-home__figures, .ic-drawer, .ic-dialog, .ic-modal) :is(.ic-state--empty, .ic-state--error, .ic-state[data-state]) {
    background: transparent; border: 0; box-shadow: none;
}
/* Safety nets. A container given tabindex="-1" as a jump target would otherwise lose the ring's
   cream bands to its own (0,1,0) shadow. The outline is restated so a focused plane gets the whole ring. */
:is(.ic-surface--card, .ic-surface--panel, .ic-drawer):focus-visible {
    outline: 2px solid var(--rw-focus-dark); outline-offset: 2px; box-shadow: var(--rw-focus-bands), var(--rw-shadow-rest);
}
:is(.ic-dialog, .ic-modal, .ic-surface--overlay):focus-visible {
    outline: 2px solid var(--rw-focus-dark); outline-offset: 2px; box-shadow: var(--rw-focus-bands), var(--rw-shadow-float);
}
.ic-surface--overlay { background: var(--rw-surface); color: var(--rw-ink); padding: var(--rw-panel-inset); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-panel); box-shadow: var(--rw-shadow-float); --rw-hover-ground: var(--rw-surface-hover); }
.ic-choice__option label { flex: 1; }
.ic-row__side a, .ic-section__action a, .ic-module__action a, .ic-textbtn { display: inline-flex; align-items: center; justify-content: center; min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); }
/* THE CHECKBOX ROW, AND TWO THINGS FOUND AGAINST IT ON 2026-09-09 THAT ARE RECORDED RATHER THAN
   CHANGED HERE, because both reach every checkbox and radio in the product and a kit pass has to
   photograph all of them before either moves.

   IT OUTRANKS ANY PAGE CLASS, SILENTLY. `label:has(> input[type="checkbox"])` scores 0-1-2 — the
   `:has()` argument's specificity counts — so a page writing `.ic-export__class { align-items: … }`
   is overridden no matter how late its sheet loads. That was measured, not guessed: the override
   read back as `center` from the browser while a `margin-block-start` beside it applied. Anybody
   adjusting a checkbox row from a page will lose here and will not be told.

   AND `align-items: center` ASSUMES THE LABEL IS ONE LINE. Every label that reaches this rule is
   one line today, so it looks right everywhere — except the export centre's class list, where the
   label is a masjid's class name in two or three scripts. "Hifz - Fortgeschrittenenunterricht -
   حفظ" wraps to three lines at that column width, and the box centres against all three: level
   with line two, with the first line of the name hanging above it beside nothing. Photographed at
   1280. The fix is first-line alignment plus the ~4px that optically centres a 16px control on a
   24px line, and it belongs to this rule rather than to that page — but it changes the vertical
   position of every checkbox in the product, including a 48px row holding a one-line label, which
   is why it is priced here instead of applied. */
.ic-checkbox, .ic-check, label:has(> input[type="checkbox"]), label:has(> input[type="radio"]) { min-block-size: var(--rw-target-min); display: flex; align-items: center; gap: var(--rw-space-3); }
.ic-state__text { overflow-wrap: anywhere; }
.ic-state__action > a, .ic-state__action > button, a.ic-pagehead__parent, .ic-pagehead__parent a { display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); }

.ic-invite__inner { display: grid; gap: var(--rw-field-stack); max-inline-size: var(--rw-form-measure); margin-inline: auto; }
.ic-invite__actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-3); }
.rw-display { font-synthesis: none; font-style: normal; }
:dir(rtl) .ic-icon--back { transform: scaleX(-1); }
/* Icons have intrinsic geometry; they must never consume a flex row's text measure. */
.ic-icon { inline-size: var(--rw-icon-size); block-size: var(--rw-icon-size); flex: none; stroke-width: 1.8; }
.ic-lockup { inline-size: var(--rw-lockup-width); block-size: auto; aspect-ratio: 252 / 64; flex: none; }
/* (0,1,1) beats :lang(ar), which re-faced the SVG wordmark in the Arabic stack's Times fallback. */
.ic-lockup text { font-family: var(--rw-font-display); }
.ic-rowchoice { flex: 1 1 26rem; }
.ic-rowchoice__option { flex: 1 1 6rem; }
/* Reflow contracts apply to isolated multilingual names as well as prose. */
.ic-collection, .ic-records, .ic-record, .ic-record__extra { min-inline-size: 0; max-inline-size: 100%; }
.ic-record__who { grid-template-columns: minmax(0, 1fr); }
.ic-record__name, .ic-table__name { min-inline-size: 0; overflow-wrap: anywhere; }
/* A MIXED-SCRIPT RECORD NAME BREAKS ONLY BETWEEN ITS SCRIPTS (Components/RecordName, A-1). Each
   script run is an atomic inline: it cannot be split across lines, and a run longer than the line
   wraps inside its own box in its own direction. Not nowrap — that overflows at 320. The
   decoration is inherited down the chain because a link's underline does not propagate into an
   atomic inline; the same underline drawn twice at the same place is one underline. */
.ic-name { text-decoration: inherit; }
/* AND A NAME IS ATOMIC ABOVE ITS RUNS (B2 fix round, M-2). Atomic runs stop a run being torn;
   nothing kept the runs of one name TOGETHER. At 320 the marking modal's heading drew
   `عائشة أحمد — Aisha` over `Ahmed` and the register's conflict sentence drew `You marked —
   عائشة أحمد` over `Aisha Ahmed as Absent.` — one child's name in two pieces with the separator
   leading a line, three inches under the same name drawn whole. The isolate gets the run's own
   contract one level up: an inline-block cannot be split across lines, so a name that fits moves
   to the next line WHOLE, and a name longer than the line wraps inside its own box — at the
   separator between its runs, never inside one. max-inline-size keeps that from widening the page;
   nowrap is still refused for the reason the run rule gives. */
.ic-name > bdi { display: inline-block; max-inline-size: 100%; text-decoration: inherit; }
/* AND THE BOX'S LINES TAKE THE HOLDER'S SIDE, NOT THE NAME'S. An atomic box lays its own lines out,
   and `text-align: start` inside a right-to-left box resolves to the RIGHT — so an Arabic-first name
   that is wider than its measure hung off the far margin of a left-aligned heading the moment the
   isolate stopped being inline. That is A1's flung name with a new cause, and the rig's `nameStarts`
   read it at 320 before this rule existed. The GLYPHS keep the name's own order, which is what the
   isolate's dir is for; the LINES follow whatever direction encloses the name, which is what
   `:dir()` on `.ic-name` — a span with no direction of its own — reports. */
.ic-name:dir(ltr) > bdi { text-align: left; }
.ic-name:dir(rtl) > bdi { text-align: right; }
.ic-name__run { display: inline-block; max-inline-size: 100%; text-decoration: inherit; }
.ic-row__name a { display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); }
.ic-pagehead { display: grid; gap: var(--rw-space-3); }
/* The eyebrow often carries a record name ("Fortgeschrittenenunterricht"): a span gets no
   wrap from the base sheet's p/h/a/label rule, and its min-content at 200% text on a phone
   is wider than the viewport (measured, quiz-bank at 320 - the whole page column stretched
   to 402px). It breaks rather than widen the page, like the title h1 already does. */
/* The kicker was 16px ink, as loud as the lede. Not uppercased or tracked, because it carries
   record names, Arabic ones included. Ink-muted measures 5.20:1 or better on every ground. */
.ic-pagehead__eyebrow { min-inline-size: 0; overflow-wrap: anywhere; color: var(--rw-ink-muted); font-size: var(--rw-text-small); font-weight: 600; }
.ic-pagehead__lede, .ic-pagehead__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-3); }
/* A lede is read, so it keeps the reading measure (2026-09-25 audit): a two-sentence intro ran
   the full 928px of the content column on Reports, Organisations and the platform settings. */
.ic-pagehead__lede { max-inline-size: var(--rw-reading-measure); }
/* The actions are a row of their own, not one more line of the description above them. */
.ic-pagehead__actions { margin-block-start: var(--rw-space-2); }
.ic-pagehead__more { position: relative; }
.ic-pagehead__morelist { position: absolute; inset-inline-end: 0; z-index: 10; inline-size: max-content; max-inline-size: min(24rem, calc(100vw - 2 * var(--rw-page-gutter))); display: grid; gap: var(--rw-space-2); padding: var(--rw-panel-inset); background: var(--rw-surface); color: var(--rw-ink); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-panel); box-shadow: var(--rw-shadow-float); --rw-hover-ground: var(--rw-surface-hover); }
/* A menu, not a row of buttons: its trigger is a button with no link underline, and each entry
   inside is a full-width row that tints under the hand. Drawn as pills they read as more actions
   stacked in a box. */
.ic-pagehead__more > summary { list-style: none; text-decoration: none; }
.ic-pagehead__more > summary::-webkit-details-marker { display: none; }
.ic-pagehead__morelist { padding: var(--rw-space-2); gap: 0; min-inline-size: 12rem; }
.ic-pagehead__morelist > :is(a, button, .ic-btn) { justify-content: flex-start; inline-size: 100%; min-block-size: var(--rw-target-min); padding: var(--rw-space-2) var(--rw-space-3); border: 0; border-radius: var(--rw-radius-control); background: transparent; color: var(--rw-ink); font-weight: 600; text-align: start; text-decoration: none; }
.ic-pagehead__morelist > :is(a, button, .ic-btn):hover { background: var(--rw-surface-hover); }
/* A record's name is a press wherever it is a link. `.ic-modulerow__name` joined the list when
   Reports gained the export row it had always parented and never named: measured at 100x20, which
   is a link a thumb misses. Same rule, same reason as UXA-025's. */
.ic-table__name a, .ic-modulerow__name a, .ic-detail__people a:not(.ic-btn), .ic-acad__class { display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); }
.ic-alert--info { background: var(--rw-info-bg); color: var(--rw-info-fg); }
/* Choice widths follow translated labels; remaining options wrap as whole controls. */
.ic-rowchoice__option { flex-basis: max-content; }

/* Shared strips keep one reading order, full labels and focus clearance while scrolling. */
.ic-localnav .ic-localnav__inner, .ic-attend__views, .ic-calendar__views {
    display: flex; flex-wrap: nowrap; gap: var(--rw-label-gap); overflow-x: auto;
    padding-block: var(--rw-focus-clearance); padding-inline: var(--rw-focus-clearance);
    scroll-padding-inline: var(--rw-focus-clearance); max-inline-size: 100%; min-inline-size: 0;
}
.ic-localnav .ic-localnav__inner > *, .ic-attend__views > *, .ic-calendar__views > * { flex: 0 0 auto; }
/* The shared diagram frame retains exact text/table values; chart-specific styling belongs to Phase 5. */
.ic-diagram { min-inline-size: 0; }
.ic-timeline__item { border: 1px solid transparent; background-clip: padding-box; }
@media (forced-colors: active) {
    .ic-bars__bar, .ic-recur__cell--on, .ic-timeline__item { border: 1px solid CanvasText; }
}

/* On the ground it is today's ground-2; inside a card, sheet or overlay it is surface-hover; in a
   well it is ground-2-hover. */
.ic-table tbody tr[data-record]:hover, .ic-table tbody tr[data-record]:focus-within { background: var(--rw-hover-ground, var(--rw-ground-2)); }
/* Table rows snapped ("all 0s") while list rows faded; this gives one row behaviour at (0,1,0), so
   the theme crossfade still wins. */
:where(.ic-table tbody tr)[data-record] { transition: background-color var(--rw-motion-fast) var(--rw-ease-enter); }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   THE INTERACTION SYSTEM, Task 7F — §6 rows, §7 overlays, §8 the loading family.
   Written before the screens that will consume it, on purpose: ninety-eight surfaces are still
   to be composed, and without one system decided up front each of them invents its own and they
   do not match. That is the one place this repository's "no rule without a route" discipline is
   deliberately suspended, and the task report names every rule that has no consumer yet.
   ══════════════════════════════════════════════════════════════════════════════════════════ */

/* §6 · A list row answers a pointer and a keyboard exactly as a table row already does. The
   table got that rule under UXA-025, because forty rules on .ic-table and not one of them fired
   on a pointer; the list rows beside it never got the same treatment, so a reader moving down a
   column of names in a record list still had nothing telling them which row they were on.
   :has(a) rather than a modifier class, so it fires on the rows that are actually routes and
   stays silent on the ones that are not — a rule no route can render is the density trap this
   repository has already paid for once. */
.ic-row:has(a):hover, .ic-record:has(a):hover, .ic-modulerow:has(a):hover,
.ic-row:focus-within, .ic-record:focus-within, .ic-modulerow:focus-within { background: var(--rw-hover-ground, var(--rw-ground-2)); }

/* THE SHEET: one raised plane per register of records. Staged: the register and the public
   directory proved it first, and the generic collections followed once those and the students
   list had been photographed. Tables keep their existing 6px focus-clearance padding, so the
   only change to .ic-tablewrap's container size is its 1px border (the labelled-records switch
   moves 2px). List sheets are padded so a square hover tint never reaches the 16px curve. Focus
   rings are never cut (see the table sheet's scroll below). Never inside a card; the header band
   stays ground-2, which makes three steps: ground, sheet, recessed head. */
.ic-collection > [data-part="records"] > :is(.ic-tablewrap, .ic-records):where(:not(:is(.ic-surface--card, .ic-surface--panel, .ic-surface--overlay, .ic-fieldset--bounded, .ic-drawer, .ic-dialog, .ic-modal, .ic-home__attention) *)),
.ic-directory .ic-tablewrap,
.ic-register > .ic-records {
    background: var(--rw-surface); border: 1px solid var(--rw-edge); border-radius: var(--rw-radius-panel);
    box-shadow: var(--rw-shadow-rest); --rw-hover-ground: var(--rw-surface-hover);
}
.ic-collection > [data-part="records"] > .ic-records:where(:not(:is(.ic-surface--card, .ic-surface--panel, .ic-surface--overlay, .ic-fieldset--bounded, .ic-drawer, .ic-dialog, .ic-modal, .ic-home__attention) *)),
.ic-register > .ic-records { padding: var(--rw-space-2); }
/* A TABLE WIDER THAN ITS SHEET SCROLLS INSIDE IT. Between the labelled-records switch (a 40rem
   container) and the width their columns need, students (46.7rem of min-content) and staff
   (44.2rem) ran their header band, rules and buttons past the sheet's curve and the page scrolled
   sideways: 48px at 768, 83px at 730. The overflow predates the sheet; the sheet made it a visible
   boundary crossed. A table that fits is unchanged. The clip edge is the padding box and the 6px
   focus clearance is the ring's full reach, so a focused control keeps its whole ring, and
   scroll-padding keeps it whole when the control is scrolled into view. Relative, so the
   visually hidden names inside the row buttons (absolute, at their static place) are clipped
   with the table instead of pushing the page 5px wide at 730. */
.ic-collection > [data-part="records"] > .ic-tablewrap:where(:not(:is(.ic-surface--card, .ic-surface--panel, .ic-surface--overlay, .ic-fieldset--bounded, .ic-drawer, .ic-dialog, .ic-modal, .ic-home__attention) *)),
.ic-directory .ic-tablewrap { position: relative; overflow-x: auto; scroll-padding-inline: var(--rw-focus-clearance); }
@media (max-width: 40rem) {
    /* Rows keep their width at 320; 4px still keeps a square tint inside the curve. */
    .ic-collection > [data-part="records"] > .ic-records:where(:not(:is(.ic-surface--card, .ic-surface--panel, .ic-surface--overlay, .ic-fieldset--bounded, .ic-drawer, .ic-dialog, .ic-modal, .ic-home__attention) *)),
    .ic-register > .ic-records { padding-inline: var(--rw-space-1); }
    /* The sheet already insets the roster, so a roster row gives back half of its own inset. At 320
       with 200% text the sheet's border and padding and the reserved bar left the 2x2 mark grid
       75px tracks under 96px presses: each press overlapped its neighbour by 5px and hid the
       inline-end side of the focused one's ring. With 8px (16px at 200%) the tracks are 91px and
       the presses stand 11px apart, wider than the ring's 6px. */
    .ic-register > .ic-records > .ic-record { padding-inline: var(--rw-space-2); }
}
/* The last row inside a sheet draws no rule to nothing. At (0,3,2) and above this also beats the
   phone labelled-records `tr` border (0,2,1) in both the @media and the @container copy. */
.ic-collection > [data-part="records"] > .ic-tablewrap tbody tr:last-child,
.ic-collection > [data-part="records"] > .ic-tablewrap tbody tr:last-child > td,
.ic-directory .ic-tablewrap tbody tr:last-child,
.ic-directory .ic-tablewrap tbody tr:last-child > td,
.ic-collection > [data-part="records"] > .ic-records > .ic-record:last-child,
.ic-register > .ic-records > .ic-record:last-child { border-block-end: 0; }

/* A NEW ROW ANNOUNCES ITSELF FOR 600ms and then lets go: long enough to find in a thirty-row
   roster, short enough not to be read as a state. A TINT, NOT A SLIDE — a slide pushes every row
   under it and the reader loses the line they were on.

   A REMOVED ROW HAS NO ANIMATION AT ALL. A row fading out is still in the document and still
   clickable while it fades, and half a second is long enough for a real thumb to reach it. It
   goes, and a role="status" line says what went — which is also the only version a screen reader
   ever got. That refusal is a decision, not an omission. */
.ic-row--new, .ic-record--new, .ic-modulerow--new { animation: rw-rowin 600ms var(--rw-ease-enter) both; }
@keyframes rw-rowin { from { background-color: var(--rw-warning-bg); } to { background-color: transparent; } }

/* §7 · A modal takes the screen, so a short rise says it arrived rather than that it was always
   there. Leaving is not animated at all: the decision is already made, and waiting for a
   dismissal to finish is the most irritating 60ms in an interface. Under reduced motion the base
   sheet's override collapses the rise and the dialog simply appears, which is the right answer —
   the meaning is carried by the backdrop and by focus moving into it, never by the movement. */
.ic-dialog[open], .ic-modal[open] { animation: rw-dialogin var(--rw-motion-enter) var(--rw-ease-enter) both; }
@keyframes rw-dialogin { from { opacity: 0; translate: 0 8px; } to { opacity: 1; translate: 0 0; } }

/* §8 · The skeleton, as the family's shared recipe. Only where the shape is genuinely known in
   advance — a list of rows, a table. NEVER A FORM: a skeleton form promises fields that may not
   come back, and the reader starts planning answers to questions the server has not asked. */
.rw-skel { display: grid; gap: var(--rw-space-3); }
.rw-skel__line { block-size: var(--rw-space-3); border-radius: var(--rw-radius-control); background: var(--rw-ground-2); animation: rw-skeleton 1200ms ease-in-out infinite alternate; }
@keyframes rw-skeleton { from { opacity: 1; } to { opacity: .45; } }

/* §8 · The route state. No card, no arch, no shadow — the page ground with a caption on it.
   AND IT DOES NOT APPEAR FOR THE FIRST 300ms: most navigations resolve faster than that, and a
   loading state that flashes on every click is worse than none. The hold is an animation DELAY
   rather than a duration, deliberately: the reduced-motion override collapses durations and
   leaves delays alone, so a reader who has asked for less motion still gets the same 300ms of
   quiet rather than a flash on every click. */
@keyframes rw-routeload-in { from { visibility: hidden; } to { visibility: visible; } }

/* A RUNG THAT WAITS BEFORE IT SAYS IT IS WAITING. The owner watched a page load, show the shell,
   blank back to a loading rung, and only then arrive — because a signed-in route crosses two
   gates in sequence and each drew a full rung, in the same two sentences. Both gates are correct
   and neither is being merged: the chrome must not wait on the network, and @Body must not render
   before the selected organization is known.

   So the rung holds for 300ms before it is visible, and a resolve faster than that draws nothing
   at all. A DELAY rather than a duration, deliberately: the reduced-motion override collapses
   durations and leaves delays alone, so a reader who asked for less motion keeps the hold rather
   than losing it and getting the flash back. Same mechanism and same keyframes as the router's
   own state, which has held this way since Task 7F.

   NOT applied to the pre-auth rung: nothing is drawn yet there, so a held rung is a blank page. */
.ic-state--held { animation: rw-routeload-in 1ms linear 300ms both; }

/* Where the global reduced-motion override is not the right answer on its own, because the
   motion was carrying the meaning rather than decorating it. */
@media (prefers-reduced-motion: reduce) {
    /* The tint IS the "this row is new" — collapsed to nothing, the row arrives silently in the
       middle of a roster. The brass flag bar carries it instead: the same bar .ic-row--flagged
       already uses to mean "look here", so no new vocabulary is invented for it. */
    .ic-row--new, .ic-record--new, .ic-modulerow--new { animation: none; border-inline-start: var(--rw-space-1) solid var(--rw-accent-border); }
    /* An alternating pulse stopped at iteration 1 holds its `to` frame, so the skeleton would
       freeze at 45% and read as disabled content rather than as content still coming. It holds
       at full opacity instead, and the shape alone says "still coming". */
    .rw-skel__line { animation: none; opacity: 1; }
}

/* ── The appearance cycle ────────────────────────────────────────────────────────────────
   One button that names its state and advances on press. Family board F13
   (claude.ai/artifact/XJkvgQkasVHoYLEBETkw6c); component `AppearanceCycle.razor`, which carries
   the reasoning for three states rather than two.

   QUIET UNTIL TOUCHED. This sits in a floor and a footer, beside a language trigger of the same
   weight; a bordered pill there is a button shouting in a room where nothing else is. The border
   is drawn transparent rather than absent so that gaining it on hover shifts no layout.

   The three marks share ONE grid cell and only the state's own is lit. That is what makes a press
   a crossfade rather than a relayout, and it is why the button's width never moves when the state
   does — the widest mark is always present, just invisible. */
.ic-appearance {
    display: inline-flex; align-items: center; gap: var(--rw-label-gap);
    min-block-size: var(--rw-target-min); padding: var(--rw-label-gap) var(--rw-row-padding-inline);
    border: 1px solid transparent; border-radius: var(--rw-radius-pill);
    background: transparent; color: var(--rw-ink); font: inherit; font-weight: 600;
    white-space: nowrap; cursor: pointer;
    transition: var(--rw-transition-hand);
}
.ic-appearance:hover { background: var(--rw-surface-hover); border-color: var(--rw-control-border); }
.ic-appearance:active { translate: 0 1px; box-shadow: var(--rw-press-inset); }
/* The press set box-shadow at (0,2,0) with no composite, so a keyboard press lost the cream bands. */
.ic-appearance:active:focus-visible { box-shadow: var(--rw-press-inset), var(--rw-focus-bands); }
.ic-appearance__mark { display: grid; inline-size: 1.25rem; block-size: 1.25rem; flex: none; }
.ic-appearance__mark > svg {
    grid-area: 1 / 1; inline-size: 100%; block-size: 100%;
    opacity: 0; rotate: -90deg; scale: .6;
    transition: opacity var(--rw-motion-fast) var(--rw-ease-enter),
                rotate var(--rw-motion-fast) var(--rw-ease-enter),
                scale var(--rw-motion-fast) var(--rw-ease-enter);
}
.ic-appearance[data-state="light"] [data-mark="light"],
.ic-appearance[data-state="dark"] [data-mark="dark"],
.ic-appearance[data-state="system"] [data-mark="system"] { opacity: 1; rotate: 0deg; scale: 1; }
.ic-appearance__text { font-size: var(--rw-text-small); }
/* The state still changes; it just stops travelling to get there. The mark swaps instantly and
   nothing turns — which is the whole of what a reader who asked for less motion is owed here. */
@media (prefers-reduced-motion: reduce) {
    .ic-appearance__mark > svg { transition: none; rotate: none; scale: 1; }
}

/* ── F14 · The daily verse and the reviewed hadith ──────────────────────────────────────
   Twelve of this module's thirteen hooks named no rule in any sheet, so the ayah rendered at
   interface size in the interface font — against the component's own instruction that the
   Arabic is the primary text and the translation supports it.

   THE ARCH IS SPENT HERE. BUILD-PLAN names the daily verse as one of the three moments of
   pause the mihrab arch is allowed on; the hadith card gets none, which is how the hierarchy
   between scripture and narration is drawn rather than asserted. Punctuation, never wallpaper:
   the figure is clipped by the card and sits behind nothing readable.

   `.ic-reflection__audio` is NOT set here — it belongs to the grouped native-player rule in
   riwaq-operations.css, which floors every player at --rw-target-min. */
/* Side by side the two cards share a height and their links rest on one line at the foot, however
   long the hadith runs; the Arabic itself is never folded. */
.ic-reflections { display: grid; gap: var(--rw-space-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); align-items: stretch; }
.ic-reflections > .ic-reflection { display: flex; flex-direction: column; }
.ic-reflection > .ic-reflection__foot { margin-block-start: auto; }

.ic-reflection {
    position: relative;
    min-inline-size: 0;
    background: var(--rw-surface);
    border: 1px solid var(--rw-line);
    border-radius: var(--rw-radius-panel);
    padding: var(--rw-space-4) var(--rw-space-6);
    display: grid;
    gap: var(--rw-space-3);
    overflow: hidden;
    /* The daily verse stays calm and gets no shadow, but its text buttons tint within parchment. */
    --rw-hover-ground: var(--rw-surface-hover);
}

.ic-reflection--verse::before {
    content: "";
    position: absolute;
    inset-block-start: -2.5rem;
    inset-inline-end: -1.5rem;
    inline-size: 11rem;
    block-size: 11rem;
    opacity: .55;
    pointer-events: none;
    background: radial-gradient(circle at 50% 100%, transparent 62%, var(--rw-line-accent) 62.5%, var(--rw-line-accent) 64%, transparent 64.5%);
}

.ic-reflection__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--rw-space-1) var(--rw-space-3);
    padding-block-end: var(--rw-space-2);
    border-block-end: 1px solid var(--rw-line-accent);
    position: relative;
}
.ic-reflection__title { font-family: var(--rw-font-display); font-weight: 400; font-size: var(--rw-text-lead); color: var(--rw-ink); margin: 0; }
.ic-reflection__date { font-size: var(--rw-text-small); color: var(--rw-ink-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* PRIMARY TEXT. Never folded away by any condition. */
.ic-reflection__arabic {
    /* Scripture keeps its metrics. */
    font-family: var(--rw-font-quran);
    font-size: clamp(1.5rem, 1.15rem + 1.4vw, 2rem);
    line-height: 2.05;
    color: var(--rw-ink);
    margin: 0;
    text-align: start;
    overflow-wrap: anywhere;
    min-inline-size: 0;
}
.ic-reflection__arabic--hadith { font-size: clamp(1.25rem, 1rem + 1vw, 1.6rem); line-height: 1.95; }

/* SUPPORT. Muted and in the body face, so it does not compete with the Amiri above it. */
.ic-reflection__translation { font-size: var(--rw-text-body); line-height: 1.65; color: var(--rw-ink-muted); margin: 0; max-inline-size: var(--rw-reading-measure); }

/* Attribution is INK, not brass: brass on limestone is 2.90:1 and fails AA as text. */
.ic-reflection__source { font-family: var(--rw-font-display); font-size: var(--rw-text-small); color: var(--rw-ink); margin: 0; }

.ic-reflection__none, .ic-reflection__nomeaning, .ic-reflection__noaudio { font-size: var(--rw-text-small); color: var(--rw-ink-muted); margin: 0; max-inline-size: var(--rw-reading-measure); }
.ic-reflection__foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-1) var(--rw-space-6); }

@media (prefers-reduced-motion: no-preference) {
    .ic-reflection__translation { animation: rw-unfold var(--rw-motion-fast) var(--rw-ease-enter); }
    @keyframes rw-unfold { from { opacity: 0; translate: 0 -2px; } to { opacity: 1; translate: none; } }
}

/* ── F16 · The threshold: three modules that speak about the product ────────────────────
   An offer, an apology, and a way to ask for something you do not have. All three shipped
   with semantic hooks and no rules behind them.

   NO ARCH ON ANY OF THEM. BUILD-PLAN spends the mihrab arch on three moments of pause — the
   home hero, the daily verse, the reader — and none of these is one. Geometry here would be
   wallpaper behind a sentence, so each gets rhythm, a hairline and at most one brass rule.
   Brass is a fill or a border and never carries text: 2.90:1 on limestone. */

/* Get the app. The phone apps offered to a website reader, drawn only for a store whose listing
   exists. Badge-shaped links in ink: the store's name is the part a reader looks for, so it is the
   large line, and the verb above it is the small one, the order both stores' own badges use. */
.ic-getapp { display: grid; gap: var(--rw-space-3); min-inline-size: 0; }
.ic-getapp__title { margin: 0; font-family: var(--rw-font-body); font-size: var(--rw-text-lead); font-weight: 600; line-height: var(--rw-leading-display); }
.ic-getapp__why { margin: 0; color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
.ic-getapp__stores { display: flex; flex-wrap: wrap; gap: var(--rw-space-3); margin: 0; padding: 0; list-style: none; }
.ic-getapp__badge {
    display: inline-grid;
    align-content: center;
    min-block-size: var(--rw-target-min);
    min-inline-size: 10rem;
    padding: var(--rw-space-2) var(--rw-space-4);
    background: var(--rw-ink);
    color: var(--rw-ground);
    border-radius: var(--rw-radius-control);
    text-decoration: none;
    line-height: 1.15;
}
.ic-getapp__badge:hover { background: var(--rw-green-ink); }
/* Block spans, and the badge's own link rules at a class's weight: a page's own link styling
   (the sign-in form underlines and lays its links inline) must not reach inside a badge. */
.ic-getapp .ic-getapp__badge { display: inline-grid; text-decoration: none; }
.ic-getapp__verb { display: block; font-size: var(--rw-text-small); }
.ic-getapp__store { display: block; font-size: var(--rw-text-lead); font-weight: 600; }
/* The landing hero is centred; the offer sits in its axis rather than against the left edge. */
.rw-land__heroinner .ic-getapp { justify-items: center; text-align: center; margin-block-start: var(--rw-space-6); }
.rw-land__heroinner .ic-getapp__stores { justify-content: center; }
/* In settings a frame would claim the offer matters more than the settings around it. */
.ic-account__device .ic-getapp { border-block-start: 1px solid var(--rw-line); padding-block-start: var(--rw-space-6); }
.ic-account__device .ic-getapp__title { font-size: var(--rw-text-body); }

/* P05 · The only full-page state in the product. The app loaded and the service did not, so
   an error rung inside a working-looking shell would blame the page. Centred and unhurried,
   lit by the concept's own courtyard glow — light rather than pattern, so it is not an arch.
   The body carries the held-marks promise verbatim and is NEVER muted: it is the sentence
   the rest of the page exists to make believable. */
.ic-outage {
    display: grid;
    justify-items: center;
    align-content: center;
    gap: var(--rw-space-4);
    min-block-size: 60dvh;
    padding: var(--rw-section-gap) var(--rw-page-gutter);
    text-align: center;
    background: radial-gradient(70% 55% at 50% 0%, var(--rw-ground-glow), transparent 72%);
}
.ic-outage__tile { display: grid; place-items: center; inline-size: 4rem; block-size: 4rem; background: var(--rw-ground-2); border: 1px solid var(--rw-line-accent); border-radius: var(--rw-radius-frame); color: var(--rw-green-ink); }
.ic-outage__title { margin: 0; font-family: var(--rw-font-display); font-weight: 400; font-size: var(--rw-text-heading); line-height: var(--rw-leading-display); max-inline-size: 22ch; }
.ic-outage__body { margin: 0; font-size: var(--rw-text-lead); line-height: var(--rw-leading-body); max-inline-size: 46ch; }
.ic-outage__status { margin: 0; color: var(--rw-ink-muted); font-size: var(--rw-text-small); }

/* A20 · The request opener. RECESSED, never lifted: a raised surface would read as the page's
   own content, while a recess reads as something that opened out of the row above it. The
   destructive kind keeps its colour on the confirm and nowhere else — asking to leave a
   masjid is not itself dangerous. */
.ic-startrequest {
    display: grid;
    gap: var(--rw-field-stack);
    padding: var(--rw-panel-inset);
    background: var(--rw-ground-2);
    border: 1px solid var(--rw-line);
    border-radius: var(--rw-radius-panel);
    max-inline-size: var(--rw-form-measure);
    min-inline-size: 0;
}
/* Vertical only: horizontal resize would let a reader drag the field past its own container. */
/* Only what is not already true. `.ic-field textarea, textarea.ic-input` sets
   min-block-size: 8rem and resize: vertical at higher specificity, so the 6rem this
   rule used to carry was dead AND would have made the field SHORTER. */
.ic-startrequest__detail { max-inline-size: var(--rw-field-measure); line-height: var(--rw-leading-body); }

/* Checklist: a short ordered list of steps with a mark, a label and a worded state, so done and
   not-done never rely on colour alone. Used by the administrator's setup checklist on Home. */
.ic-checklist { list-style: none; padding: 0; margin: 0 0 var(--rw-space-4); display: grid; gap: var(--rw-space-2); }
.ic-checklist__item { display: grid; grid-template-columns: 1.5rem minmax(0, 1fr) auto; align-items: center; gap: var(--rw-space-3); padding-block: var(--rw-space-2); border-block-end: 1px solid var(--rw-line); }
.ic-checklist__mark { inline-size: 1.5rem; block-size: 1.5rem; display: grid; place-items: center; border-radius: 50%; border: 1px solid var(--rw-control-border); font-size: var(--rw-text-small); font-weight: 800; }
.ic-checklist__item[data-done="true"] .ic-checklist__mark { background: var(--rw-success-bg); color: var(--rw-success-fg); border-color: var(--rw-success-bg); }
.ic-checklist__label { min-inline-size: 0; overflow-wrap: anywhere; }
.ic-checklist__state { font-size: var(--rw-text-small); color: var(--rw-ink-muted); }
.ic-checklist__item[data-done="true"] .ic-checklist__state { color: var(--rw-success-ink); }

/* LESS ON THE PAGE, NOTHING LOST (owner, 2026-09-25: "a lot of information … who is going to read
   all that? less is more"). The long explanations a working screen used to print in full sit behind
   one quiet line: the page reads in a glance, and the reader who needs the detail opens it. */
.ic-more { max-inline-size: var(--rw-reading-measure); color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-more > summary { cursor: pointer; inline-size: fit-content; min-block-size: var(--rw-target-min); display: flex; align-items: center; gap: var(--rw-space-2); color: var(--rw-accent-text); font-weight: 600; list-style: none; }
.ic-more > summary::-webkit-details-marker { display: none; }
.ic-more > summary::after { content: "›"; transition: transform var(--rw-motion-fast); }
.ic-more[open] > summary::after { transform: rotate(90deg); }
.ic-more > :not(summary) { margin-block: 0 var(--rw-space-3); }


/* ═══ COMPACT RECORDS ON A PHONE (worklist 4.6, 2026-09-25) ═════════════════════════════
   A labelled record stacked every field under its own label: five fields and a row of
   buttons made each student a 440px card, and a list of 526 was eleven thousand pixels. The
   identity leads on its own line, the facts share one wrapped line with their labels inline,
   and the actions sit under them. A leading checkbox stays beside the name it selects. */
@media (max-width: 40rem) {
    .ic-table[data-mobile-transform="labelled-records"] tr[data-record] { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-space-1) var(--rw-space-4); }
    .ic-table[data-mobile-transform="labelled-records"] tr[data-record] > td { inline-size: auto; flex: 0 1 auto; padding-block: 0; min-inline-size: 0; }
    .ic-table[data-mobile-transform="labelled-records"] tr[data-record] > td[data-label]:not([data-label=""])::before { display: inline; margin-inline-end: .4em; margin-block-end: 0; }
    .ic-table[data-mobile-transform="labelled-records"] tr[data-record] > td[data-priority="identity"] { flex: 1 1 100%; font-weight: 700; }
    .ic-table[data-mobile-transform="labelled-records"] tr[data-record] > td[data-priority="identity"]:has(input[type="checkbox"]) { flex: 0 0 var(--rw-target-min); padding-inline: 0; }
    .ic-table[data-mobile-transform="labelled-records"] tr[data-record] > td[data-priority="identity"]:has(input[type="checkbox"]) + td { flex: 1 1 calc(100% - var(--rw-target-min) - var(--rw-space-4) - 1px); padding-inline: 0; font-weight: 700; }
    .ic-table[data-mobile-transform="labelled-records"] tr[data-record] > td[data-priority="identity"]:has(input[type="checkbox"]) + td::before { display: none; }
    .ic-table[data-mobile-transform="labelled-records"] tr[data-record] > td:not([data-label]):not([data-priority="identity"]),
    .ic-table[data-mobile-transform="labelled-records"] tr[data-record] > td[data-label=""]:not([data-priority="identity"]) { flex: 1 1 100%; margin-block-start: var(--rw-space-2); }
}

/* ── Arrival (depth pass §2.5) ────────────────────────────────────────────────────────────
   Things that open arrive in 180ms; leaving stays instant, like the dialog. Every keyframe and
   every use lives inside no-preference, so a reader who asked for less motion gets states that
   simply appear. */
@media (prefers-reduced-motion: no-preference) {
    /* Keyed on [open], so the animation restarts on every opening whatever the engine does to closed details. */
    .ic-rowactions__more[open] > .ic-rowactions__morelist,
    .ic-pagehead__more[open] > .ic-pagehead__morelist { animation: rw-popin var(--rw-motion-enter) var(--rw-ease-enter); }
    /* In-flow reveals unfold in place (rw-unfold is declared with the reflection rules above). */
    .ic-drawer, .ic-session__confirm, .ic-detail__withdraw { animation: rw-unfold var(--rw-motion-enter) var(--rw-ease-enter); }
    @keyframes rw-popin { from { opacity: 0; translate: 0 var(--rw-pop-from, calc(-1 * var(--rw-space-1))); } }
    @keyframes rw-fadein { from { opacity: 0; } }
}

/* A row whose text is its link (the class's next session): it reads as the row, not as a link in a sentence. */
a.ic-modulerow__what { color: inherit; text-decoration: none; }
