/* Riwaq learning-operations surfaces: attendance, registers, sessions, classes, calendar,
   homework, Qur'an progress and reports. Every colour is a shared rw pair from the foundation;
   layout is by named contract (page gutter, panel inset, field stack, row density). Loaded after
   riwaq-bespoke.css. An interactive element may shed its min width (`min-inline-size: 0`) ONLY
   where the same rule guarantees the press another way — `inline-size: 100%` inside a floored
   track (the calendar entry and date buttons) or a large flex-basis in a wrapping row (the
   subscribe-url and marking inputs) — never as a bare reset, so no target falls below
   `--rw-target-min`. That pairing is what keeps the zero-specificity width-reset trap documented
   in riwaq-bespoke.css disarmed: a reset list that reaches buttons strips the 48px floor
   silently, which is exactly what a shrink tied to a structural width cannot do. */

/* ── Shared collection patterns ─────────────────────────────────────────────────────── */

/* The filter row: a chooser keeps its own size and only a search takes the slack.
   The bounds are the measured ones this product has carried since M2/M7.2 (UXA-097):
   unbounded, `.ic-input` is inline-size 100%, so a chooser's flex base resolved to 545px
   beside a 265px and a 224px one and the third control wrapped onto a line of its own.
   The floor is a min() so it can never exceed the row on a phone. */
.ic-collection__filters > .ic-field {
    flex: 0 1 auto;
    min-inline-size: min(100%, 11rem);
    max-inline-size: 18rem;
}
/* The search grows to a width somebody can read what they typed in, and no further.
   Order is the row's, not the page's: the search leads wherever the row puts it. */
.ic-collection__filters > .ic-field[role="search"] {
    flex: 1 1 14rem;
    max-inline-size: 26rem;
    order: -1;
}
/* Below the phone breakpoint every filter control takes its own row. Two half-width
   choosers side by side at 320 is the narrow nested box the responsive rule refuses,
   and the slack the cap exists to limit does not exist there. */
@media (max-width: 30rem) {
    .ic-collection__filters > .ic-field {
        flex: 1 1 100%;
        max-inline-size: none;
    }
}

/* THE ROW LINES UP ON ITS LABELS, NOT ON ITS MIDDLES, and only a photograph asked for this.
   `.ic-collection__filters` inherits `align-items: center` from the row family in
   riwaq-components.css, which is right for a row of controls that are all the same height.
   The question bank is the first filter row in the product to carry a FIELD HINT — "Only a
   quiz nobody has started can take a question." — so its chooser is two lines taller than the
   search beside it, and centring the two put "Copy into" a whole line above "Search the bank"
   in English and "انسخ إلى" above "ابحث في البنك" in Arabic. Every control here begins with its
   label, so starting them together is what makes the row read as one. On the seven adopters
   that have no hinted filter every field is the same height, and this changes nothing. */
.ic-collection__filters { align-items: start; }
/* "Clear" lines up with the field it clears, not with that field's label (worklist 4.6). */
.ic-collection__filters > [data-collection="clear-filters"] { align-self: end; }

/* AND THE SUMMARY IS ITS OWN REGION. Same photograph at 320, where each filter takes its own
   row: the hint ended one line above "Showing 2 of 2." with nothing between them, so the count
   read as a third line of the hint rather than as the answer to "how many of these am I looking
   at". The gap came free on the earlier adopters from the last field's own spacing; a hint sits
   inside that field, so it did not. */
.ic-collection__summary { margin-block-start: var(--rw-space-4); }
/* A list that is all on screen has nothing to summarise, and the empty live region takes no space. */
.ic-collection__summary:empty { margin-block-start: 0; }

/* A note inside a column heading is quieter than the heading: the small size and normal
   weight, never a second heading. Found by looking — a cell-sized meta token inside a th
   rendered louder than the column name it qualified. */
.ic-table th .ic-table__meta {
    font-size: var(--rw-text-small);
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    display: block;
}
.ic-table__meta {
    color: var(--rw-ink-muted);
    font-size: var(--rw-text-small);
}
/* TWO ROWS OF THE SAME COLLECTION MUST NOT SHOW THE SAME FIELD PAIR IN TWO SHAPES
   (Task 9, A-5). The name and the meta line under it were both inline, so whether the meta
   started on its own line was decided by how long the name happened to be: at 1280 the first
   class stacked its programme line under the link and the second ran the two together, which
   reads as two different kinds of row rather than as two classes. A field and the line that
   qualifies it are a stack, at every width and every name length.

   Scoped to a cell's OWN children: the confirmation sentence inside `.ic-rowactions` and the
   note inside a `th` are meta lines in another position and keep their own rules. */
.ic-table td > .ic-table__name,
.ic-table td > .ic-table__meta { display: block; }
/* A record's name holds a readable width; auto layout gave it the narrowest column and broke
   "Qaidah — begyndere" over two lines beside a wide timetable column. */
.ic-table td > .ic-table__name { min-inline-size: min(10rem, 40vw); }
/* THE REGISTERS NOBODY TOOK ARE A SECOND SENTENCE, NOT A SUFFIX. Razor eats the whitespace around
   a conditional block, so this ran straight into the rate beside it and a learner read
   "12 / 202 without a register". Its own line is also what F4 asks of it: unmarked is stated
   beside the rate and never folded into it. */
.ic-table__meta[data-unmarked] { display: block; }
/* Flagged rows carry the same non-colour bar the register uses for an unmarked child. */
.ic-table__row--flagged td:first-child { border-inline-start: var(--rw-space-1) solid var(--rw-accent-border); }

/* Above the card breakpoint a row of buttons in a cell stays on one line and a figure a
   reader compares down a column never breaks across lines. Below it the row is a card,
   where a value on its own line is the layout rather than a defect. `align-items:
   flex-start` because a flex row aligns stretch by default, so an opened disclosure
   stretched the visible action into a tall box beside it. The confirmation sentence
   (carrying .ic-table__meta) keeps its wrap at higher specificity. */
@media (min-width: 40.0625rem) {
    .ic-table td > .ic-rowactions {
        flex-wrap: nowrap;
        align-items: flex-start;
    }
    .ic-table td > .ic-rowactions:has(> .ic-table__meta) {
        flex-wrap: wrap;
    }
    .ic-table td.ic-tabular {
        white-space: nowrap;
    }
}

/* Small shared atoms retained from the markup vocabulary. `--tiny` stays a full-height
   target: the 48px product rule is not traded for density, only the padding is. */
.ic-btn--tiny { padding-inline: var(--rw-space-4); font-size: var(--rw-text-small); }
/* Outlined, so its ink sits on the surface and not on the danger ground: the state INK, never
   the `-fg`, which is near-white after dark (B-6). */
.ic-btn--danger-outline { background: var(--rw-surface); color: var(--rw-danger-ink); border-color: var(--rw-danger-ink); }
/* Show/Hide and "Mark as read" were the only controls with no hand feedback and a square focus
   ring. The hover tints within the plane (accent-text on ground-2 5.15 / 9.55, on surface-hover
   6.00 / 5.63). The press withdraws the tone and shifts 1px, with no inset, so no composite is owed. */
.ic-textbtn {
    background: transparent; border: 0; padding: 0 var(--rw-space-1); cursor: pointer;
    color: var(--rw-accent-text); text-decoration: underline; text-underline-offset: var(--rw-space-1);
    font: inherit; border-radius: var(--rw-radius-control); transition: var(--rw-transition-hand);
}
.ic-textbtn:hover:not(:disabled) { background: var(--rw-hover-ground, var(--rw-ground-2)); text-decoration-thickness: 2px; }
.ic-textbtn:active:not(:disabled) { background: transparent; translate: 0 1px; }
.ic-backlink { display: inline-flex; align-items: center; gap: var(--rw-label-gap); min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); }
/* The target is the anchor, not the paragraph around it: a 48px wrapper whose link is a
   25px text run still presses at 25px. Measured on the quran-student backlink. */
.ic-backlink > a { display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); }
.ic-muted { color: var(--rw-ink-muted); }
.ic-subhead { font-size: var(--rw-text-lead); font-weight: 600; }
/* .ic-eyebrow moved to riwaq-base.css: the concept's kicker is a foundation recipe now
   (owner concept alignment, 2026-09-06), and a copy here would win the cascade and undo it. */
.ic-pill { display: inline-flex; align-items: center; gap: var(--rw-space-1); padding: var(--rw-space-1) var(--rw-label-gap); border-radius: var(--rw-radius-control); background: var(--rw-ground-2); color: var(--rw-ink); font-size: var(--rw-text-small); font-weight: 600; max-inline-size: 100%; overflow-wrap: anywhere; }
.ic-pill--muted { background: var(--rw-ground-2); color: var(--rw-ink-muted); }
.ic-pill--warn { background: var(--rw-warning-bg); color: var(--rw-warning-fg); }
.ic-chip { display: inline-flex; align-items: center; gap: var(--rw-space-1); padding: var(--rw-space-1) var(--rw-label-gap); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-control); font-size: var(--rw-text-small); }
.ic-chip__count { font-variant-numeric: tabular-nums; font-weight: 700; }
/* The homework filter chips drew their only boundary in the decorative line (1.33 / 1.54:1), which
   fails WCAG 1.4.11. Scoped to button, so a static chip label keeps its hairline. The chosen
   3px-double recipe is untouched, and the transition comes from the kit's choosable rule. */
button.ic-chip { border-color: var(--rw-control-border); cursor: pointer; }
button.ic-chip:hover:not([aria-pressed="true"]):not(:disabled) { background: var(--rw-surface-hover); }
button.ic-chip:active:not(:disabled) { translate: 0 1px; box-shadow: var(--rw-press-inset); }
button.ic-chip:active:not(:disabled):focus-visible { box-shadow: var(--rw-press-inset), var(--rw-focus-bands); }
.ic-pager { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-3); }
.ic-form-actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-3); }
.ic-daterow { display: flex; flex-wrap: wrap; gap: var(--rw-field-stack); }
.ic-input--inline { inline-size: auto; }
.ic-field__hint--warn { color: var(--rw-warning-fg); background: var(--rw-warning-bg); padding: var(--rw-space-1) var(--rw-label-gap); border-radius: var(--rw-radius-control); }
.ic-alert__text { margin: 0; }
.ic-org__note { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }

/* Selection is a state, never a primary treatment (UXA-074): the chosen tab or pressed
   view switcher is drawn from the attribute that announces it, in cues that survive
   forced colors — a doubled border and a heavier weight, beside the kit's ground and
   underline. Padding gives the 2px back so choosing a view never shifts the row.
   CONSTRAINT: no `.ic-btn--tiny` is ever pressed today. If one ships, this rule's
   (0,2,0) padding outweighs `--tiny`'s (0,1,0) compact padding-inline — give the tiny
   variant its own compensated pressed padding then, rather than widening this selector
   now and disturbing the pressed contract the matrices measure. */
[role="tab"][aria-selected="true"], .ic-btn[aria-pressed="true"] {
    background: var(--rw-green);
    color: var(--rw-on-green);
    border-width: 3px;
    border-style: double;
    border-color: var(--rw-control-border);
    font-weight: 700;
    padding: calc(var(--rw-label-gap) - 2px) calc(var(--rw-row-padding-inline) - 2px);
    text-decoration: underline;
    text-underline-offset: var(--rw-space-1);
}

/* AND POINTING AT THE CHOSEN CONTROL MAY NOT TAKE ITS GROUND AWAY.

   `riwaq-components.css`'s hover rule carries four `:not(.class)` clauses and two more besides,
   which makes it (0,8,0) against this rule's (0,2,0) — so a hover replaced the chosen control's
   green with `--rw-surface-hover` while THIS rule's pale `--rw-on-green` ink stayed. Measured on
   the calendar's filter drawer at 320 in daylight: #F2EBDD on #F6F0E4. Computed from those two
   token values by WCAG relative luminance — L 0.835144 and 0.875144, so
   (0.875144 + 0.05) / (0.835144 + 0.05) — that is 1.045:1, which is simply invisible. (It read
   1.03:1 until it was recomputed; the arithmetic is written out so the next reader can check this
   figure rather than quote it.) Three of the four cues survived — the doubled border, the weight and the underline —
   which is exactly why it went unseen for as long as it did, and a tap leaves the hover state on,
   so a phone keeps it until something else is touched.

   The counterpart is spelled with the same guards rather than with `!important`, because the
   thing that must win is a rule and not a shout. */
[role="tab"][aria-selected="true"]:hover:not(:disabled):not([aria-disabled="true"]),
.ic-btn[aria-pressed="true"]:hover:not(:disabled):not([aria-disabled="true"]):not(.ic-btn--primary):not(.ic-btn--quiet):not(.ic-btn--destructive):not(.ic-btn--danger) {
    background: var(--rw-green);
    color: var(--rw-on-green);
}

/* Native players sit in the page's own box: the form measure rather than the OS default
   pill, and the product's own control radius. Deliberately not rebuilt — a custom
   transport is a keyboard trap waiting to be written; the fix is the box, not ownership.
   One grouped rule reaches all three players so none is left in the OS default. */
.ic-hw__audio, .ic-work__audio, .ic-reflection__audio {
    display: block;
    inline-size: 100%;
    max-inline-size: var(--rw-form-measure);
    min-block-size: var(--rw-target-min);
    border-radius: var(--rw-radius-control);
}
/* Still the native player, tinted into the palette: its default grey panel was the one foreign
   colour on the page. Engines without the pseudo-element keep their own. */
:is(.ic-hw__audio, .ic-work__audio, .ic-reflection__audio)::-webkit-media-controls-panel { background-color: var(--rw-ground-2); }
:is(.ic-hw__audio, .ic-work__audio, .ic-reflection__audio) { border: 1px solid var(--rw-line); background: var(--rw-ground-2); }

/* ── Figures ────────────────────────────────────────────────────────────────────────── */

.ic-figrow { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); border-block-start: 1px solid var(--rw-line); }
/* The lead figure's cut only exists while something follows it to be cut from. */
.ic-fig--lead:not(:last-child) { border-inline-end: 1px solid var(--rw-line); }
.ic-fig--lead .ic-fig__n { color: var(--rw-green-ink); }
.ic-fig__n--none { color: var(--rw-ink-muted); }
.ic-fig__label { font-weight: 600; overflow-wrap: anywhere; }
/* A figure starts on the content edge, like the heading above it; the inset belongs only beside
   the lead figure's separator. Inside a card the row drops its own top rule too, which drew a box
   inside the box. */
.ic-figrow { column-gap: var(--rw-panel-inset); }
.ic-figrow > .ic-fig { padding-inline: 0; }
.ic-figrow > .ic-fig--lead:not(:last-child) { padding-inline-end: var(--rw-panel-inset); }
.ic-module__body > .ic-figrow { border-block-start: 0; }
.ic-module__body > .ic-figrow > .ic-fig { padding-block-start: 0; border-block-start: 0; }
/* The row stacks by its own width, not the window's, so a card too narrow for two figures gets
   the stacked separator rather than a side rule down a single column. */
.ic-figrow { container-type: inline-size; }
@container (max-width: 28rem) {
    .ic-fig--lead:not(:last-child) { border-inline-end: 0; border-block-end: 1px solid var(--rw-line); }
    .ic-figrow > .ic-fig--lead:not(:last-child) { padding-inline-end: 0; }
    .ic-module__body > .ic-figrow > .ic-fig + .ic-fig { padding-block-start: var(--rw-panel-inset); }
}
@media (max-width: 40rem) {
    .ic-figrow { grid-template-columns: minmax(0, 1fr); }
    .ic-fig--lead:not(:last-child) { border-inline-end: 0; border-block-end: 1px solid var(--rw-line); }
}

/* ── The diagram frame ──────────────────────────────────────────────────────────────── */

/* The picture is decoration over a guaranteed table of values: every rate is a row of a
   real table beside it and the SVG-free bars are aria-hidden. */
.ic-diagram { display: grid; gap: var(--rw-space-3); }
.ic-diagram__caption { font-weight: 600; }
.ic-diagram__calc, .ic-diagram__none { color: var(--rw-ink-muted); font-size: var(--rw-text-small); max-inline-size: var(--rw-reading-measure); }
.ic-diagram__picture { min-inline-size: 0; }
.ic-diagram__values { min-inline-size: 0; }
/* Inside a drawer the diagram separates itself from the facts above it — the rule that
   once lived in a page scope and matched nothing because the markup is a component's. */
.ic-drawer .ic-diagram { border-block-start: 1px solid var(--rw-line); padding-block-start: var(--rw-space-4); margin-block-start: var(--rw-space-4); }

/* Marks size themselves as a percentage of their column and carry an edge; border-box
   keeps the edge inside the percentage so a bar at 100% cannot sit above its column and
   the gap cannot sit below the floor the bars are measured from. */
.ic-bars { display: flex; align-items: end; gap: var(--rw-space-2); block-size: 6rem; padding: var(--rw-space-2); border-block-end: 1px solid var(--rw-line); }
.ic-bars__col { flex: 1 1 0; min-inline-size: 0; block-size: 100%; display: grid; align-items: end; }
/* A data mark needs 3:1. At night the green fill on a card was 1.84:1; green-ink is the same
   #1E4638 by day (10.31) and 6.69 at night. */
.ic-bars__bar {
    box-sizing: border-box;
    background: var(--rw-green-ink);
    border: 1px solid var(--rw-green-ink);
    border-start-start-radius: var(--rw-radius-control);
    border-start-end-radius: var(--rw-radius-control);
}
/* A gap is not a short bar: a week nobody looked at is drawn as an outlined absence. */
.ic-bars__gap {
    box-sizing: border-box;
    block-size: 100%;
    border: 1px dashed var(--rw-control-border);
    background: transparent;
}

/* The attendance strip and history timeline keep their values in the document. */
/* A strip, not a chart: a fixed height and its own width. Scaled to the column it drew ten
   bars 350px tall, and with no fills after the old skin went they were solid black. */
.ic-strip { display: grid; gap: var(--rw-space-2); justify-self: start; max-inline-size: 100%; margin: 0; }
.ic-strip__svg { display: block; block-size: 3.5rem; inline-size: auto; max-inline-size: 100%; }
.ic-strip__caption { display: flex; flex-wrap: wrap; gap: var(--rw-space-1); }
.ic-strip__caption > span + span::before { content: "– "; }
.ic-strip__caption, .ic-strip__empty { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
/* The state inks, which are the tones measured on the page's own grounds in both themes. */
.ic-strip__bar--present { fill: var(--rw-success-ink); }
.ic-strip__bar--late { fill: var(--rw-warning-ink); }
.ic-strip__bar--absent { fill: var(--rw-danger-ink); }
.ic-strip__bar--excused { fill: var(--rw-info-ink); }
.ic-strip__bar--cancelled { fill: none; stroke: var(--rw-control-border); stroke-width: .5; stroke-dasharray: 1.5 1; }
.ic-timeline { display: flex; flex-wrap: wrap; gap: var(--rw-space-1); padding: 0; list-style: none; }
.ic-timeline__rail { display: contents; }
.ic-timeline__item { inline-size: var(--rw-space-3); block-size: var(--rw-space-3); border-radius: 50%; }
.ic-recur { display: grid; gap: var(--rw-space-1); }

/* A record's facts are pairs, not a stack: the browser's 40px dd indent is the offset
   that makes a pair read as a stack, so it is removed and each fact lays out as a row. */
/* Label and value in two columns, so a card of facts reads down one edge instead of as a run of
   phrases; a narrow card stacks each pair. */
.ic-detail__facts { display: grid; gap: var(--rw-space-2); margin: 0; container-type: inline-size; }
.ic-detail__facts > div { display: grid; grid-template-columns: minmax(6rem, 9rem) minmax(0, 1fr); gap: var(--rw-label-gap) var(--rw-space-4); align-items: baseline; }
.ic-detail__facts dt { font-weight: 400; color: var(--rw-ink-muted); }
@container (max-width: 24rem) { .ic-detail__facts > div { grid-template-columns: minmax(0, 1fr); gap: 0; } }
.ic-detail__facts dd { margin: 0; }
.ic-definition { display: grid; gap: var(--rw-space-2); margin: 0; }
.ic-definition > div { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap) var(--rw-space-3); }
.ic-definition dd { margin: 0; }

/* ── Attendance vocabulary ──────────────────────────────────────────────────────────── */

/* The four outcomes are a closed set and every value that reaches the document has a
   rule; RecordedDefectTests holds the set closed. The pairs are the foundation's opaque
   semantic tokens: present/success, late/warning, absent/danger, excused/info. */
.ic-attend__dot { inline-size: var(--rw-space-3); block-size: var(--rw-space-3); border-radius: 50%; flex: none; border: 1px solid transparent; }
.ic-attend__dot--present { background: var(--rw-success-bg); border-color: var(--rw-success-fg); }
.ic-attend__dot--late { background: var(--rw-warning-bg); border-color: var(--rw-warning-fg); }
.ic-attend__dot--absent { background: var(--rw-danger-bg); border-color: var(--rw-danger-fg); }
.ic-attend__dot--excused { background: var(--rw-info-bg); border-color: var(--rw-info-fg); }
.ic-attend__status { display: inline-flex; align-items: center; padding: var(--rw-space-1) var(--rw-label-gap); border-radius: var(--rw-radius-control); font-weight: 600; font-size: var(--rw-text-small); }
.ic-attend__status--present { background: var(--rw-success-bg); color: var(--rw-success-fg); }
.ic-attend__status--late { background: var(--rw-warning-bg); color: var(--rw-warning-fg); }
.ic-attend__status--absent { background: var(--rw-danger-bg); color: var(--rw-danger-fg); }
.ic-attend__status--excused { background: var(--rw-info-bg); color: var(--rw-info-fg); }
.ic-attend__seg { display: block; }
.ic-attend__seg--present { background: var(--rw-success-ink); }
.ic-attend__seg--late { background: var(--rw-warning-ink); }
.ic-attend__seg--absent { background: var(--rw-danger-ink); }
.ic-attend__seg--excused { background: var(--rw-info-ink); }

/* The register's chosen mark colours the choice by its value once it is pressed; unchosen
   options stay the neutral control so colour is a statement, never decoration. The same
   values reach review rows as .ic-mark[data-mark]. */
.ic-rowchoice__option[data-choice="present"][aria-pressed="true"] { background: var(--rw-success-bg); color: var(--rw-success-fg); border-color: var(--rw-success-fg); }
.ic-rowchoice__option[data-choice="late"][aria-pressed="true"] { background: var(--rw-warning-bg); color: var(--rw-warning-fg); border-color: var(--rw-warning-fg); }
.ic-rowchoice__option[data-choice="absent"][aria-pressed="true"] { background: var(--rw-danger-bg); color: var(--rw-danger-fg); border-color: var(--rw-danger-fg); }
.ic-rowchoice__option[data-choice="excused"][aria-pressed="true"] { background: var(--rw-info-bg); color: var(--rw-info-fg); border-color: var(--rw-info-fg); }
.ic-mark { display: inline-flex; align-items: center; padding: var(--rw-space-1) var(--rw-label-gap); border-radius: var(--rw-radius-control); font-weight: 600; font-size: var(--rw-text-small); background: var(--rw-ground-2); }
.ic-mark[data-mark="present"] { background: var(--rw-success-bg); color: var(--rw-success-fg); }
.ic-mark[data-mark="late"] { background: var(--rw-warning-bg); color: var(--rw-warning-fg); }
.ic-mark[data-mark="absent"] { background: var(--rw-danger-bg); color: var(--rw-danger-fg); }
.ic-mark[data-mark="excused"] { background: var(--rw-info-bg); color: var(--rw-info-fg); }

/* The attendance summary: one proportional bar over the four counts that stay the record. */
.ic-attend { display: grid; gap: var(--rw-space-4); }
/* The rate is the headline and is set like every other figure; the count and caption qualify it.
   All three stood as the same small bold line. */
.ic-attend__headline { display: grid; grid-template-columns: auto 1fr; grid-template-areas: "rate count" "rate caption"; align-items: end; column-gap: var(--rw-space-4); }
.ic-attend__headline > * { margin: 0; }
.ic-attend__rate { grid-area: rate; font-size: var(--rw-text-heading); font-weight: 300; line-height: 1; letter-spacing: -.02em; color: var(--rw-green-ink); }
.ic-attend__count { grid-area: count; font-weight: 600; }
.ic-attend__caption { grid-area: caption; color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-attend__rate { font-size: var(--rw-text-lead); font-weight: 700; }
.ic-attend__caption, .ic-attend__meaning, .ic-attend__note, .ic-attend__empty { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-attend__meaning { max-inline-size: var(--rw-reading-measure); }
/* A learner's own page: one clear figure and one sentence of rule (worklist 4.2). */
.ic-attend--own .ic-attend__rate { font-family: var(--rw-font-display); font-size: 3rem; font-weight: 400; }
.ic-attend--own .ic-attend__headline { column-gap: var(--rw-space-4); }
.ic-attend__rule { margin: 0; color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
.ic-attend__bar { display: flex; block-size: var(--rw-space-3); border-radius: var(--rw-radius-control); background: var(--rw-ground-2); }
.ic-attend__breakdown { display: flex; flex-wrap: wrap; gap: var(--rw-space-3) var(--rw-space-6); padding: 0; margin: 0; list-style: none; }
.ic-attend__item { display: flex; align-items: center; gap: var(--rw-label-gap); }
.ic-attend__num { font-weight: 700; }
.ic-attend__history { display: grid; gap: var(--rw-space-2); padding: 0; margin: 0; list-style: none; }
.ic-attend__row { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); padding-block: var(--rw-space-2); border-block-end: 1px solid var(--rw-line); }
.ic-attend__row.is-cancelled { color: var(--rw-ink-muted); }
.ic-attend__row.is-cancelled .ic-attend__date { text-decoration: line-through; }
.ic-attend__date { font-variant-numeric: tabular-nums; }
/* The class name is a record ("Fortgeschrittenenunterricht") and breaks rather than widen
   the page: its min-content at 200% on a phone is 402px against a 320px viewport (measured,
   my-attendance history rows — the same defect the queue and list rows already paid). */
.ic-attend__class { color: var(--rw-ink-muted); min-inline-size: 0; overflow-wrap: anywhere; }
.ic-attend__count { font-variant-numeric: tabular-nums; }
.ic-attend__more { justify-self: start; }

/* The workspace: queues of work waiting for somebody, one region beside another.

   THREE BESPOKE QUEUE RULES STOOD HERE AND ARE DELETED (Riwaq A1). `.ic-attend__queue` and its
   three row states were a third spelling of `.ic-record`: the same card, the same accent bar for
   "nobody has dealt with this", and a tint for "decided" that said by colour alone what the row
   already says in a word. All three queues draw `RecordList`/`RecordRow` now, so the bar comes
   from `[data-record-state="untouched"]` — one rule, on the row primitive, shared with the
   register itself. `.ic-attend__agegroup` went with the heading it styled: an age bucket is a
   `Module` at level 3 rather than a bare h3 in a scroll. */
.ic-attend__reviewintro { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
.ic-attend__state { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--rw-label-gap); color: var(--rw-ink-muted); }
.ic-attend__decide { display: grid; gap: var(--rw-space-3); }
/* A family's own sentence, on a notice and on an explanation. Its own line rather than a
   fragment of the facts row: it is the one thing on either row somebody wrote by hand, and it
   breaks rather than widening the row on a phone. */
.ic-attend__reason { flex-basis: 100%; min-inline-size: 0; overflow-wrap: anywhere; }
/* One view's panel. The panel is the stack the tablist switches between, and its own gap is the
   single source of rhythm inside it — the regions carry no outer margin of their own, which is
   the doubled-rhythm trap the redesign is measured against. */
.ic-attend__panel { display: grid; gap: var(--rw-section-gap); }
/* THE TABLIST AND THE FILTER ROW ARE ONE CLUSTER. Which work is on screen and how much of it are
   two halves of one question about the panel below them, and the page wrapper stacks its children
   at the section rhythm — the distance between two REGIONS. Loose, these two would have been
   pushed that far apart and read as regions of their own. Their own gap is the field stack, which
   is what the product puts between a control and the control under it. */
.ic-attend__controls { display: grid; gap: var(--rw-field-stack); }
/* What a masjid is owed, at the head of the workspace and on every view. Two figures at most,
   so this is a pair rather than the scoreboard PRODUCT.md section 9 bans by name: the panel is
   absent altogether when nothing is owed, which is what makes it an obligation. */
.ic-owed { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: var(--rw-space-6); }

/* ── My attendance (the learner's own record) ───────────────────────────────────────── */

.ic-my-attendance__announce { color: var(--rw-success-fg); background: var(--rw-success-bg); padding: var(--rw-space-3) var(--rw-space-4); border-radius: var(--rw-radius-control); }
/* AND IT TAKES NO GROUND WHILE IT IS EMPTY, WHICH IS ITS NORMAL STATE. The region is kept present
   and empty on purpose — a live region inserted at the same moment as its message is announced by
   nothing — so it is empty on every load, and a painted padded band spanning the content width
   between the page title and the first sentence said nothing sixty frames running. A success ground
   that is always there cannot mean "something was saved" when something is. This was pre-existing
   (a01c6e1a) and the two regions written beside it carry the same rule (`.ic-hw__announce:empty`,
   `.ic-qmine__announce:empty`); the comment at the second of those asserted this one already did. */
.ic-my-attendance__announce:empty { display: none; }
.ic-my-attendance__list { display: grid; gap: var(--rw-space-3); padding: 0; margin: 0; list-style: none; }
.ic-my-attendance__list > li { display: grid; gap: var(--rw-space-2); padding: var(--rw-space-4); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); min-inline-size: 0; overflow-wrap: anywhere; }
/* A row action hugs its own words rather than filling the row: a full-width Withdraw
   under every notice reads as the row's primary purpose. */
.ic-my-attendance__list > li > .ic-btn { inline-size: fit-content; }
.ic-my-attendance__state { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--rw-label-gap); }
.ic-my-attendance__form { display: grid; gap: var(--rw-field-stack); padding: var(--rw-panel-inset); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); max-inline-size: var(--rw-form-measure); }
.ic-my-attendance__confirm { display: grid; gap: var(--rw-space-3); padding: var(--rw-space-4); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-control); background: var(--rw-ground-2); }
.ic-my-attendance__buttons { display: flex; flex-wrap: wrap; gap: var(--rw-space-3); }
.ic-my-attendance__consequence { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap); color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
/* THE SCOPE AND THE UNCOUNTED, BEFORE THE RATE (F4 §1). A qualification that arrives after the
   number it qualifies has arrived too late, so this sits above the record and reads as prose
   rather than as a band: nothing failed and nothing is being announced. */
.ic-my-attendance__scope { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap); color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
/* WHAT A DECIDED EXPLANATION DID TO THE RECORD. Inside the row it belongs to, quiet, and with a
   rule on its leading edge so it reads as attached to the mark above rather than as a new row. */
.ic-my-attendance__outcome { margin: 0; color: var(--rw-ink-muted); padding-inline-start: var(--rw-space-3); border-inline-start: 1px solid var(--rw-line); max-inline-size: var(--rw-reading-measure); }
/* WHAT THE CHILD TOLD THE OFFICE, IN THEIR OWN WORDS. Prose somebody typed, so it keeps its own
   line breaks and a reading measure; quiet, because it is a record of what was said rather than
   something to act on. */
.ic-my-attendance__told { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; max-inline-size: var(--rw-reading-measure); }
/* THE WAY IN TO THE ONE TASK ON THE PAGE. `Add planned absence` is the last element of a page
   measuring 4,284-6,170px at 320, and the order above it is the reader's own, so what was missing was
   an anchor rather than a different arrangement. Quiet on purpose — it is a way to somewhere, not a
   second primary competing with the record it sits above — and 48px ON THE ELEMENT, which for a link
   means an inline-flex box with its own min-block-size rather than a taller line of text. */
/* Two actions, spaced and shaped like every other quiet button; they ran together as two squared boxes. */
.ic-my-attendance__plan { margin: 0; display: flex; flex-wrap: wrap; gap: var(--rw-space-3); }
.ic-my-attendance__jump { display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); padding-inline: 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-weight: 600; text-decoration: none; }
.ic-my-attendance__jump:hover { background: var(--rw-surface-hover); }

/* ── The register ───────────────────────────────────────────────────────────────────── */

/* The bulk of the register's layout is the shared slice (riwaq-slices.css); these are the
   states that slice did not carry. Failure keeps its stamp and its reason on screen. */
.ic-register__failhead { font-weight: 700; }
.ic-register__failstamp { font-variant-numeric: tabular-nums; font-weight: 700; }
.ic-register__failwhy { font-size: var(--rw-text-small); }
.ic-register__cancelled { color: var(--rw-warning-fg); background: var(--rw-warning-bg); padding: var(--rw-space-3) var(--rw-space-4); border-radius: var(--rw-radius-control); }
.ic-register__empty { color: var(--rw-ink-muted); }
.ic-register__age { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
/* F8 §1's `.markrow__state`, at its own values: the one state that has no pressed pill to say it. */
.ic-register__state { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
/* THE STATE SITS BESIDE THE FACT, NOT ABOVE IT (B2 re-review round, S-8). The board's
   `.markrow__who` is `display: flex; flex-wrap: wrap; align-items: baseline`, so it draws
   `Yusuf Ali  Not marked` on one line. The word arrived in `RecordRow`'s Note slot instead,
   whose container `.ic-record__who` is a one-column grid — every child takes a line of its
   own — and a fresh register became three stacked lines per row where the board has two.
   `.ic-record__who` is shared by every record row in the product and is not this cohort's to
   move, so the register wraps its own facts in one baseline row and hands the grid a single
   item. `flex-wrap` is what keeps it honest at 320: a long state and a long age wrap to a
   second line rather than widening the row. */
.ic-register__facts { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); min-inline-size: 0; }

/* ── Sessions ───────────────────────────────────────────────────────────────────────── */

.ic-sessions__tabs { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap); }
.ic-sessions__list { display: grid; padding: 0; margin: 0; list-style: none; }
.ic-sessions__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-label-gap) var(--rw-space-4); padding-block: var(--rw-row-padding-block); border-block-end: 1px solid var(--rw-line); min-inline-size: 0; }
/* A cancelled session keeps its slot: its absence is the thing a reader needs to notice. */
/* THE LAST ROW OF A PANEL DRAWS NO RULE UNDER IT — the components sheet's sentence, applied to the
   three row classes this cohort added without it (B1 fix round, B-8) — and .ic-session, the root's
   own term rows, found the same way the moment the past disclosure was photographed open (B-14).
   `.ic-slot` takes `:last-of-type`, not `:last-child`: the slots are the schedule fieldset's `div`s
   and "Add another time" — a `button` — is its last child, so `:last-child` never matched a slot and
   the rule under the last weekly time was drawn in every schedule frame while the sheet guard, which
   reads text, passed (scoped re-review RR-6; the B1 rig now reads the computed border). And
   `.ic-detail__list > li` — the root's When it meets, Progress's learners, the copy preview's lists —
   had the same defect one row class over (RR-7). */
.ic-sessions__row:last-child, .ic-session:last-child, .ic-slot:last-of-type, .ic-revisions__item:last-child, .ic-detail__list > li:last-child { border-block-end: 0; }
.ic-sessions__row.is-cancelled .ic-sessions__date { text-decoration: line-through; }
.ic-sessions__row.is-cancelled { color: var(--rw-ink-muted); }
.ic-sessions__when { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); flex: 1 1 16rem; min-inline-size: 0; min-block-size: var(--rw-target-min); color: var(--rw-ink); text-decoration: none; align-content: center; }
.ic-sessions__when:hover .ic-sessions__date { text-decoration: underline; text-underline-offset: var(--rw-space-1); }
.ic-sessions__date { font-weight: 600; }
.ic-sessions__time { color: var(--rw-ink-muted); }
.ic-sessions__state { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-sessions__register { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-sessions__go { display: inline-flex; gap: var(--rw-label-gap); }
.ic-sessions__none, .ic-sessions__loading { color: var(--rw-ink-muted); }

/* Session detail: what changed about one lesson, and who has been told. */
.ic-sessiondetail__lede { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
.ic-sessiondetail__cancelled { color: var(--rw-danger-fg); background: var(--rw-danger-bg); padding: var(--rw-space-3) var(--rw-space-4); border-inline-start: var(--rw-space-1) solid currentColor; }
/* The way onward out of a cancellation is a press like any other, and it measured 125x20 in the
   band (slice-b2-shots.mjs) — under the product's 48px rule, on the one link that paragraph has. */
.ic-sessiondetail__cancelled > a { display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); }
.ic-sessiondetail__moved, .ic-sessiondetail__warn { color: var(--rw-warning-fg); background: var(--rw-warning-bg); padding: var(--rw-space-3) var(--rw-space-4); border-inline-start: var(--rw-space-1) solid currentColor; }
.ic-sessiondetail__note, .ic-sessiondetail__due { color: var(--rw-ink-muted); }
.ic-sessiondetail__reason { display: grid; gap: var(--rw-label-gap); }
.ic-sessiondetail__flow { display: grid; gap: var(--rw-field-stack); }
.ic-sessiondetail__register { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-3); }
.ic-sessiondetail__scopes { display: grid; gap: var(--rw-space-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); }
.ic-scopecard { display: grid; align-content: start; gap: var(--rw-space-2); padding: var(--rw-space-4); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); min-inline-size: 0; }
/* A scope this reader is refused stays visible as refused rather than disappearing. */
.ic-scopecard.is-refused { background: var(--rw-ground-2); color: var(--rw-ink-muted); border-style: dashed; }
.ic-scopecard__counts { font-variant-numeric: tabular-nums; }
.ic-session__warn { color: var(--rw-warning-fg); background: var(--rw-warning-bg); padding: var(--rw-space-3) var(--rw-space-4); border-radius: var(--rw-radius-control); }
.ic-session__form { display: grid; gap: var(--rw-field-stack); }
.ic-session__formactions { display: flex; flex-wrap: wrap; gap: var(--rw-space-3); }
/* The workspace's term of sessions: one row per lesson, the next one marked along its
   own start edge, a cancelled one muted — the row stays, because a lesson called off is
   information the term list must keep showing. */
.ic-session { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); padding-block: var(--rw-row-padding-block); border-block-end: 1px solid var(--rw-line); min-inline-size: 0; }
.ic-session[data-session="next"] { border-inline-start: var(--rw-space-1) solid var(--rw-accent-border); padding-inline-start: var(--rw-space-3); }
.ic-session[data-session-state="cancelled"] { color: var(--rw-ink-muted); }
/* The row is the way into its session (worklist 4.1): one link across the facts, a chevron at the end. */
.ic-session__open { flex: 1 1 100%; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); min-block-size: var(--rw-target-min); color: inherit; text-decoration: none; }
.ic-session__open::after { content: "›"; margin-inline-start: auto; color: var(--rw-accent-text); font-weight: 700; }
.ic-session__open:dir(rtl)::after { content: "‹"; }
.ic-session__open:hover .ic-session__date { color: var(--rw-accent-text); text-decoration: underline; }
.ic-session__date, .ic-session__duedate { font-variant-numeric: tabular-nums; }
/* The date and the time hold their width so a term of rows keeps its buttons in one column;
   "Sun 13 Dec" and "Sat 3 Oct" otherwise start the actions at a different place on every row. */
.ic-session__date { min-inline-size: 6.5em; }
.ic-session__date + .ic-tabular { min-inline-size: 7em; }
.ic-session__due, .ic-session__note { color: var(--rw-ink-muted); }
.ic-session__noteage { display: block; margin-block-start: var(--rw-space-1); color: var(--rw-ink-muted); font-size: var(--rw-text-small); }

/* ── Classes: collection, workspace, record, students, schedule ─────────────────────── */

/* Enrolment requests waiting on the collection: each row is a decision, worst first. */
.ic-requests { display: grid; gap: var(--rw-space-3); }
.ic-requests__what { color: var(--rw-ink-muted); }
.ic-requests__actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); }

/* The public catalogue: one readable card per class, its times as a plain list. */
.ic-cat { display: grid; gap: var(--rw-space-4); padding: 0; margin: 0; list-style: none; grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr)); }
.ic-cat__item { display: grid; align-content: start; gap: var(--rw-space-3); padding: var(--rw-panel-inset); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); min-inline-size: 0; }
.ic-cat__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); }
.ic-cat__name { font-size: var(--rw-text-lead); font-weight: 700; }
.ic-cat__meta, .ic-cat__ages, .ic-cat__note { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-cat__when { display: grid; gap: var(--rw-space-1); padding: 0; margin: 0; list-style: none; }
.ic-cat__when--none { color: var(--rw-ink-muted); }
/* Sized to its sentence: it is a grid item of the record's who-column, and stretched it was a
   690px tinted bar at 1280 for nine words. */
.ic-cat__full { justify-self: start; color: var(--rw-warning-fg); background: var(--rw-warning-bg); padding: var(--rw-space-1) var(--rw-label-gap); border-radius: var(--rw-radius-control); font-size: var(--rw-text-small); font-weight: 600; }
/* The catalogue's programme regions, one stack with one gap. The collection's records slot is a
   plain block, so the modules touched; the gap is the one a module's own body uses between its
   parts, because these are regions of one collection rather than sections of the page. */
.ic-catalogue__programmes { display: grid; gap: var(--rw-field-stack); min-inline-size: 0; }
.ic-cat__actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); margin-block-start: auto; }
/* Membership standing on a catalogue card is a state, spelled with the shared pairs. */
.ic-badge--active { background: var(--rw-success-bg); color: var(--rw-success-fg); }
.ic-badge--pending_approval { background: var(--rw-warning-bg); color: var(--rw-warning-fg); }

/* The class workspace: an identity header over paired sections. `is-wide` lets one
   module take the whole row of the grid it happens to sit in. */
.ic-class__head { display: grid; gap: var(--rw-space-1); }
.ic-class__lede, .ic-class__meta { color: var(--rw-ink-muted); }
.ic-detail__grid { display: grid; gap: var(--rw-space-6); grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); align-items: start; }
/* THE URGENT PANEL GETS THE WIDE COLUMN (B1 fix round, B-12). The learner's class record is two facts,
   one large-type panel and two links: split into equal halves at 1280, the emergency contact — set
   large for reading aloud to an operator — sat in a column narrower than a phone and broke
   "+45 20 11 22 33" after the 22, while the half beside it held two lines and air. The cause is the
   allocation, not the type: above the width where two columns fit, the second column is twice the
   first, so the facts and the links stack on the left and the panel has the room its type asks for.
   Below that width the shared auto-fit rule above stacks everything, and the number was never
   breaking there. */
@media (min-width: 56rem) {
    .ic-detail__grid--urgent { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
}
.ic-detail__grid > .is-wide { grid-column: 1 / -1; }
/* Side by side, every section starts under its own rule; `.ic-section + .ic-section` left the first
   column bare and dropped the second one a rule and an inset lower. */
.ic-detail__grid > .ic-section { border-block-start: 1px solid var(--rw-line); padding-block-start: var(--rw-panel-inset); }
.ic-detail__list { display: grid; gap: var(--rw-space-2); padding: 0; margin: 0; list-style: none; }
.ic-detail__list > li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); padding-block: var(--rw-space-2); border-block-end: 1px solid var(--rw-line); min-inline-size: 0; }
.ic-detail__day { font-weight: 600; min-inline-size: 6rem; }
.ic-detail__empty, .ic-detail__note { color: var(--rw-ink-muted); }
.ic-detail__subhead { font-size: var(--rw-text-body); font-weight: 700; color: var(--rw-ink-muted); }
.ic-detail__gone { color: var(--rw-ink-muted); display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); }
.ic-term__list { display: grid; gap: var(--rw-space-1); padding: 0; margin: 0; list-style: none; }
.ic-term__list > li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); padding-block: var(--rw-space-1); }
.ic-term__past { margin-block-end: var(--rw-space-3); }
.ic-term__summary { min-block-size: var(--rw-target-min); display: flex; align-items: center; gap: var(--rw-space-2); cursor: pointer; font-weight: 600; list-style: none; }
.ic-term__summary::-webkit-details-marker { display: none; }

/* The record's editors: identity numbers side by side, publication as framed choices. */
.ic-detail__identitygrid { display: grid; gap: var(--rw-field-stack); grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr)); }
.ic-detail__capacitywarn { display: grid; gap: var(--rw-space-2); padding: var(--rw-space-4); background: var(--rw-warning-bg); color: var(--rw-warning-fg); border-inline-start: var(--rw-space-1) solid currentColor; }
/* NO LIST MARGIN INSIDE A GRID: the parent's gap is the rhythm and the browser's 16px block margin
   doubled it — the same fix .ic-revisions carries (B-8). The names and the consequences keep their
   marker with the wizard summary's indent; the dates are a plain stack, as .ic-revisions is. */
.ic-detail__capacitynames, .ic-detail__endcounts { margin: 0; padding-inline-start: var(--rw-space-4); }
.ic-detail__capacitynames { color: inherit; }
.ic-detail__expectation { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); padding-block: var(--rw-space-1); }
.ic-detail__expectsubject { font-weight: 600; }
.ic-detail__expectrow { min-inline-size: 0; }
.ic-detail__expectpace, .ic-detail__expectedby, .ic-detail__expectfoot { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-detail__publish, .ic-publish__about { display: grid; gap: var(--rw-space-4); }
.ic-detail__publishstates, .ic-publish__fields { display: grid; gap: var(--rw-space-2); padding: 0; border: 0; margin: 0; }
.ic-detail__publishlegend { font-weight: 600; padding: 0; }
.ic-detail__publishlede, .ic-detail__publishold, .ic-detail__publishpath, .ic-publish__about { color: var(--rw-ink-muted); }
.ic-detail__publishstate { display: flex; align-items: center; gap: var(--rw-space-3); min-block-size: var(--rw-target-min); padding-inline: var(--rw-space-2); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-control); }
/* The class's publication choices are framed, one to a row; their grid — control, name, and the
   description on its own row beneath the name — is the people-admin sheet's `.ic-publish__field`,
   which this page shares with the masjid's own publication form. */
.ic-detail__publishstates .ic-publish__field, .ic-detail__publish .ic-publish__field { padding: var(--rw-space-2); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-control); max-inline-size: none; }
.ic-detail__publishfields { display: grid; gap: var(--rw-space-2); }
.ic-detail__lifecycle { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
.ic-detail__lifecyclenote { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
/* Ending or deleting a class states its blast radius and waits for the second press. */
.ic-detail__endconfirm { display: grid; gap: var(--rw-space-3); padding: var(--rw-panel-inset); background: var(--rw-danger-bg); color: var(--rw-danger-fg); border: 1px solid currentColor; border-radius: var(--rw-radius-panel); }
.ic-detail__endcounts { font-variant-numeric: tabular-nums; }
.ic-detail__withdraw { display: grid; gap: var(--rw-space-3); padding: var(--rw-space-4); background: var(--rw-danger-bg); color: var(--rw-danger-fg); border-inline-start: var(--rw-space-1) solid currentColor; }
.ic-detail__assign { display: grid; gap: var(--rw-space-3); }
.ic-detail__people { display: grid; gap: var(--rw-space-2); padding: 0; margin: 0; list-style: none; }
/* The name and the facts after it are separate words; Razor drops the space between them. */
.ic-detail__people > li > span:first-child { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--rw-label-gap); min-inline-size: 0; }
.ic-class-description { max-inline-size: var(--rw-reading-measure); white-space: pre-wrap; }

/* Copying a term and editing a schedule both preview their consequences before saving:
   the impact block is information, and a clash inside it is the one that must be read. */
.ic-impact { display: grid; gap: var(--rw-space-3); padding: var(--rw-space-4); background: var(--rw-info-bg); color: var(--rw-info-fg); border-inline-start: var(--rw-space-1) solid currentColor; }
.ic-impact__line { font-weight: 600; }
.ic-impact__note, .ic-impact__homework { font-size: var(--rw-text-small); }
.ic-impact__refusal { font-weight: 600; }
.ic-clash { display: grid; gap: var(--rw-space-2); padding: var(--rw-space-4); background: var(--rw-danger-bg); color: var(--rw-danger-fg); border-inline-start: var(--rw-space-1) solid currentColor; }
.ic-clash__what { font-weight: 600; }
.ic-clash__dates { font-variant-numeric: tabular-nums; display: grid; gap: var(--rw-space-1); margin: 0; padding: 0; list-style: none; }

/* The schedule editor: one row per weekly slot, revisions as a readable history. */
.ic-schedule__lede { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
/* The classes collection: a row's Open never breaks mid-word in a narrow last column ("Ope / n" at
   1280), and an empty term's three parts — which empty, the rollover sentence, the copy — stand apart. */
.ic-table [data-row-action="open"] { white-space: nowrap; }
.ic-classes__emptyterm { display: grid; gap: var(--rw-space-6); }
.ic-schedule__onesession { display: grid; justify-items: start; gap: var(--rw-space-2); max-inline-size: var(--rw-reading-measure); }
.ic-slot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-2) var(--rw-space-3); padding-block: var(--rw-space-2); border-block-end: 1px solid var(--rw-line); }
.ic-slot__to { color: var(--rw-ink-muted); }
.ic-dstnote { display: grid; gap: var(--rw-space-2); padding: var(--rw-space-4); background: var(--rw-info-bg); color: var(--rw-info-fg); border-radius: var(--rw-radius-control); }
.ic-dstnote__head { font-weight: 600; }
.ic-dstnote__pair { font-variant-numeric: tabular-nums; }
.ic-dstnote__foot { font-size: var(--rw-text-small); }
/* No list margin or indent: inside a module body the grid gap is the rhythm, and the browser's 16px
   margin doubled it (B1 rig, doubledRhythm). */
.ic-revisions { display: grid; gap: var(--rw-space-3); margin: 0; padding: 0; list-style: none; }
.ic-revisions__item { display: grid; gap: var(--rw-space-1); padding-block: var(--rw-space-3); border-block-end: 1px solid var(--rw-line); }
.ic-revisions__when { font-weight: 600; font-variant-numeric: tabular-nums; }
.ic-revisions__what { min-inline-size: 0; }
.ic-revisions__counts { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-revisions__override { display: grid; gap: var(--rw-space-2); padding: var(--rw-space-3); background: var(--rw-ground-2); border-radius: var(--rw-radius-control); }
.ic-revisions__overridehead { font-weight: 600; }
.ic-revisions__overridewhy { color: var(--rw-ink-muted); }

/* ── Calendar ───────────────────────────────────────────────────────────────────────── */

/* min-inline-size: 0 because the view strip inside scrolls its own content: without it
   the toolbar's min-content includes the whole strip and forces the page wide — measured
   at 320 under 200% text (operations-matrix-failing.json, calendar week and month). */
.ic-calendar__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-3) var(--rw-space-6); min-inline-size: 0; }
.ic-calendar__nav { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-2); }
.ic-calendar__title { font-size: var(--rw-text-lead); font-weight: 700; min-inline-size: 0; overflow-wrap: anywhere; }
.ic-calendar__lede, .ic-calendar__note, .ic-calendar__empty, .ic-calendar__eventsintro { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
/* A stale period is named, with its retry beside it — never presented as fresh. */
.ic-calendar__stale { padding: var(--rw-space-3) var(--rw-space-4); background: var(--rw-warning-bg); color: var(--rw-warning-fg); border-inline-start: var(--rw-space-1) solid currentColor; }

/* The calendar and what was asked about it, side by side. The stack is the default and
   the split is opted into by a container query keyed on a panel being open, so neither a
   window a container does not have nor a calendar with nothing open produces one — the
   viewport-keyed first draft squeezed seven month columns into a 467px container at 200%
   zoom. The body is first in source order either way. */
.ic-calendar__workspace { container-type: inline-size; display: flex; flex-wrap: wrap; align-items: start; gap: var(--rw-space-6); }
.ic-calendar__body { flex: 1 1 100%; min-inline-size: 0; display: grid; gap: var(--rw-space-4); align-content: start; }
.ic-calendar__companion { flex: 1 1 100%; min-inline-size: 0; }
@container (min-width: 60rem) {
    [data-companion="open"] > .ic-calendar__body { flex: 1 1 30rem; }
    [data-companion="open"] > .ic-calendar__companion { flex: 0 1 24rem; }
}

/* THE WEEK IS THE CALENDAR, and the stacked reading is the default at every width: one
   band per day, every day present, today marked along its own top edge. Only above 768
   does the week spend seven real columns — below that a 7-column grid is seven
   compressed targets, which is exactly what the mobile agenda rule refuses. */
.ic-calendar__grid { min-inline-size: 0; }
.ic-calendar__grid--week { display: grid; gap: var(--rw-space-3); }
.ic-calendar__column { display: grid; align-content: start; gap: var(--rw-space-2); padding: var(--rw-space-3); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); min-inline-size: 0; }
.ic-calendar__column.is-today { border-block-start: var(--rw-space-1) solid var(--rw-accent-border); }
/* An empty active day keeps its column and says "nothing scheduled": an empty Saturday
   in a Saturday masjid is information. It is quieter, never removed. */
.ic-calendar__column.is-empty .ic-calendar__empty { font-size: var(--rw-text-small); }
.ic-calendar__columnhead { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap); font-weight: 600; }
.ic-calendar__date { font-variant-numeric: tabular-nums; }
.ic-calendar__todaymark { padding: 0 var(--rw-label-gap); background: var(--rw-accent-fill); color: var(--rw-accent-ink); border-radius: var(--rw-radius-control); font-size: var(--rw-text-small); font-weight: 700; }
/* The stacked reading is the default: the band's head leads its own row and the entries keep
   the whole width — a genuine agenda, not a squeezed grid. */
.ic-calendar__grid--week .ic-calendar__columnhead { border-block-end: 1px solid var(--rw-line); padding-block-end: var(--rw-space-1); }

/* SEVEN COLUMNS ONLY WHILE A COLUMN CAN HOLD A DAY, AND THAT IS A QUESTION ABOUT THE READER'S
   TEXT SIZE (Task 9, A-6). The viewport media query alone could not answer it: `rem` inside a
   media query resolves against the INITIAL root font size, so a reader at 200% kept the seven
   columns, each 2.5em wide, and the today badge and "Nothing scheduled" spilled out of the last
   one and out of the document — horizontal scrolling at 200%, which 1.4.4 and 1.4.10 forbid and
   this branch names as a standing bar.

   The container query IS the missing half: `em` inside it resolves against the container's own
   font size, so 43em is 688px for a reader at 100% and 1376px at 200%. Measured on the week
   fixture: at 100% the body is 708px at a 769 viewport and 928px at 1280, so both keep the seven
   columns they have today; at 200% it is 666px, so the week returns to the agenda it was designed
   to fall back to. The viewport query stays outside it because below 769 the agenda is the
   reading whatever the container measures. */
@media (min-width: 48.0625rem) {
    .ic-calendar__body { container-type: inline-size; }

    @container (min-width: 43em) {
        .ic-calendar__grid--week { display: flex; align-items: stretch; gap: var(--rw-space-2); }
        .ic-calendar__grid--week .ic-calendar__column { flex: 1 1 0; }
        .ic-calendar__grid--week .ic-calendar__columnhead { border-block-end: 0; padding-block-end: 0; }
        /* Seven columns beside the rail leave a class name about 70px; two 12px insets nested
           inside each other were spending a third of it. */
        .ic-calendar__grid--week .ic-calendar__column { padding: var(--rw-space-2); }
        .ic-calendar__grid--week .ic-calendar__entry { padding: var(--rw-space-2); }
        .ic-calendar__grid--week .ic-calendar__name { font-size: var(--rw-text-small); }
    }
}
/* Only the timetable's unused weekdays give up their columns, only at widths that need
   the room, and today never does. `is-idle` is a fact about the whole loaded window
   under no filter; `is-empty` keeps its column at every width. */
@media (min-width: 48.0625rem) and (max-width: 70rem) {
    .ic-calendar__grid--week .ic-calendar__column.is-idle:not(.is-today) { display: none; }
}

/* One entry, one press: the cell names the class and its start; the range lives in the
   panel beside it. A cancelled session keeps its slot, struck through — its absence is
   the thing a reader needs to notice. A clash carries the mark both of its sides get. */
.ic-calendar__entry {
    display: grid; gap: var(--rw-space-1); justify-items: start; text-align: start;
    min-block-size: var(--rw-target-min); min-inline-size: 0; inline-size: 100%;
    padding: var(--rw-space-2) var(--rw-space-3); background: var(--rw-ground);
    border: 1px solid var(--rw-line); border-radius: var(--rw-radius-control);
    color: var(--rw-ink); font: inherit; cursor: pointer; transition: var(--rw-transition-hand);
}
/* What kind of entry it is, at a glance: a class is filled, homework that falls due is an open
   card with the info edge, an event carries the brass edge. A clash, below, still wins the edge. */
.ic-calendar__entry[data-entry="homework"] { background: var(--rw-surface); border-inline-start: var(--rw-space-1) solid var(--rw-info-ink); }
.ic-calendar__entry[data-entry="event"] { border-inline-start: var(--rw-space-1) solid var(--rw-accent-border); }
.ic-calendar__entry[data-entry="islamic_day"] { border-inline-start: var(--rw-space-1) solid var(--rw-accent-fill); background: color-mix(in srgb, var(--rw-accent-fill) 8%, transparent); }
.ic-calendar__grid--month .ic-calendar__name { font-size: var(--rw-text-small); }
.ic-calendar__entry.is-cancelled .ic-calendar__name { text-decoration: line-through; }
.ic-calendar__entry.is-cancelled { color: var(--rw-ink-muted); }
.ic-calendar__entry.is-clashing { border-inline-start: var(--rw-space-1) solid var(--rw-warning-ink); }
/* A word breaks only when it cannot fit, and then with a hyphen: "begynder" / "e" read as two words. */
.ic-calendar__name { font-weight: 600; min-inline-size: 0; overflow-wrap: break-word; hyphens: auto; }
.ic-calendar__time { font-variant-numeric: tabular-nums; color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-calendar__entry--day { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); }
.ic-calendar__more {
    display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min);
    padding: var(--rw-space-1) var(--rw-space-3); background: transparent; border: 1px dashed var(--rw-control-border);
    border-radius: var(--rw-radius-control); color: var(--rw-accent-text); font: inherit; font-size: var(--rw-text-small);
    cursor: pointer; text-align: start;
}
/* Session chips and the overflow button answer the hand. The longhands leave the inline-start
   meaning edges alone (homework info-ink, event brass, clash warning-ink). No inset, so no composite. */
.ic-calendar__entry:hover:not(:disabled), .ic-calendar__more:hover { background: var(--rw-surface-hover); border-block-color: var(--rw-control-border); border-inline-end-color: var(--rw-control-border); }
/* An entry with no meaning edge, a class session (the commonest), darkens its fourth side too:
   three dark sides and a pale one read as a drawing fault, not as a hover. */
.ic-calendar__entry:not([data-entry="homework"], [data-entry="event"], [data-entry="islamic_day"], .is-clashing):hover:not(:disabled) { border-inline-start-color: var(--rw-control-border); }
.ic-calendar__entry:active:not(:disabled) { translate: 0 1px; }

/* THE MONTH. On a desk a real month: whole weeks as rows, neighbouring days quieter
   rather than blanked. On a phone the cell stops pretending to carry entries: the list
   goes, the count comes, and the day number is a full-width press to the day itself.
   Logical properties only — in Arabic and Urdu this grid begins on the right. */
.ic-calendar__grid--month { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--rw-space-1); }
.ic-calendar__weekday { font-weight: 600; font-size: var(--rw-text-small); color: var(--rw-ink-muted); padding: var(--rw-space-1); min-inline-size: 0; overflow-wrap: anywhere; }
.ic-calendar__daycell { display: grid; align-content: start; gap: var(--rw-space-1); padding: var(--rw-space-1); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-control); min-inline-size: 0; }
.ic-calendar__daycell.is-today { border-block-start: var(--rw-space-1) solid var(--rw-accent-border); }
.ic-calendar__daycell.is-outside { background: var(--rw-ground-2); }
.ic-calendar__daycell.is-outside .ic-calendar__datenum { color: var(--rw-ink-muted); }
/* The day number is the control and the entries are siblings of it, never children. */
button.ic-calendar__date {
    inline-size: 100%; min-block-size: var(--rw-target-min); min-inline-size: 0;
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-label-gap);
    padding: var(--rw-space-1); background: transparent; border: 0; border-radius: var(--rw-radius-control);
    color: var(--rw-ink); font: inherit; cursor: pointer; text-align: start;
}
.ic-calendar__datenum { font-variant-numeric: tabular-nums; font-weight: 600; }
/* The count is the phone's: absent by default, and "3" under a date is information a
   dot never was. */
.ic-calendar__daycount { display: none; padding-inline: var(--rw-label-gap); background: var(--rw-ground-2); border-radius: var(--rw-radius-control); font-variant-numeric: tabular-nums; font-weight: 700; }
.ic-calendar__cellentries { display: grid; gap: var(--rw-space-1); min-inline-size: 0; }
@media (max-width: 40rem) {
    /* Seven columns cannot each keep the 48px press inside 320 (measured: 27.7px per day,
       operations matrix). A month is the canonical two-dimensional layout, so the grid keeps
       seven REAL columns — each wide enough that the day button clears the floor after the
       cell's border and inset — and scrolls in its own container instead of shrinking the
       press or widening the page. Same clearance discipline as the shared strips; the agenda
       view remains the no-scroll reading of the same period. */
    .ic-calendar__grid--month {
        grid-template-columns: repeat(7, minmax(3.75rem, 1fr)); overflow-x: auto;
        padding-block: var(--rw-focus-clearance); scroll-padding-inline: var(--rw-focus-clearance);
    }
    /* Both halves of the swap live behind the same width: the list goes, the count comes. */
    .ic-calendar__daycell .ic-calendar__cellentries { display: none; }
    .ic-calendar__daycell .ic-calendar__daycount { display: inline-grid; place-items: center; }
    .ic-calendar__daycell .ic-calendar__todaymark { position: absolute; inline-size: 1px; block-size: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
    .ic-calendar__daycell { position: relative; }
}

/* The agenda: the whole period as one continuously scrollable, grouped list — the same
   markup on a phone as on a desk, which is what makes it the accessible alternative. */
.ic-calendar__agenda { display: grid; gap: var(--rw-space-6); }
.ic-calendar__agendaday { display: grid; gap: var(--rw-space-2); }
.ic-calendar__dayhead { font-size: var(--rw-text-body); font-weight: 700; border-block-end: 1px solid var(--rw-line); padding-block-end: var(--rw-space-1); }
.ic-calendar__daylist { display: grid; gap: var(--rw-space-2); padding: 0; margin: 0; list-style: none; min-inline-size: 0; }
.ic-calendar__day { display: grid; gap: var(--rw-space-3); justify-items: start; }
.ic-calendar__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-2) var(--rw-space-3); inline-size: 100%; min-inline-size: 0; }
.ic-calendar__row > .ic-calendar__entry { flex: 1 1 16rem; }
.ic-calendar__action { flex: none; }
.ic-calendar__filters { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); }

/* Events sit beside the classes; nothing here notifies, and the form says so. */
.ic-calendar__events { display: grid; padding: 0; margin: 0; list-style: none; }
.ic-calendar__eventrow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-label-gap) var(--rw-space-4); padding-block: var(--rw-row-padding-block); border-block-end: 1px solid var(--rw-line); min-inline-size: 0; }
.ic-calendar__eventwhat { display: grid; gap: var(--rw-space-1); flex: 1 1 16rem; min-inline-size: 0; }
.ic-calendar__eventname { font-weight: 600; }
.ic-calendar__eventmeta { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-calendar__eventform { max-inline-size: var(--rw-form-measure); }
.ic-calendar__eventnote { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }

/* The feed: the privacy sentence leads, the address is selectable, and replacing it
   states its blast radius in devices before the second press. */
.ic-subscribe__blurb, .ic-subscribe__meta { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
.ic-subscribe__warn { padding: var(--rw-space-3) var(--rw-space-4); background: var(--rw-warning-bg); color: var(--rw-warning-fg); border-inline-start: var(--rw-space-1) solid currentColor; max-inline-size: var(--rw-reading-measure); }
.ic-subscribe__url { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); align-items: center; }
.ic-subscribe__url .ic-input { flex: 1 1 16rem; min-inline-size: 0; font-variant-numeric: tabular-nums; }
.ic-subscribe__apps, .ic-subscribe__actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); }
.ic-subscribe__confirm { display: grid; gap: var(--rw-space-3); padding: var(--rw-panel-inset); background: var(--rw-danger-bg); color: var(--rw-danger-fg); border: 1px solid currentColor; border-radius: var(--rw-radius-panel); }

/* The detail panel: a closure says what it is and why, and a conflict names both sides. */
.ic-caldetail__closure { display: grid; gap: var(--rw-space-2); padding: var(--rw-space-4); background: var(--rw-danger-bg); color: var(--rw-danger-fg); border-inline-start: var(--rw-space-1) solid currentColor; }
.ic-caldetail__conflicts { display: grid; gap: var(--rw-space-2); padding: 0; margin: 0; list-style: none; }
.ic-caldetail__note { font-size: var(--rw-text-small); }

/* ── Homework: the learner's hand-in and the teacher's marking queue ────────────────── */

/* One list, one card per piece of work. The card is a surface because a hand-in is a
   small workspace — text box, recorder, file, send — not a row a reader scans past. */
.ic-hw { display: grid; gap: var(--rw-space-4); padding: 0; margin: 0; list-style: none; }
.ic-hw__item { display: grid; gap: var(--rw-space-3); padding: var(--rw-panel-inset); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); min-inline-size: 0; }
/* Overdue is a fact on the card's own edge, not a red wash over a child's work. */
.ic-hw__item.is-overdue { border-inline-start: var(--rw-space-1) solid var(--rw-warning-ink); }
/* A card that needs the learner's answer was flatter than an empty-state card. These are bounded
   cards in a gapped list, not rows, and they never sit inside a card. `.is-overdue`'s warning-ink
   stripe (0,2,0) still wins its edge. The absence form that opens inside a listed absence is
   already inside that card, so it stays flat there; only the standalone planned-absence form lifts. */
.ic-hw__item:where(:not(:is(.ic-surface--card, .ic-surface--panel) *)),
.ic-my-attendance__list > li:where(:not(:is(.ic-surface--card, .ic-surface--panel) *)),
.ic-my-attendance__form:where(:not(:is(.ic-surface--card, .ic-surface--panel, .ic-my-attendance__list) *)) {
    border-color: var(--rw-edge); box-shadow: var(--rw-shadow-rest); --rw-hover-ground: var(--rw-surface-hover);
}
.ic-hw__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); }
.ic-hw__title { font-size: var(--rw-text-lead); font-weight: 700; margin: 0; min-inline-size: 0; overflow-wrap: anywhere; }
.ic-hw__class, .ic-hw__kind { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-hw__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-2); color: var(--rw-ink-muted); margin: 0; }
.ic-hw__due { font-variant-numeric: tabular-nums; }
/* The sentence against the shut-door fear leads quiet and true: still open, marked late. */
.ic-hw__still-open { color: var(--rw-info-fg); background: var(--rw-info-bg); padding: var(--rw-space-3) var(--rw-space-4); border-inline-start: var(--rw-space-1) solid currentColor; margin: 0; }
.ic-hw__instructions { white-space: pre-wrap; max-inline-size: var(--rw-reading-measure); margin: 0; }
.ic-hw__resource { display: grid; gap: var(--rw-space-2); justify-items: start; }
.ic-hw__resource-failed { color: var(--rw-danger-ink); margin: 0; }
.ic-hw__resource-image { max-inline-size: min(100%, var(--rw-form-measure)); block-size: auto; border-radius: var(--rw-radius-control); border: 1px solid var(--rw-line); }
.ic-hw__mark { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-space-3); margin: 0; }
/* The mark reads as a mark: a boxed score in the success tone, not a bare number above the feedback. */
.ic-hw__grade { display: inline-grid; place-items: center; min-inline-size: 2.5rem; min-block-size: 2.5rem; padding-inline: var(--rw-space-2); border-radius: var(--rw-radius-control); background: var(--rw-success-bg); color: var(--rw-success-fg); font-weight: 700; font-size: var(--rw-text-lead); font-variant-numeric: tabular-nums; flex: none; }
.ic-hw__mark { align-items: center; gap: var(--rw-space-3); }
.ic-hw__feedback { max-inline-size: var(--rw-reading-measure); }
/* The teacher's own sentence on the class's homework tab (S-6). Its own line inside the row's
   wrapping flex rather than a fragment of the facts beside the mark: it is the one thing in the
   row somebody wrote by hand, and at 320 it would otherwise push the date off on its own — the
   argument `.ic-attend__reason` already makes one sheet along. Not muted: it is the part of
   marking that teaches. */
.ic-hwmine__feedback { flex-basis: 100%; min-inline-size: 0; max-inline-size: var(--rw-reading-measure); overflow-wrap: anywhere; }
/* Locked and waiting are states of the record, spelled as quiet paired grounds. */
.ic-hw__locked { color: var(--rw-ink-muted); background: var(--rw-ground-2); padding: var(--rw-space-3) var(--rw-space-4); border-radius: var(--rw-radius-control); margin: 0; }
.ic-hw__waiting { color: var(--rw-info-fg); background: var(--rw-info-bg); padding: var(--rw-space-3) var(--rw-space-4); border-radius: var(--rw-radius-control); margin: 0; }
.ic-hw__sent summary { min-block-size: var(--rw-target-min); display: flex; align-items: center; gap: var(--rw-space-2); cursor: pointer; font-weight: 600; list-style: none; }
.ic-hw__sent summary::-webkit-details-marker { display: none; }
/* Says it opens, like the past-sessions list: a chevron that turns, mirrored for RTL. */
.ic-hw__sent summary::before, .ic-term__summary::before { content: ""; inline-size: .45rem; block-size: .45rem; border-block-end: 2px solid currentColor; border-inline-end: 2px solid currentColor; transform: rotate(-45deg); transition: transform var(--rw-motion-fast); flex: none; }
:dir(rtl) :is(.ic-hw__sent summary, .ic-term__summary)::before { transform: rotate(135deg); }
:is(.ic-hw__sent[open] > summary, .ic-term__past[open] > .ic-term__summary)::before { transform: rotate(45deg); }
.ic-hw__sent-text { white-space: pre-wrap; max-inline-size: var(--rw-reading-measure); color: var(--rw-ink-muted); }
.ic-hw__hand-in { display: grid; gap: var(--rw-field-stack); justify-items: start; max-inline-size: var(--rw-form-measure); }
.ic-hw__hand-in .ic-field { inline-size: 100%; }
.ic-hw__record, .ic-hw__audio-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-2); }
/* The live-microphone sentence is status a sighted reader can also not miss. */
.ic-hw__recording { display: inline-flex; flex-wrap: wrap; gap: var(--rw-space-2); color: var(--rw-danger-ink); font-weight: 600; }
.ic-hw__audio-review { display: grid; gap: var(--rw-space-3); padding: var(--rw-space-4); background: var(--rw-ground-2); border-radius: var(--rw-radius-control); inline-size: 100%; }
.ic-hw__file { display: grid; gap: var(--rw-space-2); inline-size: 100%; }
/* "Change my answer" is a button like every other on the card, not a full-width bar. */
.ic-hw__waiting + .ic-btn { justify-self: start; }
/* The upload failure leads with what survived; its actions sit inside the band. */
.ic-hw__upload-failed { display: grid; gap: var(--rw-space-3); justify-items: start; }

/* The teacher's queue lead and the class that did not answer. */
.ic-hwq__lead { font-size: var(--rw-text-lead); font-weight: 600; margin: 0; }
/* ── The learner's own homework and the marking queue (C1) ──────────────────────────────────
   The announce region is the standing live region: it exists before its message, so the banner
   inside it is announced as a change to a region that was already there.
   THE FAILED CLASSES ARE A LIST INSIDE ONE BANNER, not a band each: one interrupt, exact counts,
   and a name plus its own retry per row (B4 RR-4 — a band inside a band is announced twice). */
.ic-hw__announce:empty { display: none; }
.ic-hwq__failed { display: grid; gap: var(--rw-space-2); padding: 0; margin: 0; list-style: none; }
.ic-hwq__failed > li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-3); min-inline-size: 0; }
/* 48px ON THE ELEMENT, NOT THE LABEL (F8 §8), AND THIS ONE WAS 26. `Set and mark homework` is the
   link a teacher presses to reach a class with nothing set yet — the one thing "Your classes" exists
   for — and it was a bare `.ic-detail__note`, measured at 181x26 at both widths and 98x30 in Arabic
   on every staff frame of the queue. Scoped to this list rather than fixed on `.ic-detail__note`
   itself, which a dozen other pages render as prose rather than as a control. */
[data-queue="classes"] .ic-detail__note { display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); }
/* WHAT THE DEVICE SAID NO TO, BESIDE THE CONTROL (F10 §6). Three causes, three next moves, in the
   place the reader pressed — not a page-wide band. Quiet ground, because refusing a microphone is
   an ordinary thing a person does; the unsupported case is not a failure at all and is drawn the
   same way so a child does not read one as louder than the other. */
.ic-hw__device { flex-basis: 100%; margin: 0; color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
/* WHAT A CHILD HANDED IN, LISTED. One row per file, ordered by the ordinal the API keeps, because
   page two is not interchangeable with page one. */
.ic-hw__files { display: grid; gap: var(--rw-space-1); padding: 0; margin: 0; list-style: none; }
.ic-hw__files > li { display: flex; flex-wrap: wrap; gap: var(--rw-label-gap) var(--rw-space-2); min-inline-size: 0; }

/* Setting homework: the compose fields, the counted filter chips, and the review that
   renders the learner's actual row before anything is committed. */
.ic-hwset { display: grid; gap: var(--rw-field-stack); justify-items: start; max-inline-size: var(--rw-form-measure); }
.ic-hwset .ic-field { inline-size: 100%; }
.ic-hwset__row { display: flex; flex-wrap: wrap; gap: var(--rw-field-stack); inline-size: 100%; }
.ic-hwset__row > .ic-field { flex: 1 1 12rem; }
.ic-hwset__what { display: grid; gap: var(--rw-space-2); color: var(--rw-ink-muted); }
.ic-hwset__what p { margin: 0; max-inline-size: var(--rw-reading-measure); }
.ic-hwset__actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-3); }
/* The preview is framed as the learner's card because it is one — same surface, same
   radius — so what reads badly out of context reads badly here first. */
.ic-hwpreview { display: grid; gap: var(--rw-space-2); padding: var(--rw-panel-inset); background: var(--rw-surface); border: 1px solid var(--rw-accent-border); border-radius: var(--rw-radius-panel); inline-size: 100%; }
.ic-hwpreview__eyebrow { color: var(--rw-ink-muted); font-size: var(--rw-text-small); margin: 0; }
.ic-hwpreview__title { font-size: var(--rw-text-lead); font-weight: 700; margin: 0; overflow-wrap: anywhere; }
.ic-hwpreview__instructions { white-space: pre-wrap; max-inline-size: var(--rw-reading-measure); margin: 0; }
.ic-hwpreview__shape { color: var(--rw-ink-muted); font-size: var(--rw-text-small); margin: 0; }
.ic-hwchips { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); }
/* A pressed chip says so in the same forced-colors-safe vocabulary as the switcher —
   and, like the switcher, padding gives the border's 2px back so pressing a filter
   never nudges the chips beside it. */
.ic-chip[aria-pressed="true"] { background: var(--rw-green); color: var(--rw-on-green); border: 3px double var(--rw-control-border); font-weight: 700; padding: calc(var(--rw-space-1) - 2px) calc(var(--rw-label-gap) - 2px); }

/* The marking table and its states: absence of a mark is words, never blank space. */
.ic-hwlist__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-2); color: var(--rw-ink-muted); margin: 0; }
.ic-hwlist__instructions { white-space: pre-wrap; max-inline-size: var(--rw-reading-measure); color: var(--rw-ink-muted); margin: 0; }
.ic-hwlist__learner-view { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
.ic-hwres { display: grid; gap: var(--rw-space-2); justify-items: start; }
/* THE TEACHER'S FILES, AS A LIST: name, what it is and how big, and the one action. A
   worksheet's name is what a learner recognises it by, so it leads and is allowed to wrap. */
.ic-hwfiles { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--rw-space-2); justify-self: stretch; max-inline-size: var(--rw-form-measure); }
.ic-hwfiles__item {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-2) var(--rw-space-3);
    padding: var(--rw-space-2) var(--rw-space-3); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-control);
    background: var(--rw-ground);
}
.ic-hwfiles__name { flex: 1 1 12rem; min-inline-size: 0; overflow-wrap: anywhere; font-weight: 600; }
.ic-hwfiles__meta { font-size: var(--rw-text-small); color: var(--rw-ink-muted); }
.ic-hwfiles__item--open { align-items: start; }
/* A file opened in its row is heard or seen under its name, at the row's full width. */
.ic-hwfiles__item > :is(audio, img, .ic-hw__resource-failed) { flex: 1 1 100%; }
/* MAX-INLINE-SIZE, BECAUSE `.ic-hwres` IS `justify-items: start` AND A FILE INPUT IS 361px WIDE.
   A start-aligned grid item takes its max-content width, and for a row holding an
   `<input type="file">` that is the input's own intrinsic width — so the flex basis and the
   `min-inline-size: 0` below never got to apply, and the marking pass ran the document 57px
   sideways at 320. Measured by slice-b2-shots.mjs, which names the element. */
.ic-hwres__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-3); max-inline-size: 100%; justify-self: stretch; }
.ic-hwres__row .ic-input { flex: 1 1 14rem; min-inline-size: 0; inline-size: auto; }
/* AND THE FILE INPUT ITSELF, because `inline-size: auto` on one is its own intrinsic width and
   Chromium will not shrink it below that: 361px inside a 320px phone, which ran the whole
   document sideways on the marking pass. A whole row of its own at the one-handed width. */
.ic-hwres__row .ic-input[type="file"] { flex: 1 1 100%; inline-size: 100%; max-inline-size: 100%; }
.ic-hwmark { inline-size: 100%; }
.ic-hwmark__none { color: var(--rw-ink-muted); }
.ic-hwmark__evidence { color: var(--rw-ink-muted); font-size: var(--rw-text-small); margin-inline-start: var(--rw-label-gap); }
.ic-hwmark__late { color: var(--rw-warning-fg); background: var(--rw-warning-bg); padding: 0 var(--rw-label-gap); border-radius: var(--rw-radius-control); font-size: var(--rw-text-small); font-weight: 600; margin-inline-start: var(--rw-label-gap); }
.ic-hwmark__given { font-weight: 700; font-variant-numeric: tabular-nums; }
.ic-markpick { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); }
/* The grade buttons are real targets; the chosen one is drawn from its announcement.
   The pressed padding gives the border's 2px back (the switcher's discipline) so a
   grade longer than the 48px floor does not shift the row when chosen. */
.ic-markpick .ic-mark { min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); border: 1px solid var(--rw-control-border); cursor: pointer; font: inherit; justify-content: center; }
.ic-markpick .ic-mark[aria-pressed="true"] { background: var(--rw-green); color: var(--rw-on-green); border: 3px double var(--rw-control-border); font-weight: 700; padding: calc(var(--rw-space-1) - 2px) calc(var(--rw-label-gap) - 2px); }
.ic-modal__where { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-2); color: var(--rw-ink-muted); font-variant-numeric: tabular-nums; margin: 0; }
/* "Overdue" beside a learner's own row: the note's off-state is the warning pair. */
.ic-detail__note--off { color: var(--rw-warning-fg); background: var(--rw-warning-bg); padding: 0 var(--rw-label-gap); border-radius: var(--rw-radius-control); font-size: var(--rw-text-small); font-weight: 600; }

/* ── Qur'an progress: the register, the whole-class sitting, the child's own page ───── */

/* The class chooser, the search and the include-ended switch share one wrapping row. */
.ic-qfilters { display: flex; flex-wrap: wrap; align-items: end; gap: var(--rw-field-stack); }
.ic-qfilters > .ic-field { flex: 1 1 12rem; max-inline-size: 20rem; }
.ic-qfilters > .ic-check { min-block-size: var(--rw-target-min); display: flex; align-items: center; }

/* The to-hear queue: who is waiting, what they will recite, and the way to hear them. */
.ic-qhear { display: grid; gap: var(--rw-space-2); padding: 0; margin: 0; list-style: none; }
.ic-qhear__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-label-gap) var(--rw-space-4); padding-block: var(--rw-row-padding-block); border-block-end: 1px solid var(--rw-line); min-inline-size: 0; }
.ic-qhear__name { font-weight: 600; min-block-size: var(--rw-target-min); display: inline-flex; align-items: center; }
.ic-qhear__what { color: var(--rw-ink-muted); flex: 1 1 14rem; min-inline-size: 0; }

/* The roster is the same markup at every width: each child is a labelled card row whose
   facts wrap as cells — a wide grid at 320 is unreadable and this screen is read standing
   in a hallway, so the label travels with its value instead of sitting in a distant
   heading row. */
.ic-qroster { display: grid; gap: var(--rw-space-3); padding: 0; margin: 0; list-style: none; }
.ic-qroster__row { display: flex; flex-wrap: wrap; align-items: start; gap: var(--rw-space-2) var(--rw-space-6); padding: var(--rw-space-4); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); min-inline-size: 0; }
/* The quiet child leads the eye by the same accent bar flagged rows carry elsewhere. */
.ic-qroster__row.is-stale { border-inline-start: var(--rw-space-1) solid var(--rw-accent-border); }
.ic-qroster__name { flex: 1 1 100%; font-weight: 600; min-block-size: var(--rw-target-min); display: inline-flex; align-items: center; }
.ic-qroster__cell { display: grid; gap: var(--rw-space-1); flex: 1 1 8rem; min-inline-size: 0; align-content: start; }
.ic-qroster__label { color: var(--rw-ink-muted); font-size: var(--rw-text-small); font-weight: 600; }
.ic-qroster__flags { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); flex: 1 1 100%; }
/* THE MARKS EXPLAIN THEMSELVES WHERE THEY CAN BE READ. They used to explain themselves in a
   `title`, which a touch reader never sees and a keyboard reader reaches by accident, and F7
   refuses a hover-only affordance. The legend is a definition list under the rows and the page
   draws only the marks actually present, so it costs nothing on a class that has none. */
.ic-qroster__legend { display: grid; gap: var(--rw-space-2); margin: 0; }
.ic-qroster__legendrow { display: flex; flex-wrap: wrap; gap: var(--rw-space-1) var(--rw-space-3); align-items: baseline; }
.ic-qroster__legendrow dt { font-weight: 600; }
.ic-qroster__legendrow dd { margin: 0; color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }

/* THE CHILDREN WHO DID NOT SAVE, IN ONE PLACE. F8 §7: a teacher who has heard fourteen children
   must be able to see which one did not land, and in a class of twenty-six the failed row is
   off-screen. Rows, not cards: repeated records stay rows inside one bounded surface. */
.ic-qfailed { display: grid; gap: var(--rw-space-3); padding: 0; margin: 0 0 var(--rw-space-4); list-style: none; }
.ic-qfailed__row { display: grid; gap: var(--rw-space-1); padding: var(--rw-space-3) 0; border-block-end: 1px solid var(--rw-line); }
.ic-qfailed__row:last-child { border-block-end: 0; }
.ic-qfailed__name { font-weight: 600; }
.ic-qfailed__why, .ic-qfailed__kept { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }

/* Hearing the whole class: each row carries its own saved/failed state, because each
   child is a separate confirmed call — the kept-result sentence must be readable on the
   row that failed, never summarised away. */
.ic-qclass { display: grid; gap: var(--rw-space-3); padding: 0; margin: 0; list-style: none; }
.ic-qclass__summary { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-3) var(--rw-space-6); padding: var(--rw-space-4); background: var(--rw-ground-2); border-radius: var(--rw-radius-panel); font-variant-numeric: tabular-nums; }
.ic-qclass__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-2) var(--rw-space-4); padding: var(--rw-space-4); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); min-inline-size: 0; }
.ic-qclass__row.is-saved { border-inline-start: var(--rw-space-1) solid var(--rw-success-ink); }
.ic-qclass__row.is-failed { border-inline-start: var(--rw-space-1) solid var(--rw-danger-ink); }
.ic-qclass__name { font-weight: 600; min-block-size: var(--rw-target-min); display: inline-flex; align-items: center; }
.ic-qclass__state { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-2); flex: 1 1 14rem; min-inline-size: 0; }
.ic-qclass__kept { flex-basis: 100%; color: var(--rw-warning-fg); background: var(--rw-warning-bg); padding: var(--rw-space-2) var(--rw-space-3); border-inline-start: var(--rw-space-1) solid currentColor; }
.ic-qclass__actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); }
.ic-qclass__row > .ic-qform { flex-basis: 100%; }

/* The hearing form: shared by the child's page and the whole-class sitting, so the two
   surfaces teach one vocabulary. */
.ic-qform { display: grid; gap: var(--rw-field-stack); justify-items: start; max-inline-size: var(--rw-form-measure); }
.ic-qform .ic-field { inline-size: 100%; }
.ic-qform__row { display: flex; flex-wrap: wrap; gap: var(--rw-field-stack); inline-size: 100%; }
.ic-qform__row > .ic-field { flex: 1 1 12rem; }
.ic-qform__actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-3); }

/* The result keys: thumb-height buttons in a classroom, the chosen one drawn from the
   attribute that announces it, in the forced-colors-safe double border and weight. */

/* FOUR EVEN COLUMNS OR TWO, NEVER THREE-PLUS-ONE — which is F8's recorded departure, arriving on
   the Qur'an strip (B4-21's sibling finding, B4-12). In the board's own words: "the strip is a grid
   rather than the kit's wrapping flex row, so none is ever orphaned (departure, recorded)", and
   `slice-b-brief.md` binds F8 on the whole-class recorder without exception.

   THE FRAMES SHOWED THE ORPHAN AND NOTHING READ IT. `flex: 1 1 10rem` on a wrapping row puts Needs
   work · Developing · Good on line one and MASTERED ALONE ON LINE TWO, grown by `flex-grow` to
   the full width of the three above it — at 1280, on both themes, on the child's record and on the
   sitting. A key twice the size of the other three reads as a different weight of decision, and
   Mastered is precisely the one that must not look heavier. The rig measures target sizes and
   rhythm, not orphaning; the render tests assert the four keys exist and carry `aria-pressed`.

   TWO COUNTS AND ONE BREAKPOINT, NOT `auto-fit` — B2's fix round already paid for this on the
   register (`riwaq-slices.css`): `auto-fit` takes as many columns as fit, so between roughly 420px
   and 550px of strip it takes THREE and strands the fourth, which is the defect reproduced by the
   rule meant to prevent it. The kit's own 40rem is the breakpoint. The flex declarations on the key
   go inert; no word and no target size changes. */
.ic-qresult { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--rw-space-2); inline-size: 100%; }
@media (max-width: 40rem) {
    .ic-qresult { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.ic-qresult__key { flex: 1 1 10rem; min-block-size: var(--rw-target-min); padding: var(--rw-space-2) var(--rw-space-3); background: var(--rw-surface); color: var(--rw-ink); border: 1px solid var(--rw-control-border); border-radius: var(--rw-radius-control); font: inherit; cursor: pointer; }
/* The pressed padding gives the border's 2px back (the switcher's discipline): the key's
   inline size is flex-determined so the row could not shift sideways, but at 200% text the
   content outgrows the 48px block floor and an uncompensated border then moved the row —
   so the key pays the same compensation as the chip and grade-mark rules. */
.ic-qresult__key[aria-pressed="true"] { background: var(--rw-green); color: var(--rw-on-green); border: 3px double var(--rw-control-border); font-weight: 700; padding: calc(var(--rw-space-2) - 2px) calc(var(--rw-space-3) - 2px); }

/* Picking ayahs: one bounded row per range, reordered by buttons, counted beneath. */
.ic-qranges { display: grid; gap: var(--rw-space-4); justify-items: start; padding: 0; border: 0; margin: 0; min-inline-size: 0; inline-size: 100%; }
.ic-qranges__empty { color: var(--rw-ink-muted); margin: 0; }
.ic-qranges__list { display: grid; gap: var(--rw-space-3); padding: 0; margin: 0; list-style: none; inline-size: 100%; }
.ic-qranges__row { display: flex; flex-wrap: wrap; align-items: end; gap: var(--rw-space-2) var(--rw-space-3); padding-block-end: var(--rw-space-3); border-block-end: 1px solid var(--rw-line); min-inline-size: 0; }
.ic-qranges__surah { flex: 2 1 12rem; }
.ic-qranges__ayah { flex: 1 1 5.5rem; }
.ic-qranges__actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); }
.ic-qranges__count { flex-basis: 100%; color: var(--rw-ink-muted); font-size: var(--rw-text-small); font-variant-numeric: tabular-nums; margin: 0; }
.ic-qranges__warning { color: var(--rw-warning-fg); background: var(--rw-warning-bg); padding: var(--rw-space-2) var(--rw-space-3); border-inline-start: var(--rw-space-1) solid currentColor; margin: 0; max-inline-size: var(--rw-reading-measure); }
.ic-qranges__attribution { color: var(--rw-ink-muted); font-size: var(--rw-text-small); margin: 0; }

/* The child's own page: what they have learned first, never a percentage of the Qur'an. */
.ic-qmastery { display: grid; gap: var(--rw-space-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); }
.ic-qmastery__list { max-inline-size: var(--rw-reading-measure); }
.ic-qwork { display: grid; gap: var(--rw-space-3); padding: 0; margin: 0; list-style: none; }
.ic-qwork__item { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-space-2) var(--rw-space-3); padding: var(--rw-space-4); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); min-inline-size: 0; }
/* The ranges are the record: bounded so 2:255 does not push the pill off a phone. */
.ic-qwork__ranges { font-weight: 600; min-inline-size: 0; overflow-wrap: anywhere; }
.ic-qwork__note { flex-basis: 100%; color: var(--rw-ink-muted); margin: 0; max-inline-size: var(--rw-reading-measure); }
/* A prose link inside the note ("Open the text") is a target and pays the same 48px
   floor as every button — the measured lesson Task 2's prose links already paid. */
.ic-qwork__note a { display: inline-flex; align-items: center; min-block-size: var(--rw-target-min); min-inline-size: var(--rw-target-min); }
.ic-qwork__actions { display: flex; flex-wrap: wrap; gap: var(--rw-space-2); flex-basis: 100%; }
.ic-qlatest__ranges { font-weight: 600; overflow-wrap: anywhere; margin: 0; }
.ic-qlatest__feedback { margin: 0; padding: var(--rw-space-3) var(--rw-space-4); background: var(--rw-ground-2); border-inline-start: var(--rw-space-1) solid var(--rw-accent-border); max-inline-size: var(--rw-reading-measure); }

/* The history: every hearing kept, corrections layered rather than overwritten. */
.ic-qrecord { display: grid; gap: var(--rw-space-3); padding: 0; margin: 0; list-style: none; }
.ic-qrecord__entry { display: grid; gap: var(--rw-space-2); padding-block: var(--rw-space-3); border-block-end: 1px solid var(--rw-line); min-inline-size: 0; }
/* A control inside a record entry is a control, not a bar. The entry is a grid, so a button
   stretched the full measure and two of them read as a stack of equal-weight rules rather than
   as one act on one hearing. Looked at in b4-child-record-light-1280.png. */
.ic-qrecord__entry > .ic-btn, .ic-qrecord__entry > .ic-qform .ic-qform__actions { justify-self: start; }
.ic-qrecord__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-3); }
.ic-qrecord__ranges { font-weight: 600; min-inline-size: 0; overflow-wrap: anywhere; }
.ic-qrecord__who { color: var(--rw-ink-muted); font-size: var(--rw-text-small); margin: 0; }
.ic-qrecord__feedback { margin: 0; max-inline-size: var(--rw-reading-measure); }
.ic-qrecord__corrected { color: var(--rw-ink-muted); font-size: var(--rw-text-small); margin: 0; }

/* ── Reports and the export centre ──────────────────────────────────────────────────── */

/* Four questions, four cards, no composite number. Each card is one press to the report
   that defines its own measure. */
.ic-reports__scope { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap); font-weight: 600; margin: 0; }
.ic-reports__narrow { color: var(--rw-ink-muted); font-weight: 400; }
/* THE FOUR QUESTIONS STILL WRAP THREE-AND-ONE AT 1280, AND THAT IS LEFT OPEN DELIBERATELY.
   The A1 review proposed a 14rem floor to get four across; measured, it does not. The content
   column at 1280 is 928px (1280 less the 256px rail and two 48px gutters), and four columns need
   4 × 14rem + 3 × 16px = 944px, so `auto-fit` still resolves to three. Four across needs a floor
   at or below 13.75rem, which is a 220px card holding a four-line sentence — narrower than
   anything else this product sets prose in. The honest alternative is an explicit two-by-two at
   desktop, which is a composition decision and not a value tweak, so it stays with the carried
   forward list rather than being half-done inside a spacing commit. */
.ic-reports__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--rw-space-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); }
/* The question is the grid item's whole height. As a bare `<div>` of anchors the anchors WERE
   the items and stretched; as a list the `<li>` stretches and the anchor inside it did not, so
   four cards in a row ended at four different depths. */
.ic-reports__list > li { display: grid; min-inline-size: 0; }
/* A focusable whole-card link: its edge stays the decorative --rw-line, so a RESTING link cue
   carries the control (a brass underline on the question, non-text 5.36 / 5.23 on surface). The
   rest shadow ships with its mandatory composite. Hover is a tint and an edge only: no lift, and
   no shadow change. */
.ic-reports__question { display: grid; align-content: start; gap: var(--rw-space-2); padding: var(--rw-panel-inset); background: var(--rw-surface); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-panel); color: var(--rw-ink); text-decoration: none; min-inline-size: 0; box-shadow: var(--rw-shadow-rest); transition: var(--rw-transition-hand); }
.ic-reports__question:hover .ic-reports__q, .ic-reports__question:focus-visible .ic-reports__q { text-decoration-thickness: 2px; }
.ic-reports__q { font-size: var(--rw-text-lead); font-weight: 700; }
.ic-reports__q { text-decoration: underline; text-decoration-color: var(--rw-accent-border); text-underline-offset: var(--rw-space-1); }
.ic-reports__question:hover { background: var(--rw-surface-hover); border-color: var(--rw-control-border); }
.ic-reports__question:focus-visible { box-shadow: var(--rw-focus-bands), var(--rw-shadow-rest); }
.ic-reports__measure { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
.ic-reports__figure { font-weight: 600; font-variant-numeric: tabular-nums; }
/* `.ic-reports__foot` stood here and is deleted with the paragraph it styled (Riwaq A1):
   the page closed by saying again what its header had already said, and the one sentence that
   was not a repeat moved into the header, where it is read before the four links rather than
   after them. */

/* The export centre: scope before generation, personal fields marked where they are
   ticked, and the preview scrolling in its own box rather than widening the page. */
.ic-export { display: grid; gap: var(--rw-space-6); }
.ic-export__about { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); margin: 0; }
.ic-export__classes, .ic-export__fields { display: grid; gap: var(--rw-space-1); grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr)); }
.ic-export__class, .ic-export__field { display: flex; align-items: center; gap: var(--rw-space-3); min-block-size: var(--rw-target-min); min-inline-size: 0; }
/* A personal field is visibly different at the point of choosing it, not only in a
   banner afterwards. */
.ic-export__field--personal { color: var(--rw-warning-ink); }
.ic-export__personal { background: var(--rw-warning-bg); color: var(--rw-warning-fg); padding: 0 var(--rw-label-gap); border-radius: var(--rw-radius-control); font-size: var(--rw-text-small); font-weight: 600; }
.ic-export__count { font-size: var(--rw-text-lead); font-weight: 700; font-variant-numeric: tabular-nums; margin: 0; }
.ic-export__previewlabel { font-weight: 600; margin: 0; }
/* The file's own first rows: monospace, LTR pinned (CSV is a Latin-ordered format even
   under an Arabic UI), and scrolling inside its own box — the page never scrolls
   sideways for it. */
.ic-export__preview { overflow-x: auto; background: var(--rw-ground-2); border: 1px solid var(--rw-line); border-radius: var(--rw-radius-control); padding: var(--rw-space-3); direction: ltr; }
.ic-export__preview pre { margin: 0; font-variant-numeric: tabular-nums; }
.ic-export__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rw-space-3); }
.ic-export__audited { color: var(--rw-ink-muted); font-size: var(--rw-text-small); }
/* THE FILES INSIDE A PACKAGE, NAMED BEFORE THE BYTES EXIST (Task 7, F11 sections 3 and 4). Used by the
   export centre's Qur'an package and the learner record's subject-access copy. A filename is an
   identifier and reads left to right inside a mirrored row, so it carries rw-identifier; the
   description is prose and mirrors. Padding and a rule per item are the list's only rhythm. */
.ic-manifest { list-style: none; margin: 0; padding: 0; display: grid; }
.ic-manifest__item { display: grid; gap: var(--rw-space-1); padding-block: var(--rw-space-2); border-block-end: 1px solid var(--rw-line); min-inline-size: 0; }
.ic-manifest__file { font-weight: 600; justify-self: start; overflow-wrap: anywhere; }
.ic-manifest__what { color: var(--rw-ink-muted); max-inline-size: var(--rw-reading-measure); }
.ic-manifest__item--private .ic-manifest__what { color: var(--rw-warning-ink); }

/* ── The learner's own Qur'an record (C1) ─────────────────────────────────────────────────────
   The announce region is the standing live region: it exists before its message, so the banner
   inside it is announced as a change to a region that was already there. Same rule and the same
   shape as `.ic-my-attendance__announce`, which is why it takes no ground of its own — the banner
   inside brings its own.
   A HISTORY ROW IS A SENTENCE (F4 §7), not a table of columns: a grid of rows, each carrying its
   own labels, which is also what F4 §8 asks for at 320. One rhythm per stack — the list gaps and
   the rows carry no block margin. */
.ic-qmine__announce:empty { display: none; }
.ic-qhistory { display: grid; gap: var(--rw-space-3); padding: 0; margin: 0; list-style: none; }
.ic-qhistory__row { display: grid; gap: var(--rw-space-1); padding: var(--rw-space-3) 0; border-block-start: 1px solid var(--rw-line); min-inline-size: 0; }
.ic-qhistory__row:first-child { border-block-start: 0; }
.ic-qhistory__when { color: var(--rw-ink-muted); font-size: var(--rw-text-small); font-variant-numeric: tabular-nums; }
.ic-qhistory__what { font-weight: 600; overflow-wrap: anywhere; }
.ic-qhistory__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--rw-label-gap) var(--rw-space-2); color: var(--rw-ink-muted); }

/* ── F15 · One child's handed-in work ───────────────────────────────────────────────────
   Nine of this component's ten hooks named no rule in any sheet, so the text a child wrote,
   each attachment, and the loading / failed / cleared states all rendered identically at
   browser defaults — on the screen a teacher reads before awarding a mark. A state that looks
   wrong there can be misread as a wrong mark.

   THE THREE THINGS THAT CAN GO WRONG WITH A FILE ARE NOT ONE THING. It can be coming, it can
   be broken, or it can be deliberately gone. Cleared matters most: retention takes the bytes
   at six months and the mark stays valid, so it must not look like a failure.

   `.ic-work__audio` is NOT set here. It already belongs to the grouped native-player rule
   further up this file, which floors it at --rw-target-min on purpose; the board's own
   2.25rem would have been a dead declaration under that floor. */
.ic-work { display: grid; gap: var(--rw-space-4); min-inline-size: 0; }

.ic-work__text {
    margin: 0;
    padding-inline-start: var(--rw-space-4);
    border-inline-start: 2px solid var(--rw-line-accent);
    font-size: var(--rw-text-body);
    line-height: 1.7;
    color: var(--rw-ink);
    max-inline-size: var(--rw-reading-measure);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Each file fails separately, so each is visibly its own thing. */
.ic-work__file { display: grid; gap: var(--rw-space-2); padding-block-start: var(--rw-space-3); border-block-start: 1px solid var(--rw-line); min-inline-size: 0; }
.ic-work__file:first-child { padding-block-start: 0; border-block-start: 0; }

/* The ordinal is part of the work. Isolated, because an un-isolated numeric pair reverses
   under RTL — the same defect as the enrolment ratio, fixed here so it cannot return by
   another door. */
.ic-work__which {
    font-size: var(--rw-text-small);
    letter-spacing: .06em;
    text-transform: uppercase;
    font-weight: 700;
    color: var(--rw-accent-text);
    font-variant-numeric: tabular-nums;
    unicode-bidi: isolate;
}

/* Bounded, so a 4000px phone photo cannot push the marking control off the screen. */
.ic-work__image {
    display: block;
    inline-size: 100%;
    max-inline-size: 26rem;
    max-block-size: 26rem;
    object-fit: contain;
    border: 1px solid var(--rw-line);
    border-radius: var(--rw-radius-control);
    background: var(--rw-ground-2);
}

/* Three different things, said three different ways. Only the failure carries a control,
   being the only one a teacher can act on. */
.ic-work__cleared, .ic-work__loading, .ic-work__failed {
    margin: 0;
    font-size: var(--rw-text-small);
    line-height: 1.5;
    padding: var(--rw-space-2) var(--rw-space-3);
    border-radius: var(--rw-radius-control);
    border-inline-start: 3px solid;
    max-inline-size: var(--rw-reading-measure);
}
/* CLEARED IS ITALIC, and that is the separation rather than the stripe. The stripe alone was
   --rw-line-accent on --rw-ground-2: 1.34:1 by day. The only other difference from `loading`
   was the breathing animation, which prefers-reduced-motion switches off — so for a reader
   who asked for less motion the two states were the same box. Retention took the bytes and
   the mark stands; that is a settled fact, and a settled fact is not a process. */
.ic-work__cleared { background: var(--rw-ground-2); border-color: var(--rw-control-border); color: var(--rw-ink-muted); font-style: italic; }
.ic-work__loading { background: var(--rw-ground-2); border-color: var(--rw-control-border); color: var(--rw-ink-muted); }
.ic-work__failed  { background: var(--rw-danger-bg); border-color: var(--rw-danger-fg); color: var(--rw-danger-fg); font-weight: 600; }

/* Nothing yet — and whether that is late, said without colour alone: the ring fills as well
   as changing hue, and the sentence itself already names the due date either way. */
.ic-work__none {
    display: flex;
    align-items: flex-start;
    gap: var(--rw-space-2);
    font-size: var(--rw-text-body);
    color: var(--rw-ink-muted);
    padding: var(--rw-space-2) var(--rw-space-3);
    background: var(--rw-ground-2);
    border-radius: var(--rw-radius-control);
    border-inline-start: 3px solid var(--rw-line-accent);
}
.ic-work__none::before { content: ""; flex: none; inline-size: .6rem; block-size: .6rem; margin-block-start: .45rem; border-radius: 50%; border: 2px solid var(--rw-control-border); }
.ic-work[data-work="overdue"] .ic-work__none { border-inline-start-color: var(--rw-warning-fg); background: var(--rw-warning-bg); color: var(--rw-warning-fg); }
.ic-work[data-work="overdue"] .ic-work__none::before { background: var(--rw-warning-ink); border-color: var(--rw-warning-ink); }

@media (prefers-reduced-motion: no-preference) {
    .ic-work__loading { animation: rw-breathe 1.6s ease-in-out infinite alternate; }
    @keyframes rw-breathe { from { opacity: .75; } to { opacity: 1; } }
}
