/* CampusIQ visual identity + app shell.
 *
 * Colour, geometry, elevation and typography are CSS variables defined in the
 * :root block below — re-theme the whole product by editing that one block.
 *
 * TWO COLOUR LITERALS REMAIN OUTSIDE :root, and they are known, not overlooked:
 *   .ciq-brand-mark  background: rgba(255, 255, 255, 0.16)
 *   .ciq-soon-tag    background: rgba(255, 255, 255, 0.14)
 * Both are translucent whites layered on the sidebar. .ciq-brand-mark's 0.16 equals
 * no token; .ciq-soon-tag's 0.14 DOES equal --ciq-sidebar-border, and binding it there
 * would encode a coincidence as a relationship, so it is not done. Both are carried as
 * REMAP-TIME items.
 *   ⚑ AMENDED 22-08-2026 (U2). This read "layered on the brand-RED sidebar", which U2
 *     falsified by making the sidebar navy -- a claim in this file invalidated by an
 *     edit to this file. It also read "so neither equals any token's value", which the
 *     very next sentence contradicted: MEASURED, exactly one of the two does.
 *
 * TWO SCALES EXPRESSED IN ONE UNIT WILL COLLIDE BY VALUE, AND EQUAL STRINGS ARE NOT A
 * RELATIONSHIP. That is the general form, and it is here rather than beside any one
 * instance because it recurs at every scale this file gains. The 0.14 above was read as
 * a one-off for a month; the very next edit -- minting the 4px spacing grid -- created
 * FOUR more in a single block:
 *     --ciq-font-xs   0.75rem  ==  --ciq-space-md   (was --ciq-space-sm)
 *     --ciq-font-base 1rem     ==  --ciq-space-lg   (was --ciq-space-md)
 *     --ciq-font-lg   1.25rem  ==  --ciq-space-xl   (was --ciq-space-lg)
 *     --ciq-font-xl   1.5rem   ==  --ciq-space-2xl  (was --ciq-space-xl)
 *   ⚑⚑ AND U2 (22-08-2026) PROVED THE GENERAL FORM ABOVE RATHER THAN ESCAPING IT:
 *      re-valuing the spacing scale did NOT dissolve the four collisions, it MOVED all
 *      four one step down and left the count at four. THE TRAP IS NOT A FIXED SET OF
 *      PAIRS -- it travels with any revaluation of EITHER scale, and the comment naming
 *      it goes stale silently, because nothing binds a comment to the values it lists.
 * None of those pairs is related. It is not carelessness: type and spacing are both
 * lengths in rem, so any two scales over the same unit must eventually share values, and
 * the sharing carries no meaning. BIND BY WHAT A DECLARATION IS -- a padding to a
 * --ciq-space-*, a font-size to a --ciq-font-* -- NEVER BY WHICH TOKEN HAPPENS TO HOLD
 * THE SAME STRING. Binding across scales makes a later move of ONE of them silently
 * repaint whatever borrowed from it.
 *
 * ⚠ docs/UI_Restyle_and_Deck_Gap_Analysis.md A3 — the register the palette remap
 * will be planned from — is a HEX-ONLY scan and lists NEITHER of them. It states
 * three colour literals sit outside :root; there were six. Do not treat A3 as the
 * complete colour inventory.
 *
 * This header previously claimed no colours were hard-coded outside :root. That
 * was false when written (six were) and is corrected here, 29-07-2026.
 *
 * No template carries an inline colour style. Six inline style="" attributes
 * exist across four templates; all are layout or visibility.
 */

:root {
  /* --- Brand: dark navy -----------------------------------------------------
     Ruled 22-08-2026 (U2). ONE navy serves the rail AND the accent: --ciq-sidebar-bg
     and --ciq-topbar-accent both read --ciq-brand, so a second rail navy would be a
     second token for one job. Buttons, links and active states are the same navy;
     nothing else in the product gains an accent.
     ⚑ TWO DECLARATIONS BELOW HOLD THIS COLOUR AS A LITERAL AND FOLLOW NOTHING --
       --ciq-focus-ring (rgba) and --bs-primary-rgb (decimal triple). THEY ARE
       RE-DERIVED BY HAND AT EVERY PALETTE MOVE. A differential report cannot catch
       them: it reports what MOVED and is silent on what should have moved and did
       not, and both would be OMISSIONS. */
  --ciq-brand: #1e3a5f;
  --ciq-brand-dark: #162c47; /* pressed / darkest */
  --ciq-brand-hover: #2a5183; /* sidebar hover surface tint reference */
  --ciq-brand-contrast: #ffffff;

  /* --- Sidebar -------------------------------------------------------------- */
  --ciq-sidebar-width: 208px;
  --ciq-sidebar-bg: var(--ciq-brand);
  --ciq-sidebar-fg: rgba(255, 255, 255, 0.82);
  --ciq-sidebar-fg-strong: #ffffff;
  --ciq-sidebar-fg-muted: rgba(255, 255, 255, 0.5);
  --ciq-sidebar-hover-bg: rgba(255, 255, 255, 0.12);
  --ciq-sidebar-active-bg: rgba(255, 255, 255, 0.18);
  --ciq-sidebar-active-bar: #ffffff;
  --ciq-sidebar-heading: rgba(255, 255, 255, 0.55);
  --ciq-sidebar-border: rgba(255, 255, 255, 0.14);
  /* ^ .ciq-soon-tag holds this exact string and is UNRELATED. Do not bind them --
     the header block at the top of this file states why, and states it once. */

  /* --- Surfaces ------------------------------------------------------------- */
  --ciq-page-bg: #f6f5f3; /* soft warm grey */
  --ciq-card-bg: #ffffff;
  /* ⚑⚑ BOUND TO --ciq-border 17-09-2026 (Gate 1), NOT RE-VALUED. Atrium's card
     border is a full step darker than ours was (#DDE3EA against our #ece9e5) and their
     cards read as more defined for it. That CONTRAST is what is taken; their HUE is not
     -- theirs is cool on a cool ground (#F3F5F8) and ours sits on warm (#f6f5f3), so
     #DDE3EA would put a blue-grey edge on a warm page.
     ⚑⚑⚑ WE ALREADY OWNED THE VALUE: --ciq-border is #e2ded8, our own warm grey at
     Atrium's contrast, already defined and already bound. NOTHING IS ADDED TO :root.
     ⚑ AND THIS TOKEN IS NOT CARD-ONLY DESPITE ITS NAME -- five consumers, three of
     them not cards: .ciq-table th and td (row separators, on every list in the
     product), .ciq-badge--default, and .ciq-wizard-rail. The name is older than its
     reach; the walk reads a table for that reason. */
  --ciq-card-border: var(--ciq-border);

  /* --- Geometry: radius -----------------------------------------------------
     DISCOVERED VALUES, NOT A DESIGNED SCALE. 8 / 9 / 10px — and the 7px left as
     a literal on .ciq-nav-sublink — are four near-duplicates inside a 3px band
     that ACCUMULATED. The sm/md/lg naming is a convenience; it is NOT a claim
     that anyone chose those steps. THE COLLAPSE INTO FEWER VALUES IS NOW RULED
     (29-07-2026), and it belongs to the harmonisation task rather than here:
     re-valuing a token changes what every declaration consuming it RESOLVES TO
     underneath unchanged text, which is a visual change and needs its own gate.
     THE TARGET VALUES ARE RECORDED IN CLAUDE.md AND DELIBERATELY NOT REPEATED
     HERE — stating them beside these definitions would be a false statement
     about the file the comment sits in, and a second site for one claim, one of
     the two aspirational. The type scale, unlike this one, IS defined below:
     defining a scale that has no live token to collide with costs nothing,
     while every radius name here is already bound at a different value.

     Two values are deliberately NOT tokenised: 7px (.ciq-nav-sublink, one site —
     almost certainly drift from its six 8px siblings, but harmonising it here
     would be a visual change) and 50% (.ciq-nav-dot, a circle — a shape, not a
     radius step). A token used once is aliasing.

     Trailing comments name the GrayMatter deck counterpart. They record a
     SEMANTIC counterpart, NOT an equal value — only 10px actually matches — so
     each is a future decision rather than a swap. The deck consolidated its own
     observed 2px–100px range into a scale; this file is the unconsolidated
     truth, and the two must not be confused. */
  --ciq-radius-sm: 8px; /* <- ~--ci-radius-sm (6px, NOT equal) */
  /* ⚑⚑⚑ RE-VALUED 9px -> 10px, 17-09-2026 (Gate 2), AND IT IS A SCALE EXECUTION
     RATHER THAN A BUTTON CHANGE. THREE INDEPENDENT ROUTES LAND ON 10px: Atrium's shipped
     --r-md; this file's OWN radius collapse, ruled 29-07-2026 to 8/10/14/pill and left
     unexecuted; and Gate 2's proposal. md and lg are now aliases at 10px, which IS that
     collapse -- the ruled four values, reached.
     ⚑⚑ IT TRAVELS WITH A BUTTON GATE BECAUSE SPLITTING IT COSTS A SUITE TO MOVE ONE
     PIXEL, not because a button gate is its right home. THREE CONSUMERS MOVE AND ONLY ONE
     IS A BUTTON: .ciq-body .btn, .ciq-body .form-control/.form-select (every form input in
     the product), and .ciq-dash-tile. A button and the input beside it SHOULD share a
     radius -- that is why they move together rather than by accident. */
  --ciq-radius-md: 10px; /* <- --r-md (10px DECLARED; RENDERS 8px -- see below) */
  /* ⚑⚑ CORRECTED 21-09-2026. This read "<- --r-md (10px, EQUAL in atrium.css)".
     TRUE OF THE WRITTEN VALUE AND FALSE OF THE RENDERED ONE: atrium.css:199 sets
     `html { zoom: 0.8 }`, so EVERY Atrium length renders at 80%. Their --r-md as SEEN is
     8px. A reader matching what they saw in the prototype is 25% out on every length.
     ⚑ The same distinction reaches three other comparisons in this file, which are
     left as written and read as DECLARED-value claims: "Atrium's shipped --r-md" in the
     Gate 2 block above; "IDENTICAL" / "MEET EXACTLY" in the Gate 1 block below; and
     "ATRIUM'S GEOMETRY" at --ciq-shadow-md, whose 1/2/3px Atrium renders at .8/1.6/2.4px.
     Record: docs/Atrium_Token_Extraction_And_Mapping_21-09-2026.md §1.3. */
  --ciq-radius-lg: 10px; /* <- --ci-radius-md (10px, equal) */
  /* ⚑⚑⚑ CORRECTED 17-09-2026 (Gate 1). This read "<- ~--ci-radius-lg (16px, NOT
     equal)", comparing us to the GrayMatter DECK. THE SHIPPED PROTOTYPE DISAGREES WITH
     THE DECK: atrium.css declares --r-lg: 14px, IDENTICAL to this. The deck's 16px is
     not what Atrium built. ⚑ SO THE RADIUS COLLISION BRIEFED FOR THIS GATE DOES NOT
     EXIST AT THE CARD -- their 6/10/14/20/999 and our 8/10/14/pill MEET EXACTLY at the
     step a card uses, and nothing is re-ruled. The comparison is kept because the deck
     is a real artefact; what changed is which reference it names. */
  --ciq-radius-xl: 14px; /* <- --r-lg (14px DECLARED, RENDERS 11.2px; the deck says 16px) */
  /* ⚑⚑ CORRECTED 21-09-2026. This read "<- --r-lg (14px, EQUAL in atrium.css; the deck
     says 16px)". Equal as written; under atrium.css:199's zoom 0.8 their card corner
     renders at 11.2px against our 14px. See the --ciq-radius-md note above. */
  --ciq-radius-pill: 999px; /* <- ~--ci-radius-pill (100px, NOT equal) */
  --ciq-card-radius: var(--ciq-radius-xl);

  /* --- Geometry: spacing ----------------------------------------------------
     THE 4px GRID: 2 / 4 / 8 / 12 / 16 / 20 / 24 / 32px. Ruled 21-08-2026 as
     4 / 8 / 12 / 16 / 20 / 24 / 32 / 48px, re-valued 22-08-2026 (U2) as a uniform
     one-step shift down for density.
     ⚑ THE 2px IS A SUB-GRID HAIRLINE AND IS NAMED AS ONE -- it is the ONLY value
       here off the 4px grid, and it is a convention rather than an exception. The
       grid's worth is that it EXCLUDES; a 2px grid would rule out only odd numbers,
       which is a permission and not a grid.
     ⚑ THE SHIFT MINTS EXACTLY ONE NEW NUMBER INTO THE PRODUCT: 2. Every other value
       here already shipped -- the set is the old grid minus 48, plus 2.
     ⚑ AND THE AGGRESSION AT xs (8 -> 4px) IS ENTAILED, NOT CHOSEN: eight DISTINCT
       steps that are multiples of 4 above the hairline leave only 4 and 8 below 12,
       so sm=8 requires xs=4 requires 2xs=2. Freezing xs at 8 freezes sm with it.

     DEFINED AND DELIBERATELY UNBOUND. Nothing references these yet, so this block
     renders nothing and cannot change a screen. Its consumer is the type-and-spacing
     RESTYLE, which is ruled and named -- and T-2's deviation is earned by that
     schedule, not inherited: a token awaiting a SCHEDULED consumer is deliberate, a
     token awaiting nobody is the --bs-primary-rgb shape. IF THAT RESTYLE IS EVER
     ABANDONED, THIS BLOCK IS REVERTED RATHER THAN LEFT STANDING.

     WHY THIS SCALE AND NOT THE ONE THE PROJECTION FLATTERED. Measured over the 125
     non-zero rem spacing occurrences outside :root, three candidates moved 85 (68%),
     75 (60%) and 72 (57%) declarations, with the SAME worst single move of 4.00px in
     all three. So the only thing separating them was COUNT -- and count is the wrong
     objective. The 57% candidate was derived from the nine commonest existing values,
     which optimises to PRESERVE the accumulated distribution: the same act as minting
     a token per accidental value, one level up. A scale exists to REPLACE accidental
     values, and choosing it to minimise how many it replaces defeats its purpose.
     The distribution is a long tail whose 14 rarest values are used once or twice;
     those are accidents, and this scale treats them as accidents.

     THE COST IS NOT HIDDEN AND IT IS NOT THIS BLOCK'S TO PAY: binding these moves
     85 of 125 declarations, 60 of them by at least 1px. That is the RESTYLE, it is
     judged screen by screen with the projection in hand, and it is why this block
     binds nothing today.

     NAMING: -md IS 1rem, THE BASE, with two steps below and three above. The names
     carry no value, deliberately -- naming a step for its pixel count would make the
     scale un-revaluable without renaming every consumer, which is the property the
     palette block relies on.

     ATTENTION -- FOUR OF THESE STRINGS ARE ALREADY IN THIS FILE MEANING SOMETHING
     ELSE, AND BINDING A SPACING DECLARATION TO A TYPE TOKEN BECAUSE THE STRINGS MATCH
     IS THE COINCIDENCE TRAP:
         --ciq-font-xs   0.75rem  ==  --ciq-space-md   (U2 moved this from -sm)
         --ciq-font-base 1rem     ==  --ciq-space-lg   (U2 moved this from -md)
         --ciq-font-lg   1.25rem  ==  --ciq-space-xl   (U2 moved this from -lg)
         --ciq-font-xl   1.5rem   ==  --ciq-space-2xl  (U2 moved this from -xl)
     They are UNRELATED. A padding is not a font size; the equality is arithmetic, not
     meaning. Bind spacing to a --ciq-space-* token, or a later move of the TYPE scale
     silently repaints every margin that borrowed one. */
  --ciq-space-2xs: 0.125rem; /* 2px  -- the sub-grid hairline */
  --ciq-space-xs: 0.25rem;   /* 4px  */
  --ciq-space-sm: 0.5rem;    /* 8px  */
  --ciq-space-md: 0.75rem;   /* 12px */
  --ciq-space-lg: 1rem;      /* 16px */
  --ciq-space-xl: 1.25rem;   /* 20px */
  --ciq-space-2xl: 1.5rem;   /* 24px */
  --ciq-space-3xl: 2rem;     /* 32px */

  /* --- Geometry: elevation --------------------------------------------------
     TWO ENTRIES, because the file contains exactly two elevation shadows. There
     is deliberately no --ciq-shadow-lg: nothing needs one yet, and inventing a
     step to complete a naming pattern is what T-2 exists to stop. It is one line
     whenever a real need appears.

     The focus ring is a STATE indicator, not an elevation step, and already has
     its own token (--ciq-focus-ring, below) bound at both of its sites.

     Deck counterparts differ in HUE, not just weight (the deck is blue-grey
     14,31,58; this palette is warm 40,30,25), so neither is a swap. */
  --ciq-shadow-sm: 0 1px 0 rgba(0, 0, 0, 0.02); /* <- ~--ci-shadow-sm (hue differs) */
  /* <- ~--ci-shadow-md (hue differs) */
  /* ⚑⚑⚑ RE-VALUED 17-09-2026 (Gate 1): ATRIUM'S GEOMETRY, OUR INK -- AND IT IS A
     SCALE CHANGE RATHER THAN A CARD CHANGE, WHICH IS WHY IT IS RECORDED HERE.
     Was: 0 1px 2px rgba(40,30,25,.05), 0 4px 14px rgba(40,30,25,.05) -- a soft, diffuse
     14px spread. Atrium's card uses --shadow-1, the SMALLEST of their three, at
     0 1px 2px rgba(16,24,40,.06), 0 1px 3px rgba(16,24,40,.08): tighter and slightly
     stronger. The tight geometry is the current look and is taken; the cool ink
     (16,24,40) is not, for the same reason as the border above.
     ⚑⚑ THREE CONSUMERS MOVE, AND ONLY ONE IS A CARD: .ciq-body .card, .ciq-panel
     and .ciq-wizard-rail all read --ciq-card-shadow, which reads this. A card-local
     shadow would have spared the other two and left a SECOND VALUE FOR ONE JOB, which
     this file refuses elsewhere -- so the scale moved and the walk reads a panel. */
  --ciq-shadow-md: 0 1px 2px rgba(40, 30, 25, 0.06), 0 1px 3px rgba(40, 30, 25, 0.08);
  --ciq-card-shadow: var(--ciq-shadow-md);

  /* --- Top bar -------------------------------------------------------------- */
  --ciq-topbar-bg: #ffffff;
  --ciq-topbar-accent: var(--ciq-brand);
  --ciq-topbar-accent-height: 3px;
  --ciq-topbar-height: 60px;

  /* --- Text ----------------------------------------------------------------- */
  --ciq-text: #2c2926;
  --ciq-text-muted: #78716a;
  --ciq-heading: #211d1a;

  /* --- Typography -----------------------------------------------------------
     THE TYPE SCALE IS RULED AND DEFINED BELOW (29-07-2026). That REVERSES, by
     ruling rather than by drift, this block's earlier decision that font-size
     would not be tokenised at all. The reasoning it replaces is kept two
     paragraphs down, because the reversal is the part worth reading.

     ⚑⚑ RE-MEASURED 22-08-2026 (U3), AND THE FIRST FIGURE BELOW IS NOT STALE -- IT IS
     RE-SCOPED, WHICH IS WORSE BECAUSE NO INSTRUMENT CATCHES IT. "39 across 20" WAS THE
     TOTAL when nothing was bound; TODAY IT IS THE REMAINDER, and it is STILL EXACTLY
     39 across 20 -- the fourteen bound declarations are all LATER ADDITIONS, so the
     original 39 literals were untouched and the figure survived by the population
     moving out from under it. THE TOTAL IS 53 AND APPEARS NOWHERE IN THE OLD TEXT.
     A STALE FIGURE IS DETECTABLE BY RE-MEASUREMENT; THIS ONE SURVIVES RE-MEASUREMENT.
       font-size       53 total | 14 bound before U3 | 39 literal across 20 values
       letter-spacing  16 total | 6 bound before U3  | 10 literal across 8 values
                       (the old text said 13 across 9 -- wrong on both)
       font-weight     36 total | 36 BOUND | 0 literal -- A CLOSED AXIS since Pass A,
                       and every weight declaration added since was bound at birth.
                       (the old text said 26 across 4, which was Pass A's subject)
     ⚑ AND THE OLD CLOSING CLAIM "it reaches BOTH PRINTED SHEETS" IS WRONG BY ONE:
       .ciq-page-title is carried by admission_memo.html, employee_acknowledgment.html
       AND employee_profile.html -- THREE surfaces -- and by 79 of 89 templates, not
       the 51 this project has quoted since. The superseded text follows verbatim.

     MEASURED LIVE, NOT CARRIED: 39 font-size declarations across 20 distinct
     values, ELEVEN of them used exactly once — this block and CLAUDE.md both
     said TWELVE, and both were wrong. The arithmetic closes on eleven: 11
     singletons plus 28 declarations across 9 repeated values is 39, and 11 + 9
     is 20. Twelve closes on neither. Clusters like 0.85 / 0.875 / 0.88rem
     remain indistinguishable. Also 13 letter-spacing declarations across 9
     values, and 26 font-weight declarations across 4.

     WHAT THE OLD DECISION GOT RIGHT, AND WHY IT NO LONGER APPLIES: twenty
     tokens for twenty values IS aliasing, and a token named for its own value
     carries no information and helps no future remap. That argument defeats a
     one-token-per-value scale, which was what was on the table then. It does
     not defeat a RULED six-step scale, which is what exists now. The old note
     also estimated "changing fourteen of them"; the measured figure against
     the ruled scale is 32 of 34 font-size declarations changing resolved
     value, 4 of them by more than 1px at a 16px root. Different counting
     bases — quote the basis, never a bare number.

     ⚑⚑ SUPERSEDED 22-08-2026: THE SCALE IS BOUND. U3 IS THE "SEPARATE TASK" THE
     PARAGRAPH BELOW NAMES, AND IT HAS RUN -- 33 font-size and 8 letter-spacing
     declarations bound, with .ciq-page-title held back for a ruling taken by LOOKING
     rather than by nearest-step. The paragraph is kept verbatim because it is the
     record of the decision that deferred the binding, and because its last clause is
     the one U3's walk acted on: the pass reaches PRINTED SHEETS, where nothing about
     it is visible on screen.

     THE SCALE IS DEFINED HERE AND DELIBERATELY NOT YET BOUND. Not one of the
     39 / 13 / 26 declarations is rewritten by the task that added these
     tokens. Harmonising them is a SEPARATE task with its own gate and its own
     walkthrough: it moves 41 resolved values, 5 of them perceptibly, and it
     reaches BOTH PRINTED SHEETS, where nothing about it is visible on screen.

     DEFINED-AND-UNUSED IS THE EXACT SHAPE T-2 FORBIDS, and the distinction
     that permits it here is RV-52's own test read in the other direction.
     RV-52 dropped a display-family token because it had NO NAMED FUTURE
     CONSUMER: display versus body "becomes real only when Phase E's design
     document gives headings a different face", a decision nobody had taken.
     These twelve have a named consumer IN A FROZEN BUILD PLAN, arriving in the
     next phase — Phase E's ten screens consume the scale from their first
     line. A token awaiting a SCHEDULED consumer is a deliberate deviation; a
     token awaiting nobody is the --bs-primary-rgb shape.

     DELIBERATELY UNBOUND, THIRTEEN IN ALL: the six font sizes, both new
     tracking steps, all four weights, and --ciq-brand-hover, which was already
     dead before this task. THE LIST MUST SHRINK as Phase E binds them. It is a
     LIST rather than an assertion for a reason — the used-subset-of-defined
     check CANNOT see a dead token, because new definitions only ever enlarge
     the defined set, and the inverse assertion would fail on day one by
     design. A named list is what lets a later reader tell "not yet bound" from
     "orphaned by a deletion".

     EXCLUDED FROM THE SCALE BY RULING, each for a stated reason: three
     icon-glyph sizes (a .bi character sized by font-size is not a type step);
     two brand-lockup sizes (.ciq-brand-mark 0.95rem, .ciq-auth-brand .wordmark
     1.7rem) together with that wordmark's -0.02em tracking; and the memo ID's
     0.04em tracking — the ONLY tracking value that lands on paper, moving
     ~5.8px across a printed document identifier for no benefit on the other
     side. A memo ID is a record, not interface chrome. Recorded so that none
     of these is folded in later by someone who assumes it was missed.

     NO DECK COUNTERPART COMMENT ON ANY OF THE TWELVE, considered rather than
     forgotten: the deck's only typography tokens are its three font families
     and one tracking value, so it has NO size scale and NO weight scale to
     name a counterpart to. Inventing one would be the misreading the radius
     comments above already warn against.

     THERE IS EXACTLY ONE FAMILY TOKEN, AND NEITHER --ciq-font-display NOR
     --ciq-font-mono EXISTS (RV-52, 29-07-2026). The test both fail is the same
     one: A TOKEN THAT NOTHING CONSUMES IS WORSE THAN AN ABSENT ONE, because it
     LOOKS connected and is not — the --bs-primary-rgb shape, where someone sets
     a value at remap time, sees nothing change, and debugs it. mono would also
     have been invented (no monospace face exists anywhere in the product);
     display would merely have been unbound, which is enough on its own.

     Headings inherit this stack today, so display-vs-body is not a distinction
     the product currently makes. It becomes real only when Phase E's design
     document gives headings a different face — and --ciq-font-display gets
     added THEN, together with its binding, as a deliberate visual change with
     its own gate. Binding it here would have added a declaration where none
     exists (this task replaces literals, it does not introduce rules) AND
     pre-empted a decision nobody has taken.

     If Phase E's marks grid wants aligned figures, that is likewise a specific
     decision then, and probably font-variant-numeric rather than a family.

     None of the three tracking tokens has a deck counterpart, and none gets a
     mapping comment: the deck's own --ci-tracking is a BODY letter-spacing
     (-0.003em), whereas 0.06em here is uppercase-kicker tracking and the two
     new steps are a tight display value and a slight one. Pairing any of them
     would mislead. */
  --ciq-font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, sans-serif; /* <- --ci-font-body */
  --ciq-tracking: 0.06em;

  /* The ruled scale. Every token from here to the end of this block is
     DEFINED AND NOT YET USED — see the deliberately-unbound list above. */
  /* ⚑⚑ THE COINCIDENCE TRAP RUNS BOTH WAYS AND THE ONLY SITE-LEVEL WARNING POINTS
     THE OTHER WAY. The ATTENTION block above the SPACING scale warns against binding a
     SPACING declaration to a TYPE token -- C1's direction. U3 walked the opposite one,
     and 11 of its 34 font-size literals landed on a value a --ciq-space-* token also
     holds. A WARNING WRITTEN BY THE PASS THAT NEEDED IT POINTS THE WAY THAT PASS WAS
     WALKING; THE NEXT PASS WALKS THE OTHER WAY AND MEETS NOTHING.
     The four live pairs are named ONCE, in that block, and are deliberately NOT
     repeated here -- four copies of one claim is the defect, not the remedy. Read the
     file header for the general form: BIND BY WHAT A DECLARATION IS. */
  --ciq-font-micro: 0.6875rem; /* 11px — uppercase kickers, table headers, tags */
  --ciq-font-xs: 0.75rem; /* 12px — badges, chips, hints, field labels */
  --ciq-font-sm: 0.875rem; /* 14px — table cells, secondary text, nav, controls */
  --ciq-font-base: 1rem; /* 16px — body anchor; deliberately NOT bound to body */
  --ciq-font-lg: 1.25rem; /* 20px — auth card titles */
  --ciq-font-xl: 1.5rem; /* 24px — page titles, display IDs */
  --ciq-tracking-tight: -0.01em;
  --ciq-tracking-slight: 0.02em;
  --ciq-weight-normal: 400;
  --ciq-weight-medium: 500;
  --ciq-weight-semibold: 600;
  --ciq-weight-bold: 700;

  /* --- Controls -------------------------------------------------------------
     ⚠ REGRESSION RISK — READ BEFORE ANY PALETTE REMAP.

     --ciq-control-border below exists because of the M-C/C5 fix: an unchecked
     tick box drawn in the same near-white as the card edge reads as ABSENT.
     It is THE ONE CONTROL THAT MUST ANNOUNCE ITSELF WHEN EMPTY, which is a
     different requirement from every other border in this file.

     It is bound at exactly one place — .form-check-input:not(:checked):not(
     :indeterminate) — and THAT BINDING MUST SURVIVE ANY REMAP. Deleting the rule
     silently restores Bootstrap's default and reintroduces the defect.

     THE GRAYMATTER TOKEN SET HAS NO EQUIVALENT. It carries --ci-border and
     --ci-border-strong, both keyed to the long input/card edge — neither answers
     the empty-tick-box case. So ANY future palette MUST define a dedicated
     higher-contrast control-outline value. A blind value swap silently
     reintroduces a defect that was already found and fixed once.

     --ciq-status-red-fg / .ciq-required was checked in the same pass and IS
     SAFE (the deck has a clean equivalent) — named here so a later reader knows
     the scan covered it. Note it has THREE consumers, not one: .ciq-badge--red,
     .ciq-form-error and .ciq-required. Changing it for the badge moves the
     mandatory-field asterisk too. */
  --ciq-border: #e2ded8;
  /* M-C/C5: checkbox + radio borders only. A tick box is a small, unfilled
     outline that has to read as PRESENT at a glance, so it needs materially more
     contrast than the long input/card edges --ciq-border draws.
     ⚑ RE-VALUED 22-08-2026 (U2) FROM #a8a099, ON THE WALK, AS A NAMED SINGLE-TOKEN
       CHANGE. #a8a099 was a WARM grey chosen for a maroon brand and it read washed
       out under navy. MEASURED against the white card: #a8a099 = 2.58:1, BELOW the
       3:1 WCAG minimum for a UI component boundary; #8b929c = 3.14:1, above it.
       So the re-value strengthens the very property this token exists for rather
       than merely re-tinting it -- and it is what the block above demands, in its
       own words: "ANY future palette MUST define a dedicated higher-contrast
       control-outline value."
     ⚑ THE SUPERSEDED WORDING SAID: "Bootstrap's own default (rgba(0,0,0,.25)) is a
       cool grey that fights the warm palette; this is the same weight, warmed to
       match." BOTH HALVES ARE FALSIFIED BY U2 -- the palette is no longer warm, and
       this value is no longer warmed. A COOL grey is now the correct neighbour. */
  --ciq-control-border: #8b929c;
  --ciq-input-bg: #ffffff;
  /* Step 5 (28-09-2026): an EDITABLE field is white (the theme's surface), and only a
     field that cannot change is grey. Separate tokens because the theme layer re-values
     --ciq-input-bg to its grey --surface-3 on :root, after this file. */
  --ciq-field-bg: var(--surface, #ffffff);
  --ciq-field-locked-bg: var(--surface-3, #eceff3);
  /* ⚑ THE BRAND AT 25% ALPHA, WRITTEN AS A LITERAL. It follows no token, so it is
     RE-DERIVED BY HAND at every palette move -- and it is the focus ring on EVERY
     focused control on every screen, so forgetting it is not a corner case. */
  --ciq-focus-ring: rgba(30, 58, 95, 0.25);

  --ciq-btn-secondary-bg: #efedea;
  --ciq-btn-secondary-bg-hover: #e4e1dc;
  --ciq-btn-secondary-fg: #4a4540;
  --ciq-btn-secondary-border: #dcd8d2;

  /* --- Status badges (green / amber / red) ---------------------------------- */
  --ciq-status-green-fg: #1f7a3d;
  --ciq-status-green-bg: #e5f4ea;
  --ciq-status-amber-fg: #9a6a12;
  --ciq-status-amber-bg: #fbf1dd;
  --ciq-status-red-fg: #a83029;
  --ciq-status-red-bg: #fbe9e8;
  --ciq-status-neutral-fg: #6b6660;
  --ciq-status-neutral-bg: #edeae5;

  /* --- Print: black on white, INDEPENDENT of the screen palette ---------------
     Added 17-09-2026 (P5). These three are the only colours the printed page is
     allowed to resolve to, and they are DELIBERATELY NOT derived from the brand:
     the whole defect F-1 and F-2 name is that a palette change reached paper by
     default. A print colour that followed --ciq-brand would reproduce it exactly.
     ⚑ ALL THREE ARE BOUND IN THE @media print BLOCK AT THE END OF THIS FILE.
       A print token with no consumer would be the shape RV-56 exists to forbid. */
  --ciq-print-fg: #000000;
  --ciq-print-bg: #ffffff;
  --ciq-print-border: #000000;

  /* ⚑⚑⚑ THE BUTTON TYPE SIZE IS SET AS BOOTSTRAP'S OWN VARIABLE AND NOT AS A
     PROPERTY, AND THAT DISTINCTION IS THE WHOLE OF WHY btn-sm STILL WORKS. Bootstrap 5's
     .btn reads `font-size: var(--bs-btn-font-size)`, and .btn-sm works by OVERRIDING THAT
     VARIABLE. A literal `font-size` on `.ciq-body .btn` (0,2,0) would beat it and kill
     btn-sm's last living effect at its 19 sites, SILENTLY -- nothing in this repository
     reads this file.
     ⚑⚑ AND btn-sm IS ALREADY MOSTLY DEAD FOR EXACTLY THAT REASON: the three LITERALS
     on `.ciq-body .btn` (padding, border-radius, font-weight) already beat its variable
     overrides, so only its font-size still did anything. Set here as a variable, an
     element's own declaration beats this inherited one and btn-sm keeps it.
     ⚑ RESTORING THE OTHER TWO -- converting those literals to variables -- IS
     REGISTERED AND NOT TAKEN: it would make 19 buttons genuinely smaller, which is a real
     visible change and not a values gate's business. */
  --bs-btn-font-size: var(--ciq-font-sm);
  /* Map Bootstrap's primary onto the brand so stray components stay on-palette. */
  --bs-primary: var(--ciq-brand);
  /* ⚑ THE BRAND IN DECIMAL. Bootstrap composes its own alpha variants from this, so
     it cannot read var(--ciq-brand) -- RE-DERIVED BY HAND. It is also outside
     scripts/css_resolve.py's stated coverage ("does not cover ... the --bs-* bridge"),
     so the differential gate is structurally blind to it. */
  --bs-primary-rgb: 30, 58, 95;
  --bs-link-color: var(--ciq-brand);
  --bs-link-hover-color: var(--ciq-brand-dark);
  /* Step 8 (defect 13): Bootstrap 5.3 paints a link from the RGB triple, not from
     --bs-link-color, so without these two every link stayed Bootstrap blue. The
     triples are --ciq-brand and --ciq-brand-dark in decimal, re-derived by hand like
     --bs-primary-rgb above. */
  --bs-link-color-rgb: 30, 58, 95;
  --bs-link-hover-color-rgb: 22, 44, 71;
  --bs-body-color: var(--ciq-text);
}

/* ============================ Base ========================================= */

body.ciq-body {
  background-color: var(--ciq-page-bg);
  color: var(--ciq-text);
  -webkit-font-smoothing: antialiased;
  font-family: var(--ciq-font-body);
}

.ciq-body h1,
.ciq-body h2,
.ciq-body h3,
.ciq-body h4 {
  color: var(--ciq-heading);
  letter-spacing: var(--ciq-tracking-tight);
}

/* ============================ App layout =================================== */

.ciq-app {
  min-height: 100vh;
}

.ciq-main {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

@media (min-width: 992px) {
  .ciq-main {
    margin-left: var(--ciq-sidebar-width);
  }
}

.ciq-content {
  flex: 1 1 auto;
  padding: var(--ciq-space-xl);
  max-width: 1200px;
  width: 100%;
}

/* ============================ Sidebar ===================================== */

.ciq-sidebar {
  background-color: var(--ciq-sidebar-bg);
  color: var(--ciq-sidebar-fg);
  width: var(--ciq-sidebar-width);
}

/* Fixed rail on desktop; the offcanvas variant reuses the same class on mobile. */
.ciq-sidebar-fixed {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 1030;
  display: flex;
  flex-direction: column;
}

.ciq-sidebar-brand {
  display: flex;
  align-items: center;
  gap: var(--ciq-space-xs);
  height: var(--ciq-topbar-height);
  padding: 0 var(--ciq-space-lg);
  color: var(--ciq-sidebar-fg-strong);
  text-decoration: none;
  border-bottom: 1px solid var(--ciq-sidebar-border);
  flex: 0 0 auto;
}

.ciq-sidebar-brand:hover {
  color: var(--ciq-sidebar-fg-strong);
}

.ciq-brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--ciq-radius-sm);
  background: rgba(255, 255, 255, 0.16);
  font-weight: var(--ciq-weight-bold);
  font-size: 0.95rem;
  flex: 0 0 auto;
}

/* Institute logo swapped in at static/branding/logo.png (single branding point). */
.ciq-sidebar-logo {
  height: 30px;
  width: auto;
  max-width: 120px;
  object-fit: contain;
  flex: 0 0 auto;
}

.ciq-brand-word {
  font-weight: var(--ciq-weight-semibold);
  font-size: var(--ciq-font-base);
  line-height: 1.05;
  letter-spacing: var(--ciq-tracking-tight);
}

.ciq-brand-word small {
  display: block;
  font-weight: var(--ciq-weight-normal);
  font-size: var(--ciq-font-micro);
  color: var(--ciq-sidebar-fg-muted);
  letter-spacing: var(--ciq-tracking-slight);
}

.ciq-nav {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--ciq-space-sm) var(--ciq-space-xs) var(--ciq-space-lg);
}

.ciq-nav-heading {
  padding: var(--ciq-space-md) var(--ciq-space-xs) var(--ciq-space-2xs);
  font-size: var(--ciq-font-micro);
  font-weight: var(--ciq-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--ciq-tracking);
  color: var(--ciq-sidebar-heading);
}

.ciq-nav-link {
  display: flex;
  align-items: center;
  gap: var(--ciq-space-sm);
  padding: var(--ciq-space-xs) var(--ciq-space-xs);
  margin: var(--ciq-space-2xs) 0;
  border-radius: var(--ciq-radius-sm);
  color: var(--ciq-sidebar-fg);
  text-decoration: none;
  font-size: var(--ciq-font-sm);
  line-height: 1.2;
  border-left: 3px solid transparent;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.ciq-nav-link:hover {
  background-color: var(--ciq-sidebar-hover-bg);
  color: var(--ciq-sidebar-fg-strong);
}

.ciq-nav-link.is-active {
  background-color: var(--ciq-sidebar-active-bg);
  color: var(--ciq-sidebar-fg-strong);
  border-left-color: var(--ciq-sidebar-active-bar);
  font-weight: var(--ciq-weight-semibold);
}

.ciq-nav-link.is-soon {
  color: var(--ciq-sidebar-fg-muted);
  cursor: default;
}

.ciq-nav-link.is-soon:hover {
  background-color: transparent;
  color: var(--ciq-sidebar-fg-muted);
}

.ciq-nav-ico {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.9;
}

/* NAV-1: the glyph is a vendored Bootstrap Icons font character rendered by an
   <i class="bi bi-*">, sized here (color is inherited via currentColor). */
.ciq-nav-ico .bi {
  font-size: 1rem;
  line-height: 1;
}
.ciq-nav-ico .bi::before {
  vertical-align: middle;
}

.ciq-nav-label {
  flex: 1 1 auto;
}

/* R3/R4 (25-08-2026) — the label half of a group that is ALSO a link. Ten lines, no new
   token: `--ciq-space-sm` is the gap `.ciq-nav-link` already uses, so the anchor's two
   children sit exactly where they sat as direct children of the summary.

   ⚑⚑ M-22 GOVERNS ONE JUDGEMENT AT A TIME — A PALETTE, A NEUTRAL, A TYPE SCALE — AND
   THIS IS NOT ONE. It is the MECHANISM a ruled judgement (R3) requires, and without that
   said here a later reader meets rail styling in a phase that swore off visual change and
   reads it as exactly the fold-in M-22 forbids.

   ⚑⚑ CONTENT-SIZED, NOT STRETCHED, AND THAT IS THE RULING RATHER THAN A DEFAULT.
   `flex: 0 1 auto` keeps the anchor to its own icon and label, so the strip between the
   label and the caret belongs to the SUMMARY and toggles. A stretched anchor would make
   almost the whole row navigate and leave only the caret to toggle — which is R4's
   rejected alternative arriving by the back door.

   ⚑ AND IT NEVER RESCUES THE CARET, STRUCTURALLY: that is `summary::after`, a
   pseudo-element, which an <a> inside the summary cannot contain whatever it is sized to.
   `margin-left: auto` on it then absorbs the free space, which IS the toggle strip. */
.ciq-nav-grouplink {
  display: inline-flex;
  align-items: center;
  gap: var(--ciq-space-sm);
  flex: 0 1 auto;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

.ciq-soon-tag {
  flex: 0 0 auto;
  font-size: var(--ciq-font-micro);
  font-weight: var(--ciq-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--ciq-tracking);
  padding: var(--ciq-space-2xs) var(--ciq-space-2xs);
  border-radius: var(--ciq-radius-pill);
  /* NOT --ciq-sidebar-border, which holds this exact string and means something else.
     One of the two colour literals outside :root, deliberately unbound; the header
     block at the top of this file is where that is stated. */
  background: rgba(255, 255, 255, 0.14);
  color: var(--ciq-sidebar-fg-muted);
}

/* Master Data expandable subsection */
.ciq-nav-group {
  margin: var(--ciq-space-2xs) 0;
}

/* U1 — the collapsible rail group. STRUCTURAL ONLY: no colour, no spacing value and no
   type size is introduced here; every declaration consumes a token that already exists.
   Design values are U2's and the type scale is U3's.

   ⚑ THE MARKER SUPPRESSION IS THE SAME IDIOM `.ciq-perms` AND `.ciq-panel` ALREADY USE —
   two <details> disclosures that shipped before this one. `list-style: none` covers
   Firefox and the modern engines; `::-webkit-details-marker` covers older WebKit. Both
   are needed and neither is redundant. */
details.ciq-nav-group > summary {
  cursor: pointer;
  list-style: none;
}

details.ciq-nav-group > summary::-webkit-details-marker {
  display: none;
}

/* The disclosure caret. Rotates on open; the element is the summary's own ::after, so
   there is no extra markup and nothing for a screen reader to read out. */
details.ciq-nav-group > summary::after {
  content: "";
  margin-left: auto;
  width: 0.4em;
  height: 0.4em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  opacity: 0.55;
}

details.ciq-nav-group[open] > summary::after {
  transform: rotate(45deg);
}

/* ⚑ FORK A(c)'s sub-heading: a non-clickable label above a flat run of leaves, inside an
   open group. It is NOT a second collapse level — no leaf in this rail is ever two clicks
   deep. */
.ciq-nav-subhead {
  padding: var(--ciq-space-2xs) var(--ciq-space-xs) var(--ciq-space-2xs) var(--ciq-space-lg);
  color: var(--ciq-sidebar-fg-muted);
  font-size: var(--ciq-font-micro);
  font-weight: var(--ciq-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--ciq-tracking);
}

.ciq-nav-sub {
  list-style: none;
  margin: var(--ciq-space-2xs) 0 var(--ciq-space-2xs);
  padding: 0 0 0 var(--ciq-space-2xs);
}

/* FOCUS RING CONTAINMENT (U2 walk, 22-08-2026). .ciq-nav carries overflow-y: auto,
   and any overflow-y other than visible forces HORIZONTAL clipping too -- so a focus
   ring drawn outside a rail item's border box is cut by .ciq-nav's padding box. The
   clearance is .ciq-nav's horizontal padding, and U2 halved it from 8px to 4px, at
   which the ring renders as a fragment. WALK-MEASURED BOTH WAYS: whole at 8px on the
   U1 tree, fragmentary at 4px.

   NEITHER PASS OWNS THE CLIPPER -- overflow-y: auto predates both, U1 added the
   focusable <summary> surfaces, U2 removed the clearance. It is fixed HERE on the
   ground that A DEFECT WHICH ONLY MANIFESTS AFTER A PASS IS THAT PASS'S TO CLOSE,
   EVEN WHEN THE MECHANISM IS OLDER.

   A NEGATIVE outline-offset DRAWS THE RING INSIDE THE BORDER BOX, so it cannot reach
   the clip edge at any clearance. Chosen over restoring .ciq-nav's 8px gutter because
   that would re-open a rail density the walk had already approved; this is local to
   the two focusable rail classes and changes no layout.

   ⚑ THE COLOUR IS DELIBERATELY THE BROWSER DEFAULT AND IS NOT SET. app.css declares
     no outline rule anywhere, and --ciq-focus-ring is bound to .btn-primary and the
     form controls ONLY -- a rail item is neither, which is why U2's palette change
     was ruled OUT of this defect. The default ring is two-tone by design and reads on
     the navy rail; binding it to a token would be a new claim, not a fix. */
.ciq-nav-link:focus-visible,
.ciq-nav-sublink:focus-visible {
  outline-offset: -2px;
}

.ciq-nav-sublink {
  display: flex;
  align-items: center;
  gap: var(--ciq-space-xs);
  padding: var(--ciq-space-2xs) var(--ciq-space-xs) var(--ciq-space-2xs) var(--ciq-space-2xl);
  border-radius: 7px;
  color: var(--ciq-sidebar-fg-muted);
  text-decoration: none;
  font-size: var(--ciq-font-sm);
  border-left: 3px solid transparent;
}

.ciq-nav-sublink:hover {
  background-color: var(--ciq-sidebar-hover-bg);
  color: var(--ciq-sidebar-fg);
}

.ciq-nav-sublink.is-active {
  background-color: var(--ciq-sidebar-active-bg);
  color: var(--ciq-sidebar-fg-strong);
  border-left-color: var(--ciq-sidebar-active-bar);
  font-weight: var(--ciq-weight-semibold);
}

.ciq-nav-dot {
  flex: 0 0 auto;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.6;
}

/* ============================ Top bar ===================================== */

.ciq-topbar {
  position: sticky;
  top: 0;
  z-index: 1020;
  display: flex;
  align-items: center;
  gap: var(--ciq-space-sm);
  min-height: var(--ciq-topbar-height);
  padding: 0 var(--ciq-space-lg);
  background: var(--ciq-topbar-bg);
  border-bottom: var(--ciq-topbar-accent-height) solid var(--ciq-topbar-accent);
  box-shadow: var(--ciq-shadow-sm);
}

.ciq-topbar-toggle {
  border: 1px solid var(--ciq-border);
  background: var(--ciq-card-bg);
  border-radius: var(--ciq-radius-sm);
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ciq-text);
}

.ciq-topbar-toggle .bi {
  font-size: 1.25rem;
  line-height: 1;
}

.ciq-topbar-institute {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  min-width: 0;
}

.ciq-topbar-institute .name {
  font-weight: var(--ciq-weight-semibold);
  font-size: var(--ciq-font-base);
  color: var(--ciq-heading);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ciq-topbar-institute .kicker {
  font-size: var(--ciq-font-micro);
  text-transform: uppercase;
  letter-spacing: var(--ciq-tracking);
  color: var(--ciq-text-muted);
}

.ciq-context {
  display: flex;
  align-items: center;
  gap: var(--ciq-space-sm);
  margin-left: auto;
  min-width: 0;
}

/* CI-BUG-004: the identity block may shrink (its name and roles already ellipsise), so
   the Log out button beside it always stays inside the viewport. */
.ciq-personal {
  min-width: 0;
}

.ciq-personal .ciq-identity {
  max-width: 100%;
}

/* CI-BUG-004: from a tablet down the top bar wraps instead of overflowing sideways. */
@media (max-width: 991px) {
  .ciq-topbar-flow {
    flex-wrap: wrap;
    padding: var(--ciq-space-xs) var(--ciq-space-md);
    row-gap: var(--ciq-space-2xs);
  }

  .ciq-topbar-flow .ciq-context {
    flex: 0 1 auto;
  }

  .ciq-topbar-flow .ciq-identity {
    max-width: 50vw;
  }
}

.ciq-context-controls {
  display: flex;
  align-items: center;
  gap: var(--ciq-space-sm);
}

.ciq-field {
  display: flex;
  flex-direction: column;
  gap: var(--ciq-space-2xs);
}

.ciq-field-label {
  font-size: var(--ciq-font-micro);
  font-weight: var(--ciq-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--ciq-tracking);
  color: var(--ciq-text-muted);
  padding-left: var(--ciq-space-2xs);
}

.ciq-campus-label {
  min-width: 11rem;
}

.ciq-campus-label {
  display: inline-flex;
  align-items: center;
  padding: var(--ciq-space-2xs) var(--ciq-space-sm);
  font-weight: var(--ciq-weight-medium);
  font-size: var(--ciq-font-sm);
  background: var(--ciq-page-bg);
  border: 1px solid var(--ciq-border);
  border-radius: var(--ciq-radius-sm);
  min-height: 34px;
}

/* Header identity (E6.1 / E5-L4) — who is acting, and under what. Deliberately the
   same stacked name-over-kicker shape as .ciq-topbar-institute above, so the two
   read as one row of context rather than as a bolted-on badge. Every value is a
   TOKEN: this rule is new, so it consumes the scale from its first line and binds
   nothing that was not already bound (the residual count does not move). */
.ciq-identity {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  min-width: 0;
  text-align: right;
}

.ciq-identity-name {
  font-size: var(--ciq-font-sm);
  font-weight: var(--ciq-weight-semibold);
  color: var(--ciq-heading);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ciq-identity-roles {
  font-size: var(--ciq-font-micro);
  /* Step 8 walk: mixed case, so "Teacher · HoD CSE · Main campus" reads as written. */
  color: var(--ciq-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A login with no role in force is a real state (an assignment revoked mid-session,
   or a user whose only role was deactivated) and it is STATED rather than left
   blank — a blank reads as "roles not shown", which is the opposite of the truth. */
.ciq-identity-roles--none {
  font-style: italic;
}

/* ============================ Cards ======================================= */

.ciq-body .card {
  background: var(--ciq-card-bg);
  border: 1px solid var(--ciq-card-border);
  border-radius: var(--ciq-card-radius);
  box-shadow: var(--ciq-card-shadow);
}

.ciq-body .card-body {
  padding: var(--ciq-space-lg) var(--ciq-space-xl);
}

.ciq-card-title {
  /* Walk #4 (Venkat, 30-09-2026): headings in normal case, never CSS capitals. */
  font-size: var(--ciq-font-sm);
  font-weight: var(--ciq-weight-semibold);
  color: var(--ciq-text-muted);
  margin-bottom: var(--ciq-space-sm);
}

.ciq-page-title {
  font-size: var(--ciq-font-xl);
  font-weight: var(--ciq-weight-semibold);
  margin-bottom: var(--ciq-space-2xs);
}

.ciq-page-sub {
  color: var(--ciq-text-muted);
  margin-bottom: var(--ciq-space-xl);
}

/* Definition-list rows used on the dashboard working-context card */
.ciq-dl {
  display: grid;
  grid-template-columns: minmax(7rem, auto) 1fr;
  gap: var(--ciq-space-xs) var(--ciq-space-md);
  margin: 0;
}

.ciq-dl dt {
  color: var(--ciq-text-muted);
  font-weight: var(--ciq-weight-medium);
  font-size: var(--ciq-font-sm);
}

.ciq-dl dd {
  margin: 0;
  font-weight: var(--ciq-weight-medium);
  font-size: var(--ciq-font-sm);
}

.ciq-perms {
  margin-top: var(--ciq-space-md);
}

.ciq-perms > summary {
  cursor: pointer;
  color: var(--ciq-brand);
  font-size: var(--ciq-font-sm);
  font-weight: var(--ciq-weight-medium);
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: var(--ciq-space-2xs);
}

.ciq-perms > summary::-webkit-details-marker {
  display: none;
}

.ciq-perms[open] > summary {
  margin-bottom: var(--ciq-space-sm);
}

.ciq-perm-list {
  columns: 2;
  gap: var(--ciq-space-lg);
  margin: 0;
  padding-left: var(--ciq-space-md);
  font-size: var(--ciq-font-sm);
  color: var(--ciq-text-muted);
}

/* ===================== Student portal panels (SU-12) ======================= */
/* The five-panel accordion. NATIVE <details>/<summary>, on `.ciq-perms`'s idiom
   directly above — no JS, keyboard-accessible, and mobile-first by construction
   (SU-11/SD-6: this is the one module whose surface is a phone).

   ⚑ B-3's ACCORDION RULING IS FOR THE NAV RAIL AND NOT FOR A CONTENT PANEL. UI §5.4
   says so in terms: cite B-3 for the rail, §3.1 for these. The two share a widget and
   answer different questions, and conflating them is how a content panel acquires the
   rail's Bootstrap collapse and its JS for no reason.

   ⚑ EVERY VALUE IS A TOKEN. No literal colour, no literal radius — the palette remap
   (G-1) must stay a `:root` edit, which is the whole ground the remap ruling rests on. */

.ciq-panels {
  display: flex;
  flex-direction: column;
  gap: var(--ciq-space-sm);
}

.ciq-panel {
  background: var(--ciq-card-bg);
  border: 1px solid var(--ciq-border);
  border-radius: var(--ciq-card-radius);
  box-shadow: var(--ciq-card-shadow);
}

.ciq-panel > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ciq-space-xs);
  padding: var(--ciq-space-md) var(--ciq-space-md);
  font-size: var(--ciq-font-sm);
  font-weight: var(--ciq-weight-semibold);
  color: var(--ciq-heading);
}

.ciq-panel > summary::-webkit-details-marker {
  display: none;
}

/* The disclosure affordance, drawn in text so it needs no icon font and no asset. */
.ciq-panel > summary::after {
  content: "+";
  font-weight: var(--ciq-weight-normal);
  font-size: var(--ciq-font-base);
  color: var(--ciq-text-muted);
}

.ciq-panel[open] > summary::after {
  content: "\2212"; /* minus */
}

.ciq-panel[open] > summary {
  border-bottom: 1px solid var(--ciq-border);
}

.ciq-panel-body {
  padding: var(--ciq-space-md);
}

/* ============================ Buttons ===================================== */

.ciq-body .btn {
  border-radius: var(--ciq-radius-md);
  font-weight: var(--ciq-weight-semibold);
  padding: var(--ciq-space-xs) var(--ciq-space-lg);
}

/* ⚑⚑ ATRIUM'S PRESS FEEDBACK, ADOPTED AS-IS. One declaration, no token, and the only
   thing in this gate a finger rather than an eye reports. */
.ciq-body .btn:active {
  transform: translateY(1px);
}

.ciq-body .btn-primary {
  background-color: var(--ciq-brand);
  border-color: var(--ciq-brand);
  color: var(--ciq-brand-contrast);
}

.ciq-body .btn-primary:hover,
.ciq-body .btn-primary:focus {
  background-color: var(--ciq-brand-dark);
  border-color: var(--ciq-brand-dark);
  color: var(--ciq-brand-contrast);
}

.ciq-body .btn-primary:focus,
.ciq-body .btn-primary:focus-visible {
  box-shadow: 0 0 0 0.2rem var(--ciq-focus-ring);
}

.ciq-body .btn-secondary,
.ciq-body .btn-outline-secondary {
  background-color: var(--ciq-btn-secondary-bg);
  border-color: var(--ciq-btn-secondary-border);
  color: var(--ciq-btn-secondary-fg);
}

.ciq-body .btn-secondary:hover,
.ciq-body .btn-outline-secondary:hover {
  background-color: var(--ciq-btn-secondary-bg-hover);
  border-color: var(--ciq-btn-secondary-border);
  color: var(--ciq-btn-secondary-fg);
}

/* ============================ Form controls =============================== */

.ciq-body .form-control,
.ciq-body .form-select {
  border-color: var(--ciq-border);
  border-radius: var(--ciq-radius-md);
  background-color: var(--ciq-field-bg);
  color: var(--ciq-text);
}

/* Grey only for a field that cannot change; the template says why in a line under it. */
.ciq-body .form-control:disabled,
.ciq-body .form-control[readonly],
.ciq-body .form-select:disabled {
  background-color: var(--ciq-field-locked-bg);
}

.ciq-body .form-select-sm {
  border-radius: var(--ciq-radius-sm);
}

.ciq-body .form-control:focus,
.ciq-body .form-select:focus {
  border-color: var(--ciq-brand);
  box-shadow: 0 0 0 0.2rem var(--ciq-focus-ring);
}

/* ============================ Status badges =============================== */

.ciq-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--ciq-space-2xs);
  font-size: var(--ciq-font-xs);
  font-weight: var(--ciq-weight-semibold);
  padding: var(--ciq-space-2xs) var(--ciq-space-xs);
  border-radius: var(--ciq-radius-pill);
  line-height: 1.2;
}

.ciq-badge--green {
  color: var(--ciq-status-green-fg);
  background: var(--ciq-status-green-bg);
}

.ciq-badge--amber {
  color: var(--ciq-status-amber-fg);
  background: var(--ciq-status-amber-bg);
}

.ciq-badge--red {
  color: var(--ciq-status-red-fg);
  background: var(--ciq-status-red-bg);
}

.ciq-badge--neutral {
  color: var(--ciq-status-neutral-fg);
  background: var(--ciq-status-neutral-bg);
}

/* ============================ Auth screens =============================== */

.ciq-auth-main {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--ciq-space-2xl) var(--ciq-space-md);
}

.ciq-auth-card {
  width: 100%;
  max-width: 26rem;
}

/* The import screen (step 10): room for the list of problems and the summary. */
.ciq-auth-card--wide {
  max-width: 48rem;
}

.ciq-auth-brand {
  text-align: center;
  margin-bottom: var(--ciq-space-xl);
}

.ciq-auth-logo {
  display: block;
  height: 48px;
  width: auto;
  max-width: 180px;
  object-fit: contain;
  margin: 0 auto var(--ciq-space-md);
}

.ciq-auth-brand .wordmark {
  font-size: 1.7rem;
  font-weight: var(--ciq-weight-bold);
  color: var(--ciq-brand);
  letter-spacing: -0.02em;
  line-height: 1.1;
}

.ciq-auth-brand .tagline {
  color: var(--ciq-text-muted);
  font-size: var(--ciq-font-sm);
  margin-top: var(--ciq-space-2xs);
}

.ciq-auth-card .card-body {
  padding: var(--ciq-space-2xl) var(--ciq-space-2xl) var(--ciq-space-2xl);
}

/* ============================ 403 ======================================== */

.ciq-empty {
  text-align: center;
  padding: var(--ciq-space-3xl) var(--ciq-space-md);
}

/* ==================== Config screens (Phase 4) =========================== */

/* List toolbar: search + filter on the left, primary action on the right. */
.ciq-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--ciq-space-sm);
  margin-bottom: var(--ciq-space-md);
}

.ciq-toolbar .ciq-toolbar-spacer {
  margin-left: auto;
}

.ciq-toolbar .form-control,
.ciq-toolbar .form-select {
  min-width: 12rem;
}

/* 10b-1b walk (A3): a read-only step (the Campus box, a step with one value) is as wide
   as its text, so a long campus name is never cut off. `size` is set to the text's length
   for a browser without field-sizing. */
.form-control.ciq-fixed,
.ciq-form-field input[type="text"].form-control.ciq-fixed[readonly] {
  field-sizing: content;
  width: auto;
  max-width: calc(100vw - 2rem);
}

/* 10b-1b walk (B4): every Evaluation Pattern card's columns share one set of widths, so
   the cards line up. */
.ciq-sc-table { table-layout: fixed; }
.ciq-sc-table col.ciq-sc-type { width: 30%; }
.ciq-sc-table col.ciq-sc-num { width: 12%; }
.ciq-sc-table col.ciq-sc-fold { width: 16%; }
.ciq-sc-table col.ciq-sc-act { width: 18%; }
.ciq-sc-table .form-control,
.ciq-sc-table .form-select { width: 100%; min-width: 0; }

/* 10b-1b walk (B1): Reporting Positions' Replace / Assign opens a small box on its row. */
.ia-post-change { display: inline-block; vertical-align: top; margin-right: 0.25rem; }
.ia-post-change > summary { list-style: none; }
.ia-post-change > summary::-webkit-details-marker { display: none; }
.ia-post-change .ts-wrapper { width: 20rem; }

/* 10b-1b re-walk (F2): a green note on the row just saved, and Mark Correction's form
   opened in place under its student's row. */
.ciq-success {
  color: var(--ciq-status-green-fg);
  background: var(--ciq-status-green-bg);
  border-radius: var(--ciq-radius-sm, 6px);
  padding: var(--ciq-space-xs) var(--ciq-space-sm);
}
.ep-correction-box {
  border-left: 3px solid var(--ciq-status-green-fg);
  padding: var(--ciq-space-xs) var(--ciq-space-sm);
}
.ep-correction-reason { flex: 1 1 20rem; }
.ep-corrected-only { display: inline-flex; gap: 0.4rem; align-items: center; align-self: flex-end; }

/* 10b-1b re-walk (F10): the "Link an existing login" box is as wide as the card's other
   fields, so its hint is never cut off. */
.ia-link-login .ciq-person-field,
.ia-link-login .ts-wrapper { min-width: 22rem; }

/* 10b-1b re-walk (F11): Room Requests' remarks box shows its whole hint. */
.ciq-inline-form .form-control.hs-remarks { min-width: 15rem; }

/* Phase 3 (08-10-2026): a column as narrow as its content (a tick box, a short word). */
.ciq-col-narrow { width: 1%; white-space: nowrap; }
/* Venkat, 08-10-2026: a code or a date never breaks across lines in a list. */
.ciq-nowrap { white-space: nowrap; }

/* Step 7 walk: a filter bar that keeps many narrow filters on one row (the Exams list's
   six), sharing the width; below a laptop width it wraps as usual. */
@media (min-width: 992px) {
  .ciq-toolbar--compact {
    flex-wrap: nowrap;
  }
  .ciq-toolbar--compact .ciq-form-field {
    flex: 1 1 0;
    min-width: 0;
  }
  .ciq-toolbar--compact .form-control,
  .ciq-toolbar--compact .form-select {
    min-width: 0;
    width: 100%;
  }
}

/* Workflow pass gate 1 (T3). `ciq-filter-row` sat on 12 filter forms in 8 templates with no
   rule in any stylesheet, so each select ran the full card width and a four-filter cohort
   picker stacked down the page. It is laid out as `.ciq-toolbar` is. Two child shapes use
   it, a `<label class="ciq-field">` wrapping its select, and a bare label beside its select
   (Detention), and both sit in one wrapping row. How it reads is a walk reading: no test
   reads rendered CSS. */
.ciq-filter-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--ciq-space-sm);
}

.ciq-filter-row .form-control,
.ciq-filter-row .form-select {
  width: auto;
  min-width: 12rem;
}

/* Data table shared by every list screen. */
.ciq-table {
  width: 100%;
  border-collapse: collapse;
}

.ciq-table th {
  text-align: left;
  font-size: var(--ciq-font-micro);
  font-weight: var(--ciq-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--ciq-tracking);
  color: var(--ciq-text-muted);
  padding: var(--ciq-space-xs) var(--ciq-space-xs);
  border-bottom: 1px solid var(--ciq-card-border);
  white-space: nowrap;
}

.ciq-table td {
  padding: var(--ciq-space-xs) var(--ciq-space-xs);
  border-bottom: 1px solid var(--ciq-card-border);
  font-size: var(--ciq-font-sm);
  vertical-align: middle;
}

.ciq-table tbody tr:last-child td,
.ciq-table tr:last-child td {
  border-bottom: none;
}

.ciq-table-scroll {
  overflow-x: auto;
}

/* Inactive / discontinued rows stay visible but greyed. */
.ciq-row-inactive td {
  color: var(--ciq-text-muted);
  background: var(--ciq-page-bg);
}

/* Filter tabs (Active / Inactive / All) — a view over one list, not navigation. */
.ciq-filter-tabs {
  display: flex;
  gap: var(--ciq-space-2xs);
  margin-bottom: var(--ciq-space-sm);
}

.ciq-filter-tab {
  padding: var(--ciq-space-2xs) var(--ciq-space-sm);
  border-radius: var(--ciq-radius-pill);
  font-size: var(--ciq-font-sm);
  color: var(--ciq-text-muted);
  text-decoration: none;
  border: 1px solid transparent;
}

.ciq-filter-tab:hover {
  background: var(--ciq-page-bg);
  color: var(--ciq-text);
}

.ciq-filter-tab--on,
.ciq-filter-tab--on:hover {
  background: var(--ciq-brand);
  border-color: var(--ciq-brand);
  color: var(--ciq-brand-contrast);
}

.ciq-code {
  font-variant-numeric: tabular-nums;
  font-weight: var(--ciq-weight-semibold);
  letter-spacing: var(--ciq-tracking-slight);
}

.ciq-cell-actions {
  display: flex;
  gap: var(--ciq-space-xs);
  align-items: center;
  white-space: nowrap;
}

.ciq-cell-actions .btn {
  padding: var(--ciq-space-2xs) var(--ciq-space-sm);
  font-size: var(--ciq-font-xs);
}

/* Drag handle for reorderable rows. */
.ciq-drag-handle {
  cursor: grab;
  color: var(--ciq-text-muted);
  user-select: none;
  font-size: 1rem;
  line-height: 1;
}

.ciq-row-dragging {
  opacity: 0.5;
}

/* Neutral pill variant used for the "System default" tag. */
.ciq-badge--default {
  color: var(--ciq-text-muted);
  background: var(--ciq-page-bg);
  border: 1px solid var(--ciq-card-border);
}

/* Inline scope note shown to campus-scoped users on read-only screens. */
.ciq-note {
  display: flex;
  gap: var(--ciq-space-xs);
  align-items: flex-start;
  padding: var(--ciq-space-sm) var(--ciq-space-md);
  margin-bottom: var(--ciq-space-md);
  background: var(--ciq-status-neutral-bg);
  color: var(--ciq-status-neutral-fg);
  border-radius: var(--ciq-radius-lg);
  font-size: var(--ciq-font-sm);
}

/* Hard-warning banner for a first-time code change. */
.ciq-warn {
  padding: var(--ciq-space-sm) var(--ciq-space-md);
  margin-bottom: var(--ciq-space-md);
  background: var(--ciq-status-amber-bg);
  color: var(--ciq-status-amber-fg);
  border-radius: var(--ciq-radius-lg);
  font-size: var(--ciq-font-sm);
}

.ciq-warn strong {
  font-weight: var(--ciq-weight-bold);
}

/* "Nothing is wrong" and "it is done": the import screen's check and summary. */
.ciq-ok {
  padding: var(--ciq-space-sm) var(--ciq-space-md);
  margin-bottom: var(--ciq-space-md);
  background: var(--ciq-status-green-bg);
  color: var(--ciq-status-green-fg);
  border-radius: var(--ciq-radius-lg);
  font-size: var(--ciq-font-sm);
}

/* Form validation message. */
.ciq-form-error {
  padding: var(--ciq-space-sm) var(--ciq-space-md);
  margin-bottom: var(--ciq-space-md);
  background: var(--ciq-status-red-bg);
  color: var(--ciq-status-red-fg);
  border-radius: var(--ciq-radius-lg);
  font-size: var(--ciq-font-sm);
}

/* Two-column form field grid on the create/edit cards. */
.ciq-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--ciq-space-md) var(--ciq-space-md);
}

.ciq-form-field {
  display: flex;
  flex-direction: column;
  gap: var(--ciq-space-2xs);
}

.ciq-form-field label {
  font-size: var(--ciq-font-xs);
  font-weight: var(--ciq-weight-semibold);
  color: var(--ciq-text-muted);
}

/* Step 8 walk: field widths by kind, one rule. Percentages, marks, counts and capacities
   (number boxes) about 8 characters; dates, times and codes about 12; names and one-line
   reasons wider; only descriptions and addresses (textareas) take the full row. The
   padding is added so the characters themselves get the width named. */
.ciq-form-field input[type="number"] {
  max-width: calc(8ch + 2rem);
}
.ciq-form-field input[type="date"],
.ciq-form-field input[type="time"],
.ciq-form-field input[name="code"],
.ciq-form-field input[name$="_code"] {
  max-width: calc(12ch + 2.5rem);
}
.ciq-form-field input[type="text"]:not([name="code"]):not([name$="_code"]),
.ciq-form-field input[type="email"],
.ciq-form-field input[type="tel"] {
  max-width: calc(40ch + 2rem);
}
.ciq-form-field textarea {
  max-width: none;
}

/* Step 8 walk: the Campus Admin's timetable -- equal day columns that fit the screen. */
#tt-grid-table {
  table-layout: fixed;
  width: 100%;
}
#tt-grid-table th:first-child {
  width: 4rem;
}
#tt-grid-table td {
  overflow: hidden;
}
#tt-grid-table .form-select-sm {
  width: 100%;
  min-width: 0;
  font-size: var(--ciq-font-xs);
}

/* Checkbox/radio border contrast (M-C / C5). An unchecked box drawn in the same
   near-white as the card edges reads as absent; this is the one control that must
   announce itself when EMPTY. --ciq-control-border exists for exactly this. */
.form-check-input:not(:checked):not(:indeterminate) {
  border-color: var(--ciq-control-border);
}

/* Step 8 (defect 13): a ticked box or chosen radio is painted the brand, not Bootstrap
   blue. Bootstrap sets these as literals on :checked, so the bridge cannot reach them. */
.form-check-input:checked {
  background-color: var(--ciq-brand);
  border-color: var(--ciq-brand);
}

/* Mandatory-field marker (M-C / D5). The single place the asterisk is styled —
   labels write <span class="ciq-required">*</span>, never a bare glyph. */
.ciq-required {
  color: var(--ciq-status-red-fg);
  margin-left: var(--ciq-space-2xs);
}

.ciq-form-field .form-check {
  margin-top: var(--ciq-space-2xs);
}

.ciq-form-actions {
  display: flex;
  gap: var(--ciq-space-xs);
  margin-top: var(--ciq-space-md);
}

.ciq-hint {
  font-size: var(--ciq-font-xs);
  color: var(--ciq-text-muted);
}

/* ==================== Programmes + Setup Wizard (Phase 5) ================= */

/* List/page header: title on the left, primary action on the right. */
.ciq-page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--ciq-space-md);
  flex-wrap: wrap;
}

/* Wizard: fixed step rail + the active step's panel. */
.ciq-wizard {
  display: grid;
  grid-template-columns: minmax(190px, 240px) 1fr;
  gap: var(--ciq-space-lg);
  align-items: start;
}

@media (max-width: 767px) {
  .ciq-wizard {
    grid-template-columns: 1fr;
  }
}

.ciq-wizard-rail {
  background: var(--ciq-card-bg);
  border: 1px solid var(--ciq-card-border);
  border-radius: var(--ciq-card-radius);
  box-shadow: var(--ciq-card-shadow);
  padding: var(--ciq-space-sm);
}

.ciq-wizard-rail ol {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: none;
}

.ciq-wizard-step a,
.ciq-wizard-step span {
  display: block;
  padding: var(--ciq-space-xs) var(--ciq-space-sm);
  border-radius: var(--ciq-radius-sm);
  font-size: var(--ciq-font-sm);
  color: var(--ciq-text);
  text-decoration: none;
  border-left: 3px solid transparent;
}

.ciq-wizard-step a:hover {
  background: var(--ciq-page-bg);
}

.ciq-wizard-step.is-active a,
.ciq-wizard-step.is-active span {
  background: var(--ciq-status-neutral-bg);
  border-left-color: var(--ciq-brand);
  font-weight: var(--ciq-weight-semibold);
}

.ciq-wizard-step.is-disabled span {
  color: var(--ciq-text-muted);
  cursor: default;
}

.ciq-wizard-step.is-disabled em {
  font-style: normal;
  font-size: var(--ciq-font-micro);
  text-transform: uppercase;
  letter-spacing: var(--ciq-tracking);
  color: var(--ciq-text-muted);
}

/* Inline per-item action form (enable/disable, discontinue/re-enable). */
.ciq-inline-form {
  display: inline-flex;
  align-items: center;
  gap: var(--ciq-space-xs);
  margin: 0;
}

.ciq-inline-form .form-control,
.ciq-inline-form .form-select {
  width: auto;
  min-width: 8.5rem;
  padding: var(--ciq-space-2xs) var(--ciq-space-xs);
  font-size: var(--ciq-font-sm);
}

.ciq-inline-form .btn {
  padding: var(--ciq-space-2xs) var(--ciq-space-sm);
  font-size: var(--ciq-font-xs);
}

/* Two-pane transfer layout (Section Assignment, Teachers UI §2). Stacks on
   narrow screens; each pane is an ordinary card. */
.ciq-two-pane {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ciq-space-md);
  align-items: start;
}

@media (max-width: 900px) {
  .ciq-two-pane {
    grid-template-columns: 1fr;
  }
}

/* A row for a record that is retained but inactive (e.g. the history trail of a
   student who has left). Greyed, never hidden. */
.ciq-row--muted {
  color: var(--ciq-text-muted);
}

/* Step 8 walk: Subject Allotment, one row per subject -- teacher names with a small ×,
   and the "+ Add teacher" picker in the same row. */
.ta-teachers {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ciq-space-xs);
}
/* 10b-1b walk (A9): the names on line one; on line two "Add teacher [picker] for batch
   [Choose batch] [Add]", the picker one width on every row, nothing wrapping inside. */
.ta-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ciq-space-xs) var(--ciq-space-sm);
}
.ta-teacher {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ciq-space-2xs);
  width: 16rem;
  padding: var(--ciq-space-2xs) var(--ciq-space-sm);
  border: 1px solid var(--ciq-border);
  border-radius: var(--ciq-radius-pill);
  background: var(--ciq-page-bg);
}
.ta-remove {
  display: inline;
  margin: 0;
}
.ta-remove-btn {
  padding: 0 var(--ciq-space-2xs);
  line-height: 1;
  text-decoration: none;
  color: var(--ciq-text-muted);
}
.ta-add {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--ciq-space-xs);
}
.ta-add-label {
  white-space: nowrap;
  font-size: var(--ciq-font-sm);
}
.ta-add .ts-wrapper {
  flex: 0 0 20rem;
  width: 20rem;
  min-width: 0;
}
.ta-add .ta-batch {
  flex: 0 0 auto;
  min-width: 9rem;
  width: auto;
}
.ta-add .btn {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Step 8: the class whose register is open, on the teacher's list of today's classes. */
.ciq-row--selected td {
  background: var(--ciq-page-bg);
  font-weight: 600;
}

/* Section chips (Institute Administration §5.9). */
.ciq-chip-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ciq-space-xs);
  padding: var(--ciq-space-xs) 0 var(--ciq-space-xs);
}

.ciq-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--ciq-space-xs);
  padding: var(--ciq-space-2xs) var(--ciq-space-xs);
  border-radius: var(--ciq-radius-pill);
  font-size: var(--ciq-font-sm);
  font-weight: var(--ciq-weight-semibold);
  color: var(--ciq-status-neutral-fg);
  background: var(--ciq-status-neutral-bg);
  border: 1px solid transparent;
}

.ciq-chip--off {
  opacity: 0.55;
  text-decoration: line-through;
}

.ciq-chip--ghost {
  background: transparent;
  border: 1px dashed var(--ciq-status-neutral-fg);
  cursor: pointer;
  font-weight: var(--ciq-weight-medium);
}

.ciq-chip-add {
  margin: 0;
}

.ciq-chip form {
  display: inline;
  margin: 0;
}

.ciq-chip-btn {
  border: none;
  background: transparent;
  padding: 0;
  font-size: var(--ciq-font-xs);
  color: var(--ciq-brand);
  cursor: pointer;
}

/* Multi-select scope groups on the document-requirement form. */
.ciq-check-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ciq-space-2xs) var(--ciq-space-md);
  padding: var(--ciq-space-xs) 0;
}

.ciq-check-group .form-check {
  min-width: 9rem;
}

/* --- Onboarding wizard (Admission Process §5) --- */
#wizard-rail .btn.is-active {
  background: var(--ciq-brand);
  border-color: var(--ciq-brand);
  color: var(--ciq-brand-contrast);
}

.ciq-yesno {
  display: inline-flex;
  align-items: center;
}

.ciq-step .ciq-card-title {
  margin-bottom: var(--ciq-space-sm);
}

/* --- Admission memo (§5.1): a clean sheet; print hides the app shell. --- */
.ciq-memo {
  max-width: 52rem;
  margin: 0 auto;
}

.ciq-memo .ciq-memo-id {
  font-size: var(--ciq-font-xl);
  font-weight: var(--ciq-weight-bold);
  letter-spacing: 0.04em;
  color: var(--ciq-brand);
}

/* ==================== Exam Process (Phase E1) ============================= */

/* THE FIRST CONSUMER OF THE TYPE SCALE (b08e146 / 4f04997). Every declaration in
   this block resolves through a token; nothing here is a literal.

   NEW RULES ONLY — no declaration above this line was rewritten. Harmonising the
   existing 39 font-size declarations to the scale is Task B, a deliberate visual
   change with its own gate, and smuggling it into a screen phase is precisely what
   that gate exists to prevent. `.ciq-table td` keeps its 0.9rem.

   RV-58 — WHY THE GRID GETS ITS OWN SIZES. Bootstrap's unwritten 16px body leaves a
   `.ciq-table` cell at 0.9rem (14.4px) while a `.form-control` inside that cell is
   1rem (16px). The Exam Type manager and Exam Setup are both grids of exactly that:
   the operator reads a subject or type name and types a number beside it. Shipping
   those at visibly different sizes, in the phase whose whole point is consuming a
   type scale, is a poor first use of it. So E1 makes ITS OWN grid internally
   consistent — cell text and nested controls both `--ciq-font-sm` — and does NOT
   touch the platform-wide mismatch, which is a Task B item. E2's marks grid needs
   the same treatment.

   Source order matters: these sit after `.ciq-table td` / `.ciq-table th`, which
   carry identical specificity, so the later rule wins. Do not move this block above
   the table section. `.ciq-ep-grid .form-control` is (0,2,0) and beats Bootstrap's
   `.form-control` (0,1,0) on specificity as well as on load order. */

.ciq-ep-grid th {
  font-size: var(--ciq-font-micro);
}

.ciq-ep-grid td {
  font-size: var(--ciq-font-sm);
}

.ciq-ep-grid .form-control,
.ciq-ep-grid .form-select {
  font-size: var(--ciq-font-sm);
}

/* A cross-screen consequence, not a field hint. U-10 and U-11 exist because
   `weightage_pct` and `is_internal` reach off this screen — into how SGPA computes
   and into how next term's exams generate — so they read a step heavier than the
   plain `.ciq-hint` beside a locked code. */
.ciq-ep-consequence {
  display: block;
  font-size: var(--ciq-font-xs);
  font-weight: var(--ciq-weight-medium);
  color: var(--ciq-text-muted);
}

/* The exam types named in the mixed-weightage warning: the actionable part of the
   sentence, so it carries the weight. */
.ciq-ep-names {
  font-weight: var(--ciq-weight-semibold);
}

/* Checkbox + label on one line. `--ciq-control-border` is what makes the EMPTY box
   read as present against the near-white card (M-C/C5); nothing here overrides it. */
.ciq-ep-check {
  display: flex;
  align-items: center;
  gap: var(--ciq-space-xs);
  font-size: var(--ciq-font-sm);
}

/* The branch ticks on Exam Setup: a wrapping row, all ticked by default (U-12). */
.ciq-ep-ticks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ciq-space-xs) var(--ciq-space-lg);
}

/* ===================== Dashboard tiles and CSS bars ======================= */
/* Admin dashboard Step 4. Every value here is a TOKEN — no literal colour, no
   literal radius, no literal type size (RV-49's used-subset-of-defined check is what
   holds that, and it is run at this gate).

   ⚑ THE BARS ARE CSS AND CARRY NO CHART LIBRARY, AND THAT IS A RULING RATHER THAN A
   STOPGAP (G-2, deferred 13-08-2026). A CSS bar PRINTS. A <canvas> in a print
   stylesheet is a known problem on a platform that prints mark sheets, memos and
   rosters — sharpened by F-2, which records that @media print below declares no type
   or colour property at all, so paper inherits whatever the screen does. Bar-shaped
   data keeps this treatment even once Chart.js is vendored for the line and pie
   charts that genuinely need one. */

.ciq-dash-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--ciq-space-md) var(--ciq-space-md);
}

.ciq-dash-tile {
  display: flex;
  flex-direction: column;
  gap: var(--ciq-space-2xs);
  /* ⚑⚑⚑ ONE STEP UP THE 4px GRID, 18-09-2026 (Gate 3), AND IT IS OUR SCALE RATHER
     THAN ATRIUM'S LITERAL. Their `.kpi` takes `--s-4 --s-5` (16/20px) because their kpi IS
     A STANDALONE CARD sitting on the page grid. THIS TILE IS A NESTED BOX inside a
     `.card > .card-body` that already carries its own padding, so their value would pad a
     box inside a padded box. 12/16px is one step on our own grid and is the whole change.
     ⚑⚑ THE SAME MEASUREMENT REFUSED TWO ADOPTIONS AND IS RECORDED AT THE RADIUS
     BELOW, WHICH IS WHERE A LATER READER WILL BE TEMPTED. */
  padding: var(--ciq-space-md) var(--ciq-space-lg);
  border: 1px solid var(--ciq-border);
  /* ⚑⚑⚑ THIS STAYS AT `--ciq-radius-md` (10px) AND MUST NOT BECOME THE CARD RADIUS,
     WHICH IS THE ADOPTION A READER COMPARING US TO ATRIUM WILL REACH FOR. Measured
     18-09-2026: their `.kpi` is `a.card.kpi` and takes `--r-lg` 14px BECAUSE IT IS THE
     CARD. Ours renders inside `<div class="card"><div class="card-body">` on all five
     dashboard groups, and `--ciq-card-radius` IS `--ciq-radius-xl` = 14px — so a tile
     taking their value would wear ITS OWN PARENT'S CORNER. 10px inside 14px is correct
     nesting, and Gate 2's 9 -> 10px left it correctly nested.
     ⚑⚑ A SHADOW IS REFUSED ON THE SAME GROUND: their `.kpi` carries `--shadow-1`
     because it is the card; ours sits inside one that already carries
     `--ciq-card-shadow`, and a shadow inside a shadow is depth that means nothing.
     ⚑ THE LESSON GENERALISES PAST THIS TILE: A VALUE IS SHAPED BY THE BOX IT SITS IN,
     and a prototype's value carries that box with it. */
  border-radius: var(--ciq-radius-md);
  background: var(--ciq-card-bg);
}

.ciq-dash-figure {
  font-size: var(--ciq-font-xl);
  font-weight: var(--ciq-weight-semibold);
  line-height: 1.1;
  color: var(--ciq-heading);
}

/* ⚑⚑⚑ THE KICKER TREATMENT IS OURS AND NOT ATRIUM'S, AND THE CORROBORATION RUNS
   THAT WAY ROUND. `.ciq-bar-label` — ON THIS SAME SCREEN — has read
   `font-xs · weight-medium · uppercase · var(--ciq-tracking)` since U1, and
   `.ciq-dash-label` was the OUTLIER. Gate 3 makes it consistent with our own house
   pattern, verbatim.
   ⚑⚑ WHICH IS WHY THE WEIGHT STAYS `medium` AND NO MUTED COLOUR ARRIVES. Atrium's
   `.kpi .label` is `600` and `--text-muted`; adopting those would import THEIR hierarchy
   on the strength of them agreeing with us about uppercase. They agree; that is
   corroboration, and corroboration is not a ground. */
.ciq-dash-label {
  font-size: var(--ciq-font-xs);
  font-weight: var(--ciq-weight-medium);
  text-transform: uppercase;
  letter-spacing: var(--ciq-tracking);
}

/* The track. The FILL's width is the only inline style the dashboard writes, and it
   is layout rather than colour — the six pre-existing inline styles in this codebase
   are all layout or visibility too, and the no-inline rule is about COLOUR. */
.ciq-bar {
  display: block;
  height: 0.6rem;
  border-radius: var(--ciq-radius-pill);
  background: var(--ciq-status-neutral-bg);
  overflow: hidden;
}

.ciq-bar-fill {
  display: block;
  height: 100%;
  border-radius: var(--ciq-radius-pill);
  background: var(--ciq-brand);
}

/* Step 5: the screen's own year/campus filter (replacing the header selectors), the
   closed-year note under it, the personal menu under the user's name, and the role
   headings inside the launcher. */
.ciq-screen-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ciq-space-sm);
  margin-bottom: var(--ciq-space-md);
}
.ciq-screen-filter .form-select-sm {
  width: auto;
  min-width: 11rem;
}
.ciq-closed-note {
  color: var(--ciq-status-amber-fg);
  background: var(--ciq-status-amber-bg);
  border-radius: var(--ciq-radius-sm);
  padding: var(--ciq-space-sm) var(--ciq-space-md);
  font-size: var(--ciq-font-sm);
}
.ciq-personal .ciq-identity {
  background: none;
  border: 0;
  text-align: left;
}
/* Phase 2e (6c): the name button stacks its lines in a column, which stretched Bootstrap's
   dropdown arrow into a dark bar across the full width under the name. The name itself
   opens the menu, so the arrow is not drawn. */
.ciq-personal .ciq-identity.dropdown-toggle::after {
  display: none;
}
.ciq-personal-menu {
  min-width: 15rem;
}
.ciq-field-error {
  display: block;
  color: var(--ciq-status-red-fg);
  font-size: var(--ciq-font-xs);
  margin-top: var(--ciq-space-xs);
}
.ciq-apps-group {
  font-size: var(--ciq-font-micro);
  font-weight: var(--ciq-weight-bold);
  letter-spacing: var(--ciq-tracking);
  text-transform: uppercase;
  color: var(--ciq-text-muted);
  margin: var(--ciq-space-md) 0 var(--ciq-space-xs);
}

/* Step 7 walk: a stand-alone print page (hall lists) -- no menu, no top bar. */
.ciq-print-sheet {
  padding: var(--ciq-space-md);
}

@media print {
  .ciq-sidebar,
  .ciq-sidebar-fixed,
  .ciq-topbar,
  .ciq-no-print {
    display: none !important;
  }
  /* Step 7: one hall list per printed page. */
  .ciq-print-page {
    break-after: page;
  }
  .ciq-main,
  .ciq-content {
    margin: 0 !important;
    padding: 0 !important;
  }
  /* ⚑⚑⚑ THE TILE GOES THE WAY THE CARD GOES, ADDED 18-09-2026 (Gate 3), AND IT
     FIXES AN ARTEFACT NOBODY HAD EVER SEEN. `.ciq-dash-tile` is NOT a `.card`, so this
     rule never reached it — while P5's wildcard below forces `border-color` to
     `--ciq-print-border` on `.ciq-body *`. THE RESULT ON PAPER: every tile boxed in BLACK
     inside a card with no box at all. It is pre-existing and this gate amplifies it,
     because Gate 3's padding makes those boxes bigger — and discovering it at a later
     walk AFTER amplifying it is the wrong order.
     ⚑⚑ THE GROUND IS THE CARD'S OWN: A TILE IS CHROME ON PAPER EXACTLY AS A CARD IS.
     A reader of a printed dashboard wants the figures, not the containers they were
     arranged in on a screen.
     ⚑ AND IT IS A SELECTOR ADDED TO AN EXISTING RULE RATHER THAN A RULE OF ITS OWN,
     which is why `css_resolve.py` CANNOT SEE IT AT ALL: no declaration is added and no
     resolved value moves, so the differential reports nothing in either direction. Its
     only instrument is the print reading. */
  .card,
  .ciq-dash-tile {
    box-shadow: none !important;
    border: none !important;
  }

  /* ⚑⚑⚑ F-2 — PAPER IS BLACK ON WHITE, AND IT NO LONGER INHERITS THE SCREEN
     PALETTE. Added 17-09-2026 (P5). Until today this block declared NO colour
     property at all, so every screen colour reached paper BY DEFAULT RATHER THAN
     BY DECISION -- which is what made any palette change a change to printed
     records as well.

     ⚑⚑ IT IS A WILDCARD AND NOT A LIST, DELIBERATELY. Fifteen colour-bearing
     rules reach the two print sheets today, across six token families (the brand,
     card-bg, text-muted, the four status pairs, btn-secondary-fg, page-bg). AN
     ENUMERATED LIST GOES STALE THE MOMENT A NEW CLASS REACHES A PRINT SHEET, and
     nothing would report it -- no test in this repository reads this file. The
     wildcard cannot go stale.

     ⚑ THIS ALSO DISCHARGES F-1 WITHOUT A RULE OF ITS OWN: .ciq-memo-id resolves
     var(--ciq-brand) on screen and is caught here like everything else. A separate
     memo rule would be a second site for one claim. */
  .ciq-body,
  .ciq-body * {
    color: var(--ciq-print-fg) !important;
    background: var(--ciq-print-bg) !important;
    border-color: var(--ciq-print-border) !important;
    box-shadow: none !important;
  }

  /* ⚑⚑ A BADGE LOSES ITS TINT ON PAPER AND MUST NOT LOSE ITS MEANING WITH IT.
     The colour is gone by the rule above, so the outline is what keeps a status
     legible as a status -- and the LABEL carries which one it is. That is the
     convention Atrium states in its own token block (icon and label always, never
     colour alone), and it is the reason this rule is here rather than a licence
     to print tints. */
  .ciq-body .ciq-badge {
    border: 1px solid var(--ciq-print-border) !important;
  }

  /* A card is a unit of meaning on paper; splitting one across a page break makes
     a memo harder to read than no card at all. */
  .ciq-body .card {
    break-inside: avoid;
  }

  /* Single column: the two grid layouts in this file collapse, so a printed page
     is one column whatever the viewport was when Print was pressed. */
  .ciq-body .ciq-two-pane,
  .ciq-body .ciq-wizard {
    grid-template-columns: 1fr !important;
  }
}

/* Step 9: the leave forms. Two date fields side by side, the day-count line under
   them, and the inline action forms in a table cell. */
.ciq-form-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1rem;
}
.ciq-field--date {
  flex: 0 0 auto;
  min-width: 11rem;
  max-width: 13rem;
}
.ciq-days-hint {
  display: inline-block;
  color: var(--ciq-muted);
}
.ciq-days-hint--warn {
  color: var(--ciq-danger);
}
.ciq-actions .ciq-inline-form {
  display: inline-block;
  margin-right: 0.25rem;
}

/* 10b-1b walk (01-10-2026): the person picker. The list is as wide as its lines need, up
   to the screen, so a line never wraps mid-ID; a line too long shortens its class part
   (an ellipsis), never the ID. While typing, the person chosen before is hidden. */
.ts-wrapper.ciq-person .ts-dropdown {
  width: max-content;
  min-width: 100%;
  max-width: min(46rem, calc(100vw - 2rem));
}
.ciq-person-opt {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  min-width: 0;
}
.ciq-person-main {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ciq-person-id {
  flex-shrink: 0;
  white-space: nowrap;
}
.ts-wrapper.single.ciq-person.ciq-typing .ts-control > .item {
  display: none;
}

/* Staff exit (11a): one resignation per block on Leave Approvals. */
.ciq-resign-row {
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--ciq-border);
}
.ciq-resign-row:last-of-type {
  border-bottom: 0;
}

/* Staff exit walk, H13: a vehicle row's Change driver box shows its whole hint. */
.tr-driver-change .ts-wrapper { min-width: 22rem; }
