/* ============================================================================
   PIA SYSTEM — ADMIN CONSOLE v2
   Page styles unique to the admin console.
   REQUIRES styles/global.css (reset, tokens, shared components) and
   styles/shell.css (rail, workspace, top bar).
   ==========================================================================*/
/* ---------------------------------------------------------------- 9. TABLES */
.table-wrap {
    overflow-x: auto;
}

.table { font-size: 13px; }

.table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    height: 44px;
    padding: 0 var(--sp-5);
    background: var(--bg-surface);
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-faint);
    white-space: nowrap;
}

.table tbody td {
    height: var(--row-h);          /* fixed row height across every state */
    padding: 0 var(--sp-5);
    vertical-align: middle;
    white-space: nowrap;
}

/* Long rows are kept scannable by an almost subliminal alternating fill —
   a background difference, never a line. */
.table tbody tr { transition: background-color var(--t-base); }
.table tbody tr:nth-child(even) { background: rgba(255, 255, 255, 0.012); }
.table tbody tr:hover,
.table tbody tr:nth-child(even):hover { background: var(--bg-surface-hover); }
.table tbody tr.is-clickable { cursor: pointer; }

.col-right { text-align: right; }
/* One 36px button plus the cell's padding. Was 116px, sized for two. */
.col-w-actions { width: 72px; }

.cell-user { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.cell-user-text { min-width: 0; }
.cell-name { display: block; font-weight: 600; line-height: 18px; overflow: hidden; text-overflow: ellipsis; }
.cell-mail { display: block; font-size: 11.5px; line-height: 16px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; }

.row-actions { display: inline-flex; align-items: center; gap: var(--sp-1); justify-content: flex-end; }

/* Empty + loading states occupy the same block height as a filled table. */
.state-block {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--sp-3);
    min-height: 280px;
    padding: var(--sp-6);
    text-align: center;
}

.state-glyph {
    width: 48px;
    height: 48px;
    display: grid;
    place-items: center;
    border-radius: var(--r-lg);
    background: var(--bg-field);
    color: var(--text-faint);
}

.state-title { font-size: 14px; font-weight: 600; }
.state-desc { font-size: 12.5px; color: var(--text-muted); max-width: 42ch; }

/* Skeleton shimmer uses opacity only — no size change, no jump. */
.skeleton {
    border-radius: var(--r-xs);
    background: var(--bg-surface-hi);
    animation: skel 1.4s ease-in-out infinite;
}

@keyframes skel { 0%, 100% { opacity: 0.45; } 50% { opacity: 0.9; } }

.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    min-height: 56px;             /* always reserved */
    padding: var(--sp-2) var(--sp-5);
    font-size: 12.5px;
    color: var(--text-muted);
}

.pager-btns { display: flex; align-items: center; gap: var(--sp-2); }

/* -------------------------------------------------- 10. VIEW-LEVEL PATTERNS */
/* Stage funnel strip */
.funnel { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-4); }

.funnel-step {
    display: flex;
    flex-direction: column;
    justify-content: space-between;   /* bar always sits on the baseline */
    min-height: 132px;
    padding: var(--sp-4) var(--sp-5);
    border-radius: var(--r-md);
    background: var(--bg-field);
}

.funnel-num { font-size: 24px; font-weight: 700; line-height: 30px; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.funnel-name { margin-top: 2px; font-size: 12.5px; font-weight: 600; color: var(--text-muted); line-height: 1.35; }
.funnel-foot { margin-top: var(--sp-4); }
.funnel-meta { margin-top: var(--sp-2); font-size: 11.5px; color: var(--text-faint); }

/* Section cards */
.section-card {
    display: flex;
    flex-direction: column;
    min-height: 216px;             /* uniform card height in the grid */
    background: var(--bg-surface);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-card);
    transition: background-color var(--t-base);
}

.section-card:hover { background: var(--bg-surface-hover); }

.section-card-body { flex: 1 1 auto; padding: var(--sp-5); }

.section-card-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-bottom: var(--sp-4);
}

.section-name { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; }
.section-prof { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

.section-metrics {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-3);
    margin-bottom: var(--sp-4);
}

.metric-label { font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); }
.metric-value { font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; margin-top: 2px; }

.section-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    min-height: 56px;
    padding: 0 var(--sp-5);
}

/* Avatar stack with fixed slot widths */
.avatar-stack { display: flex; align-items: center; }
.avatar-stack .avatar { width: 26px; height: 26px; flex: 0 0 26px; font-size: 10px; border-radius: 50%; background: var(--bg-surface-hi); }
.avatar-stack .avatar + .avatar { margin-left: -8px; }
.avatar-stack .avatar-more { color: var(--text-muted); }

/* Stage gate cards (System Controls) */
.gate {
    display: flex;
    flex-direction: column;
    min-height: 268px;
    background: var(--bg-surface);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-card);
}

.gate-body { flex: 1 1 auto; padding: var(--sp-5); }
.gate-top { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.gate-title { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.gate-desc { margin-top: var(--sp-2); font-size: 12.5px; color: var(--text-muted); line-height: 1.55; }

.gate-control {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    min-height: 60px;
    margin-top: var(--sp-4);
    padding: 0 var(--sp-4);
    border-radius: var(--r-md);
    background: var(--bg-field);
}

.gate-control-label { font-size: 13px; font-weight: 600; }
.gate-control-sub { font-size: 11.5px; color: var(--text-muted); }

.gate-foot {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-5);
}

/* Notice / policy block */
.notice {
    display: flex;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border-radius: var(--r-md);
    background: var(--bg-field);
}

.notice-accent { background: var(--accent-soft); }
.notice .icon { color: var(--accent); margin-top: 1px; }
.notice-title { font-size: 13px; font-weight: 700; }
.notice-text { margin-top: var(--sp-1); font-size: 12.5px; color: var(--text-muted); line-height: 1.6; }

/* Device rows */
.device-row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: 64px;
    padding: 0 var(--sp-4);
    border-radius: var(--r-md);
    background: var(--bg-field);
}

.device-row + .device-row { margin-top: var(--sp-2); }
.device-text { flex: 1 1 auto; min-width: 0; }
.device-name { font-size: 13px; font-weight: 600; }
.device-meta { font-size: 11.5px; color: var(--text-muted); }

/* Trait meters (drawer) */
.trait + .trait { margin-top: var(--sp-4); }
.trait-top { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: var(--sp-2); }
.trait-name { font-size: 12.5px; font-weight: 600; }
.trait-val { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- 12. DRAWER */
.drawer-overlay { justify-content: flex-end; padding: 0; }

.drawer {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 460px;
    height: 100%;
    background: var(--bg-surface);
    box-shadow: var(--shadow-lg);
    transform: translateX(16px);
    opacity: 0;
    transition: transform var(--t-layout), opacity var(--t-base);
}

.overlay.is-open .drawer { transform: translateX(0); opacity: 1; }

.drawer-head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    flex: 0 0 auto;
    min-height: var(--topbar-h);
    padding: var(--sp-3) var(--sp-4) var(--sp-3) var(--sp-5);
}

.drawer-body { flex: 1 1 auto; overflow-y: auto; padding: var(--sp-5); }

.drawer-identity {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    padding-bottom: var(--sp-5);
}

.drawer-avatar {
    width: 56px;
    height: 56px;
    flex: 0 0 56px;
    border-radius: var(--r-lg);
    background: var(--bg-field);
    display: grid;
    place-items: center;
    font-size: 18px;
    font-weight: 700;
}

.kv-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }

.kv {
    min-height: 66px;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-md);
    background: var(--bg-field);
}

.kv-label { font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); }
.kv-value { margin-top: var(--sp-1); font-size: 14px; font-weight: 600; }
/* ============================================================================
   16. BACKEND-WIRED STATES
   Added when the console was connected to Supabase: boot gate, error banner,
   skeleton rows, drawer action list and the score-encoding grid.
   ========================================================================== */

/* ---- 16.1 Boot gate ----
   The shell stays hidden until the server confirms role = 'admin'. Using
   visibility (not display) keeps the layout measured, so nothing reflows at
   the moment the dashboard is revealed. */
body[data-boot="pending"] .app,
body[data-boot="pending"] .toast-stack { visibility: hidden; }

.boot-veil {
    position: fixed;
    inset: 0;
    z-index: var(--z-boot);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--sp-4);
    background: var(--bg-body);
    opacity: 1;
    transition: opacity var(--t-base);
}

body:not([data-boot="pending"]) .boot-veil { opacity: 0; pointer-events: none; }
body:not([data-boot="pending"]) .boot-veil[hidden] { display: none; }

.spinner {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2px solid var(--track);
    border-top-color: var(--accent);
    animation: spin 700ms linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

.boot-text { font-size: 13px; color: var(--text-muted); }

/* ---- 16.2 Global error banner ----
   Fixed overlay, so showing it never pushes the topbar or content down. */
.error-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: var(--z-banner);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-3);
    min-height: 44px;
    padding: var(--sp-2) var(--sp-5);
    background: var(--danger);
    color: var(--text-on-solid);
    font-size: 12.5px;
    font-weight: 600;
    text-align: center;
}

.error-banner .icon { width: 16px; height: 16px; }
.error-banner[hidden] { display: none; }

/* ---- 16.3 Skeleton rows ----
   A loading row is exactly as tall as a real row, so the table does not
   resize when live data replaces the placeholder. */
.skeleton-line { height: 10px; border-radius: var(--r-pill); }
.skeleton-line + .skeleton-line { margin-top: 6px; }
.skeleton-pill { height: 24px; width: 88px; border-radius: var(--r-pill); }
.skeleton-avatar { width: 32px; height: 32px; border-radius: var(--r-sm); flex: 0 0 32px; }

/* Numeric placeholder keeps the tile height identical to a loaded value. */
.stat-value .skeleton-line { width: 56px; height: 22px; margin: 7px 0; }

/* ---- 16.4 Action list (student drawer) ---- */
.action-list {
    border-radius: var(--r-md);
    background: var(--bg-field);
    overflow: hidden;
}

.action-list-item {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    width: 100%;
    min-height: 48px;
    padding: 0 var(--sp-4);
    text-align: left;
    font-size: 13px;
    font-weight: 500;
    color: var(--text);
    transition: background-color var(--t-base), color var(--t-base);
}

.action-list-item:hover { background: var(--bg-surface-hover); }
.action-list-item .icon { width: 16px; height: 16px; color: var(--text-muted); }
.action-list-item:hover .icon { color: var(--text); }
.action-list-item.is-danger { color: var(--danger); }
.action-list-item.is-danger .icon { color: var(--danger); }
.action-list-item.is-danger:hover { background: var(--danger-soft); }
.action-list-item:disabled { opacity: 0.45; cursor: not-allowed; }

/* ---- 16.5 Score encoding grid ---- */
.score-input {
    height: 34px;
    width: 96px;
    padding: 0 var(--sp-3);
    font-size: 13px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* ---- 16.6 Notice modal body text ----
   `pre-wrap` so multi-line RPC results (granted / skipped lists) keep their
   line breaks without any HTML in the message string. */
.notice-pre {
    max-height: 260px;
    overflow-y: auto;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-md);
    background: var(--bg-field);
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--text-muted);
    white-space: pre-wrap;
    text-align: left;
}

/* ---- 16.7 Inline busy state for buttons ----
   Width is preserved while the label swaps, so toolbars never jump. */
.btn.is-busy { pointer-events: none; opacity: 0.7; }

/* ---- 16.8 Live duration cell ---- */
.duration-cell { font-variant-numeric: tabular-nums; color: var(--text-muted); }

/* ============================================================================
   17. AVATAR PRESENCE DOT
   A live indicator attached to the bottom-right of an avatar.
   ----------------------------------------------------------------------------
   The dot needs to read as separate from the avatar beneath it, which is
   normally done with a border. Instead the "ring" is a box-shadow painted in
   the SURROUNDING surface's own colour: it punches a gap out of the avatar
   rather than drawing an outline. Rows that change their background override
   the ring colour so the gap always matches what is behind it.
   ========================================================================== */
.avatar-wrap {
    position: relative;
    flex: 0 0 auto;
    display: inline-flex;
}

.avatar-dot {
    position: absolute;
    right: -2px;
    bottom: -2px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--track);                       /* offline: muted graphite */
    box-shadow: 0 0 0 2.5px var(--bg-surface);      /* the gap, not a border */
    transition: background-color var(--t-base), box-shadow var(--t-base);
}

.avatar-dot.is-online {
    background: var(--accent);
}

/* Keep the gap matching whatever sits behind the avatar. */
.table tbody tr:hover .avatar-dot,
.pick-row:hover .avatar-dot { box-shadow: 0 0 0 2.5px var(--bg-surface-hover); }
.modal .avatar-dot,
.drawer .avatar-dot { box-shadow: 0 0 0 2.5px var(--bg-surface); }

/* Larger variant for the 56px drawer avatar. */
.drawer-avatar-wrap .avatar-dot {
    width: 15px;
    height: 15px;
    right: -1px;
    bottom: -1px;
    box-shadow: 0 0 0 3px var(--bg-surface);
}

/* Optional soft halo so an online student is findable at a glance in a long
   roster. Kept to a glow — it never resolves into a ring. */
.avatar-dot.is-online::after {
    content: "";
    position: absolute;
    inset: -3px;
    border-radius: 50%;
    background: var(--accent-soft);
}
