html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ════════════════════════════════════════════════════════════════
   Light theme (first pass)
   The whole portal is driven by the design tokens defined on .shell
   (see DashboardLayout.razor.css). Flipping those tokens under a
   [data-theme=light] scope re-skins every page and the admin kit,
   which inherit them. Applied by js/theme.js + the no-flash bootstrap
   in App.razor. A few pages carry hard-coded colours that this pass
   can't reach via tokens alone; those are patched below.
   ════════════════════════════════════════════════════════════════ */

html[data-theme="light"] .shell {
    --bg:        #F4F5F7;
    --bg-side:   #FFFFFF;
    --surface:   #FFFFFF;
    --surface-2: #EDF0F3;
    --map-land:  #BAC3CC;
    --line:      rgba(16, 24, 32, 0.13);
    --line-soft: rgba(16, 24, 32, 0.07);
    /* Dark ink rather than an inverted white — see the --hair note in site/layout.css. */
    --hair:      rgba(16, 24, 32, 0.24);
    --text:      #171A1D;
    --muted:     #566069;
    --faint:     #838C94;
    --accent:      #0E9E8E;
    --accent-2:    #0B7C70;
    --accent-soft: rgba(14, 158, 142, 0.12);
    --accent-line: rgba(14, 158, 142, 0.40);

    --good: #0E9E8E;
    --warn: #B4770E;
    --bad:  #D14343;
    --crit: #7A4FD1;

    /* No background override needed. site/layout.css sets .shell to a flat var(--bg), and --bg is
       re-pointed above, so the light shell picks up #F4F5F7 on its own. This block used to re-tune
       the teal corner wash for light mode; the wash is gone from both themes. */
}

/* No page-title override here: .content .page-head h1 is var(--text) in site/layout.css, and --text
   is flipped to near-black by the block above, so the heading is the plain ink in both themes. It
   used to be tinted the darkened brand cyan, which is the one heading the accent still owned. */

/* SOC dashboard "goofy mode" (Riley's corner) bouncing pucks + corner counter. These live here, NOT
   in the scoped SocDashboard.razor.css, because the elements are created in JavaScript at runtime and
   so never receive Blazor's component scope attribute — scoped rules wouldn't match them. Their size,
   colour, position (transform) and font-size are set inline by js/dashboard.js; the rest is here. */
.dvd-puck {
    position: absolute; top: 0; left: 0; border-radius: 50%; overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    box-sizing: border-box; will-change: transform; box-shadow: 0 2px 10px rgba(0, 0, 0, .55);
    /* Grab & fling: the pointer belongs to the puck (touch-action stops touch-drag scrolling the page). */
    cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none;
}
.dvd-puck img { width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.dvd-puck.is-initials { color: #fff; font-weight: 700; font-family: var(--font-display, "Space Grotesk", sans-serif); }
.dvd-puck.is-drag { cursor: grabbing; box-shadow: 0 6px 20px rgba(0, 0, 0, .7); }
.dvd-corners {
    position: absolute; bottom: 8px; right: 12px; z-index: 3; pointer-events: auto; cursor: help;
    font-size: 11px; font-weight: 600; letter-spacing: .03em; color: #ffd54f; text-shadow: 0 1px 2px #000;
}
.dvd-corners:hover { text-decoration: underline; }

/* Corner-hit blast (created in js/dashboard.js _explode). The wrapper is a zero-size anchor at the hit
   point; ::before is the central flash, and each .dvd-spark flies out along its own --dx/--dy vector.
   Everything self-removes after the animations finish; pointer-events stay off so it can't be grabbed. */
.dvd-boom { position: absolute; z-index: 4; width: 0; height: 0; pointer-events: none; }
.dvd-boom::before {
    content: ""; position: absolute; left: -15px; top: -15px; width: 30px; height: 30px; border-radius: 50%;
    background: radial-gradient(circle, rgba(255, 213, 79, .95), rgba(255, 143, 0, 0) 70%);
    animation: dvd-boom-flash .45s ease-out forwards;
}
.dvd-spark {
    position: absolute; left: -3px; top: -3px; width: 6px; height: 6px; border-radius: 50%;
    background: currentColor; box-shadow: 0 0 6px currentColor;
    animation: dvd-spark-fly .6s cubic-bezier(.15, .6, .3, 1) forwards;
}
@keyframes dvd-boom-flash { 0% { transform: scale(.2); opacity: 1; } 100% { transform: scale(2.4); opacity: 0; } }
@keyframes dvd-spark-fly {
    0%   { transform: translate(0, 0) scale(1); opacity: 1; }
    100% { transform: translate(var(--dx), var(--dy)) scale(.3); opacity: 0; }
}

/* Corner-hit leaderboard: fades in above the ★ badge on hover, ranking pucks by corners nailed. A full
   SOC roster is taller than the box, so the list scrolls, which means the pointer has to be able to
   leave the badge and land in the list without it closing: it takes pointer events once shown (never
   while hidden, or it would swallow presses meant for the pucks under it) and js/dashboard.js keeps it
   up while the pointer is over either element. overscroll-behavior stops the page taking over the wheel
   once the list bottoms out. */
.dvd-corner-pop {
    position: absolute; bottom: 28px; right: 10px; z-index: 4;
    min-width: 168px; max-width: 220px; max-height: 62%;
    overflow-y: auto; overscroll-behavior: contain;
    padding: 8px; border-radius: 8px; box-sizing: border-box;
    background: rgba(18, 18, 20, .96); border: 1px solid rgba(255, 213, 79, .35);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .6);
    opacity: 0; transform: translateY(4px); pointer-events: none;
    transition: opacity .15s ease, transform .15s ease;
}
.dvd-corner-pop.show { opacity: 1; transform: translateY(0); pointer-events: auto; }
.dvd-corner-pop .cp-head {
    font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: #ffd54f; margin: 0 2px 6px;
}
.dvd-corner-pop .cp-empty { font-size: 11px; color: rgba(255, 255, 255, .65); padding: 2px 2px 4px; }
.dvd-corner-pop .cp-row {
    display: flex; align-items: center; gap: 8px; padding: 3px 4px; border-radius: 5px;
    font-size: 12px; color: #fff;
}
.dvd-corner-pop .cp-row.is-top { background: rgba(255, 213, 79, .16); }
.dvd-corner-pop .cp-swatch {
    flex: none; width: 20px; height: 20px; border-radius: 50%; overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    font-size: 8px; font-weight: 700; color: #fff;
    font-family: var(--font-display, "Space Grotesk", sans-serif);
}
.dvd-corner-pop .cp-swatch img { width: 100%; height: 100%; object-fit: cover; }
.dvd-corner-pop .cp-name {
    flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dvd-corner-pop .cp-count { flex: none; font-weight: 700; color: #ffd54f; }

/* ════════════════════════════════════════════════════════════════
   Cold-start splash (see App.razor + js/theme.js)
   Shown until the Blazor circuit renders. The theme tokens live on
   .shell, which doesn't exist yet at splash time, so this keys off
   html[data-theme] (set pre-paint by the inline script in App.razor)
   using the same base bg/accent values as the dark/light themes.
   ════════════════════════════════════════════════════════════════ */
#app-splash {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #131517;                 /* --bg (dark) */
    opacity: 1;
    transition: opacity .35s ease;
}
html[data-theme="light"] #app-splash { background: #F4F5F7; }   /* --bg (light) */

#app-splash.app-splash--hidden { opacity: 0; pointer-events: none; }

.app-splash__box { display: flex; flex-direction: column; align-items: center; gap: 18px; }
.app-splash__logo { height: 30px; width: auto; opacity: .9; }

.app-splash__spinner {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 3px solid rgba(255, 255, 255, 0.15);
    border-top-color: #2DD4BF;           /* --accent (dark) */
    animation: app-splash-spin .8s linear infinite;
}
html[data-theme="light"] .app-splash__spinner {
    border-color: rgba(16, 24, 32, 0.12);
    border-top-color: #0E9E8E;           /* --accent (light) */
}
@keyframes app-splash-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .app-splash__spinner { animation-duration: 2s; } }