/* ==========================================================================
   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;

    /* The dark the bloom.insure footer is painted in, sampled from the live site. Every dark surface
       in the portal uses this one value — the navigation rail and the footer — so the shell reads as
       the same organisation as the website.

       It replaced #0f172a (slate-900) on the rail. The two are almost the same darkness and a
       noticeably different hue: slate is blue-leaning, this is teal-leaning. Almost-matching is worse
       than either matching or contrasting, and with a footer in the site's colour the rail's blue cast
       was visible in the same viewport. */
    --bloom-ink: #0e2028;

    /* 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 — Maven Pro, matching bloom.insure.
       The system stack stays behind it as a fallback so a blocked or slow font request degrades to
       readable text rather than to a default serif. */
    --font-sans: "Maven Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;

    /* Weights, measured off bloom.insure.
       One correction rather than a straight copy: the site asks for 300 on section headings and body
       prose, but Maven Pro's lightest cut is 400, so those already render at 400 — carrying the 300
       across would encode a number that does nothing and invite someone to "fix" it later by loading
       a light weight that changes the design. */
    --weight-body: 400;      /* site: p */
    --weight-ui: 500;        /* site: body, links, nav — the working weight for interface text */
    --weight-subheading: 500;
    --weight-heading: 700;   /* site: h1 */

    /* Raised from 24-32px towards the site's presence, but not to its 45px: that is a marketing hero
       above a single sentence, whereas this sits above dense tables and forms. */
    --text-h1: clamp(1.75rem, 1.35rem + 1.4vw, 2.375rem);
    --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");

    /* Height of the sticky top navigation bar. Anything that sticks BELOW the header offsets itself
       by this (see ReviewDocument's sticky decision panel), so it must stay equal to
       .shell__header-inner's min-height or those panels slide under the bar. */
    --shell-topbar-h: 4.75rem;
    --content-max: 1200px;

    /* The dark sidebar rail and its palette are gone — navigation moved into the top bar to match the
       bloom.insure header. --shell-sidebar-w and the whole --sidebar-* ramp went with it; the only
       dark surface left is the footer, which uses --bloom-ink directly. */
}

/* 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);

    /* 500 is the site's body weight and it is the right one for interface text — labels, buttons,
       table cells — which is most of what this product is. Prose drops to 400 on <p> below, matching
       how the site sets its paragraphs lighter than its chrome. */
    font-weight: var(--weight-ui);
    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 ========================================================== */
/* Tracking is `normal` throughout, which is a change: the old scale tightened headings to -0.02em and
   body to -0.011em, tuned for the system stack. bloom.insure sets none at any size, and Maven Pro is
   drawn tight enough already — negative tracking on it closes the counters and costs legibility at
   the small sizes this product is mostly made of. */
h1, h2, h3, h4, h5, h6 {
    margin: 0 0 var(--space-3);
    color: var(--color-text);
    font-weight: var(--weight-heading);
    letter-spacing: normal;
    text-wrap: balance;
}

h1 {
    font-size: var(--text-h1);

    /* The site runs its headings tight — 45px over 52px, and 1.0 on several others. */
    line-height: 1.15;
}

/* Section headings take the site's lighter treatment, which is where most of its character lives:
   a large heading at 500 against 700 above it. Held at 500 rather than dropped further because at
   24px and below a lighter cut reads as washed out on a white app surface. */
h2 {
    font-size: var(--text-h2);
    line-height: 1.3;
    font-weight: var(--weight-subheading);
}

h3 {
    font-size: var(--text-h3);
    line-height: 1.3;
    font-weight: 600;
}

/* Below h3 the sizes are small enough that 500 loses against surrounding body text. */
h4, h5, h6 { font-weight: 600; }

/* Running prose only. The site sets paragraphs lighter and looser than its interface text —
   16px/26px — which reads well for explanation but would be wrong for a table cell, so it is scoped
   to <p> rather than applied to the body. */
p {
    margin: 0 0 var(--space-4);
    font-weight: var(--weight-body);
    line-height: 1.625;
    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); }

/* The .shell__sidebar link override is gone with the rail. Navigation links now sit on a white bar and
   are coloured by the scoped .nav-item rules, on a surface the global link colour was tuned for. */

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.
   The 1100px threshold was set when a 268px sidebar rail ate into the content column from 768px up:
   the old 640px rule collided with it exactly at 768px, where three stat tiles needing ~555px were
   given a 436px column and the dashboard scrolled sideways.

   That rail is gone — navigation is in the top bar now, so the content column is the full viewport at
   every width and 1100px is more conservative than it strictly needs to be. It is left alone: three cards in a row want the space regardless of what is beside them, and moving a
   breakpoint that no longer has a forcing reason is a change to every 3-up grid in the app for no
   reported problem. */
@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: 700;
    letter-spacing: normal;
    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); }
