@font-face {
    font-family: 'Mulish';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('fonts/mulish-v18-latin-regular.woff2') format('woff2');
}
@font-face {
    font-family: 'Mulish';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('fonts/mulish-v18-latin-500.woff2') format('woff2');
}
@font-face {
    font-family: 'Mulish';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('fonts/mulish-v18-latin-600.woff2') format('woff2');
}
@font-face {
    font-family: 'Mulish';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('fonts/mulish-v18-latin-700.woff2') format('woff2');
}

html, body {
    font-family: 'Nunito', 'Mulish', 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    margin: 0;
}

/* ============================================================
   Redesign design tokens — mirror of design_handoff_dashboard/app/theme.css.
   Dark is the default; [data-theme="light"] is set on <html> by the anti-flash
   script in App.razor and kept in sync by MainLayout's theme toggle. The key
   colours are also mirrored into MudTheme so MudBlazor components inherit them.
   ============================================================ */
:root, [data-theme="dark"] {
    --accent: #9d6bff;
    --accent-2: #7c4dff;
    --accent-soft: rgba(157, 107, 255, 0.14);
    --accent-ring: rgba(157, 107, 255, 0.45);
    --ok: #2fd08a;
    --danger: #ff7a7a;
    --warn: #f5b544;
    --info: #6aa8ff;
    --spotify: #25d366;
    --youtube: #ff5d5d;
    --discord: #6b78ff;
    --bg: #08060f;
    --bg-2: #0c0a17;
    --glow-a: rgba(124, 77, 255, 0.22);
    --glow-b: rgba(157, 107, 255, 0.10);
    --glow-c: rgba(40, 30, 80, 0.5);
    --surface: rgba(255, 255, 255, 0.045);
    --surface-hover: rgba(255, 255, 255, 0.075);
    --surface-solid: #14111f;
    --border: rgba(255, 255, 255, 0.085);
    --border-strong: rgba(255, 255, 255, 0.15);
    --panel: rgba(20, 16, 32, 0.55);
    --panel-blur: rgba(16, 13, 26, 0.78);
    --teal: #1fb6c4;
    --pink: #e25fae;
    --text: #ece9f6;
    --text-2: #b3aecb;
    --text-3: #7e7896;
    --shadow-sm: 0 8px 24px -16px rgba(0, 0, 0, 0.8);
    --shadow: 0 18px 50px -22px rgba(0, 0, 0, 0.85), 0 2px 8px -4px rgba(0, 0, 0, 0.6);
    --radius: 14px;
    --r-sm: calc(var(--radius) * 0.6);
    --r-pill: 999px;
    --font-display: "Quicksand", system-ui, sans-serif;
    --font-body: "Nunito", system-ui, sans-serif;
    --font-mono: "JetBrains Mono", ui-monospace, monospace;
}

[data-theme="light"] {
    --accent: #7c3aed;
    --accent-2: #6d28d9;
    --accent-soft: rgba(124, 58, 237, 0.10);
    --accent-ring: rgba(124, 58, 237, 0.35);
    --bg: #eceaf4;
    --bg-2: #f4f2fb;
    --glow-a: rgba(124, 77, 255, 0.16);
    --glow-b: rgba(157, 107, 255, 0.10);
    --glow-c: rgba(210, 200, 255, 0.55);
    --surface: rgba(255, 255, 255, 0.72);
    --surface-hover: rgba(255, 255, 255, 0.95);
    --surface-solid: #ffffff;
    --border: rgba(36, 24, 70, 0.09);
    --border-strong: rgba(36, 24, 70, 0.16);
    --panel: rgba(255, 255, 255, 0.7);
    --panel-blur: rgba(255, 255, 255, 0.82);
    --text: #1b1730;
    --text-2: #524c6b;
    --text-3: #8a849e;
    --shadow-sm: 0 8px 22px -16px rgba(60, 40, 120, 0.35);
    --shadow: 0 20px 48px -26px rgba(60, 40, 120, 0.45), 0 2px 6px -3px rgba(60, 40, 120, 0.18);
}

/* Background ambiance: layered radial glows behind the whole app, fixed so they
   don't scroll. Overrides MudBlazor's flat palette background (loaded after
   app.css, hence !important). */
body {
    background:
        radial-gradient(1100px 700px at 8% -8%, var(--glow-a), transparent 60%),
        radial-gradient(900px 600px at 100% 110%, var(--glow-b), transparent 55%),
        radial-gradient(700px 500px at 60% 40%, var(--glow-c), transparent 70%),
        var(--bg) !important;
    background-attachment: fixed !important;
}

/* Frosted chrome — MudBlazor doesn't set backdrop-filter, so these layer on top
   of the translucent appbar/drawer backgrounds from the palette. */
.mud-appbar {
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    border-bottom: 1px solid var(--border) !important;
}
.mud-drawer {
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    border-right: 1px solid var(--border) !important;
}

/* Primary filled buttons → 135° accent gradient with a soft glow. */
.mud-button-filled.mud-button-filled-primary {
    background: linear-gradient(135deg, var(--accent), var(--accent-2)) !important;
    box-shadow: 0 12px 28px -12px var(--accent-ring), inset 0 1px 0 rgba(255, 255, 255, 0.22) !important;
}
.mud-button-filled.mud-button-filled-primary:hover { filter: brightness(1.08); }

/* Sidebar active nav item: accent-soft fill, ring border, left accent bar. */
.mud-nav-link.active, .mud-nav-link[aria-current="page"] {
    background: var(--accent-soft) !important;
    border: 1px solid var(--accent-ring) !important;
    border-radius: 11px !important;
    position: relative;
}
.mud-nav-link.active::before, .mud-nav-link[aria-current="page"]::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 4px;
    height: 22px;
    border-radius: 0 4px 4px 0;
    background: linear-gradient(var(--accent), var(--accent-2));
}

/* Flat sidebar section labels (uppercase, muted) — interleaved with the nav
   links in NavMenu instead of collapsible groups. */
.streamionbot-nav-label {
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-3);
    padding: 16px 16px 6px;
    white-space: nowrap;
}

/* Frosted dropdown panel for the topbar Help + User menus (applied via the
   MudMenu PopoverClass). Translucent + blurred, radius 14, soft shadow. */
.streamionbot-frosted-menu.mud-popover {
    background: var(--panel-blur) !important;
    backdrop-filter: blur(24px) saturate(150%);
    -webkit-backdrop-filter: blur(24px) saturate(150%);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius) !important;
    box-shadow: var(--shadow) !important;
}
.streamionbot-frosted-menu .mud-list { background: transparent !important; }

/* Help-menu sub-labels in teal (the design's secondary accent). */
.streamionbot-help-sub { color: var(--teal) !important; font-weight: 600; }

/* Reusable redesign primitives shared across the content pages (Commands,
   Quotes, Events, Media, admin tables, …). Frosted card + mono trigger chip +
   frosted modal panel. Pages that already ship their own page-local copies
   (Dashboard, Stats) are unaffected — those <style> blocks only exist while
   their page is mounted. */
.streamionbot-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    backdrop-filter: blur(16px) saturate(130%);
    -webkit-backdrop-filter: blur(16px) saturate(130%);
    box-shadow: var(--shadow-sm);
}
.streamionbot-card .mud-table { background: transparent !important; }

/* Command / quote / counter trigger — mono accent chip. */
.trigger-chip {
    font-family: var(--font-mono);
    font-size: 13px;
    font-weight: 600;
    background: var(--accent-soft);
    color: var(--accent);
    padding: 3px 9px;
    border-radius: 7px;
    border: 1px solid var(--accent-ring);
    white-space: nowrap;
}

/* Small accent icon tile used as a section-header glyph (Alerts "Alert types",
   Tools section headers, …). Mirrors the scene-card icon tile. */
.streamionbot-section-ico {
    width: 34px;
    height: 34px;
    flex: none;
    border-radius: 9px;
    display: grid;
    place-items: center;
    background: var(--accent-soft);
    color: var(--accent);
    border: 1px solid var(--accent-ring);
}

/* Friendly in-table empty state (MudTable NoRecordsContent): a muted icon over
   centred text. Reusable across the table pages. */
.streamionbot-table-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 36px 16px;
    text-align: center;
    color: var(--text-2);
    font-weight: 600;
}
.streamionbot-table-empty__ico { color: var(--text-3); opacity: 0.5; }

/* Frosted modal panels — the redesign renders every dialog as frosted glass
   (radius 14 comes from the MudTheme). */
.mud-dialog {
    background: var(--panel-blur) !important;
    backdrop-filter: blur(24px) saturate(150%);
    -webkit-backdrop-filter: blur(24px) saturate(150%);
    border: 1px solid var(--border-strong);
    box-shadow: var(--shadow) !important;
}

/* Code, command triggers and {variables} → JetBrains Mono. */
code, kbd, samp, pre { font-family: var(--font-mono); }

/* Smooth scrollbar on dark backgrounds */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.08); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.18); }

/* Permission level chips on the Commands page. Colour hints follow the
   visual language of the Twitch chat badges (broadcaster camera, mod
   sword, VIP diamond). Everyone uses the default outlined MudChip look.
   All role pills share a fixed width so the column lines up cleanly. */
.permission-chip {
    width: 120px !important;
    justify-content: center !important;
}
.permission-chip .mud-chip-content {
    flex-grow: 1;
    justify-content: center;
}
.permission-follower,
.permission-subscriber,
.permission-vip,
.permission-moderator,
.permission-broadcaster {
    color: white !important;
    font-weight: 500 !important;
}
.permission-follower    { background: #7d6ff0 !important; }  /* Twitch heart purple */
.permission-subscriber  { background: #4fc3f7 !important; }  /* light blue */
.permission-vip         { background: #e005b9 !important; }  /* Twitch VIP pink */
.permission-moderator   { background: #00ad03 !important; }  /* Twitch mod green */
.permission-broadcaster { background: #e71a28 !important; }  /* Twitch broadcaster red */

/* Highlighted header for StreamionBot data tables. MudTable's HeaderClass
   lands on the <thead> element, so we style its th children. */
.streamionbot-table-header {
    background: var(--mud-palette-action-default-hover) !important;
}
.streamionbot-table-header th {
    font-weight: 600 !important;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    font-size: 0.75rem;
}

/* Page-level header (see PageHeader.razor). The redesign drops the old underline
   accent — the title is just the big Quicksand display type (set via MudTheme),
   with the subtitle below. */
.streamionbot-page-header__title {
    align-self: flex-start;
    letter-spacing: -0.025em;
}
.streamionbot-page-header__actions {
    align-self: flex-end;
}

/* Section header within a page (see SectionHeader.razor). The left accent
   bar visually anchors the section title without competing with the
   page-level underline. */
.streamionbot-section-header__title {
    border-left: 3px solid var(--mud-palette-primary);
    padding-left: 12px;
}

/* AlertPreview — admin-side stand-in for the OBS browser-source
   toast. The structure mirrors wwwroot/overlays/alerts/alerts.css
   (same class names, same layout modifiers) so the preview reads as
   "what OBS will paint" minus animation/sound. Everything is scoped
   under .streamionbot-alert-preview so the global selectors of the live
   overlay can't leak into the admin UI. */
.streamionbot-alert-preview__stage {
    background:
        /* Subtle vignette so the foreground text doesn't drown when
           the streamer picks a near-white accent against the dark
           preview surface. */
        radial-gradient(circle at center, rgba(255,255,255,0.04) 0%, transparent 70%),
        repeating-linear-gradient(45deg, rgba(255,255,255,0.02) 0 8px, transparent 8px 16px),
        #1a1f24;
    border-radius: 8px;
    padding: 24px;
    min-height: 160px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.streamionbot-alert-preview .toast {
    display: flex;
    align-items: center;
    gap: 14px;
    font-family: var(--font-family);
    color: var(--text-color);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
    max-width: 100%;
}
.streamionbot-alert-preview .toast.layout-left   { flex-direction: row;            text-align: left;   }
.streamionbot-alert-preview .toast.layout-right  { flex-direction: row-reverse;    text-align: right;  }
.streamionbot-alert-preview .toast.layout-top    { flex-direction: column;         text-align: center; align-items: center; }
.streamionbot-alert-preview .toast.layout-bottom { flex-direction: column-reverse; text-align: center; align-items: center; }
.streamionbot-alert-preview .toast-icon {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 56px;
    line-height: 1;
}
.streamionbot-alert-preview .toast-icon img {
    max-width: var(--image-max-size, 160px);
    max-height: var(--image-max-size, 160px);
    width: auto;
    height: auto;
    object-fit: contain;
    display: block;
}
.streamionbot-alert-preview .toast-text {
    min-width: 0;
    display: flex;
    flex-direction: column;
}
.streamionbot-alert-preview .toast-title {
    /* Title scales with the same --font-size custom prop that drives
       the subtitle, with a fixed offset so the headline stays larger.
       At the 18 px subtitle default this resolves to 28 px — matches
       the hardcoded value the old layout shipped with, so existing
       configs paint identically. */
    font-size: calc(var(--font-size, 18px) + 10px);
    font-weight: 700;
    line-height: 1.15;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.streamionbot-alert-preview .toast-subtitle {
    font-size: var(--font-size, 18px);
    font-weight: var(--font-weight, 400);
    opacity: 0.9;
    margin-top: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.streamionbot-alert-preview .alert-var {
    color: var(--accent, inherit);
}
.streamionbot-alert-preview .toast-message {
    font-size: calc(var(--font-size, 18px) * 0.85);
    font-weight: var(--font-weight, 400);
    color: var(--text-color, inherit);
    opacity: 0.75;
    margin-top: 4px;
    line-height: 1.25;
    white-space: normal;
    word-break: break-word;
}

/* AppBar user menu: swap MudMenu's StartIcon SVG for the Twitch profile picture.
   Activated when the `--user-avatar-url` CSS var is set on the MudMenu root. */
.streamionbot-user-menu-avatar .mud-button-icon-start {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    overflow: hidden;
    background-image: var(--user-avatar-url);
    background-size: cover;
    background-position: center;
    margin-right: 8px;
}
.streamionbot-user-menu-avatar .mud-button-icon-start svg {
    display: none;
}

/* Drag-to-position handle inside the layout-preview rectangles
   (used by /alerts → "Alert Overlay Layout" and /media-commands →
   "Video overlay position"). cursor: grab while idle, grabbing
   while a drag is in flight. touch-action: none stops the browser
   from claiming the pointer for scroll / pinch on touch devices.
   user-select: none keeps a long-drag from accidentally selecting
   the glyph inside the box. The .is-dragging modifier is toggled
   from app.js, and disables the 120 ms position transition so the
   box stays under the cursor without visible lag. */
.media-preview-handle {
    cursor: grab;
    touch-action: none;
    user-select: none;
}
.media-preview-handle.is-dragging {
    cursor: grabbing;
    transition: none !important;
}

/* --------------------------------------------------------------------
   Overlay scene editor canvas (/overlays/scenes/{id}). Global, not a
   .razor.css scoped file — the host doesn't link the scoped-CSS bundle,
   so scoped styles never load. The class names are unique to the editor
   so there is no collision risk at global scope.
   -------------------------------------------------------------------- */
.scene-canvas {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #0d1117;
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid var(--mud-palette-lines-default);
}

.scene-canvas-frame {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    background: transparent;
}

/* While a widget is being dragged/resized the iframe must ignore pointer
   events, otherwise it can swallow the move events and the drag gets stuck. */
.scene-canvas.editor-busy .scene-canvas-frame {
    pointer-events: none;
}

/* Transparent layer above the iframe that captures pointer events for
   selecting / dragging widgets. The iframe shows pixels; this owns input. */
.scene-interaction {
    position: absolute;
    inset: 0;
    z-index: 1;
}

.editor-hitbox {
    position: absolute;
    box-sizing: border-box;
    border: 1.5px solid rgba(124, 77, 255, 0.55);
    background: rgba(124, 77, 255, 0.07);
    border-radius: 4px;
    cursor: move;
    touch-action: none;
    transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}

.editor-hitbox:hover {
    border-color: var(--mud-palette-primary);
    background: rgba(124, 77, 255, 0.18);
    box-shadow: 0 0 0 1px rgba(124, 77, 255, 0.35);
}

.editor-hitbox.selected {
    border: 2px solid var(--mud-palette-primary);
    background: rgba(124, 77, 255, 0.14);
    box-shadow: 0 0 0 2px rgba(124, 77, 255, 0.3);
}

.editor-hitbox-label {
    position: absolute;
    top: 4px;
    left: 4px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.5;
    color: #fff;
    background: var(--mud-palette-primary);
    padding: 0 6px;
    border-radius: 4px;
    pointer-events: none;
    white-space: nowrap;
    opacity: 0.92;
}

.editor-handle {
    position: absolute;
    width: 14px;
    height: 14px;
    background: #fff;
    border: 2px solid var(--mud-palette-primary);
    border-radius: 3px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

.editor-handle[data-handle="nw"] { top: -8px; left: -8px; cursor: nwse-resize; }
.editor-handle[data-handle="ne"] { top: -8px; right: -8px; cursor: nesw-resize; }
.editor-handle[data-handle="sw"] { bottom: -8px; left: -8px; cursor: nesw-resize; }
.editor-handle[data-handle="se"] { bottom: -8px; right: -8px; cursor: nwse-resize; }
