/* EdgePanel UI foundation styles: mobile-first, theme-aware.
 *
 * Layered ON TOP of Bootstrap 5 via CSS variables and a thin set of component
 * polish rules — no build step, no framework swap. The design tokens live in
 * :root and are overridden per theme, so a colour or radius change is one line.
 */

/* ============================ design tokens ============================ */
:root {
    --ep-radius: 14px;
    --ep-radius-sm: 10px;
    --ep-gap: 1rem;
    --ep-ease: cubic-bezier(.2, .7, .3, 1);
    --ep-accent: #4dabf7;
    --ep-accent-2: #b197fc;

    /* Elevation — soft, layered shadows read as "designed" rather than boxed. */
    --ep-shadow: 0 1px 2px rgba(0,0,0,.06), 0 4px 16px -6px rgba(0,0,0,.12);
    --ep-shadow-hover: 0 2px 6px rgba(0,0,0,.10), 0 14px 34px -10px rgba(0,0,0,.28);
}

/* Dark: deep charcoal, never pure black — softer, and lets glows read. */
[data-bs-theme="dark"] {
    --bs-body-bg: #0f1216;
    --bs-body-bg-rgb: 15,18,22;
    --bs-tertiary-bg: #171b21;
    --bs-border-color: #262c35;
    --bs-border-color-translucent: rgba(255,255,255,.08);
    --ep-card-bg: #161a20;
    --ep-card-border: #262c35;
    --ep-elevate: #1b2027;
    --ep-shadow: 0 1px 2px rgba(0,0,0,.4), 0 8px 24px -8px rgba(0,0,0,.55);
    --ep-shadow-hover: 0 2px 6px rgba(0,0,0,.5), 0 18px 44px -12px rgba(0,0,0,.7);
}
[data-bs-theme="light"] {
    --bs-body-bg: #f4f6fa;
    --ep-card-bg: #ffffff;
    --ep-card-border: #e6e9ef;
    --ep-elevate: #ffffff;
}

/* ============================ base ============================ */
body {
    padding-bottom: 3rem;
    font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    font-feature-settings: 'cv02','cv03','cv04','ss01';
    letter-spacing: -.01em;
    background-image:
        radial-gradient(1200px 600px at 80% -10%, rgba(77,171,247,.06), transparent 60%),
        radial-gradient(1000px 500px at -10% 0%, rgba(177,151,252,.05), transparent 55%);
    background-attachment: fixed;
}
h1, h2, h3, h4, h5, h6, .navbar-brand { letter-spacing: -.02em; }
.mono, .font-mono { font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; }
.navbar-brand { font-weight: 700; letter-spacing: -.02em; }

/* Numbers that update (KPIs, counters) want tabular figures so they don't
 * jitter as digits change. */
.fs-3, .fs-4, .h4, .kpi-num, [id^="stat-"], [id^="cf-"] { font-variant-numeric: tabular-nums; }

/* ============================ cards ============================ */
.card {
    background: var(--ep-card-bg);
    border: 1px solid var(--ep-card-border);
    border-radius: var(--ep-radius);
    box-shadow: var(--ep-shadow);
    transition: transform .18s var(--ep-ease), box-shadow .18s var(--ep-ease), border-color .18s var(--ep-ease);
}
.card-header {
    background: transparent;
    border-bottom: 1px solid var(--bs-border-color-translucent);
    font-weight: 600;
}
/* Only cards that are meant to be interactive lift — KPI tiles and links. */
.kpi-card:hover,
a > .card:hover {
    transform: translateY(-2px);
    box-shadow: var(--ep-shadow-hover);
    border-color: color-mix(in srgb, var(--ep-accent) 40%, var(--ep-card-border));
}

/* A subtle top accent line on KPI cards — the small detail that reads premium. */
.kpi-card::before {
    content: ''; position: absolute; inset: 0 0 auto 0; height: 2px;
    background: linear-gradient(90deg, var(--ep-accent), var(--ep-accent-2));
    opacity: 0; transition: opacity .2s var(--ep-ease);
}
.kpi-card:hover::before { opacity: .9; }
.card.border-danger { box-shadow: var(--ep-shadow), 0 0 0 1px rgba(255,107,107,.35), 0 0 22px -6px rgba(255,107,107,.5); }

.stat-card .display-6 { font-weight: 700; }
.table-sm td, .table-sm th { vertical-align: middle; }
.badge-online { background: #198754; }
.badge-offline { background: #6c757d; }
.tier-active { background: rgba(25,135,84,.18); }
.tier-active-throttled { background: rgba(220,53,69,.18); }
.tier-consumed { opacity: .55; }
.pulse { animation: pulse 2s infinite; }
@keyframes pulse { 0%{opacity:1} 50%{opacity:.4} 100%{opacity:1} }

/* Badges & buttons: a touch rounder, snappier press feedback. */
.badge { border-radius: 999px; font-weight: 600; letter-spacing: 0; }
.btn { border-radius: var(--ep-radius-sm); transition: transform .12s var(--ep-ease), box-shadow .12s var(--ep-ease), background-color .12s var(--ep-ease); }
.btn:active { transform: translateY(1px) scale(.99); }
.form-control, .form-select { border-radius: var(--ep-radius-sm); }

/* ============================ KPI cards ============================ */
.kpi-card { position: relative; overflow: hidden; }
.kpi-card .kpi-spark { position: absolute; inset: auto 0 0 0; height: 34px; opacity: .85; pointer-events: none; }
.kpi-card .card-body { padding-bottom: 2.4rem; }
.kpi-live-dot {
    width: 8px; height: 8px; border-radius: 50%; background: #51cf66; display: inline-block;
    animation: pulse 2s infinite; box-shadow: 0 0 8px rgba(81,207,102,.8);
}

/* ============================ count-up + skeletons ============================ */
/* A number mid-animation shouldn't wrap or shift layout. */
.kpi-counting { transition: none; }

.ep-skeleton {
    position: relative; overflow: hidden;
    background: color-mix(in srgb, var(--bs-secondary-color) 16%, transparent);
    border-radius: 6px; color: transparent !important;
}
.ep-skeleton::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.10), transparent);
    transform: translateX(-100%); animation: ep-shimmer 1.3s infinite;
}
@keyframes ep-shimmer { 100% { transform: translateX(100%); } }

/* ============================ sortable tables ============================ */
th.ep-sortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.ep-sortable::after { content: '↕'; opacity: .3; margin-left: .3em; font-size: .8em; }
th.ep-sortable[data-dir="asc"]::after { content: '↑'; opacity: .9; }
th.ep-sortable[data-dir="desc"]::after { content: '↓'; opacity: .9; }
.table-hover > tbody > tr { transition: background-color .12s var(--ep-ease); }

/* Variant-tinted rows (the overdue .table-danger highlight on tickets) keep
 * Bootstrap's LIGHT background in dark mode, while .text-body links and the
 * default link color stay theme-light — near-white text on pink. Pull every
 * cell and non-button link back to the variant's own dark foreground so the
 * row is readable in both themes. !important is needed to out-rank the
 * .text-body utility, which is itself !important. */
.table-danger td, .table-danger th,
.table-danger .text-body,
.table-danger a:not(.btn) {
    color: var(--bs-table-color) !important;
}

/* ======================= compact summary mini-cards =======================
   Fixed-height scroll bodies for the roll-up cards (subscriber list). The
   sticky header must be OPAQUE or rows show through it, and the gutter must be
   reserved or the scrollbar sits on top of the last column and clips it. */
.ep-minitable {
    max-height: 210px;
    overflow-y: auto;
    overflow-x: auto;
    scrollbar-gutter: stable;
    scrollbar-width: thin;
}
.ep-minitable::-webkit-scrollbar { width: 8px; height: 8px; }
.ep-minitable::-webkit-scrollbar-thumb {
    background: var(--bs-border-color); border-radius: 8px;
}
.ep-minitable table { font-size: .8125rem; }
.ep-minitable thead th {
    position: sticky; top: 0; z-index: 2;
    background: var(--ep-card-bg);
    box-shadow: inset 0 -1px 0 var(--bs-border-color-translucent);
    white-space: nowrap;
    padding-top: .3rem; padding-bottom: .3rem;
    font-weight: 600;
}
.ep-minitable td { padding-top: .22rem; padding-bottom: .22rem; }
.ep-minitable tbody tr:hover { background: var(--ep-elevate); }

/* One location per row: name + counts, with a bar showing this site's share of
   all online sessions and, inside it, the slice that is throttled. */
.ep-locrow {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: .2rem .75rem;
    padding: .45rem .75rem;
    font-size: .8125rem;
    border-bottom: 1px solid var(--bs-border-color-translucent);
}
.ep-locrow:last-child { border-bottom: 0; }
.ep-bar {
    grid-column: 1 / -1;
    height: 4px; border-radius: 4px;
    background: var(--bs-border-color-translucent);
    overflow: hidden;
}
.ep-bar > i {
    display: block; height: 100%; width: var(--w, 0%);
    border-radius: 4px;
    background: linear-gradient(90deg, var(--ep-accent), var(--ep-accent-2));
    position: relative;
}
/* the throttled slice, drawn on top of the site's bar — only when non-zero,
   else the min-width floor would invent a sliver where there is none */
.ep-bar > i.has-thr::after {
    content: ''; position: absolute; inset: 0 auto 0 0;
    width: var(--t, 0%); min-width: 3px; border-radius: 4px;
    background: var(--bs-danger);
}
/* A number with its magnitude drawn behind it — no extra column needed.
   z-index:0 makes the span a stacking context so the bar (z-index:-1) paints
   behind the digits instead of over them. */
.ep-cellbar {
    position: relative; z-index: 0;
    display: inline-block; padding: 0 .35rem; border-radius: 4px;
}
.ep-cellbar::before {
    content: ''; position: absolute; inset: 0 0 0 auto; z-index: -1;
    width: var(--w, 0%); min-width: 2px; border-radius: 4px;
    background: color-mix(in srgb, var(--ep-accent) 22%, transparent);
}

/* ============================ chart containers ============================ */
.chart-box { position: relative; height: 260px; }
.chart-box-sm { position: relative; height: 180px; }

/* ============================ toasts ============================ */
#ep-toasts { z-index: 1090; }
.toast { border-radius: var(--ep-radius-sm); box-shadow: var(--ep-shadow-hover); }

/* ============================ nav ============================ */
/* backdrop-filter makes the navbar its own stacking context, which would trap
 * the dropdown menu BELOW the absolutely-positioned Gridstack cards that come
 * later in the DOM. Lifting the whole navbar above the grid keeps the menu on
 * top. 1030 = Bootstrap's fixed-element tier, still under modals/toasts. */
.navbar { position: relative; z-index: 1030; backdrop-filter: saturate(1.4) blur(6px); }
.nav-link { border-radius: var(--ep-radius-sm); transition: background-color .12s var(--ep-ease), color .12s var(--ep-ease); }
.navbar-nav .nav-link.active { color: var(--bs-emphasis-color); }
.dropdown-menu { border-radius: var(--ep-radius); box-shadow: var(--ep-shadow-hover); border-color: var(--bs-border-color-translucent); }

/* ============================ dashboard grid (Gridstack) ============================ */
.grid-stack { margin: 0 -6px; }
.grid-stack-item-content { inset: 6px; }
.grid-stack-item-content > .card { height: 100%; margin: 0; }
/* The drag handle only appears in edit mode, so a locked dashboard looks and
 * behaves exactly like the old static one. */
.ep-grid-handle {
    display: none; position: absolute; top: 8px; right: 10px; z-index: 5;
    cursor: move; color: var(--bs-secondary-color); font-size: 1.05rem; line-height: 1;
    opacity: .6; user-select: none;
}
.ep-editing .ep-grid-handle { display: block; }
.ep-editing .grid-stack-item-content > .card {
    outline: 1px dashed color-mix(in srgb, var(--ep-accent) 55%, transparent);
    outline-offset: -1px;
}
.ep-editing .kpi-card:hover { transform: none; }  /* don't fight the drag */
.grid-stack-item.ui-draggable-dragging .card,
.grid-stack-item.ui-resizable-resizing .card { box-shadow: var(--ep-shadow-hover); }
.grid-stack > .grid-stack-placeholder > .placeholder-content {
    border: 1px dashed color-mix(in srgb, var(--ep-accent) 60%, transparent);
    border-radius: var(--ep-radius); background: color-mix(in srgb, var(--ep-accent) 8%, transparent);
}
@media (prefers-reduced-motion: reduce) {
    * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ---------------- Mobile ---------------- */
@media (max-width: 767.98px) {
    body { padding-bottom: calc(4.4rem + env(safe-area-inset-bottom)); }
    .container-fluid { padding-left: .75rem; padding-right: .75rem; }
    h4 { font-size: 1.15rem; }
    .fs-3 { font-size: 1.35rem !important; }

    /* Bottom tab bar */
    .ep-bottomnav {
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 1080;
        display: flex; justify-content: space-around; align-items: stretch;
        border-top: 1px solid var(--bs-border-color);
        background: color-mix(in srgb, var(--bs-body-bg) 88%, transparent);
        backdrop-filter: saturate(1.4) blur(10px);
        padding-bottom: env(safe-area-inset-bottom);
    }
    .ep-bottomnav a {
        flex: 1; text-align: center; padding: .45rem 0 .35rem;
        font-size: .68rem; text-decoration: none; color: var(--bs-secondary-color);
        line-height: 1.15; transition: color .12s var(--ep-ease);
    }
    .ep-bottomnav a .ep-bn-ico { display: block; font-size: 1.25rem; line-height: 1.2; }
    .ep-bottomnav a.active { color: var(--bs-emphasis-color); font-weight: 600; }
    .ep-bottomnav a:active { color: var(--ep-accent); }

    /* Card-collapse tables: any table marked data-mobile="cards" */
    table[data-mobile="cards"] thead { display: none; }
    table[data-mobile="cards"] tbody tr {
        display: block; border: 1px solid var(--bs-border-color);
        border-radius: var(--ep-radius-sm); margin: .5rem; padding: .25rem .6rem;
        background: var(--ep-card-bg);
    }
    table[data-mobile="cards"] tbody td {
        display: flex; justify-content: space-between; gap: 1rem;
        border: 0; padding: .28rem 0; text-align: right !important;
    }
    table[data-mobile="cards"] tbody td::before {
        content: attr(data-th); font-weight: 600; color: var(--bs-secondary-color);
        text-align: left;
    }
    table[data-mobile="cards"] tbody td[data-th=""]::before { content: none; }
}
@media (min-width: 768px) { .ep-bottomnav { display: none; } }

/* Light-theme tweaks */
[data-bs-theme="light"] .navbar { background: rgba(255,255,255,.8) !important; }
[data-bs-theme="light"] .tier-active { background: rgba(25,135,84,.12); }
[data-bs-theme="light"] .tier-active-throttled { background: rgba(220,53,69,.12); }
