/* ============================================
   PNT RMMS PORTAL — UNIFIED DESIGN SYSTEM
   Ported from PNT-Website site.css + admin.css
   ============================================ */

/* ============================================
   KEYFRAMES
   ============================================ */
@keyframes fadeInUp {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.6; }
}

@keyframes pulse-danger {
    0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.3); }
    50% { box-shadow: 0 0 0 4px rgba(239, 68, 68, 0); }
}

@keyframes pulse-warning {
    0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.4); }
    50% { box-shadow: 0 0 0 8px rgba(245, 158, 11, 0); }
}

@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* ============================================
   CSS CUSTOM PROPERTIES (Design Tokens)
   ============================================ */
:root {
    /* Primary Colors - Deep Blue/Purple Gradient Theme */
    --pnt-primary: #6366f1;
    --pnt-primary-dark: #4f46e5;
    --pnt-primary-light: #818cf8;
    --pnt-secondary: #8b5cf6;
    --pnt-accent: #06b6d4;

    /* Neutral Colors */
    --pnt-dark: #0f172a;
    --pnt-dark-light: #1e293b;
    --pnt-dark-lighter: #334155;
    /* ─── AUDIT-2026-08-05 PC-A11Y-07 / PC-ASSETS-05 / PC-UXCUSTOMER-16 (wave 10) ───

       WAS: #64748b. It is the colour `.text-muted` resolves to (see the text-colour
       utilities block), and `.text-muted` is used 182+ times across the product —
       including every StatCard label, every table caption, and most "last seen"
       and "created" timestamps. Against the page background (--pnt-dark #0f172a) it
       measures ~3.7:1 and against the card gradient (~#1a2537) ~3.0:1. WCAG 1.4.3
       requires 4.5:1 for body text. So a substantial fraction of the information
       density in this product — the labels that tell you WHAT a number is — sat
       below the legibility floor.

       #8b98ad computes to roughly 6.1:1 on the page background and 5.3:1 on the
       card gradient, so it clears AA against BOTH surfaces with margin. Lightening
       the single token rather than splitting into per-surface variants: every one of
       those 182 sites would have had to choose the right variant, and the ones that
       chose wrong would fail silently.

       It stays visibly quieter than the body text (--pnt-light #f1f5f9), which is
       what "muted" is for — it is now quiet AND readable rather than quiet and not.

       MEASUREMENT CAVEAT, stated rather than glossed: the ratios above are computed
       from the sRGB values, not measured in a browser. The card gradient is a
       semi-transparent overlay, so its EFFECTIVE background depends on what is
       behind it; ~#1a2537 is the composite over --pnt-dark, which is the normal
       case. Re-measure in a browser against both surfaces before signing this off. */
    --pnt-gray: #8b98ad;
    --pnt-gray-light: #94a3b8;
    --pnt-light: #f1f5f9;
    --pnt-white: #ffffff;

    /* ─── AUDIT-2026-08-08 PC-A11Y-08 (wave 12) — input boundary token ───

       WAS: `.form-control` / `.form-select` / `.form-check-input` drew their border
       with --pnt-dark-lighter (#334155) over the field fill --pnt-dark-light
       (#1e293b). Computed from the sRGB values that is ~1.41:1. WCAG 2.2 AA 1.4.11
       (Non-text Contrast) requires 3:1 between a UI component's visual boundary and
       its adjacent colours, so on a dark page the fields had effectively no edge —
       a text box and the card it sits on looked like the same slab of navy.

       #75859c computes to ~3.89:1 against the field fill and ~5.0:1 against the page
       background (--pnt-dark #0f172a), so it clears 3:1 against BOTH neighbouring
       surfaces with margin rather than sitting on the threshold. (#64748b, the
       obvious slate-500 pick, lands at ~3.07:1 — passing only until someone rounds.)

       A dedicated token rather than reusing --pnt-gray: --pnt-gray is the TEXT muted
       colour and is tuned against the 4.5:1 body-text floor. Tying the border to it
       would mean a future readability tweak silently moves a boundary contrast, which
       is exactly the coupling that produced this defect (the border rode on
       --pnt-dark-lighter, a surface token, and moved for surface reasons).

       MEASUREMENT CAVEAT, stated rather than glossed: ratios computed from sRGB
       values, not measured in a browser. Re-measure before sign-off. */
    --pnt-border-input: #75859c;

    /* Semantic Colors */
    --pnt-success: #10b981;
    --pnt-warning: #f59e0b;
    --pnt-danger: #ef4444;
    --pnt-info: #3b82f6;

    /* Gradients */
    --pnt-gradient-primary: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%);
    --pnt-gradient-dark: linear-gradient(180deg, #0f172a 0%, #1e293b 100%);
    --pnt-gradient-card: linear-gradient(145deg, rgba(30, 41, 59, 0.8) 0%, rgba(15, 23, 42, 0.9) 100%);
    --pnt-gradient-glow: radial-gradient(circle at 50% 0%, rgba(99, 102, 241, 0.15) 0%, transparent 50%);

    /* v1.6.2 (F-515): stat-card icon gradients extracted from inline styles
       in customer/admin dashboards. Centralizing here means a brand
       palette change touches one file instead of every dashboard. */
    --pnt-gradient-stat-cyan:    linear-gradient(135deg, #00b4ff, #0077cc);
    --pnt-gradient-stat-blue:    linear-gradient(135deg, #3b82f6, #1d4ed8);
    --pnt-gradient-stat-green:   linear-gradient(135deg, #10b981, #059669);
    --pnt-gradient-stat-purple:  linear-gradient(135deg, #8b5cf6, #6d28d9);
    --pnt-gradient-stat-red:     linear-gradient(135deg, #ef4444, #dc2626);
    --pnt-gradient-stat-amber:   linear-gradient(135deg, #eab308, #ca8a04);

    /* Typography */
    --font-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --font-display: 'Poppins', var(--font-primary);

    /* Spacing */
    --space-xs: 0.25rem;
    --space-sm: 0.5rem;
    --space-md: 1rem;
    --space-lg: 1.5rem;
    --space-xl: 2rem;
    --space-2xl: 3rem;
    --space-3xl: 4rem;

    /* Site Edge Padding */
    --site-edge-padding: 1.5rem;
    --site-edge-padding-md: 2rem;
    --site-edge-padding-lg: 3rem;

    /* Border Radius */
    --radius-sm: 0.375rem;
    --radius-md: 0.5rem;
    --radius-lg: 0.75rem;
    --radius-xl: 1rem;
    --radius-2xl: 1.5rem;
    --radius-full: 9999px;

    /* Shadows */
    --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
    --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
    --shadow-glow: 0 0 40px rgba(99, 102, 241, 0.3);
    --shadow-card: 0 4px 20px rgba(0, 0, 0, 0.25);

    /* Transitions */
    --transition-fast: 150ms ease;
    --transition-normal: 250ms ease;
    --transition-slow: 350ms ease;
}

/* ============================================
   BASE STYLES
   ============================================ */
*, *::before, *::after {
    box-sizing: border-box;
}

html {
    font-size: 16px;
    scroll-behavior: smooth;
}

body {
    font-family: var(--font-primary);
    background: var(--pnt-dark);
    color: var(--pnt-light);
    line-height: 1.6;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    margin: 0;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Background Pattern */
body::before {
    content: '';
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--pnt-gradient-glow);
    pointer-events: none;
    z-index: -1;
}

/* ============================================
   TYPOGRAPHY
   ============================================ */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-display);
    font-weight: 600;
    line-height: 1.2;
    color: var(--pnt-white);
}

h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.5rem; }
h4 { font-size: 1.25rem; }

.display-4 {
    font-size: 3rem;
    font-weight: 700;
    background: var(--pnt-gradient-primary);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* ============================================
   LINKS
   ============================================ */
a {
    color: var(--pnt-primary-light);
    text-decoration: none;
    transition: color var(--transition-fast);
}

a:hover {
    color: var(--pnt-accent);
}

/* ============================================
   ANNOUNCEMENT BAR
   ============================================ */
.announcement-bar {
    background: var(--pnt-gradient-primary);
    color: var(--pnt-white);
    text-align: center;
    padding: var(--space-sm) var(--space-md);
    font-size: 0.875rem;
    font-weight: 500;
}

.announcement-bar i {
    animation: pulse 2s ease-in-out infinite;
}

/* ============================================
   NAVBAR
   ============================================ */
.pnt-navbar {
    background: rgba(15, 23, 42, 0.95) !important;
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-bottom: 1px solid rgba(99, 102, 241, 0.1);
    padding: var(--space-md) 0;
    transition: all var(--transition-normal);
}

.pnt-navbar.scrolled {
    padding: var(--space-sm) 0;
    box-shadow: var(--shadow-lg);
}

/* Brand */
.navbar-brand {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.brand-logo {
    width: 45px;
    height: 45px;
    background: var(--pnt-gradient-primary);
    border-radius: var(--radius-lg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    color: var(--pnt-white);
    box-shadow: var(--shadow-glow);
    transition: transform var(--transition-normal);
}

.navbar-brand:hover .brand-logo {
    transform: rotate(-5deg) scale(1.05);
}

.brand-text {
    display: flex;
    flex-direction: column;
    line-height: 1.1;
}

.brand-name {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.25rem;
    color: var(--pnt-white);
}

.brand-tagline {
    font-size: 0.75rem;
    color: var(--pnt-gray-light);
    font-weight: 400;
    letter-spacing: 0.5px;
}

/* Nav Links */
.pnt-navbar .nav-link {
    color: var(--pnt-gray-light) !important;
    font-weight: 500;
    padding: var(--space-sm) var(--space-md) !important;
    border-radius: var(--radius-md);
    transition: all var(--transition-fast);
    position: relative;
}

.pnt-navbar .nav-link:hover {
    color: var(--pnt-white) !important;
    background: rgba(99, 102, 241, 0.1);
}

.pnt-navbar .nav-link.admin-link {
    color: var(--pnt-warning) !important;
}

.pnt-navbar .nav-link.admin-link:hover {
    background: rgba(245, 158, 11, 0.1);
}

/* Auth Buttons in Navbar */
.nav-auth-btn {
    padding: var(--space-sm) var(--space-lg) !important;
    border-radius: var(--radius-full) !important;
    font-weight: 600 !important;
    transition: all var(--transition-normal) !important;
}

.nav-auth-btn.btn-login {
    background: transparent;
    border: 2px solid var(--pnt-primary);
    color: var(--pnt-primary-light) !important;
}

.nav-auth-btn.btn-login:hover {
    background: var(--pnt-primary);
    color: var(--pnt-white) !important;
}

.nav-auth-btn.btn-register {
    background: var(--pnt-gradient-primary);
    border: none;
    color: var(--pnt-white) !important;
}

.nav-auth-btn.btn-register:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-glow);
}

/* ============================================
   MAIN CONTENT
   ============================================ */
.main-content {
    flex: 1;
    padding: var(--space-xl) 0;
    padding-left: var(--site-edge-padding);
    padding-right: var(--site-edge-padding);
}

.main-content > .container,
.main-content > .container-fluid {
    padding-left: 0;
    padding-right: 0;
}

/* Full-width sections */
.full-width-section {
    margin-left: calc(-1 * var(--site-edge-padding));
    margin-right: calc(-1 * var(--site-edge-padding));
    padding-left: var(--site-edge-padding);
    padding-right: var(--site-edge-padding);
}

.edge-to-edge {
    margin-left: calc(-1 * var(--site-edge-padding));
    margin-right: calc(-1 * var(--site-edge-padding));
    width: calc(100% + (2 * var(--site-edge-padding)));
}

/* ============================================
   BUTTONS
   ============================================ */
.btn {
    font-weight: 600;
    padding: var(--space-sm) var(--space-lg);
    border-radius: var(--radius-md);
    transition: all var(--transition-normal);
    border: none;
    cursor: pointer;
}

/* ─── AUDIT-2026-08-08 PC-A11Y-06 (wave 12) — the BUTTON half of the
   white-on-colour contrast defect, which wave 10 fixed for badges and explicitly
   deferred for buttons. ───

   WAS, and measured from the sRGB values against white ink:
     .btn-primary  var(--pnt-gradient-primary)  #6366f1 → #8b5cf6 → #a855f7
                   = 4.47 : 4.23 : 3.96  (fails at every stop)
     .btn-success  #10b981 → #059669       = 2.54 : 3.77  (fails badly)
     .btn-danger   #ef4444                 = 3.76        (fails)
   WCAG 2.2 AA 1.4.3 wants 4.5:1 for the button label. These are the product's
   primary action affordances — Connect, Activate, Cancel Subscription — so the
   least readable text on the page was the text you have to click.

   FIXED by darkening the button SURFACE and keeping white ink, which is the
   opposite direction from the badge fix and deliberately so. A badge is a passive
   status chip, so inverting its ink to dark cost nothing. A button is an
   affordance: dark-on-colour reads as "disabled/secondary" next to the dark-on-
   amber .btn-warning that already exists, and would have made the primary action
   the quietest thing in a button group. Darkening the fill keeps the "solid, high-
   contrast, white-labelled = primary action" language intact.

   NEW values, same hue families (indigo/violet, emerald, red) so no operator has to
   relearn a colour, all measured against white:
     .btn-primary  #4f46e5 → #6d28d9 → #7e22ce  = 6.29 : 7.10 : 6.98
     .btn-success  #047857 → #065f46            = 5.48 : 7.68
     .btn-danger   #dc2626                      = 4.83
   #4f46e5 is --pnt-primary-dark, i.e. exactly the background the wave-10 badge fix
   already moved .badge.bg-primary to — the two now agree instead of contradicting.

   NOT changed, and not claimed:
   - The --pnt-gradient-primary TOKEN is untouched. It paints 16 other surfaces
     (hero headers, admin icons, .text-gradient) where the ink is large display type
     or the gradient is decorative; moving the token would have re-tinted all of
     them for a defect that is specific to small white button labels. The button
     therefore carries its own literal gradient rather than a token — a deliberate
     de-coupling, same reasoning as --pnt-border-input above.
   - .btn-warning already paints #000 on #f59e0b (~8.3:1) and needed nothing.
   - .btn-info has no rule here; it inherits Bootstrap's dark-on-#0dcaf0.
   - The OUTLINE variants were already remediated (they use the lightened #34d399 /
     #f87171 / #fbbf24 inks on a transparent fill) and are untouched.
   - The box-shadow glows still key off the ORIGINAL brighter hues. That is
     intentional: a shadow is decoration with no contrast requirement, and keeping
     the glow preserves the brand feel the darker fill loses.

   MEASUREMENT CAVEAT, stated rather than glossed: every ratio above is computed
   from the sRGB values with the WCAG relative-luminance formula, not measured in a
   browser, and a gradient's effective ratio varies continuously across the button —
   the three stops are sampled, not a proof about every pixel between them.
   Re-measure in a browser before sign-off. */
.btn-primary {
    background: linear-gradient(135deg, #4f46e5 0%, #6d28d9 50%, #7e22ce 100%);
    color: var(--pnt-white);
    box-shadow: 0 4px 15px rgba(99, 102, 241, 0.3);
}

.btn-primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(99, 102, 241, 0.4);
    /* Must restate the darkened gradient: this rule previously reset `background`
       back to var(--pnt-gradient-primary), so a hover would have re-introduced the
       failing 4.47:1 surface the rule above just removed. */
    background: linear-gradient(135deg, #4f46e5 0%, #6d28d9 50%, #7e22ce 100%);
}

.btn-success {
    background: linear-gradient(135deg, #047857 0%, #065f46 100%);
    color: var(--pnt-white);
    box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
}

.btn-success:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4);
}

.btn-outline-primary {
    background: transparent;
    border: 2px solid var(--pnt-primary);
    color: var(--pnt-primary-light);
}

.btn-outline-primary:hover {
    background: var(--pnt-primary);
    color: var(--pnt-white);
    border-color: var(--pnt-primary);
}

.btn-outline-secondary {
    background: transparent;
    border: 2px solid var(--pnt-gray);
    color: var(--pnt-gray-light);
}

.btn-outline-secondary:hover {
    background: var(--pnt-dark-lighter);
    border-color: var(--pnt-gray-light);
    color: var(--pnt-white);
}

.btn-outline-danger {
    background: transparent;
    border: 2px solid var(--pnt-danger);
    color: #f87171;
}

.btn-outline-danger:hover {
    background: rgba(239, 68, 68, 0.15);
    border-color: var(--pnt-danger);
    color: #f87171;
}

.btn-outline-warning {
    background: transparent;
    border: 2px solid var(--pnt-warning);
    color: #fbbf24;
}

.btn-outline-warning:hover {
    background: rgba(245, 158, 11, 0.15);
    border-color: var(--pnt-warning);
    color: #fbbf24;
}

.btn-outline-info {
    background: transparent;
    border: 2px solid var(--pnt-accent);
    color: var(--pnt-accent);
}

.btn-outline-info:hover {
    background: rgba(6, 182, 212, 0.15);
    border-color: var(--pnt-accent);
    color: var(--pnt-accent);
}

.btn-outline-success {
    background: transparent;
    border: 2px solid var(--pnt-success);
    color: #34d399;
}

.btn-outline-success:hover {
    background: rgba(16, 185, 129, 0.15);
    border-color: var(--pnt-success);
    color: #34d399;
}

/* AUDIT-2026-08-08 PC-A11Y-06 (wave 12) — see the block above .btn-primary.
   WAS var(--pnt-danger) #ef4444, which is 3.76:1 under white ink. #dc2626 is 4.83:1
   and is the next stop down the same red ramp. The BORDER moves with the fill: left
   on --pnt-danger it would have drawn a brighter 2px rim that reads as a halo. The
   --pnt-danger token itself is untouched — it still paints .text-danger, .border-
   danger and the danger badges, none of which are white-on-solid. */
.btn-danger {
    background-color: #dc2626;
    border-color: #dc2626;
    color: var(--pnt-white);
    transition: all var(--transition-normal);
}

.btn-danger:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 15px rgba(239, 68, 68, 0.3);
}

.btn-warning {
    background-color: var(--pnt-warning);
    border-color: var(--pnt-warning);
    color: #000;
    transition: all var(--transition-normal);
}

.btn-warning:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 15px rgba(245, 158, 11, 0.3);
}

.btn-secondary {
    background-color: var(--pnt-dark-light);
    border-color: rgba(99, 102, 241, 0.15);
    color: var(--pnt-light);
    transition: all var(--transition-normal);
}

.btn-secondary:hover {
    background-color: var(--pnt-dark-lighter);
    border-color: rgba(99, 102, 241, 0.25);
    color: var(--pnt-white);
}

.btn-lg {
    padding: var(--space-md) var(--space-2xl);
    font-size: 1.1rem;
    border-radius: var(--radius-lg);
}

/* RMMS Devices Button */
.btn-rmms {
    background: linear-gradient(135deg, #0078D4 0%, #106EBE 100%);
    border: none;
    color: white;
    padding: 0.65rem 1.15rem;
    border-radius: 0.5rem;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    transition: all 0.2s ease;
    text-decoration: none;
}

.btn-rmms:hover {
    background: linear-gradient(135deg, #106EBE 0%, #0078D4 100%);
    color: white;
    transform: translateY(-2px);
    box-shadow: 0 4px 15px rgba(0, 120, 212, 0.4);
}

.rmms-badge {
    background: rgba(255, 255, 255, 0.2);
    padding: 0.15rem 0.5rem;
    border-radius: 1rem;
    font-size: 0.7rem;
    font-weight: 500;
}

/* ============================================
   CARDS
   ============================================ */
.card {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.1);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-card);
    transition: all var(--transition-normal);
    overflow: hidden;
    color: var(--pnt-light);
}

.card:hover {
    transform: translateY(-5px);
    border-color: rgba(99, 102, 241, 0.3);
    box-shadow: var(--shadow-glow), var(--shadow-xl);
}

.card-body {
    padding: var(--space-xl);
}

.card-title {
    font-family: var(--font-display);
    font-weight: 600;
    color: var(--pnt-white);
    margin-bottom: var(--space-md);
}

.card-text {
    color: var(--pnt-gray-light);
}

.card .card-text strong {
    color: var(--pnt-accent);
}

.card-header {
    background: rgba(30, 41, 59, 0.6);
    border-bottom: 1px solid rgba(99, 102, 241, 0.1);
    color: var(--pnt-white);
}

.card-footer {
    background: rgba(15, 23, 42, 0.4);
    border-top: 1px solid rgba(99, 102, 241, 0.1);
}

.card.shadow-sm {
    box-shadow: var(--shadow-md) !important;
}

/* ============================================
   FORMS
   ============================================ */
/* AUDIT-2026-08-08 PC-A11Y-08 (wave 12): border colour moved from
   --pnt-dark-lighter (#334155, ~1.41:1 against the field fill) to
   --pnt-border-input (#75859c, ~3.89:1). Full argument and the measurement caveat
   are on the token declaration at the top of this file. The 2px WIDTH is unchanged
   and was never the problem — a 2px edge you cannot see is not a boundary. */
.form-control,
.form-select {
    background: var(--pnt-dark-light);
    border: 2px solid var(--pnt-border-input);
    border-radius: var(--radius-md);
    color: var(--pnt-white);
    padding: var(--space-md);
    transition: all var(--transition-fast);
}

.form-control:focus,
.form-select:focus {
    background: var(--pnt-dark-light);
    border-color: var(--pnt-primary);
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
    color: var(--pnt-white);
}

.form-control::placeholder {
    color: var(--pnt-gray);
}

.form-label {
    color: var(--pnt-gray-light);
    font-weight: 500;
    margin-bottom: var(--space-sm);
}

.form-floating > .form-control {
    height: calc(3.5rem + 4px);
}

.form-floating > .form-control:focus ~ label,
.form-floating > .form-control:not(:placeholder-shown) ~ label {
    color: var(--pnt-primary-light);
}

/* AUDIT-2026-08-08 PC-A11Y-08 (wave 12): same defect, same fix. An UNCHECKED
   checkbox/radio is nothing BUT its boundary — there is no fill and no glyph to
   fall back on — so at ~1.41:1 an unchecked box was invisible against the card it
   sat on, which is worse than the text inputs, not better. The :checked rule below
   is untouched: it paints --pnt-primary on the dark fill and already had an edge. */
.form-check-input {
    background-color: var(--pnt-dark-light);
    border-color: var(--pnt-border-input);
    transition: all var(--transition-fast);
}

.form-check-input:checked {
    background-color: var(--pnt-primary);
    border-color: var(--pnt-primary);
    box-shadow: 0 0 8px rgba(99, 102, 241, 0.3);
}

.form-check-label {
    color: var(--pnt-gray-light);
}

.form-control:disabled,
.form-select:disabled {
    background-color: rgba(15, 23, 42, 0.5);
    color: var(--pnt-gray);
}

.input-group-text {
    background-color: rgba(99, 102, 241, 0.1);
    border-color: var(--pnt-dark-lighter);
    color: var(--pnt-gray-light);
}

/* ============================================
   VALIDATION (Blazor)
   ============================================ */
.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--pnt-success);
    box-shadow: 0 0 8px rgba(16, 185, 129, 0.15);
}

.invalid {
    outline: 1px solid var(--pnt-danger);
    box-shadow: 0 0 8px rgba(239, 68, 68, 0.15);
}

.validation-message {
    color: var(--pnt-danger);
    font-size: 0.85rem;
}

/* ============================================
   ALERTS
   ============================================ */
.alert {
    border: none;
    border-radius: var(--radius-lg);
    padding: var(--space-lg);
    font-weight: 500;
}

.alert-success {
    background: rgba(16, 185, 129, 0.15);
    color: #34d399;
    border-left: 4px solid var(--pnt-success);
}

.alert-danger {
    background: rgba(239, 68, 68, 0.15);
    color: #f87171;
    border-left: 4px solid var(--pnt-danger);
}

.alert-warning {
    background: rgba(245, 158, 11, 0.15);
    color: #fbbf24;
    border-left: 4px solid var(--pnt-warning);
}

.alert-info {
    background: rgba(59, 130, 246, 0.15);
    color: #60a5fa;
    border-left: 4px solid var(--pnt-info);
}

.alert-secondary {
    background: rgba(30, 41, 59, 0.5);
    border-left: 4px solid var(--pnt-gray);
    color: var(--pnt-gray-light);
}

.alert a, .alert .alert-link {
    color: var(--pnt-primary-light);
}

.alert-sm {
    padding: 0.5rem 0.75rem;
    font-size: 0.85rem;
}

/* ============================================
   TABLES
   ============================================ */
.table {
    color: var(--pnt-light);
    --bs-table-bg: transparent;
    --bs-table-color: var(--pnt-light);
    border-color: rgba(99, 102, 241, 0.1);
}

.table > :not(caption) > * > * {
    border-bottom-color: rgba(99, 102, 241, 0.08);
    background-color: transparent;
    color: var(--pnt-light);
}

.table-striped > tbody > tr:nth-of-type(odd) > * {
    background-color: rgba(99, 102, 241, 0.03);
    color: var(--pnt-light);
}

.table-hover > tbody > tr:hover > * {
    background-color: rgba(99, 102, 241, 0.06);
    color: var(--pnt-light);
    transition: background-color 0.2s ease;
}

.table-dark, .table thead.table-dark {
    --bs-table-bg: rgba(30, 41, 59, 0.6);
    --bs-table-color: var(--pnt-light);
    --bs-table-border-color: rgba(99, 102, 241, 0.1);
}

.table-dark th {
    background-color: rgba(30, 41, 59, 0.6) !important;
    color: var(--pnt-light) !important;
    border-color: rgba(99, 102, 241, 0.1) !important;
    font-weight: 600;
}

/* Severity row tints (log viewer, admin tables) on the DARK theme.
 *
 * These used to set only --bs-table-bg, which did nothing useful, and the rows
 * rendered as Bootstrap's light-theme cream (#fff3cd) with our light text on
 * top — genuinely unreadable, reported from /admin/logs 2026-08-04.
 *
 * Why --bs-table-bg alone cannot work here: Bootstrap 5.3 paints table
 * variants with an INSET BOX-SHADOW, not a background:
 *
 *   .table > :not(caption) > * > * {
 *       background-color: var(--bs-table-bg);
 *       box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state,
 *                   var(--bs-table-bg-type, var(--bs-table-accent-bg)));
 *   }
 *
 * So the variant colour arrives through the shadow layer. Our own
 * `.table > :not(caption) > * > * { background-color: transparent }` above
 * clears the background but leaves that shadow painting cream over the top,
 * while `.admin-table tbody td { color: #e2e8f0 }` (higher specificity than
 * `.table-warning`'s colour) keeps the text light. Light on cream.
 *
 * The fix has to kill the shadow and set both colours at cell level. */
.table > tbody > tr.table-danger > *,
.table > tbody > tr.table-warning > * {
    box-shadow: none !important;
}

.table > tbody > tr.table-danger > *,
.table > tbody > tr.table-danger:hover > * {
    background-color: rgba(239, 68, 68, 0.16) !important;
    color: #fecaca !important;
}

.table > tbody > tr.table-warning > *,
.table > tbody > tr.table-warning:hover > * {
    background-color: rgba(245, 158, 11, 0.15) !important;
    color: #fde9c3 !important;
}

/* Keep the variables sane too, for anything that reads them directly. */
.table-danger {
    --bs-table-bg: rgba(239, 68, 68, 0.16);
    --bs-table-color: #fecaca;
}

.table-warning {
    --bs-table-bg: rgba(245, 158, 11, 0.15);
    --bs-table-color: #fde9c3;
}

.table-sm td, .table-sm th {
    padding: 0.4rem 0.5rem;
}

/* GL-61 (audit 2026-08-05, PC-A11Y-02 ≡ PC-ASSETS-01) — part 1 of 2; part 2 is
 * the mobile block near the bottom of this file, which is where the defect was.
 *
 * UI_STYLEGUIDE.md §9 says a wide data table absorbs overflow by scrolling
 * inside `<div class="table-responsive">`. 28 of the 37 tables in Components/
 * do that. Eight of the remaining nine sit bare and directly inside a
 * `.card-body` — Admin Dashboard "Recent Activity", AdminCompanyDetail "Recent
 * Admin Actions", CompanyDetail (Locations, Device Groups, Devices),
 * CompanyMembers (Members, Pending Invitations), DeviceSharing. Those are the
 * tables that would start pushing the document sideways once the
 * column-deleting rule below is gone, so they need a scroll container of their
 * own before it can be removed. (The ninth is the three-column "User Access"
 * table in the AdminDevices detail modal, which cannot overflow a phone.)
 *
 * The correct fix is a .table-responsive wrapper in each of those .razor files;
 * those files are owned by other work this cycle, so the container is granted
 * from CSS instead. `:has(> .table)` matches only a card body whose direct
 * child is the table, so no other card is turned into a scroll container.
 * Not scoped to a media query on purpose: a 19-column table overflows a narrow
 * card at 900px just as it does at 375px, and a scroll container is inert until
 * its content actually overflows.
 *
 * Deliberate non-action: no @supports fallback for browsers without :has()
 * (pre-Chrome 105 / Firefox 121 / Safari 15.4). There the rule simply does not
 * match and the page scrolls horizontally instead — worse layout, but every
 * column is still present and reachable, which is the property this item is
 * about. Making the table itself `display: block` would scroll in those
 * browsers but strips the table's semantics from assistive tech, which trades
 * one accessibility defect for another. */
.card-body:has(> .table) {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* ============================================
   BADGES
   ============================================ */
.badge.bg-primary {
    background-color: var(--pnt-primary) !important;
}

.badge.bg-success {
    background-color: var(--pnt-success) !important;
    color: #fff !important;
}

.badge.bg-danger {
    background-color: var(--pnt-danger) !important;
}

.badge.bg-warning {
    background-color: var(--pnt-warning) !important;
    color: #000 !important;
}

.badge.bg-info {
    background-color: var(--pnt-info) !important;
    color: #fff !important;
}

.badge.bg-secondary {
    background-color: rgba(99, 102, 241, 0.15) !important;
    color: var(--pnt-gray-light) !important;
}

/* ============================================
   MODALS
   ============================================ */
.modal-content {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.15);
    color: var(--pnt-light);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
}

.modal-header {
    border-bottom: 1px solid rgba(99, 102, 241, 0.1);
}

.modal-footer {
    border-top: 1px solid rgba(99, 102, 241, 0.1);
}

.modal-header .btn-close {
    filter: invert(1);
}

/* ============================================
   PAGINATION
   ============================================ */
.page-link {
    background: var(--pnt-dark-light);
    border-color: rgba(99, 102, 241, 0.1);
    color: var(--pnt-primary-light);
    transition: all var(--transition-fast);
}

.page-link:hover {
    background-color: rgba(99, 102, 241, 0.1);
    border-color: rgba(99, 102, 241, 0.2);
    color: var(--pnt-white);
}

.page-item.active .page-link {
    background: var(--pnt-gradient-primary);
    border-color: var(--pnt-primary);
    color: #fff;
}

.page-item.disabled .page-link {
    background-color: rgba(15, 23, 42, 0.5);
    border-color: rgba(99, 102, 241, 0.05);
    color: var(--pnt-gray);
}

/* ============================================
   PROGRESS BARS
   ============================================ */
.progress {
    background-color: rgba(30, 41, 59, 0.5);
    border-radius: 4px;
    overflow: hidden;
}

/* ============================================
   CODE
   ============================================ */
code {
    color: var(--pnt-accent);
    background-color: rgba(6, 182, 212, 0.1);
    padding: 0.15em 0.4em;
    border-radius: 4px;
    font-size: 0.85em;
}

pre {
    background-color: rgba(15, 23, 42, 0.9) !important;
    border: 1px solid rgba(99, 102, 241, 0.1);
    color: var(--pnt-light);
    border-radius: 6px;
}

/* ============================================
   DROPDOWN / SELECT
   ============================================ */
option {
    background-color: var(--pnt-dark-light);
    color: var(--pnt-light);
}

/* ============================================
   BORDER UTILITIES (dark theme overrides)
   ============================================ */
.border-bottom { border-color: rgba(99, 102, 241, 0.1) !important; }
.border-primary { border-color: var(--pnt-primary) !important; }
.border-success { border-color: var(--pnt-success) !important; }
.border-danger { border-color: var(--pnt-danger) !important; }
.border-warning { border-color: var(--pnt-warning) !important; }
.border-info { border-color: var(--pnt-info) !important; }

/* ============================================
   TEXT COLOR UTILITIES (dark theme overrides)
   ============================================ */
.text-primary { color: var(--pnt-primary-light) !important; }
.text-success { color: var(--pnt-success) !important; }
.text-danger { color: var(--pnt-danger) !important; }
.text-warning { color: var(--pnt-warning) !important; }
.text-info { color: var(--pnt-info) !important; }
.text-muted { color: var(--pnt-gray) !important; }
.text-dark { color: var(--pnt-light) !important; }

/* ─── AUDIT-2026-08-05 PC-A11Y-06 / PC-ASSETS-04 (wave 10) — badge contrast ───

   WAS: status badges rendered Bootstrap's default WHITE text on the semantic tokens.
   Measured against those backgrounds:
     bg-success #10b981 + white  ≈ 2.5:1
     bg-warning #f59e0b + white  ≈ 2.2:1
     bg-info    #3b82f6 + white  ≈ 3.7:1
     bg-danger  #ef4444 + white  ≈ 3.8:1
   All below the 4.5:1 body-text floor, and the first two are below even the 3:1
   large-text floor. The "Online" badge on the device list — the single most-read
   piece of state in the product — was one of the worst.

   The same colours with DARK text clear it comfortably (≈7.0, ≈8.3, ≈4.9 and ≈4.7
   respectively). That is the direction chosen because it preserves the palette: the
   badge stays the green/amber/blue/red the operator has learned to read at a glance,
   and only the ink changes. Darkening the tokens instead would have shifted every
   other use of them — buttons, borders, icons, chart series.

   bg-primary is handled differently: dark text on #6366f1 is ~4.0:1 (fails) and white
   is ~4.5:1 (exactly at the threshold, i.e. fails under any rounding). Neither ink
   works on that background, so the BACKGROUND moves to --pnt-primary-dark #4f46e5,
   where white measures ~6.3:1.

   !important is required: these override Bootstrap's own .badge colour AND this
   file's `.text-dark { color: var(--pnt-light) }` utility, which several call sites
   pair with bg-warning and which — despite its name — sets a LIGHT colour, making
   those badges worse rather than better.

   The button half of this finding WAS deferred here in wave 10 with the note that
   "the right answer is probably a darker button-specific background". Wave 12 did
   exactly that — see the PC-A11Y-06 block above .btn-primary in the BUTTONS section,
   which darkens the .btn-primary / .btn-success / .btn-danger fills and keeps white
   ink. Badges and buttons therefore diverge on purpose (dark ink vs dark fill) and
   that divergence is argued there, not an oversight. Ratios above are computed from
   sRGB, not measured; verify in a browser before sign-off. */
.badge.bg-success,
.badge.bg-warning,
.badge.bg-info,
.badge.bg-danger {
    color: var(--pnt-dark) !important;
}

.badge.bg-primary {
    background-color: var(--pnt-primary-dark) !important;
    color: var(--pnt-white) !important;
}

/* ============================================
   BACKGROUND UTILITIES
   ============================================ */
.bg-light { background-color: var(--pnt-dark-lighter) !important; color: var(--pnt-light) !important; }
.bg-dark { background-color: var(--pnt-dark) !important; }

/* ============================================
   NAV PILLS / TABS
   ============================================ */
.nav-link {
    color: var(--pnt-gray-light);
    transition: all var(--transition-fast);
}

.nav-link:hover {
    color: var(--pnt-white);
}

.nav-link.active {
    color: var(--pnt-primary-light);
}

/* ============================================
   ACCORDION
   ============================================ */
.accordion-button {
    background-color: var(--pnt-dark-light);
    color: var(--pnt-light);
    transition: all var(--transition-fast);
}

.accordion-button:not(.collapsed) {
    background-color: rgba(99, 102, 241, 0.08);
    color: var(--pnt-primary-light);
}

.accordion-body {
    background-color: var(--pnt-dark-light);
}

/* ============================================
   HR
   ============================================ */
hr {
    border-color: rgba(99, 102, 241, 0.1);
    opacity: 0.5;
}

/* ============================================
   SELECTION
   ============================================ */
::selection {
    background: var(--pnt-primary);
    color: var(--pnt-white);
}

/* ============================================
   SPINNER
   ============================================ */
.spinner-border {
    color: var(--pnt-primary);
}

/* ============================================
   UTILITIES
   ============================================ */
.text-gradient {
    background: var(--pnt-gradient-primary);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.glow {
    box-shadow: var(--shadow-glow);
}

/* Section Utilities */
.section {
    padding: var(--space-3xl) 0;
}

.section-light {
    background: var(--pnt-dark-light);
}

.section-dark {
    background: var(--pnt-dark);
}

.page-header {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.15);
    border-radius: var(--radius-xl);
    padding: var(--space-xl) var(--space-2xl);
    margin-bottom: var(--space-xl);
}

/* ============================================
   MAINTENANCE CARD
   ============================================ */
.maintenance-card {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.2);
    border-radius: var(--radius-2xl);
    max-width: 500px;
    margin: 0 auto;
}

.maintenance-icon {
    font-size: 4rem;
    color: var(--pnt-primary);
    animation: spin 10s linear infinite;
}

/* ============================================
   FOOTER
   ============================================ */
.pnt-footer,
.site-footer {
    background: var(--pnt-dark);
    border-top: 1px solid rgba(99, 102, 241, 0.1);
    margin-top: auto;
    padding: var(--space-2xl) 0;
    color: var(--pnt-gray-light);
}

.footer-main {
    padding: var(--space-3xl) 0;
}

.footer-brand {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.footer-brand .brand-logo {
    width: 40px;
    height: 40px;
    font-size: 1.25rem;
}

.footer-description {
    color: var(--pnt-gray-light);
    font-size: 0.9rem;
    margin-top: var(--space-md);
    max-width: 300px;
}

.footer-heading {
    font-family: var(--font-display);
    font-weight: 600;
    color: var(--pnt-white);
    margin-bottom: var(--space-lg);
    text-transform: uppercase;
    font-size: 0.875rem;
    letter-spacing: 1px;
}

.footer-links {
    list-style: none;
    padding: 0;
    margin: 0;
}

.footer-links li {
    margin-bottom: var(--space-sm);
}

.footer-links a {
    color: var(--pnt-gray-light);
    font-size: 0.9rem;
    transition: all var(--transition-fast);
    display: inline-block;
}

.footer-links a:hover {
    color: var(--pnt-primary-light);
    transform: translateX(3px);
}

.social-links {
    display: flex;
    gap: var(--space-md);
    margin-top: var(--space-lg);
}

.social-link {
    width: 40px;
    height: 40px;
    background: var(--pnt-dark-light);
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--pnt-gray-light);
    font-size: 1.1rem;
    transition: all var(--transition-normal);
}

.social-link:hover {
    background: var(--pnt-primary);
    color: var(--pnt-white);
    transform: translateY(-3px);
}

.footer-bottom {
    background: rgba(0, 0, 0, 0.2);
    padding: var(--space-lg) 0;
    font-size: 0.875rem;
    color: var(--pnt-gray-light);
}

.footer-badge {
    display: inline-flex;
    align-items: center;
    background: rgba(16, 185, 129, 0.1);
    color: var(--pnt-success);
    padding: var(--space-xs) var(--space-md);
    border-radius: var(--radius-full);
    font-size: 0.8rem;
    font-weight: 500;
}

/* ============================================
   HERO SECTION (Landing page)
   ============================================ */
.hero-section {
    background: linear-gradient(135deg, #0f172a 0%, #1e293b 40%, #0f172a 70%, #1e293b 100%);
    border-bottom: 1px solid rgba(99, 102, 241, 0.1);
    padding: 5rem 0;
    position: relative;
    overflow: hidden;
}

.hero-section::before {
    content: '';
    position: absolute;
    top: 20%;
    left: 15%;
    width: 300px;
    height: 300px;
    background: radial-gradient(circle, rgba(99, 102, 241, 0.1) 0%, transparent 70%);
    border-radius: 50%;
    pointer-events: none;
}

.hero-section::after {
    content: '';
    position: absolute;
    bottom: 10%;
    right: 20%;
    width: 250px;
    height: 250px;
    background: radial-gradient(circle, rgba(139, 92, 246, 0.08) 0%, transparent 70%);
    border-radius: 50%;
    pointer-events: none;
}

.hero-section h1 {
    font-size: 3rem;
    font-weight: 700;
    background: var(--pnt-gradient-primary);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.hero-section .lead {
    color: var(--pnt-gray-light);
    font-size: 1.25rem;
    max-width: 600px;
    margin: 0 auto;
}

/* ============================================
   FEATURE CARDS (Landing page)
   ============================================ */
.feature-card {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.1);
    border-radius: var(--radius-xl);
    padding: 2rem;
    transition: all var(--transition-normal);
    animation: fadeInUp 0.4s ease;
}

.feature-card:hover {
    border-color: rgba(99, 102, 241, 0.3);
    transform: translateY(-2px);
    box-shadow: var(--shadow-glow), var(--shadow-lg);
}

.feature-icon {
    font-size: 2.5rem;
    margin-bottom: 1rem;
    color: var(--pnt-primary-light);
}

/* ============================================
   STEP NUMBER (Landing page)
   ============================================ */
.step-number {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    background: var(--pnt-gradient-primary);
    color: #fff;
    font-weight: 700;
    font-size: 1.25rem;
    margin-bottom: 1rem;
    box-shadow: 0 4px 15px rgba(99, 102, 241, 0.35);
}

/* ============================================
   BLAZOR ERROR UI
   ============================================ */
.blazor-error-boundary {
    background: rgba(239, 68, 68, 0.1);
    border: 1px solid rgba(239, 68, 68, 0.3);
    border-radius: var(--radius-lg);
    padding: var(--space-lg);
    color: #f87171;
}

#blazor-error-ui {
    background: var(--pnt-danger);
    color: white;
    position: fixed;
    bottom: 0;
    width: 100%;
    z-index: 1000;
    padding: 0.5rem 1.5rem;
    display: none;
}

/* AUDIT-2026-08-05 PC-A11Y-14 (wave 10): the dismiss control changed from an
   href-less <a> (not focusable, not keyboard-operable) to a real <button>. The
   selector was already tag-agnostic, so only the button's own user-agent chrome
   needs neutralising here — background, border, padding and inherited colour — to
   keep the bar looking exactly as it did. The focus outline is deliberately NOT
   suppressed: this control is now reachable by keyboard and must show where focus
   is. */
#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 1rem;
    top: 0.3rem;
    background: none;
    border: none;
    padding: 0;
    color: inherit;
    font: inherit;
    line-height: 1;
}

/* ============================================
   SCROLLBAR
   ============================================ */
::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: var(--pnt-dark);
}

::-webkit-scrollbar-thumb {
    background: var(--pnt-dark-lighter);
    border-radius: var(--radius-full);
}

::-webkit-scrollbar-thumb:hover {
    background: var(--pnt-gray);
}

/* ============================================
   FOCUS STATES (Accessibility)
   ============================================ */
.btn:focus-visible,
.form-control:focus-visible,
.nav-link:focus-visible {
    outline: 2px solid var(--pnt-primary-light);
    outline-offset: 2px;
}

/* 5.E6/E7 (audit 2026-06-11): broaden keyboard focus visibility to every
   interactive element, not just .btn / .form-control / .nav-link. Keyboard
   users get a clear focus ring on links, raw buttons, all form controls and
   anything made focusable via tabindex. Mirrors the outline style above; does
   not override the existing rule (same values where they overlap). */
a:focus-visible,
button:focus-visible,
.btn:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--pnt-primary-light);
    outline-offset: 2px;
}

/* GL-60 / item 4 (audit 2026-08-05) — focus-outline suppression sweep.
 *
 * app.css itself contains no `outline: none` / `outline: 0` and never did; the
 * suppression is INHERITED. wwwroot/lib/bootstrap/.../bootstrap.min.css sets
 * `outline: 0` on twelve focus states, and App.razor loads app.css AFTER it, so
 * the two rules above win wherever the selectors tie — but only where they tie.
 * They do not for the CLASS-based Bootstrap components:
 *
 *     bootstrap  .form-select:focus         → (0,2,0)  outline: 0
 *     app.css    select:focus-visible       → (0,1,1)  outline: 2px  ← LOSES
 *
 * So every `<select class="form-select">`, every checkbox and radio
 * (.form-check-input), the pager (.page-link), the modal ✕ (.btn-close), the
 * accordion header and the navbar toggler had NO focus outline at all — their
 * entire keyboard-focus indicator was Bootstrap's `box-shadow`, which app.css
 * :598-604 and :2050-2058 then REPLACE with a 3px indigo glow at 0.2 alpha over
 * a #0f172a page. On the admin surfaces that is most of the interactive
 * controls: /admin/logs alone puts the type, level, device and date filters
 * behind .form-select. WCAG 2.4.7 Focus Visible (AA) — a keyboard operator
 * cannot tell which filter is focused before typing into it.
 *
 * Fixed by restating the outline at a specificity that matches Bootstrap's and
 * relying on app.css's later source order, rather than by reaching for
 * `!important`: !important here would also beat a page's deliberate local focus
 * styling, and nothing in the Portal needs that today.
 *
 * :focus-visible, NOT :focus — Bootstrap's `outline: 0` on plain :focus is
 * CORRECT for pointer interaction (that is what `button:focus:not(:focus-visible)`
 * in reboot exists to express). The defect is only that keyboard focus lost its
 * ring with it. An inline `style="outline: none"` still beats all of this; the
 * one instance of that in the Portal (the viewer's run-command input) is removed
 * in RemoteViewer.razor as part of the same item.
 *
 * Deliberately NOT changed: the box-shadow focus styling at :598 and :2050. It
 * is weak, but it is an additional indicator, not the only one now, and
 * restyling every form control's focus glow is a design change rather than a
 * conformance fix. */
.form-select:focus-visible,
.form-check-input:focus-visible,
.form-control-plaintext:focus-visible,
.form-range:focus-visible,
.navbar-toggler:focus-visible,
.accordion-button:focus-visible,
.page-link:focus-visible,
.btn-close:focus-visible,
.btn-check:focus-visible + .btn {
    outline: 2px solid var(--pnt-primary-light);
    outline-offset: 2px;
}


/* ==============================================================
   ADMIN DESIGN SYSTEM (from admin.css)
   ============================================================== */

/* ============================================
   ADMIN LAYOUT (Sidebar + Content)
   ============================================ */

/* v5.E1 (audit 2026-06-11): skip-to-content link. First focusable element in
   the admin/customer layouts — hidden off-screen until focused, then slides into
   the top-left so a keyboard user can Tab once and jump past the nav (WCAG 2.4.1). */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 2000;
    padding: 0.6rem 1rem;
    background: var(--pnt-primary, #6366f1);
    color: #fff;
    font-weight: 600;
    text-decoration: none;
    border-radius: 0 0 6px 0;
}
.skip-link:focus {
    left: 0;
    outline: 2px solid #fff;
    outline-offset: 2px;
}

.admin-wrapper {
    display: flex;
    min-height: calc(100vh - 200px);
    gap: 0;
}

/* Sidebar */
.admin-sidebar {
    width: 260px;
    flex-shrink: 0;
    background: linear-gradient(180deg, rgba(15, 23, 42, 0.95) 0%, rgba(15, 23, 42, 0.85) 100%);
    border-right: 1px solid rgba(99, 102, 241, 0.1);
    padding: 1.5rem 0;
    position: sticky;
    top: 80px;
    height: calc(100vh - 80px);
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: var(--pnt-dark-lighter) transparent;
    z-index: 10;
}

.admin-sidebar::-webkit-scrollbar {
    width: 4px;
}

.admin-sidebar::-webkit-scrollbar-thumb {
    background: var(--pnt-dark-lighter);
    border-radius: 4px;
}

.admin-sidebar-brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0 1.25rem 1.25rem;
    border-bottom: 1px solid rgba(99, 102, 241, 0.1);
    margin-bottom: 1rem;
    text-decoration: none;
    color: inherit;
}

.admin-sidebar-brand:hover {
    color: inherit;
}

.admin-sidebar-brand-icon {
    width: 36px;
    height: 36px;
    background: var(--pnt-gradient-primary);
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1rem;
    color: white;
    flex-shrink: 0;
}

.admin-sidebar-brand-text {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--pnt-white);
}

.admin-sidebar-brand-text small {
    display: block;
    font-size: 0.65rem;
    font-weight: 400;
    color: var(--pnt-gray);
    text-transform: uppercase;
    letter-spacing: 1px;
}

/* Sidebar Navigation Groups */
.sidebar-nav-group {
    margin-bottom: 0.5rem;
}

.sidebar-nav-label {
    padding: 0.5rem 1.25rem 0.35rem;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    color: var(--pnt-gray);
}

.sidebar-nav-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.55rem 1.25rem;
    color: var(--pnt-gray-light);
    text-decoration: none;
    font-size: 0.85rem;
    font-weight: 500;
    transition: all 0.15s ease;
    border-left: 3px solid transparent;
    position: relative;
}

.sidebar-nav-item:hover {
    color: var(--pnt-white);
    background: rgba(99, 102, 241, 0.08);
    border-left-color: rgba(99, 102, 241, 0.3);
}

.sidebar-nav-item.active {
    color: var(--pnt-white);
    background: rgba(99, 102, 241, 0.12);
    border-left-color: var(--pnt-primary);
}

.sidebar-nav-item.active .sidebar-nav-icon {
    color: var(--pnt-primary-light);
}

.sidebar-nav-icon {
    width: 20px;
    text-align: center;
    font-size: 1rem;
    flex-shrink: 0;
    opacity: 0.7;
}

.sidebar-nav-item:hover .sidebar-nav-icon,
.sidebar-nav-item.active .sidebar-nav-icon {
    opacity: 1;
}

.sidebar-nav-badge {
    margin-left: auto;
    background: rgba(99, 102, 241, 0.2);
    color: var(--pnt-primary-light);
    font-size: 0.65rem;
    font-weight: 700;
    padding: 0.15rem 0.5rem;
    border-radius: 50px;
}

.sidebar-nav-badge.badge-danger {
    background: rgba(239, 68, 68, 0.2);
    color: #f87171;
}

.sidebar-nav-badge.badge-success {
    background: rgba(16, 185, 129, 0.2);
    color: #34d399;
}

.sidebar-nav-badge.badge-warning {
    background: rgba(245, 158, 11, 0.2);
    color: #fbbf24;
}

/* Admin Content Area */
.admin-content {
    flex: 1;
    min-width: 0;
    padding: 1.5rem 2rem;
    background: #0c1220;
}

/* Mobile Sidebar Toggle */
.admin-sidebar-toggle {
    display: none;
    position: fixed;
    bottom: 1.5rem;
    right: 1.5rem;
    z-index: 1050;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--pnt-gradient-primary);
    border: none;
    color: white;
    font-size: 1.25rem;
    box-shadow: 0 4px 20px rgba(99, 102, 241, 0.4);
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

/* Sidebar Overlay for Mobile */
.admin-sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 1039;
}

/* ============================================
   ADMIN PAGE HEADER
   ============================================ */
.admin-header {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.15);
    border-radius: 1rem;
    padding: 1.5rem 2rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 1rem;
}

.admin-header-content {
    display: flex;
    align-items: center;
    gap: 1.25rem;
}

.admin-icon {
    width: 56px;
    height: 56px;
    background: var(--pnt-gradient-primary);
    border-radius: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    color: white;
    flex-shrink: 0;
}

.admin-icon-main {
    width: 56px;
    height: 56px;
    background: var(--pnt-gradient-primary);
    border-radius: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    color: white;
    flex-shrink: 0;
}

.admin-header h1 {
    font-size: 1.5rem;
    margin-bottom: 0.15rem;
}

.admin-header p {
    color: var(--pnt-gray-light);
    font-size: 0.875rem;
}

.admin-header-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* ============================================
   STAT CARDS (Admin)
   ============================================ */
.stat-card {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.12);
    border-radius: 0.75rem;
    padding: 1.15rem 1.25rem;
    display: flex;
    align-items: center;
    gap: 1rem;
    transition: all 0.2s ease;
}

.stat-card:hover {
    border-color: rgba(99, 102, 241, 0.25);
    transform: translateY(-1px);
}

.stat-card.stat-warning {
    border-color: rgba(239, 68, 68, 0.35);
}

.stat-card-link {
    cursor: pointer;
}

.stat-card-link:hover {
    transform: translateY(-3px);
    border-color: rgba(99, 102, 241, 0.4);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}

.stat-icon {
    width: 44px;
    height: 44px;
    border-radius: 0.625rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    color: white;
    flex-shrink: 0;
}

.stat-content {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.stat-value {
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--pnt-white);
    line-height: 1.2;
}

.stat-label {
    font-size: 0.75rem;
    color: var(--pnt-gray-light);
    font-weight: 500;
}

/* ============================================
   ICON COLOR VARIANTS (Gradient backgrounds)
   ============================================ */
.icon-purple { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); }
.icon-blue { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }
.icon-green { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }
.icon-orange { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
.icon-cyan { background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%); }
.icon-pink { background: linear-gradient(135deg, #ec4899 0%, #db2777 100%); }
.icon-yellow { background: linear-gradient(135deg, #eab308 0%, #ca8a04 100%); }
.icon-red { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }
.icon-teal { background: linear-gradient(135deg, #14b8a6 0%, #0d9488 100%); }
.icon-indigo { background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%); }

/* Subtle / muted icon variants for stat-icon */
.stat-icon.icon-blue { background: rgba(59, 130, 246, 0.15); color: #60a5fa; }
.stat-icon.icon-red { background: rgba(239, 68, 68, 0.15); color: #f87171; }
.stat-icon.icon-green { background: rgba(34, 197, 94, 0.15); color: #4ade80; }
.stat-icon.icon-purple { background: rgba(139, 92, 246, 0.15); color: #a78bfa; }
.stat-icon.icon-orange { background: rgba(245, 158, 11, 0.15); color: #fbbf24; }
.stat-icon.icon-cyan { background: rgba(6, 182, 212, 0.15); color: #22d3ee; }
.stat-icon.icon-teal { background: rgba(20, 184, 166, 0.15); color: #2dd4bf; }
.stat-icon.icon-pink { background: rgba(236, 72, 153, 0.15); color: #f472b6; }
.stat-icon.icon-indigo { background: rgba(99, 102, 241, 0.15); color: #818cf8; }

/* Solid icon variants for section-icon / admin-card-icon */
.section-icon, .admin-card-icon {
    color: white;
}

.bg-purple { background: linear-gradient(135deg, #6366f1, #8b5cf6) !important; }

/* ============================================
   ADMIN SECTIONS & CARDS
   ============================================ */
.admin-section {
    margin-bottom: 2rem;
}

.section-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid rgba(99, 102, 241, 0.1);
}

.section-icon {
    width: 40px;
    height: 40px;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1rem;
    color: white;
    flex-shrink: 0;
}

.section-header h2 {
    font-size: 1.1rem;
    font-weight: 600;
    margin-bottom: 0;
    color: var(--pnt-white);
}

.section-header p {
    font-size: 0.8rem;
    color: var(--pnt-gray-light);
    margin-bottom: 0;
}

/* Admin navigation cards */
.admin-card {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1.15rem 1.25rem;
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.1);
    border-radius: 0.75rem;
    text-decoration: none;
    transition: all 0.25s ease;
    height: 100%;
}

.admin-card:hover {
    transform: translateY(-3px);
    border-color: rgba(99, 102, 241, 0.3);
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
}

.admin-card-icon {
    width: 44px;
    height: 44px;
    border-radius: 0.6rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.25rem;
    color: white;
    flex-shrink: 0;
}

.admin-card-content {
    flex: 1;
    min-width: 0;
}

.admin-card-content h5 {
    color: var(--pnt-white);
    margin-bottom: 0.15rem;
    font-size: 0.95rem;
    font-weight: 600;
}

.admin-card-content p {
    color: var(--pnt-gray-light);
    font-size: 0.8rem;
    margin-bottom: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.admin-card-arrow {
    color: var(--pnt-gray);
    font-size: 1rem;
    transition: transform 0.2s ease;
    flex-shrink: 0;
}

.admin-card:hover .admin-card-arrow {
    transform: translateX(4px);
    color: var(--pnt-primary-light);
}

.admin-card-dev {
    border-color: rgba(239, 68, 68, 0.2);
}

.admin-card-dev:hover {
    border-color: rgba(239, 68, 68, 0.4);
}

.dev-badge {
    display: inline-block;
    background: rgba(239, 68, 68, 0.2);
    color: #f87171;
    padding: 0.1rem 0.4rem;
    border-radius: 50px;
    font-size: 0.6rem;
    font-weight: 700;
    margin-top: 0.2rem;
}

/* ============================================
   QUICK STATS (Pill badges)
   ============================================ */
.quick-stats {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.stat-pill {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    background: rgba(99, 102, 241, 0.1);
    border: 1px solid rgba(99, 102, 241, 0.2);
    border-radius: 50px;
    color: var(--pnt-gray-light);
    font-size: 0.85rem;
}

.stat-pill i {
    color: var(--pnt-primary-light);
}

.stat-pill strong {
    color: white;
    margin-right: 0.25rem;
}

.stat-pill-warning {
    background: rgba(245, 158, 11, 0.15);
    border-color: rgba(245, 158, 11, 0.3);
}

.stat-pill-warning i { color: #fbbf24; }

.stat-pill-success {
    background: rgba(16, 185, 129, 0.15);
    border-color: rgba(16, 185, 129, 0.3);
}

.stat-pill-success i { color: #34d399; }

.stat-pill-danger {
    background: rgba(239, 68, 68, 0.15);
    border-color: rgba(239, 68, 68, 0.3);
    animation: pulse-danger 2s infinite;
}

.stat-pill-danger i { color: #f87171; }

/* ============================================
   MAINTENANCE TOGGLE
   ============================================ */
.maintenance-toggle {
    flex-shrink: 0;
}

.maintenance-toggle .btn-warning {
    background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
    border: none;
    animation: pulse-warning 2s infinite;
}

/* ============================================
   ADMIN PANELS (Forms, detail cards)
   ============================================ */
.admin-panel {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.15);
    border-radius: 0.75rem;
    overflow: hidden;
}

.panel-header {
    padding: 1rem 1.25rem;
    border-bottom: 1px solid rgba(99, 102, 241, 0.1);
    background: rgba(99, 102, 241, 0.05);
}

.panel-header h5 {
    margin: 0;
    font-weight: 600;
    color: var(--pnt-white);
    font-size: 0.95rem;
}

.panel-body {
    padding: 1.25rem;
}

.bg-dark-card {
    background: var(--pnt-gradient-card) !important;
    border: 1px solid rgba(99, 102, 241, 0.1);
}

.bg-darker {
    background: rgba(0, 0, 0, 0.3) !important;
}

/* ============================================
   ADMIN FILTER BAR
   ============================================ */
.filter-bar {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.1);
    border-radius: 0.75rem;
    padding: 1rem 1.25rem;
}

.filter-bar .form-label {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-bottom: 0.25rem;
}

/* ============================================
   ADMIN TABLES
   ============================================ */
.admin-table {
    font-size: 0.875rem;
}

.admin-table thead th,
.table-dark th {
    color: var(--pnt-gray-light);
    font-weight: 600;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    border-color: rgba(99, 102, 241, 0.1);
    white-space: nowrap;
}

.admin-table tbody td,
.table-dark td {
    color: #e2e8f0;
    border-color: rgba(99, 102, 241, 0.08);
    vertical-align: middle;
}

.row-error {
    background: rgba(239, 68, 68, 0.05) !important;
}

/* ============================================
   ADMIN FORMS (Dark overrides)
   ============================================ */
/* AUDIT-2026-08-08 PC-A11Y-08 (wave 12) — the admin half of the same finding.
   WAS border-color: rgba(99, 102, 241, 0.25). This selector is 0,2,0 and therefore
   BEATS the base `.form-control` rule, so fixing only the base rule would have left
   every field on every /admin page exactly as unreadable as before while the audit
   item read "closed" — a half-applied fix, which is worse than none.

   A 25%-alpha indigo over rgba(15,23,42,0.8) composites to roughly #2a3050 against
   the field's own fill: ~1.3:1, i.e. marginally WORSE than the #334155 the base rule
   used. Same token, same 3:1 target. --pnt-border-input is opaque on purpose —
   an alpha border's effective contrast depends on whatever surface happens to be
   behind the field, so it cannot be reasoned about once and relied on. */
.admin-content .form-control,
.admin-content .form-select,
.admin-panel .form-control,
.admin-panel .form-select {
    background: rgba(15, 23, 42, 0.8);
    border-color: var(--pnt-border-input);
    color: #e2e8f0;
}

.admin-content .form-control:focus,
.admin-content .form-select:focus,
.admin-panel .form-control:focus,
.admin-panel .form-select:focus {
    background: rgba(15, 23, 42, 0.9);
    border-color: rgba(99, 102, 241, 0.5);
    color: #fff;
    box-shadow: 0 0 0 0.2rem rgba(99, 102, 241, 0.2);
}

.admin-content .input-group-text {
    background: rgba(99, 102, 241, 0.15);
    border-color: rgba(99, 102, 241, 0.25);
    color: #e2e8f0;
}

/* ============================================
   BADGE VARIANTS (Method, Status)
   ============================================ */
.method-badge {
    font-size: 0.65rem;
    font-weight: 700;
    padding: 0.15rem 0.4rem;
    border-radius: 0.25rem;
    text-transform: uppercase;
}

.method-get { background: rgba(59, 130, 246, 0.15); color: #60a5fa; }
.method-post { background: rgba(34, 197, 94, 0.15); color: #4ade80; }
.method-put { background: rgba(245, 158, 11, 0.15); color: #fbbf24; }
.method-delete { background: rgba(239, 68, 68, 0.15); color: #f87171; }

.status-badge {
    font-size: 0.7rem;
    font-weight: 700;
    padding: 0.2rem 0.5rem;
    border-radius: 0.25rem;
}

.badge-success { background: rgba(34, 197, 94, 0.15); color: #4ade80; }
.badge-warning { background: rgba(245, 158, 11, 0.15); color: #fbbf24; }
.badge-error { background: rgba(239, 68, 68, 0.15); color: #f87171; }

/* ============================================
   LOG VIEWER
   ============================================ */
.log-table {
    font-size: 0.875rem;
}

.log-table thead th {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    border-bottom-width: 2px;
}

.log-pre {
    background: rgba(15, 23, 42, 0.8);
    border: 1px solid rgba(99, 102, 241, 0.15);
    border-radius: 0.5rem;
    padding: 0.75rem 1rem;
    font-size: 0.8rem;
    max-height: 300px;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-all;
}

.log-error {
    border-color: rgba(239, 68, 68, 0.3);
    color: #f87171;
}

/* ============================================
   SUPPORT MANAGEMENT
   ============================================ */
.ticket-number {
    font-family: monospace;
    font-size: 0.85rem;
}

.ticket-message {
    padding: 1rem;
    border-radius: 0.5rem;
    background: rgba(0, 0, 0, 0.2);
    border-left: 3px solid rgba(99, 102, 241, 0.5);
}

.ticket-message.admin-message {
    border-left-color: #f59e0b;
    background: rgba(245, 158, 11, 0.1);
}

.ticket-message.internal-message {
    border-left-color: #6b7280;
    background: rgba(107, 114, 128, 0.1);
    border-style: dashed;
}

/* ============================================
   DATABASE HEALTH
   ============================================ */
.health-banner {
    border-radius: 0.75rem;
    padding: 1.25rem 1.5rem;
    display: flex;
    align-items: center;
    gap: 1.25rem;
}

.health-banner.health-good {
    background: rgba(16, 185, 129, 0.12);
    border: 1px solid rgba(16, 185, 129, 0.3);
    color: #34d399;
}

.health-banner.health-warning {
    background: rgba(245, 158, 11, 0.12);
    border: 1px solid rgba(245, 158, 11, 0.3);
    color: #fbbf24;
}

.health-banner.health-critical {
    background: rgba(239, 68, 68, 0.12);
    border: 1px solid rgba(239, 68, 68, 0.3);
    color: #f87171;
}

.health-icon {
    font-size: 2rem;
}

/* ============================================
   STATUS CARDS
   ============================================ */
.status-card {
    border-radius: 0.75rem;
    padding: 1rem 1.25rem;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    border: 1px solid;
    background: var(--pnt-gradient-card);
    transition: all 0.2s ease;
}

.status-card.status-good {
    background: rgba(16, 185, 129, 0.1);
    border-color: rgba(16, 185, 129, 0.3);
}

.status-card.status-warn {
    background: rgba(245, 158, 11, 0.1);
    border-color: rgba(245, 158, 11, 0.3);
}

.status-card.status-bad {
    background: rgba(239, 68, 68, 0.1);
    border-color: rgba(239, 68, 68, 0.3);
}

.status-icon {
    width: 36px;
    height: 36px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.status-good .status-icon { background: rgba(16, 185, 129, 0.15); color: var(--pnt-success); }
.status-warn .status-icon { background: rgba(245, 158, 11, 0.15); color: var(--pnt-warning); }
.status-bad .status-icon { background: rgba(239, 68, 68, 0.15); color: var(--pnt-danger); }

.status-label {
    font-size: 0.7rem;
    color: var(--pnt-gray);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.status-value {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--pnt-white);
}

.status-banner {
    border-radius: 0.75rem;
    padding: 1.25rem 1.5rem;
    display: flex;
    align-items: center;
    gap: 1rem;
}

.status-banner.status-success {
    background: rgba(16, 185, 129, 0.1);
    border: 1px solid rgba(16, 185, 129, 0.25);
}

.status-banner.status-warning {
    background: rgba(245, 158, 11, 0.1);
    border: 1px solid rgba(245, 158, 11, 0.25);
}

/* ============================================
   SETTINGS CARDS
   ============================================ */
.settings-card {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.15);
    border-radius: var(--radius-xl);
    overflow: hidden;
    transition: all var(--transition-normal);
}

/* Equal height only inside grid columns */
.col-lg-6 > .settings-card,
.col-md-6 > .settings-card {
    height: 100%;
}

.settings-card:hover {
    border-color: rgba(99, 102, 241, 0.3);
    box-shadow: var(--shadow-glow);
}

.settings-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1.25rem 1.5rem;
    background: rgba(30, 41, 59, 0.5);
    border-bottom: 1px solid rgba(99, 102, 241, 0.1);
}

.settings-icon {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.25rem;
    color: white;
    flex-shrink: 0;
}

.settings-body {
    padding: 1.5rem;
}

.settings-footer {
    padding: 1rem 1.5rem;
    background: rgba(0, 0, 0, 0.2);
    border-top: 1px solid rgba(99, 102, 241, 0.1);
}

.current-status {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
}

.info-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1rem;
}

.badge-good {
    background: rgba(16, 185, 129, 0.15);
    color: #34d399;
    border: 1px solid rgba(16, 185, 129, 0.3);
}

.badge-warn {
    background: rgba(245, 158, 11, 0.15);
    color: #fbbf24;
    border: 1px solid rgba(245, 158, 11, 0.3);
}

.badge-bad {
    background: rgba(239, 68, 68, 0.15);
    color: #f87171;
    border: 1px solid rgba(239, 68, 68, 0.3);
}

.debug-active-card {
    border-color: rgba(245, 158, 11, 0.4);
}

.security-list {
    padding-left: 1.25rem;
    color: var(--pnt-gray-light);
    font-size: 0.9rem;
}

.security-list li {
    margin-bottom: 0.5rem;
}

/* ============================================
   LOGIN CARD
   ============================================ */
.login-card {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.15);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
}

/* ============================================
   UPLOAD OVERLAY
   ============================================ */
.upload-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
}

.upload-overlay-card {
    background: var(--pnt-gradient-card);
    border: 1px solid rgba(99, 102, 241, 0.3);
    border-radius: 16px;
    padding: 2rem 2.5rem;
    width: 100%;
    max-width: 480px;
    text-align: center;
}

.upload-overlay-card h5 {
    color: #fff;
    margin-bottom: 1.5rem;
}

.upload-progress-bar-track {
    width: 100%;
    height: 12px;
    background: rgba(99, 102, 241, 0.15);
    border-radius: 6px;
    overflow: hidden;
    margin-bottom: 1rem;
}

.upload-progress-bar-fill {
    height: 100%;
    width: 0%;
    background: var(--pnt-gradient-primary);
    border-radius: 6px;
    transition: width 0.3s ease;
}

/* ============================================
   LANGUAGE SWITCHER
   ============================================ */
.language-switcher {
    font-size: 0.875rem;
}

.language-switcher a {
    color: var(--pnt-gray-light);
    text-decoration: none;
    transition: color 0.2s ease;
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
}

.language-switcher a:hover {
    color: var(--pnt-white);
    background: rgba(255, 255, 255, 0.1);
}

.language-switcher a.active {
    color: var(--pnt-primary-light);
    font-weight: 500;
}


/* ==============================================================
   RESPONSIVE BREAKPOINTS
   ============================================================== */

/* Responsive edge padding */
@media (min-width: 768px) {
    .main-content {
        padding-left: var(--site-edge-padding-md);
        padding-right: var(--site-edge-padding-md);
    }

    .full-width-section {
        margin-left: calc(-1 * var(--site-edge-padding-md));
        margin-right: calc(-1 * var(--site-edge-padding-md));
        padding-left: var(--site-edge-padding-md);
        padding-right: var(--site-edge-padding-md);
    }

    .edge-to-edge {
        margin-left: calc(-1 * var(--site-edge-padding-md));
        margin-right: calc(-1 * var(--site-edge-padding-md));
        width: calc(100% + (2 * var(--site-edge-padding-md)));
    }
}

@media (min-width: 1200px) {
    .main-content {
        padding-left: var(--site-edge-padding-lg);
        padding-right: var(--site-edge-padding-lg);
    }

    .full-width-section {
        margin-left: calc(-1 * var(--site-edge-padding-lg));
        margin-right: calc(-1 * var(--site-edge-padding-lg));
        padding-left: var(--site-edge-padding-lg);
        padding-right: var(--site-edge-padding-lg);
    }

    .edge-to-edge {
        margin-left: calc(-1 * var(--site-edge-padding-lg));
        margin-right: calc(-1 * var(--site-edge-padding-lg));
        width: calc(100% + (2 * var(--site-edge-padding-lg)));
    }
}

/* Navbar responsive */
@media (max-width: 991.98px) {
    .pnt-navbar .navbar-collapse {
        background: var(--pnt-dark-light);
        padding: var(--space-lg);
        border-radius: var(--radius-lg);
        margin-top: var(--space-md);
    }

    .pnt-navbar .nav-link {
        padding: var(--space-md) !important;
    }

    .display-4 {
        font-size: 2rem;
    }
}

/* Admin layout responsive */
@media (max-width: 991.98px) {
    /* ─── AUDIT-2026-06-11 PC-A11Y-17 (wave 38) — the CLOSED drawer stayed in the
       tab order ───

       WAS: `position: fixed; left: -280px;` here with `.admin-sidebar.show { left: 0 }`
       below, and nothing else. Parking an element off-screen hides it from the eye
       only: every link in the drawer stayed focusable, so at <=991px a keyboard or
       screen-reader user tabbed through the ENTIRE hidden navigation — on every admin
       and customer page — before reaching the page content. The skip link at the top
       of both layouts mitigates that for a user who knows to take it; it does not fix
       it, and nothing else in this file did.

       NOW: hidden means hidden. `visibility: hidden` removes the drawer from the tab
       order and from the accessibility tree, and `.show` puts it back. `display: none`
       would do the same but cannot be transitioned, so the slide would be lost;
       `inert` is an HTML attribute, not a property, and would have to be toggled from
       script on an element two components away from the toggle.

       WHY visibility IS IN THE TRANSITION LIST, which looks pointless at 0s duration:
       a bare `visibility: hidden` takes effect the instant `.show` is removed, so the
       slide-OUT would never be seen — the drawer would vanish instead of sliding. The
       0.3s DELAY holds the element paintable for exactly as long as the `left`
       transition runs and flips it at the far end. `.show` restates the transition with
       a 0s delay so the slide-IN is visible from its first frame rather than appearing
       0.3s late.

       Under prefers-reduced-motion the block near the end of this file resets
       transition-duration AND transition-delay to ~0 with !important, so both flips
       become instant. That is the right outcome there: there is no slide left to stay
       visible for.

       SCOPE — read this before touching it: these rules are INSIDE
       `@media (max-width: 991.98px)`. The desktop sidebar is the ungated
       `.admin-sidebar` rule ~1100 lines up (position: sticky) and is deliberately not
       touched. Hiding that one would blank the navigation on every desktop admin page,
       which would be far worse than the defect fixed here.

       The other half of PC-A11Y-17 — focus never moving into, being trapped by, or
       being restored from the OPEN drawer — is in wwwroot/js/sidebar.js. */
    .admin-sidebar {
        position: fixed;
        left: -280px;
        top: 0;
        height: 100vh;
        z-index: 1040;
        visibility: hidden;
        transition: left 0.3s ease, visibility 0s linear 0.3s;
        padding-top: 1rem;
    }

    .admin-sidebar.show {
        left: 0;
        visibility: visible;
        transition: left 0.3s ease, visibility 0s linear 0s;
    }

    .admin-sidebar-overlay.show {
        display: block;
    }

    .admin-sidebar-toggle {
        display: flex;
    }

    .admin-content {
        padding: 1rem;
    }

    .admin-header {
        flex-direction: column;
        text-align: center;
        padding: 1.25rem;
    }

    .admin-header-content {
        flex-direction: column;
        text-align: center;
    }

    .admin-header-actions {
        justify-content: center;
    }

    .section-header {
        flex-direction: column;
        text-align: center;
        gap: 0.5rem;
    }

    .quick-stats {
        justify-content: center;
    }

    /* Admin section cards stack */
    .admin-section .row {
        flex-direction: column;
    }

    .admin-section .section-header {
        flex-wrap: wrap;
        gap: 1rem;
    }

    .col-md-3 {
        flex: 0 0 50%;
        max-width: 50%;
    }
}

@media (max-width: 767.98px) {
    .admin-content {
        padding: 0.75rem;
    }

    .stat-card {
        padding: 0.85rem 1rem;
    }

    .stat-value {
        font-size: 1.15rem;
    }

    .admin-header {
        padding: 1rem;
    }

    .admin-icon {
        width: 44px;
        height: 44px;
        font-size: 1.25rem;
        border-radius: 10px;
    }

    .admin-header h1 {
        font-size: 1.25rem;
    }

    /* Full width cards on mobile */
    .col-md-3, .col-md-4, .col-md-6 {
        flex: 0 0 100%;
        max-width: 100%;
    }

    /* ─── AUDIT-2026-08-08 PC-A11Y-29 (wave 12) ───

       WAS, verbatim:
           .d-flex.justify-content-between { flex-direction: column !important; gap: 1rem; }

       Unscoped. Any element carrying both Bootstrap utilities got force-stacked
       below 768px — which is every phone, every tablet in portrait, and every
       desktop at 200% zoom. The comment said "header elements" but the selector
       could not tell a header from anything else, so it also caught the small
       label/value rows those utilities are most commonly used for
       (CustomerDevices.razor:570 `<div class="d-flex justify-content-between mt-1">`
       is one: a caption and its number, which became two lines a full 1rem apart).
       `!important` meant a page could not opt out even with Bootstrap's own
       .flex-row / .flex-md-row utilities, which DO carry !important — so page
       intent lost to a global rule that had no idea what it was stacking.

       FIXED in two rules, deliberately separate:

       (1) The semantic wrappers below are headers/banners by NAME, so stacking them
           is a decision about a known thing. This rule carries no :has() so it
           survives on any engine.

       (2) The :has() rule identifies a page header STRUCTURALLY — a flex row that
           contains a heading. That is what <PageHeader> renders
           (`<div class="d-flex justify-content-between align-items-center mb-4">`
           wrapping an <h1>) and it ships no distinguishing class, so structure is
           the only handle available without editing the .razor components, which
           are owned elsewhere this cycle. It is a SEPARATE rule because an invalid
           selector invalidates its whole comma list: folded into rule (1) it would
           take the semantic wrappers down with it on an engine without :has().

       `!important` is dropped from both. A page that needs a row back now writes
       .flex-row and wins, which is the correct precedence.

       Consequence stated rather than glossed: a header row that is neither named
       nor heading-bearing no longer stacks and may overflow at 320px. That is the
       house convention (UI_STYLEGUIDE.md §9 — overflow is absorbed by horizontal
       scrolling) and it is the same trade GL-61 made further down this same media
       block when it deleted the column-priority rule. Present-and-scrollable beats
       reflowed-by-guesswork. */
    .admin-topbar.d-flex.justify-content-between,
    .admin-header.d-flex.justify-content-between,
    .page-header.d-flex.justify-content-between,
    /* PC-A11Y-29 (wave 13): Components/Shared/PageHeader.razor now emits a
       `pnt-page-header` class, so the Portal's most common header row is matched by
       NAME here instead of relying on the :has() rule immediately below. The prefixed
       name (not the bare `page-header` this list already carried) is deliberate —
       app.css:1240 declares a dormant `.page-header` card style, and reusing that name
       would have restyled every page header in the product. See the component. */
    .pnt-page-header.d-flex.justify-content-between,
    .alert.d-flex.justify-content-between {
        flex-direction: column;
        gap: 1rem;
    }

    .d-flex.justify-content-between:has(h1, h2, h3, .h1, .h2, .h3) {
        flex-direction: column;
        gap: 1rem;
    }

    .btn-group {
        flex-wrap: wrap;
    }

    .table-responsive {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .admin-card {
        padding: 0.75rem;
    }

    .admin-card-icon {
        width: 40px;
        height: 40px;
        font-size: 1.2rem;
    }

    .d-none-mobile {
        display: none !important;
    }

    .container-fluid {
        padding-left: 0.75rem;
        padding-right: 0.75rem;
    }

    .card-body {
        padding: 1rem;
    }

    /* ─── AUDIT-2026-08-05 PC-A11Y-19 / PC-ASSETS-02 (wave 10) ───

       WAS: a bare `canvas { max-height: 250px; }` element selector inside the ≤768px
       media query. It was written for the dashboard chart canvases and it caught the
       REMOTE DESKTOP canvas too. The viewer's canvas carries only an inline
       `height: 100%` (RemoteViewer.razor) — a DIFFERENT property, which does not
       override a stylesheet `max-height` no matter how specific the inline style is.
       So on a phone, on a tablet in portrait, or on any desktop at 200% browser zoom
       (which is what puts the viewport under 768 CSS px, and is a WCAG 1.4.4
       requirement), the remote machine's screen collapsed into a 250px-tall strip
       with the rest of the viewport black.

       Scoped away from the viewer by id rather than deleted: the chart canvases on
       the dashboards genuinely need the cap, and removing it would let them grow to
       full page height on mobile. #remoteCanvas is the only canvas in the product
       that must be allowed to fill its container — it IS the content. */
    canvas:not(#remoteCanvas) {
        max-height: 250px;
    }

    /* Footer mobile */
    .footer-main {
        text-align: center;
    }

    .footer-brand {
        justify-content: center;
    }

    .footer-description {
        margin: var(--space-md) auto;
    }

    .social-links {
        justify-content: center;
    }

    .footer-links a:hover {
        transform: none;
    }

    .footer-bottom .row > div {
        padding: 0.5rem 0;
    }

    .language-switcher {
        margin: 0.5rem 0;
    }

    /* GL-61 (audit 2026-08-05, PC-A11Y-02 ≡ PC-ASSETS-01) — part 2 of 2.
     *
     * REMOVED from this spot, under the heading "Table column priority":
     *
     *     .table th:nth-child(n+4),
     *     .table td:nth-child(n+4) { display: none; }
     *     .table th:last-child,
     *     .table td:last-child    { display: table-cell; }
     *
     * What it did: unscoped, so it deleted the 4th column onward of EVERY
     * table in the Portal below 768px — which is every phone, every tablet in
     * portrait, and every desktop at 200% browser zoom (1280 CSS px / 2 = 640).
     * :nth-child counts sibling position regardless of `display`, so the
     * per-column d-none utilities the pages already carry did not shift the
     * count. On /customer/devices the 5th cell is Status: the mobile device
     * list rendered Name and a Connect button and nothing else, so the operator
     * could not tell whether a machine was reachable and every Connect was a
     * guess. /admin/logs lost the Message column; /admin/fleet-health, which
     * carries no per-column classes at all, lost 15 of its 19 columns.
     * Nothing signalled the loss: `display: none` generates no overflow, so the
     * wrapping .table-responsive had nothing to scroll and showed no affordance.
     * WCAG 2.2 AA 1.4.10 Reflow requires content to REFLOW at 320 CSS px, not
     * be removed, so this was a conformance failure and not only a UX one.
     * The `:last-child` exception was also wrong on its own terms — it assumed
     * the last column is always Actions, which on /admin/logs it is not.
     *
     * Why deleting it is right: column priority is page knowledge. A positional
     * rule in a global stylesheet cannot know which column is load-bearing and
     * silently breaks whenever a page reorders its columns — CustomerDevices
     * .razor:76-77 says in a comment "always keep Name, Status, and Actions
     * visible" and expresses exactly that with d-none/d-lg-table-cell/
     * d-xl-table-cell, and the global rule overrode it. With the rule gone,
     * that page renders Name + Status + Actions on a phone, as its author
     * intended, and pages that declare no priority show all their columns and
     * scroll. Per UI_STYLEGUIDE.md §9 overflow is absorbed by horizontal
     * scrolling; part 1 above extends that to the tables that ship without the
     * .table-responsive wrapper, so nothing here reintroduces page-level
     * overflow.
     *
     * The cell floor below is what makes that scroll actually happen. Without
     * it a 19-column table does not overflow a 375px viewport — it compresses
     * to ~20px per column and wraps a character per line, which is present-but-
     * unreadable and no better than hidden. 5rem is a deliberate floor, not a
     * width: it applies to rendered cells only, so it sums over the columns a
     * page actually shows (device list: 3 visible × 5rem = 15rem, still fits a
     * 375px viewport with no scrollbar; fleet health: 19 × 5rem, scrolls).
     * Selector matches the Bootstrap 5.3 cell selector already used at the top
     * of the TABLES section, so it lands on th and td in every section.
     *
     * Deliberate non-action: no stacked/card restructure at this breakpoint.
     * That is the nicer pattern but it means `display: block` on rows and cells
     * across 36 tables plus data-label content on every td, i.e. edits to the
     * .razor files, which are owned elsewhere this cycle. Horizontal scroll is
     * the house convention and preserves both the content and the table
     * semantics. */
    .table > :not(caption) > * > * {
        min-width: 5rem;
    }

    .table .btn-sm {
        padding: 0.25rem 0.5rem;
        font-size: 0.75rem;
    }
}

@media (max-width: 575.98px) {
    h1.h3, .h1, .h2, .h3 {
        font-size: 1.25rem;
    }

    .col-6 {
        flex: 0 0 100%;
        max-width: 100%;
    }

    .dropdown-menu {
        width: 100%;
        max-width: 280px;
    }

    .modal-dialog {
        margin: 0.5rem;
    }

    .badge {
        font-size: 0.7rem;
        padding: 0.25em 0.5em;
    }
}

/* Touch-friendly targets */
@media (hover: none) and (pointer: coarse) {
    /* ─── AUDIT-2026-08-08 PC-ASSETS-08 (wave 12) ───

       WAS: `.btn, .nav-link, .dropdown-item { min-height: 44px; display: flex; ... }`

       `display: flex` makes a BLOCK-level flex container. .btn is inline-block by
       design, so on every touch device — which is every phone and tablet, i.e. the
       exact devices the 44px target was added for — each button became full-width
       and claimed its own line. Two buttons meant to sit side by side stacked, and
       any `<button>` sitting inline in a sentence broke the line.

       Worse and less obvious: a flex container ignores `text-align: center`, which
       is how Bootstrap centres a button label. With the button now stretched to the
       container width and no justify-content set (flex-start is the initial value),
       labels drifted to the left edge of a full-width bar. So the rule both changed
       layout and un-centred the text it was widening.

       FIXED: .btn gets inline-flex, which keeps the inline-block flow the rest of
       the stylesheet assumes while still giving a flex box to vertically centre in,
       plus an explicit justify-content: center to restore the label centring that
       ANY flex value costs. .nav-link and .dropdown-item keep `flex`: those are
       block-level list items already, full-width is what they want, and a wide
       target row is the point on touch. Splitting the selector is what lets the two
       cases differ — one rule could not serve both.

       min-height: 44px is unchanged on all three; it is the WCAG 2.2 AA 2.5.8
       target size and was never the defect. */
    .btn {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    .nav-link, .dropdown-item {
        min-height: 44px;
        display: flex;
        align-items: center;
    }

    .dropdown-item {
        padding: 0.75rem 1rem;
    }
}

/* Print styles */
@media print {
    .navbar, .footer, .btn, .dropdown, .admin-sidebar {
        display: none !important;
    }

    .main-content, .admin-content {
        padding: 0 !important;
    }

    .card {
        break-inside: avoid;
        border: 1px solid #ddd !important;
        box-shadow: none !important;
    }

    body {
        background: white !important;
        color: black !important;
    }

    /* ─── AUDIT-2026-08-08 PC-ASSETS-19 (wave 12) ───

       WAS: the `body { background: white; color: black }` above was the whole ink
       reset. `color` inherits, but ONLY into descendants that do not set their own —
       and this stylesheet sets its own everywhere, with !important. `.text-muted`
       (182+ call sites: every StatCard label, every table caption, every timestamp)
       resolves to --pnt-gray; card/badge/table rules paint their own light inks over
       their own dark fills. All of those still won on the printed page, so a printed
       report came out as pale grey and near-white text on white paper — most of the
       information density in the product, invisible.

       Why a `* { color: #000 !important }` would NOT have fixed it, since that is the
       obvious first attempt: `*` has specificity 0,0,0 and `.text-muted` has 0,1,0.
       When both declarations are !important the CASCADE STILL RUNS — importance only
       flips which origin/layer wins, specificity then decides inside it. So the
       universal selector loses to every class-based utility in this file. The
       offenders have to be named at their own specificity or higher, which is why
       the list below is explicit rather than a wildcard. Equal specificity is enough
       (0,1,0 vs 0,1,0) because this block is later in the file and source order
       breaks the tie — and @media print does not change specificity, so nothing here
       relies on the media query for its weight.

       Backgrounds go transparent rather than white so the paper shows through
       without each element compositing its own sheet, and so nested cards do not
       print a stack of overlapping white boxes.

       Badges keep a hairline border: with the fill gone, "Online" and "Offline" would
       otherwise print as bare words with no chip around them and lose the visual
       grouping that makes a status column scannable.

       .text-gradient is the one case that needs more than a colour: it paints text
       with -webkit-text-fill-color: transparent over a background-clip: text
       gradient, so a `color` override alone leaves it INVISIBLE (fill wins over
       color). It needs the fill and the clip unwound too.

       NOT claimed: this is a legibility fix, not a print-layout redesign. Page
       breaks, table repeat-headers and link-target expansion are untouched. */
    .text-primary, .text-success, .text-danger, .text-warning, .text-info,
    .text-muted, .text-dark, .text-white, .text-light, .text-secondary,
    .card, .card-body, .card-header, .card-footer, .card-title, .card-text,
    .admin-card, .admin-panel, .stat-card, .stat-value, .stat-label,
    .table, .table th, .table td, .table thead th, .table tbody tr,
    .list-group-item, .alert,
    h1, h2, h3, h4, h5, h6, p, li, dt, dd, small, strong, label, a, span, div {
        color: #000 !important;
        background: transparent !important;
        background-color: transparent !important;
        background-image: none !important;
        text-shadow: none !important;
        box-shadow: none !important;
    }

    .text-gradient {
        color: #000 !important;
        background: none !important;
        -webkit-text-fill-color: #000 !important;
        -webkit-background-clip: border-box !important;
        background-clip: border-box !important;
    }

    .badge, .badge.bg-primary, .badge.bg-secondary, .badge.bg-success,
    .badge.bg-warning, .badge.bg-info, .badge.bg-danger {
        color: #000 !important;
        background: transparent !important;
        background-color: transparent !important;
        border: 1px solid #666 !important;
    }

    .table, .table th, .table td {
        border-color: #999 !important;
    }

    a[href] {
        text-decoration: underline;
    }
}

/* ============================================
   REDUCED MOTION
   ============================================ */
/* ─── AUDIT-2026-08-08 PC-A11Y-12 / PC-ASSETS-27 (wave 12) ───

   WAS: no prefers-reduced-motion query anywhere in this file's 2947 lines, while it
   defines five infinite/entrance keyframes (fadeInUp, pulse, pulse-danger,
   pulse-warning, spin) and drives `transition: all` from --transition-fast/normal on
   most interactive surfaces. Only the vendored Bootstrap copy honoured the setting,
   so a user with vestibular disorder who has asked their OS to reduce motion still
   got a pulsing red stat pill (.stat-pill-danger, 2s infinite), a pulsing maintenance
   button (.maintenance-toggle .btn-warning, 2s infinite), an infinitely spinning
   maintenance icon and a hover-lift on every button. WCAG 2.2 AAA 2.3.3 asks for
   motion to be suppressible; the OS-level media query is the mechanism.

   Scoped with the universal selector ON PURPOSE, which is the opposite of the
   PC-ASSETS-19 argument twenty lines up and worth spelling out so the two do not
   read as contradictory. Print needed to BEAT named class rules, and `*` cannot do
   that. Here `*` is the only selector that reaches keyframes this file does not know
   about — the vendored Bootstrap ones, and any future rule someone adds without
   remembering this block exists. A named list would silently stop covering new
   animations, which is how the gap opened in the first place.

   ─── AUDIT-2026-08-08 PC-A11Y-12 (wave 13 repair) — THE "UNOPPOSED" CLAIM WAS FALSE ───

   The wave-12 version of this paragraph asserted: "no rule in this file sets
   animation-duration or transition-duration with !important, so `*` is unopposed".
   That verification evidently grepped for the LONGHAND property names only.
   `.nav-auth-btn` (app.css:342-347) declares `transition: all var(--transition-normal)
   !important` — an !important SHORTHAND, which expands to transition-duration at
   specificity 0,1,0. Among !important author declarations the cascade still runs and
   specificity still decides (the same argument this file makes correctly for the print
   block ~90 lines up), so `.nav-auth-btn` BEAT the `*` reset and the navbar Sign In /
   Register buttons kept their full transition under prefers-reduced-motion. The
   `.nav-auth-btn.btn-register:hover` translateY (0,3,0) likewise outranked the
   `.btn:hover { transform: none }` exemption below (0,2,0), so the 2px lift survived too.

   `*` is therefore NOT unopposed, and the honest statement is: `*` is the floor, and
   any rule in this file that sets a transition/animation shorthand with !important, or
   a transform at higher specificity than the exemption list, has to be named
   explicitly. Today that is exactly one selector, and it is named at the bottom of
   this block. If you add another !important transition, add it there too.

   0.01ms rather than 0: a zero duration cancels the animation outright in some
   engines, which means animationend never fires and any script waiting on it hangs.
   The near-zero value keeps the event and removes the motion. iteration-count: 1
   stops the infinite pulses after a single pass rather than freezing them mid-cycle.

   SPINNERS ARE EXEMPTED and that is deliberate, not an oversight: .spinner-border /
   .spinner-grow are not decoration, they are the only signal that a request is still
   in flight, and a frozen spinner reads as a hung page. "Reduce motion" means remove
   gratuitous motion, not remove status. Their rotation is smooth, small and
   non-parallax, which is the category the guideline exempts. They are restored at
   0,1,0 specificity in a LATER rule, so they beat the `*` reset by specificity and
   do not depend on the !important flags for it.

   NOT claimed: this suppresses CSS animation and transition only. It cannot reach
   the canvas frame stream in the remote viewer (that is video, and suppressing it
   would be suppressing the product), JS-driven scroll, or anything animated by a
   third-party script. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        animation-delay: 0ms !important;
        transition-duration: 0.01ms !important;
        transition-delay: 0ms !important;
        scroll-behavior: auto !important;
    }

    /* Motion that carries status, not decoration — see the note above. */
    .spinner-border,
    .spinner-grow {
        animation-duration: 0.75s !important;
        animation-iteration-count: infinite !important;
    }

    /* The hover/focus lift is a transform, not a transition property, so the
       duration reset above makes it INSTANT rather than absent — a 2px jump on
       hover, which is exactly the kind of abrupt movement the setting is meant to
       remove. Suppress the displacement itself. */
    .btn:hover,
    .btn:focus-visible,
    .card:hover,
    .feature-card:hover,
    .stat-card:hover,
    .admin-card:hover,
    .btn-rmms:hover,
    .footer-links a:hover {
        transform: none !important;
    }

    /* ─── AUDIT-2026-08-08 PC-A11Y-12 (wave 13 repair) — the named exception ───

       app.css:346 is `.nav-auth-btn { transition: all var(--transition-normal)
       !important }`. That shorthand expands to transition-duration at 0,1,0 and so
       outranks the `*` reset at the top of this block; the navbar's Sign In and
       Register buttons kept their full transition under reduced motion. Restating it
       here at the same 0,1,0 specificity wins on source order (this rule is later in
       the file), which is the same tie-break the print block relies on.

       The second selector is the hover lift: `.nav-auth-btn.btn-register:hover`
       declares `transform: translateY(-2px)` at 0,3,0 and therefore beat the
       `.btn:hover { transform: none }` exemption above (0,2,0). It is restated at
       0,3,0 for the same reason.

       This is a NAMED exception list, and that is a known cost — it does not
       self-extend. See the note at the top of the block: a future !important
       transition or a higher-specificity transform has to be added here by hand. */
    .nav-auth-btn {
        transition-duration: 0.01ms !important;
        transition-delay: 0ms !important;
    }

    .nav-auth-btn.btn-register:hover,
    .nav-auth-btn.btn-login:hover {
        transform: none !important;
    }
}

/* ============================================
   DARK-THEME LIST GROUPS
   ============================================ */
/* ─── AUDIT-2026-08-08 PC-A11Y-25 (wave 13) ───

   WAS: this file carried no `.list-group` selector at all (verified at HEAD before this
   block was added), so every list group in the product rendered with vendored
   Bootstrap's LIGHT defaults — `--bs-list-group-bg: #fff`, `--bs-list-group-color:
   #212529`, `--bs-list-group-border-color: rgba(0,0,0,.175)`. The Portal's surfaces are
   dark (`--pnt-dark` / `--pnt-gradient-card`), so those groups painted a white slab
   inside a dark card, and the contextual `.list-group-item-info` variant painted dark
   blue text on a pale blue fill that then sat on the dark card with no relationship to
   anything around it.

   Live call sites, all on dark surfaces: Customer/Dashboard.razor:284,
   Customer/CreateTicket.razor:74,77, Admin/CreateAdminTicket.razor:102,105 and
   Pages/Notifications.razor:37,40 (the notifications list, including its unread
   `.list-group-item-info` rows).

   WHY THE BOOTSTRAP CSS VARIABLES RATHER THAN OVERRIDING THE PROPERTIES: Bootstrap 5's
   list-group rules read `--bs-list-group-*` from the component root, so setting the
   variables reaches the item backgrounds, the borders, the hover/active/disabled states
   and the contextual variants in one place, at the component's own specificity, without
   this file having to restate — and then drift from — each of those rules. Overriding
   `background-color` on `.list-group-item` alone would have left the borders and the
   `:hover` fill on the light values.

   `.list-group-flush` is covered because flush only zeroes the outer border; the item
   background and the divider colour still come from these variables.

   NOT CLAIMED: this is a colour-surface fix. It does not change list-group markup,
   spacing, or focus behaviour, and it does not attempt to restyle the contextual
   variants other than `-info` (that is the only one the Portal uses today — the others
   would need the same treatment if they are adopted). Text contrast is inherited from
   `--pnt-white` / `--pnt-gray-light`, which the rest of this file already uses on the
   same dark fills. */
.list-group {
    --bs-list-group-color: var(--pnt-white, #f8fafc);
    --bs-list-group-bg: transparent;
    --bs-list-group-border-color: rgba(99, 102, 241, 0.18);
    --bs-list-group-action-color: var(--pnt-white, #f8fafc);
    --bs-list-group-action-hover-color: var(--pnt-white, #f8fafc);
    --bs-list-group-action-hover-bg: rgba(99, 102, 241, 0.10);
    --bs-list-group-action-active-color: var(--pnt-white, #f8fafc);
    --bs-list-group-action-active-bg: rgba(99, 102, 241, 0.16);
    --bs-list-group-active-color: var(--pnt-white, #f8fafc);
    --bs-list-group-active-bg: var(--pnt-primary, #6366f1);
    --bs-list-group-active-border-color: var(--pnt-primary, #6366f1);
    --bs-list-group-disabled-color: var(--pnt-gray, #64748b);
    --bs-list-group-disabled-bg: transparent;
}

/* The contextual variant Bootstrap ships as pale-fill/dark-ink (`--bs-info-bg-subtle`
   behind `--bs-info-text-emphasis`). It re-declares the same variables ON THE ITEM, so
   the item's own declaration beats the `.list-group` block above by proximity — which is
   why this needs its own rule rather than being covered by that one. Restated as a
   tinted fill with light ink, the same "needs attention" language the rest of this file
   uses. Written as variables, for the reason given above: the border and the
   hover/active fills follow from them instead of being a second set of overrides that
   can drift. This rule is 0,1,0, the same as Bootstrap's, and wins on source order —
   app.css is linked after bootstrap.min.css (App.razor:12-13). */
.list-group-item-info {
    --bs-list-group-color: var(--pnt-white, #f8fafc);
    --bs-list-group-bg: rgba(99, 102, 241, 0.14);
    --bs-list-group-border-color: rgba(99, 102, 241, 0.28);
    --bs-list-group-action-hover-color: var(--pnt-white, #f8fafc);
    --bs-list-group-action-hover-bg: rgba(99, 102, 241, 0.20);
    --bs-list-group-action-active-color: var(--pnt-white, #f8fafc);
    --bs-list-group-action-active-bg: rgba(99, 102, 241, 0.24);
    --bs-list-group-active-color: var(--pnt-white, #f8fafc);
    --bs-list-group-active-bg: var(--pnt-primary, #6366f1);
    --bs-list-group-active-border-color: var(--pnt-primary, #6366f1);
}
