  /* Self-hosted so the UI never depends on the visitor's OS already having
     Roboto installed, or on a request to fonts.googleapis.com. */
  @font-face {
    font-family: "Roboto";
    src: url("/static/fonts/roboto-regular.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
  }
  @font-face {
    font-family: "Roboto";
    src: url("/static/fonts/roboto-bold.woff2") format("woff2");
    font-weight: 700;
    font-style: normal;
    font-display: swap;
  }
  /* Palette v2 — cool neutral (slate) instead of the previous violet-tinted
     grey, and a single electric-blue accent instead of indigo/purple. This
     is the Linear/Vercel register: a near-black/near-white neutral axis
     that reads as "considered", not "default grey", plus one decisive
     accent hue used sparingly rather than tinting everything toward it.

     NOTE: core layout tokens (--sap-bg, --sap-surface, --sap-text, --sap-border,
     --sap-radius-*, --sap-shadow-*, etc.) are intentionally defined in
     design-system-v3.css which loads after this file and wins the cascade.
     Only tokens unique to this file live here. */
  :root {
    --sap-success: #15803d;
    --sap-success-bg: #dcfce7;
    --sap-warning: #b45309;
    --sap-warning-bg: #fef3c7;
    --sap-danger: #b91c1c;
    --sap-danger-bg: #fee2e2;
    --sap-info: #0e7490;
    --sap-info-bg: #cffafe;
    --sap-success-bg-dark: rgba(34, 197, 94, .12);
    --sap-warning-bg-dark: rgba(245, 158, 11, .12);
    --sap-danger-bg-dark: rgba(239, 68, 68, .12);
    --sap-info-bg-dark: rgba(34, 211, 238, .12);

    --sap-color-brand: var(--bs-primary);
    --sap-bg-subtle: var(--bs-tertiary-bg, rgba(255,255,255,.03));
    --sap-bg-hover: var(--bs-tertiary-bg, rgba(255,255,255,.06));
    --sap-border-subtle: var(--bs-border-color-translucent, rgba(255,255,255,.1));
    /* Sidebar tokens not defined in design-system-v3.css */
    --sap-sidebar-text: #94a3b8;
    --sap-sidebar-text-active: #ffffff;
    --sap-sidebar-active-bg: rgba(37, 99, 235, .15);
    --sap-sidebar-active-border: #2563eb;

    --sap-transition-fast: .12s ease;
    --sap-transition-normal: .2s ease;
    /* Risk semantic palette */
    --risk-low: #15803d;
    --risk-low-bg: #dcfce7;
    --risk-medium: #b45309;
    --risk-medium-bg: #fef3c7;
    --risk-high: #c2410c;
    --risk-high-bg: #ffedd5;
    --risk-critical: #b91c1c;
    --risk-critical-bg: #fee2e2;
  }
  /* Dark-mode overrides: only semantic/status colors and risk tokens that
     design-system-v3.css does not define. Core layout tokens (--sap-bg,
     --sap-surface, --sap-text, --sap-border, etc.) are handled by v3. */
  @media (prefers-color-scheme: dark) {
    :root {
      --sap-success: #22c55e;
      --sap-success-bg: var(--sap-success-bg-dark);
      --sap-warning: #f59e0b;
      --sap-warning-bg: var(--sap-warning-bg-dark);
      --sap-danger: #ef4444;
      --sap-danger-bg: var(--sap-danger-bg-dark);
      --sap-info: #22d3ee;
      --sap-info-bg: var(--sap-info-bg-dark);
      --risk-low: #22c55e;
      --risk-low-bg: rgba(34, 197, 94, .12);
      --risk-medium: #f59e0b;
      --risk-medium-bg: rgba(245, 158, 11, .12);
      --risk-high: #fb923c;
      --risk-high-bg: rgba(251, 146, 60, .12);
      --risk-critical: #ef4444;
      --risk-critical-bg: rgba(239, 68, 68, .12);
    }
  }
  :root[data-theme="dark"] {
    --sap-success: #22c55e;
    --sap-success-bg: var(--sap-success-bg-dark);
    --sap-warning: #f59e0b;
    --sap-warning-bg: var(--sap-warning-bg-dark);
    --sap-danger: #ef4444;
    --sap-danger-bg: var(--sap-danger-bg-dark);
    --sap-info: #22d3ee;
    --sap-info-bg: var(--sap-info-bg-dark);
    --risk-low: #22c55e;
    --risk-low-bg: rgba(34, 197, 94, .12);
    --risk-medium: #f59e0b;
    --risk-medium-bg: rgba(245, 158, 11, .12);
    --risk-high: #fb923c;
    --risk-high-bg: rgba(251, 146, 60, .12);
    --risk-critical: #ef4444;
    --risk-critical-bg: rgba(239, 68, 68, .12);
  }
  :root[data-theme="light"] {
    --sap-bg: #f8fafc;
    --sap-surface: #ffffff;
    --sap-border: #e2e8f0;
    --sap-text: #0f172a;
    --sap-text-muted: #64748b;
    --sap-text-dim: #5a6675;
    --sap-sidebar-bg: #0b1120;
    --sap-sidebar-hover: #1a2436;
    --sap-primary-light: #eff6ff;
    --sap-success: #15803d;
    --sap-success-bg: #dcfce7;
    --sap-warning: #b45309;
    --sap-warning-bg: #fef3c7;
    --sap-danger: #b91c1c;
    --sap-danger-bg: #fee2e2;
    --sap-info: #0e7490;
    --sap-info-bg: #cffafe;
    --risk-low: #15803d;
    --risk-low-bg: #dcfce7;
    --risk-medium: #b45309;
    --risk-medium-bg: #fef3c7;
    --risk-high: #c2410c;
    --risk-high-bg: #ffedd5;
    --risk-critical: #b91c1c;
    --risk-critical-bg: #fee2e2;
    --sap-shadow-sm: 0 1px 2px rgba(15, 23, 42, .06);
    --sap-shadow-md: 0 4px 16px -4px rgba(15, 23, 42, .1), 0 1px 3px rgba(15, 23, 42, .06);
    --sap-shadow-lg: 0 8px 32px -8px rgba(15, 23, 42, .12), 0 2px 8px rgba(15, 23, 42, .08);
  }

  body {
    background-color: var(--sap-bg);
    color: var(--sap-text);
    font-family: -apple-system, "Segoe UI", Inter, Roboto, sans-serif;
    font-variant-numeric: tabular-nums;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
  /* Bootstrap's .text-muted resolves through --bs-secondary-color, which
     (like the table variables above) only theme-switches under
     data-bs-theme="dark" — an attribute this app never sets. Left alone it
     stays pinned to Bootstrap's light-mode gray, which reads as low-contrast
     "washed out" text against a dark --sap-surface/--sap-bg. */
  body .text-muted { color: var(--sap-text-muted); }
  /* Same Bootstrap-dark-mode gap as .text-muted above — .form-text also
     resolves through --bs-secondary-color, unthemed here. */
  .form-text { color: var(--sap-text-muted); }

h1, .h1 { font-size: 1.35rem; font-weight: 650; letter-spacing: -0.015em; line-height: 1.25; margin-bottom: .5rem; }
  h2, .h2 { font-size: 1.1rem; font-weight: 650; letter-spacing: -0.01em; margin-bottom: .4rem; }
  h3, .h3 { font-size: 1.1rem; font-weight: 650; letter-spacing: -0.01em; margin-bottom: .35rem; }
  h4, .h4 { font-size: .95rem; font-weight: 600; margin-bottom: .3rem; }
  h5, .h5 { font-size: .875rem; font-weight: 600; margin-bottom: .25rem; }
  h6, .h6 { font-size: .8rem; font-weight: 600; margin-bottom: .2rem; }
  .sap-page-title { font-size: 1.35rem; font-weight: 650; letter-spacing: -0.01em; line-height: 1.25; }
  .sap-page-subtitle { color: var(--sap-text-muted); font-size: .875rem; margin-top: .1rem; }
  .card-title { margin-bottom: .4rem; }
  /* Section eyebrow — the small uppercase label already used ad hoc above
     several panels ("LÍNEA DE TIEMPO", "MARCADORES DISPONIBLES", etc.) —
     centralized so every one of those gets identical tracking/weight/color
     instead of each page hand-rolling its own inline style="font-size:..."
     (which is how they'd drifted slightly out of sync from each other). */
  .sap-eyebrow { font-size: .68rem; font-weight: 650; text-transform: uppercase; letter-spacing: .06em; color: var(--sap-text-muted); }

  /* Top-risk-users list (improvement_plans/list.html) — a compact row per
     user rather than a full table, since it's a short top-5 with exactly
     one action per row (assign a plan), not a sortable/filterable dataset
     that would justify a <table>. */
  .sap-top-risk-list { display: flex; flex-direction: column; }
  .sap-top-risk-row {
    display: flex; align-items: center; gap: .75rem;
    padding: .55rem .25rem; border-bottom: 1px solid var(--sap-border);
  }
  .sap-top-risk-row:last-child { border-bottom: none; }
  .sap-top-risk-row .sap-badge { min-width: 2.2rem; justify-content: center; font-size: .75rem; }
  .sap-top-risk-name { flex: 1 1 auto; min-width: 0; font-size: .85rem; }

  .sap-shell { display: flex; min-height: 100vh; }
  .sap-skip-link { position:fixed; top:.5rem; left:.5rem; z-index:2000; transform:translateY(-160%); padding:.55rem .8rem; border-radius:var(--sap-radius-sm); background:var(--sap-primary); color:#fff; text-decoration:none; }
  .sap-skip-link:focus { transform:translateY(0); }

  .sap-sidebar {
    width: 220px;
    flex-shrink: 0;
    background: rgba(11, 17, 32, .88);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    color: var(--sap-sidebar-text);
    display: flex;
    flex-direction: column;
    padding: .85rem .7rem;
    border-right: 1px solid rgba(255,255,255,.04);
    /* Without an explicit height + scroll region, a superadmin (who sees
       every nav section: Visión General, Simulaciones, Capacitación,
       Configuración, AND Proveedor/Tenants stacked on top of each other)
       on a short viewport had no way to reach whatever got pushed below
       the fold — .sap-shell has no height cap of its own, so the sidebar
       just grew past the viewport with no scrollbar. Pinning it to the
       viewport height and scrolling internally means the brand row stays
       visible at the top and the user menu at the bottom (both outside
       the scrolling nav, see .sap-sidebar-nav-scroll) no matter how many
       permission-gated sections render in between. */
    height: 100vh;
    position: sticky;
    top: 0;
  }
  .sap-sidebar-nav-scroll {
    overflow-y: auto;
    flex: 1;
    min-height: 0;
    scrollbar-width: thin;
  }
  .sap-sidebar-brand {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: 0 .5rem .9rem;
    color: #fff;
    font-weight: 700;
    font-size: .95rem;
    letter-spacing: -0.01em;
    border-bottom: 1px solid rgba(255,255,255,.06);
    margin-bottom: .4rem;
  }
  .sap-sidebar-brand svg { flex-shrink: 0; }
  .sap-sidebar-brand img { flex-shrink: 0; max-height: 24px; }
.sap-nav-section {
    text-transform: uppercase;
    font-size: .62rem;
    letter-spacing: .08em;
    /* #5a6a80 only cleared 3.41:1 against --sap-sidebar-bg — below WCAG
       AA's 4.5:1 for normal text. #7889a3 clears 5.3:1 on the same
       background while staying a subtle, secondary-label tone. */
    color: #7889a3;
    padding: .6rem .5rem .25rem;
    font-weight: 600;
  }
  .sap-nav-link {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .4rem .55rem;
    border-radius: var(--sap-radius-sm);
    color: var(--sap-sidebar-text);
    text-decoration: none;
    font-size: .82rem;
    font-weight: 500;
    margin-bottom: 1px;
    transition: background-color var(--sap-transition-fast), color var(--sap-transition-fast);
    position: relative;
  }
  .sap-nav-link:hover { background: var(--sap-sidebar-hover); color: #fff; }
  /* Nav links are the sidebar's primary keyboard-navigated element — every
     other interactive control in the shell (.btn, .sap-notif-bell) already
     has its own :focus-visible ring, but this one didn't, leaving keyboard
     users with no visible indicator of which link is focused while
     Tabbing through the menu. */
  .sap-nav-link:focus-visible { outline: 2px solid var(--sap-primary); outline-offset: -2px; background: var(--sap-sidebar-hover); color: #fff; }
  .sap-nav-link.active {
    background: var(--sap-sidebar-active-bg);
    color: #fff;
    box-shadow: inset 2px 0 0 var(--sap-sidebar-active-border);
  }
  .sap-nav-link svg { flex-shrink: 0; opacity: .7; width: 16px; height: 16px; transition: opacity var(--sap-transition-fast); }
  .sap-nav-link:hover svg { opacity: 1; }
  .sap-nav-link.active svg { opacity: 1; }
  .sap-nav-sub { margin-left: 1.7rem; border-left: 1px solid rgba(255,255,255,.06); padding-left: .5rem; }
  .sap-nav-sub .sap-nav-link { font-size: .8rem; padding: .32rem .55rem; }
  .sap-nav-sub .sap-nav-link:hover { background: rgba(255,255,255,.04); }
  .sap-nav-group .sap-nav-link { font-size: .82rem; padding: .4rem .55rem; }
  /* C16: unread clicked/submitted_data count next to "Campañas" — hidden
     (d-none, see the markup) until the polling script has something to
     show, then set as plain text content, never innerHTML. */
  .sap-nav-badge {
    margin-left: auto; background: var(--sap-danger); color: #fff;
    font-size: .72rem; font-weight: 700; line-height: 1;
    padding: .2rem .45rem; border-radius: 999px;
  }

  .sap-sidebar-user {
    margin-top: auto;
    padding-top: .7rem;
    border-top: 1px solid rgba(255,255,255,.06);
    display: flex;
    align-items: center;
    gap: .5rem;
  }
  .sap-avatar {
    width: 28px; height: 28px; border-radius: 50%;
    background: linear-gradient(135deg, var(--sap-primary), var(--sap-primary-dark));
    color: #fff; font-weight: 600; font-size: .72rem;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    cursor: pointer;
  }
  .sap-sidebar-user { position: relative; }
  .sap-sidebar-user-menu { display: none; position: absolute; bottom: calc(100% + 8px); left: 0; background: var(--sap-bg); border: 1px solid var(--sap-border); border-radius: 8px; min-width: 200px; box-shadow: 0 4px 16px rgba(0,0,0,.15); z-index: 60; padding: 4px 0; }
  .sap-sidebar-user-menu.open { display: block; }
  .sap-sidebar-user-menu-item { display: flex; align-items: center; gap: .45rem; padding: 8px 12px; font-size: .8rem; color: var(--sap-text); text-decoration: none; transition: background .12s; cursor: pointer; }
  .sap-sidebar-user-menu-item:hover { background: var(--sap-hover); color: var(--sap-text); }
  .sap-sidebar-user-menu-item svg { flex-shrink: 0; opacity: .6; }
  .sap-sidebar-user-menu-divider { height: 1px; background: var(--sap-border); margin: 4px 0; }
  .sap-sidebar-user-menu-btn { background: none; border: none; padding: 0; font-size: .8rem; color: inherit; display: flex; align-items: center; gap: .45rem; width: 100%; cursor: pointer; }
  .sap-sidebar-user-name { font-size: .78rem; color: #fff; font-weight: 500; line-height: 1.2; }
  .sap-sidebar-user-chevron { flex-shrink: 0; color: rgba(255,255,255,.45); transition: transform .15s; margin-left: auto; }
  .sap-sidebar-user.menu-open .sap-sidebar-user-chevron { transform: rotate(180deg); color: rgba(255,255,255,.75); }
  .sap-sidebar-tools { display:flex; align-items:center; justify-content:center; gap:.4rem; margin-bottom:4px; }
  .sap-notif-bell, .sap-theme-toggle { position: relative; display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; border: none; background: rgba(255,255,255,.08); border-radius: 50%; color: rgba(255,255,255,.65); cursor: pointer; transition: background .15s, color .15s; }
  .sap-notif-bell:hover, .sap-theme-toggle:hover { background: rgba(255,255,255,.14); color: #fff; }
  .sap-notif-bell:focus-visible, .sap-theme-toggle:focus-visible { outline: 2px solid var(--sap-primary); outline-offset: 2px; }
  .sap-theme-toggle svg { width:18px; height:18px; }
  .sap-notif-badge { position: absolute; top: -2px; right: -2px; min-width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; background: var(--sap-primary); color: #fff; font-size: .6rem; font-weight: 700; border-radius: 8px; padding: 0 4px; line-height: 1; }
  .sap-notif-dropdown { position: fixed !important; bottom: 80px !important; left: 12px !important; transform: none !important; width: 340px; max-height: 400px; border-radius: 10px; border: 1px solid var(--sap-border); box-shadow: 0 8px 30px rgba(0,0,0,.18); padding: 0; overflow: hidden; }
  .sap-notif-dropdown-header { padding: .65rem 1rem; font-size: .82rem; font-weight: 650; border-bottom: 1px solid var(--sap-border); }
  .sap-notif-item { display: flex; gap: .6rem; padding: .6rem 1rem; align-items: flex-start; cursor: default; transition: background .12s; }
  .sap-notif-item:hover { background: var(--sap-hover); }
  .sap-notif-item-icon { flex-shrink: 0; width: 24px; height: 24px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: .72rem; font-weight: 700; }
  .sap-notif-item-icon.clicked { background: rgba(231,76,60,.15); color: #e74c3c; }
  .sap-notif-item-icon.submitted_data { background: rgba(231,76,60,.2); color: #c0392b; }
  .sap-notif-item-icon.recurrence_created { background: var(--sap-info-bg); color: var(--sap-info); }
  .sap-notif-item-body { min-width: 0; font-size: .8rem; line-height: 1.35; }
  .sap-notif-item-body strong { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .sap-notif-item-body span { color: var(--sap-text-muted); font-size: .75rem; }
  .sap-notif-dropdown-empty { padding: 1.5rem 1rem; text-align: center; font-size: .82rem; color: var(--sap-text-muted); }
  /* Skeleton loader */
  @keyframes sap-shimmer { 0% { background-position: -200px 0; } 100% { background-position: calc(200px + 100%) 0; } }
  .sap-skeleton-row { display: flex; gap: 1rem; padding: .75rem 1rem; align-items: center; }
  .sap-skeleton-cell { height: 14px; border-radius: 4px; background: linear-gradient(90deg, var(--sap-border) 25%, var(--sap-hover) 50%, var(--sap-border) 75%); background-size: 200px 100%; animation: sap-shimmer 1.4s ease-in-out infinite; }
  .sap-sidebar-user-logout {
    font-size: .7rem;
    color: #7a8aa0;
    background: none;
    border: none;
    padding: .15rem .35rem;
    border-radius: var(--sap-radius-sm);
    transition: color var(--sap-transition-fast), background var(--sap-transition-fast);
  }
  .sap-sidebar-user-logout:hover {
    color: #fff;
    background: rgba(255,255,255,.06);
  }

  /* Subtle entrance animation for authenticated page content — a fast
     30ms delay prevents a "flash of empty content" while the JS/resources
     finish loading, while still being fast enough that navigating between
     pages never feels like a deliberate "and now the content appears"
     transition (which gets old after the first two navigations). */
  /* Keep the page entrance transition opacity-only. A transform on .sap-main
     creates a stacking context and traps any page-defined position:fixed
     Bootstrap modal below the backdrop appended directly to <body>. */
  @keyframes sap-fade-in { from { opacity: .6; } to { opacity: 1; } }
  .sap-main { flex: 1; min-width: 0; padding: 1.25rem 1.5rem; animation: sap-fade-in .2s ease-out both; }
  .sap-main.sap-main-full { padding: 0; }

  /* Real card surface — a defined edge plus a minimal, tight-radius shadow */
  .card {
    background: var(--sap-surface);
    border: 1px solid var(--sap-border);
    border-radius: var(--sap-radius-lg);
    box-shadow: var(--sap-shadow-sm);
    color: var(--sap-text);
    transition: box-shadow var(--sap-transition-normal), border-color var(--sap-transition-normal);
  }
  .card-body { padding: .85rem .9rem; }
  .card-header {
    padding: .65rem .9rem;
    border-bottom: 1px solid var(--sap-border);
    background: transparent;
    font-weight: 600;
  }
  .card-footer { padding: .65rem .9rem; border-top: 1px solid var(--sap-border); background: transparent; }
  .card:not(.modal-content):not(.login-card):not(.sap-toast):hover {
    border-color: var(--sap-border-light);
    box-shadow: var(--sap-shadow-md);
  }
  /* Bootstrap's own dark mode only activates under data-bs-theme="dark",
     an attribute this app never sets (it uses data-theme, driving the
     --sap-* tokens instead) — left alone, --bs-table-bg/--bs-table-color
     stay pinned to Bootstrap's light defaults (white/near-black) even
     when the rest of the page is dark, making every table unreadable.
     Rebinding Bootstrap's table variables to the --sap-* tokens here
     makes .table follow the same theme as everything else. */
  .table {
    --bs-table-bg: var(--sap-surface);
    --bs-table-color: var(--sap-text);
    --bs-table-striped-bg: var(--sap-bg);
    --bs-table-striped-color: var(--sap-text);
    --bs-table-hover-bg: var(--sap-bg-hover, #f1f4f9);
    --bs-table-hover-color: var(--sap-text);
    --bs-table-border-color: var(--sap-border);
    color: var(--sap-text);
    font-size: .85rem;
    margin-bottom: 0;
  }
  .table > :not(caption) > * > * {
    padding: .45rem .6rem;
    border-bottom-color: var(--sap-border);
  }
  .table thead th {
    font-weight: 600;
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--sap-text-dim);
    border-bottom-width: 1px;
    padding-top: .55rem;
    padding-bottom: .55rem;
    background: var(--sap-surface);
  }
  /* Sortable column headers (see the "sortableHeader" partial) — the link
     inherits thead th's own type treatment exactly rather than restating
     it, so a sortable column looks identical to a plain one except for
     the tri-state icon (⇅ unsorted / ▲ / ▼) and a hover/active tint. */
  th.sap-sortable-th { padding: 0; }
  .sap-sortable-th-link {
    display: flex; align-items: center; gap: .3rem;
    padding: .55rem .6rem; color: inherit; text-decoration: none;
    white-space: nowrap; user-select: none;
  }
  .sap-sortable-th-link:hover { color: var(--sap-text); }
  th.sap-sortable-th.active .sap-sortable-th-link { color: var(--sap-primary); }
  .sap-sortable-th-icon { font-size: .65rem; opacity: .5; line-height: 1; }
  th.sap-sortable-th.active .sap-sortable-th-icon { opacity: 1; }
  .table tbody tr {
    transition: background-color var(--sap-transition-fast);
  }
  .table tbody tr:hover {
    background: var(--bs-table-hover-bg);
  }
  .table tbody tr:last-child td {
    border-bottom: none;
  }
  /* Every list page in the app puts its <table> directly inside a .card,
     with no per-page .table-responsive wrapper — on a narrow viewport a
     wide table (Usuarios' role column, Campañas' status+date columns)
     otherwise gets crushed into unreadable, wrapped cells instead of just
     scrolling sideways. Targeting .card > table directly means every
     existing list picks this up with no template changes, and any new
     one gets it automatically too. */
  @media (max-width: 768px) {
    .card > table {
      display: block;
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
    }
    /* display:block alone doesn't force horizontal scroll — table cells
       still wrap their text to fit, so scrollWidth never exceeds
       clientWidth. white-space: nowrap keeps each cell at its natural
       width, which is what actually makes the table wider than the
       viewport and the scrollbar engage. thead/tbody/tr also need
       display:block again here (Bootstrap sets them to table-*
       display values), or wrapping the outer <table> alone leaves an
       inconsistent box model across browsers. */
    .card > table thead,
    .card > table tbody,
    .card > table tr {
      display: table;
      width: 100%;
      table-layout: auto;
    }
    .card > table th,
    .card > table td {
      white-space: nowrap;
    }
  }

  .btn {
    font-weight: 600;
    transition: transform var(--sap-transition-fast), box-shadow var(--sap-transition-fast), background-color var(--sap-transition-fast), border-color var(--sap-transition-fast);
    border-radius: var(--sap-radius-md);
  }
  .btn:active { transform: scale(.97); }
  .btn:focus-visible { outline: 2px solid var(--sap-primary); outline-offset: 2px; }
  /* Bootstrap's own .dropdown-item:focus already highlights the background
     (no outline:none override exists anywhere in this app to have broken
     that), but it doesn't match the ring style every other interactive
     element here uses — added for visual parity, not to fix a missing
     indicator. */
  .dropdown-item:focus-visible { outline: 2px solid var(--sap-primary); outline-offset: -2px; }
  .btn-primary {
    background: var(--sap-primary);
    border-color: var(--sap-primary);
    box-shadow: 0 1px 2px rgba(37,99,235,.2);
  }
  .btn-primary:hover { background: var(--sap-primary-dark); border-color: var(--sap-primary-dark); box-shadow: 0 2px 6px rgba(37,99,235,.3); transform: translateY(-1px); }
  .btn-primary:active { transform: translateY(0) scale(.97); }
  .btn-outline-primary { color: var(--sap-primary); border-color: var(--sap-primary); }
  .btn-outline-primary:hover { background: var(--sap-primary); border-color: var(--sap-primary); box-shadow: 0 1px 3px rgba(37,99,235,.2); }
  .btn-outline-secondary { color: var(--sap-text-muted); border-color: var(--sap-border); }
  .btn-outline-secondary:hover { background: var(--sap-bg); border-color: var(--sap-text-muted); color: var(--sap-text); }

  .sap-badge { display: inline-flex; align-items: center; gap: .25rem; padding: .18rem .5rem; border-radius: 999px; font-size: .7rem; font-weight: 600; line-height: 1.3; }
  .sap-badge-success { background: var(--sap-success-bg); color: var(--sap-success); }
  .sap-badge-warning { background: var(--sap-warning-bg); color: var(--sap-warning); }
  .sap-badge-danger  { background: var(--sap-danger-bg);  color: var(--sap-danger); }
  .sap-badge-info    { background: var(--sap-info-bg);    color: var(--sap-info); }
  .sap-badge-neutral { background: var(--sap-bg); color: var(--sap-text-muted); }
  .sap-badge-recurrence { background: var(--sap-bg-subtle); color: var(--sap-color-brand); border: 1px solid var(--sap-border-subtle); }

  /* Toast notification */
  .sap-toast {
    background: var(--sap-surface); border: 1px solid var(--sap-border);
    border-radius: var(--sap-radius-lg); box-shadow: 0 8px 32px rgba(0,0,0,.12);
    min-width: 320px; max-width: 420px; padding: .5rem; position: relative; overflow: hidden;
  }
  .sap-toast .toast-body { color: var(--sap-text); font-size: .82rem; padding: .5rem .25rem; }
  .sap-toast-progress {
    position: absolute; bottom: 0; left: 0; height: 3px;
    background: var(--sap-primary); border-radius: 0 0 var(--sap-radius-lg) 0;
    transition: width linear;
  }
  .sap-toast-success .sap-toast-progress { background: var(--sap-success); }
  .sap-toast-danger .sap-toast-progress { background: var(--sap-danger); }
  .sap-toast-warning .sap-toast-progress { background: var(--sap-warning); }
  .sap-toast-info .sap-toast-progress { background: var(--sap-info); }
  .sap-toast-icon {
    width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center; margin-left: .25rem;
  }
  .sap-toast-icon svg { width: 18px; height: 18px; }
  .sap-toast-success .sap-toast-icon { background: var(--sap-success-bg); color: var(--sap-success); }
  .sap-toast-danger  .sap-toast-icon { background: var(--sap-danger-bg);  color: var(--sap-danger); }
  .sap-toast-warning .sap-toast-icon { background: var(--sap-warning-bg); color: var(--sap-warning); }
  .sap-toast-info    .sap-toast-icon { background: var(--sap-info-bg);    color: var(--sap-info); }

  .sap-confirm-modal-icon {
    width: 42px; height: 42px; border-radius: 50%; flex-shrink: 0;
    background: var(--sap-danger-bg); color: var(--sap-danger);
    display: flex; align-items: center; justify-content: center;
  }
  .sap-confirm-modal-icon svg { width: 22px; height: 22px; }

  /* Typed-confirmation variant — for irreversible deletes (see
     data-confirm-typed on <form>). The user must retype the exact phrase
     shown (usually the record's own name) before the accept button
     enables, the same "type the repo name to delete it" pattern GitHub
     uses, so a reflexive double-click can't trigger data loss the way a
     single OK-styled modal can. */
  .sap-confirm-modal-typed-phrase {
    font-weight: 700; color: var(--sap-text); word-break: break-word;
  }
  #sap-confirm-modal-typed-input.is-match { border-color: var(--sap-success); }

  /* Real-time form validation feedback (see the "form[novalidate]" script)
     — reuses Bootstrap's is-invalid/is-valid/invalid-feedback classes and
     display rules, only recoloring to the app's own status tokens instead
     of Bootstrap's default red/green. */
  .form-control.is-invalid, .form-select.is-invalid { border-color: var(--sap-danger); }
  .form-control.is-valid, .form-select.is-valid { border-color: var(--sap-success); }
  .sap-invalid-feedback { color: var(--sap-danger); font-size: .8rem; margin-top: .25rem; }

  .risk-score-high { color: var(--risk-high); font-weight: 600; }
  .risk-score-medium { color: var(--risk-medium); font-weight: 600; }
  .risk-score-low { color: var(--risk-low); font-weight: 600; }

  /* Risk 4-level classes — replaces old 3-level risk-score-* with the full
     palette: low/medium/high/critical. Keep old 3-level classes for
     backward compat during migration. */
  .risk-low { color: var(--risk-low); }
  .risk-medium { color: var(--risk-medium); }
  .risk-high { color: var(--risk-high); }
  .risk-critical { color: var(--risk-critical); }
  .risk-bg-low { background: var(--risk-low-bg); color: var(--risk-low); }
  .risk-bg-medium { background: var(--risk-medium-bg); color: var(--risk-medium); }
  .risk-bg-high { background: var(--risk-high-bg); color: var(--risk-high); }
  .risk-bg-critical { background: var(--risk-critical-bg); color: var(--risk-critical); }
  /* Chart card pattern — shared by org dashboard + admin dashboard */
  .chart-card { padding: 1.25rem 1.4rem; }
  .chart-card-title { font-size: .78rem; font-weight: 650; text-transform: uppercase; letter-spacing: .04em; color: var(--sap-text-muted); margin-bottom: 1rem; }
  .chart-legend { display: flex; gap: .85rem; flex-wrap: wrap; font-size: .76rem; color: var(--sap-text); margin-top: .6rem; }
  .chart-legend-item { display: flex; align-items: center; gap: .35rem; }
  .chart-legend-swatch { display: inline-block; width: 8px; height: 8px; border-radius: 2px; }
  .sap-chart-skeleton { background: linear-gradient(90deg, var(--sap-bg) 25%, var(--sap-border) 50%, var(--sap-bg) 75%); background-size: 200% 100%; animation: sap-shimmer 1.2s ease infinite; border-radius: 4px; }
  @keyframes sap-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

  /* Risk distribution progress bar for admin dashboard */
  .risk-progress-bar { display: flex; height: 24px; border-radius: 6px; overflow: hidden; gap: 2px; }
  .risk-progress-bar .seg { transition: flex .3s ease; }
  .risk-progress-bar .seg.low { background: var(--risk-low); }
  .risk-progress-bar .seg.medium { background: var(--risk-medium); }
  .risk-progress-bar .seg.high { background: var(--risk-high); }
  .risk-progress-bar .seg.critical { background: var(--risk-critical); }
  .risk-legend { display: flex; gap: .7rem; flex-wrap: wrap; margin-top: .5rem; font-size: .78rem; }
  .risk-legend-item { display: flex; align-items: center; gap: .3rem; }
  .risk-legend-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
  .risk-legend-count { color: var(--sap-text-muted); font-weight: 600; margin-left: .2rem; }
  .admin-dash-section { margin-bottom: 1.75rem; }

  /* Segmented progress bar for risk distribution — each segment uses its
     own color and the whole bar sums to 100%. */
  .risk-progress { display: flex; height: 8px; border-radius: 999px; overflow: hidden; background: var(--sap-border); }
  .risk-progress-segment { height: 100%; transition: width .3s ease; }
  .risk-progress-segment.low { background: var(--risk-low); }
  .risk-progress-segment.medium { background: var(--risk-medium); }
  .risk-progress-segment.high { background: var(--risk-high); }
  .risk-progress-segment.critical { background: var(--risk-critical); }

  /* KPI card pattern — deliberately compact: a metric tile's job is to be
     scanned in a row of 4-6 at a glance, not to anchor the page the way a
     hero number would. The previous version (1rem/1.1rem padding, 1.65rem
     value) read as oversized next to the density of the rest of the app
     (tables, forms) once there were more than 2-3 of these in a row — this
     trims padding/font-size/gap across the board while keeping the same
     left-border colour cue and hover lift. */
  .kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .6rem; margin-bottom: 1.1rem; }
  .kpi-card {
    min-width: 0;
    overflow: hidden;
    padding: .6rem .8rem;
    border-left-width: 3px;
    border-radius: var(--sap-radius-md);
    box-shadow: var(--sap-shadow-sm);
    transition: transform var(--sap-transition-normal), box-shadow var(--sap-transition-normal);
  }
  .kpi-card:hover { transform: translateY(-1px); box-shadow: var(--sap-shadow-md); }
  .kpi-label { font-size: .65rem; font-weight: 650; text-transform: uppercase; letter-spacing: .05em; color: var(--sap-text-muted); margin-bottom: .15rem; line-height: 1.3; overflow-wrap: anywhere; }
  .kpi-value { max-width: 100%; font-size: clamp(1.05rem, 1.6vw, 1.3rem); font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; line-height: 1.15; overflow-wrap: anywhere; }
  .kpi-meta { margin-top: .2rem; color: var(--sap-text-muted); font-size: .72rem; line-height: 1.25; overflow-wrap: anywhere; }
  .kpi-value.neutral { color: var(--sap-text); }
  .kpi-value.good { color: var(--sap-success); }
  .kpi-value.bad { color: var(--sap-danger); }
  .kpi-value.warning { color: var(--sap-warning); }
  /* .neutral previously got no left-border accent at all (fell through to
     the plain --sap-border edge below) — every KPI card now gets a color
     cue matching its value's semantics, including neutral ones (brand
     primary, since a plain count like "Total" isn't good or bad news). */
  .kpi-card:has(.kpi-value.neutral) { border-left-color: var(--sap-primary); }
  .kpi-card:has(.kpi-value.good) { border-left-color: var(--sap-success); }
  .kpi-card:has(.kpi-value.bad) { border-left-color: var(--sap-danger); }
  .kpi-card:has(.kpi-value.warning) { border-left-color: var(--sap-warning); }
  .kpi-card:not(:hover) { border-color: var(--sap-border); }

  /* Executive summary card — same compactness pass as .kpi-card above,
     scaled down proportionally rather than dropping the icon (which earns
     its space here, unlike .kpi-card's plain left-border accent). */
  .exec-summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: .6rem; margin-bottom: 1.1rem; }
  .exec-card { padding: .65rem .85rem; border-radius: var(--sap-radius-md); }
  .exec-card-head { display: flex; align-items: center; gap: .45rem; margin-bottom: .4rem; }
  .exec-card-icon {
    width: 24px; height: 24px; border-radius: var(--sap-radius-sm); flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
  }
  .exec-card-icon svg { width: 13px; height: 13px; }
  .exec-card-icon.neutral { background: var(--sap-primary-light); color: var(--sap-primary); }
  .exec-card-icon.good { background: var(--sap-success-bg); color: var(--sap-success); }
  .exec-card-icon.warn { background: var(--sap-warning-bg); color: var(--sap-warning); }
  .exec-card-icon.bad { background: var(--sap-danger-bg); color: var(--sap-danger); }
  .exec-card-label { font-size: .68rem; font-weight: 600; color: var(--sap-text-muted); }
  .exec-card-value { font-size: 1.35rem; font-weight: 700; letter-spacing: -0.01em; line-height: 1.1; color: var(--sap-text); }
  .exec-card-trend { display: flex; align-items: center; gap: .3rem; margin-top: .3rem; font-size: .72rem; }
  .exec-card-trend svg { width: 12px; height: 12px; }
  .exec-card-trend.good { color: var(--sap-success); }
  .exec-card-trend.bad { color: var(--sap-danger); }
  .exec-card-trend.neutral { color: var(--sap-text-muted); }
  .exec-card-trend-label { color: var(--sap-text-muted); font-weight: 500; }

  /* Reusable "what does this mean" affordance — a small (?) glyph next to
     any KPI/chart label. Native title= + Bootstrap's Tooltip (see the
     global data-bs-toggle="tooltip" init below) rather than a custom
     popover, since it's keyboard/screen-reader accessible for free and
     needs no extra JS per usage site. */
  .info-tip {
    display: inline-flex; align-items: center; justify-content: center;
    width: 15px; height: 15px; border-radius: 50%; cursor: help;
    color: var(--sap-text-muted); border: 1px solid var(--sap-border);
    font-size: .68rem; font-weight: 700; line-height: 1;
  }
  /* A plain <span> (even with tabindex="0", added at each usage site)
     doesn't reliably get a default focus ring the way a real <button>
     would — Bootstrap's Tooltip already shows on focus for free, but
     without this the focus itself would be invisible to a keyboard user. */
  .info-tip:focus-visible { outline: 2px solid var(--sap-primary); outline-offset: 1px; }

  /* Wizard stepper — a horizontal, numbered progress indicator shared by
     any multi-step flow (today: campaign creation). Segments are joined
     by a connecting line drawn as each .sap-step's own ::after pseudo
     rather than a separate element, so the count of steps stays purely
     data-driven (one .sap-step per step, no extra "connector" markup to
     keep in sync). Three states: done (checkmark, clickable — going back
     must not lose data already entered in later steps, so this is a plain
     same-page show/hide via JS, never a re-fetch), active (filled, brand
     color), upcoming (outline only, unclickable — visible for orientation
     but a user can't skip ahead of validation). */
  .sap-stepper { display: flex; list-style: none; margin: 0 0 1.25rem; padding: 0; }
  .sap-step { flex: 1; position: relative; text-align: center; }
  /* Completed steps are keyboard-reachable (role="button", tabindex kept
     in sync with maxCompletedStep by the wizard's own JS) — an <li> has no
     native focus ring the way a real <button> would, so one is added
     explicitly. Steps ahead of the current one are tabindex="-1"
     (unreachable) and never receive this outline. */
  .sap-step:focus-visible { outline: 2px solid var(--sap-primary); outline-offset: 2px; border-radius: var(--sap-radius-sm); }
  .sap-step:not(:last-child)::after {
    content: ""; position: absolute; top: 13px; left: calc(50% + 20px); right: calc(-50% + 20px);
    height: 2px; background: var(--sap-border);
  }
  .sap-step.done:not(:last-child)::after { background: var(--sap-primary); }
  .sap-step-circle {
    width: 28px; height: 28px; border-radius: 50%; margin: 0 auto .35rem;
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: .8rem; position: relative; z-index: 1;
    border: 2px solid var(--sap-border); color: var(--sap-text-muted); background: var(--sap-surface);
  }
  .sap-step.active .sap-step-circle { border-color: var(--sap-primary); background: var(--sap-primary); color: #fff; }
  .sap-step.done .sap-step-circle { border-color: var(--sap-primary); color: var(--sap-primary); cursor: pointer; }
  .sap-step.done .sap-step-circle svg { width: 16px; height: 16px; }
  .sap-step-label { font-size: .8rem; font-weight: 600; color: var(--sap-text-muted); }
  .sap-step.active .sap-step-label { color: var(--sap-text); }
  .sap-step.done .sap-step-label { color: var(--sap-text); cursor: pointer; }
  /* Collapses to "Paso N de 4" + the current step's name below ~700px —
     a full 4-wide stepper has no room to stay legible on a phone. */
  .sap-stepper-mobile { display: none; font-size: .8rem; font-weight: 600; color: var(--sap-text-muted); margin-bottom: .85rem; }
  @media (max-width: 768px) {
    .sap-stepper { display: none; }
    .sap-stepper-mobile { display: block; }
  }

  /* Scenario-picker card grid (wizard step 1) — a selectable card variant
     distinct from a plain .card: a visible left-edge color bar (per
     template category, falling back to the neutral border color for
     uncategorized templates) and a selected state that must read clearly
     even in dark mode, hence the box-shadow ring rather than relying on
     border-color alone against a dark surface. */
  .sap-scenario-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: .65rem; }
  .sap-scenario-card {
    position: relative; padding: .75rem .85rem .75rem 1rem; cursor: pointer;
    border-left-width: 3px; transition: box-shadow .12s ease;
  }
  .sap-scenario-card:hover { box-shadow: var(--sap-shadow-md); }
  .sap-scenario-card.selected { box-shadow: 0 0 0 2px var(--sap-primary); }
  .sap-scenario-card-check {
    position: absolute; top: .65rem; right: .7rem; width: 18px; height: 18px; border-radius: 50%;
    background: var(--sap-primary); color: #fff; display: none; align-items: center; justify-content: center;
  }
  .sap-scenario-card.selected .sap-scenario-card-check { display: flex; }
  .sap-scenario-card.selected .sap-scenario-card-check svg { width: 10px; height: 10px; }
  .sap-scenario-card-title { font-weight: 600; margin-bottom: .2rem; padding-right: 1.4rem; font-size: .875rem; }
  .sap-scenario-card-desc {
    font-size: .78rem; color: var(--sap-text-muted); margin-bottom: .5rem;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .sap-scenario-card-links { display: flex; gap: .7rem; margin-bottom: .5rem; font-size: .76rem; }
  .sap-scenario-card-links a { color: var(--sap-primary); }
  /* Thumbnail: the iframe is rendered at a fixed "real" width (600px, a
     typical email body width) then scaled down with a CSS transform to fit
     the card — scaling the box rather than fighting the email's own fixed
     widths/absolute font-sizes is what makes an arbitrary HTML email look
     like a proportional miniature instead of an illegibly reflowed sliver.
     sandbox="" (no allow-scripts, no allow-same-origin — see
     landing_page_handlers.go's own preview sandboxing for the same
     reasoning) neutralizes any script an operator's template might
     contain; pointer-events:none means the thumbnail is purely visual and
     clicks fall through to the card underneath. */
  .sap-scenario-card-thumb {
    height: 84px; margin: -.75rem -.85rem .55rem -1rem; overflow: hidden;
    background: var(--sap-bg); border-bottom: 1px solid var(--sap-border);
    border-radius: var(--sap-radius-lg) var(--sap-radius-lg) 0 0;
  }
  .sap-scenario-card-thumb-frame {
    width: 600px; height: 400px; border: 0; pointer-events: none;
    transform: scale(.34); transform-origin: top left;
  }
  .sap-scenario-card-footer { display: flex; align-items: center; gap: .5rem; }
  .sap-scenario-card-footer .scenario-select-btn { flex: 1; }
  .sap-scenario-card-icon-links { display: flex; gap: .3rem; flex-shrink: 0; }
  .sap-scenario-card-icon-links a {
    display: flex; align-items: center; justify-content: center; width: 28px; height: 28px;
    border-radius: var(--sap-radius-sm); color: var(--sap-text-muted);
    transition: background var(--sap-transition-fast), color var(--sap-transition-fast);
  }
  .sap-scenario-card-icon-links a svg { width: 15px; height: 15px; }
  .sap-scenario-card-icon-links a:hover { background: var(--sap-bg); color: var(--sap-primary); }

  /* Thin progress track under a percentage KPI value (campaign detail's
     click/submit/report rates) — the number alone makes 8% and 38% look
     like the same kind of fact; a filled bar gives the magnitude a second,
     faster-to-scan channel below it. Track width is fixed so bars for
     different metrics stay visually comparable at a glance. */
  .sap-kpi-bar { height: 5px; border-radius: 999px; background: var(--sap-border); margin-top: .5rem; overflow: hidden; }
  .sap-kpi-bar-fill { height: 100%; border-radius: 999px; }
  .sap-kpi-bar-fill.good { background: var(--sap-success); }
  .sap-kpi-bar-fill.bad { background: var(--sap-danger); }
  .sap-kpi-bar-fill.neutral { background: var(--sap-primary); }

  /* Small status-color dot preceding a KPI count, e.g. "3 running" —
     lets a status-broken-down KPI grid (campaigns list) carry the same
     color meaning as the status-pill badges without repeating the whole
     pill (too visually heavy at kpi-value's large font size). */
  .sap-dot { display: inline-block; width: .55rem; height: .55rem; border-radius: 50%; margin-right: .5rem; vertical-align: middle; }
  .sap-dot-running, .sap-dot-scheduled { background: var(--sap-info); }
  .sap-dot-paused { background: var(--sap-warning); }
  .sap-dot-completed { background: var(--sap-success); }
  .sap-dot-cancelled { background: var(--sap-text-muted); }

  /* A table row's primary link (the record name) — inherits the body text
     color instead of Bootstrap's default link blue, since the row itself
     is already the click target and a loud blue name competes with the
     status pill/action buttons beside it. Underline only appears on
     hover/focus, so it still reads as interactive. */
  .sap-row-link { color: var(--sap-text); font-weight: 500; text-decoration: none; }
  .sap-row-link:hover, .sap-row-link:focus { color: var(--sap-primary); text-decoration: underline; }

  /* Icon + label buttons in the campaign detail page's action bar
     (Pausar/Reanudar/Cancelar/Duplicar/Eliminar) — a plain text button row
     reads as a wall of same-weight red/grey outline buttons; a small icon
     ahead of each label gives the eye a faster way to tell them apart at a
     glance before reading the word. */
  .sap-campaign-actions .btn { display: inline-flex; align-items: center; gap: .35rem; }
  .sap-campaign-actions .btn svg { width: 14px; height: 14px; flex-shrink: 0; }
    .sap-summary-sticky { position: sticky; top: 0; z-index: 5; background: var(--sap-bg); transition: box-shadow .2s ease; margin: 0 -1.5rem; padding: 0 1.5rem; }
  .sap-summary-sticky.is-stuck { box-shadow: 0 2px 8px rgba(0,0,0,.08); }
  /* Onboarding tour */
  .sap-tour-backdrop { position: fixed; inset: 0; z-index: 1060; background: rgba(0,0,0,.35); }
  .sap-tour-card { position: fixed; z-index: 1070; background: var(--sap-bg); border: 1px solid var(--sap-border); border-radius: 10px; padding: 1.2rem 1.4rem; max-width: 380px; box-shadow: 0 8px 30px rgba(0,0,0,.18); }
  .sap-tour-card h3 { font-size: 1rem; font-weight: 650; margin-bottom: .4rem; }
  .sap-tour-card p { font-size: .85rem; color: var(--sap-text-muted); margin-bottom: 0; line-height: 1.45; }
  .sap-tour-card .sap-tour-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 1rem; gap: .5rem; }
  .sap-tour-card .sap-tour-progress { font-size: .78rem; color: var(--sap-text-muted); }
  .sap-tour-highlight { position: relative; z-index: 1065; }

  /* Friendly empty-state hint used across list/table views */
  /* .empty-hint: a compact one-line notice inside a KPI card/chart/table
     that already has its own heading (dashboard panels) — an icon here
     would be disproportionate to the space. .empty-state (below, via the
     "emptyState" template) is for a whole card/page with nothing else in
     it, where the icon carries real visual weight. Not an inconsistency:
     two different contexts, on purpose. */
  .empty-hint { color: var(--sap-text-muted); font-size: .82rem; padding: .5rem 0; }
  .empty-state { padding: 2.5rem 1rem; text-align: center; color: var(--sap-text-muted); }
  .empty-state-icon {
    width: 36px; height: 36px; margin: 0 auto .85rem;
    display: flex; align-items: center; justify-content: center;
    color: var(--sap-text-muted); opacity: .5;
  }
  .empty-state-icon svg { width: 36px; height: 36px; }
  .empty-state-message { font-size: .92rem; color: var(--sap-text); margin-bottom: .75rem; }

  /* Status pill mapping — campaigns, targets, enrollments, users, quizzes */
  .status-pill-running, .status-pill-in_progress, .status-pill-sent, .status-pill-opened, .status-pill-html { background: var(--sap-info-bg); color: var(--sap-info); font-weight: 600; }
  .status-pill-completed, .status-pill-active, .status-pill-reported, .status-pill-approved, .status-pill-achieved { background: var(--sap-success-bg); color: var(--sap-success); font-weight: 600; }
  .status-pill-draft, .status-pill-not_started, .status-pill-pending, .status-pill-invited, .status-pill-abandoned { background: var(--sap-bg); color: var(--sap-text-muted); font-weight: 500; }
  .status-pill-cancelled, .status-pill-scheduled, .status-pill-paused, .status-pill-overdue, .status-pill-send_failed, .status-pill-video, .status-pill-scorm { background: var(--sap-warning-bg); color: var(--sap-warning); font-weight: 600; }
  .status-pill-clicked, .status-pill-submitted_data { background: var(--sap-danger-bg); color: var(--sap-danger); font-weight: 600; }

  /* Bootstrap's default form-label weight (400) reads thin next to the
     rest of the system's now-heavier type — 600 puts a field's label at
     the same visual weight as a button or nav item, so a form doesn't
     feel like the one under-designed corner of the page. */
  .form-label { font-weight: 600; font-size: .82rem; color: var(--sap-text); margin-bottom: .25rem; }
  /* Same Bootstrap-dark-mode gap as .text-muted/.form-text above:
     .form-control/.form-select resolve their background/color through
     --bs-body-bg/--bs-body-color, which only theme-switches under
     data-bs-theme="dark" (never set here) — left alone every text input,
     select, and textarea stays pinned to a plain white background
     regardless of the app's own dark mode. */
  .form-control, .form-select {
    font-size: .875rem;
    padding: .4rem .65rem;
    background-color: var(--sap-surface);
    color: var(--sap-text);
    border-color: var(--sap-border);
    transition: border-color var(--sap-transition-fast), box-shadow var(--sap-transition-fast);
    border-radius: var(--sap-radius-md);
  }
  .form-control:disabled, .form-select:disabled { background-color: var(--sap-bg); color: var(--sap-text-muted); }
  .form-control:focus, .form-select:focus {
    border-color: var(--sap-primary);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .1);
  }
  .input-group > .form-control, .input-group > .form-select { padding: .4rem .65rem; }

  /* Monospace treatment for code-like fields (HTML bodies, DKIM keys, tracking markers) */
  .mono-field { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .85rem; }

  /* Reveal control for single-line secrets (SMTP password) — see auth/login.html
     for the password input this pattern originated in. */
  .secret-field { position: relative; }
  .secret-field .form-control { padding-right: 4.5rem; }
  .secret-toggle {
    position: absolute; right: .5rem; top: 50%; transform: translateY(-50%);
    border: none; background: none; color: var(--sap-text-muted);
    font-size: .78rem; padding: .25rem .5rem; cursor: pointer;
  }
  .secret-toggle:hover { color: var(--sap-text); }

  /* Reveal control for multi-line secrets (DKIM private key) — blurred at
     rest since a textarea has no native password mode; revealing removes
     the blur rather than swapping input type. */
  .secret-textarea.masked { filter: blur(4px); user-select: none; }

  /* Form sectioning — subtle divider + spacing between logical field groups */
  .form-section + .form-section { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--sap-border); }
  .form-section-title { font-size: .72rem; font-weight: 650; text-transform: uppercase; letter-spacing: .04em; color: var(--sap-text-muted); margin-bottom: .6rem; }

  /* Leaderboard rank styling */
  .rank-badge {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; border-radius: 50%;
    font-size: .8rem; font-weight: 700;
    background: var(--sap-border); color: var(--sap-text-muted);
  }
  .rank-badge.rank-top { background: var(--sap-primary-light); color: var(--sap-primary); }
  .rank-row-top td { font-weight: 600; }

  /* Campaign detail's activity timeline (creation/scheduling/lifecycle
     milestones + aggregated per-minute event counts) — a genuine
     horizontal timeline: one shared rail line running left-to-right,
     oldest event first, each entry a dot on the rail with its label/time
     card below it. The rail scrolls horizontally on its own axis (not the
     page) when there are more entries than fit, exactly like a wide table
     would — "Ver historial completo" reveals the earlier entries into
     that same scrollable strip rather than expanding vertically. */
  .sap-timeline-scroll { overflow-x: auto; padding-bottom: .15rem; }
  .sap-timeline {
    list-style: none; margin: 0; padding: 1rem 0 0; position: relative;
    display: flex; align-items: flex-start; min-width: max-content;
  }
  .sap-timeline::before {
    content: ""; position: absolute; left: 0; right: 0; top: 1rem;
    height: 2px; background: var(--sap-border);
  }
  .sap-timeline-item {
    position: relative; display: flex; flex-direction: column; align-items: center;
    flex: 0 0 auto; width: 140px; text-align: center; padding-top: .8rem;
  }
  .sap-timeline-dot {
    position: absolute; top: 0; left: 50%; transform: translate(-50%, -50%);
    width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0;
    background: var(--sap-text-muted); box-shadow: 0 0 0 2px var(--sap-surface);
  }
  .sap-timeline-created .sap-timeline-dot,
  .sap-timeline-scheduled .sap-timeline-dot { background: var(--sap-info); }
  .sap-timeline-launched .sap-timeline-dot,
  .sap-timeline-resumed .sap-timeline-dot,
  .sap-timeline-sent .sap-timeline-dot { background: var(--sap-primary); }
  .sap-timeline-paused .sap-timeline-dot { background: var(--sap-warning); }
  .sap-timeline-cancelled .sap-timeline-dot,
  .sap-timeline-send_failed .sap-timeline-dot { background: var(--sap-danger); }
  .sap-timeline-opened .sap-timeline-dot,
  .sap-timeline-clicked .sap-timeline-dot,
  .sap-timeline-submitted_data .sap-timeline-dot { background: var(--sap-danger); }
  .sap-timeline-reported .sap-timeline-dot { background: var(--sap-success); }
  .sap-timeline-label { font-size: .78rem; font-weight: 600; line-height: 1.2; padding: 0 .3rem; }
  .sap-timeline-time { font-size: .68rem; color: var(--sap-text-muted); font-variant-numeric: tabular-nums; margin-top: .15rem; }

  .campaign-target-row > td { vertical-align: middle; padding-top: .7rem; padding-bottom: .7rem; }
  .campaign-overview-card { background: linear-gradient(135deg, var(--sap-surface), var(--sap-bg)); border: 1px solid var(--sap-border); border-radius: var(--sap-radius-lg); padding: 1rem 1.15rem; box-shadow: var(--sap-shadow-sm); }
  .campaign-overview-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: .9rem; }
  .campaign-overview-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: .55rem; }
  .campaign-overview-metric { min-width: 0; padding: .65rem .7rem; border-left: 3px solid var(--sap-info); background: var(--sap-surface); border-radius: var(--sap-radius-sm); }
  .campaign-overview-metric span { display: block; color: var(--sap-text-muted); font-size: .68rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .campaign-overview-metric strong { display: block; margin-top: .12rem; color: var(--sap-text); font-size: 1.15rem; font-variant-numeric: tabular-nums; }
  .campaign-overview-metric.risk { border-color: var(--sap-danger); }
  .campaign-overview-metric.good { border-color: var(--sap-success); }
  .campaign-overview-metric.captured { border-color: var(--sap-warning); }
  .campaign-funnel-inline { display: flex; flex-wrap: wrap; gap: .3rem; max-width: 330px; }
  .campaign-funnel-inline span { padding: .2rem .4rem; border-radius: var(--sap-radius-sm); background: var(--sap-bg); color: var(--sap-text-muted); font-size: .68rem; white-space: nowrap; }
  .campaign-funnel-inline span.risk { background: var(--sap-danger-bg); color: var(--sap-danger); }
  .campaign-funnel-inline span.good { background: var(--sap-success-bg); color: var(--sap-success); }
  .campaign-funnel-inline b { color: inherit; font-variant-numeric: tabular-nums; }
  .campaign-rate-stack { display: flex; flex-wrap: wrap; gap: .25rem; max-width: 220px; }
  .campaign-rate { padding: .18rem .38rem; border-radius: 999px; background: var(--sap-bg); color: var(--sap-text-muted); font-size: .67rem; white-space: nowrap; }
  .campaign-rate.risk { background: var(--sap-danger-bg); color: var(--sap-danger); }
  .campaign-rate.good { background: var(--sap-success-bg); color: var(--sap-success); }
  .campaign-rate.captured { background: var(--sap-warning-bg); color: var(--sap-warning); }
  .campaign-target-detail > td { padding: 0 !important; border-top: 0; background: var(--sap-bg); }
  .target-detail-panel { padding: 1.1rem 1.25rem 1.25rem; border-top: 1px solid var(--sap-border); border-bottom: 1px solid var(--sap-border); }
  .target-detail-heading { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; }
  .target-detail-card { background: var(--sap-surface); border: 1px solid var(--sap-border); border-radius: var(--sap-radius-lg); padding: 1rem; box-shadow: var(--sap-shadow-sm); }
  .target-detail-card-title { color: var(--sap-text); font-size: .76rem; font-weight: 700; letter-spacing: .045em; text-transform: uppercase; margin-bottom: .8rem; }
  .target-detail-help { color: var(--sap-text-muted); font-size: .72rem; line-height: 1.4; margin: -.35rem 0 .8rem; }
  .target-event-timeline { list-style: none; margin: 0; padding: 0; }
  .target-event { position: relative; display: grid; grid-template-columns: 16px minmax(0, 1fr); gap: .7rem; padding-bottom: 1rem; }
  .target-event:last-child { padding-bottom: 0; }
  .target-event:not(:last-child)::before { content: ""; position: absolute; left: 7px; top: 14px; bottom: 0; width: 2px; background: var(--sap-border); }
  .target-event-marker { width: 10px; height: 10px; margin: 3px 0 0 3px; border-radius: 50%; background: var(--sap-info); box-shadow: 0 0 0 3px var(--sap-info-bg); z-index: 1; }
  .target-event-clicked .target-event-marker,
  .target-event-submitted_data .target-event-marker { background: var(--sap-danger); box-shadow: 0 0 0 3px var(--sap-danger-bg); }
  .target-event-reported .target-event-marker { background: var(--sap-success); box-shadow: 0 0 0 3px var(--sap-success-bg); }
  .target-event-send_failed .target-event-marker { background: var(--sap-warning); box-shadow: 0 0 0 3px var(--sap-warning-bg); }
  .target-event-header { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; font-size: .8rem; }
  .target-event-time { color: var(--sap-text-muted); font-size: .7rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .target-event-meta { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .35rem; }
  .target-meta-chip { display: inline-flex; max-width: 100%; padding: .18rem .45rem; border-radius: 999px; background: var(--sap-bg); color: var(--sap-text-muted); font-size: .68rem; line-height: 1.3; }
  .target-event-context { display: flex; flex-wrap: wrap; gap: .35rem .7rem; color: var(--sap-text-muted); font-size: .68rem; margin-top: .35rem; }
  .target-event-origin { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
  .captured-data-card { border: 1px solid var(--sap-border); border-radius: calc(var(--sap-radius-lg) - 2px); overflow: hidden; margin-bottom: .55rem; }
  .captured-data-card:last-child { margin-bottom: 0; }
  .captured-data-card summary { display: flex; align-items: center; justify-content: space-between; gap: .75rem; padding: .65rem .75rem; cursor: pointer; background: var(--sap-bg); color: var(--sap-text); font-size: .74rem; font-weight: 600; list-style: none; }
  .captured-data-card summary::-webkit-details-marker { display: none; }
  .captured-data-card[open] summary { border-bottom: 1px solid var(--sap-border); }
  .captured-data-toggle { color: var(--sap-primary); font-size: .68rem; white-space: nowrap; }
  .captured-data-card table { font-size: .72rem; }
  .captured-data-card th { padding-left: .75rem; color: var(--sap-text-muted); font-weight: 600; }
  .captured-data-card td { padding-right: .75rem; }
  .captured-data-value { max-width: 230px; overflow-wrap: anywhere; }
  @media (max-width: 767.98px) {
    .campaign-overview-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .target-detail-panel { padding: .9rem; }
    .target-event-header { align-items: flex-start; flex-direction: column; gap: .15rem; }
    .target-event-time { white-space: normal; }
  }

  /* Placeholder/marker reference panel (email template + landing page
     editors) — lives in the column that would otherwise be dead
     whitespace beside a narrower form, and doubles as an insert control
     (click copies the literal {{.Marker}} into the editor or clipboard,
     see each page's own extra_scripts), not just a read-only glossary. */
  .sap-placeholder-panel { position: sticky; top: .9rem; }
  .sap-placeholder-item {
    display: block; width: 100%; text-align: left; border: 1px solid var(--sap-border);
    background: var(--sap-bg); border-radius: var(--sap-radius-sm); padding: .4rem .6rem; margin-bottom: .4rem;
    cursor: pointer; transition: border-color .12s ease;
  }
  .sap-placeholder-item:last-of-type { margin-bottom: 0; }
  .sap-placeholder-item:hover { border-color: var(--sap-primary); }
  .sap-placeholder-item code { font-size: .82rem; font-weight: 700; color: var(--sap-primary); }
  .sap-placeholder-desc { display: block; font-size: .76rem; color: var(--sap-text-muted); margin-top: .2rem; line-height: 1.35; }
  /* Warns when a template's saved body contains a {{.Xxx}}-shaped marker
     this system doesn't substitute (see FindUnknownPlaceholders) — usually
     leftover GoPhish syntax from migrated content. Styled off the shared
     warning tokens rather than Bootstrap's .text-warning-emphasis, which
     (like .text-muted before it) isn't dark-mode aware here. */
  .sap-placeholder-warning { border-color: var(--sap-warning); }
  .sap-placeholder-warning h2 { color: var(--sap-warning); }
  .sap-placeholder-warning code { color: var(--sap-warning); font-weight: 700; }

  /* Email template editor (Quill) — the toolbar carries a lot more
     buttons than the old 5-group version, so it gets to wrap onto a
     second row instead of ellipsis-truncating or scrolling
     horizontally; the body gets a visible border since Quill's own
     snow theme border only wraps the toolbar+body as one box, and
     that box needs a taller default height than a plain textarea.
     Custom toolbar buttons (table/fullscreen/undo/redo) reuse Quill's
     own .ql-formats/button sizing so they don't look bolted-on. */
  .sap-email-editor { border: 1px solid var(--sap-border); border-radius: var(--sap-radius-md); overflow: hidden; }
  .sap-email-editor .ql-toolbar.ql-snow { border: none; border-bottom: 1px solid var(--sap-border); background: var(--sap-bg); flex-wrap: wrap; }
  .sap-email-editor .ql-container.ql-snow { border: none; }
  .sap-email-editor #template-wysiwyg-editor { background: var(--sap-surface); min-height: 320px; }
  .sap-email-editor .ql-picker.ql-font .ql-picker-label[data-value="georgia"]::before,
  .sap-email-editor .ql-picker.ql-font .ql-picker-item[data-value="georgia"]::before { content: "Georgia"; font-family: Georgia, serif; }
  .sap-email-editor .ql-picker.ql-font .ql-picker-label[data-value="arial"]::before,
  .sap-email-editor .ql-picker.ql-font .ql-picker-item[data-value="arial"]::before { content: "Arial"; font-family: Arial, sans-serif; }
  .sap-email-editor .ql-picker.ql-font .ql-picker-label[data-value="helvetica"]::before,
  .sap-email-editor .ql-picker.ql-font .ql-picker-item[data-value="helvetica"]::before { content: "Helvetica"; font-family: Helvetica, sans-serif; }
  .sap-email-editor .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
  .sap-email-editor .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before { content: "Serif"; font-family: serif; }
  .sap-email-editor .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
  .sap-email-editor .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { content: "Monospace"; font-family: monospace; }
  .sap-email-editor .ql-picker.ql-size .ql-picker-label::before,
  .sap-email-editor .ql-picker.ql-size .ql-picker-item::before { content: attr(data-value); }

  /* Fullscreen toggle — an email HTML body is long enough that the
     usual ~320px editor box makes formatting the far end of a long
     template tedious; expanding it to the viewport (position: fixed,
     no layout reflow of the rest of the page) fixes that without
     needing a dedicated fullscreen-editing route. */
  .sap-email-editor-fullscreen {
    position: fixed; inset: 1rem; z-index: 1060;
    display: flex; flex-direction: column;
    box-shadow: 0 20px 50px -12px rgba(0,0,0,.5);
  }
  .sap-email-editor-fullscreen #template-wysiwyg-editor { flex: 1; min-height: 0; }

  .sap-preview-toolbar { padding: .5rem .75rem; background: var(--sap-bg); border: 1px solid var(--sap-border); border-bottom: none; border-radius: 8px 8px 0 0; }
  .sap-email-preview-frame { width: 100%; height: 420px; border: 1px solid var(--sap-border); border-radius: 0 0 8px 8px; background: #fff; }

  /* Landing page HTML editor (CodeMirror) — unlike the email template
     editor above, landing pages routinely carry a full cloned document
     (forms, inline <style>/<script>, real login-page markup) that a
     rich-text editor can't represent at all, so this is a real code
     editor (syntax highlighting, line numbers, tag auto-closing) with a
     live-rendered iframe alongside it rather than a WYSIWYG canvas. */
  .sap-code-editor { border: 1px solid var(--sap-border); border-radius: var(--sap-radius-md); overflow: hidden; }
  .sap-code-editor-toolbar {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    padding: .5rem .75rem; background: var(--sap-bg); border-bottom: 1px solid var(--sap-border);
  }
  .sap-code-editor-toolbar .sap-eyebrow { margin: 0; }
  .sap-code-editor-body { display: flex; min-height: 420px; }
  .sap-code-editor-body.sap-code-editor-split .sap-code-editor-pane { width: 50%; }
  .sap-code-editor-body.sap-code-editor-split .sap-code-editor-preview-pane { border-left: 1px solid var(--sap-border); }
  .sap-code-editor-pane { width: 100%; min-width: 0; display: flex; flex-direction: column; }
  .sap-code-editor-preview-pane { display: none; flex-direction: column; }
  .sap-code-editor-body.sap-code-editor-split .sap-code-editor-preview-pane,
  .sap-code-editor-body.sap-code-editor-preview-only .sap-code-editor-preview-pane { display: flex; }
  .sap-code-editor-body.sap-code-editor-preview-only .sap-code-editor-pane:not(.sap-code-editor-preview-pane) { display: none; }
  .sap-code-editor .CodeMirror {
    height: 100%; min-height: 420px; font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
    font-size: .84rem; line-height: 1.5; background: var(--sap-surface); color: var(--sap-text);
  }
  .sap-code-editor .CodeMirror-gutters { background: var(--sap-bg); border-right: 1px solid var(--sap-border); }
  .sap-code-editor .CodeMirror-linenumber { color: var(--sap-text-muted); }
  .sap-code-editor .CodeMirror-cursor { border-left-color: var(--sap-text); }
  :root[data-theme="dark"] .sap-code-editor .cm-s-default.CodeMirror { color: var(--sap-text); }
  .sap-code-editor-preview-frame { width: 100%; flex: 1; min-height: 420px; border: none; background: #fff; }
  .sap-code-editor-status { font-size: .76rem; color: var(--sap-text-muted); padding: .35rem .75rem; border-top: 1px solid var(--sap-border); background: var(--sap-bg); }
  .sap-code-editor-fullscreen {
    position: fixed; inset: 1rem; z-index: 1060; box-shadow: 0 20px 50px -12px rgba(0,0,0,.5);
  }

  /* Custom scrollbar — subtle, fits the slate theme. Only affects
     Webkit/Chromium (the vast majority of desktop users); Firefox and
     Safari use their own native thin scrollbars which are fine as-is. */
  ::-webkit-scrollbar { width: 6px; height: 6px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: var(--sap-border); border-radius: 999px; }
  ::-webkit-scrollbar-thumb:hover { background: var(--sap-text-muted); }
  /* Overlay scrollbars are already thin enough; this just tints them
     to match the app palette instead of the OS default grey. */
  * { scrollbar-width: thin; scrollbar-color: var(--sap-border) transparent; }

  /* Modal refinements */
  .modal-content { border: none; border-radius: var(--sap-radius-xl); box-shadow: 0 20px 60px rgba(0,0,0,.15); }
  .modal-header { padding: .85rem .9rem .4rem; border-bottom: none; }
  .modal-body { padding: .4rem .9rem .75rem; }
  .modal-footer { padding: .6rem .9rem .85rem; border-top: none; }
  .modal-title { font-size: 1rem; font-weight: 650; }
  .modal-backdrop.show { opacity: .4; }
  .btn-close { font-size: .75rem; }

  /* Alert refinements */
  .alert { padding: .6rem .8rem; font-size: .85rem; margin-bottom: .75rem; border: none; border-radius: var(--sap-radius-md); }
  .alert-success { background: var(--sap-success-bg); color: var(--sap-success); }
  .alert-danger { background: var(--sap-danger-bg); color: var(--sap-danger); }
  .alert-warning { background: var(--sap-warning-bg); color: var(--sap-warning); }
  .alert-info { background: var(--sap-info-bg); color: var(--sap-info); }

  /* Breadcrumb refinements */
  .sap-breadcrumb { display: flex; align-items: center; gap: .35rem; font-size: .78rem; color: var(--sap-text-muted); margin-bottom: .4rem; }
  .sap-breadcrumb a { color: var(--sap-text-muted); text-decoration: none; transition: color var(--sap-transition-fast); }
  .sap-breadcrumb a:hover { color: var(--sap-primary); }
  .sap-breadcrumb-sep { color: var(--sap-border); user-select: none; margin: 0 .1rem; }
  .sap-breadcrumb-current { color: var(--sap-text); font-weight: 600; }
  .sap-code-editor-fullscreen .sap-code-editor-body { flex: 1; min-height: 0; }
  .sap-code-editor-fullscreen.sap-code-editor { display: flex; flex-direction: column; }

  /* Compact Bootstrap overrides */
  .pagination { margin-bottom: 0; }
  .page-link { padding: .3rem .55rem; font-size: .82rem; }
  .btn-sm { font-size: .8rem; padding: .28rem .55rem; }
  .btn-xs { font-size: .74rem; padding: .2rem .45rem; }
  .mb-3 { margin-bottom: .75rem !important; }
  .mt-3 { margin-top: .75rem !important; }
  .gap-2 { gap: .4rem !important; }
  .form-check { min-height: 0; margin-bottom: 0; }
  .form-check-input { margin-top: 0; }
  hr { margin: .75rem 0; opacity: .4; }

  /* Auth page shared styles (login, forgot/reset password, 2FA, errors) */
  body.login-page {
    background: linear-gradient(160deg, #0b1120 0%, #162033 40%, #1a3a4a 100%);
    min-height: 100vh;
    position: relative;
  }
  body.login-page::before {
    content: '';
    position: fixed;
    inset: 0;
    background:
      radial-gradient(ellipse 80% 50% at 20% 30%, rgba(37,99,235,.10) 0%, transparent 100%),
      radial-gradient(ellipse 60% 40% at 80% 70%, rgba(8,145,178,.08) 0%, transparent 100%);
    pointer-events: none;
  }
  .auth-wrap { min-height: calc(100vh - 2rem); display: flex; align-items: center; justify-content: center; }
  .auth-brand { display: flex; align-items: center; justify-content: center; gap: .6rem; margin-bottom: 1.25rem; }
  .auth-brand-icon {
    /* brandMark (the shared brand isotype partial defined at the top of
       this file) already paints its own shield-shaped background in
       --sap-primary — this
       wrapper no longer needs a background of its own (that previously
       doubled up with the old icon's transparent shield outline, which
       relied on the wrapper's own fill to read as a filled badge). */
    width: 38px; height: 38px;
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  }
  .auth-brand-text { color: #f1f5f9; font-weight: 700; font-size: 1.1rem; letter-spacing: -0.01em; }
  .auth-brand-text small { display: block; color: #94a3b8; font-weight: 400; font-size: .7rem; letter-spacing: .02em; }
  .auth-card {
    background: #0b1120;
    border: 1px solid rgba(255,255,255,.1);
    border-radius: var(--sap-radius-lg, 8px);
    box-shadow: 0 20px 45px -12px rgba(0,0,0,.45);
    animation: auth-fade-in .35s ease-out both;
  }
  @keyframes auth-fade-in {
    from { opacity: 0; transform: translateY(12px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .auth-card .card-body { padding: 1.5rem; }
  .auth-card .form-label { font-size: .82rem; color: #e2e8f0; }
  .auth-card .form-control { padding: .45rem .7rem; border-color: rgba(255,255,255,.1); background: rgba(255,255,255,.04); color: #f1f5f9; }
  .auth-card .form-control::placeholder { color: #6a7a90; }
  .auth-card .form-control:focus { border-color: var(--sap-primary); background: rgba(255,255,255,.06); box-shadow: 0 0 0 .2rem rgba(37,99,235,.15); }
  .auth-card .btn-primary { padding: .5rem; }
  .auth-footer { text-align: center; color: #94a3b8; font-size: .76rem; margin-top: 1rem; }

  /* Password reveal toggle shared across auth pages */
  .password-field { position: relative; }
  .password-toggle {
    position: absolute; right: .5rem; top: 50%; transform: translateY(-50%);
    border: none; background: none; color: #6a7a90; font-size: .8rem; padding: .25rem .5rem; cursor: pointer;
  }
  .password-toggle:hover { color: #f1f5f9; }

  /* Error pages (403/404/500), shared by public and authenticated shells. */
  .error-wrap { min-height: calc(100vh - 2rem); display: flex; align-items: center; justify-content: center; }
  .error-panel { width: 100%; max-width: 480px; }
  .error-card { border-radius: var(--sap-radius-xl, 16px); text-align: center; animation: auth-fade-in .35s ease-out both; }
  .error-card .card-body { padding: 2.5rem 2rem; }
  .error-code { font-size: 4.5rem; font-weight: 700; color: var(--sap-text); line-height: 1; margin-bottom: .5rem; letter-spacing: -0.02em; }
  .error-icon { width: 48px; height: 48px; margin: 0 auto 1rem; color: var(--sap-text-muted); opacity: .6; }
  .error-icon svg { width: 100%; height: 100%; }
  .error-title { font-weight: 650; color: var(--sap-text); margin-bottom: .5rem; }
  .error-message { color: var(--sap-text-muted); margin-bottom: 1.75rem; }
  .error-actions { display: flex; justify-content: center; gap: .75rem; flex-wrap: wrap; }
  .error-actions .btn { min-width: 140px; }
  .login-page .error-card { border-color: rgba(255,255,255,.08); box-shadow: 0 20px 45px -12px rgba(0,0,0,.45); }
  .login-page .error-code, .login-page .error-title { color: #f1f5f9; }
  .login-page .error-message { color: #94a3b8; }

  /* Sticky table headers */
  .table thead { position: sticky; top: 0; z-index: 2; }
  .table thead th { background: var(--sap-surface); border-bottom-width: 2px; }
  /* Loading spinner on submit buttons — add .btn-loading to any <button>
     and its content hides while a CSS-only spinner shows. */
  .btn-loading { position: relative; pointer-events: none; }
  .btn-loading::after {
    content: ''; position: absolute; inset: 0; margin: auto;
    width: 16px; height: 16px; border: 2px solid transparent;
    border-top-color: currentColor; border-radius: 50%;
    animation: sap-spin .5s linear infinite;
  }
  @keyframes sap-spin { to { transform: rotate(360deg); } }
  .btn-loading .btn-text { visibility: hidden; }

  /* Chart placeholder — a shimmer/pulse skeleton for canvas elements that
     are populated asynchronously by Chart.js. Add .sap-chart-placeholder to
     the <canvas> and the pulse class is automatically removed once Chart.js
     renders (by the chart init script). */
  .sap-chart-skeleton {
    background: linear-gradient(90deg, var(--sap-surface) 25%, var(--sap-bg) 50%, var(--sap-surface) 75%);
    background-size: 200% 100%;
    animation: sap-shimmer 1.5s ease-in-out infinite;
    border-radius: var(--sap-radius-md, 6px);
    min-height: 180px;
  }
  @keyframes sap-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

  /* Mobile topbar: hidden on desktop, shown below 900px in place of the
     always-visible sidebar brand row. */
  .sap-mobile-topbar { display: none; }
  .sap-mobile-menu-btn {
    border: none; background: none; color: #fff; padding: .4rem;
    display: flex; align-items: center; justify-content: center;
  }
  .sap-sidebar-backdrop { display: none; }

  @media (max-width: 900px) {
    .sap-shell { display: block; }
    .sap-mobile-topbar {
      display: flex; align-items: center; justify-content: space-between;
      background: var(--sap-sidebar-bg); color: #fff;
      padding: .75rem 1rem; position: sticky; top: 0; z-index: 40;
    }
    .sap-mobile-topbar .sap-sidebar-brand { padding: 0; }
    .sap-sidebar {
      position: fixed; inset: 0 25% 0 0; z-index: 50;
      width: auto; flex-direction: column; align-items: stretch; flex-wrap: nowrap;
      transform: translateX(-100%); transition: transform .2s ease-out;
      will-change: transform;
      overflow-y: auto;
    }
    .sap-shell.sap-nav-open .sap-sidebar { transform: translateX(0); }
    .sap-shell.sap-nav-open .sap-sidebar-backdrop {
      display: block; position: fixed; inset: 0; z-index: 45;
      background: rgba(0,0,0,.4);
      transition: opacity .2s ease-out;
    }
    .sap-sidebar .sap-sidebar-brand { display: none; }
    .sap-main { padding: 1.25rem 1rem; }
  }

  /* Shared operational views: action-first cards, readable evidence and SLA state. */
  .page-hero { display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap; }
  .attention-score { display:flex; align-items:baseline; gap:.5rem; padding:.7rem 1rem; border:1px solid color-mix(in srgb,var(--sap-warning) 35%,var(--sap-border)); border-radius:var(--sap-radius); background:color-mix(in srgb,var(--sap-warning) 8%,var(--sap-surface)); }
  .attention-score strong { font-size:1.65rem; color:var(--sap-warning); }
  .attention-score span { color:var(--sap-text-muted); font-size:.85rem; }
  .ops-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:1rem; }
  .ops-card { display:flex; flex-direction:row; align-items:center; gap:.8rem; padding:1rem; color:var(--sap-text); text-decoration:none; transition:transform var(--sap-transition-fast),border-color var(--sap-transition-fast),box-shadow var(--sap-transition-fast); }
  .ops-card:hover { transform:translateY(-2px); border-color:var(--sap-primary); box-shadow:var(--sap-shadow-md); }
  .ops-card > div { display:flex; flex-direction:column; min-width:0; }
  .ops-card strong { font-size:1.55rem; line-height:1; }
  .ops-card span:not(.ops-icon) { font-weight:650; }
  .ops-card small { color:var(--sap-text-muted); margin-top:.2rem; }
  .ops-icon { width:2.5rem; height:2.5rem; display:grid; place-items:center; flex:0 0 auto; border-radius:50%; font-size:1.1rem; font-weight:800; }
  .ops-icon.warning { color:var(--sap-warning); background:color-mix(in srgb,var(--sap-warning) 14%,transparent); }
  .ops-icon.danger { color:var(--sap-danger); background:color-mix(in srgb,var(--sap-danger) 12%,transparent); }
  .card-header-clean,.card-heading { display:flex; justify-content:space-between; align-items:center; gap:1rem; padding:1rem; border-bottom:1px solid var(--sap-border); }
  .card-heading { padding:0 0 .9rem; margin-bottom:.9rem; }
  .card-header-clean p { margin:0; color:var(--sap-text-muted); font-size:.85rem; }
  .operation-list { display:flex; flex-direction:column; }
  .operation-row { display:flex; align-items:center; gap:.75rem; min-width:0; padding:.85rem 1rem; color:var(--sap-text); text-decoration:none; border-bottom:1px solid var(--sap-border); }
  .operation-row:last-child { border-bottom:0; }
  .operation-row:hover { background:var(--sap-surface-hover); }
  .operation-main { display:flex; flex:1; flex-direction:column; min-width:0; }
  .operation-main strong,.operation-main small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .operation-main small { color:var(--sap-text-muted); }
  .risk-orb { width:2.35rem; height:2.35rem; display:grid; place-items:center; flex:0 0 auto; border-radius:50%; font-size:.78rem; font-weight:800; border:1px solid currentColor; }
  .risk-low { color:var(--risk-low); background:color-mix(in srgb,var(--risk-low) 9%,transparent); }
  .risk-medium { color:var(--risk-medium); background:color-mix(in srgb,var(--risk-medium) 9%,transparent); }
  .risk-critical { color:var(--risk-critical); background:color-mix(in srgb,var(--risk-critical) 9%,transparent); }
  .status-chip,.count-pill { display:inline-flex; align-items:center; width:max-content; padding:.25rem .55rem; border-radius:999px; font-size:.72rem; font-weight:700; white-space:nowrap; background:var(--sap-surface-subtle); color:var(--sap-text-muted); border:1px solid var(--sap-border); }
  .status-new,.status-scheduled,.class-pending { color:var(--sap-info); border-color:color-mix(in srgb,var(--sap-info) 40%,var(--sap-border)); }
  .status-analyzing,.status-running,.class-suspicious { color:var(--sap-warning); border-color:color-mix(in srgb,var(--sap-warning) 40%,var(--sap-border)); }
  .status-resolved,.status-completed,.class-safe,.class-simulation { color:var(--sap-success); border-color:color-mix(in srgb,var(--sap-success) 40%,var(--sap-border)); }
  .class-malicious { color:var(--sap-danger); border-color:color-mix(in srgb,var(--sap-danger) 40%,var(--sap-border)); }
  .indicator-list { display:flex; flex-direction:column; gap:.65rem; }
  .indicator-item { display:flex; align-items:center; gap:.75rem; padding:.7rem; border:1px solid var(--sap-border); border-radius:var(--sap-radius-sm); }
  .indicator-item > div { min-width:0; display:flex; flex-direction:column; }
  .indicator-item code,.attachment-item code { color:var(--sap-text-muted); overflow-wrap:anywhere; }
  .attachment-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.75rem; }
  .attachment-item { display:flex; flex-direction:column; gap:.25rem; padding:.8rem; border:1px solid var(--sap-border); border-radius:var(--sap-radius-sm); min-width:0; }
  .attachment-item span { color:var(--sap-text-muted); font-size:.8rem; }
  .attachment-item code { font-size:.65rem; }
  .header-list > div { padding:.55rem 0; border-bottom:1px solid var(--sap-border); }
  .header-list > div:last-child { border-bottom:0; }
  .header-list dt { font-size:.72rem; color:var(--sap-text-muted); text-transform:uppercase; letter-spacing:.04em; }
  .header-list dd { margin:0; font-size:.85rem; }
  :where(a,button,input,select,textarea):focus-visible { outline:3px solid color-mix(in srgb,var(--sap-primary) 55%,transparent); outline-offset:2px; }
  @media (max-width:1100px) { .ops-grid { grid-template-columns:repeat(2,minmax(0,1fr)); } }
  @media (max-width:575px) { .ops-grid,.attachment-grid { grid-template-columns:1fr; } .operation-row { align-items:flex-start; } .page-hero .status-chip { white-space:normal; } }
  @media (prefers-reduced-motion:reduce) { *,*::before,*::after { scroll-behavior:auto!important; animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important; } }

  /* Icon-only collapsed sidebar — toggled by .sap-shell.sap-nav-collapsed
     (persisted in localStorage, see the script below) so a medium-width
     screen (a laptop with a side panel open, a tablet in landscape) can
     reclaim horizontal space without losing the sidebar's nav entirely to
     the mobile drawer pattern, which requires an extra tap just to see
     what's available. Only active above the 900px mobile-drawer
     breakpoint — collapsing AND the drawer at once would fight each other. */
  .sap-nav-collapse-btn {
    display: flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; margin-left: auto; flex-shrink: 0;
    border: none; background: rgba(255,255,255,.06); color: rgba(255,255,255,.6);
    border-radius: var(--sap-radius-sm); cursor: pointer;
    transition: background var(--sap-transition-fast), color var(--sap-transition-fast);
  }
  .sap-nav-collapse-btn:hover { background: rgba(255,255,255,.12); color: #fff; }
  .sap-nav-collapse-btn:focus-visible { outline: 2px solid var(--sap-primary); outline-offset: 1px; }
  .sap-nav-collapse-btn svg { transition: transform var(--sap-transition-normal); }
  @media (min-width: 901px) {
    .sap-shell.sap-nav-collapsed .sap-sidebar { width: 60px; padding-left: .4rem; padding-right: .4rem; }
    .sap-shell.sap-nav-collapsed .sap-sidebar-tools { flex-direction: column; }
    .sap-shell.sap-nav-collapsed .sap-sidebar-brand,
    .sap-shell.sap-nav-collapsed .sap-nav-link { justify-content: center; }
    .sap-shell.sap-nav-collapsed .sap-nav-link span,
    .sap-shell.sap-nav-collapsed .sap-nav-badge,
    .sap-shell.sap-nav-collapsed .sap-nav-section,
    .sap-shell.sap-nav-collapsed #sap-user-name-area,
    .sap-shell.sap-nav-collapsed .sap-tenant-switcher,
    .sap-shell.sap-nav-collapsed .sap-sidebar-brand span { display: none; }
    .sap-shell.sap-nav-collapsed .sap-nav-sub { margin-left: 0; border-left: none; padding-left: 0; }
    .sap-shell.sap-nav-collapsed .sap-nav-link { gap: 0; }
    .sap-shell.sap-nav-collapsed .sap-nav-collapse-btn svg { transform: rotate(180deg); }
    .sap-shell.sap-nav-collapsed .sap-sidebar-brand { justify-content: center; padding-left: 0; padding-right: 0; }
  }
