/*
    RetirementCare central brand theme.

    This is the single source of truth for brand colours, typography, spacing,
    radius and shadow tokens. The public website, the future Admin Portal and the
    future Resident Portal must all consume these variables instead of redefining
    their own hard-coded values.

    The values below currently match the SenoCare template palette exactly, so the
    public website renders identically to the source template. Rebranding is done
    by changing the values here - public-website.css maps the template's own
    variables onto these tokens, so nothing else needs editing.

    Do not copy these values into portal-specific or component-isolated CSS.
*/

/*
    Minimal global reset.

    ⚠️ Do not remove. The shells (login, admin, resident) deliberately load NO CSS
    framework, which also means they get no framework reset - so the browser's
    default `body { margin: 8px }` applies. That 8px is invisible until something is
    position:fixed: the admin sidebar then sits at 0 while the content column beside
    it starts at 8, which showed up as an unexplained gap down the left AND as the
    top bar's bottom border sitting 8px below the sidebar's, making two panels of
    identical height look mismatched. Measured and fixed 2026-07-29.

    The public website is unaffected - Bootstrap's own reset already zeroes this.
*/
body {
    margin: 0;
}

/*
    Global border-box.

    ⚠️ Do not remove, and do not "fix" a width bug by adding box-sizing locally instead.
    Content-box has now caused the same bug three times in this shell: an element given
    `max-width` or `height` renders WIDER or TALLER than that value, because padding and
    border are added on top. It produced a sidebar brand block 1.2rem taller than the top
    bar beside it, and a portal hero card 50px wider than the content column it was meant
    to line up with. Both looked like careless design; both were this.

    Applied globally rather than per-component because the shells deliberately load no CSS
    framework and therefore inherit no reset. The public website is unaffected - Bootstrap
    already sets exactly this.
*/
*,
*::before,
*::after {
    box-sizing: border-box;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   THE BRAND FACES, SELF-HOSTED (2026-08-24, RP164 — product owner: "you can download them for use
   as assets")

   ⚠️⚠️⚠️ THIS BLOCK IS WHY EVERY SHELL NOW GETS THE BRAND FACES, AND THAT IS A FIX RATHER THAN A
   TIDY-UP. Before it, the webfonts were loaded by exactly TWO shells — `AccountLayout` via a
   Google `<link>` and the marketing template via an `@import` — while `--rc-font-family-base`
   ('Roboto') and `--rc-font-family-heading` ('Ubuntu') are read by 188 declarations across the
   product. So `AdminLayout`, `LoginLayout`, `SigningLayout` and the resident portal named families
   the browser did not have and FELL THROUGH TO THE SYSTEM SANS: the admin portal, which is most of
   this product, had never once rendered in the brand faces. It reads as a styling choice rather
   than a fault, which is exactly why it survived.
   📌 EVERY ONE OF THE NINE SHELLS ALREADY LINKS THIS STYLESHEET — checked, not assumed — so the
   faces arrive everywhere with no per-layout change, and a shell added next year cannot forget.
   That property is the whole reason the rules live here rather than in a `<HeadContent>`.

   ⚠️⚠️ AND IT REMOVES A SILENT PRODUCTION DEPENDENCY. Google Fonts needs outbound internet AT THE
   OPERATOR'S SITE, and the fallback is silent: no route to fonts.gstatic.com and the page renders
   in the system sans, looking merely ordinary rather than broken. Self-hosting makes the faces a
   deployed asset like any other.

   ⚠️ WEIGHTS ARE THE UNION OF WHAT THE PRODUCT ALREADY REQUESTED — Roboto 100/300/400/500/700/900
   and Ubuntu 300/400/500/700. The profile area only ever asked for 400/500/700; shipping just
   those three would have silently re-weighted the MARKETING site, which requests the rest.
   ⚠️ Latin and latin-ext only. `unicode-range` is preserved exactly as Google serves it, so a
   browser downloads only the faces a page actually needs — 20 files exist, a typical page pulls
   two to four.

   ⚠️⚠️ THESE ARE REDISTRIBUTED BINARIES AND THEIR LICENCES SHIP BESIDE THEM in `theme/fonts/`:
   Roboto is Apache-2.0, Ubuntu is the Ubuntu Font Licence 1.0. Both permit self-hosting. Do not
   add a face without checking its licence allows it.

   ⚠️ `font-display: swap` is kept from Google's own CSS: text paints immediately in the fallback
   and swaps when the face arrives. The alternative — blocking — trades a flash for invisible text.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 100;
    font-display: swap;
    src: url("../fonts/roboto-100-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 100;
    font-display: swap;
    src: url("../fonts/roboto-100-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src: url("../fonts/roboto-300-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src: url("../fonts/roboto-300-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("../fonts/roboto-400-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("../fonts/roboto-400-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("../fonts/roboto-500-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("../fonts/roboto-500-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("../fonts/roboto-700-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("../fonts/roboto-700-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 900;
    font-display: swap;
    src: url("../fonts/roboto-900-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 900;
    font-display: swap;
    src: url("../fonts/roboto-900-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Ubuntu';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src: url("../fonts/ubuntu-300-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Ubuntu';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src: url("../fonts/ubuntu-300-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Ubuntu';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("../fonts/ubuntu-400-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Ubuntu';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("../fonts/ubuntu-400-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Ubuntu';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("../fonts/ubuntu-500-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Ubuntu';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("../fonts/ubuntu-500-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Ubuntu';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("../fonts/ubuntu-700-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Ubuntu';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("../fonts/ubuntu-700-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
    /*
        Brand palette (product owner, 2026-07-29). Taken from the real logo rather than
        inherited from the SenoCare template, and the ROLES WERE SWAPPED at the same time:

            primary   = the teal      (was secondary, #00ADB8)
            secondary = the coral     (was primary,   #FF7A7C)

        The swap corrects a long-standing wart: every shell already led with teal, so
        "secondary" was doing the primary's job. If you find CSS that reads
        --rc-color-secondary expecting teal, it predates this and is a bug.

        ⚠️ CONTRAST. Both hues sit near 3.5:1 against white:
            #0197a1 on white = 3.53:1      #f34d59 on white = 3.49:1
        That clears the 3:1 floor for LARGE text, buttons, badges, borders and focus
        rings - but NOT the 4.5:1 floor for normal-size text. For coloured text at body
        size use --rc-color-primary-dark (5.11:1). Do not put small white text on a
        primary/secondary fill.
        (Both are still a clear improvement: the old #00ADB8 managed only 2.74:1, which
        failed even the large-text floor despite a code comment claiming otherwise.)
    */
    --rc-color-primary: #0197a1;

    /*
        Darker teal. Two jobs: the hover/pressed fill under a primary button, and the
        AA-safe colour for coloured TEXT at body size (5.11:1 on white).
        ⚠️ This used to be #2C374B - a navy, identical to --rc-color-dark and unrelated
        to the primary. If you want the navy, name it: `--rc-color-nav-surface` for a SURFACE, or
        `--rc-color-dark` for INK. ⚠️ They hold the same navy in the light theme and diverge in the
        dark scope, where `--rc-color-dark` is a near-white (RP184) — so "use --rc-color-dark for the
        navy", which this line used to say, is now true of ink only.
    */
    --rc-color-primary-dark: #017a82;

    --rc-color-secondary: #f34d59;

    /*
        The secondary at a weight that can carry TEXT, and the counterpart of --rc-color-primary-dark
        above (added 2026-08-18).

        ⚠️⚠️ --rc-color-secondary IS 3.49:1 ON WHITE, so it fails the 4.5:1 floor for normal text — the
        same trap the primary carries at 3.53:1 and for the same reason. It is a FILL colour: correct
        behind white text at large sizes, wrong for a figure somebody has to read. This shade is 4.57:1
        and is what the coral should be whenever it is the ink rather than the ground.

        📌 It also fixes a live one: `.rc-action--destructive` prints its label in the raw secondary, so
        every Delete/Withdraw/Deactivate label in the product currently sits at 3.49:1.
    */
    --rc-color-secondary-dark: #D1424D;

    /* ══════════════════════════════════════════════════════════════════════════════════════
       THE ACTION PALETTE (RP7 / RP10, agreed 2026-08-18) — one colour per ACTION TYPE.

       ⚠️⚠️ THESE ARE DARKENED VARIANTS OF THE BRAND HUES, NEVER THE RAW ONES, and RP10 is the
       reason. Measured on white: primary teal 3.53:1, secondary coral 3.49:1, the accent red
       3.44, amber 1.98, green 1.86 — every one below the 4.5:1 floor for normal text. An
       action button's label IS normal text, so assigning a brand hue straight to a button
       ships labels nobody can read (the P-D81 defect, which measured 1.09:1).

       📌 Contrast is symmetric, so each shade works as the ink of an outlined button AND as
       the ground of a filled one. That is what lets one token serve both treatments.

       ⚠️ The ratio beside each is measured, not estimated. Anything changed here must be
       re-measured — a hue nudged "to look better" is how a 4.5:1 floor is lost silently.
       ══════════════════════════════════════════════════════════════════════════════════════ */

    /* Primary / confirm — Save · Add · Register now · Confirm. FILLED.
       ⚠️ Also the ink of the OPEN family below: the map gives the two the same colour and
       distinguishes them by treatment, so they share one token deliberately. */
    --rc-action-primary: #01828A;          /* 4.60:1 */
    --rc-action-primary-hover: #016F76;    /* 5.94:1 */
    --rc-action-primary-pressed: #015C62;  /* 7.76:1 */

    /* The same teal used as INK rather than as a FILL — a link, not a button (2026-08-23, RP132).

       ⚠️⚠️⚠️ IT EXISTS BECAUSE THE FILL AND THE INK CANNOT SHARE ONE VALUE ON A DARK GROUND, AND
       RE-POINTING `--rc-action-primary` ITSELF WOULD HAVE BROKEN THE BUTTON. Measured on the
       published `/account/security`: `Manage your passkeys` renders `--rc-action-primary` as TEXT
       on the #171D28 ground at **3.67:1**, under the 4.5:1 floor for body copy. The obvious fix is
       to give `--rc-action-primary` a dark variant — and that token is also the BACKGROUND of
       `.account-detail__button`, where white-on-teal measures 4.60:1; lightening it to #21A3AB
       drops that to roughly 3.05:1. One token, two jobs, opposite directions.

       ⚠️ On white the two are the same colour, so nothing outside the dark scope changes. */
    --rc-action-primary-ink: #01828A;      /* 4.60:1 on white */

    --rc-action-edit: #0049D0;             /* 7.33:1 — Edit · Rename · Correct · Change … */
    --rc-action-status: #B5179E;           /* 5.86:1 — Change status ONLY (RP8) */
    --rc-action-move: #7C5CE0;             /* 4.70:1 — Move · Transfer */
    --rc-action-record: #0D837D;           /* 4.61:1 — Record · Mark · Attach · outcomes */
    --rc-action-restore: #A16B04;          /* 4.55:1 — Reactivate · Restore · Reopen */

    /* WhatsApp — the send-on-WhatsApp control, and the only third-party brand mark in this palette
       (WA47, product owner 2026-10-08). FILLED and icon-only, exactly like the download beside it.

       ⚠️⚠️⚠️ ALL THREE ARE GENUINE WHATSAPP BRAND COLOURS, AND THE COMBINATION WAS MEASURED RATHER THAN
       ASSUMED. The familiar #25D366 is kept as the FILL — which is what the product owner asked for —
       and it works only because the glyph is WhatsApp's DARK ink rather than white: white on #25D366
       is 1.98:1, below even the 3:1 floor for a non-text graphic, while #0B141A on it is 9.38:1.
       📌 So the recognisable green survives; what changed is the glyph, not the brand colour.
       ⚠️⚠️ THE BORDER IS NOT DECORATION. #25D366 against a WHITE card is 1.98:1, so the button's own
       edge would fall below WCAG 1.4.11's 3:1 for a component boundary — invisible on every light
       screen. #128C7E is 4.14:1 on white and reads as a quiet edge on the green. */
    --rc-action-whatsapp: #25D366;         /* fill — the brand green */
    --rc-action-whatsapp-hover: #1FB457;   /* 6.84:1 with the glyph — a shade down on hover */
    --rc-action-whatsapp-glyph: #0B141A;     /* 9.38:1 on the fill — WhatsApp's own dark ink */
    --rc-action-whatsapp-edge: #128C7E;    /* 4.14:1 on white — the boundary a light card needs */

    /* Document / download — every PDF control in the product (RcPdfButton). FILLED.

       ⚠️⚠️ IT IS A COLOUR OF ITS OWN, AND THAT IS THE WHOLE POINT (RP27, product owner
       2026-08-18). It used to be `--rc-color-primary-dark` (#017a82), which is the same teal
       family as --rc-action-primary (#01828A) — so on the invoice row the download button sat
       beside an outlined `Open` in a hue 4° away and read as another instance of the same
       action rather than as a different kind of control.

       ⚠️ CHOSEN ON MEASUREMENT, the way RP8's fuchsia was. Green is the one region of the
       wheel this palette leaves empty: at hue 126° it is 51° from the record teal (177°),
       87° from the restore amber (39°) and further still from everything else — where the
       nearest pair anywhere else in the map is 46°. White on it is 5.35:1. */
    --rc-action-document: #2A7A32;         /* 5.35:1 */
    --rc-action-document-hover: #2D8536;   /* 4.65:1 — lighter on hover, still over the floor */

    /* Surfaces and text */
    --rc-color-background: #ffffff;
    --rc-color-surface: #F6F6F6;
    --rc-color-dark: #2C374B;

    /* ⚠️⚠️ A DOCUMENT'S INK IS THEME-INDEPENDENT BY DEFINITION, AND THAT IS WHY IT IS ITS OWN TOKEN
       (2026-08-25, RP192). `.terms-body` renders the residency agreement a family is about to sign —
       the same content that prints — so it keeps its paper-white ground in both themes. Its ink
       therefore cannot be `--rc-color-dark`, which the dark scope re-points to the near-white #E8EBF1
       and would put near-white ON that white. ⚠️ Declared at `:root` and DELIBERATELY NOT re-pointed
       in the dark scope: a value that must not follow the theme is stated once, here, rather than
       written as a literal at the point of use — where the button-treatment guard would read it as a
       stray hue and, worse, where nothing would say why it never changes. */
    --rc-document-ink: #2C374B;
    --rc-color-text: #2C374B;
    --rc-color-muted: #757F95;
    --rc-color-on-primary: #ffffff;
    --rc-color-on-dark: #F5FAFF;

    /*  ⚠️⚠️⚠️ THE NAVY BRAND SURFACE — THE SIDEBAR AND THE ACCOUNT MENU (2026-08-25, RP184).
        IT IS DELIBERATELY *NOT* RE-POINTED IN THE DARK SCOPE, EXACTLY LIKE ITS INK
        `--rc-color-on-dark` BELOW, AND THE PAIR ONLY MAKES SENSE TOGETHER.

        `--rc-color-dark` holds this same navy in the light theme, which is why four surfaces came to
        read it — but that token means *"the strongest ink on this surface"* and therefore resolves to a
        near-white `#E8EBF1` inside the dark scope (RP177). So the sidebar painted itself near-white and
        kept every ink that had been measured against #2C374B: the labels went to **1.11:1**, the rail
        labels to **2.14:1**, and the flyout — `color-mix(… var(--rc-color-dark) 92%, white)` — followed
        it. **Measured: 12 light grounds and 123 text failures, the entire remainder of the roll-out.**

        📌 The token was doing TWO JOBS, and the counts say which one it is: **78 uses as an ink against
        4 as a surface.** So the ink keeps the name and the surface gets its own.
        ⚠️ It is the SAME VALUE in the light theme, so nothing there moves — verified by measurement, not
        by reasoning.
        ⚠️⚠️ **The avatar disc is NOT one of the four and must not be "made consistent" with them.** RP177
        gave it `--rc-color-inset` on purpose: it sits on the top BAR rather than on a navy panel, and a
        navy disc on a dark bar is the contrast problem it was fixed for.  */
    --rc-color-nav-surface: #2C374B;

    /* Accents carried over from the template */
    --rc-color-green: #15D4C9;
    --rc-color-blue: #0049D0;
    --rc-color-yellow: #FBA707;
    --rc-color-red: #F05454;
    --rc-color-border: rgba(0, 0, 0, 0.08);

    /* Typography - SenoCare --body-font / --heading-font */
    --rc-font-family-base: 'Roboto', sans-serif;
    --rc-font-family-heading: 'Ubuntu', sans-serif;

    /* ⚠️⚠️ THE ONE SIZE EVERY ALERT / NOTICE / ERROR PANEL READS AT, AND THE FIRST FONT-SIZE TOKEN THIS
       PRODUCT HAS HAD (product owner, 2026-08-25). Before it, the family rendered at THREE sizes and the
       largest group stated nothing at all: 17 error panels declared no `font-size` and therefore inherited
       the BROWSER DEFAULT of 16px — there is no base `font-size` anywhere in this product, `body` sets only
       `margin` — while `.account-detail__*` sat at 0.94rem and the page intro at 0.875rem.
       📌 The page intro is the reference because it is the panel a reader meets first on every screen.
       ⚠️ It is a SIZE, so it is deliberately NOT re-pointed in the dark scope: a ground changes what ink
       is legible, never how large the words should be. */
    --rc-font-size-note: 0.875rem;

    /* Radius */
    --rc-radius-sm: 0.375rem;
    --rc-radius-md: 0.75rem;
    --rc-radius-lg: 1.25rem;

    /* Elevation */
    --rc-shadow-soft: 0 0 40px 5px rgb(0 0 0 / 5%);
    --rc-shadow-card: 0 2px 16px rgb(44 55 75 / 8%);

    /* ⚠️ The RAISED state of a card you can click (product owner, 2026-08-19: a hover change that reads
       as more than a darker border). Same navy hue as `--rc-shadow-card` so the two are one family —
       twice the alpha and a slightly deeper offset, because it has to be legible against a card that
       rests with NO shadow at all.
       ⚠️ It is a token rather than a value written into one rule, per this project's styling rule that
       shadows are central: any clickable card wants this, and the medication round's is simply the
       first. Do NOT re-point `--rc-shadow-card` to it — that is the RESTING elevation of every ordinary
       `.card` in both shells, and raising it would lift every panel in the product. */
    --rc-shadow-card-hover: 0 4px 20px rgb(44 55 75 / 16%);

    /* ------------------------------------------------------------------
       Shell layout tokens (Login / Admin / Resident).

       CHANGE THE WIDTH AND PADDING HERE AND NOWHERE ELSE. Every shell sizes
       its content from these, so adjusting the feel of the whole application
       is a one-line edit rather than a hunt through layout files.

       The public website is NOT sized from these - it keeps the SenoCare
       template's own container widths.
       ------------------------------------------------------------------ */

    /* Maximum width of a shell's content column. */
    --rc-shell-content-max-width: 1400px;

    /* Gutter between the content column and the viewport edge, so admin and
       resident pages never run edge to edge. */
    --rc-shell-gutter-x: 2rem;
    --rc-shell-gutter-x-sm: 1rem;

    /* Vertical breathing room above and below page content. */
    --rc-shell-gutter-y: 2rem;

    /* Admin/resident chrome. Sidebar width and top bar height are here rather than
       in the layouts so the two panels stay aligned with each other by construction. */
    --rc-shell-sidebar-width: 16rem;
    --rc-shell-topbar-height: 4rem;

    /* Spacing scale used by shell chrome. */
    --rc-space-xs: 0.25rem;
    --rc-space-sm: 0.5rem;
    --rc-space-md: 1rem;
    --rc-space-lg: 1.5rem;
    --rc-space-xl: 2.5rem;

    /* ------------------------------------------------------------------
       Tabular data.

       APPLICATION-WIDE STANDARD (product owner, 2026-07-29): the outer edge
       padding for any tabular data anywhere in the product - Syncfusion grids,
       plain HTML tables, list rows. Set here so every table in every feature
       indents identically and one edit re-spaces all of them.

       It equals --rc-space-lg on purpose: that is the card's own padding, so a
       grid sitting flush inside a card lines its first column up with the text
       of every ordinary card on the page.
       ------------------------------------------------------------------ */
    --rc-table-edge-padding: var(--rc-space-lg);

    /* Inner padding between columns. Smaller than the edge, so the outer margin
       reads as deliberate rather than as one more column gap. */
    --rc-table-cell-padding-x: 0.75rem;

    /* ------------------------------------------------------------------
       Resident portal.

       The portal is read by residents and their family - often elderly, often on a
       phone or tablet - whereas the admin portal is read by staff at a desk. It
       therefore runs a LARGER type scale and BIGGER tap targets than the admin
       shell, deliberately, rather than sharing one scale across the product
       (product owner, 2026-07-29).

       Change these to re-tune the whole portal; the admin shell does not read them.
       ------------------------------------------------------------------ */

    /* Body text. Admin runs 0.9375rem. */
    --rc-portal-font-size: 1.0625rem;
    --rc-portal-line-height: 1.65;

    /* Minimum height for anything tappable. 44px is the long-standing accessibility
       floor for a finger, and it is the reason the nav items are not simply links. */
    --rc-portal-tap-target: 2.75rem;

    /* The portal is a single centred column, much narrower than the admin content
       cap - it is reading material, not a data grid. */
    --rc-portal-content-max-width: 60rem;

    /* Height of the brand bar. Sized around the logo it contains: the portal runs the
       wordmark at 4rem, and this leaves ~0.75rem of air above and below it. Changing
       the logo height without changing this makes the bar look crammed. */
    --rc-portal-brandbar-height: 5.5rem;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   THE DARK SCOPE (2026-08-22, RP118 — supersedes RP103's light ground)

   ⚠️⚠️⚠️ IT RE-POINTS THE TOKENS ABOVE; IT DOES NOT DEFINE A PARALLEL PALETTE. Every component in
   this product already reads `--rc-color-background`, `--rc-color-surface`, `--rc-color-border`,
   `--rc-color-dark`, `--rc-color-text` and `--rc-color-muted`, so re-pointing those six inside one
   scope turns everything underneath it dark WITHOUT touching a single component stylesheet. A second
   set of `--rc-dark-*` names would have to be adopted file by file, and every file that had not
   adopted them yet would stay light — which is the same defect as having no dark mode at all, only
   harder to see.

   ⚠️⚠️ HOW THE PRODUCT-WIDE ROLL-OUT HAPPENS (product owner, 2026-08-22: "I might use the dark theme
   across the board later"). Today `AccountLayout` puts `data-rc-theme="dark"` on its own root, so the
   scope covers the profile area and nothing else. Going product-wide is MOVING THAT ATTRIBUTE TO
   `<html>` in `App.razor` — one line, no rewrite, and it is the reason the values live here in the
   theme rather than in the two scoped stylesheets that need them today.

   ⚠️⚠️⚠️ `--rc-color-dark` RESOLVES TO A LIGHT VALUE IN HERE, AND THAT IS CORRECT. The token means
   "the strongest ink on this surface", not "the colour dark" — it is the heading/label colour
   throughout the product. Reading the name literally and "fixing" it to something dark would make
   every heading in this scope invisible against its own card. The same applies to `--rc-color-text`.

   ⚠️ The ACTION palette above is deliberately NOT re-pointed. Those ten colours are measured against
   a 4.5:1 text floor on white and each carries its ratio in a comment; re-pointing them here would
   silently invalidate every one of those measurements. A control that needs a dark-ground variant
   gets one when somebody measures it — not as a side effect of this block.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* ⚠️⚠️⚠️ THE SECOND SELECTOR IS NOT A CONVENIENCE — WITHOUT IT THE DARK AREA'S OWN DIALOGS STAY WHITE
   (2026-08-22, RP125). `SfDialog` appends its container to `document.body`, so a dialog opened from a
   page inside `AccountLayout` is a SIBLING of the shell, not a descendant: measured on the published
   site as `div.e-dialog > div.e-dlg-container.rc-modal > body`, with `shell.contains(dialog) === false`
   and the nearest `[data-rc-theme]` ancestor NONE. A descendant selector off the shell would therefore
   have matched nothing at all, while reading perfectly.
   📌 It also means the white dialog reported in review was a SCOPE LEAK rather than a design choice:
   its footer and inner note are painted by OUR `--rc-color-surface`, which resolved to the light value
   only because the element sits outside the scope. `RcModal` puts the modifier on when the shell above
   it cascades a dark theme, so no caller has to remember. */

/* ⚠️⚠️⚠️ AND THE THIRD SELECTOR IS A DATE PICKER'S POPUP, FOR EXACTLY THE SAME REASON (2026-08-23,
   RP141). `SfDatePicker` appends its calendar to `document.body` just as `SfDialog` does, so a picker
   placed on `/account/payslips` renders its INPUT inside the dark scope and its POPUP outside it — a
   white calendar hanging off a dark field, which reads worse than either would alone.
   ⚠️⚠️ `CssClass` on the component is what puts the class on BOTH elements; there is no way to reach the
   popup from the page's own markup, and no selector written against the shell can reach it either.
   📌 Named here rather than given a block of its own, for the reason the modal selector already
   records: two blocks holding the same tokens is two things to keep in step. */
/* ⚠️⚠️⚠️ THE LIGHT DEFAULTS FOR THE THREE TOKENS THE DARK BLOCK BELOW ALSO SETS, AND THEY MUST STAY
   ABOVE IT (2026-08-25, RP178). The light theme needs them or a `.rc-note` outside the dark scope
   resolves to nothing and renders with NO bar at all — a silent failure, since the text stays
   perfectly readable. **They are declared HERE, before the dark block, because `:root` and
   `[data-rc-theme="dark"]` are both (0,1,0):** once the attribute is on `<html>` the two selectors
   match the same element and whichever comes LAST wins. Below the dark block, these light values
   silently won inside the dark scope. **Do not move them back down, and do not "tidy" them into the
   main `:root` at the top of the file** — keeping them adjacent to the dark values is what stops the
   pair drifting, and that was always the intent. */
:root {
    --rc-note-accent: #01828A;
    --rc-note-accent-warn: #D1424D;
    --rc-color-inset: #F6F6F6;

    /* ⚠️⚠️ A COMPLETED STEP, FOR `RcWorkflowSteps` (RP241) — AND THE PRODUCT HAD NO SUCCESS COLOUR AT ALL
       BEFORE THIS. `--rc-color-green` is #15D4C9, which is cyan despite its name, and `--rc-action-record`
       is the teal-green of an action family rather than a statement that something is finished. Declared
       as a PAIR in both themes so the rail follows the shell; the first draft used a literal behind a
       `var(…, #0F7F55)` fallback, which always applied and therefore never followed the theme at all.
       📌 #0F7F55 carries white at 4.86:1, which clears the 3:1 floor for the tick it holds.
       ⚠️ The INK differs between themes and the fill does not: #1B6B43 is 6.2:1 on white, while the dark
       scope needs a pale ink on a dark card. */
    --rc-color-success: #0F7F55;
    --rc-color-success-ink: #1B6B43;

    /* ⚠️ The dashed ring on a step not yet reached. `--rc-color-border` is rgba(0,0,0,0.08) here — right
       for a hairline between rows, far too faint to read as a dashed placeholder. */
    --rc-color-border-strong: rgba(0, 0, 0, 0.26);

    /* ⚠️ PC61 — "saved but not published" (the approved mock, 2026-09-22): the page editor's banner, the side
       menu's count and dot, and the leave dialog's icon. Its OWN family, not --rc-warning-ink: that one is
       an ink for text with no ground, and this is a filled amber with a dark label (8.4:1) plus a tinted
       panel. Declared HERE, before the dark block, for the source-order reason stated above. The amber and
       its ink are the same in both themes; the panel's ground, rule and text are what the dark block moves. */
    --rc-pending: #F0A33A;
    --rc-pending-on: #231A07;
    --rc-pending-ground: #FFF4E0;
    --rc-pending-line: #E8C07A;
    --rc-pending-title: #6B4400;
    --rc-pending-text: #5C4410;
}

[data-rc-theme="dark"],
.rc-modal--dark,
.rc-datepicker--dark {
    /* The page ground. The example's `--page-bg`. */
    --rc-color-surface: #171D28;

    /* Anything raised off the ground: a card, a tile, a panel, the bar. The example's `--card`. */
    --rc-color-background: #1F2635;

    /* ⚠️ AN OPAQUE HAIRLINE, NEVER THE LIGHT THEME'S `rgba(0, 0, 0, .08)`. A translucent black edge
       over a #1F2635 card computes to something DARKER than the card it is meant to outline, so the
       border disappears and every tile loses the edge the design is built on. */
    --rc-color-border: #2E3849;

    /* The strongest ink — headings, labels, figures. See the warning above before "correcting" it. */
    --rc-color-dark: #E8EBF1;
    --rc-color-text: #E8EBF1;

    /* Secondary ink: facts, leads, captions. */
    --rc-color-muted: #8B94A8;

    /* ⚠️ `--rc-color-on-dark` is the ink used ON a navy surface (the sidebar, the account menu).
       Those surfaces do not change here, so neither does it. Stated so its absence reads as a
       decision rather than as an omission. */

    /* ⚠️ Elevation is carried by BLACK here, not by the navy of the light theme. A navy shadow on a
       navy-black ground is invisible, so a tile would lose its lift entirely and read as flat. */
    --rc-shadow-card: 0 1px 2px rgb(0 0 0 / 40%), 0 8px 24px rgb(0 0 0 / 38%);
    --rc-shadow-card-hover: 0 2px 4px rgb(0 0 0 / 46%), 0 14px 32px rgb(0 0 0 / 44%);
    --rc-shadow-soft: 0 0 40px 5px rgb(0 0 0 / 35%);

    /* ── Ink for the note bars ──────────────────────────────────────────────────────────────────
       ⚠️⚠️ THE BRAND TEAL AND CORAL ARE LIGHTENED FOR THIS GROUND, and the two are NOT
       interchangeable with the action tokens. `--rc-action-primary` (#01828A) is measured for white
       and drops to roughly 2.6:1 against #171D28 — a bar nobody can see. These are the example's own
       dark-mode values. */
    --rc-note-accent: #21A3AB;
    --rc-note-accent-warn: #D1424D;

    /* A chip / inset ground: one step up from a card, never down. */
    --rc-color-inset: #262F40;

    /* ⚠️⚠️ THE DARK HALF OF THE SUCCESS PAIR (RP241). The FILL is the same #0F7F55 as the light theme —
       it carries white at 4.86:1 on either ground — while the INK must be pale here: #1B6B43 on the
       #1F2635 card would be 1.6:1 and effectively invisible. 📌 That asymmetry is the reason this is a
       pair rather than one token: a single value cannot serve a fill and a label on opposite grounds. */
    --rc-color-success: #0F7F55;
    --rc-color-success-ink: #8FDCA8;

    /* ⚠️ The dashed ring on a step not yet reached, one step clear of `--rc-color-border` (#2E3849). */
    --rc-color-border-strong: #46536A;

    /* ⚠️⚠️ DECLARED FOR THE FIRST TIME HERE, AND THAT IS THE POINT (product owner, 2026-08-25 — the
       Residency terms *Draft* badge was near-white on near-white). `--rc-color-surface-muted` is read
       by six rules and was DECLARED NOWHERE, so every one of them fell through to a light literal
       (#F1F5F7 / #F7F8FA) — while `--rc-color-dark`, which several of them use as their ink, IS
       re-pointed here to the near-white #E8EBF1 (RP184). Light ground, light ink, invisible content.
       📌 Defining it in this scope fixes all six at once BECAUSE they already read it with a fallback:
       the token was a contract nobody had honoured on dark. ⚠️ It matches `--rc-color-inset` today and
       is kept separate because the two say different things — "a quieter surface" and "a well sunk
       into one" — and a token doing two jobs is exactly what RP184 had to unpick. */
    --rc-color-surface-muted: #262F40;

    /* ⚠️⚠️⚠️ THE DARK SCOPE HAS A SECOND HALF, AND IT IS IN `syncfusion-brand.css` (2026-08-25,
       RP179). Twelve `--color-sf-*` re-points — the dialog ground, the calendar and dropdown
       flyout, the input groups, the placeholders, the icons and the secondary inks — used to sit
       HERE, and every one of them silently reverted to Syncfusion's light default the moment the
       attribute moved to `<html>`: the vendor declares them all at `:root` in `bootstrap5.3.css`,
       which loads AFTER this file, and `:root` and `[data-rc-theme="dark"]` are both (0,1,0).
       ⚠️⚠️ THE SELECTOR LIST ABOVE AND THE ONE OVER THERE MUST STAY IDENTICAL, or a dialog resolves
       our surfaces from one scope and Syncfusion's from the other — the half-dark state RP125
       exists to prevent. `DarkScopeSelectorListGuardTests` fails if they drift.
       📌 Anything reading a `--rc-*` token belongs here; anything re-pointing a VENDOR token
       belongs in the file that loads after the vendor. That is the whole rule. */

    /* ══ THE LIFTED INKS — ONE PER ACTION FAMILY (2026-08-24, RP160) ═══════════════════════════
       ⚠️⚠️⚠️ EVERY ACTION COLOUR IN THIS PRODUCT FAILS THE 4.5:1 TEXT FLOOR ON THE DARK CARD, AND
       OUTLINED IS THE DEFAULT BUTTON SHAPE (RP9) — an outlined button draws its LABEL in the family
       colour, so without this table every action button on a dark screen is under the readability
       floor. Measured against #1F2635, where buttons actually sit:

           family        fill      on dark        lifted ink   on dark
           primary/open  #01828A   ⛔ 3.29:1      #21A3AB      ✅ 4.97:1
           edit          #0049D0   ⛔ 2.06:1      #5B8DEF      ✅ 4.69:1
           status        #B5179E   ⛔ 2.58:1      #CF68C0      ✅ 4.61:1
           move          #7C5CE0   ⛔ 3.22:1      #967DE6      ✅ 4.60:1
           record        #0D837D   ⛔ 3.29:1      #3D9C97      ✅ 4.62:1
           restore       #A16B04   ⛔ 3.33:1      #B48936      ✅ 4.74:1
           document      #2A7A32   ⛔ 2.83:1      #5F9B65      ✅ 4.59:1
           destructive   #D1424D   ⛔ 3.31:1      #DD717A      ✅ 4.84:1

       ⚠️⚠️⚠️ THIS SUPERSEDES RP126's NARROWNESS, AND *ONLY* ITS NARROWNESS. That decision said a
       family gets a dark variant "when somebody measures it" and warned that converting nine on the
       strength of one is how a known-good ratio is replaced by an unknown one. All eight are measured
       now, so the condition it set is met rather than waived.

       ⚠️⚠️ AN INK TOKEN, NEVER A RE-POINT OF THE FILL — AND RP126 DID THE LATTER, WHICH WAS A LIVE
       DEFECT. `--rc-action-edit` was re-pointed to #5B8DEF here, and the family rule uses that same
       token as the HOVER BACKGROUND with #FFF text: white on #5B8DEF is 3.23:1, so hovering
       `Edit contact details` on this page produced an unreadable label. Splitting ink from fill is
       what fixes it — the resting label keeps #5B8DEF, the hover fill goes back to the saturated
       #0049D0, where white is 7.33:1. RP132 already had this shape for the teal; it is now the rule
       for all eight rather than an exception for one.

       ⚠️⚠️ PRIMARY/OPEN KEEPS #21A3AB (product owner, 2026-08-24). The RP160 table computed #349BA1
       for this slot; #21A3AB was already SHIPPED and browser-measured under RP132, and a second teal
       for one job is exactly the drift these tokens exist to prevent — the same call RP160 made for
       Edit. It is also the BETTER value, not a compromise: 4.97:1 against #349BA1's 4.58:1.
       ⚠️ It is the value the note bars above use on this ground, so the profile area's teal ink is
       ONE colour rather than two near-misses.
       ⚠️ And it is deliberately NOT a re-point of `--rc-action-primary`: that token is the FILL of
       `.account-detail__button`, where white on it is 4.60:1, so re-pointing it would fix the link
       and break the button in one edit.

       ⚠️ THE PASTEL EDGE IS NOT TABLED — it is derived with `color-mix` from the fill it borders, in
       `syncfusion-brand.css`. See RP159: derive a decoration's colour from the colour being painted,
       never from a copy of it. */
    --rc-action-primary-ink: #21A3AB;
    --rc-action-edit-ink: #5B8DEF;
    --rc-action-status-ink: #CF68C0;
    --rc-action-move-ink: #967DE6;
    --rc-action-record-ink: #3D9C97;
    --rc-action-restore-ink: #B48936;
    --rc-action-document-ink: #5F9B65;
    --rc-action-destructive-ink: #DD717A;
    /* ⚠️⚠️ WHATSAPP IS DELIBERATELY ABSENT FROM THIS BLOCK, AND THE ABSENCE IS THE DECISION. Every
       family above is OUTLINED, so its ink is text on a dark card and has to be lifted. WhatsApp is
       FILLED — brand green with a dark glyph — so both halves travel with the button: the glyph is
       9.38:1 on the fill whatever is behind it, and the fill is 7.63:1 on this #1F2635 panel.
       ⛔ An override here would repaint the glyph #25D366 on a #25D366 fill. */

    /* ⚠️⚠️ THE ALERT RED, LIFTED FOR A DARK GROUND (product owner, 2026-08-25). #A11B1B is the right
       red on white and measures 1.94:1 on the #1F2635 card — dark-on-dark, which reads as absent
       rather than as urgent. #fe6868 measures 5.33:1 there and 4.70:1 on the 12%-coral chip tint.

       ⚠️ Declared ONLY here, and every rule keeps #A11B1B as its `var()` FALLBACK, so the light theme
       is unchanged BY CONSTRUCTION rather than by two values happening to agree — RP160's shape.
       ⚠️⚠️ IT IS AN **INK**, NEVER A FILL (RP171). Two rules paint an appointment with #A11B1B as a
       BACKGROUND and put white on it — the diary's outstanding event and the roster's unfilled gap —
       and lifting those would drop white-on-red to about 2.8:1. They keep the strong red.
       ⚠️⚠️ AND IT IS NOT APPLIED WHERE THE PANEL ITSELF STAYS LIGHT. The error toast, the wizard's
       error note, the invoice "late" chip and the billing-run error all paint an opaque #FBDCDC-family
       ground that the dark theme does not override, so #A11B1B is still the readable ink on them —
       lifting it there would put #fe6868 on pale pink. ⬜ **Those panels being light on a dark page is
       its own gap, raised with the product owner and deliberately not fixed here.**
       📌 The badges and the maintenance room chip already carry their own dark overrides and are
       untouched, so this token does not give them a second opinion. */
    --rc-danger-ink: #fe6868;

    /* ⚠️⚠️ THE WARNING INK, THE OTHER HALF OF THE PAIR (product owner, 2026-08-25 — "all three should
       change together"). `#7A5300` is the right amber on white and measures ~2.2:1 on the #1F2635 card.
       Three rules ink text with it and set NO ground of their own — `.wizard-field__hint--warning`,
       `.wizard-actions__reason` and `.rc-invoice-totals__warning` — so all three were dark-on-dark.
       ⚠️⚠️ IT REUSES THE DARK THEME'S EXISTING AMBER (`--rc-action-restore-ink`) RATHER THAN ADDING A
       FOURTH COLOUR. The workstream already carries three reds by role and that is recorded as a cost;
       repeating it in amber would be doing it on purpose. **One amber on dark, two names for it — the
       role that needs it and the action family that already had it.**
       ⚠️ Declared ONLY here, with `#7A5300` kept as each rule's `var()` fallback, so the light theme is
       unchanged BY CONSTRUCTION — the same shape as `--rc-danger-ink`. */
    --rc-warning-ink: #B48936;

    /* PC61 — the mock's own values, exactly: ground #3A2D14, rule #7A5A1F, title #FFD592, text #E7CFA3. */
    --rc-pending-ground: #3A2D14;
    --rc-pending-line: #7A5A1F;
    --rc-pending-title: #FFD592;
    --rc-pending-text: #E7CFA3;

    color-scheme: dark;
}

/* ⚠️⚠️⚠️ THESE THREE LIGHT DEFAULTS USED TO SIT HERE, AFTER THE DARK BLOCK, AND THAT BROKE ALL THREE
   TOKENS THE MOMENT THE THEME WENT PRODUCT-WIDE (found and fixed 2026-08-25, RP178).
   `:root` and `[data-rc-theme="dark"]` are BOTH specificity (0,1,0). While the attribute lives on a
   `<div>` — `AccountLayout`'s shell — the two match DIFFERENT elements and the nearer one wins, so the
   dark values applied and everything looked correct. Move the attribute to `<html>`, which is what §4
   calls "one line, no rewrite", and both selectors match the SAME element: the tie falls to source
   order and the LIGHT values win INSIDE THE DARK SCOPE.
   ✅ Measured on the published site with the attribute flipped onto `<html>`:
   `--rc-color-inset` resolved to **#F6F6F6** on a dark page, and `--rc-note-accent` — which is what
   `RcAlert` paints its bar with — resolved to the light **#01828A**.
   📌 They have been moved ABOVE the dark block, which fixes the cascade and keeps the pair adjacent,
   which is what the original note wanted. See the block below. */

/* ======================================================================
   The page heading's focus ring.

   `Routes.razor` uses <FocusOnNavigate Selector="h1" />, which stamps tabindex="-1"
   on the page's <h1> and focuses it after every navigation so a screen-reader user
   lands on the heading rather than at the top of the chrome. That is worth keeping.

   The side effect is that the browser then paints its DEFAULT focus ring around the
   heading — a black box across the full content width, on every page, after every
   navigation. It looks like a rendering fault and was reported as one.

   ⚠️ Removing the ring here is safe, and ONLY here. tabindex="-1" means the element
   cannot be reached by tabbing, so this focus is always programmatic — there is no
   keyboard user whose position is being hidden. Do NOT generalise this rule to
   :focus on other elements; every genuinely tabbable control must keep a visible
   focus indicator.
   ====================================================================== */
h1[tabindex="-1"]:focus,
h1[tabindex="-1"]:focus-visible {
    outline: none;
}

/* ════════════════════════════════════════════════════════════════════════
   THE FAMILY PORTAL'S OWN SCREENS.

   ⚠️⚠️ THIS IS THE PORTAL'S HOME SHEET, AND IT IS NO LONGER THE ONLY ONE IT
   LOADS. This block used to say "ResidentPortalLayout links this file and
   nothing else"; that stopped being true at TS14, when the shell took the
   Syncfusion theme for the testimonials rating — and syncfusion-brand.css
   came with it, because the three are a cascade contract rather than a menu.
   ⚠️ CORRECTED 2026-09-05, having been relied on while false: it is what
   makes `rc-num` and `rc-table--stacked` (both declared in
   syncfusion-brand.css) work on the portal's own tables. Read
   ResidentPortalLayout's own HeadContent before believing either version.

   ⚠️⚠️ WHAT IS STILL TRUE, AND IS THE REASON RULES BELONG HERE: a rule this
   shell needs is written in THIS file. syncfusion-brand.css scopes much of
   its own furniture to `.account-shell__body` or keys it on `.e-btn`, so a
   portal rule dropped in there can silently reach nothing.

   ⚠️⚠️ AND `.btn` IS NOT AVAILABLE TO THIS SHELL AT ALL. It is declared once,
   as `.login-shell__body ::deep .btn` in LoginLayout's SCOPED sheet (P-D153),
   so `class="btn btn-primary"` on a portal page is a raw browser button. Use
   `.rc-portal-btn` below. It fails in complete silence — no build error, no
   console warning, no failing test — and merely looks like markup nobody
   finished, which is how the invoices list's own "Pay now" shipped unstyled.
   ════════════════════════════════════════════════════════════════════════ */

/* ---- The family portal's invoices screen (stage 10). ---- */

.rc-portal-invoices {
    width: 100%;
    border-collapse: collapse;
}

.rc-portal-invoices th,
.rc-portal-invoices td {
    padding: 0.6rem var(--rc-table-cell-padding-x, 0.75rem);
    border-bottom: 1px solid var(--rc-color-border);
    text-align: left;
}

.rc-portal-invoices th {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--rc-color-muted);
}

.rc-portal-balance {
    font-size: 1.05rem;
}

/* ⚠️ The word "overdue" is IN THE TEXT, not conveyed by this colour — a colour-blind
   reader must get the same information. The panel only ever renders for an invoice
   where payment is actually required, which is what keeps a fee-free resident off it. */
.rc-portal-overdue {
    padding: 0.75rem 1rem;
    border-radius: var(--rc-radius, 0.5rem);
    background-color: #FBDCDC;
    color: #A11B1B;
}

.rc-portal-pay {
    display: grid;
    gap: 0.5rem 1.5rem;
    margin: 0 0 0.75rem;
}

.rc-portal-pay div {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.rc-portal-pay dt {
    min-width: 9rem;
    color: var(--rc-color-muted);
}

.rc-portal-pay dd {
    margin: 0;
}

.rc-portal-pay__note {
    font-size: 0.875rem;
    color: var(--rc-color-muted);
}


/* ---- The family portal's SECTION TILES (feature 5, PS9 / PS10). ----

   ⚠️⚠️⚠️ THIS IS THE THIRD COPY OF THIS TILE TREATMENT, AND THAT IS A KNOWN COST
   RATHER THAN AN OVERSIGHT. `ResidentProfile.razor.css` and
   `AccountDashboard.razor.css` each carry their own, because BOTH ARE SCOPED
   stylesheets and scoped CSS is never shared between components (P-D20). Writing
   `rc-profile-tile` here would compile to a selector that matches nothing — the
   class would be plainly present in the markup and do absolutely nothing, which is
   the P-D153 trap.

   ⚠️ PROMOTION IS OWED AND IS ITS OWN SLICE. Merging the three means editing two
   accepted, published screens, which is not this feature's scope. The five accent
   hues below are COPIED FROM `ResidentProfile.razor.css` — its comment carries the
   measured contrast ratios (blue 5.17/6.70, teal 3.74/5.47, violet 5.70/7.10,
   red 4.83/6.47, green 3.30/5.02, white-on-badge / ink-on-white). **Change them
   together or the two sides of the product drift.**
   ──────────────────────────────────────────────────────────────────────── */

.rc-portal-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.75rem;

    /* ⚠️ RP35: every card in a row takes the height of the tallest. `stretch` is
       already the default — it is stated so the next person does not "tidy" it
       into `start` and give the row several bottom edges. */
    align-items: stretch;
}

.rc-portal-tile {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    padding: 0.875rem;

    background-color: var(--rc-color-background);
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-md);

    /* ⚠️ The portal shell underlines every non-button anchor (see its layout's scoped
       sheet). A tile is a card, not a sentence, so it opts out — and it must opt the
       LABEL out too, because text-decoration propagates from the anchor to its
       in-flow descendants. */
    text-decoration: none;
}

/*  ⚠️ 38px at a 10px radius — the admin tile's own metrics, so a reader meets ONE
    tile in this product rather than three. Deliberately not `--rc-radius-sm`, which
    is the control radius and would read as a button rather than as a mark. */
.rc-portal-tile__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 38px;
    height: 38px;

    background-color: var(--rc-tile-badge, var(--rc-color-primary-dark));
    border-radius: 10px;
    color: #ffffff;
}

.rc-portal-tile__label {
    font-weight: 500;
    color: var(--rc-tile-ink, var(--rc-color-text));

    /* ⚠️ Stated again on the label, not only on the anchor: the shell's underline rule
       is a descendant selector and propagation is what actually draws the line. */
    text-decoration: none;
}


/*  The plain fact line — what the section shows. Muted, because the CHIP below is the
    thing that changes and should catch the eye first. Mirrors `rc-profile-tile__fact`. */
.rc-portal-tile__fact {
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--rc-color-muted);
    overflow-wrap: anywhere;
}

/*  The chip: the count or status, as a pill (product owner, 2026-09-06).

    ⚠️⚠️ `margin-block-start: auto` IS WHAT PINS IT TO THE BOTTOM of a tile that RP35 has
    stretched to its tallest neighbour — without it the chips sit at five different
    heights across the grid, which is exactly the ragged edge RP35 exists to remove. It
    needs the tile to be a flex COLUMN; the two rules are one mechanism and deleting
    either leaves the other doing nothing visible.

    ⚠️ `align-self: flex-start` so it hugs its own text rather than stretching the width
    of the tile — a full-width pill reads as a banner, a different weight of statement.

    📌 Copied part for part from `rc-profile-tile__chip`. The two are one design; change
    them together. */
.rc-portal-tile__chip {
    align-self: flex-start;
    margin-block-start: auto;
    padding: 0.16rem 0.5rem;

    background-color: var(--rc-color-inset);
    border-radius: 999px;

    font-size: 0.69rem;
    font-weight: 400;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    line-height: 1.5;
    color: var(--rc-tile-ink, var(--rc-color-muted));
}

/*  ⚠️⚠️ ONLY A CHIP SOMEBODY SHOULD ACT ON TAKES THIS — an overdue balance, an unread
    message. Colouring every chip makes none of them mean anything.
    ⚠️ `--rc-color-secondary-dark` is the DARKENED coral (4.57:1); the raw brand coral is
    3.49:1 and under the text floor.
    ⚠️ The chip's WORDS already say "overdue" or "unread", so the colour is a second
    signal and never the only one. */
.rc-portal-tile__chip--alert {
    background-color: rgb(243 77 89 / 12%);
    color: var(--rc-color-secondary-dark, #D1424D);
}

/*  The third tone: SETTLED. Added 2026-09-25 for the Photographs & marketing tile (product
    owner), and it mirrors the admin profile tile's `--settled` exactly — the same record seen
    from the other side should not read as two different badges.

    ⚠️⚠️ IT IS NOT "GOOD NEWS". The rule it states is "nothing is outstanding here", which is
    why a family who was asked and DECLINED gets this tone: they have given a real answer.
    ⚠️ Same caution as the alert above — the chip's WORDS already say what it means, so the
    colour is a second signal and never the only one.

    📌 Computed: #1F7A34 on the resulting wash is 5.6:1, clear of the 4.5:1 text floor.  */
.rc-portal-tile__chip--settled {
    background-color: rgb(42 122 50 / 12%);
    color: #1F7A34;
}

/*  ⚠️ No `::deep` — unlike the admin tile's rule, these live in a GLOBAL stylesheet, so a
    plain descendant selector reaches NavIcon's <svg> perfectly. Adding `::deep` here
    would compile to a selector matching nothing. */
/* ⚠️ The card heading's mark is named here too — it draws the same `NavIcon` at the same
   38px badge, so the glyph inside must match or the two marks read as different sizes. */
.rc-portal-tile .nav-icon,
.rc-portal-card-head .nav-icon {
    width: 20px;
    height: 20px;
}

/*  The lift, and the border taking the tile's OWN icon colour (product owner,
    2026-09-06) — the admin resident tile's hover, so a clickable card behaves the same
    way in both places. `--rc-tile-badge` is the icon badge's colour, set by the accent
    modifier, which is what makes the border match the icon without naming it twice. */
.rc-portal-tile:hover,
.rc-portal-tile:focus-visible {
    text-decoration: none;
    border-color: var(--rc-tile-badge, var(--rc-color-primary-dark));
    box-shadow: var(--rc-shadow-card-hover, 0 4px 20px rgb(44 55 75 / 16%));
    transform: translateY(-2px);
}

/* ⚠️ Named properties only — never `all`, which would animate the tile's own geometry. */
.rc-portal-tile {
    transition: box-shadow 150ms ease-out, transform 150ms ease-out, border-color 150ms ease-out;
}

/* ⚠️ Motion is opt-out, not opt-in. */
@media (prefers-reduced-motion: reduce) {
    .rc-portal-tile {
        transition: none;
    }

    .rc-portal-tile:hover,
    .rc-portal-tile:focus-visible {
        transform: none;
    }
}

.rc-portal-tile:focus-visible {
    outline: 2px solid var(--rc-color-primary-dark, #017a82);
    outline-offset: 2px;
}

/*  ⚠️⚠️⚠️ ONE COLOUR PER TILE — THIS SUPERSEDES THE "FIVE FAMILIES" DECISION OF
    2026-08-24 (product owner, 2026-09-06). That decision read: "A colour names a
    FAMILY of sections, not a section. Thirteen hues 27° apart are a rainbow nobody
    can learn." The product owner has reversed it: each tile takes its own colour,
    and a tile that exists in BOTH portals takes the SAME colour in both — so
    Contacts is one colour everywhere and that colour is used by nothing else.

    ⚠️⚠️ ONLY THE TILES THAT EXIST IN THE FAMILY PORTAL ARE ASSIGNED HERE. The seven
    care sections ship switched OFF and have no portal page, so they keep the shared
    `violet` (and Incidents `red`) as a PLACEHOLDER FAMILY until each is built and
    reviewed. Those two hues are therefore RESERVED: do not assign either to a tile.

    ⚠️ THE BADGE AND THE INK ARE TWO SHADES OF ONE HUE, AND THAT IS A CONTRAST
    DECISION, not decoration. The badge carries a WHITE GLYPH and need only clear 3:1
    as a non-text graphic; the label is TEXT and must clear 4.5:1.
    📌 Computed white-on-badge / ink-on-white:
       blue 5.17/6.70 · indigo 7.90/8.9 · teal 3.74/5.47 · amber 3.18/5.02 ·
       green 3.30/5.02 · fuchsia 4.71/6.32 · cyan 3.68/5.36 · lime 4.99/6.6 ·
       rose 4.60/6.1 · (reserved) violet 5.70/7.10 · red 4.83/6.47

    ⚠️⚠️ THE TWO PAIRS TO WATCH ARE blue/indigo AND teal/cyan — 22° and 18° apart, so
    they are separated by LIGHTNESS as much as hue. They are deliberately not put on
    adjacent tiles in either portal's reading order. If a future tile needs a hue,
    take it from a gap, not from between these.  */
.rc-portal-tile--blue { --rc-tile-badge: #2563EB; --rc-tile-ink: #1D4ED8; }
.rc-portal-tile--indigo { --rc-tile-badge: #4338CA; --rc-tile-ink: #3730A3; }
.rc-portal-tile--teal { --rc-tile-badge: #0D9488; --rc-tile-ink: #0F766E; }
.rc-portal-tile--cyan { --rc-tile-badge: #0891B2; --rc-tile-ink: #0E7490; }
.rc-portal-tile--green { --rc-tile-badge: #16A34A; --rc-tile-ink: #15803D; }
.rc-portal-tile--lime { --rc-tile-badge: #4D7C0F; --rc-tile-ink: #3F6212; }
.rc-portal-tile--amber { --rc-tile-badge: #D97706; --rc-tile-ink: #B45309; }
.rc-portal-tile--rose { --rc-tile-badge: #DB2777; --rc-tile-ink: #BE185D; }
.rc-portal-tile--fuchsia { --rc-tile-badge: #C026D3; --rc-tile-ink: #A21CAF; }

/*  RESERVED — the care-section placeholder family and the incident warning. */
.rc-portal-tile--violet { --rc-tile-badge: #7C3AED; --rc-tile-ink: #6D28D9; }
.rc-portal-tile--red { --rc-tile-badge: #DC2626; --rc-tile-ink: #B91C1C; }

/*  ⚠️⚠️ THE CARE SECTIONS' OWN COLOURS (2026-09-06). They shared `violet` for one day as a staged
    placeholder, on the reading that they were "unbuilt" — TRUE of the family portal and FALSE of the
    admin resident profile, where all seven are live tiles with real content. The product owner reported
    six identical tiles. **Every tile in either portal now has its own colour.**

    ⚠️⚠️⚠️ THESE FIVE ARE SEPARATED FROM THEIR NEAREST NEIGHBOUR BY LIGHTNESS, NOT BY HUE, AND THAT IS
    DELIBERATE. Sixteen tiles over the wheel is ~22° apart — the spacing the superseded "five families"
    note called a rainbow — so where a hue had to sit close to one already in use, the pair is split by
    making one markedly darker instead. Computed white-on-badge, near neighbour first:
      amber 3.18  vs ochre   4.92   (38° / 43°)
      teal  3.74  vs emerald 7.69   (174° / 162°)
      cyan  3.68  vs sky     7.56   (192° / 200°)
      violet 5.70 vs purple  8.72   (262° / 272°)
      red   4.83  vs orange  3.56   (0° / 25°, far enough on hue alone)
    ⚠️ Ink-on-white for each clears the 4.5:1 text floor: orange 5.18 · emerald 5.48 · sky 5.93 ·
    purple 6.98 · ochre 6.85.  */
.rc-portal-tile--orange { --rc-tile-badge: #EA580C; --rc-tile-ink: #C2410C; }
.rc-portal-tile--emerald { --rc-tile-badge: #065F46; --rc-tile-ink: #064E3B; }
.rc-portal-tile--sky { --rc-tile-badge: #075985; --rc-tile-ink: #0C4A6E; }
.rc-portal-tile--purple { --rc-tile-badge: #6B21A8; --rc-tile-ink: #581C87; }
.rc-portal-tile--ochre { --rc-tile-badge: #A16207; --rc-tile-ink: #854D0E; }

/*  A PORTAL CARD HEADING THAT CARRIES A SECTION'S OWN MARK (product owner, 2026-09-20).

    The overview card on the resident's landing page. It exists because the Overview
    TILE was removed from the Sections grid — its route was that page's own address, so
    it linked to the page a family was already reading — and the card holding the same
    facts is now named after it. The mark is how the card says which section it is.

    ⚠️⚠️ THE COLOURS ARE NOT RESTATED HERE. The heading takes `--rc-tile-badge` and
    `--rc-tile-ink` from a `rc-portal-tile--<colour>` class on the same element, so the
    card and the tiles cannot drift: change that tile's colour and this moves with it.
    A hard-coded blue here would be a second home for one decision.

    ⚠️⚠️ THE SPECIFICITY IS DELIBERATE AND MUST NOT BE "SIMPLIFIED". The portal layout's
    own scoped sheet paints every heading inside the shell —
    `.portal-shell__container ::deep h2 { color: var(--rc-color-dark); margin: 0 0 … }`
    at (0,2,1) — so a plain `.rc-portal-card-head__title` (0,1,0) loses and the heading
    renders in the ordinary dark ink with a margin this flex row has already applied.
    Naming BOTH classes on the wrapper plus the element reaches (0,3,1) and wins
    wherever the two sheets sit in the cascade. */
.rc-portal-card-head {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-block-end: var(--rc-space-md);
}

/*  THE ACTION ROW AT THE FOOT OF A PORTAL CARD — today the Overview card's agreement download.

    ⚠️⚠️ RIGHT-ALIGNED AND SEPARATED FROM THE FACTS ABOVE IT, which is the whole design: the rows above
    are statements and this is a thing to press, so it reads as a second class of content rather than as
    another row whose value happens to be a button. The rule above it is the same hairline the card head
    uses underneath itself, so the card has one visual grammar top and bottom.

    ⚠️ It WRAPS: the label is a sentence, and on a phone a single control pinned right can otherwise sit
    half off the card. `flex-end` with wrapping keeps it right-aligned where there is room and lets it
    fall onto its own line where there is not. */
.rc-portal-card-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--rc-space-sm);
    margin-block-start: var(--rc-space-md);
    padding-block-start: var(--rc-space-md);
    border-block-start: 1px solid var(--rc-color-border);
}

/*  THE FAMILY PORTAL'S "WHAT WAS AGREED" FIGURES — THREE ACROSS (product owner, 2026-10-04).

    ⚠️⚠️ IT CARRIES A CLASS ON PURPOSE, AND THAT IS WHAT MAKES IT WORK. `ResidentPortalLayout`'s plain
    list rule is scoped `dl:not([class])` precisely so a list needing its own shape can take one — a `dl`
    with a class is excluded from the two-column label/value grid rather than fighting it. Read that
    comment before changing either.

    ⚠️⚠️ THREE EQUAL TRACKS, NEVER `auto-fit`. The figures are monthly care, admission and deposit, and
    a track list that reflows on content width would put two on one row and one below on exactly the data
    this card shows — which is what it looked like before. `minmax(0, 1fr)` rather than `1fr` so a long
    figure wraps inside its own column instead of widening the grid.

    ⚠️ Two across on a tablet and one on a phone: three columns of "LKR 80,000.00" are unreadable at
    those widths, and a figure that wraps mid-number is worse than a stacked list. */
.rc-portal-fees {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    /* ⚠️ STRETCH, which is the default and is stated because `CardRowEqualHeightGuardTests` is keyed
       on the SHAPE of a multi-column repeat() and has an empty allow-list — deliberately, so a row of
       equal tracks cannot opt out case by case. It costs nothing here: these items carry no border or
       ground, so a stretched box and a content-height one look identical. */
    align-items: stretch;
    gap: var(--rc-space-md) var(--rc-space-lg);
    margin: 0;
}

.rc-portal-fees dt {
    font-size: 0.9375rem;
    color: var(--rc-color-muted);
}

.rc-portal-fees dd {
    margin: 0;
    font-weight: 500;
}

@media (max-width: 47.9375rem) {
    .rc-portal-fees {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 30rem) {
    .rc-portal-fees {
        grid-template-columns: 1fr;
        gap: var(--rc-space-sm);
    }
}

/*  "(to be confirmed)" beside a room on the family portal's Your stay page (product owner, 2026-09-20).

    ⚠️⚠️ IT IS A QUALIFIER, NOT A SECOND FACT, so it is muted and smaller than the room it follows — a
    family should read "Room 12" first and then learn that it is not settled yet. Given equal weight it
    reads as two competing statements about where the resident is.
    ⚠️ The leading space is a MARGIN rather than a literal space in the markup: the Razor compiler strips
    whitespace-only text between elements, which is exactly what ran a room's number into its name on the
    admin profile (see `rc-room-current__name`). */
.rc-room-expected {
    margin-left: 0.375rem;
    color: var(--rc-color-muted);
    font-size: 0.875rem;
}

/* ⚠️ 38px at a 10px radius — `rc-portal-tile__icon`'s own metrics, so the card's mark
   and the tiles' marks are one shape. */
.rc-portal-card-head__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    background-color: var(--rc-tile-badge, var(--rc-color-primary-dark));
    border-radius: 10px;
    color: #ffffff;
}

.rc-portal-card-head[class*="rc-portal-tile--"] h2.rc-portal-card-head__title {
    margin: 0;
    color: var(--rc-tile-ink, var(--rc-color-dark));
}

/*  THE SEVENTEENTH TILE — the meal menu (MN12, product owner 2026-09-09).

    ⚠️⚠️ CHOSEN BY MEASUREMENT, AND THE OBVIOUS ANSWER WAS THE WRONG ONE. The largest
    gap on the HUE wheel is 56.2 degrees, between lime (86) and green (142) — and it
    is the WORST region available. Hue angles compress in the greens, so the best a
    114-degree candidate reaches is dE 20.5 from its nearest neighbour, against a
    field whose closest existing pair is already 17.6 (indigo/purple). The real room
    is the 51-57 degree band, between ochre (35) and lime (86).

    ⚠️ THE DECIDING AXIS IS LIGHTNESS, NOT THE dE COLUMN. Both neighbours sit at
    L* 47; this is L* 53, so it is split from each by lightness as well as hue —
    the same technique the five care colours above use. A darker candidate scored
    marginally better on dE (29.6) and was REJECTED: at L* 39 it would put a third
    dark yellow-brown into one grid, which reads as muddy however good the number.

    📌 Computed white-on-badge / ink-on-white: mustard 3.97 / 5.26. The badge carries
    a white glyph and needs 3:1 as a non-text graphic; the ink is TEXT and needs 4.5.
    Nearest neighbours: ochre dE 27.2, lime dE 28.1.
    Record: docs/domain/Meal-Menu-Design.md section 9.  */
.rc-portal-tile--mustard { --rc-tile-badge: #8E810B; --rc-tile-ink: #786D0A; }

/*  Photographs & marketing — the same DESATURATED slate the admin profile's Consent &
    marketing tile uses, because it is the same record seen from the other side (the
    Gallery precedent, where one colour is shared for exactly that reason).

    ⚠️⚠️ A NEW HUE WAS NOT THE ANSWER. This grid is already at the spacing the note above
    describes as the limit, and the two colours still unused in the ADMIN palette are this
    portal's own Messages (cyan) and Testimonials (lime). A neutral cannot collide with any
    of them, because it has almost no hue to collide with.

    ⚠️ It is also the honest signal: every other tile here is about the resident's care,
    and this one is a permission and a marketing note.

    📌 Computed white-on-badge / ink-on-white: 10.3 / 14.6 — far clear of the 3:1 the badge
    glyph needs and the 4.5:1 the ink needs as text.  */
.rc-portal-tile--slate { --rc-tile-badge: #334155; --rc-tile-ink: #1E293B; }

/* ---- The family portal's INVOICE DETAIL screen (feature 2, 2026-09-05). ----

   ⚠️⚠️ THE BUTTONS ARE DEFINED HERE BECAUSE `.btn` DOES NOTHING UNDER THIS SHELL.
   It exists only as `.login-shell__body ::deep .btn` in LoginLayout's SCOPED sheet
   (P-D153), so `class="btn btn-primary"` on a portal page renders a raw browser
   button — which is exactly what the invoices list's own "Pay now" had been doing
   since it shipped. PortalGalleryPage already worked this out and says so in its
   own stylesheet; this is the same answer, promoted so both screens share it.

   ⚠️ A `<dl>` under this shell is ALREADY a two-column grid (see the layout's
   scoped sheet), so every definition list below restates its own layout. Omitting
   that does not fall back to "no layout" — it inherits a label/value grid built
   for the resident's details panel. */

.rc-portal-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.45rem 1rem;
    min-height: 40px;

    font: inherit;
    font-weight: 500;
    text-decoration: none;
    border: 1px solid transparent;
    border-radius: var(--rc-radius-sm);
    cursor: pointer;
}

/* ⚠️ 40px minimum on both axes: the portal's readers are elderly and often on a
   phone, and `rc-portal-tap-target` already sets that floor elsewhere in this
   file for the same reason. */
.rc-portal-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgb(1 151 161 / 40%);
}

/* ⚠️ primary-DARK, not primary. White on the plain teal is 3.53:1, under the 4.5:1
   text floor — the same reason the layout's own anchor rule darkens. */
.rc-portal-btn--pay {
    color: #fff;
    background-color: var(--rc-color-primary-dark);
    border-color: var(--rc-color-primary-dark);
}

/* ⚠️ `--rc-action-document` (#2A7A32) is the product-wide download colour (RP27).
   Stated as the token so a download reads the same here as in the admin portal. */
.rc-portal-btn--doc {
    color: #fff;
    background-color: var(--rc-action-document);
    border-color: var(--rc-action-document);
}

.rc-portal-btn:hover {
    filter: brightness(0.94);
}

.rc-portal-invoice__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.625rem;
    margin-top: var(--rc-space-md);
}

/* The identity block and the one figure a family opened the page to read. */
.rc-portal-invoice__top {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem 1.75rem;
    justify-content: space-between;
    align-items: flex-start;
}

.rc-portal-invoice__id {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    min-width: 0;
}

.rc-portal-invoice__number {
    font-family: var(--rc-font-family-heading);
    font-size: 1.5rem;
    font-weight: 700;
    margin: 0;
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

.rc-portal-invoice__label {
    font-size: 0.7rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--rc-color-muted);
}

.rc-portal-invoice__money {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    text-align: right;
}

.rc-portal-invoice__figure {
    font-family: var(--rc-font-family-heading);
    font-size: 1.85rem;
    font-weight: 700;
    line-height: 1.15;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.rc-portal-invoice__figure .rc-portal-invoice__currency {
    font-size: 1rem;
    font-weight: 500;
    color: var(--rc-color-muted);
    margin-right: 0.25rem;
}

/* ⚠️ The state where there is nothing to pay is a SENTENCE, not a 0.00 — and the
   two reasons for it (paid, or never payable) say different things. Muted and
   smaller so it does not read as a figure. */
.rc-portal-invoice__figure--nil {
    font-size: 1.25rem;
    color: var(--rc-color-muted);
    white-space: normal;
}

.rc-portal-invoice__note {
    font-size: 0.8125rem;
    color: var(--rc-color-muted);
}

/* ⚠️ Restates its own layout — see the header note about `<dl>` under this shell. */
.rc-portal-invoice__facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.875rem 1.5rem;
    margin: var(--rc-space-md) 0 0;
    padding: var(--rc-space-md) 0 0;
    border-top: 1px solid var(--rc-color-border);
}

.rc-portal-invoice__facts > div {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.rc-portal-invoice__facts dt {
    font-size: 0.7rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--rc-color-muted);
}

.rc-portal-invoice__facts dd {
    margin: 0;
    font-size: 0.9375rem;
}

/* ⚠️ The WORD carries the overdue state (the list page's own rule); this colour is
   a second signal and never the only one. */
.rc-portal-invoice__facts dd.rc-portal-invoice__overdue {
    color: #A11B1B;
    font-weight: 500;
}

/* The way back to a section's parent, rendered by `PortalBackLink` on every portal
   page below the overview.

   ⚠️ PROMOTED FROM `.rc-portal-invoice__back`, which was the invoice detail's own.
   Removing the tab strip (features 4/5) left every OTHER section with no way back at
   all, so what was one screen's affordance is now the shell's. */
.rc-portal-back {
    display: inline-block;
    margin-bottom: var(--rc-space-sm);
    font-size: 0.9375rem;
}

/* ────────────────────────────────────────────────────────────────────────────
   The family portal's own form controls and notes (PS11).

   ⚠️⚠️ THESE EXIST BECAUSE `.form-control`, `.alert` AND `.btn` DO NOT REACH THIS
   SHELL. All three are declared once, in LoginLayout's SCOPED stylesheet
   (P-D153), so writing them here would render raw browser controls — silently.
   ⚠️ They are named `rc-portal-*` rather than borrowed from `rc-portal-messages__*`:
   a class whose NAME says "messages" sends the next reader to the wrong screen,
   which is the P-D30 mistake even when the rule itself is right.
   ──────────────────────────────────────────────────────────────────────────── */

.rc-portal-input {
    width: 100%;
    padding: 0.5rem 0.75rem;
    font: inherit;
    color: var(--rc-color-text);
    background-color: var(--rc-color-background);
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-sm);
}

/* ⚠️ 44px is the portal's tap-target floor — its readers are elderly and often on a
   phone. `rc-portal-btn` already sets 40px for a control; an input somebody has to
   hit precisely gets a little more. */
.rc-portal-input:not(textarea) {
    min-height: 44px;
}

.rc-portal-input:focus-visible {
    outline: none;
    border-color: var(--rc-color-primary-dark);
    box-shadow: 0 0 0 3px rgb(1 151 161 / 40%);
}

/* A block message. ⚠️ The colour is never the only signal — every caller writes
   words, so the panel reads correctly to somebody who cannot distinguish the tints. */
.rc-portal-note {
    padding: var(--rc-space-md);
    border-inline-start: 3px solid var(--rc-color-border);
    border-radius: var(--rc-radius-sm);
}

.rc-portal-note p:last-child {
    margin-bottom: 0;
}

/* The calm green of `rc-badge--settled`, so "this went well" is one colour across
   the product. Measured 6.5:1 for the body text on this tint. */
.rc-portal-note--success {
    border-inline-start-color: #1F5B29;
    background-color: #E3F3E5;
}

.rc-portal-note--error {
    border-inline-start-color: #A11B1B;
    background-color: #FBDCDC;
}

/* The row a portal form's own controls sit in. */
.rc-portal-form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.625rem;
    margin-top: var(--rc-space-md);
}

/* A secondary portal action — outlined rather than filled, so a row holding both
   has one obvious primary. ⚠️ It inherits every metric from `.rc-portal-btn`,
   including the 40px floor; only the colours are stated here. */
.rc-portal-btn--quiet {
    color: var(--rc-color-primary-dark);
    background-color: transparent;
    border-color: var(--rc-color-primary-dark);
}

/* ⚠️⚠️ THE STATUS BADGE MUST NOT BE STRETCHED BY THE COLUMN IT SITS IN (P-D73).
   `.rc-portal-invoice__id` is `display: flex; flex-direction: column`, and a flex
   item's default `align-items: stretch` applies to the CROSS axis — horizontal in a
   column — so the pill filled the whole card, edge to edge: the right colours and the
   wrong shape. `display: inline-flex` on `.rc-badge` does NOT save it, because a flex
   item is blockified and stretched regardless.
   📌 Fixed HERE rather than on the shared `.rc-badge`, which every admin screen also
   uses — the stretch is a property of THIS container, not of the badge. */
.rc-portal-invoice__id > .rc-badge {
    align-self: flex-start;
}

/* The charge lines. Shares the invoices table's look so the two screens read as
   one place; `rc-table--stacked` (syncfusion-brand.css, which this shell loads)
   turns it into one card per line on a phone. */
.rc-portal-lines {
    width: 100%;
    border-collapse: collapse;
}

.rc-portal-lines th,
.rc-portal-lines td {
    padding: 0.6rem var(--rc-table-cell-padding-x, 0.75rem);
    border-bottom: 1px solid var(--rc-color-border);
    text-align: left;
    vertical-align: top;
}

.rc-portal-lines th {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--rc-color-muted);
}

.rc-portal-lines__description {
    font-weight: 500;
}

/* ⚠️ Only ever rendered for a line the office explicitly shared (F7). The service
   strips the rest, so this element simply has nothing to show for them. */
.rc-portal-lines__detail {
    display: block;
    margin-top: 0.2rem;
    font-size: 0.8125rem;
    font-weight: 400;
    color: var(--rc-color-muted);
    max-width: 46ch;
}

/* ⚠️ Restates its own layout — see the header note about `<dl>` under this shell. */
.rc-portal-totals {
    display: flex;
    flex-direction: column;
    margin: var(--rc-space-md) 0 0;
    margin-inline-start: auto;
    width: min(22rem, 100%);
}

.rc-portal-totals > div {
    display: flex;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 0.3rem 0;
}

.rc-portal-totals dt {
    color: var(--rc-color-muted);
}

.rc-portal-totals dd {
    margin: 0;
    font-variant-numeric: tabular-nums;
}

.rc-portal-totals > div.rc-portal-totals__sum {
    border-top: 1px solid var(--rc-color-border);
    margin-top: 0.25rem;
    padding-top: 0.6rem;
    font-weight: 700;
    font-size: 1.0625rem;
}

.rc-portal-totals > div.rc-portal-totals__sum dt {
    color: var(--rc-color-text);
}

@media (max-width: 47.9375rem) {
    /* On a phone the figure leads the card rather than sitting to the right of a
       number that has wrapped underneath it. */
    .rc-portal-invoice__money {
        text-align: left;
    }

    .rc-portal-invoice__actions .rc-portal-btn {
        flex: 1 1 auto;
    }

    .rc-portal-totals {
        width: 100%;
    }
}

/* The admin twin of this rule lives in syncfusion-brand.css — that screen loads both stylesheets,
   this one loads only this file, so the pair is split rather than shared. */
.rc-portal-message__attachment {
    margin: 0.5rem 0 0;
    font-size: 0.875rem;
}

/* ── The portal's conversation list ──────────────────────────────────────── */

.rc-portal-messages__threads {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* The whole row is the link — a family reaching for a conversation on a phone
   should not have to hit the subject text exactly. */
.rc-portal-thread {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.75rem;
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-sm);
    text-decoration: none;
    color: inherit;
}

.rc-portal-thread:hover {
    border-color: var(--rc-color-primary);
    background: var(--rc-color-surface);
}

.rc-portal-thread__subject {
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.rc-portal-thread__preview,
.rc-portal-thread__when {
    font-size: 0.875rem;
    color: var(--rc-color-muted);
}

.rc-portal-messages__empty {
    color: var(--rc-color-muted);
}

/* ── One conversation, as the family reads it ────────────────────────────── */

.rc-portal-messages__status {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.rc-portal-messages__closed-note {
    font-size: 0.875rem;
    color: var(--rc-color-muted);
    max-width: 40rem;
}

.rc-portal-messages__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.rc-portal-message {
    padding: 0.75rem 1rem;
    border-radius: var(--rc-radius-sm);
    max-width: 44rem;
}

/* ⚠️ Alignment and tint are the SECONDARY signal — each message also names its
   side in words, because position is not readable to a screen reader. */
.rc-portal-message--mine {
    margin-left: auto;
    background: color-mix(in srgb, var(--rc-color-primary) 12%, #fff);
}

.rc-portal-message--home {
    margin-right: auto;
    background: var(--rc-color-surface);
}

.rc-portal-message__meta {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 0.5rem;
    font-size: 0.8125rem;
    color: var(--rc-color-muted);
}

.rc-portal-message__who {
    font-weight: 600;
}

/* pre-wrap keeps the paragraphs a family member typed — a message about a
   relative is often a list, and collapsing it changes how it reads. */
.rc-portal-message__body {
    margin: 0.35rem 0 0;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* ── The reply box ───────────────────────────────────────────────────────── */

.rc-portal-messages__label {
    display: block;
    font-weight: 600;
    margin-bottom: 0.35rem;
}

.rc-portal-messages__input {
    width: 100%;
    min-height: 8rem;
    padding: 0.5rem;
    font: inherit;
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-sm);
    resize: vertical;
}

.rc-portal-messages__error {
    margin-top: 0.75rem;
    color: var(--rc-color-secondary);
}

/* ⚠️ The portal raises no toast — there is no circuit — so this inline note is
   the ENTIRE success signal. A write that says nothing is indistinguishable
   from one that silently did nothing. */
.rc-portal-messages__sent {
    margin-top: 0.75rem;
    color: var(--rc-color-green);
    font-weight: 600;
}

@media (max-width: 47.9375rem) {
    .rc-portal-message {
        max-width: none;
    }
}

/* ── Status badges — SHARED between the admin shell and the family portal ──────
   ⚠️ Here, not in syncfusion-brand.css, because the portal links only this file.
   They are our own markup, not Syncfusion overrides, so nothing about them
   belongs in the Syncfusion sheet. Moved 2026-08-05, when the Messages screen
   revealed that the portal's invoice badges had never been styled either. */

/*  ⚠️⚠️ THE TEXT SITS OPTICALLY CENTRED NOW, AND IT DID NOT BEFORE (product owner, 2026-08-19: the
    "Not linked" badge's top and bottom spacing did not match). **Measured on the published page:
    5.00px above the text and 5.80px below.**

    ⚠️⚠️ THE CAUSE IS `line-height`, NOT THE PADDING — which is why adding padding could not have fixed
    it. At `line-height: 1.4` on a 12px font the line box is 16.8px while the font's own box is 14px,
    and the 2.8px of half-leading is NOT split evenly by the font's ascent/descent metrics. `inline-flex`
    with `line-height: 1` takes the leading out of the question altogether and lets the flex centring do
    the work, so the gap above and below is whatever the padding says it is.

    ⚠️ The vertical padding rises 3px → 5px to keep the pill roughly its old height once the leading is
    gone (measured 24.8px before). **This class is product-wide**, so every badge in the application
    moves by a fraction — that is the point: they were all very slightly bottom-heavy.  */
.rc-badge {
    display: inline-flex;
    align-items: center;
    padding: 0.3125rem 0.625rem;
    border: 1px solid transparent;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
}

/* Built-in: RED. Says "this is part of the product and you cannot remove it".

   ⚠️⚠️ RED BY PRODUCT-OWNER DECISION (2026-08-09), replacing the teal below it. It was
   teal-for-system / neutral-for-custom, on the reasoning that a built-in value is
   protected rather than a problem and that red should stay reserved for genuine alerts.
   The product owner asked for red/green twice and that is the decision - recorded here
   rather than argued again, so nobody "restores" the teal in six months.

   ⚠️ These are the exact values from --badge--overdue and --badge--settled below, reused
   deliberately: both were contrast-measured (6.10:1 and better) against their own
   backgrounds, so borrowing the values keeps a property somebody already checked rather
   than inventing two new colours that nobody has.

   ⚠️ Colour is never the only signal - the badge carries the word "System". A
   colour-blind reader gets the same information as anyone else. Do not reduce this to a
   coloured dot.

   📌 SHARED BY TWO SCREENS, and that is the point: reference data
   (/admin/settings/reference-data) and roles (/admin/security/roles) both distinguish
   "built into the product" from "added by you". One meaning, one colour - a System badge
   that is red on one screen and teal on another is the drift these shared classes exist
   to prevent. */
.rc-badge--system {
    background-color: #FBDCDC;
    border-color: #F0A0A0;
    color: #A11B1B;
}

/* Custom: GREEN. Says "you added this, and you may change or withdraw it".
   ⚠️ Same product-owner decision as --system above; it was neutral grey before. */
.rc-badge--custom {
    background-color: #E3F3E5;
    border-color: #A3D2A9;
    color: #1F5B29;
}

/* Overdue: an alert.

   ⚠️⚠️ THIS USED TO READ "the one badge in the product that is an alert … red is reserved
   for things that actually are one". THAT IS NO LONGER TRUE and is corrected rather than
   left to mislead: --badge--system above is red too, by product-owner decision on
   2026-08-09, and shares these exact values. Red on this screen still means "act on
   this"; red on a System badge means "you cannot remove this". They never appear in the
   same table, which is what keeps the two readings apart.

   ⚠️ Red, NOT the brand coral (--rc-color-secondary, #f34d59). The coral is now close
   enough to a warning red that a decorative coral chip reads as an alert - so red is
   reserved for things that actually are one, and this is one.

   ⚠️ Dark red on a pale red tint, not white on bright red. At badge size white-on-red
   is hard to read, and the pale tint keeps a grid of mostly-fine residents calm enough
   that the few that are not stand out. Measured 6.10:1 against its own background,
   clearing the 4.5:1 floor for text this size.

   ⚠️ Colour is never the only signal - the badge carries the word "Overdue". A
   colour-blind reader gets the same information as anyone else. Do not reduce this to
   a coloured dot. */
.rc-badge--overdue {
    background-color: #FBDCDC;
    border-color: #F0A0A0;
    color: #A11B1B;
}

/* Reversed: an APPROVED run that was deliberately undone. Red like overdue, because the one thing a
   reader must not misread is that its figures no longer stand -- it used to fall through to the GREEN
   neutral badge and looked like a settled run. Its own name rather than borrowing `--overdue`, whose
   name is about a date nobody has passed here. */
.rc-badge--reversed {
    background-color: #FBDCDC;
    border-color: #F0A0A0;
    color: #A11B1B;
}

/* Outstanding: billed and unpaid, but not yet due. Ordinary, not a problem - so amber
   rather than red, and deliberately calmer than the overdue badge sitting beside it.
   Measured 6.07:1. */
.rc-badge--outstanding {
    background-color: #FDF0D5;
    border-color: #E8C46A;
    color: #7A5300;
}

/* ⚠️⚠️⚠️ A FOURTH BADGE FAMILY, AND IT WAS RAISED WITH THE PRODUCT OWNER BEFORE IT WAS DRAWN (the
   approved mock, screen 03/07). The existing three are settled, outstanding and cancelled. Reusing
   `--outstanding` was the obvious economy and is WRONG: on every other screen in this product it means
   "owed by a family", and a charge nobody has priced is not owed by anybody — the home does not yet know
   what it is.
   ⚠️ Deliberately close to `--outstanding` in hue rather than a new colour altogether: both say "somebody
   must act", and inventing a fifth hue for a near-identical meaning is how a palette stops being read.
   📌 Darker ink than the ground it sits on, on the same footing as its three siblings above. */
.rc-badge--awaiting-price {
    background-color: #FEF3E2;
    border-color: #E9A85C;
    color: #8A4B08;
}

/* Settled: GREEN. A paid invoice, a completed payment — the good outcome, and the product
   owner asked for it to read as one (2026-08-15).

   ⚠️⚠️⚠️ THIS RULE WAS SILENTLY DISCARDED BY THE BROWSER FROM THE DAY IT WAS WRITTEN UNTIL
   2026-08-15, BECAUSE THIS COMMENT HAD NO OPENING DELIMITER. The block began mid-sentence at
   the word "purpose", so the parser read that stray text, a comment close and the selector as
   ONE invalid qualified rule — and per CSS error recovery, which skips to and including the
   next declaration block, it threw these declarations away with it. Measured, not deduced:
   .rc-badge--settled was ABSENT from document.styleSheets while every sibling modifier was
   present, and a rendered "Paid" badge computed to background rgba(0,0,0,0) with a
   transparent border.

   ⚠️⚠️ AND DO NOT WRITE A COMMENT DELIMITER INSIDE A COMMENT, WHICH IS HOW THE FIRST DRAFT OF
   THIS VERY NOTE REINTRODUCED THE DEFECT. Quoting the opening or closing delimiter to explain
   it closes the comment early and discards the rule below — caught within a minute by
   BadgeStylingTests, which is exactly the failure it was written for. Describe them in words.

   📌 THE LESSON IS THE FAILURE MODE, NOT THE TYPO. A CSS syntax error does not fail the
   build, log anything, or break the rules around it — it removes ONE rule and leaves the
   file looking entirely correct, which is why "Paid" read as unstyled plain text on the
   invoice register, the invoice detail, the resident profile, the family portal AND the
   payments register for months without anybody being able to point at a cause. It is the
   invented-class trap (P-D52) reached by a different route: the class exists, the markup is
   right, the mapping is right, and the rule is not there. ⚠️ Grep the PARSED stylesheet, not
   the source, when a class that plainly exists appears to do nothing.

   ⚠️ The values are --badge--custom's, deliberately: that green was contrast-measured
   against its own background, so this borrows a property somebody has already checked
   rather than inventing a colour nobody has. Dark green on a pale green tint for the same
   reason --badge--overdue is dark red on pale red — at badge size, white on a saturated
   fill is hard to read.

   ⚠️ Colour is never the only signal — the badge carries the word "Paid". Do not reduce
   this to a coloured dot. */
.rc-badge--settled {
    background-color: #E3F3E5;
    border-color: #A3D2A9;
    color: #1F5B29;
}

/* Cancelled and waived are both "nothing to do here", and read as neutral rather than
   as a status on the way somewhere. */
.rc-badge--cancelled,
.rc-badge--waived {
    background-color: var(--rc-color-surface);
    border-color: var(--rc-color-border);
    color: var(--rc-color-muted);
}

/* ══ A RESIDENT'S STATUS — five badges, one per value (product owner, 2026-10-09) ═══════════════════
   `/admin/b/{branch}/residents` printed the enum as plain text while the Financial column beside it
   wore badges, so the one column a reader scans for "who is actually here" was the only one with no
   colour at all.

   ⚠️⚠️ THREE OF THE FIVE REUSE A TRIPLE THAT WAS ALREADY MEASURED, DELIBERATELY. Active takes
   `--settled`'s green, Hospitalised takes `--outstanding`'s amber and Discharged takes the neutral
   `--cancelled` set. Inventing three new colours for meanings this palette already expresses is how a
   palette stops being readable — and it would replace three known contrast ratios with three unknown
   ones. 📌 Only the two with no existing equivalent are new.

   ⚠️⚠️ RED IS NOT USED HERE, AND THAT IS THE DECISION MOST LIKELY TO BE "CORRECTED" LATER. In this
   product red means ACT ON THIS (`--overdue`) or YOU CANNOT REMOVE THIS (`--system`). Neither
   Discharged nor Deceased is either; both are settled facts needing nothing from the reader, and
   painting a death red would make the register read as an alarm. ⛔ Do not give Deceased the overdue
   triple.

   ⚠️ The badge carries the WORD, as every other one does. Colour is the second signal, never the only
   one — five hues in one column is precisely where a colour-blind reader would otherwise be stranded.
   ════════════════════════════════════════════════════════════════════════════════════════════════ */

/* Enquiry: a prospect, not yet resident. Blue — informational, and the one hue in the action palette
   that no badge had taken. ⚠️ The ink is `--rc-action-edit`'s own #0049D0 rather than a near-miss, so
   the product has ONE blue. Measured 6.07:1 on this ground. */
.rc-badge--enquiry {
    background-color: #E2EAFB;
    border-color: #A8BFF0;
    color: #0049D0;
}

/* Active: in residence, the ordinary good state. `--settled`'s green, measured. */
.rc-badge--active {
    background-color: #E3F3E5;
    border-color: #A3D2A9;
    color: #1F5B29;
}

/* Hospitalised: away and expected back — somebody should know, but nothing is wrong with the record.
   `--outstanding`'s amber, measured 6.07:1, and calmer than red for exactly that reason. */
.rc-badge--hospitalised {
    background-color: #FDF0D5;
    border-color: #E8C46A;
    color: #7A5300;
}

/* Discharged: the stay is over and nothing is outstanding. The neutral set, same as `--cancelled`. */
.rc-badge--discharged {
    background-color: var(--rc-color-surface);
    border-color: var(--rc-color-border);
    color: var(--rc-color-muted);
}

/* Deceased: terminal and sombre. ⚠️ It must be DISTINCT from Discharged — both are "nothing to do", and
   the product owner asked for a colour per status — so the neutral set could not serve both. Violet is
   the remaining unused hue (`--rc-action-move`'s family), deepened to #4B2FA8 because the action token
   itself is 4.70:1 on WHITE and would fall under the floor on a tinted ground. Measured 7.56:1. */
.rc-badge--deceased {
    background-color: #EAE6F8;
    border-color: #BFB0EA;
    color: #4B2FA8;
}

/* Unread carries the WORD and the number; the tint is the secondary signal, not
   the only one. */
.rc-badge--unread {
    margin-left: 0.5rem;
    color: #fff;
    background: var(--rc-color-secondary);
}

/* ─────────────────────────────────────────────────────────────────────────────
   Staff register and employee profile (EMP-001/002/003, 2026-08-05)

   In THIS file rather than syncfusion-brand.css because none of it styles markup
   Syncfusion renders — it is all our own. (P-D21: a rule in the wrong sheet is
   dead and fails in complete silence.)
   ───────────────────────────────────────────────────────────────────────────── */

/* ⚠️ "every family at this branch sees it" — the gallery's shared audience (RES-012, GA1).
   It is deliberately in this GLOBAL sheet with the rest of the badge family rather than in the gallery
   page's scoped stylesheet: a scoped rule compiles to `.rc-badge--shared[b-<page>]`, so the portal
   screen (slice 4) writing the same class would get nothing at all — the P-D52 trap from the
   "defined, and inert" side.

   ⚠️ It takes the SECONDARY hue rather than the primary, so it cannot be mistaken for `rc-badge--linked`
   above at a glance — the two appear on different screens but read as the same chip when they share a
   colour. `--rc-color-secondary-dark` (4.57:1), never the raw coral (3.49:1, under the text floor). */
.rc-badge--shared {
    background-color: var(--rc-color-surface);
    border-color: var(--rc-color-secondary-dark);
    color: var(--rc-color-secondary-dark);
}

/* "has a login" — says what it is, and the tint is the secondary signal. It matters
   because terminating this person switches that login off. */
/* Incentive: the share of a charge an employee earns, shown on the service catalogue (2026-10-15).
   ⚠️⚠️ IT REUSES `--enquiry`'S MEASURED TRIPLE RATHER THAN INVENTING A HUE — blue is this palette's
   "informational" colour, and an incentive rate is a fact rather than a state. The three chips it sits
   beside on that register are green, amber and grey, so blue is free there. 📌 It gets its OWN class
   and never `--enquiry`, which means a resident who has not moved in: sharing the class would tie a
   rate's colour to a recolouring of a resident status. Measured 6.07:1, the same pair as `--enquiry`. */
.rc-badge--incentive {
    background-color: #E2EAFB;
    border-color: #A8BFF0;
    color: #0049D0;
}

.rc-badge--linked {
    margin-left: 0.5rem;
    background-color: var(--rc-color-surface);
    border-color: var(--rc-color-primary);
    color: var(--rc-color-primary);
}

/* "has NO login" — the red status badge (product owner, 2026-08-19). It was a plain
   `rc-muted-cell` reading "Not linked", which said the same words in the same grey as
   "Not recorded" two rows above it: an employee who CANNOT sign in looked identical to a
   field nobody had filled in.

   ⚠️⚠️ IT IS ITS OWN NAME RATHER THAN A REUSE OF `--overdue`, THOUGH THE THREE VALUES ARE
   DELIBERATELY IDENTICAL. They are the house red status pair (measured on the invoice
   register), so matching them is the point — but an account's state and a payment's are
   different facts, and sharing one selector means the day somebody restyles "overdue" they
   silently repaint every employee's sign-in status too.
   ⚠️ NO `margin-left`, unlike `--linked` above: that one is written inline after a name on
   the register, this one sits in a layout that supplies its own gap. */
.rc-badge--unlinked {
    background-color: #FBDCDC;
    border-color: #F0A0A0;
    color: #A11B1B;
}

.rc-employee-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-end;
}

.rc-employee-filters__field {
    flex: 1 1 14rem;
    min-width: 12rem;
}

/* A plain <input>, not an SfTextBox (P-D18), so it needs to be styled to sit level
   with the Syncfusion control beside it rather than reading as foreign on the form. */
.rc-employee-filters__search {
    width: 100%;
    padding: 0.5rem 0.75rem;
    font: inherit;
    color: inherit;
    background: var(--rc-color-surface);
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-md);
}

.rc-employee-filters__search:focus {
    outline: 2px solid var(--rc-color-primary);
    outline-offset: 1px;
}

.rc-employee-filters__actions {
    display: flex;
    gap: 0.5rem;
}

.rc-employee-heading {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
    justify-content: space-between;
}

.rc-employee-heading__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.rc-employee-empty,
.rc-employee-error p {
    color: var(--rc-color-muted);
}

.rc-employee-table {
    width: 100%;
    border-collapse: collapse;
}

.rc-employee-table th,
.rc-employee-table td {
    padding: var(--rc-table-cell-padding-x, 0.75rem);
    text-align: left;
    border-bottom: 1px solid var(--rc-color-border);
    vertical-align: top;
}

/*  ⚠️⚠️ RIGHT-ALIGNED AND MIDDLE-ALIGNED, WHICH IS THE WHOLE OF THE "End and Approve look inconsistent"
    REPORT (product owner, 2026-08-19). Every other row-action group in the product is `.rc-row-actions`,
    which carries `justify-content: flex-end` — this one carried NO `justify-content` at all, so it fell
    to `flex-start` and the buttons sat left while every other register's sat right.
    📌 **One missing declaration explained the entire complaint**, across four tables: the designation
    history's `End`, the salary rates' and leave requests' `Approve`, and the employee register's row.
    ⚠️ `align-items: center` is the second half of the same ask — a cell mixing a badge, a reason line
    and a button otherwise aligns them on their box tops.  */
.rc-employee-table__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
}

/* ⚠️ Middle, not baseline. These rows mix badges, two-line reasons and buttons, and `baseline` lines
   them up on the first line of text — which reads as the badge sitting high in its cell. */
.rc-employee-table td {
    vertical-align: middle;
}

/* The decline or cancel reason, under the status badge — the employee is entitled to
   be told, so it belongs on the row rather than behind a click. */
.rc-employee-table__reason {
    display: block;
    margin-top: 0.25rem;
    font-size: 0.875rem;
    color: var(--rc-color-muted);
}

.rc-employee-paging {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    justify-content: flex-end;
    margin-top: 1rem;
}

.rc-employee-paging__label {
    color: var(--rc-color-muted);
}

/* The employment facts. A definition list rather than a table: these are label/value
   pairs, not tabular data, and they must not stack under rc-table--stacked's rules. */
.rc-employee-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1rem 2rem;
    margin: 1rem 0 0;
}

.rc-employee-facts dt {
    font-size: 0.875rem;
    color: var(--rc-color-muted);
}

.rc-employee-facts dd {
    margin: 0.125rem 0 0;
}

.rc-employee-facts__hint {
    margin: 0.375rem 0 0;
    font-size: 0.875rem;
    color: var(--rc-color-muted);
}

/*  ⚠️⚠️ A FACT WHOSE VALUE IS A BADGE PUTS THE LABEL AND THE BADGE ON ONE ROW, MIDDLE-ALIGNED, AND ANY
    BUTTON ON ITS OWN ROW BENEATH (product owner, 2026-08-19). The default `rc-employee-facts` stacks
    `dt` above `dd`, which for a badge left the label adrift above a pill and — on the sign-in account —
    put "Link an account" hard against the words beside it.

    ⚠️⚠️ THE ACTIONS ARE A SECOND `dd`, NOT A `div`. A `div` inside `dl` may contain `dt`s followed by
    `dd`s and nothing else, so a wrapper div for the buttons is not conforming markup — whereas MULTIPLE
    `dd`s per `dt` are exactly the standard's "one term, several values". It also means the grid can span
    it across both columns without the label having to centre against a two-line column, which is what
    `align-items: center` would otherwise do (the label would float mid-way down beside the button rather
    than sit level with the badge).  */
.rc-employee-facts__item--badge {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    column-gap: 0.75rem;
    row-gap: 0.5rem;
}

.rc-employee-facts__item--badge > dd {
    margin: 0;
}

/* ⚠️ `rc-badge--linked` carries `margin-left: 0.5rem` for its ORIGINAL use — written inline after a
   name on the employee register. In this grid the spacing comes from `column-gap`, so that margin is
   an extra 8px pushing the badge off the label's gap. Zeroed here rather than removed from the badge,
   because the register still needs it. */
.rc-employee-facts__item--badge > dd > .rc-badge {
    margin-left: 0;
}

.rc-employee-facts__actions {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
}

/* ⚠️ The hint keeps its own top margin inside the actions row, or it sits on the buttons' baseline. */
.rc-employee-facts__actions .rc-employee-facts__hint {
    flex-basis: 100%;
    margin-block-start: 0;
}

/* A care package's facts. Feature-scoped rather than borrowing .rc-employee-facts: the two happen to
   want the same grid today, and a shared name would tie a package's layout to whatever the employee
   profile needs next. (The documented alternative — inventing a shared class as a side effect of one
   screen — gives every other screen a convention nobody agreed to.) */
.rc-package-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1rem 2rem;
    margin: 1rem 0 0;
}

.rc-package-facts dt {
    font-size: 0.875rem;
    color: var(--rc-color-muted);
}

.rc-package-facts dd {
    margin: 0.125rem 0 0;
}

/* A field that needs the whole dialog width — a description, or a reason. ⚠️ `wizard-field--wide` is
   written on three fields elsewhere and EXISTS NOWHERE (P-D20), so it does nothing at all; this is the
   feature-scoped rule that actually applies. */
.rc-package-field--wide {
    grid-column: 1 / -1;
}

/* Residents a price rollout passed over, named rather than counted (PKG-009). ⚠️ Scrolls rather than
   growing without limit: a package with forty residents on it would otherwise push the dialog's own
   buttons off the bottom of the screen, which is the P-D32 shape — controls present and unreachable. */
.rc-package-skipped {
    margin: 0 0 var(--rc-space-md);
    padding-inline-start: var(--rc-space-lg);
    max-height: 12rem;
    overflow-y: auto;
    color: var(--rc-color-muted);
    font-size: 0.875rem;
}

.rc-package-skipped li {
    margin-bottom: var(--rc-space-xs);
}

/* Approved leave per type. Deliberately NOT called a balance anywhere — there is no
   accrual rule, so an entitlement figure here would be invented. */
.rc-employee-balances {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.5rem;
    margin: 1rem 0 0;
    padding: 0;
    list-style: none;
}

.rc-employee-balances__type {
    font-weight: 600;
}

.rc-employee-balances__value {
    margin-left: 0.375rem;
    color: var(--rc-color-muted);
}

.rc-employee-balances__note {
    margin: 0.5rem 0 0;
    font-size: 0.875rem;
    color: var(--rc-color-muted);
}

.rc-employee-checkbox {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-top: 0.75rem;
}

/* `.rc-employee-back` was removed on 2026-10-02 with the bespoke anchor it styled; the staff profile
   uses the shared RcBackLink now. Noted rather than silently dropped: it is the fourth back-link
   treatment this product turned out to have, and the one that hid from a survey because it drew its
   arrow with the `&larr;` entity instead of NavIcon. */

/* An inline message beside the fields, kept ALONGSIDE the toast rather than instead of
   it: a dialog that stays open on refusal should say why where the person is looking. */
.rc-employee-form-error {
    margin: 0.75rem 0 0;
    color: var(--rc-color-secondary);
}

/* The notification centre's inline failure message (NT2). Feature-scoped rather than a shared
   `rc-form-error`, per the project rule: adding a shared class as a side effect of one screen gives
   every other screen a convention nobody agreed to. */
.rc-notifications-error {
    margin: 0 0 1rem;
    color: var(--rc-color-secondary);
}

/* The audit trail's inline failure message (AUD-001). Feature-scoped for the same reason as
   `rc-notifications-error` above. */
.rc-audit-error {
    margin: 0 0 1rem;
    color: var(--rc-color-secondary);
}

/* The before/after payload on an audit entry. Monospaced and WRAPPING: a serialized state is one long
   line, and letting it overflow would push the dialog wider than the viewport — the detail somebody
   opened the entry to read would then be the part off the edge of the screen. */
.rc-audit-json {
    margin: 0 0 1rem;
    padding: 0.75rem;
    font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
    font-size: 0.85rem;
    color: var(--rc-color-text);
    background: var(--rc-color-surface);
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-sm);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* The audit entry's own facts — when, who, activity, channel. A definition list rather than a table:
   these are label/value pairs about ONE record, so the stacking rule for data tables does not apply. */
.rc-audit-detail {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.35rem 1rem;
    margin: 0 0 1rem;
}

.rc-audit-detail dt {
    font-weight: 600;
    color: var(--rc-color-text-muted);
}

.rc-audit-detail dd {
    margin: 0;
}

/* Below the stacking breakpoint the two columns become one, or a long activity name squeezes the value
   column to nothing. Matches the table rule's 47.9375rem. */
@media (max-width: 47.9375rem) {
    .rc-audit-detail {
        grid-template-columns: 1fr;
        gap: 0 0;
    }

    .rc-audit-detail dd {
        margin: 0 0 0.5rem;
    }
}

/* Says what else an action will do — used where ending employment also disables a login.
   A warning about a consequence, not a failure, so it reads as neither. */
.rc-employee-form-warning {
    margin: 0 0 1rem;
    padding: 0.75rem;
    background: var(--rc-color-surface);
    border-left: 3px solid var(--rc-color-secondary);
    border-radius: var(--rc-radius-md);
}

/* ══ EVERY ALERT PANEL READS AT ONE SIZE (product owner, 2026-08-25) ═══════════════════════════
   ⚠️⚠️⚠️ ONE RULE RATHER THAN A `font-size` ON EACH, BECAUSE EDITING ~30 RULES IS EXACTLY HOW THEY
   DRIFTED APART. Measured before this landed: the family rendered at THREE sizes — `.admin-page-intro__body`
   and `.wizard-note` at 0.875rem, the three `.account-detail__*` panels at 0.94rem, and SEVENTEEN
   error/alert panels stating no `font-size` at all, which means the BROWSER DEFAULT of 16px: there is no
   base `font-size` anywhere in this product, `body` sets only `margin`.
   📌 The page intro is the reference because it is the panel a reader meets first on every screen.

   ⚠️⚠️ THESE ARE BLOCK MESSAGE PANELS ONLY. Inline notes are DELIBERATELY smaller and are not in this
   list — `.rc-cell-note` (a second line inside a table cell), `.rc-row__note`, `.rc-contacts__footnote`,
   `.wizard-field__hint--warning`, `.rc-supply-note`. A sweep that swept those would make a table's
   secondary line the same size as its primary one, which is the distinction they exist to draw.
   📌 RP192's rule, one axis over: a sweep is a CLASSIFICATION, never a replace.

   ⚠️⚠️ A HEADING INSIDE A PANEL KEEPS ITS OWN SIZE. `.rc-day-alert h2`, `.rc-risk-alert h2` and
   `.rc-incident-alert h2` are 1rem in their own scoped sheets and out-specify this rule, correctly.

   ⚠️ Scoped classes are reachable from here: a global plain-class selector matches an element that also
   carries a scope attribute. It is only the reverse — a scoped rule reaching unscoped markup — that fails.
   ⚠️ Anything that states its OWN `font-size` at equal specificity wins on source order, so the three
   `.account-detail__*` panels are pointed at the token where they are declared rather than listed here. */
.rc-employee-form-warning,
.rc-employee-form-error,
.rc-employee-error,
.rc-room-error,
.rc-clinical-alert,
.rc-clinical-error,
.rc-round-error,
.rc-audit-error,
.rc-incidents-alert,
.rc-incidents-error,
.rc-invoice-error,
.rc-invoice-dialog__warning,
.rc-property-error,
.rc-notifications-error,
.rc-thread-error,
.rc-thread-dialog__error,
.rc-portal-messages__error,
.rc-billing-run__error,
.rc-dashboard-error,
.rc-resident-error,
.rc-users__error,
.rc-access__error,
.rc-photo-dialog__error,
.rc-care-alert,
.rc-care-error,
.rc-day-alert,
.rc-day-error,
.rc-incident-alert,
.rc-incident-error,
.rc-risk-alert,
.rc-risk-error,
.body-map__error,
.onboarding-document__error,
.wizard-note {
    font-size: var(--rc-font-size-note);
}

/* The job description in force, rendered read-only above the editor that will supersede it (RP48).
   It is a QUOTATION of what stands today, so it is inset and muted rather than styled as a field —
   the person is reading it, not editing it, and the editor immediately below is the writable one.
   Scrolls rather than growing: a real job description runs to pages, and letting it push the editor
   off the bottom of the dialog would make the control it exists to inform unreachable. */
.rc-designation-jd-current {
    max-height: 14rem;
    overflow-y: auto;
    padding: 0.75rem;
    background: var(--rc-color-surface);
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-md);
    color: var(--rc-color-muted);
}

/* ⚠️⚠️ THE BOTTOM MARGIN IS NOT SYMMETRY FOR ITS OWN SAKE — WITHOUT IT THE DIALOG'S BUTTONS SIT FLUSH
   AGAINST THIS BORDER (product owner, 2026-10-13, reported on three dialogs at once: the incentive
   category's Treatment box and the Employee incentive box on the catalogue item and the care package).
   `.rc-modal__actions` sets no top margin of its own, deliberately — it is used in dialogs whose last
   element already provides the gap — so the space has to come from whatever precedes it, and a BOXED
   element is the one case where "no gap" reads as a rendering fault rather than as tight spacing.
   📌 One rule rather than three: the same class is on eight screens and the defect is the same on all
   of them, which is why this is fixed here and not at the call sites. */
.rc-employee-fieldset {
    margin: 1rem 0;
    padding: 1rem;
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-md);
}

/* ⚠️ A note or an alert immediately after the people list inside a fieldset would otherwise touch the
   last row (product owner, 2026-10-13). The list is a flex column with its own small gap, which is right
   BETWEEN rows and too tight between the rows and a sentence about them. */
.rc-charge-providers + p {
    margin-block-start: var(--rc-space-md, 1rem);
}

.rc-employee-fieldset legend {
    padding: 0 0.5rem;
    font-size: 0.875rem;
    color: var(--rc-color-muted);
}

/* A checkbox and the words that explain it, on one line, with the whole thing clickable.
   ⚠️⚠️ THIS CLASS WAS ON THE STAFF WIZARD'S TWO STATUTORY CHECKBOXES AND DEFINED NOWHERE — one of the
   19 phantoms P-D56 records, on a screen the product owner has already seen. Unstyled, the box and its
   label were two stacked block elements, so "Contribute EPF for this employee" sat UNDER its own
   checkbox and read as a heading over an unlabelled tick. Defined here rather than replaced with an
   `SfCheckBox`, per P-D53: reuse the convention the neighbouring screen already adopted instead of
   minting a parallel one. */
.rc-checkbox {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    cursor: pointer;
}

.rc-checkbox input[type="checkbox"] {
    flex: 0 0 auto;
    width: 1rem;
    height: 1rem;
    margin: 0;
    accent-color: var(--rc-color-primary);
}

/* The identity scan the staff wizard is holding in memory until submit (PR22), named so the person can
   see WHICH file they attached. ⚠️ The class was on the markup and defined NOWHERE (P-D53), so the
   confirmation rendered as unstyled body text indistinguishable from the hint above it — on a step whose
   whole job is to say "yes, we have your document". */
.rc-staff-scan__attached {
    margin: 0.25rem 0 0.5rem;
    padding: 0.5rem 0.75rem;
    border-radius: var(--rc-radius-sm);
    background: var(--rc-color-surface-muted, rgba(1, 151, 161, 0.08));
    font-size: 0.875rem;
    color: var(--rc-color-dark);
    overflow-wrap: anywhere;
}

/* The roster's week range, under the heading that carries the unfilled count. Feature-scoped rather
   than a shared "subtitle" class — a shared name invented for one screen gives every other screen a
   convention nobody agreed to (P-D20). */
.rc-roster-range {
    margin: 0.25rem 0 0;
    color: var(--rc-color-muted);
}

/* A SECOND LINE inside a table cell — a location under an asset's name, a residual under a cost, a
   schedule under a carrying value, an occurrence number under a description.

   ⚠️ WHY THIS EXISTS RATHER THAN `rc-muted-cell` (2026-08-06, found by looking at the published asset
   register). `rc-muted-cell` sets a colour and nothing else, so it is INLINE — correct when it is the
   whole content of a cell ("top level", "n/a", "none"), and wrong the moment it follows other text:
   the asset register rendered "Profiling bed 57efcaRoom 12", running the name and the location together
   with no boundary a reader could see. Slice 1's expense register had the same latent fault on its
   occurrence and paid-date cells; neither had ever rendered in a screenshot.

   ⚠️ This is not spacing polish, which is deferred until every module exists. Two distinct values with
   no visible boundary misrepresent the data — the same class of fault as a grid clipping its columns on
   a phone. */
/* The muted sentence that explains the value above it.

   ⚠️⚠️ 11px, NOT the 14px it was until 2026-10-09 (product owner, after the care package screen grew a
   second one). At 14px it was EXACTLY THE SIZE AND COLOUR OF THE LABEL above the value — measured — so
   nothing in the type told the eye which of the three to read first, and a five-line note stood four
   times the height of the figure it explained. There are 157 of these across 46 screens, so the screen
   that prompted it is not the only one that was reading as mostly explanation.

   ⚠️⚠️⚠️ THE COLOUR IS DELIBERATELY UNTOUCHED, AND THE FIRST ATTEMPT GOT THIS WRONG. That version also
   dimmed the note to `opacity: 0.85`, which drops it from 4.97:1 to 4.03:1 against the card — under the
   4.5:1 floor for normal text, which applies at every size. 📌 The SIZE costs no contrast at all: the
   muted token holds 4.97:1 at 11px exactly as it did at 14px. ⛔ Do not add transparency here.

   ⚠️ Leading OPENS UP as the type shrinks (1.5 against the old 1.45). Small text set tight is what makes
   a paragraph tiring to read; the saving has to come from the size, not from the line spacing. */
.rc-cell-note {
    display: block;
    margin-top: 0.25rem;
    font-size: 0.6875rem;
    line-height: 1.5;
    color: var(--rc-color-muted);
}

/*  ⚠️⚠️ A VALUE AND A BADGE ON ONE LINE, MIDDLE-ALIGNED AND SPACED (product owner, 2026-08-19 — the
    second half of the badge ask, reported after the first pass missed it).

    ⚠️⚠️⚠️ IT IS A WRAPPER RATHER THAN A MARGIN ON THE BADGE, AND THAT IS FORCED BY THE MARKUP.
    `Senior Nurse<span class="rc-badge">current</span>` puts the badge FIRST among the cell's *element*
    children — the value before it is a TEXT NODE in the Pay cell — so `:not(:first-child)` matches
    nothing there, while a blanket `margin-inline-start` on every badge would indent the ones that ARE a
    cell's whole content (leave status, letter status, remaining hours). **There is no selector that
    separates the two cases**, which is why the pairing is stated in the markup instead of guessed at
    in CSS.

    ⚠️ `inline-flex`, so the cell still lays out as text around it and a `rc-cell-note` after it keeps
    its own line — that note is `display: block` and must NOT be brought inside this wrapper, or it
    becomes a flex item and joins the badge's row.
    ⚠️ `flex-wrap` so a long value plus a badge in a narrow column wraps instead of widening the table. */
.rc-cell-pair {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}


/* ─────────────────────────────────────────────────────────────────────────────
   THE PROFILE AREA'S SHARED PAGE FURNITURE (2026-08-23, P-D154 / RP131)

   The back link, the page title, the panel heading and the aside note that every
   `/account/...` detail page carries. Promoted here out of `PersonalDetails.razor.css` and
   `Security.razor.css`, which each held a byte-identical copy.

   ⚠️⚠️⚠️ THEY WERE DUPLICATED BECAUSE SCOPED CSS CANNOT SHARE A DECLARATION (P-D20), AND THE
   MITIGATION WAS THAT THE TWO FILES NAMED EACH OTHER. Both said so, in capitals, and both were
   right that it did not scale: slice 5 would have made a third copy, at which point naming each
   other stops being a mitigation and becomes a ritual. It is promoted here rather than into
   `syncfusion-brand.css` because these are OUR markup rather than anything Syncfusion renders,
   and because this file is the one every shell links.

   ⚠️⚠️ `::deep` WAS DROPPED ON THE WAY IN, AND IT HAD TO BE. It is a Blazor CSS-isolation
   construct that the build rewrites; a browser does not know it, so `::deep` in a plain
   stylesheet is an unknown pseudo-element, which makes the whole selector invalid and the rule
   is discarded — silently, exactly like P-D92. In a scoped sheet the icon rule needed it because
   `NavIcon` is a COMPONENT and its markup carries its own scope; here there is no scope to
   descend from and a plain descendant selector is both correct and sufficient.

   ⚠️ These are unconditional, not dark-only. The profile area is dark today (RP118) and the
   attribute is expected to move to `<html>` later, so every value below is a token that
   re-points itself rather than a literal.
   ───────────────────────────────────────────────────────────────────────────── */

/* `.account-detail__back` was REMOVED on 2026-10-02: all seven profile-area pages use the shared
   RcBackLink now, so the product has one back-link treatment instead of four. Its metrics live on in
   that component — they were the ones every other back link had already been measured against.

   ⚠️ One difference was deliberate and is recorded there rather than carried over: this rule hovered to
   `--rc-action-primary`, which is a button FILL. RcBackLink hovers to `--rc-action-primary-ink`, the
   token measured for TEXT (4.60:1 on white, re-pointed inside the dark scope). So the converted pages
   gained a contrast fix along the way. */

.account-detail__title {
    margin: 0 0 var(--rc-space-lg);
    font-family: var(--rc-font-family-heading);
    font-size: 1.5rem;
    font-weight: 500;
    color: var(--rc-color-dark);
}

/*  The panel heading, and its one control where a page has one.

    ⚠️ `align-items: center`, and RP35's equal-height rule does NOT apply — that rule is about
    CARDS IN A ROW and its own statement excludes toolbars and action rows. */
.account-detail__heading {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--rc-space-sm);
    margin-block-end: var(--rc-space-md);
}

.account-detail__heading h2 {
    margin: 0;
    font-family: var(--rc-font-family-heading);
    font-size: 1.125rem;
    font-weight: 500;
    color: var(--rc-color-dark);
}

/*  An aside that informs rather than warns — the Personal details boundary note, and the
    Security page's "this signs you out".

    ⚠️⚠️ A NOTE, NOT A WARNING (RP112). Nothing is wrong: each states a consequence or a
    boundary, and colouring either as an alert would make an ordinary, correct page look
    dangerous every time somebody opens it.
    📌 Contrast `.account-detail__empty`, which keeps the coral warn accent and stays in
    `PersonalDetails.razor.css` — that one describes a state the reader should act on. Teal or
    nothing for an aside, coral for something to do, settled by RP119 and RP122. */
.account-detail__note {
    margin: var(--rc-space-lg) 0 0;
    padding-block-start: var(--rc-space-md);
    border-block-start: 1px solid var(--rc-color-border);

    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--rc-color-muted);
}


/* ─────────────────────────────────────────────────────────────────────────────
   THE PROFILE AREA'S FORM CONTROLS (2026-08-23, P-D154 / RP133)

   ⚠️⚠️⚠️ THIS IS THE FAMILY P-D153 SAID WAS OWED, AND IT IS DELIBERATELY *NOT* `.btn` /
   `.form-control` / `.form-group` / `.alert`. Those four are `LoginLayout`'s, defined in its own
   SCOPED stylesheet, and they are inert under every other shell — which is the whole of P-D153.
   Defining them a second time here would give the product two sets of rules for one set of class
   names, in two scopes, with nothing keeping the values in step, and it would make P-D153's own
   guard meaningless. The passkeys pages were CONVERTED to this family instead of the family being
   bent to fit them.

   ⚠️⚠️ IT IS GLOBAL RATHER THAN SCOPED, WHICH IS WHAT MAKES IT REUSABLE AT ALL. These rules began
   in `Security.razor.css`, where `.account-detail__input` had to be written as
   `.account-detail ::deep .account-detail__input` because `<InputText>` is a COMPONENT and renders
   its own markup. A second page writing the same class got nothing — the P-D20 trap that also
   forced the furniture above to be promoted. Here there is no scope, so no `::deep`, and every
   page under the shell gets the same controls.

   ⚠️ Every colour is a token, so the whole family re-points itself if the dark attribute moves to
   `<html>` (RP118). Do not write a literal here.
   ───────────────────────────────────────────────────────────────────────────── */

.account-detail__input {
    display: block;
    width: 100%;
    padding: 0.45rem 0.65rem;

    font-family: inherit;
    font-size: 0.94rem;
    color: var(--rc-color-text);

    background-color: var(--rc-color-surface);
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-sm);
}

.account-detail__input:focus {
    outline: none;
    border-color: var(--rc-action-primary);

    /* ⚠️ A visible ring, because removing the outline without replacing it leaves a keyboard user
       with no idea which of three identical password boxes has focus. */
    box-shadow: 0 0 0 3px rgb(1 151 161 / 25%);
}

/*  ⚠️ The framework's own class name, so it cannot be renamed — `ValidationMessage` emits
    `class="validation-message"` and nothing else. Scoped to the shell rather than left bare, so it
    cannot reach into the admin portal or the login shell, both of which style it themselves.
    ⚠️⚠️ `--rc-note-accent-warn`, NOT an invented `--rc-color-danger`. There is no danger token in
    this theme, and naming one that does not exist resolves to nothing while reading as though a
    palette decision had been made — the invented-class trap in its custom-property spelling. */
.account-shell__body .validation-message {
    display: block;
    margin-block-start: 0.25rem;
    font-size: 0.85rem;
    color: var(--rc-note-accent-warn);
}

/*  The one action on each state of a profile page.

    ⚠️ NOT an `SfButton`: these pages are static SSR, so a component whose behaviour needs a circuit
    would render and not behave. It is styled to match the product's primary button rather than to
    be one — which also means the `rc-action--*` families do not apply, since those rules are keyed
    on `.e-btn`. */
.account-detail__button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.4rem 1rem;
    min-height: 32px;

    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 500;
    color: #fff;

    background-color: var(--rc-action-primary);
    border: 1px solid var(--rc-action-primary);
    border-radius: var(--rc-radius-sm);
    cursor: pointer;
    text-decoration: none;
}

.account-detail__button:hover {
    filter: brightness(0.94);
}

.account-detail__button:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgb(1 151 161 / 40%);
}

/*  An ordinary secondary action beside the primary one — Rename, Cancel.

    ⚠️ Outlined rather than filled, which is P-D116's reasoning one layer down: filled weight is
    reserved for the one action a group wants you to take, so a row offering Rename beside Delete
    must not give either of them it. */
.account-detail__button--secondary {
    color: var(--rc-color-text);
    background-color: transparent;
    border-color: var(--rc-color-muted);
}

/*  ⚠️⚠️ FILLED, and it keeps its weight on purpose. P-D116 records that a destructive CONFIRM stays
    filled — these are the controls built to make somebody stop and read.
    ⚠️ `--rc-note-accent-warn` (#D1424D) rather than the raw `--rc-color-red`: white on it is
    4.57:1, where the raw coral is under the text floor. The same darkened shade
    `rc-action--destructive` moved to in pass B, so a destructive control is one colour across the
    product rather than two near-misses. */
.account-detail__button--destructive {
    color: #fff;
    background-color: var(--rc-note-accent-warn);
    border-color: var(--rc-note-accent-warn);
}

/*  ⚠️⚠️ THE SEPARATION IS P-D100, NOT SPACING. A destructive control stands physically apart from
    ordinary ones, and these are plain `<button>`s rather than `SfButton`s, so the global
    `rc-action--destructive` rules — which are keyed on `.e-btn` — cannot reach them. The row states
    it itself. */
.account-detail__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rc-space-sm);
}

/*  A status badge that follows a row's own title needs a gap of its own.

    Measured on the published site before this rule existed: the Documents page rendered
    "Contract of employmentAwaiting signature" with the two boxes touching at exactly 0.0px, because
    `.rc-badge` is `display: inline-flex` with no margin and Razor collapses the whitespace between a
    text expression and the `@if` that follows it.

    It reads as one run-on string rather than as a title and a status, and only LOOKING found it —
    the row measured correctly in every other respect.

    It is scoped to the profile area's own table rather than added to `.rc-badge`, which is shared by
    every register in the product and is almost always laid out by its own cell.

    It uses a LOGICAL margin, so it follows the writing direction rather than assuming left-to-right,
    and it is inline-start only: a badge that wraps onto its own line then carries no stray indent.

    It also covers `/account/payslips`, whose "correction" badge has the identical shape and shipped
    with the identical gap in slice 5 — latent there only because the review data carries no
    corrected payslip to render one.  */
.account-detail__table .rc-badge {
    margin-inline-start: var(--rc-space-xs);
}

/*  ⚠️⚠️⚠️ A `<form>` WRAPPING ONE OF THE CONTROLS MUST NOT BECOME THE FLEX CHILD. The passkeys row
    posts its Delete through an `EditForm`, so without this the FORM is the flex item and the auto
    margin below — which is on the BUTTON — has nothing to push against, leaving the destructive
    control flush against Rename with nothing failing. `display: contents` makes the form's box
    disappear and promotes the button to the row.
    📌 The same mechanism `UserMenu` records for its Sign out tile, met a second time. */
.account-detail__actions form {
    display: contents;
}

.account-detail__actions .account-detail__button--destructive {
    margin-inline-start: auto;
}

/*  "No employee record is linked to this account." — a state, not a fault.

    ⚠️⚠️ A NOTE, NOT AN ALERT (RP112). Every SystemAdmin is permanently in this state and so is
    `demo.manager@` on the live database, so a danger banner tells the people most likely to open these
    pages that something is wrong with their account, every single time. It was a red
    `alert alert-danger` for the first hour of slice 3, and the PUBLISHED page is what showed how harsh
    that reads — the service refuses correctly, and the SCREEN decides whether a refusal reads as a
    fault.
    ⚠️⚠️ THE ACCENT IS THE WARN ONE, NOT THE ORDINARY TEAL, AND THAT IS NOT DECORATION. RP119 settled
    that teal is an ordinary aside and coral is one the reader should act on, and RP122 gave THIS EXACT
    STATEMENT the coral accent on the dashboard. The same sentence about the same person on two pages
    must not arrive in two different colours — that is what makes a colour mean nothing.

    ⚠️⚠️⚠️ PROMOTED HERE FROM `PersonalDetails.razor.css` ON 2026-08-23 BECAUSE SLICE 5 BECAME A SECOND
    WRITER, AND THE GUARD IS WHAT CAUGHT IT. Written on the payslips page while the rule was still
    scoped, it compiled to `.account-detail__empty[b-<personal>]` and would have rendered as **nothing
    at all** on the new page — present in the markup, present in a stylesheet, inert (P-D20 / P-D52).
    `Every_account_detail_class_is_defined_somewhere_that_reaches_every_page_using_it` failed naming the
    class and both writers. */
.account-detail__empty {
    margin: 0;
    padding: 0.1rem 0 0.1rem 1rem;
    border-inline-start: 3px solid var(--rc-note-accent-warn);
    color: var(--rc-color-muted);
    /* ⚠️ The alert-panel size, NOT this family's 0.94rem body size (product owner, 2026-08-25). It is
       stated here rather than left to the shared rule above because that rule ties on specificity and
       would lose to this block on source order. The area's `__input`, `__lead` and `__link` keep 0.94rem
       — they are body text, not panels. */
    font-size: var(--rc-font-size-note);
    line-height: 1.6;
}

/*  ⚠️ The lead-in is BRIGHTER than the body of its own note, not merely bold — the contrast step is
    what lets the eye take the first clause and move on. ⚠️⚠️ AND IT STAYS INLINE: `display: block`
    turns a one-sentence aside back into a headed panel, which is the box this format exists to
    replace. */
.account-detail__empty strong {
    color: var(--rc-color-dark);
    font-weight: 500;
}

/*  A card's one-line explanation — the passkeys blurb, and "No passkeys are registered."

    ⚠️ Ordinary body copy, brighter than a note: it is the CONTENT of its card rather than an aside
    beside it, which is the distinction `.account-detail__note` draws in the other direction.
    ⚠️⚠️ It is global rather than scoped because TWO pages write it, and that was found by the guard
    rather than by reading: it was removed from `Security.razor.css` under a comment saying it had
    been promoted, and the promotion was not actually made — so for a few minutes the class was
    defined NOWHERE and rendered as nothing on both pages, with a clean build and a green suite.
    P-D52 introduced by the very change that was tidying P-D52 away. */
.account-detail__lead {
    margin: 0 0 var(--rc-space-md);
    font-size: 0.94rem;
    line-height: 1.6;
    color: var(--rc-color-text);
}

/*  "Manage your passkeys →" — an anchor, and one of the permitted ones under the buttons-not-links
    rule: it GOES somewhere rather than doing something.

    ⚠️⚠️ `--rc-action-primary-ink`, NEVER `--rc-action-primary`. As TEXT on this ground the fill
    token measures 3.67:1, under the body-copy floor; see the token's own note above. */
.account-detail__link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;

    font-size: 0.94rem;
    font-weight: 500;
    color: var(--rc-action-primary-ink);
    text-decoration: none;
}

.account-detail__link:hover,
.account-detail__link:focus-visible {
    text-decoration: underline;
}

/*  A refusal from a service.

    ⚠️⚠️ THE CORAL WARN ACCENT. RP119 settled that teal is an ordinary aside and coral is something
    the reader must act on; a failed password change or a passkey that could not be renamed is
    exactly that. Same bar format as every other note in the profile area, so the colour is the
    whole signal. */
.account-detail__alert {
    margin: 0 0 var(--rc-space-md);
    padding: 0.1rem 0 0.1rem 1rem;
    border-inline-start: 3px solid var(--rc-note-accent-warn);
    color: var(--rc-color-text);
    /* ⚠️ The alert-panel size — see `.account-detail__empty` for why it is restated here. */
    font-size: var(--rc-font-size-note);
    line-height: 1.6;
}

/*  A statement of fact that needs no action — "your passkey was renamed", "you are impersonating".

    ⚠️ The ordinary teal accent, and the distinction from the bar above is the whole point: coral is
    something to act on, teal is something to know (RP119). An outcome message rendered in coral
    tells somebody a completed action went wrong. */
.account-detail__notice {
    margin: 0 0 var(--rc-space-md);
    padding: 0.1rem 0 0.1rem 1rem;
    border-inline-start: 3px solid var(--rc-note-accent);
    color: var(--rc-color-text);
    /* ⚠️ The alert-panel size — see `.account-detail__empty` for why it is restated here. */
    font-size: var(--rc-font-size-note);
    line-height: 1.6;
}

/*  A plain list of records on a profile page — today, the passkeys somebody has registered.

    ⚠️ It is NOT `rc-table--stacked`. That family turns each row into a labelled card on a phone and
    needs a `data-label` on every cell; this table has no headings at all, so there is nothing to
    label with and the stacked rules would render blank labels. */
.account-detail__table {
    width: 100%;
    border-collapse: collapse;
}

.account-detail__table td {
    padding: var(--rc-space-sm) 0;
    border-block-end: 1px solid var(--rc-color-border);
    vertical-align: middle;
}

/*  The LAST row draws no separator, because something else always closes the list.

    ⚠️⚠️ MEASURED ON THE PUBLISHED SITE, NOT REASONED ABOUT: on `/account/documents` the final row's
    border landed at y=367.3 and `.account-detail__note`'s own `border-block-start` at y=391.8 — two
    parallel rules 24.5px apart with nothing between them, which reads as an empty row rather than as
    a boundary. `/account/leave` measured the same at 740.8 and 765.3.
    📌 Where no note follows — the entitlement card, the passkeys list — the card's own edge closes the
    list, so the last row's rule was drawing a line immediately above a border either way.
    ⚠️ It is `tr:last-child`, so a list of one row draws no separator at all, which is correct: there
    is nothing below it to be separated from. */
.account-detail__table tr:last-child td {
    border-block-end: 0;
}

/*  ⚠️ A SECOND LINE under a value, never a whole cell's content — the `rc-cell-note` distinction.
    `display: block` is what stops "Unnamed passkeyCreated Aug 3, 2026" running together, which is
    exactly what `rc-muted-cell` does when it is used as a second line. */
.account-detail__meta {
    display: block;
    margin-block-start: 0.15rem;
    font-size: 0.8125rem;
    color: var(--rc-color-muted);
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   THE BADGE FAMILY ON A DARK GROUND (2026-08-25, RP175)

   ⚠️⚠️⚠️ MEASURED, NOT ASSUMED: WITHOUT THIS BLOCK 28 BADGES ACROSS THE ADMIN PORTAL KEEP A PALE
   LITERAL GROUND WHEN THE THEME FLIPS. `rc-badge--settled` (14), `--system` (10), `--overdue`,
   `--outstanding`, `--custom` and `--unlinked` are painted with hard-coded pale hexes — #FBDCDC,
   #E3F3E5, #FDF0D5 — chosen and contrast-checked against WHITE. They are the single largest group of
   light survivors in the whole product, counted by flipping `data-rc-theme` onto `<html>` on five real
   admin pages and auditing what did not respond.

   ⚠️⚠️ THE LIGHT VALUES ARE UNTOUCHED, DELIBERATELY. They were measured (6.10:1 and better) and the
   product owner chose red-for-system / green-for-custom TWICE; this adds a dark variant beside them
   rather than replacing anything, which is the RP126 / RP160 pattern. A badge outside this scope
   computes exactly what it always did.

   ⚠️⚠️ AND THE MEANING SURVIVES. The badge rules above say in their own comments that the WORD carries
   the information as well as the colour, so a colour-blind reader loses nothing — that is unaffected
   here, and the HUE is preserved per family so the two palettes agree about what red and green mean.

   📌 ONE HUE PER FAMILY, AND THE GROUND, THE EDGE AND THE INK ARE ALL DERIVED FROM IT (RP172). Nothing
   below names a second colour, so a family cannot end up with a ground from one hue and an ink from
   another — the drift that made four action families glow green on the button specimen. The hues are
   tokens this product already ships; no new colour is introduced by this block.

   ✅ Computed against the #1F2635 card, which is where badges actually sit — in tables and on cards:
       red    ground #3F2B39   ink #E28A91   5.12:1
       green  ground #213534   ink #7BAD80   5.01:1
       amber  ground #36322C   ink #C5A363   5.33:1
   ⚠️ The GROUND is mixed into the card rather than the inset, and that is load-bearing: mixing into
   the lighter inset raised the ground and dropped every ink under the 4.5:1 floor — measured at 3.2
   to 3.7 across all three families before the base was changed.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* The hue, and only the hue. Everything else is derived from it in the shared rule below. */
[data-rc-theme="dark"] .rc-badge--system,
[data-rc-theme="dark"] .rc-badge--reversed,
[data-rc-theme="dark"] .rc-badge--overdue,
[data-rc-theme="dark"] .rc-badge--unlinked {
    --rc-badge-hue: var(--rc-color-secondary-dark);
}

[data-rc-theme="dark"] .rc-badge--custom,
[data-rc-theme="dark"] .rc-badge--settled,
[data-rc-theme="dark"] .rc-badge--active {
    --rc-badge-hue: var(--rc-action-document);
}

[data-rc-theme="dark"] .rc-badge--outstanding,
[data-rc-theme="dark"] .rc-badge--hospitalised {
    --rc-badge-hue: var(--rc-action-restore);
}

/* ⚠️⚠️⚠️ THE TWO NEW STATUS HUES TAKE THE `-ink` TOKENS, NOT THE LIGHT ONES BESIDE THEM, AND THE
   DIFFERENCE IS MEASURED RATHER THAN STYLISTIC. The light `--rc-action-edit` (#0049D0) run through the
   62%-hue derivation below lands at **4.27:1** on its own ground — under the floor — because it is a
   dark blue being asked to act as a light ink. `--rc-action-edit-ink` (#5B8DEF) is the value this
   theme already uses for the same meaning and measures **5.79:1**; `--rc-action-move-ink` measures
   **5.83:1**. 📌 The green and amber above get away with the light token only because both are
   mid-tone; **a hue's suitability here is a property of its LIGHTNESS, so check a new one rather than
   following the neighbouring line.** */
[data-rc-theme="dark"] .rc-badge--enquiry,
[data-rc-theme="dark"] .rc-badge--incentive {
    --rc-badge-hue: var(--rc-action-edit-ink);
}

[data-rc-theme="dark"] .rc-badge--deceased {
    --rc-badge-hue: var(--rc-action-move-ink);
}

[data-rc-theme="dark"] .rc-badge--system,
[data-rc-theme="dark"] .rc-badge--overdue,
[data-rc-theme="dark"] .rc-badge--unlinked,
[data-rc-theme="dark"] .rc-badge--custom,
[data-rc-theme="dark"] .rc-badge--settled,
[data-rc-theme="dark"] .rc-badge--outstanding,
[data-rc-theme="dark"] .rc-badge--enquiry,
[data-rc-theme="dark"] .rc-badge--incentive,
[data-rc-theme="dark"] .rc-badge--active,
[data-rc-theme="dark"] .rc-badge--hospitalised,
[data-rc-theme="dark"] .rc-badge--deceased {
    background-color: color-mix(in srgb, var(--rc-badge-hue) 18%, var(--rc-color-background));
    border-color: color-mix(in srgb, var(--rc-badge-hue) 40%, transparent);

    /* ⚠️⚠️ 62% HUE, AND THE FIRST VERSION SHIPPED 38% BY INVERTING THE MIX. That paler ink measured
       7.50:1 — comfortably over the floor and therefore invisible to a contrast check — but it washes
       the hue out, and the hue is what the badge MEANS: red for system, green for custom. A pale pink
       and a pale cream at 7.5:1 are more readable and less informative, which is the wrong trade for
       this control. 📌 Caught by measuring the published page, where the computed value did not match
       the one that had been validated. **A contrast floor is a floor, not a target.** */
    color: color-mix(in srgb, var(--rc-badge-hue) 62%, #fff);
}

/*  ⚠️ `--linked` keeps its token ground and needs only its INK lifting. `--rc-color-primary` is the raw
    brand teal, which RP132 measured at 3.67:1 on this ground — under the body-copy floor — and
    `--rc-action-primary-ink` is the value that area already uses, so the profile's teal and this
    badge's teal stay one colour rather than two near-misses. */
[data-rc-theme="dark"] .rc-badge--linked {
    border-color: var(--rc-action-primary-ink);
    color: var(--rc-action-primary-ink);
}

/*  ⚠️ `--cancelled`, `--waived`, `--discharged` and `--unread` are deliberately ABSENT from this block.
    The first two are already painted entirely from `--rc-color-*` tokens, so they re-point for free —
    stated here so their absence reads as a check rather than an omission. `--unread` is white on the
    brand coral and is unchanged by the ground behind it. */

/* ── The family portal's "Update Details" popup (product owner, 2026-09-07) ──────────────────────
   A NATIVE <dialog>, because every portal page is static SSR and `RcModal`/`SfDialog` need a circuit.
   See js/portal-dialog.js for the mechanism and why this is the second documented exception to the
   standing "every add/edit form is a modal" rule.

   ⚠️⚠️ GLOBAL, NOT SCOPED. `PortalContactsPage` has no `.razor.css` of its own, and the classes it
   already uses — `.rc-portal-btn`, `.rc-portal-input` — live here. A scoped copy would compile to
   `[b-xxxxx]` and match nothing (P-D52's trap), with the class plainly present in the markup. */

/* ⚠️ The row that carries "Update Details" under the details list. `justify-content: flex-end` is the
   whole of the right-alignment; the row is a separate element rather than a `<dl>` child because a
   `<dl>` under this shell is ALREADY a two-column grid (see the layout's scoped sheet), so a button
   dropped inside it would land in the value column and inherit a label/value track. */
.rc-portal-detail-actions {
    display: flex;
    justify-content: flex-end;
    margin-block-start: var(--rc-space-md);
    padding-block-start: var(--rc-space-md);
    border-block-start: 1px solid var(--rc-color-border);
}

/* ⚠️⚠️⚠️ EXACTLY ONE SCROLL CONTAINER, AND GETTING THIS WRONG SHOWS TWO SCROLLBARS SIDE BY SIDE
   (product owner, 2026-09-07 — reported on the published build). The first version put `max-height` on
   BOTH this element and `__body`: a <dialog> whose content exceeds its max-height scrolls on its own,
   and the body then scrolled inside it. Worse, the two heights were EQUAL while this element adds a 1px
   border top and bottom, so the outer bar could travel about 2px — a scrollbar that looks broken rather
   than one that is redundant.
   📌 The rule: this element caps the height and CLIPS (`overflow: hidden`); `__body` is the only thing
   in here allowed to scroll. Adding a `max-height` back to `__body` reintroduces the defect exactly. */
.rc-portal-dialog {
    width: min(40rem, calc(100vw - 2rem));
    max-height: calc(100vh - 4rem);
    overflow: hidden;
    padding: 0;
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-md);
    background-color: var(--rc-color-surface);

    /* ⚠️ A <dialog> has a UA `color: canvastext`, which resolves against the ROOT element's
       colour-scheme rather than this shell's (RP140). Stating the ink here is what stops the same
       inherit-the-wrong-default defect the profile area shipped with. */
    color: var(--rc-color-text);
}

/* ⚠️⚠️⚠️ THE `display` BELONGS TO `[open]` AND NOWHERE ELSE — THIS SHIPPED WRONG ON 2026-09-07 AND THE
   PRODUCT OWNER REPORTED IT AS "the close button is broken".

   A <dialog> is hidden by the UA rule `dialog:not([open]) { display: none }`, and an AUTHOR rule beats a
   UA rule whatever its specificity. So `display: flex` on the bare `.rc-portal-dialog` — added in good
   faith to make the body the only scroller — kept the dialog PERMANENTLY VISIBLE: in normal flow, below
   the cards, with no backdrop, before anybody clicked anything.

   📌 AND IT PRESENTS AS A DEAD CLOSE BUTTON, WHICH IS WHY IT COST A ROUND. `close()` fires correctly and
   removes `open`; the element then still matches `.rc-portal-dialog`, still resolves to `display: flex`,
   and does not move. Nothing throws, nothing logs, and the script is entirely innocent — so the search
   starts at the JavaScript, which is the one place the defect is not.

   ⚠️ `showModal()` sets the `open` attribute itself, so this covers the modal case too; there is no
   second state to write. */
.rc-portal-dialog[open] {
    display: flex;
    flex-direction: column;
}

/* ⚠️ `::backdrop` only exists for a dialog opened with showModal(); an `open` ATTRIBUTE renders the
   dialog in flow with no backdrop at all, which is why the script calls showModal(). */
.rc-portal-dialog::backdrop {
    background-color: rgb(0 0 0 / 45%);
}

/* ⚠️⚠️ THE HEAD IS PINNED, THE BODY SCROLLS UNDER IT. The correction form is seven fields and a text
   area and is already taller than the viewport on a laptop, so with the head inside the scroller the
   close control scrolls out of reach — reachable only by Escape, which is not a control anybody can
   see. `flex: 0 0 auto` is what keeps it out of the shrink. */
.rc-portal-dialog__head {
    flex: 0 0 auto;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--rc-space-md);
    padding: var(--rc-space-lg) var(--rc-space-lg) var(--rc-space-sm);
}

/* ⚠️⚠️ THE ONLY SCROLLER IN THE DIALOG — see the note on `.rc-portal-dialog`. It takes `flex: 1 1 auto`
   and states NO max-height of its own: the cap belongs to the dialog, and a second one here is what
   produced the double scrollbar. `min-height: 0` is required, or a flex item refuses to shrink below
   its content and the overflow never engages. */
.rc-portal-dialog__body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 0 var(--rc-space-lg) var(--rc-space-lg);
}

.rc-portal-dialog__head h2 {
    margin: 0;
}

/* ⚠️ 40px on both axes, the portal's own tap-target floor — the readers are elderly and often on a
   phone, and a close control is the one a person reaches for in a hurry. */
.rc-portal-dialog__close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    min-height: 40px;
    font: inherit;
    font-size: 1.25rem;
    line-height: 1;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--rc-radius-sm);
    color: var(--rc-color-muted);
    cursor: pointer;
}

.rc-portal-dialog__close:hover,
.rc-portal-dialog__close:focus-visible {
    background-color: var(--rc-color-inset);
    color: var(--rc-color-text);
}

/* ⚠️ On a phone the dialog IS the screen, so the rounded corners and the width cap both go — the same
   call `.rc-modal` makes in the admin shell below its own breakpoint.
   ⚠️⚠️ The height cap moves with it and stays on the DIALOG ALONE. A matching `max-height` on `__body`
   stood here until 2026-09-07 and is what put a second scrollbar beside the first. */
@media (max-width: 47.9375rem) {
    .rc-portal-dialog {
        width: 100vw;
        max-width: 100vw;
        max-height: 100vh;
        border: none;
        border-radius: 0;
    }
}

/* ⚠️⚠️⚠️ DEFINED BEFORE IT WAS USED, WHICH IS THE POINT. `.rc-radio` was written into the service
   catalogue's markup and existed in NO stylesheet — P-D52's phantom-class trap, caught by grepping the
   sheets rather than by any suite, because an undefined class renders as plain stacked text that looks
   merely unfinished rather than broken.
   📌 Modelled on `.rc-checkbox` above rather than minting a parallel convention (P-D53), and extended
   with the note line the approved mock draws under each option. */
.rc-radio {
    display: flex;
    gap: 0.5rem;
    align-items: flex-start;
    cursor: pointer;
    padding-block: 0.15rem;
}

/* ⚠️ `flex: 0 0 auto` and a stated size: a flex item's default `align-items: stretch` applies to the
   CROSS axis, so without this the control is pulled to the height of a two-line note beside it (P-D73). */
.rc-radio input[type="radio"] {
    flex: 0 0 auto;
    width: 1rem;
    height: 1rem;
    margin: 0.15rem 0 0;
    accent-color: var(--rc-color-primary);
}

.rc-radio > span {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
}

/* The note under each option. Muted and smaller, because it explains the choice rather than being it. */
.rc-radio small {
    color: var(--rc-color-muted);
    font-size: 0.75rem;
    line-height: 1.35;
}

/* ⚠️⚠️ THE UNPRICED-CHARGE NOTICE ON THE BILLING RUN. Both classes were written into the markup and
   existed in NO stylesheet — P-D52's phantom trap, which renders as unstyled body text that looks merely
   unfinished rather than broken, and which no build or test can see.
   📌 The two variants differ ONLY in colour, deliberately: the layout must not change with the branch's
   policy, or the screen reads as two different screens depending on a setting. */
.rc-billing-run__awaiting {
    border: 1px solid #E9A85C;
    border-inline-start-width: 3px;
    border-radius: var(--rc-radius-sm);
    background-color: #FEF3E2;
    color: var(--rc-color-text);
    padding: var(--rc-space-sm) var(--rc-space-md);
    margin-block-end: var(--rc-space-md);
}

/* ⚠️ Red, because under this policy it is not advice — the run will not proceed. */
.rc-billing-run__awaiting--stop {
    border-color: #B42318;
    background-color: #FEF0EE;
}

.rc-billing-run__awaiting p {
    margin: 0;
}

/* ⚠️⚠️ The remedy sits BESIDE the refused button, not only in the notice above it. A disabled control
   whose reason is three lines away reads as a broken screen rather than a guarded one. */
.rc-billing-run__remedy {
    margin: var(--rc-space-xs) 0 0;
    font-size: 0.8125rem;
    color: var(--rc-color-muted);
}

/* ======================================================================
   SITE BANNER STRIP (SB2, SB3, SB7) — docs/domain/Site-Banners-Design.md
   ----------------------------------------------------------------------
   Here, in the theme sheet, because it is the ONE stylesheet all three
   layouts load: admin, family portal and public website. A page-level
   HeadContent would replace the layout's, so no component declares one.
   The strip carries its own colours inline (each banner states them), so
   nothing here is a theme colour; every ink is currentColor.
   Driven by js/site-banner-strip.js — our own carousel, an approved
   exception to the Syncfusion-only rule (SB7).
   ====================================================================== */
.rc-banner-strip {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 16px;
    min-height: 42px;
    font-size: 0.875rem;
    line-height: 1.45;
    transition: background-color 0.3s ease, color 0.3s ease;
}

.rc-banner-strip__slides {
    flex: 1;
    min-width: 0;
    display: grid;
}

/* Every slide occupies the same grid cell, so the strip is as tall as its
   tallest message and does not jump as it rotates. */
.rc-banner-strip__slide {
    grid-area: 1 / 1;
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.35s ease;
}

.rc-banner-strip__slide--current {
    visibility: visible;
    opacity: 1;
}

.rc-banner-strip__icon {
    flex: none;
    width: 20px;
    height: 20px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: currentColor;
}

.rc-banner-strip__icon svg {
    width: 12px;
    height: 12px;
    fill: var(--rc-banner-slide-bg, #ffffff);
}

.rc-banner-strip__tag {
    flex: none;
    font-size: 0.65625rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    border: 1px solid currentColor;
    border-radius: 4px;
    padding: 0 5px;
    opacity: 0.85;
}

.rc-banner-strip__text {
    flex: 1;
    min-width: 0;
}

/* The chosen text colour wins over anything the rich text or a shell says. */
.rc-banner-strip__text,
.rc-banner-strip__text * {
    color: inherit;
}


/* ⚠️⚠️ THE STRIP OWNS ITS PARAGRAPHS' SPACING (product owner, 2026-09-19: "content doesn't align in
   the middle"). Each area's own stylesheet styles a bare `p` — the admin page gave the message
   margin-bottom 16px and a taller line-height (measured), so a one-line message sat centred with an
   invisible margin under it and read as HIGH. Two classes and an element beat every shell's `p` rule;
   the strip is one line, so its paragraphs carry no margin at all. */
.rc-banner-strip .rc-banner-strip__text p,
.rc-banner-strip .rc-banner-strip__text h1,
.rc-banner-strip .rc-banner-strip__text h2,
.rc-banner-strip .rc-banner-strip__text h3,
.rc-banner-strip .rc-banner-strip__text h4 {
    margin: 0;
    line-height: inherit;
}

.rc-banner-strip .rc-banner-strip__text p + p {
    margin-top: 0.25em;
}
.rc-banner-strip a.rc-banner-strip__cta,
.rc-banner-strip a.rc-banner-strip__cta:hover,
.rc-banner-strip a.rc-banner-strip__cta:visited {
    flex: none;
    font-weight: 600;
    font-size: 0.78125rem;
    border-radius: 6px;
    padding: 4px 10px;
    border: 1.5px solid currentColor;
    white-space: nowrap;
    text-decoration: none;
    color: inherit;
}

.rc-banner-strip__areas {
    flex: none;
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
}

.rc-banner-strip__area {
    font-size: 0.65625rem;
    font-weight: 600;
    border-radius: 999px;
    padding: 1px 7px;
    background: rgba(127, 127, 127, 0.22);
    white-space: nowrap;
}

.rc-banner-strip__area--all {
    background: transparent;
    border: 1px solid currentColor;
}

.rc-banner-strip__controls {
    flex: none;
    display: flex;
    align-items: center;
    gap: 4px;
}

.rc-banner-strip__button {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 0;
    padding: 0;
    background: rgba(127, 127, 127, 0.2);
    color: inherit;
    font: inherit;
    font-size: 0.75rem;
    line-height: 1;
    cursor: pointer;
    display: grid;
    place-items: center;
}

.rc-banner-strip__button:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 1px;
}

.rc-banner-strip__dismiss {
    margin-left: 6px;
    background: transparent;
    font-size: 1.0625rem;
    font-weight: 600;
}

.rc-banner-strip__dismiss:hover {
    background: rgba(127, 127, 127, 0.2);
}

.rc-banner-strip__dots {
    display: flex;
    gap: 4px;
    padding: 0 2px;
}

.rc-banner-strip__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.35;
}

.rc-banner-strip__dot--current {
    opacity: 1;
}

/* The [hidden] attribute must win over the display values above. */
.rc-banner-strip [hidden] {
    display: none;
}

@media (prefers-reduced-motion: reduce) {
    .rc-banner-strip,
    .rc-banner-strip__slide {
        transition: none;
    }
}

/* On a phone the tag and the area chips go, the message wraps, and the
   controls take a line of their own (approved mock, Phone 390). */
@media (max-width: 47.9375rem) {
    .rc-banner-strip {
        flex-wrap: wrap;
        row-gap: 4px;
        padding: 8px 12px;
    }

    .rc-banner-strip__slide {
        flex-wrap: wrap;
        row-gap: 4px;
    }

    /* The message keeps a readable width; the button wraps beneath it rather
       than squeezing it into a column a few words wide. */
    .rc-banner-strip__text {
        flex: 1 1 12rem;
    }

    .rc-banner-strip__tag,
    .rc-banner-strip__areas {
        display: none;
    }

    .rc-banner-strip__controls {
        margin-left: auto;
    }
}

/* ══ The password field's reveal adornment ═══════════════════════════════════════════════════════════

   Shared by every shell that shows a password box: the sign-in page, /account/set-password and
   /account/security (product owner, 2026-09-20 — "add show password functionality to every password UI
   as the login UI").

   ⚠️⚠️⚠️ IT LIVES HERE BECAUSE IT USED TO LIVE IN `LoginLayout.razor.css`, WHICH IS SCOPED TO THE LOGIN
   SHELL. Written into a page under `AccountLayout` the same markup would have rendered as a bare button
   floating beside the field — class present, correct, and inert. That is the P-D154 trap exactly: both
   shells link THIS sheet, and a class only reaches a page whose shell defines it, not merely one that
   links some stylesheet. The rules are unchanged apart from the dropped `.login-shell__body ::deep`
   prefix, so the sign-in page looks as it did.

   📌 The script (`js/password-reveal.js`) owns no markup: both glyphs are rendered and CSS picks one off
   the `is-revealed` class, so the eye's path data lives in `NavIcon` and nowhere else. */
/* ── The password field's reveal adornment (RP78, product owner, 2026-08-20) ─────────────────────────
   ⚠️⚠️ ::deep IS REQUIRED, exactly as it is for .form-control above: this markup arrives through
   @Body, so it carries the PAGE's scope attribute and not the layout's. A plain selector here compiles
   to one that matches nothing — the class would be present in the DOM, correct, and inert (P-D52). */
.password-field {
    position: relative;
}

/* ⚠️ The input's own right-hand padding is opened up to make room for the control. Without this the
   icon sits ON TOP of the last characters of a revealed password, which is precisely the moment
   somebody is trying to read it.
   ⚠️⚠️ IT NAMES THE ELEMENT, NOT A CLASS, BECAUSE THE SHELLS DRESS THEIR INPUTS DIFFERENTLY: the sign-in
   page's boxes are `.form-control` (defined in the login shell's own scoped sheet) and the account
   pages' are `.account-detail__input`. A class-based rule would have opened the padding on one shell and
   let the eye sit over the password on the other — visible only on the screen nobody thought to reopen. */
.password-field input {
    padding-inline-end: 3rem;
}

.password-field__toggle {
    position: absolute;
    inset-block-start: 50%;
    inset-inline-end: 0.25rem;
    transform: translateY(-50%);

    /* ⚠️ A 2.25rem square: this is the whole tap target on a phone, and an icon sized to the glyph
       alone would be well under the 44px guidance with nothing around it to catch a thumb. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;

    /* ⚠️ It must not inherit the shell's .btn treatment — this is an adornment inside a field, not an
       action in the form's own hierarchy, and a second filled button beside Sign in would compete with
       it for the primary action (RP9). */
    padding: 0;
    border: 0;
    background: none;
    border-radius: var(--rc-radius-sm);
    color: var(--rc-color-muted);
    cursor: pointer;
}

.password-field__toggle:hover {
    color: var(--rc-color-primary-dark);
    background-color: rgb(0 173 184 / 10%);
}

/* ⚠️ A visible focus ring, matching .form-control:focus above rather than a new one. This control is
   in the tab order between the password box and Remember me, so a keyboard user meets it. */
.password-field__toggle:focus-visible {
    outline: none;
    color: var(--rc-color-primary-dark);
    box-shadow: 0 0 0 3px rgb(0 173 184 / 20%);
}

/* ⚠️⚠️ BOTH GLYPHS ARE IN THE MARKUP AND CSS PICKS ONE — the script toggles `is-revealed` and owns no
   markup, so the eye's path data lives in `NavIcon` and nowhere else. Default (password hidden) offers
   the plain eye, meaning "reveal"; once revealed it offers the struck-through eye, meaning "hide". */
.password-field__icon {
    display: inline-flex;
}

.password-field__icon--hide,
.password-field__toggle.is-revealed .password-field__icon--show {
    display: none;
}

.password-field__toggle.is-revealed .password-field__icon--hide {
    display: inline-flex;
}

/* ⚠️ The glyph is sized here rather than by `.nav-icon`'s own rules, which are written for a menu rail
   and would draw it at the navigation's size inside a 2.25rem button. */
.password-field__toggle .nav-icon {
    width: 1.25rem;
    height: 1.25rem;
}

/*
    ⚠️⚠️⚠️ THE CLOSING BRACE ABOVE WAS MISSING, AND IT HAD BEEN SINCE THE RULE WAS WRITTEN. This file ended
    with an UNTERMINATED rule, which a CSS parser forgives at end-of-file — so nothing was ever wrong and
    nothing could be. 📌 **The first thing appended after it was swallowed whole**: the pay-lines panel,
    the toggle reset and the employee chip all parsed as further declarations of
    `.password-field__toggle .nav-icon` and were discarded, so the panel rendered as a raw UA button with
    an unsized SVG — a grey block with a giant triangle (product owner, 2026-10-02).

    ⚠️⚠️ **A BRACE COUNT DOES NOT CATCH THIS AND MINE DID NOT.** The file was 325 open against 324 close
    BOTH before and after the append, so comparing the counts to `HEAD` said "unchanged, therefore fine" —
    when what it actually showed was a pre-existing fault that had just become live. **Compare the counts
    to EACH OTHER, not to the previous revision.**

    ⚠️ It is P-D92's family from a third direction: there a stray delimiter discarded one rule, here an
    unclosed rule discarded every rule that followed it. Both fail silently, both leave a stylesheet that
    reads perfectly, and in both the class is plainly present in the file.
*/

/* ═════════════════════════ PAYROLL · the pay-lines panel and the employee chip (RP241) ═════════════════════════

   ⚠️⚠️⚠️ THE PANEL EXISTS BECAUSE A PAY LINE WAS FILED UNDER A HEADING THAT CONTRADICTED IT. The breakdown
   was a HEADERLESS three-column table inside a `colspan="8"` cell, so its columns borrowed the outer
   table's: "Earning" read under *Employee*, "Basic pay" under *Role*, and the amount — right-aligned,
   full width — landed under **Employer cost**, which on an opted-out employee is 0.00 (product owner,
   2026-10-02). 📌 What removes the ambiguity is the panel's OWN `<thead>`, one line above the figures;
   the ground and border are what stop it reading as a continuation of the row above.
   ⚠️ An earlier draft made the panel NARROW to keep its amount away from the outer column edge. That was
   solving the problem twice and it looked unfinished — a 470px card adrift in a 1440px row. Full width,
   with its own headings, is both correct and the professional shape. DO NOT re-narrow it. */

.rc-paylines-cell {
    /* ⚠️ No bottom border: the panel belongs to the employee row above it, and a rule between the two
       would split one person's record into two. The separator comes after the panel instead. */
    border-bottom: 1px solid var(--rc-color-border);
    padding-block-start: 0.625rem !important;
}

.rc-paylines {
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-sm);
    background: var(--rc-color-inset);
    overflow: hidden;
}

/*
    ⚠️⚠️ IT IS A `<button>`, SO IT MUST BE RESET — a UA button brings its own background, border, font and
    centred text, and without these four lines the header strip renders as a grey system button across the
    top of the panel. ⚠️ `font: inherit` is the one most often missed: the label would otherwise fall back
    to the UA's button font and sit in a different face from the panel beneath it.

    ⚠️⚠️ THE WHOLE STRIP IS THE TARGET, NOT THE CHEVRON. A 16px chevron fails the 44px touch floor the
    mobile rule sets, and on a row somebody is scanning it is a dart to hit with a mouse too.
*/
.rc-paylines__head {
    display: flex;
    align-items: center;
    gap: 0.75rem;

    width: 100%;
    padding: 0.625rem 1rem;
    border: 0;
    border-bottom: 1px solid var(--rc-color-border);
    background: transparent;
    font: inherit;
    text-align: start;
    cursor: pointer;
}

.rc-paylines__head:hover .rc-paylines__title,
.rc-paylines__head:focus-visible .rc-paylines__title {
    color: var(--rc-color-dark);
}

/* ⚠️ The count is pushed right rather than the header being `space-between`, because the chevron and the
   title must stay together on the left — `space-between` on three children spreads all three. */
.rc-paylines__count {
    font-size: 0.71875rem;
    color: var(--rc-color-muted);
    margin-inline-start: auto;
}

/*
    ⚠️ The chevron ROTATES rather than swapping glyphs, so there is one piece of path data and no second
    icon to keep in step. It points right when closed and down when open, which is the convention every
    disclosure in this product should follow.
*/
.rc-paylines__chevron svg {
    display: block;
    width: 0.875rem;
    height: 0.875rem;
    fill: none;
    stroke: var(--rc-color-muted);
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: transform 120ms ease;
}

.rc-paylines__head[aria-expanded="true"] .rc-paylines__chevron svg {
    transform: rotate(90deg);
}

/* ⚠️ A collapsed panel has no table beneath it, so its header's bottom rule would be a line under
   nothing. */
.rc-paylines__head[aria-expanded="false"] {
    border-bottom: 0;
}

.rc-paylines__title {
    font-size: 0.65625rem;
    font-weight: 500;
    letter-spacing: 0.075rem;
    text-transform: uppercase;
    color: var(--rc-color-muted);
}


/* ⚠️ The panel's own cells are quieter than the outer table's and carry NO bottom border on the last row,
   so the panel reads as one block rather than as more table. */
/*
    ⚠️⚠️ THE PANEL'S BODY TEXT IS A STEP SMALLER THAN THE TABLE IT SITS INSIDE (product owner,
    2026-10-02). Its headings were already small while its CELLS inherited the browser's 16px — the same
    size as the employee row above — so a breakdown read as being as important as the figures it breaks
    down. 📌 This product sets no base `font-size` anywhere, so a cell with no rule of its own lands at
    16px rather than at anything the design chose; the panel has to state its size to be smaller.
    ⚠️ 0.8125rem is a deliberate step rather than a nudge: a half-size difference reads as a rendering
    inconsistency, where a clear one reads as hierarchy.
*/
.rc-paylines__table th,
.rc-paylines__table td {
    padding: 0.5625rem 1rem;
    border-bottom: 0;
    font-size: 0.8125rem;
}

.rc-paylines__table thead th {
    font-size: 0.625rem;
    font-weight: 500;
    letter-spacing: 0.06875rem;
    text-transform: uppercase;
    color: var(--rc-color-muted);
}

.rc-paylines__table tbody td {
    border-top: 1px solid var(--rc-color-border);
}

/* ⚠️ Stated widths so Kind and Amount do not collapse onto their content and leave Line adrift in the
   middle. Amount keeps a wide track because a six-figure salary plus separators is not short. */
.rc-paylines__kind { width: 10rem; }
.rc-paylines__amount { width: 12.5rem; }

/* ⚠️⚠️ A TAG, NOT PLAIN TEXT. Once a row carries an earning and a deduction together the two must be told
   apart at a glance; read word by word, a long run of lines is where a wrong sign hides. */
.rc-paylines__tag {
    display: inline-block;
    padding: 0.125rem 0.5rem;
    border-radius: var(--rc-radius-xs, 4px);

    font-size: 0.625rem;
    font-weight: 500;

    border: 1px solid var(--rc-color-border);
    color: var(--rc-color-muted);
}

.rc-paylines__tag--earning {
    border-color: var(--rc-color-success);
    color: var(--rc-color-success-ink);
}

.rc-paylines__tag--deduction {
    border-color: var(--rc-action-destructive, #8C3A42);
    color: var(--rc-action-destructive-ink);
}

/* ───────────── the employee's identity line ───────────── */

.rc-employee-ident {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-block-start: 0.25rem;
}

/*
    ⚠️⚠️ THE OPT-OUT IS A CHIP ON THE PERSON, NOT A ROW UNDER THE TABLE (product owner, 2026-10-02). It
    spanned all eight columns beneath the pay lines, where it read as a statement about the TABLE — and on
    a run with twenty people, whose opt-out it described was anybody's guess.
    ⚠️ It is amber rather than red: an opt-out is a lawful choice the employee made, not a fault.
*/
.rc-employee-chip {
    display: inline-block;
    padding: 0.125rem 0.5rem;
    border-radius: var(--rc-radius-xs, 4px);

    font-size: 0.625rem;
    font-weight: 500;
    white-space: nowrap;

    border: 1px solid var(--rc-action-restore, #6B5518);
    color: var(--rc-action-restore-ink);
}

/* ───────────── the run total ───────────── */

/*
    ⚠️ It closes the Employees table so the Net pay column can be checked against the summary card above —
    which is the one arithmetic a reviewer actually performs before approving. The figures come from the
    run's own totals, NOT from adding the rows up here: a footer that re-added its own inputs would prove
    only that addition works (the PR39 lesson).
*/
/*
    ⚠️⚠️ THE TOTAL ROW HAS TO CARRY MORE THAN BOLD TEXT (product owner, 2026-10-02: "it doesn't get
    attention"). Weight alone does not separate it, because the figures above it are already dense and
    right-aligned — it simply read as one more row. Four things do the work together: a tinted band, a
    rule in the BRAND colour rather than the hairline grey every other row uses, more vertical room, and
    a size a step above the rows rather than below.

    ⚠️ The size is `1.0625rem` and not something smaller, which is the trap this product's lack of a base
    `font-size` sets: the rows have no size rule, so they sit at the browser's 16px = 1rem. Anything under
    1rem makes the TOTAL smaller than the figures it totals.

    ⚠️ The ink stays neutral. A coloured total would read as a status — overdue, refused, outstanding —
    and this figure carries no state at all; it is simply the sum.
*/
.rc-employee-table tfoot td {
    border-bottom: 0;
    border-top: 2px solid var(--rc-color-primary);
    background: var(--rc-color-inset);
    padding-block: 0.875rem;
    font-weight: 700;
    font-size: 1.0625rem;
}

.rc-employee-table tfoot .rc-employee-total-label {
    font-size: 0.6875rem;
    font-weight: 500;
    letter-spacing: 0.075rem;
    text-transform: uppercase;
    color: var(--rc-color-muted);
}

/* ── The family's calendar (product owner, 2026-10-12) ────────────────────────────────────────────
   Appointments and day-care stays on one view, in the portal's own card.

   ⚠️⚠️ IN THIS SHEET AND NOT IN syncfusion-brand.css. The family portal links ONLY the theme
   stylesheet, so portal rules written into the Syncfusion file are dead — a recorded trap, and one
   that costs nothing to avoid by putting them where the page can reach them.

   ⚠️⚠️ NO SYNCFUSION AT ALL, by the product owner's approval (2026-10-12): every portal page is
   static SSR, so no Syncfusion component can run here. This is our own markup, as the public site's
   banner strip is. */

.rc-cal-lead {
    color: var(--rc-color-muted);
    margin: 0 0 var(--rc-space-md);
}

.rc-cal-bar {
    display: flex;
    align-items: center;
    gap: var(--rc-space-sm);
    flex-wrap: wrap;
    margin-block-end: var(--rc-space-md);
}

/*  ⚠️ An anchor, not a button: with no circuit the month IS a navigation, and anchor semantics are
    what make it middle-clickable and bookmarkable. This is one of the permitted anchors. */
.rc-cal-nav {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-sm, 0.375rem);
    color: var(--rc-color-text);
    text-decoration: none;
}

.rc-cal-nav:hover,
.rc-cal-nav:focus-visible {
    border-color: var(--rc-color-primary);
    color: var(--rc-color-primary);
}

/*  ⚠️ The ‹ at the cap is RENDERED AND INERT rather than removed, so the › does not jump sideways
    as a family pages back — a control that moves under the pointer is how somebody clicks the wrong
    month. It carries no href, so it is not in the tab order either. */
.rc-cal-nav--end {
    opacity: 0.35;
    cursor: default;
}

.rc-cal-month {
    font-family: var(--rc-font-family-heading);
    font-weight: 500;
    font-size: 1rem;
}

.rc-cal-legend {
    display: flex;
    gap: var(--rc-space-md);
    flex-wrap: wrap;
    margin-inline-start: auto;
    font-size: 0.8125rem;
}

.rc-cal-key {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    color: var(--rc-color-muted);
}

.rc-cal-key i {
    width: 11px;
    height: 11px;
    border-radius: 3px;
    display: inline-block;
}

/*  ⚠️⚠️ THE SAME TWO MEASURED TRIPLES THE STAFF REGISTER USES — amber for an appointment (something
    still to happen) and green for a day-care stay (a settled fact). One arrangement is described one
    way whether a care worker or a family is looking at it. */
.rc-cal-key--appt i,
.rc-cal-chip--appt,
.rc-cal-tag--appt {
    background-color: #FDF2DC;
    border: 1px solid #E8C77C;
    color: #7A4F01;
}

.rc-cal-key--stay i,
.rc-cal-chip--stay,
.rc-cal-tag--stay {
    background-color: #E3F3E5;
    border: 1px solid #A3D2A9;
    color: #1F5B29;
}

.rc-cal-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-md, 0.5rem);
    overflow: hidden;
}

.rc-cal-dow {
    padding: 0.4rem 0.5rem;
    font-size: 0.7rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--rc-color-muted);
    background: var(--rc-color-surface);
    border-bottom: 1px solid var(--rc-color-border);
    text-align: center;
}

.rc-cal-cell {
    min-height: 84px;
    padding: 0.35rem;
    border-right: 1px solid var(--rc-color-border);
    border-bottom: 1px solid var(--rc-color-border);
    display: grid;
    gap: 0.25rem;
    align-content: start;
    min-width: 0;
}

.rc-cal-cell:nth-child(7n + 7) {
    border-right: 0;
}

.rc-cal-cell--out {
    background: var(--rc-color-surface);
}

.rc-cal-n {
    font-size: 0.75rem;
    color: var(--rc-color-muted);
    font-variant-numeric: tabular-nums;
}

/*  ⚠️ `min-width: 0` and the ellipsis together: a grid child holding running text pushes the whole
    page wider without them, and a seven-column grid has no room to give. */
.rc-cal-chip {
    font-size: 0.7rem;
    line-height: 1.3;
    padding: 0.15rem 0.35rem;
    border-radius: 4px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rc-cal-agenda {
    display: grid;
    gap: var(--rc-space-sm);
    margin-block-start: var(--rc-space-lg);
}

.rc-cal-agenda__head {
    font-family: var(--rc-font-family-heading);
    font-size: 0.95rem;
    margin: 0;
}

.rc-cal-row {
    display: grid;
    grid-template-columns: 56px 1fr;
    gap: var(--rc-space-md);
    align-items: start;
    padding: 0.65rem 0.75rem;
    border: 1px solid var(--rc-color-border);
    border-radius: var(--rc-radius-sm, 0.375rem);
}

.rc-cal-row__when {
    text-align: center;
}

.rc-cal-row__d {
    font-family: var(--rc-font-family-heading);
    font-size: 1.2rem;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.rc-cal-row__m {
    font-size: 0.7rem;
    color: var(--rc-color-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.rc-cal-row__what {
    min-width: 0;
}

.rc-cal-row__title {
    font-weight: 500;
}

.rc-cal-row__note {
    color: var(--rc-color-muted);
    font-size: 0.85rem;
}

.rc-cal-tag {
    display: inline-block;
    font-size: 0.7rem;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    margin-inline-start: 0.5rem;
    vertical-align: 1px;
}

.rc-cal-empty,
.rc-cal-foot {
    color: var(--rc-color-muted);
    font-size: 0.85rem;
}

/*  ⚠️⚠️ THE GRID IS DROPPED ON A PHONE AND THE LIST IS THE WHOLE PAGE. Seven columns at 390px is
    unreadable, and the list already carries everything the grid does — so this is a removal rather
    than a second rendering, and the two can never disagree about the month. */
@media (max-width: 47.9375rem) {
    .rc-cal-grid {
        display: none;
    }

    .rc-cal-legend {
        margin-inline-start: 0;
    }
}
