/* =============================================================
   INSITE — Shared UI Component Layer
   -------------------------------------------------------------
   One place for the patterns every portal was re-inventing inline:
   page headers, empty states, status badges, alerts, buttons,
   tables, modals and toasts.

   Rules this file follows, so that it keeps composing cleanly:
     - Colour only ever comes from the shadcn HSL tokens declared
       in style.css (--background, --card, --primary, --muted ...).
       No raw hex. That is what makes dark mode free.
     - Single-class selectors, so page-level Tailwind utilities can
       still override a component without anyone reaching for
       !important.
     - Spacing steps on a 4px grid.

   Pair with: includes/ui/ui.php (markup) and assets/js/ui.js
   (modal / confirm / toast behaviour).
   ============================================================= */

/* ── Layout scale ───────────────────────────────────────────── */
:root {
    --ui-gap-xs: 4px;
    --ui-gap-sm: 8px;
    --ui-gap:    16px;
    --ui-gap-lg: 24px;
    --ui-gap-xl: 32px;
}

/* ============================================================
   Page header
   A single rhythm for "where am I / what can I do here".
   ============================================================ */
.ui-page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--ui-gap);
    flex-wrap: wrap;
    padding-bottom: var(--ui-gap);
    margin-bottom: var(--ui-gap-lg);
    border-bottom: 1px solid hsl(var(--border));
}
.ui-page-header__text { min-width: 0; }
.ui-page-header__eyebrow {
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: hsl(var(--muted-foreground));
    margin-bottom: 6px;
}
.ui-page-header__title {
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -.022em;
    line-height: 1.25;
    color: hsl(var(--foreground));
    margin: 0;
}
/* The subtitle carries the explanation, so the title never has to
   become a sentence to be understood. */
.ui-page-header__subtitle {
    margin: 6px 0 0;
    font-size: .875rem;
    line-height: 1.55;
    color: hsl(var(--muted-foreground));
    max-width: 68ch;
}
.ui-page-header__actions {
    display: flex;
    align-items: center;
    gap: var(--ui-gap-sm);
    flex-shrink: 0;
    flex-wrap: wrap;
}

/* ============================================================
   Buttons
   Exactly one primary per view; everything else recedes.
   ============================================================ */
.ui-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ui-gap-sm);
    padding: 9px 16px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-size: .875rem;
    font-weight: 500;
    line-height: 1.2;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
    transition: background-color .15s ease, border-color .15s ease,
                color .15s ease, opacity .15s ease;
}
.ui-btn:disabled,
.ui-btn[aria-disabled="true"] {
    opacity: .5;
    cursor: not-allowed;
}
.ui-btn svg { width: 16px; height: 16px; flex-shrink: 0; }

.ui-btn--primary {
    background-color: hsl(var(--primary));
    color: hsl(var(--primary-foreground));
}
.ui-btn--primary:hover:not(:disabled) { background-color: hsl(219 100% 60%); }

.ui-btn--secondary {
    background-color: hsl(var(--card));
    border-color: hsl(var(--border));
    color: hsl(var(--foreground));
}
.ui-btn--secondary:hover:not(:disabled) { background-color: hsl(var(--muted) / .6); }

/* Ghost — for tertiary actions that should not add visual weight */
.ui-btn--ghost {
    background-color: transparent;
    color: hsl(var(--muted-foreground));
}
.ui-btn--ghost:hover:not(:disabled) {
    background-color: hsl(var(--muted) / .6);
    color: hsl(var(--foreground));
}

/* Destructive stays quiet until the pointer is on it. A button that
   is red at rest stops reading as a warning. */
.ui-btn--danger {
    background-color: hsl(var(--destructive) / .1);
    border-color: hsl(var(--destructive) / .25);
    color: hsl(var(--destructive));
}
.ui-btn--danger:hover:not(:disabled) {
    background-color: hsl(var(--destructive));
    color: hsl(var(--destructive-foreground));
    border-color: hsl(var(--destructive));
}
/* Use only as the confirming button inside a destructive dialog,
   where the commitment has already been explained. */
.ui-btn--danger-solid {
    background-color: hsl(var(--destructive));
    color: hsl(var(--destructive-foreground));
}
.ui-btn--danger-solid:hover:not(:disabled) { background-color: hsl(0 84% 52%); }

.ui-btn--sm { padding: 6px 12px; font-size: .8125rem; }
.ui-btn--lg { padding: 12px 22px; font-size: .9375rem; }
.ui-btn--block { width: 100%; }

/* ============================================================
   Status badges
   Tone is derived from meaning, never chosen per page — so
   "Approved" is the same green everywhere in the system.
   ============================================================ */
.ui-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 500;
    line-height: 1.4;
    white-space: nowrap;
    border: 1px solid transparent;
}
.ui-badge__dot {
    width: 6px;
    height: 6px;
    border-radius: 999px;
    background-color: currentColor;
    flex-shrink: 0;
}
.ui-badge--neutral {
    background-color: hsl(var(--muted) / .7);
    border-color: hsl(var(--border));
    color: hsl(var(--muted-foreground));
}
.ui-badge--success {
    background-color: hsl(var(--success) / .12);
    border-color: hsl(var(--success) / .25);
    color: hsl(160 84% 30%);
}
.dark .ui-badge--success { color: hsl(160 70% 55%); }
.ui-badge--warning {
    background-color: hsl(var(--warning) / .14);
    border-color: hsl(var(--warning) / .28);
    color: hsl(30 90% 36%);
}
.dark .ui-badge--warning { color: hsl(38 92% 62%); }
.ui-badge--danger {
    background-color: hsl(var(--destructive) / .12);
    border-color: hsl(var(--destructive) / .25);
    color: hsl(0 72% 46%);
}
.dark .ui-badge--danger { color: hsl(0 90% 72%); }
.ui-badge--info {
    background-color: hsl(var(--info) / .12);
    border-color: hsl(var(--info) / .25);
    color: hsl(192 85% 30%);
}
.dark .ui-badge--info { color: hsl(189 90% 60%); }
.ui-badge--brand {
    background-color: hsl(var(--primary) / .12);
    border-color: hsl(var(--primary) / .25);
    color: hsl(219 70% 45%);
}
.dark .ui-badge--brand { color: hsl(219 100% 74%); }

/* Skill chips — shared by posting editors, boards and match explanations. */
.ui-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 26px;
    padding: 3px 10px;
    border: 1px solid hsl(var(--primary) / .2);
    border-radius: 999px;
    background-color: hsl(var(--primary) / .12);
    color: hsl(219 70% 45%);
    font-size: .75rem;
    font-weight: 600;
    line-height: 1.35;
    white-space: nowrap;
}
.dark .ui-chip { color: hsl(219 100% 74%); }
.ui-chip--match {
    background-color: hsl(var(--primary) / .14);
    border-color: hsl(var(--primary) / .28);
}
.ui-chip--missing {
    background-color: hsl(var(--muted) / .7);
    border-color: hsl(var(--border));
    color: hsl(var(--muted-foreground));
}
.ui-chip--add {
    background-color: transparent;
    border-style: dashed;
    color: hsl(var(--primary));
}
.ui-chip__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    margin: -1px -5px -1px 0;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: currentColor;
    font: inherit;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}
.ui-chip__btn:hover { background-color: hsl(var(--primary) / .16); }
.ui-chip__btn svg { width: 13px; height: 13px; }

/* ============================================================
   Empty state
   The most under-used surface in the app: it is the one place a
   first-time user is guaranteed to read. Say what belongs here
   and how it gets here.
   ============================================================ */
.ui-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 48px 24px;
    border-radius: var(--radius-lg);
}
.ui-empty--boxed {
    border: 1px dashed hsl(var(--border));
    background-color: hsl(var(--muted) / .25);
}
.ui-empty--sm { padding: 28px 20px; }
.ui-empty__icon {
    width: 44px;
    height: 44px;
    border-radius: 999px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: hsl(var(--muted) / .8);
    color: hsl(var(--muted-foreground));
    margin-bottom: 14px;
}
.ui-empty__icon svg { width: 20px; height: 20px; }
.ui-empty__title {
    font-size: .9375rem;
    font-weight: 600;
    color: hsl(var(--foreground));
    margin: 0 0 4px;
}
.ui-empty__body {
    font-size: .8125rem;
    line-height: 1.6;
    color: hsl(var(--muted-foreground));
    margin: 0;
    max-width: 46ch;
}
.ui-empty__actions {
    display: flex;
    gap: var(--ui-gap-sm);
    margin-top: var(--ui-gap);
    flex-wrap: wrap;
    justify-content: center;
}

/* Legacy class bridge
   Pages predating this file mark empty regions with .empty-state or
   .no-data and then restyle them locally. Giving both names the
   shared treatment means those pages line up with the rest of the
   system as soon as their local override is deleted, without having
   to be rewritten first. */
.empty-state,
p.no-data,
.no-data {
    text-align: center;
    padding: 32px 20px;
    margin: 0;
    font-size: .8125rem;
    line-height: 1.6;
    color: hsl(var(--muted-foreground));
}
.empty-state strong {
    display: block;
    font-size: .9375rem;
    font-weight: 600;
    color: hsl(var(--foreground));
    margin-bottom: 4px;
}
.empty-state svg {
    color: hsl(var(--muted-foreground));
    opacity: .5;
    margin-bottom: 10px;
}

/* ============================================================
   Alerts — inline, contextual messages
   ============================================================ */
.ui-alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-size: .8125rem;
    line-height: 1.55;
    margin-bottom: var(--ui-gap);
}
.ui-alert__icon { flex-shrink: 0; margin-top: 1px; }
.ui-alert__icon svg { width: 16px; height: 16px; }
.ui-alert__title { font-weight: 600; margin: 0 0 2px; }
.ui-alert__body  { margin: 0; opacity: .92; }
.ui-alert--success { background-color: hsl(var(--success) / .1);     border-color: hsl(var(--success) / .22);     color: hsl(160 84% 26%); }
.ui-alert--warning { background-color: hsl(var(--warning) / .12);    border-color: hsl(var(--warning) / .26);    color: hsl(30 90% 32%); }
.ui-alert--danger  { background-color: hsl(var(--destructive) / .1); border-color: hsl(var(--destructive) / .22); color: hsl(0 72% 42%); }
.ui-alert--info    { background-color: hsl(var(--info) / .1);        border-color: hsl(var(--info) / .22);        color: hsl(192 85% 26%); }
.dark .ui-alert--success { color: hsl(160 70% 62%); }
.dark .ui-alert--warning { color: hsl(38 92% 66%); }
.dark .ui-alert--danger  { color: hsl(0 90% 74%); }
.dark .ui-alert--info    { color: hsl(189 90% 64%); }

/* ============================================================
   Cards & stat tiles
   ============================================================ */
.ui-card {
    background-color: hsl(var(--card));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius-lg);
    padding: var(--ui-gap-lg);
}
.ui-card__title {
    font-size: .9375rem;
    font-weight: 600;
    color: hsl(var(--foreground));
    margin: 0 0 2px;
}
.ui-card__subtitle {
    font-size: .8125rem;
    color: hsl(var(--muted-foreground));
    margin: 0 0 var(--ui-gap);
}

.ui-stat {
    background-color: hsl(var(--card));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius-lg);
    padding: 18px 20px;
}
/* Label above value: the label is read once, the number is scanned
   repeatedly, so the number gets the visual weight. */
.ui-stat__label {
    font-size: .75rem;
    font-weight: 500;
    color: hsl(var(--muted-foreground));
    margin: 0 0 6px;
}
.ui-stat__value {
    font-size: 1.75rem;
    font-weight: 600;
    letter-spacing: -.02em;
    line-height: 1.1;
    color: hsl(var(--foreground));
    font-variant-numeric: tabular-nums;
}
.ui-stat__meta {
    font-size: .75rem;
    color: hsl(var(--muted-foreground));
    margin-top: 6px;
}

/* ============================================================
   Tables
   ============================================================ */
.ui-table-wrap {
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius-lg);
    overflow: hidden;
    background-color: hsl(var(--card));
}
.ui-table { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.ui-table thead th {
    text-align: left;
    padding: 10px 16px;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: hsl(var(--muted-foreground));
    background-color: hsl(var(--muted) / .45);
    border-bottom: 1px solid hsl(var(--border));
    white-space: nowrap;
}
.ui-table tbody td {
    padding: 12px 16px;
    border-bottom: 1px solid hsl(var(--border) / .6);
    color: hsl(var(--foreground));
    vertical-align: middle;
}
.ui-table tbody tr:last-child td { border-bottom: none; }
.ui-table tbody tr:hover td { background-color: hsl(var(--muted) / .3); }
/* Numbers line up when they share a tabular figure and right edge */
.ui-table .ui-num { text-align: right; font-variant-numeric: tabular-nums; }

/* ============================================================
   Modal / dialog — replaces native alert() and confirm()
   ============================================================ */
.ui-modal-backdrop {
    position: fixed;
    inset: 0;
    background-color: hsl(232 59% 6% / .55);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    z-index: 9000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    opacity: 0;
    transition: opacity .16s ease;
}
.ui-modal-backdrop.is-open { opacity: 1; }
.ui-modal {
    background-color: hsl(var(--card));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    width: 100%;
    max-width: 420px;
    overflow: hidden;
    transform: translateY(6px) scale(.99);
    transition: transform .16s ease;
}
.ui-modal-backdrop.is-open .ui-modal { transform: none; }
.ui-modal--wide { max-width: 640px; }
.ui-modal__header {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 20px 20px 0;
}
.ui-modal__icon {
    width: 36px;
    height: 36px;
    border-radius: 999px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.ui-modal__icon svg { width: 18px; height: 18px; }
.ui-modal__icon--danger  { background-color: hsl(var(--destructive) / .12); color: hsl(var(--destructive)); }
.ui-modal__icon--warning { background-color: hsl(var(--warning) / .14);     color: hsl(var(--warning)); }
.ui-modal__icon--info    { background-color: hsl(var(--primary) / .12);     color: hsl(var(--primary)); }
.ui-modal__icon--success { background-color: hsl(var(--success) / .12);     color: hsl(var(--success)); }
.ui-modal__title {
    font-size: 1rem;
    font-weight: 600;
    color: hsl(var(--foreground));
    margin: 0;
    line-height: 1.35;
}
.ui-modal__body {
    padding: 8px 20px 0 68px;
    font-size: .8125rem;
    line-height: 1.6;
    color: hsl(var(--muted-foreground));
}
.ui-modal__body--full { padding-left: 20px; }
.ui-modal__footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--ui-gap-sm);
    padding: 20px;
}
@media (max-width: 420px) {
    .ui-modal__body { padding-left: 20px; }
    /* Stack in reverse so the confirming action is not under the thumb
       by accident on a narrow screen. */
    .ui-modal__footer { flex-direction: column-reverse; }
    .ui-modal__footer .ui-btn { width: 100%; }
}

/* ============================================================
   Toasts
   ============================================================ */
.ui-toast-region {
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9500;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ui-gap-sm);
    pointer-events: none;
    width: max-content;
    max-width: calc(100vw - 32px);
}
.ui-toast {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 11px 16px;
    border-radius: var(--radius-sm);
    background-color: hsl(var(--card));
    border: 1px solid hsl(var(--border));
    box-shadow: var(--shadow-lg);
    font-size: .8125rem;
    line-height: 1.5;
    color: hsl(var(--foreground));
    pointer-events: auto;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity .18s ease, transform .18s ease;
}
.ui-toast.is-open { opacity: 1; transform: none; }
.ui-toast__icon { flex-shrink: 0; margin-top: 1px; }
.ui-toast__icon svg { width: 16px; height: 16px; }
.ui-toast--success .ui-toast__icon { color: hsl(var(--success)); }
.ui-toast--danger  .ui-toast__icon { color: hsl(var(--destructive)); }
.ui-toast--warning .ui-toast__icon { color: hsl(var(--warning)); }
.ui-toast--info    .ui-toast__icon { color: hsl(var(--primary)); }
/* A coloured left edge carries the tone without tinting the text,
   which keeps contrast predictable in both themes. */
.ui-toast--success { border-left: 3px solid hsl(var(--success)); }
.ui-toast--danger  { border-left: 3px solid hsl(var(--destructive)); }
.ui-toast--warning { border-left: 3px solid hsl(var(--warning)); }
.ui-toast--info    { border-left: 3px solid hsl(var(--primary)); }
@media (min-width: 640px) {
    .ui-toast-region { bottom: 24px; right: 24px; left: auto; transform: none; align-items: flex-end; }
}
/* The student portal has a fixed bottom nav — lift clear of it */
body.student-dashboard .ui-toast-region { bottom: 84px; }

/* ============================================================
   Utilities
   ============================================================ */
.ui-stack   { display: flex; flex-direction: column; gap: var(--ui-gap); }
.ui-row     { display: flex; align-items: center; gap: var(--ui-gap-sm); flex-wrap: wrap; }
.ui-spacer  { flex: 1 1 auto; }
.ui-muted   { color: hsl(var(--muted-foreground)); }
.ui-nowrap  { white-space: nowrap; }
/* Visible only to screen readers — for labels an icon-only control needs */
.ui-sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

@media (prefers-reduced-motion: reduce) {
    .ui-btn, .ui-modal, .ui-modal-backdrop, .ui-toast {
        transition: none;
    }
}
