/* ==========================================================================
   Bloom Client Portal — foundation
   See docs/DESIGN-SYSTEM.md. Bootstrap remains the grid/layout engine; its
   custom properties are remapped onto Bloom tokens below so existing markup
   inherits the design system without per-page edits.

   1. Tokens
   2. Bootstrap remap
   3. Reset & base
   4. Typography
   5. Focus & motion
   6. Component skins
   7. Utilities
   ========================================================================== */

/* 1. Tokens ============================================================== */
:root {
    /* Brand ramp — derived from the Bloom logo. --bloom-teal-500 (#4988a7) is the
       exact wordmark hue; it measures 3.92:1 on white, so it is used only where
       >=3:1 is sufficient (logo, gradients, focus rings, large display text).
       Interactive surfaces (buttons, links) use 600/700, which pass AA for text. */
    --bloom-teal-50: #eef5f8;
    --bloom-teal-100: #d5e6ed;
    --bloom-teal-200: #b3d0dc;
    --bloom-teal-300: #89b6c8;
    --bloom-teal-400: #5f97b1;
    --bloom-teal-500: #4988a7;   /* logo wordmark — 3.92:1 */
    --bloom-teal-600: #3a7189;   /* action — white on this: 5.38:1 (AA) */
    --bloom-teal-700: #2b5162;   /* hover — 8.55:1 (AAA) */
    --bloom-teal-800: #244251;   /* active — 10.64:1 */
    --bloom-teal-900: #1b323d;

    /* Warm accent — the logo's flower. Reserved for small, deliberate highlights
       (never primary CTAs) so the portal stays calm and trustworthy. */
    --bloom-coral-500: #e36046;  /* logo petal — decorative only, 3.48:1 */
    --color-accent: #cf4b30;     /* 4.47:1 — icons / large accent text */
    --color-accent-strong: #b23e26; /* 5.82:1 */
    --color-accent-soft: #fdeeeb;

    /* Roles — every text/background pair below is >=4.5:1 unless noted. */
    --color-primary: var(--bloom-teal-600);          /* white on this: 5.38:1 */
    --color-primary-hover: var(--bloom-teal-700);    /* 8.55:1 */
    --color-primary-active: var(--bloom-teal-800);   /* 10.64:1 */
    --color-primary-soft: var(--bloom-teal-50);
    --color-link: var(--bloom-teal-600);             /* 5.38:1 */

    /* Surface ramp. The canvas was #f7f9fc, which is only 1.05:1 against a white card — so
       every surface in the product read as weightless and its 1px border did all the work,
       which is exactly what "borders everywhere" looks like. #eef2f7 lifts a white card to
       1.12:1 (~2.4x the separation) without turning the page grey.
       Sunken has to move with it: at #f1f5f9 it would now be LIGHTER than the canvas, so a
       recessed area inside a card would read as raised.
       Verified against the deepest surface each text role can land on, not just white:
       text 15.88 / muted 6.74 / subtle 4.81 on the canvas, and 15.18 / 6.44 / 4.59 on sunken. */
    --color-bg: #eef2f7;
    --color-surface: #ffffff;
    --color-surface-sunken: #e8edf4;

    /* Ratios below are against --color-surface (#ffffff). "Subtle" also lands on
       --color-bg and --color-surface-sunken, so it is verified against the darkest
       surface it can sit on, not just white: the previous #64748b measured 4.76:1 on
       white but only 4.34:1 on the sunken surface used for card footers and inset
       panels, i.e. it failed AA for normal text wherever it was recessed. #5d6b81 is
       the lightest slate on the same hue that clears 4.5:1 on every surface —
       white 5.40, canvas 5.12, sunken 4.93. */
    --color-text: #0f172a;                           /* 17.85:1 */
    --color-text-muted: #475569;                     /* 7.58:1 */
    --color-text-subtle: #5d6b81;                    /* 5.40:1 — 4.93:1 on sunken */
    --color-text-inverse: #ffffff;

    /* #94a3b8 measures 2.56:1 — dividers only, never text.
       Border darkens in step with the canvas: as the canvas deepens, a #e2e8f0 edge fades into
       it (1.17 -> 1.10), so cards would have gained fill only to lose their outline. #d8e0ea
       restores the edge against the new canvas (1.18) and firms it against white (1.23 -> 1.33). */
    --color-border: #d8e0ea;
    --color-border-strong: #c3cfdd;
    --color-hover: #f1f5f9;
    --color-focus: var(--bloom-teal-600);

    /* Success and warning were verified against white only (5.02:1 each). Once the canvas
       deepened they fell to 4.46 / 4.47 — below AA — and to 4.26 / 4.27 on the sunken surface.
       Darkened 4%, which is imperceptible but restores the floor on every surface they can
       reach: white 5.36 / canvas 4.77 / sunken 4.55, and >= 5.1 on their own tint pills.
       White-on-solid also holds at 5.36 for filled badges. */
    --color-success: #147b3b;
    --color-success-bg: #f0fdf4;
    --color-warning: #ad5009;
    --color-warning-bg: #fffbeb;
    --color-danger: #b91c1c;
    --color-danger-bg: #fef2f2;
    --color-info: #0369a1;
    --color-info-bg: #f0f9ff;

    /* Type */
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;

    --text-h1: clamp(1.5rem, 1.3rem + 0.8vw, 2rem);
    --text-h2: 1.5rem;
    --text-h3: 1.25rem;
    --text-body: 1rem;
    --text-sm: 0.875rem;
    --text-xs: 0.75rem;

    /* Space — 4px base */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;
    --space-8: 4rem;
    --space-9: 6rem;

    --radius-sm: 6px;
    --radius-md: 10px;
    --radius-lg: 14px;
    --radius-full: 9999px;

    --shadow-xs: 0 1px 2px rgb(15 23 42 / 4%);
    --shadow-sm: 0 1px 3px rgb(15 23 42 / 6%), 0 1px 2px rgb(15 23 42 / 4%);
    --shadow-md: 0 4px 12px rgb(15 23 42 / 7%), 0 2px 4px rgb(15 23 42 / 4%);
    --shadow-lg: 0 12px 32px rgb(15 23 42 / 10%), 0 4px 8px rgb(15 23 42 / 4%);
    --shadow-focus: 0 0 0 3px rgb(58 113 137 / 30%);

    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --dur-fast: 120ms;
    --dur-base: 200ms;

    /* Lucide "check" as a mask source, for the rare spot that needs an icon in CSS (::before)
       rather than the <Icon> component — keeps those tick marks off Unicode too. */
    --icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");

    --shell-sidebar-w: 268px;
    --shell-topbar-h: 64px;
    --content-max: 1200px;

    /* Sidebar — a dark operations rail. Every value below is measured against
       --sidebar-bg: inactive 12.02:1, active 17.85:1, section labels 6.96:1.
       The accent is a light teal #6ba9c4 (6.88:1) — the brand hue lifted to pass
       the 3:1 minimum for non-text indicators on the dark rail. */
    --sidebar-bg: #0f172a;
    --sidebar-text: #cbd5e1;
    --sidebar-text-active: #ffffff;
    --sidebar-label: #94a3b8;
    --sidebar-accent: #6ba9c4;
    --sidebar-hover: rgb(255 255 255 / 6%);
    --sidebar-active: rgb(255 255 255 / 10%);
    --sidebar-divider: rgb(255 255 255 / 10%);
}

/* 2. Bootstrap remap ===================================================== */
/* Existing pages use Bootstrap classes; remapping its variables restyles them
   in place rather than requiring markup edits on all 40 routed pages. */
:root {
    --bs-primary: var(--color-primary);
    --bs-primary-rgb: 58, 113, 137;
    --bs-link-color: var(--color-link);
    --bs-link-color-rgb: 58, 113, 137;
    --bs-link-hover-color: var(--color-primary-active);
    --bs-body-bg: var(--color-bg);
    --bs-body-color: var(--color-text);
    --bs-body-font-family: var(--font-sans);
    --bs-body-font-size: var(--text-body);
    --bs-body-line-height: 1.6;
    --bs-secondary-color: var(--color-text-muted);
    --bs-border-color: var(--color-border);
    --bs-border-radius: var(--radius-md);
    --bs-border-radius-sm: var(--radius-sm);
    --bs-border-radius-lg: var(--radius-lg);
    --bs-emphasis-color: var(--color-text);
}

/* 3. Reset & base ======================================================== */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    /* Reserve the scrollbar lane so short pages don't shift when a long one loads. */
    scrollbar-gutter: stable;
}

/* Brand-consistent text selection.
   These were --bloom-blue-200/-900, which do not exist: the palette moved to the teal ramp
   derived from the logo and the blue tokens went with it. Both declarations were therefore
   invalid and dropped, so highlighting a policy number anywhere in the portal painted it the
   browser's default blue — the one place a customer drags across the screen showed a
   non-Bloom colour. */
::selection {
    background-color: var(--bloom-teal-100);
    color: var(--bloom-teal-900);
}

body {
    margin: 0;
    min-height: 100vh;
    font-family: var(--font-sans);
    font-size: var(--text-body);
    line-height: 1.6;
    color: var(--color-text);
    background-color: var(--color-bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

img,
svg {
    max-width: 100%;
    height: auto;
    vertical-align: middle;
}

hr {
    border: 0;
    border-top: 1px solid var(--color-border);
    opacity: 1;
    margin: var(--space-5) 0;
}

/* 4. Typography ========================================================== */
h1, h2, h3, h4, h5, h6 {
    margin: 0 0 var(--space-3);
    color: var(--color-text);
    font-weight: 600;
    letter-spacing: -0.011em;
    text-wrap: balance;
}

h1 {
    font-size: var(--text-h1);
    line-height: 1.2;
    font-weight: 650;
    letter-spacing: -0.02em;
}

h2 { font-size: var(--text-h2); line-height: 1.25; }
h3 { font-size: var(--text-h3); line-height: 1.3; }

p {
    margin: 0 0 var(--space-4);
    text-wrap: pretty;
}

a {
    color: var(--color-link);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
    transition: color var(--dur-fast) var(--ease-out);
}

a:hover { color: var(--color-primary-active); }

/* Links inside the dark sidebar use the rail's own text colour, never the global link
   colour (tuned for light surfaces — teal-600 measures only 3.31:1 on the navy and fails
   AA). Global + specific so it holds regardless of scoped-CSS load order. Hover/active
   states are still handled by the higher-specificity scoped .nav-item rules. */
.shell__sidebar a { color: var(--sidebar-text); }

code, kbd, pre, samp { font-family: var(--font-mono); font-size: 0.9em; }


/* 5. Focus & motion ====================================================== */
/* Visible only for keyboard users; never removed outright. */
:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

:focus:not(:focus-visible) { outline: none; }

h1:focus { outline: none; }

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* 6. Component skins ===================================================== */
/* Buttons */
.btn {
    --bs-btn-focus-box-shadow: var(--shadow-focus);
    font-weight: 550;
    border-radius: var(--radius-md);
    padding: 0.5625rem var(--space-4);
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
}

.btn:active { transform: translateY(0.5px); }

.btn-primary {
    --bs-btn-bg: var(--color-primary);
    --bs-btn-border-color: var(--color-primary);
    --bs-btn-hover-bg: var(--color-primary-hover);
    --bs-btn-hover-border-color: var(--color-primary-hover);
    --bs-btn-active-bg: var(--color-primary-active);
    --bs-btn-active-border-color: var(--color-primary-active);
    --bs-btn-disabled-bg: var(--color-border-strong);
    --bs-btn-disabled-border-color: var(--color-border-strong);
    box-shadow: var(--shadow-xs);
}

.btn-lg { padding: 0.75rem var(--space-5); font-size: 1.0625rem; border-radius: var(--radius-md); }

/* Cards */
.card {
    --bs-card-bg: var(--color-surface);
    --bs-card-border-color: var(--color-border);
    --bs-card-border-radius: var(--radius-lg);
    --bs-card-inner-border-radius: calc(var(--radius-lg) - 1px);
    --bs-card-spacer-x: var(--space-5);
    --bs-card-spacer-y: var(--space-5);
    box-shadow: var(--shadow-sm);
    transition: box-shadow var(--dur-base) var(--ease-out);
}

/* Forms */
.form-label {
    font-weight: 550;
    font-size: var(--text-sm);
    color: var(--color-text);
    margin-bottom: var(--space-2);
}

.form-control {
    border-radius: var(--radius-md);
    border-color: var(--color-border-strong);
    padding: 0.5625rem var(--space-3);
    color: var(--color-text);
    background-color: var(--color-surface);
    transition: border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}

.form-control::placeholder { color: var(--color-text-subtle); }

.form-control:focus {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-focus);
    outline: none;
}

.form-floating > label { color: var(--color-text-subtle); }

/* Validation — replaces the scaffold's 1px outlines. */
.valid.modified:not([type=checkbox]) { border-color: var(--color-success); outline: none; }

.invalid {
    border-color: var(--color-danger) !important;
    outline: none;
}

.validation-message {
    display: block;
    color: var(--color-danger);
    font-size: var(--text-sm);
    margin-top: var(--space-2);
}

/* Alerts */
.alert {
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    padding: var(--space-4) var(--space-5);
    font-size: var(--text-sm);
    box-shadow: var(--shadow-xs);
}

.alert-warning { background-color: var(--color-warning-bg); border-color: #fde68a; color: var(--color-warning); }
.alert-info    { background-color: var(--color-info-bg);    border-color: #bae6fd; color: var(--color-info); }

/* Badges */
.badge {
    font-weight: 550;
    font-size: var(--text-xs);
    letter-spacing: 0.01em;
    padding: 0.3125rem var(--space-2);
    border-radius: var(--radius-full);
}

.text-bg-primary   { background-color: var(--color-primary) !important; color: var(--color-text-inverse) !important; }
.text-bg-success   { background-color: var(--color-success) !important; color: var(--color-text-inverse) !important; }
.text-bg-warning   { background-color: var(--color-warning) !important; color: var(--color-text-inverse) !important; }
.text-bg-danger    { background-color: var(--color-danger)  !important; color: var(--color-text-inverse) !important; }
.text-bg-info      { background-color: var(--color-info)    !important; color: var(--color-text-inverse) !important; }
.text-bg-secondary { background-color: var(--color-surface-sunken) !important; color: var(--color-text-muted) !important; }

/* Tables */
.table {
    --bs-table-bg: transparent;
    --bs-table-border-color: var(--color-border);
    margin-bottom: 0;
    color: var(--color-text);
}

.table > thead th {
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
    border-bottom: 1px solid var(--color-border);
    padding: var(--space-3) var(--space-4);
    white-space: nowrap;
}

.table > tbody td {
    padding: var(--space-4);
    border-bottom: 1px solid var(--color-border);
    vertical-align: middle;
}

.table > tbody tr:last-child td { border-bottom: 0; }

/* Scaffold error banner — kept functional, restyled off the base64 template art. */
.blazor-error-boundary {
    background: var(--color-danger);
    color: var(--color-text-inverse);
    padding: var(--space-4) var(--space-5);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
}

.blazor-error-boundary::after { content: "An error has occurred."; }

.form-floating > .form-control-plaintext::placeholder,
.form-floating > .form-control::placeholder { color: transparent; }

/* 7. Utilities =========================================================== */
.stack   { display: flex; flex-direction: column; gap: var(--space-4); }
.cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }

/* Layout primitives — the shared page anatomies. Pages compose these instead of
   declaring their own grids so every screen shares one rhythm. */

/* Main content + aside, stacking on mobile. */
.layout-split {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-5);
    align-items: start;
}

.layout-split__main,
.layout-split__aside {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    min-width: 0;
}

@media (min-width: 992px) {
    .layout-split { grid-template-columns: minmax(0, 2fr) minmax(19rem, 1fr); }
}

/* Aside-first variant, for sections whose aside is navigation rather than supporting
   detail (account settings). Reordering is desktop-only: stacked on mobile the content
   must still come first, so someone arriving with a status message sees it immediately
   instead of scrolling past a menu. */
@media (min-width: 992px) {
    .layout-split--reverse { grid-template-columns: minmax(17rem, 1fr) minmax(0, 3fr); }
    .layout-split--reverse > .layout-split__main { order: 2; }
    .layout-split--reverse > .layout-split__aside { order: 1; }
}

/* Responsive card grids: single column on mobile, N columns from the breakpoint. */
.grid-cards {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
}

/* 3-up grids step 1 → 2 → 3.
   These media queries measure the VIEWPORT, but the grid lives in a content column that
   loses --shell-sidebar-w (268px) to the rail from 768px up — and the old rule went
   3-across at 640px. The two collided exactly at 768px: three stat tiles with ~185px of
   min-content needed ~555px inside a 436px column, so the dashboard scrolled sideways
   (864px against a 768px viewport). The 3-column step now waits until the viewport is wide
   enough to afford it *after* the rail is subtracted, which also holds on the public
   layout, where there is no rail and the column is simply wider. */
@media (min-width: 640px) {
    .grid-cards--3 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1100px) {
    .grid-cards--3 { grid-template-columns: repeat(3, 1fr); }
}

@media (min-width: 768px) {
    .grid-cards--2 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 992px) {
    .grid-cards--2-lg { grid-template-columns: repeat(2, 1fr); }
}

/* Summary band — one surface holding N figure cells, divided by hairlines.
   Replaces a row of individually bordered, shadowed stat cards. Three boxes each carrying a
   tinted badge is the shape of an analytics dashboard; a single quiet panel stating the
   account's figures is the shape of a bank statement, which is what this is.
   The 1px grid gap plus a border-coloured background paints the dividers, so there is no
   per-cell border to double up at the joins and no :last-child exceptions to maintain. It
   works unchanged for the customer's 3 figures and the back office's 6. */
.statband {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1px;
    background-color: var(--color-border);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-xs);
}

@media (min-width: 560px) {
    .statband--3, .statband--2 { grid-template-columns: repeat(2, 1fr); }
}

/* Waits for room the content column actually has: the app shell spends 268px of the viewport
   on the rail from 768px up (see .grid-cards--3 for the same reasoning). */
@media (min-width: 1100px) {
    .statband--3 { grid-template-columns: repeat(3, 1fr); }
}

/* Label/value rows for a <dl>: label left, value right, hairline between rows. */
.dl-rows { margin: 0; }

.dl-rows > div {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--color-border);
}

.dl-rows > div:first-child { padding-top: 0; }
.dl-rows > div:last-child { border-bottom: 0; }

.dl-rows dt { font-size: var(--text-sm); color: var(--color-text-muted); }

.dl-rows dd {
    margin: 0;
    font-size: 0.9375rem;
    font-weight: 550;
    color: var(--color-text);
    text-align: right;
    overflow-wrap: anywhere;
}

/* Administration screens share one filter bar, notice slot and identity cell so
   Users / Roles / Audit stay visually identical. */
.admin-notice { margin-bottom: var(--space-5); }
.admin-filters { margin-bottom: var(--space-5); }

/* Bottom-aligned, so every control in the row sits on one line.
   With align-items:start each cell hugged the top of the row, so a field carrying a hint
   ("Email or name.") pushed its input lower than the fields without one, and the submit button
   sat up level with the labels. The old margin-bottom on .admin-filter-action was written to
   correct that but could not: nothing moves a start-aligned grid item downwards.
   The fields' own --space-5 bottom margin is dropped here rather than matched, because it also
   stacked with the card's padding and left ~48px of empty surface under the row. With no
   margins, aligning to the row's end lands every control directly on one baseline and the
   card's padding alone sets the breathing room. */
.admin-filter-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
    align-items: end;
}

.admin-filter-grid > .field,
.admin-filter-grid > .admin-filter-action { margin-bottom: 0; }

@media (min-width: 768px) {
    .admin-filter-grid {
        grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr) auto;
    }

    .admin-filter-action { min-width: 8rem; }
}

/* Five or six controls rather than three. The base track list packs them two-up and drops the
   submit button onto a line of its own, which reads as a second, unrelated row.
   A modifier here rather than another per-page copy of the grid: the queue already carries its own
   near-identical duplicate (see ReviewQueue.razor.css, which says so), and a third would guarantee
   the three filter bars drift apart. Two rows of three up to 1200px, then one row, because six
   controls plus a button need the width the rail leaves only on a wide desktop. */
@media (min-width: 768px) {
    .admin-filter-grid--wide {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (min-width: 1400px) {
    .admin-filter-grid--wide {
        grid-template-columns:
            minmax(0, 0.8fr) minmax(0, 0.9fr) minmax(0, 1fr)
            minmax(0, 1.2fr) minmax(0, 1fr) auto;
    }
}

.admin-user__name {
    display: block;
    font-weight: 600;
    color: var(--color-text);
}

.admin-user__email {
    display: block;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    overflow-wrap: anywhere;
}

.admin-roles {
    display: flex;
    flex-direction: column;
    border-top: 1px solid var(--color-border);
}

.admin-role-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--color-border);
}

.admin-role-row__name {
    font-size: 0.9375rem;
    font-weight: 550;
    color: var(--color-text);
}

/* Quick actions — a shared list pattern used by the admin and customer dashboards. */
/* Actions read as a list, not as boxes.
   Each row was a bordered, rounded panel containing a bordered, rounded icon tile — inside a
   bordered, rounded card. Three nested frames for one link is the clearest "built from
   components" tell on the dashboard, and the borders competed with the card that held them.
   Now: full-bleed rows separated by hairlines, so the card's own edge is the only frame. */
.quick-actions {
    display: flex;
    flex-direction: column;
    /* Cancel the card body's padding so the rows and their hover run edge to edge. */
    margin: calc(var(--space-5) * -1);
}

.quick-action {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--color-border);
    text-decoration: none;
    transition: background-color var(--dur-fast) var(--ease-out);
}

/* The first row keeps its rule: with the card header's divider removed, this is what separates
   the "Quick actions" title from the list beneath it. */

.quick-action:hover { background-color: var(--color-hover); }
.quick-action:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }

/* The one action the customer still owes us. Marked by a left rule and a tint rather than a
   gradient — the previous one faded into #e6f0ff and set its border from --bloom-blue-200,
   a token that no longer exists, so the border silently did nothing. */
.quick-action--accent {
    background-color: var(--color-primary-soft);
    box-shadow: inset 3px 0 0 var(--color-primary);
}

.quick-action--accent:hover { background-color: var(--bloom-teal-100); }

.quick-action__icon {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 1.25rem;
    color: var(--color-primary);
    font-size: 1.125rem;
    line-height: 1;
}

.quick-action__text { display: flex; flex-direction: column; gap: 0.125rem; min-width: 0; }

.quick-action__title {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--color-text);
}

.quick-action__sub {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.role__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

.role__icon {
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--radius-md);
    background-color: var(--color-primary-soft);
    color: var(--color-primary);
    font-size: 1.125rem;
    line-height: 1;
}

.role__name {
    margin: 0 0 var(--space-2);
    font-size: 1.0625rem;
    font-weight: 650;
    letter-spacing: -0.01em;
    color: var(--color-text);
}

.role__desc {
    margin: 0 0 var(--space-5);
    font-size: var(--text-sm);
    line-height: 1.55;
    color: var(--color-text-muted);
}

/* Stacked table rows right-align their content; keep the identity cell readable. */
@media (max-width: 767.98px) {
    .admin-user__name, .admin-user__email { text-align: right; }
}

/* Inside a flush card: rows carry the card's horizontal padding themselves. */
.dl-rows--inset > div {
    padding-left: var(--space-5);
    padding-right: var(--space-5);
}

.dl-rows--inset > div:first-child { padding-top: var(--space-3); }
.dl-rows--inset > div:last-child { padding-bottom: var(--space-3); }

.surface {
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.muted { color: var(--color-text-muted); }

/* SVG icons (Lucide). Size to 1em so an icon scales with its container's font-size — one
   consistent icon scale everywhere, no per-page pixel values. Vertical-align keeps inline
   icons centred on the text baseline. */
.icon {
    width: 1em;
    height: 1em;
    flex: none;
    vertical-align: -0.15em;
}

/* Account settings ------------------------------------------------------- */
/* Shared by the /account pages. Global rather than component-scoped because the
   Identity pages are separate components that must present one consistent section. */

.settings-prose {
    margin: 0 0 var(--space-4);
    max-width: 68ch;
    color: var(--color-text-muted);
    line-height: 1.65;
}

.settings-note {
    margin: 0 0 var(--space-4);
    max-width: 68ch;
    font-size: var(--text-sm);
    /* --muted, not --subtle: a note can sit on a sunken card footer where subtle #64748b is
       only 4.34:1; muted clears AA on both white and sunken surfaces. */
    color: var(--color-text-muted);
    line-height: 1.6;
}

.settings-note:last-child { margin-bottom: 0; }

/* Irreversible actions. The tint is a supporting signal only — the heading and body
   text state the consequence, so nothing here depends on seeing the colour. */
.danger-zone { border-color: #fecaca; }
.danger-zone .card__title { color: var(--color-danger); }

/* Current-email summary above the change form. */
.email-current {
    padding: var(--space-4);
    margin-bottom: var(--space-5);
    background: var(--color-surface-sunken);
    border-radius: var(--radius-md);
}

.email-current__dl { margin: 0; }
.email-current__dl dt {
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    /* --muted (not --subtle): subtle #64748b measures only 4.34:1 on the sunken panel; muted
       #475569 clears AA there. */
    color: var(--color-text-muted);
}

.email-current__dl dd {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin: var(--space-2) 0 0;
}

.email-current__value {
    font-weight: 550;
    color: var(--color-text);
    overflow-wrap: anywhere;
}

/* Two-step verification on/off summary. */
.tfa__state {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-5);
    color: var(--color-text-muted);
}

/* Screen-reader-only text. Bootstrap also ships this, but several components
   depend on it for their accessible names — owning it here removes the coupling. */
.visually-hidden {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

.skip-link {
    position: absolute;
    left: var(--space-4);
    top: calc(-1 * var(--space-9));
    z-index: 1100;
    padding: var(--space-3) var(--space-4);
    background-color: var(--color-surface);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    font-weight: 600;
    text-decoration: none;
    transition: top var(--dur-base) var(--ease-out);
}

.skip-link:focus { top: var(--space-4); }
