/* AdminPortal css 08: Admin components.

   The capability plan requires new admin components to live here rather than in a
   page-local <style> block, so that one definition serves every page and both
   themes. This file holds:

     1. the shared Blazor components in Components/Shared (SparkLine, TrendChart,
        ShareBarList, StatusPill),
     2. the admin vocabulary the docs name (product chip, SLA chip, attention row,
        uptime segment bar, capability-gated affordance),
     3. the component vocabularies hoisted OUT of page-local <style> blocks
        (.pill, .segmented, .trend-*, .kv, .drill-card, .clickable-row, .kpi,
        .platform-metric, .attn-*, .alert-*),
     4. the per-page rules hoisted here in the polish pass (sidebar chrome, ledger
        search + pager, settings key/value list, customer-profile notes and
        actions, .all-clear, .coupon-form, .cap-*, auth-page odds and ends).

   ---------------------------------------------------------------------------
   SPECIFICITY. A page-local <style> block is injected AFTER these linked sheets,
   so at equal specificity a page always wins. Sections 3 and 4 are therefore
   written at plain specificity and are the single definition - the page-local
   copies they replaced have been deleted, and the three deeper `OVERRIDE` rules
   that existed to beat a page's hardcoded #04140c ink went with them.

   Anything hoisted here that was scoped to one page by accident of living in that
   page (Flags' bare `code` selector, for instance) must be given an explicit
   scope on the way in: this file applies to all 20 pages.
   ---------------------------------------------------------------------------
*/

/* ============================================================================
   1. Shared Blazor components
   ============================================================================ */

/* ---- Card header slots ---------------------------------------------------
   Card.razor was deleted in the polish pass: it emitted byte-identical classes to
   the ~50 hand-written .dashboard-card blocks, was adopted by zero pages, and left
   two card implementations for the next reader to choose between. These two slot
   classes are kept because hand-written headers use them. */

.card-title-group {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.card-title-group .bi {
    color: var(--foreground-muted);
    font-size: 0.875rem;
}

.card-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

/* ---- SparkLine.razor -----------------------------------------------------
   A figure's own history, drawn small enough to sit inside a KPI tile. No axes,
   no labels: it answers "which way has this been going", not "what was it in
   March". Stroke colour comes from currentColor so a caller can tint it by
   setting `color` on a wrapper without a new parameter. */

.sparkline {
    display: block;
    width: 100%;
    height: 28px;
    overflow: visible;
    color: var(--chart-1);
}

.sparkline-fill {
    fill: currentColor;
    opacity: 0.14;
}

.sparkline-line {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}

.sparkline-last {
    fill: currentColor;
}

/* One data point cannot show a trend. Rather than draw a flat line - which reads
   as "measured, unchanged" and is a lie - the component renders this. */
.sparkline-empty {
    display: flex;
    align-items: center;
    min-height: 28px;
    font-size: 0.7rem;
    color: var(--foreground-subtle);
}

/* ---- TrendChart.razor ----------------------------------------------------- */

.trendchart {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
    color: var(--chart-1);
}

.trendchart-grid {
    stroke: var(--border);
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
}

.trendchart-axis {
    fill: var(--foreground-subtle);
    font-size: 11px;
    font-family: inherit;
}

.trendchart-area {
    fill: currentColor;
    opacity: 0.12;
}

.trendchart-line {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}

/* Hover is pure CSS: each point owns a full-height transparent band, and hovering
   the band reveals that point's crosshair, dot and tooltip. No JS, so it survives
   a Blazor re-render mid-hover (the filter pills re-render these charts). */
.trendchart-band {
    fill: transparent;
    cursor: crosshair;
}

.trendchart-cross,
.trendchart-dot,
.trendchart-tip {
    opacity: 0;
    transition: opacity 0.12s ease;
    pointer-events: none;
}

.trendchart-point:hover .trendchart-cross,
.trendchart-point:hover .trendchart-dot,
.trendchart-point:hover .trendchart-tip {
    opacity: 1;
}

.trendchart-cross {
    stroke: var(--border-strong);
    stroke-width: 1;
    stroke-dasharray: 3 3;
    vector-effect: non-scaling-stroke;
}

.trendchart-dot {
    fill: currentColor;
    stroke: var(--card);
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
}

.trendchart-tip-bg {
    fill: var(--popover);
    stroke: var(--border-strong);
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
}

.trendchart-tip-label {
    fill: var(--foreground-muted);
    font-size: 10px;
    font-weight: 600;
    font-family: inherit;
}

.trendchart-tip-value {
    fill: var(--foreground);
    font-size: 12px;
    font-weight: 700;
    font-family: inherit;
}

/* A single point is a reading, not a trend: draw it as a dot with its value and
   say so, rather than extending it into a horizontal line. */
.trendchart-single {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 12px 2px;
}

.trendchart-single-value {
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--foreground);
    font-variant-numeric: tabular-nums;
}

.trendchart-empty {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 14px 4px;
    font-size: 0.8rem;
    color: var(--foreground-muted);
}

.trendchart-empty .bi {
    color: var(--foreground-subtle);
}

/* ---- ShareBarList.razor ---------------------------------------------------
   Horizontal share list: the prescribed form for "mix" (revenue by product,
   tickets by status). Ordered, so the ranking is readable without the bars, and
   each row states its own percentage - the bar is reinforcement, not the datum.

   The fill uses color-mix() against the row's series colour, which is how the
   reference gets a tint that stays correct in both themes from one declaration.
   Browsers without color-mix() fall back to the flat --primary-subtle set first. */

.sharebar-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.sharebar-row {
    position: relative;
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    gap: 12px;
    padding: 7px 10px;
    border-radius: var(--radius);
    overflow: hidden;
    text-decoration: none;
    color: inherit;
}

/* The bar is a background layer behind the text, not a separate track: the row
   itself is the bar, which keeps label and magnitude on one line at high density. */
.sharebar-fill {
    position: absolute;
    inset: 0 auto 0 0;
    background: var(--primary-subtle);
    background: color-mix(in srgb, var(--sharebar-color, var(--chart-1)) 18%, transparent);
    border-right: var(--hairline) solid var(--sharebar-color, var(--chart-1));
    transition: width var(--transition);
    pointer-events: none;
}

.sharebar-row:hover .sharebar-fill {
    background: color-mix(in srgb, var(--sharebar-color, var(--chart-1)) 28%, transparent);
}

.sharebar-label,
.sharebar-value,
.sharebar-percent {
    position: relative;
    z-index: 1;
    min-width: 0;
}

.sharebar-label {
    font-size: 0.8125rem;
    color: var(--foreground);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sharebar-value {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--foreground);
    font-variant-numeric: tabular-nums;
}

.sharebar-percent {
    font-size: 0.75rem;
    color: var(--foreground-muted);
    font-variant-numeric: tabular-nums;
    min-width: 3.5em;
    text-align: right;
}

/* ---- StatusPill.razor -----------------------------------------------------
   Renders .status-badge from 06 plus these two additions. */

.status-badge .bi {
    font-size: 0.75rem;
    line-height: 1;
}

/* Lowercase variant for chips that carry a proper noun (a product name, a
   customer tier) where SHOUTING is noise rather than emphasis. */
.status-badge.plain {
    text-transform: none;
    letter-spacing: 0;
    font-weight: 500;
}

/* ============================================================================
   2. The admin vocabulary named in the capability plan
   ============================================================================ */

/* ---- Product chip --------------------------------------------------------- */
.product-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 7px;
    border-radius: var(--radius);
    background: var(--neutral-subtle);
    border: var(--hairline) solid var(--border);
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--foreground);
    white-space: nowrap;
    text-decoration: none;
}

a.product-chip:hover {
    background: var(--card-hover);
    border-color: var(--border-strong);
}

.product-chip .bi {
    color: var(--foreground-muted);
    font-size: 0.8rem;
}

/* Removed as dead in the polish pass, with no markup anywhere that used them:
   .sla-chip (+ .ok/.due/.breached), .uptime-bar / .uptime-seg / .uptime-legend,
   and .requires-cap / .requires-cap-note. They were written ahead of the pages
   that were meant to consume them. The uptime bar in particular came with a rule
   attached - every segment must carry a title/aria-label, because a bar that
   conveys state by colour cannot be read otherwise - so if it is reintroduced,
   reintroduce that requirement with it. Same for .requires-cap: it only belongs
   next to an <AuthorizeView NotAuthorized> branch and a server-side re-check,
   never a client-side test on its own. */

/* ---- Attention / alert feed row -------------------------------------------
   One row shape for both the /attention queue and the /monitoring alert list,
   which today ship as two incompatible ad-hoc implementations. */
.attn-list,
.alert-feed {
    display: flex;
    flex-direction: column;
    list-style: none;
    margin: 0;
    padding: 0;
}

.attn-row,
.alert-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    border-bottom: var(--hairline) solid var(--border);
    text-decoration: none;
    color: inherit;
    transition: background var(--transition);
}

.attn-row:last-child,
.alert-row:last-child {
    border-bottom: none;
}

.attn-row:hover {
    background: var(--card-hover);
}

.attn-sev {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: var(--radius);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.9rem;
    background: var(--neutral-subtle);
    color: var(--foreground-muted);
}

.attn-main,
.alert-body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.attn-title,
.alert-msg {
    color: var(--foreground);
    font-weight: 600;
    font-size: 0.875rem;
}

.attn-detail,
.alert-meta {
    font-size: 0.75rem;
    color: var(--foreground-muted);
}

.attn-sevlabel {
    flex: 0 0 auto;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--foreground-muted);
}

.attn-chevron {
    color: var(--foreground-subtle);
    flex: 0 0 auto;
}

.alert-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    background: var(--foreground-subtle);
}

.alert-row.danger .alert-dot { background: var(--danger); }
.alert-row.warning .alert-dot { background: var(--warning); }
.alert-row.info .alert-dot { background: var(--info); }

/* Severity in text as well as colour. The dot is 8px of pure hue with no content:
   a red-green colourblind operator cannot separate "unreachable" (danger) from
   "service key rejected" (warning), and a screen reader gets nothing from it at
   all - the row's own text does not resolve severity either, because both of
   those emit the category "outage". So every severity-bearing row pairs the dot
   with this uppercase chip. Plain specificity: it serves .attn-list and
   .alert-feed alike. */
.attn-sev.danger { background: var(--danger-subtle); color: var(--danger-strong); }
.attn-sev.warning { background: var(--warning-subtle); color: var(--warning-strong); }
.attn-sev.info { background: var(--info-subtle); color: var(--info-strong); }
.attn-sevlabel.danger { color: var(--danger-strong); }
.attn-sevlabel.warning { color: var(--warning-strong); }
.attn-sevlabel.info { color: var(--info-strong); }

/* ============================================================================
   3. Vocabularies hoisted out of page-local <style> blocks

   Each of these is currently defined 2-5 times across the pages, with values
   that have already drifted apart (three different .kv column widths, four
   different KPI label+figure treatments). These are the single definitions.
   ============================================================================ */

/* ---- Filter pills (Billing, Coupons, Growth, Sales) ---- */
.chart-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 14px;
}

/* flex-wrap is load-bearing, not cosmetic: these groups hold an unbounded, data-driven
   list (every product in the ledger, every allowlisted Search Console property), so on a
   narrow viewport the row is wider than the card. Without wrapping the group refuses to
   shrink below its content and .dashboard-card's overflow:hidden CLIPS the last filters
   away with no scrollbar and no way to reach them. Matches .segmented below.
   max-width keeps the group inside its column so the wrap point is the card, not the page. */
.pill-group {
    display: inline-flex;
    flex-wrap: wrap;
    max-width: 100%;
    gap: 1px;
    padding: 0;
    border-radius: var(--radius);
    background: var(--border);
    border: var(--hairline) solid var(--border);
    overflow: hidden;
}

.pill {
    border: none;
    background: var(--card);
    color: var(--foreground-muted);
    font-family: inherit;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 5px 11px;
    border-radius: 0;
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}

.pill:hover {
    background: var(--card-hover);
    color: var(--foreground);
}

.pill.active {
    background: var(--primary);
    color: var(--primary-ink);
}

/* Disabled state. Without this a pill that is disabled during an in-flight load still shows
   the pointer cursor and still lights up on hover (CSS :hover matches disabled buttons), so a
   click that the disabled attribute swallowed is indistinguishable from one that worked - the
   user clicks again and concludes the filter is broken. Same treatment as .pager-btn:disabled.
   :not(.active) keeps the selected pill readable rather than letting the hover reset win over
   .pill.active's background. */
.pill:disabled {
    opacity: 0.5;
    cursor: default;
}

.pill:disabled:hover:not(.active) {
    background: var(--card);
    color: var(--foreground-muted);
}

/* .form-input / select set an explicit background and color, which override the user agent's
   own disabled greying, so a disabled select looked exactly like a live one. */
.form-input:disabled,
select:disabled,
textarea.form-input:disabled {
    opacity: 0.55;
    cursor: default;
}

/* ---- Segmented control (Tickets) - the same idea under another name ---- */
.segmented {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 1px;
    padding: 0;
    border-radius: var(--radius);
    background: var(--border);
    border: var(--hairline) solid var(--border);
    overflow: hidden;
}

.segmented-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 6px 12px;
    border: none;
    border-radius: 0;
    background: var(--card);
    color: var(--foreground-muted);
    font-family: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}

.segmented-btn:hover {
    background: var(--card-hover);
    color: var(--foreground);
}

.segmented-btn.active {
    background: var(--primary-subtle);
    color: var(--primary-strong);
}

.segmented-count {
    font-size: 0.6875rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    min-width: 18px;
    padding: 1px 5px;
    border-radius: var(--radius-sm);
    background: var(--neutral-subtle);
    color: var(--foreground-muted);
}

.segmented-btn.active .segmented-count {
    background: var(--primary);
    color: var(--primary-ink);
}

/* ---- Trend cell (Growth, Sales) ---- */
.trend-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
    gap: var(--hairline);
    background: var(--border);
    border: var(--hairline) solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}

.trend-cell {
    border: none;
    border-radius: 0;
    padding: 12px 14px;
    background: var(--card);
    min-width: 0;
}

.trend-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 10px;
    margin-bottom: 6px;
}

.trend-label {
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--foreground-muted);
}

.trend-now {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--foreground);
    font-variant-numeric: tabular-nums;
}

/* .trend-svg and .trend-foot were removed as dead: TrendChart brings its own
   .trendchart-* geometry and no page emits either class. */

/* ---- KPI blocks (Platforms .platform-metric, ProductDashboard .kpi) --------
   Two more names for the same thing. Both are pinned to the .stat-card scale so
   a figure is the same size on every screen in the portal. */
.platform-metrics,
.kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(120px, 100%), 1fr));
    gap: var(--hairline);
    background: var(--border);
    border: var(--hairline) solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    margin-bottom: 4px;
}

.platform-metric,
.kpi {
    display: flex;
    /* The markup nests value-before-label; column-reverse puts the muted label
       above the figure without either page having to change its markup. */
    flex-direction: column-reverse;
    gap: 2px;
    padding: 10px 12px;
    border: none;
    border-radius: 0;
    background: var(--card);
    min-width: 0;
}

.platform-metric-value,
.kpi-value {
    font-size: 1.35rem;
    font-weight: 600;
    color: var(--foreground);
    font-variant-numeric: tabular-nums;
}

.platform-metric-label,
.kpi-label {
    font-size: 0.75rem;
    color: var(--foreground-muted);
}

/* ---- Definition list (Monitoring, ProductDashboard, CustomerProfile) -------
   One column width so the keys line up across pages. `auto` rather than a fixed
   px so a long key wraps instead of colliding with its value. */
.kv {
    display: grid;
    grid-template-columns: minmax(90px, max-content) 1fr;
    gap: 6px 14px;
    margin: 0;
    align-items: baseline;
}

.kv dt {
    color: var(--foreground-muted);
    font-size: 0.75rem;
}

.kv dd {
    margin: 0;
    color: var(--foreground);
    font-size: 0.8125rem;
    font-weight: 500;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* ---- Drill-down affordances (Platforms, Monitoring, Customers, ProductDashboard)
   Every aggregate links to its filtered view, so these two get used a lot.

   Both are attached to non-interactive elements (a <div> card, a <tr>), so the
   markup must supply tabindex="0", role="button" and an Enter/Space keydown
   handler - without them a keyboard user could Tab the whole of /platforms and
   never reach a single product. The focus rules below only matter once that is
   done, and they are here so the ring is not lost to the hover tint. */
.drill-card {
    cursor: pointer;
    transition: border-color var(--transition), background var(--transition);
}

.drill-card:hover {
    border-color: var(--border-strong);
    background: var(--card-hover);
}

.drill-card:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
    border-color: var(--border-strong);
}

.clickable-row {
    cursor: pointer;
}

.clickable-row:hover {
    background: var(--card-hover);
}

/* An outline on a <tr> is unreliable across engines in the separated-borders
   model, so the ring is drawn as an inset shadow on the cells instead. */
.clickable-row:focus-visible {
    outline: none;
    background: var(--card-hover);
}

.clickable-row:focus-visible td {
    box-shadow: inset 0 2px 0 0 var(--ring), inset 0 -2px 0 0 var(--ring);
}

.clickable-row:focus-visible td:first-child {
    box-shadow: inset 2px 0 0 0 var(--ring), inset 0 2px 0 0 var(--ring), inset 0 -2px 0 0 var(--ring);
}

.clickable-row:focus-visible td:last-child {
    box-shadow: inset -2px 0 0 0 var(--ring), inset 0 2px 0 0 var(--ring), inset 0 -2px 0 0 var(--ring);
}

/* ---- Compact table variant ---- */
.data-table.compact th,
.data-table.compact td {
    padding: 5px 10px;
    font-size: 0.78rem;
}

/* ---- Confirm / danger modal (Shared/ConfirmDialog) ---- */
.confirm-dialog {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.confirm-body {
    margin: 0;
    color: var(--foreground);
    font-size: 0.875rem;
    line-height: 1.5;
}

.confirm-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.confirm-field label {
    font-size: 0.8rem;
    color: var(--foreground-muted);
}

.confirm-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 4px;
}

.tier-danger .confirm-body {
    border-left: 3px solid var(--danger);
    padding-left: 12px;
}

.tier-sensitive .confirm-body {
    border-left: 3px solid var(--warning);
    padding-left: 12px;
}

/* ============================================================================
   4. Hoisted out of page-local <style> blocks (polish pass)

   Everything below used to live in a <style> block inside a single .razor. The
   capability plan requires new admin components to live here so one definition
   serves every page and both themes; these were left behind only because the
   page-migration agent did not own this file. Values are unchanged except where
   noted, and every selector that was implicitly page-scoped has been given an
   explicit scope.
   ============================================================================ */

/* ---- Sidebar chrome (Layout/Sidebar.razor) ------------------------------- */
.hq-logo {
    display: flex;
    align-items: center;
    gap: 8px;
    text-decoration: none;
}

.hq-logo-img {
    height: 30px;
    width: auto;
    object-fit: contain;
    display: block;
}

.hq-logo-tag {
    font-weight: 700;
    font-size: 0.62rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--primary-strong);
    border: var(--hairline) solid var(--border-strong);
    border-radius: var(--radius-sm);
    padding: 2px 6px;
    line-height: 1;
}

.nav-section-label {
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--foreground-subtle);
}

.nav-section-rule {
    display: block;
    height: 1px;
    background: var(--border);
}

.logout-form { margin: 0; }

.logout-btn {
    width: 100%;
    background: none;
    border: none;
    cursor: pointer;
    font: inherit;
    text-align: left;
}

/* ---- "All clear" banner (Attention, Monitoring, Sales) --------------------
   One definition replacing three near-identical copies that disagreed on padding
   and glyph size. --success-strong, not --success: this is text-weight colour
   sitting on --card. */
.all-clear {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 20px;
}

.all-clear > .bi {
    color: var(--success-strong);
    font-size: 1.4rem;
}

.all-clear strong {
    color: var(--foreground);
}

/* ---- Ledger search + pager (Billing) ------------------------------------- */
.ledger-search {
    position: relative;
    display: flex;
    align-items: center;
}

.ledger-search i {
    position: absolute;
    left: 10px;
    color: var(--foreground-subtle);
    font-size: 0.9rem;
    pointer-events: none;
}

/* Field chrome comes from .form-input in 06; this only makes room for the glyph. */
.ledger-search .form-input {
    padding-left: 30px;
}

.ledger-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    padding: 12px;
    font-size: 0.8125rem;
    border-top: var(--hairline) solid var(--border);
}

.pager-btn {
    border: var(--hairline) solid var(--border);
    background: var(--card);
    color: var(--foreground);
    font-family: inherit;
    font-weight: 600;
    font-size: 0.78rem;
    padding: 5px 12px;
    border-radius: var(--radius);
    cursor: pointer;
}

.pager-btn:disabled { opacity: 0.4; cursor: default; }
.pager-btn:not(:disabled):hover { background: var(--card-hover); border-color: var(--border-strong); }

/* ---- Settings key/value list + form grids --------------------------------
   .kv-list is deliberately NOT the .kv description-list grid above: this one is a
   full-width row with the value right-aligned. Both names are kept on purpose. */
.kv-list { display: flex; flex-direction: column; gap: 2px; }

.kv-row {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 8px 0;
    border-bottom: var(--hairline) solid var(--border);
}

.kv-row:last-child { border-bottom: none; }
.kv-key { color: var(--foreground-muted); font-size: 0.85rem; }
.kv-val { color: var(--foreground); font-size: 0.9rem; text-align: right; }

.settings-subsection {
    margin-top: 20px;
    padding-top: 16px;
    border-top: var(--hairline) solid var(--border);
}

.invite-grid,
.coupon-form {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 12px 16px;
    margin-bottom: 10px;
}

@media (max-width: 900px) {
    .invite-grid,
    .coupon-form { grid-template-columns: 1fr 1fr; }
    .invite-grid .form-group[style*="span 2"] { grid-column: span 2 !important; }
}

/* ---- Customer profile: linked accounts, guarded actions, notes ------------ */
.text-danger { color: var(--danger-strong); }

.linked-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.linked-list a {
    display: flex;
    align-items: center;
    gap: 8px;
    text-decoration: none;
    color: var(--foreground);
}

.linked-list a:hover strong { text-decoration: underline; }

.action-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.action-desc { display: flex; flex-direction: column; gap: 1px; }
.action-desc span { font-size: 0.78rem; }
.action-controls { display: flex; align-items: center; gap: 8px; }

.flag-row {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.flag-row label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.85rem;
    color: var(--foreground);
    cursor: pointer;
}

.note-add { display: flex; gap: 8px; align-items: flex-start; }
.note-add textarea { flex: 1; }

.note-list {
    list-style: none;
    margin: 14px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.note-list li {
    border-left: 2px solid var(--border-strong);
    padding-left: 12px;
}

.note-meta { font-size: 0.72rem; }

.note-body {
    color: var(--foreground);
    font-size: 0.85rem;
    white-space: pre-wrap;
    margin-top: 2px;
}

/* ---- Capability groups (Flags) -------------------------------------------
   The `code` rule arrived here unscoped. In a page-local block that was harmless;
   in this file it would restyle every <code> in the portal, including the recovery
   codes and the SEO tables. Scoped to the block it was written for. */
.cap-group { margin-bottom: 16px; }
.cap-group:last-child { margin-bottom: 0; }

.cap-group-name {
    font-weight: 600;
    color: var(--foreground);
    margin-bottom: 8px;
}

.cap-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.cap-note code {
    background: var(--neutral-subtle);
    border-radius: var(--radius-sm);
    padding: 1px 5px;
    font-size: 0.82em;
    color: var(--foreground);
}

/* ---- Snapshot history header (ProductDashboard) -------------------------- */
.snapshot-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 6px;
}

.snapshot-spark {
    width: 100%;
    height: 44px;
    display: block;
    margin-bottom: 10px;
}

/* ---- Auth pages ----------------------------------------------------------
   The @media print block for the recovery codes stays in RecoveryCodes.razor: its
   literals are correct for paper and must never become theme tokens. */
.auth-legal-notice {
    text-align: center;
    font-size: 0.75rem;
    color: var(--foreground-subtle);
    padding: 12px 16px 0;
    line-height: 1.5;
}

.totp-qr-wrap { display: flex; justify-content: center; margin-bottom: 16px; }

.totp-manual {
    text-align: center;
    font-size: 0.8rem;
    color: var(--foreground-muted);
    margin-bottom: 20px;
    word-break: break-all;
}

.totp-manual code {
    display: block;
    margin-top: 6px;
    font-size: 0.85rem;
    color: var(--primary-strong);
    letter-spacing: 0.08em;
}

.recovery-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }

.recovery-grid code {
    background: var(--neutral-subtle);
    border: var(--hairline) solid var(--border);
    border-radius: var(--radius);
    padding: 10px 12px;
    text-align: center;
    font-size: 0.9rem;
    color: var(--primary-strong);
    letter-spacing: 0.05em;
}

.recovery-actions { display: flex; gap: 10px; margin-top: 16px; }
.recovery-actions .btn { flex: 1; justify-content: center; }

.recovery-ack {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 16px;
    font-size: 0.85rem;
    color: var(--foreground);
}

/* Row-action affordance (the chevron in a clickable table row). Came in with the ticket
   detail view; kept here rather than in a page-local <style> block so every clickable-row
   table shares it. Rest state is deliberately quiet: the whole row is the target, the
   chevron only confirms it once the pointer is there. */
.clickable-row .row-action {
    color: var(--foreground-subtle);
    text-align: center;
    transition: color var(--transition);
}

.clickable-row:hover .row-action {
    color: var(--foreground);
}

/* Entity cell: a name followed by optional state badges and a drill-through link. Laid out
   as a flex row because the parts are separate elements with no whitespace between them in
   the rendered markup, so inline flow ran them together ("FFLScaninvoices"). The gap is the
   separator, which also keeps badge spacing consistent whether or not a link follows. */
.data-table td.entity-cell {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

/* ============================================
   Mobile bottom tab bar

   Ported from the FFLScan / BiometricSource shell so the fleet's phone navigation is one
   pattern, but written against the semantic tokens rather than their legacy --green /
   --gray-500 / --overlay-inv names. Hidden above the breakpoint; the sidebar is the
   desktop navigation and the drawer is still what "More" opens.
   ============================================ */
.mobile-tabbar {
    display: none;
}

@media (max-width: 768px) {
    .mobile-tabbar {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 98;
        display: grid;
        grid-template-columns: repeat(5, 1fr);
        background: color-mix(in srgb, var(--surface) 88%, transparent);
        -webkit-backdrop-filter: blur(20px);
        backdrop-filter: blur(20px);
        border-top: var(--hairline) solid var(--border);
        /* Keeps the last row of tabs clear of the home indicator on notched phones. */
        padding: 5px 6px calc(5px + env(safe-area-inset-bottom));
    }

    .mtab {
        position: relative;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        min-height: 52px;          /* 44px is the minimum comfortable touch target */
        padding: 4px 2px;
        background: none;
        border: none;
        border-radius: var(--radius-lg);
        color: var(--foreground-muted);
        text-decoration: none;
        font-family: inherit;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        transition: color var(--transition);
    }

    .mtab-ico {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 46px;
        height: 27px;
        border-radius: var(--radius-pill);
        font-size: 1.18rem;
        line-height: 1;
        transition: background-color var(--transition), transform var(--transition);
    }

    .mtab-label {
        font-size: 0.62rem;
        font-weight: 600;
        letter-spacing: 0.03em;
    }

    .mtab:active .mtab-ico { transform: scale(0.88); }

    .mtab.active { color: var(--primary); }
    .mtab.active .mtab-ico { background: var(--primary-subtle); }

    /* The bar is fixed, so the page needs room underneath or the last row hides behind it. */
    .dashboard-content {
        padding-bottom: calc(76px + env(safe-area-inset-bottom));
    }

    /* "More" replaces the header hamburger, which would otherwise be a second control
       doing the same job in the same viewport. */
    .mobile-menu-btn { display: none; }
}

/* Product build tag in a card header. Was an inline font-size with the muted colour class;
   given a name so the header markup carries no styling of its own. */
.build-tag {
    color: var(--foreground-muted);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
}

@media (max-width: 768px) {
    /* A card header whose action row cannot wrap pushes itself past a narrow viewport: the
       product drill-down ran to 400px inside a 390px screen. Let both axes wrap instead. */
    .card-header {
        flex-wrap: wrap;
        row-gap: 8px;
    }

    .card-actions {
        flex-wrap: wrap;
        flex-shrink: 1;
    }

    /* Filter rows are label/control pairs in one wrapping flex line. At narrow widths the
       pairs break apart and a label ends up beside the PREVIOUS control, so it appears to
       name the wrong thing (the audit page read "Actor" next to the category select).
       Giving labels a full-width basis starts each pair on its own line. */
    .chart-controls > label {
        flex-basis: 100%;
        margin-bottom: -4px;
    }
}

/* Reason attached to a control set the current product cannot drive. Sits under the
   controls rather than replacing them, so the screen keeps the same shape everywhere and
   the operator learns why rather than finding a dead button. */
.write-blocked {
    margin-top: 8px;
    font-size: 0.8125rem;
    max-width: 70ch;
}

/* The comparison-window selector sits above the KPI row it drives, tighter than a card's own
   controls so it reads as a modifier on the row rather than a section of its own. */
.compare-controls {
    margin-bottom: 10px;
    align-items: center;
}
