/* ==========================================================================
   ETERNITYARC DESIGN SYSTEM
   ==========================================================================

   Loaded after common.css so it can settle inconsistencies that accumulated there.

   Why this file exists. common.css had grown to ~19,000 lines containing 194 separate
   box-shadow declarations and no elevation tokens at all, 304 distinct hard-coded hex
   colours (which therefore do not respond to the dark theme), and 4 :focus-visible rules
   in total. "Consistent theme", "3d depth" and "accessible" were all blocked on the same
   missing layer, so it is defined once here and inherited everywhere rather than being
   re-decided per screen.

   Depth model — layered, not skeuomorphic. Three surface planes only:

       plane 0   the page
       plane 1   things resting on it (cards, panels, grid rows)
       plane 2   things floating above it (modals, drawers, popovers, menus)

   Each shadow is two layers: a tight one that reads as contact with the surface below, and
   a wide diffuse one that reads as distance from it. One-layer shadows are what make an
   interface look flat-with-a-blur rather than genuinely layered.
   ========================================================================== */

:root {
    /* ── Elevation ──────────────────────────────────────────────────────────
       Shadow colour is expressed as channels so the dark theme can swap in a
       near-black without every alpha needing to be restated. */
    --dnx-shadow-rgb: 15, 23, 42;

    --dnx-elevation-1:
        0 1px 2px rgba(var(--dnx-shadow-rgb), 0.06),
        0 2px 8px rgba(var(--dnx-shadow-rgb), 0.06);

    /* Hover/active. Deliberately close to elevation-1 — a large jump reads as the element
       jumping toward the cursor rather than lifting. */
    --dnx-elevation-2:
        0 2px 4px rgba(var(--dnx-shadow-rgb), 0.08),
        0 8px 20px rgba(var(--dnx-shadow-rgb), 0.10);

    --dnx-elevation-3:
        0 4px 8px rgba(var(--dnx-shadow-rgb), 0.10),
        0 16px 40px rgba(var(--dnx-shadow-rgb), 0.16);

    /* Inset hairline that catches "light" on the top edge of a raised surface. This is what
       sells depth on a flat colour; the shadow alone only sells separation. */
    --dnx-edge-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.6);

    --dnx-radius-sm: 8px;
    --dnx-radius-md: 12px;
    --dnx-radius-lg: 16px;

    /* ── Motion ─────────────────────────────────────────────────────────────
       One curve, three durations. Ad-hoc easing per component is why transitions
       elsewhere in the app feel unrelated to each other. */
    --dnx-ease: cubic-bezier(0.32, 0.72, 0, 1);
    --dnx-duration-fast: 120ms;
    --dnx-duration-base: 180ms;
    --dnx-duration-slow: 280ms;

    --dnx-lift: -2px;

    /* ── Status ─────────────────────────────────────────────────────────────
       Five roles, each a triple: a wash to sit on, a border, and a foreground that
       meets WCAG AA (>= 4.5:1) against that wash. Status was previously drawn with
       Bootstrap's *-subtle utilities in some places and raw hex in others, so the same
       state looked different from screen to screen. */
    --dnx-status-success-fg: #106b45;
    --dnx-status-success-bg: #e7f7ef;
    --dnx-status-success-bd: #9ad9bd;

    --dnx-status-warning-fg: #8a5a00;
    --dnx-status-warning-bg: #fdf3e2;
    --dnx-status-warning-bd: #e8c48a;

    --dnx-status-danger-fg: #a4232b;
    --dnx-status-danger-bg: #fdeceb;
    --dnx-status-danger-bd: #f0b0ae;

    --dnx-status-info-fg: #1b4ea8;
    --dnx-status-info-bg: #eaf1fd;
    --dnx-status-info-bd: #a9c6f2;

    --dnx-status-neutral-fg: #4a5568;
    --dnx-status-neutral-bg: #f1f3f6;
    --dnx-status-neutral-bd: #cfd6e0;

    /* ── Focus ──────────────────────────────────────────────────────────────
       A single visible ring for keyboard users. Offset so it reads as a ring around the
       control rather than a border on it, which is what makes it survive on top of
       already-bordered inputs. */
    --dnx-focus-ring: #2563eb;
    --dnx-focus-ring-contrast: #ffffff;
}

.dark-theme {
    /* Dark surfaces cannot show a shadow the way light ones do, so depth leans harder on
       the shadow being genuinely black and on the edge highlight becoming a faint light
       line rather than a white one. */
    --dnx-shadow-rgb: 0, 0, 0;

    --dnx-elevation-1:
        0 1px 2px rgba(var(--dnx-shadow-rgb), 0.32),
        0 2px 8px rgba(var(--dnx-shadow-rgb), 0.28);

    --dnx-elevation-2:
        0 2px 4px rgba(var(--dnx-shadow-rgb), 0.38),
        0 8px 20px rgba(var(--dnx-shadow-rgb), 0.40);

    --dnx-elevation-3:
        0 4px 8px rgba(var(--dnx-shadow-rgb), 0.45),
        0 16px 40px rgba(var(--dnx-shadow-rgb), 0.55);

    --dnx-edge-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.06);

    /* Washes become translucent role colour rather than another grey, so a warning still
       reads as a warning instead of flattening into the surface. Foregrounds are lightened
       to keep AA contrast against those darker washes. */
    --dnx-status-success-fg: #5fd7a4;
    --dnx-status-success-bg: rgba(45, 190, 140, 0.16);
    --dnx-status-success-bd: rgba(45, 190, 140, 0.42);

    --dnx-status-warning-fg: #f0b657;
    --dnx-status-warning-bg: rgba(239, 159, 39, 0.16);
    --dnx-status-warning-bd: rgba(239, 159, 39, 0.42);

    --dnx-status-danger-fg: #f4837f;
    --dnx-status-danger-bg: rgba(226, 75, 74, 0.16);
    --dnx-status-danger-bd: rgba(226, 75, 74, 0.42);

    --dnx-status-info-fg: #7fb0ff;
    --dnx-status-info-bg: rgba(79, 140, 255, 0.16);
    --dnx-status-info-bd: rgba(79, 140, 255, 0.42);

    --dnx-status-neutral-fg: #b8c1d1;
    --dnx-status-neutral-bg: rgba(255, 255, 255, 0.06);
    --dnx-status-neutral-bd: rgba(255, 255, 255, 0.16);

    --dnx-focus-ring: #7fb0ff;
    --dnx-focus-ring-contrast: #0b1220;
}

/* ==========================================================================
   ACCESSIBILITY — focus
   ==========================================================================
   Applied by element rather than by component class so a control added later is covered
   by default. :focus-visible (not :focus) so a mouse click does not leave a ring behind,
   which is the reason focus styling tends to get deleted in the first place.
   tabindex="-1" is excluded: it means "focusable by script only", never a keyboard tab
   stop, so a ring there is never telling a keyboard user where they are. Blazor's
   <FocusOnNavigate Selector="h1"> adds tabindex="-1" to each page's <h1> and focuses it
   after navigation, which otherwise painted a ring around the hero and sign-in headings. */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible,
[role="button"]:focus-visible,
.form-check-input:focus-visible,
.dnx-select-trigger:focus-visible {
    /* !important is deliberate, and this is the case that justifies it. There are 33
       `outline: none` declarations across the app's stylesheets and component <style> blocks —
       for example `.eternityarc-login-page input:focus` on the sign-in screen. Each was written to
       suppress the browser's default ring for mouse users, and each also removes the only
       indicator a keyboard user has of where they are. They tie this rule on specificity and
       several load later, so without !important the ring silently disappears on exactly the
       screens someone hand-styled. An accessibility floor has to outrank local styling. */
    /* Expressed with outline alone rather than an outline plus a box-shadow "contrast ring".
       box-shadow is contested by ~194 declarations across the app, so a shadow-based ring is
       fragile and hard to prove; outline is not used for decoration anywhere here, so it wins
       cleanly and is what assistive tooling and forced-colors modes key on anyway.
       3px at a 2px offset stays visible against both the light and dark surfaces. */
    outline: 3px solid var(--dnx-focus-ring) !important;
    outline-offset: 2px !important;
    border-radius: var(--dnx-radius-sm);
}

/* Windows High Contrast Mode ignores colour entirely; without this the ring vanishes. */
@media (forced-colors: active) {
    a:focus-visible,
    button:focus-visible,
    input:focus-visible,
    select:focus-visible,
    textarea:focus-visible,
    [tabindex]:not([tabindex="-1"]):focus-visible {
        outline: 3px solid Highlight;
        outline-offset: 2px;
    }
}

/* ── Icon buttons ───────────────────────────────────────────────────────────
   The app has ~98 places where a <div>, <span> or <i> carries an @onclick. Each looks
   clickable to a mouse user and does not exist at all for a keyboard or screen-reader one:
   not focusable, not announced, not activatable by Enter or Space. The fix is a real
   <button>; this class strips the chrome a button brings so swapping the element does not
   change how the control looks. */

/* Both are wrapped in :where(), which carries zero specificity. That matters: this file loads
   after common.css, so a normal class here would beat a component's own styling on every
   property it happens to share — turning an element into a <button> would silently restyle it.
   At zero specificity these only ever replace the browser's own button chrome, which any
   author rule outranks, so the element keeps whatever look it already had. */

/* Pure reset, for elements that already carry their own padding, background and shape. */
:where(.dnx-button-reset) {
    appearance: none;
    background: none;
    border: 0;
    color: inherit;
    font: inherit;
    text-align: inherit;
    cursor: pointer;
}

/* The fuller treatment, for a bare icon that had no styling of its own. */
:where(.dnx-icon-button) {
    appearance: none;
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    color: inherit;
    font: inherit;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Comfortably above the 24px WCAG 2.2 target minimum without visually changing an
       icon that was previously sized by its glyph alone. */
    min-width: 2rem;
    min-height: 2rem;
    border-radius: var(--dnx-radius-sm);
    transition: background-color var(--dnx-duration-fast) var(--dnx-ease),
                color var(--dnx-duration-fast) var(--dnx-ease);
}

:where(.dnx-icon-button:hover) {
    background-color: var(--app-tint-hover-a);
}

:where(.dnx-icon-button:disabled) {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Keyboard users must be able to reach the main content without tabbing the whole nav. */
.dnx-skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 4000;
    padding: 0.65rem 1rem;
    background: var(--dnx-focus-ring);
    color: #fff;
    font-weight: 600;
    border-radius: 0 0 var(--dnx-radius-sm) 0;
}

.dnx-skip-link:focus {
    left: 0;
}

/* ==========================================================================
   ELEVATION — applied to the app's real surfaces
   ==========================================================================
   Targets the existing class names rather than requiring markup changes, so every screen
   already using them inherits the depth model. */

.common-card,
.dnx-card,
.card.common-card {
    box-shadow: var(--dnx-elevation-1), var(--dnx-edge-highlight);
    border-radius: var(--dnx-radius-md);
    transition:
        box-shadow var(--dnx-duration-base) var(--dnx-ease),
        transform var(--dnx-duration-base) var(--dnx-ease),
        border-color var(--dnx-duration-base) var(--dnx-ease);
}

/* Only interactive cards lift. A static panel that rises under the cursor implies a click
   target that isn't there. */
.dnx-card:hover,
.svc-card:hover {
    box-shadow: var(--dnx-elevation-2), var(--dnx-edge-highlight);
    transform: translateY(var(--dnx-lift));
}

/* Plane 2. The drawer presentation in common.css sets its own shadow with !important, so
   this covers the dialogs that do not go through it. */
.dnx-page-purpose,
.dnx-provider-picker .dnx-provider-option {
    box-shadow: var(--dnx-elevation-1);
}

.dnx-provider-option:hover {
    box-shadow: var(--dnx-elevation-2), var(--dnx-edge-highlight);
    transform: translateY(var(--dnx-lift));
}

.dnx-provider-option.is-selected {
    box-shadow: var(--dnx-elevation-2);
}

/* Pressing should feel like the surface returns to the page, not like nothing happened. */
.dnx-card:active,
.svc-card:active,
.dnx-provider-option:active {
    transform: translateY(0);
    box-shadow: var(--dnx-elevation-1);
}

/* ==========================================================================
   STATUS
   ==========================================================================
   One badge shape, five roles. Includes a dot so state is not conveyed by colour alone —
   colour-blind users and greyscale printouts both need the text and shape to carry it. */

.dnx-status {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    border: 1px solid transparent;
}

.dnx-status::before {
    content: '';
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 auto;
}

/* Some states are better served by an icon than a dot — the caller opts out. */
.dnx-status.dnx-status-no-dot::before {
    display: none;
}

.dnx-status-success {
    color: var(--dnx-status-success-fg);
    background: var(--dnx-status-success-bg);
    border-color: var(--dnx-status-success-bd);
}

.dnx-status-warning {
    color: var(--dnx-status-warning-fg);
    background: var(--dnx-status-warning-bg);
    border-color: var(--dnx-status-warning-bd);
}

.dnx-status-danger {
    color: var(--dnx-status-danger-fg);
    background: var(--dnx-status-danger-bg);
    border-color: var(--dnx-status-danger-bd);
}

.dnx-status-info {
    color: var(--dnx-status-info-fg);
    background: var(--dnx-status-info-bg);
    border-color: var(--dnx-status-info-bd);
}

.dnx-status-neutral {
    color: var(--dnx-status-neutral-fg);
    background: var(--dnx-status-neutral-bg);
    border-color: var(--dnx-status-neutral-bd);
}

/* A live state (running, syncing) reads as live rather than as another static label. */
.dnx-status-live::before {
    animation: dnxStatusPulse 1.8s var(--dnx-ease) infinite;
}

@keyframes dnxStatusPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.45; transform: scale(0.82); }
}

/* ==========================================================================
   MOTION PREFERENCES
   ==========================================================================
   One blanket rule rather than per-component opt-outs, which is how animations survive
   into a reduced-motion session by accident. Kept above 0 so state changes still register
   instead of teleporting. */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }

    .dnx-card:hover,
    .svc-card:hover,
    .dnx-provider-option:hover {
        transform: none;
    }
}

/* ==========================================================================
   TABS
   ==========================================================================
   Styling for the shared DnxTabs component. The underline is drawn on the strip rather than
   each tab so the tabs sit on a continuous rule, which is what stops a tab strip reading as a
   row of disconnected buttons. */

.dnx-tablist {
    display: flex;
    gap: 0.25rem;
    align-items: stretch;
    border-bottom: 1px solid var(--app-border-color);
    /* Many strips have more tabs than fit a phone; scrolling beats wrapping, which would push
       the content down by a whole row. The scrollbar itself is hidden — scrolling still works
       via touch/trackpad/arrow keys, it just doesn't paint a track under the tab strip. */
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.dnx-tablist::-webkit-scrollbar {
    display: none;
}

.dnx-tab {
    appearance: none;
    background: none;
    border: 0;
    /* Sits over the strip's rule so the active tab's underline replaces it rather than
       doubling up. */
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    padding: 0.6rem 0.9rem;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    white-space: nowrap;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--app-text-neutral);
    cursor: pointer;
    transition: color var(--dnx-duration-fast) var(--dnx-ease),
                border-color var(--dnx-duration-fast) var(--dnx-ease),
                background-color var(--dnx-duration-fast) var(--dnx-ease);
}

.dnx-tab:hover:not(:disabled) {
    color: var(--app-text-slate);
    background-color: var(--app-tint-hover-a);
}

.dnx-tab.is-active {
    color: var(--app-primary-color);
    border-bottom-color: var(--app-primary-color);
}

.dnx-tab:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.dnx-tab-badge {
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    background: var(--dnx-status-neutral-bg);
    border: 1px solid var(--dnx-status-neutral-bd);
    color: var(--dnx-status-neutral-fg);
    font-size: 0.68rem;
    font-weight: 600;
}

.dnx-tab.is-active .dnx-tab-badge {
    background: var(--dnx-status-info-bg);
    border-color: var(--dnx-status-info-bd);
    color: var(--dnx-status-info-fg);
}

.dnx-tabpanel {
    padding-top: 1rem;
}

/* The panel is focusable so it can be reached from its tab; it is not an interactive control,
   so it should not advertise itself with a focus ring on click. */
.dnx-tabpanel:focus {
    outline: none;
}

@media (max-width: 576px) {
    .dnx-tab {
        padding: 0.55rem 0.7rem;
        font-size: 0.8rem;
    }
}

/* ==========================================================================
   GLASS FINISH
   ==========================================================================
   A liquid-glass treatment applied app-wide, dialled to ~40% strength: clearly
   translucent and blurred so what's behind a surface reads through it, while
   staying short of full elevation-shadow competition or text-contrast loss. Targets the
   shell chrome (header, sidebar, nav) and the recurring surface classes (cards,
   modals, dropdowns, inputs, tables) rather than requiring markup changes.

   Uses --app-surface-rgb (already themed for light/dark) so the tint always
   matches the active theme. Falls back gracefully where backdrop-filter isn't
   supported: the near-opaque rgba background alone still reads as a surface,
   just without the blur. */

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .admin-header,
    .admin-sidebar,
    .app-header,
    .app-sidebar,
    .common-card,
    .dnx-card,
    .card.common-card,
    .modal-content,
    .dropdown-menu,
    .dnx-select-dropdown,
    .dnx-tablist,
    .toast,
    .custom-toast {
        backdrop-filter: blur(10px) saturate(130%);
        -webkit-backdrop-filter: blur(10px) saturate(130%);
    }
}

.admin-header,
.admin-sidebar,
.app-header,
.app-sidebar,
.common-card,
.dnx-card,
.card.common-card,
.modal-content,
.dropdown-menu,
.dnx-select-dropdown,
.dnx-tablist,
.toast,
.custom-toast {
    background-color: rgba(var(--app-surface-rgb), 0.6) !important;
    border: 1px solid rgba(var(--app-surface-rgb), 0.75);
    /* A faint light edge on top of the blur is what sells "glass" rather than
       "semi-transparent panel" — same trick as --dnx-edge-highlight, restated here
       so it survives on elements that set their own border above. */
    border-color: rgba(255, 255, 255, 0.28);
}

.dark-theme .admin-header,
.dark-theme .admin-sidebar,
.dark-theme .app-header,
.dark-theme .app-sidebar,
.dark-theme .common-card,
.dark-theme .dnx-card,
.dark-theme .card.common-card,
.dark-theme .modal-content,
.dark-theme .dropdown-menu,
.dark-theme .dnx-select-dropdown,
.dark-theme .dnx-tablist,
.dark-theme .toast,
.dark-theme .custom-toast {
    background-color: rgba(var(--app-surface-rgb), 0.55) !important;
    border-color: rgba(255, 255, 255, 0.14);
}

/* The page body behind everything needs its own tint so the blur has something
   visibly different to pick up — a flat single-colour background makes
   backdrop-filter invisible even though it's active. Kept to the same ~40%
   intensity as the surfaces above. */
body {
    background-image:
        radial-gradient(circle at 15% 10%, rgba(37, 99, 235, 0.14), transparent 45%),
        radial-gradient(circle at 85% 90%, rgba(37, 99, 235, 0.11), transparent 45%);
    background-attachment: fixed;
}

.dark-theme body {
    background-image:
        radial-gradient(circle at 15% 10%, rgba(127, 176, 255, 0.16), transparent 45%),
        radial-gradient(circle at 85% 90%, rgba(127, 176, 255, 0.14), transparent 45%);
}

/* ==========================================================================
   WORKSPACE DRAFT NOTICE
   ==========================================================================
   The "restored your unsaved changes" bar (WorkspaceDraft.razor), shared by any
   page that opts into draft autosave. Amber, not the semantic "info" blue —
   this is a one-off transient notice about the user's own edit history, not a
   system status. */
.workspace-draft-notice {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 9px 20px;
    margin-bottom: 0.5rem;
    background: var(--notice-bg, #fff9e6);
    border: 1px solid var(--notice-border, #f0e0a0);
    border-radius: var(--r-control, 8px);
    font-size: 13px;
    color: var(--notice-ink, #7a5c00);
}

.workspace-draft-notice-message {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.workspace-draft-notice-discard {
    appearance: none;
    background: none;
    border: 0;
    padding: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--app-primary-color);
    cursor: pointer;
    white-space: nowrap;
}

.workspace-draft-notice-discard:hover {
    text-decoration: underline;
}
