/* Nova app.css — general styling (code rule 1).
   Area styles go in their own file, e.g. volunteers.css. */

/* ---------- fonts ---------- Jost for display text, Inter for body/tables/forms; self-hosted variable woff2 keeps CSP at default-src 'self'. */

@font-face {
    font-family: 'Jost';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url(../fonts/jost-var.woff2) format('woff2');
}

@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url(../fonts/inter-var.woff2) format('woff2');
}

/* Palette taken from 2wish.org.uk: the blue range (900 dark -> 50 pale) is THE colour system, pick from it rather than inventing new hexes. */
:root {
    --sidebar-w: 240px;

    --c-blue-900: #0e2438;   /* deep navy (sidebar hover, darkest badge) */
    --c-blue-700: #2f6395;   /* strong blue (links, emphasis) */
    --c-blue-500: #5b93c2;   /* mid blue (primary hover) */
    --c-blue-300: #78add3;   /* the 2wish brand blue (primary) */
    --c-blue-100: #d7e5f1;   /* pale tint (accents, light badges) */
    --c-blue-50:  #eef5fa;   /* palest wash (row hover, filter bar) */

    --c-bg: #f2f6fa;
    --c-surface: #ffffff;
    --c-border: #e0e9f2;
    --c-text: #54565a;
    --c-heading: #37393e;
    --c-muted: #85888e;

    --c-primary: var(--c-blue-300);
    --c-primary-dark: var(--c-blue-500);
    --c-primary-tint: var(--c-blue-100);

    --c-sidebar: #041223;
    --c-sidebar-hover: var(--c-blue-900);
    --c-sidebar-text: var(--c-blue-100);
    --c-sidebar-muted: #8fa5bd;

    --c-success-bg: #ecfdf5;
    --c-success-text: #065f46;
    --c-error-bg: #fef2f2;
    --c-error-text: #991b1b;

    --radius: 10px;
    --shadow: 0 2px 4px rgba(4, 18, 35, .07);

    --font-display: 'Jost', system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-body: 'Inter', system-ui, -apple-system, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: var(--font-body);
    font-size: 15px;
    line-height: 1.5;
    color: var(--c-text);
    background: var(--c-bg);
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-family: var(--font-display); font-weight: 600; letter-spacing: 1px; }
h1 { font-size: 25px; color: var(--c-heading); margin: 0 0 15px; }
h2 { font-size: 20px; color: var(--c-heading); margin: 0 0 10px; }
a { color: var(--c-blue-700); }
.muted { color: var(--c-muted); }

/* Visually hidden but still read by screen readers — for a label an icon/select already conveys visually. */
.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;
}

:focus-visible { outline: 1px solid var(--c-primary); outline-offset: 2px; }

/* ---------- shell layout ---------- */

.shell { display: flex; min-height: 100vh; }

.main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.content { flex: 1; padding: 25px; }

/* ---------- sidebar ---------- */

.sidebar {
    width: var(--sidebar-w);
    flex-shrink: 0;
    /* barely-there gradient keeps the long navy column from looking flat */
    background: linear-gradient(180deg, var(--c-sidebar), #082036);
    color: var(--c-sidebar-text);
    padding-bottom: 15px;
    /* never taller than the viewport: brand + user block stay put, only .sidebar-nav scrolls */
    position: sticky;
    top: 0;
    /* lifts the rail above .main (DOM order would otherwise let content paint over it), below the notification panel (50), dialogs (100), toasts (1000) */
    z-index: 40;
    height: 100vh;
    display: flex;
    flex-direction: column;
}

/* the scrollable middle: menu sections only */
.sidebar-nav {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    scrollbar-width: thin;                        /* Firefox */
    scrollbar-color: var(--c-blue-100) transparent;
}

.sidebar-nav::-webkit-scrollbar { width: 8px; }

.sidebar-nav::-webkit-scrollbar-track { background: transparent; }

.sidebar-nav::-webkit-scrollbar-thumb {
    background: var(--c-blue-100);
    border-radius: 4px;
}

/* signed-in user + sign out, pinned to the sidebar's bottom edge */
.sidebar-user {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 20px 0;
    border-top: 1px solid rgba(255, 255, 255, .08);
}

.sidebar-user-name {
    font-size: 14px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
}

.sidebar-user button {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: 0;
    padding: 4px 0;
    font: inherit;
    font-size: 14px;
    color: var(--c-sidebar-muted);
    cursor: pointer;
    white-space: nowrap;
}

.sidebar-user button .icon { flex-shrink: 0; }

.sidebar-user button:hover {
    color: #fff;
}

/* The logo is dark grey + blue on transparency, so on the dark sidebar it sits on a white rounded chip. */
.sidebar-brand { padding: 15px 20px 10px; }

.sidebar-brand img {
    display: block;
    width: 100%;
    max-width: 100%;
    height: auto;
    background: #fff;
    border-radius: var(--radius);
    padding: 10px 15px;
}

/* small square logo shown only in slim mode, in place of the wide brand mark */
.sidebar-brand-slim { display: none; padding: 15px 12px 10px; }

.sidebar-brand-slim img {
    display: block;
    width: 100%;
    max-width: 40px;
    height: auto;
    margin: 0 auto;
    background: #fff;
    border-radius: var(--radius);
    padding: 6px;
}

.nav-section-title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    background: none;
    border: 0;
    color: var(--c-sidebar-muted);
    font-family: var(--font-display);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 1px;
    text-transform: uppercase;
    padding: 15px 20px 5px;
    cursor: pointer;
}

.nav-section-title .chev { transition: transform .15s; }
.nav-section.collapsed .chev { transform: rotate(-90deg); }

.nav-items {
    list-style: none;
    margin: 0;
    padding: 0;
    opacity: 1;
    overflow: hidden;
    transition: max-height .2s ease, opacity .15s ease;
}
.nav-section.collapsed .nav-items {
    opacity: 0;
}

.nav-items a {
    display: flex;
    align-items: center;
    gap: 10px;
    font-family: var(--font-display);
    font-weight: 500;
    color: var(--c-sidebar-text);
    text-decoration: none;
    padding: 10px 20px;
    border-left: 4px solid transparent;
}

.nav-items a .icon { opacity: .7; }

.nav-items a:hover { background: var(--c-sidebar-hover); color: #fff; }
.nav-items a.active {
    background: var(--c-sidebar-hover);
    color: #fff;
    border-left-color: var(--c-primary);
}
.nav-items a:hover .icon, .nav-items a.active .icon { opacity: 1; }

/* inline SVG icons (icon() helper): sized by font, coloured by currentColor */
.icon { width: 15px; height: 15px; flex-shrink: 0; }

/* ---------- sidebar: slim mode ---------- Toggled from the topbar's ☰ button (localStorage nav-slim); narrows to icons only, labels become floating tooltips via app.js. */
.sidebar { transition: width .2s ease; }
html.nav-slim .sidebar { width: 64px; }
html.nav-slim .sidebar-brand,
html.nav-slim .nav-section-title,
html.nav-slim .sidebar-user-name { display: none; }
html.nav-slim .sidebar-brand-slim { display: block; }
html.nav-slim .nav-items a { justify-content: center; padding: 10px; border-left: none; position: relative; }
/* section titles are hidden in slim mode, so always show items regardless of .collapsed; !important beats the inline max-height from app.js's animation */
html.nav-slim .nav-section.collapsed .nav-items {
    max-height: none !important;
    opacity: 1;
}
/* Stacked, icon-only: avatar + text button side by side would spill past the 64px rail's edge. */
html.nav-slim .sidebar-user {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 12px 0;
}
/* rule between sections, skipped on the first (has the brand logo); only shown in slim mode, since expanded mode has title text as divider */
html.nav-slim .nav-section + .nav-section { border-top: 1px solid rgba(255, 255, 255, .3); }

/* the tooltip: reuses .nav-label, position:fixed so it isn't clipped by .sidebar-nav's overflow; app.js sets top/left from the link's bounding rect */
html.nav-slim .nav-label {
    position: fixed;
    transform: translateY(-50%) translateX(6px);
    margin-left: 8px;
    padding: 6px 10px;
    background: var(--c-blue-900);
    color: #fff;
    font-size: 13px;
    font-weight: 500;
    letter-spacing: normal;
    text-transform: none;
    white-space: nowrap;
    border-radius: 6px;
    box-shadow: var(--shadow);
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity .12s, transform .12s;
    z-index: 30;
}
html.nav-slim .nav-items a:hover .nav-label,
html.nav-slim .nav-items a:focus-visible .nav-label,
html.nav-slim .sidebar-user button:hover .nav-label,
html.nav-slim .sidebar-user button:focus-visible .nav-label {
    opacity: 1;
    visibility: visible;
    transform: translateY(-50%) translateX(0);
}
html.nav-slim .sidebar-user button { padding: 4px; }

/* ---------- topbar & footer ---------- */

.topbar {
    display: flex;
    align-items: center;
    gap: 15px;
    background: var(--c-surface);
    border-bottom: 1px solid var(--c-border);
    padding: 10px 25px;
}

.topbar-spacer { flex: 1; }

.clock-status-pill {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 14px;
    border-radius: 999px;
    border: 1px solid transparent;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.clock-status-pill.is-clocked-in {
    background: var(--c-success-bg);
    color: var(--c-success-text);
}

.clock-status-pill.is-clocked-out {
    background: var(--c-error-bg);
    color: var(--c-error-text);
}

.clock-status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: currentColor;
}


.footer {
    padding: 10px 25px;
    color: var(--c-muted);
    font-size: 15px;
    border-top: 1px solid var(--c-border);
}

/* ---------- topbar notifications ---------- */

.notif {
    position: relative;
}

.notif-bell {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.notif-bell svg {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.notif-count {
    position: absolute;
    top: -6px;
    right: -8px;
    min-width: 17px;
    padding: 1px 4px;
    border-radius: 9px;
    background: var(--c-blue-700);
    color: #fff;
    font-family: var(--font-display);
    font-size: 11px;
    font-weight: 600;
    line-height: 15px;
    text-align: center;
}

.notif-panel {
    position: absolute;
    top: calc(100% + 10px);
    right: -10px;
    width: 340px;
    max-width: 90vw;
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    box-shadow: 0 8px 24px rgba(14, 36, 56, .12);
    z-index: 50;
}

.notif-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 15px;
    border-bottom: 1px solid var(--c-border);
}

.notif-empty {
    margin: 0;
    padding: 15px;
    font-size: 14px;
}

.notif-list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 320px;
    overflow-y: auto;
}

.notif-list li + li {
    border-top: 1px solid var(--c-border);
}

.notif-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    width: 100%;
    padding: 9px 15px;
    border: 0;
    background: none;
    text-align: left;
    font: inherit;
    color: var(--c-text);
    cursor: pointer;
}

.notif-item:hover {
    background: var(--c-blue-50);
}

.notif-item.is-unread .notif-message {
    font-weight: 600;
    color: var(--c-heading);
}

.notif-item.is-read {
    color: var(--c-muted);
}

.notif-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--c-blue-50);
    color: var(--c-blue-700);
}

.notif-item.is-read .notif-icon {
    background: var(--c-bg);
    color: var(--c-muted);
}

.notif-icon .icon {
    width: 15px;
    height: 15px;
}

.notif-body {
    min-width: 0;
    flex: 1;
}

.notif-message {
    display: block;
    font-size: 14px;
}

.notif-time {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--c-muted);
}

.notif-foot {
    padding: 8px 15px;
    border-top: 1px solid var(--c-border);
    text-align: center;
    font-size: 14px;
}

/* type icon on the /notifications page — inline before the message text */
table .notif-icon {
    display: inline-flex;
    margin-right: 8px;
    vertical-align: middle;
}

/* unread marker on the /notifications page */
.notif-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    margin-right: 6px;
    border-radius: 50%;
    background: var(--c-blue-700);
    vertical-align: middle;
}

.notif-row-read td {
    color: var(--c-muted);
}

/* ---------- cards & stats ---------- */

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 15px;
    margin-bottom: 15px;
}

.card {
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    padding: 20px;
    box-shadow: var(--shadow);
}

.card.stat { border-left: 4px solid var(--c-primary); }
/* a stat that doubles as a link to its screen */
a.stat-link { text-decoration: none; color: inherit; display: block; }
a.stat-link:hover { border-color: var(--c-blue-100); box-shadow: var(--shadow); }
.stat-value { font-family: var(--font-display); font-size: 30px; font-weight: 600; color: var(--c-blue-700); }
.stat-label { color: var(--c-muted); font-size: 15px; margin-top: 4px; }

/* ---------- forms & buttons ---------- */

label { display: block; font-weight: 600; margin: 15px 0 4px; }

/* :not(checkbox, radio) — those need their native box, not a padded/bordered input shell which would render as an oversized pill-shaped hit-area. */
input:not([type="checkbox"], [type="radio"]), select, textarea {
    width: 100%;
    padding: 10px 10px;
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    font: inherit;
    background: #fff;
}

input:focus, select:focus, textarea:focus {
    outline: 1px solid var(--c-primary);
    outline-offset: 0;
    border-color: var(--c-primary);
}

.btn {
    display: inline-block;
    padding: 10px 20px;
    border: 1px solid transparent;
    border-radius: var(--radius);
    font: inherit;
    font-family: var(--font-display);
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    transition: background .12s, color .12s, border-color .12s, box-shadow .12s, transform .12s;
}

.btn:focus-visible {
    outline: 2px solid var(--c-primary);
    outline-offset: 2px;
}

.btn-primary { background: var(--c-primary); color: #fff; box-shadow: var(--shadow); }
.btn-primary:hover { background: var(--c-primary-dark); box-shadow: 0 4px 10px rgba(4, 18, 35, .16); transform: translateY(-1px); }
.btn-primary:active { background: var(--c-primary-dark); box-shadow: none; transform: translateY(0); }

.btn-ghost { background: none; color: var(--c-muted); border-color: var(--c-border); }
.btn-ghost:hover { color: var(--c-blue-700); border-color: var(--c-blue-500); background: var(--c-blue-50); box-shadow: 0 2px 6px rgba(4, 18, 35, .08); }
.btn-ghost:active { background: var(--c-blue-100); box-shadow: none; }

/* A second action reading as more than a plain ghost link but not the page's primary CTA (never two btn-primary buttons in one header). */
.btn-navy { background: #10416f; color: #fff; box-shadow: var(--shadow); }
.btn-navy:hover { background: var(--c-blue-900); box-shadow: 0 4px 10px rgba(4, 18, 35, .16); transform: translateY(-1px); }
.btn-navy:active { background: var(--c-blue-900); box-shadow: none; transform: translateY(0); }

.btn-block { width: 100%; margin-top: 20px; }

.btn-sm { padding: 4px 10px; font-weight: 600; font-size: 15px; }

.icon-btn {
    background: none;
    border: 0;
    font-size: 20px;
    cursor: pointer;
    color: var(--c-muted);
}

/* Subtle icon-only actions (edit/delete on a row) — no visible border/background until interaction. */
.icon-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: none;
    color: var(--c-muted);
    cursor: pointer;
    transition: background .12s, color .12s, border-color .12s;
}

.icon-action:hover {
    color: var(--c-blue-700);
    background: var(--c-blue-50);
    border-color: var(--c-blue-100);
}

.icon-action:active {
    background: var(--c-blue-100);
}

.icon-action:focus-visible {
    outline: 2px solid var(--c-primary);
    outline-offset: 2px;
}

.icon-action.icon-action-danger:hover {
    color: var(--c-error-text);
    background: var(--c-error-bg);
    border-color: var(--c-error-text);
}

.icon-action svg {
    width: 16px;
    height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.75;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* ---------- page shell (code rule 6) ---------- Every page is one main card: .card-header, optional .card-filters, content, optional .card-footer. Narrow forms add .narrow. */

.page-card { padding: 0; overflow: hidden; }
/* Widened from 640px so a two-column .form-grid form has room to breathe without going full list-page width. */
.page-card.narrow { max-width: 960px; }

.card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 15px;
    padding: 15px 20px;
    background: linear-gradient(to bottom, var(--c-blue-50), var(--c-surface));
    border-bottom: 1px solid var(--c-border);
}

.card-header h1, .card-header h2 { margin: 0; color: var(--c-heading); }

.card-filters {
    display: flex;
    gap: 10px;
    padding: 15px 20px;
    background: var(--c-bg);
    border-bottom: 1px solid var(--c-border);
}

/* Inputs default to width:100%; reset to content-width here so a filter bar stays on one line, search growing to fill the slack. */
.card-filters input, .card-filters select { width: auto; flex: none; }
.card-filters input[type="search"] { flex: 1 1 auto; min-width: 10rem; }

.card-body { padding: 20px; }

.card-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 15px 20px;
    border-top: 1px solid var(--c-border);
    font-size: 15px;
}

.pagination-links { display: flex; gap: 5px; }

/* ---------- form layout ---------- */

.form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 15px;
    margin-bottom: 15px;
}

.form-actions { display: flex; gap: 10px; margin-top: 20px; }

/* helper/hint text under a field or heading — consistent rhythm with label's own margin */
.card-body p, .card-body .muted { margin: 0 0 15px; }

/* a form split into labelled sections: each h2 after the first gets space above it so sections read as distinct */
.card-body h2 { margin: 0 0 10px; }
.card-body h2 ~ h2 { margin-top: 25px; }

/* bordered box around a sub-picker inside a bigger form, so it doesn't visually blend into the fields around it */
.picker-box {
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    padding: 15px;
    background: var(--c-bg);
    margin-bottom: 20px;
}
.picker-box > .toggle-group:first-child,
.picker-box > .picker-selected { margin-top: 0; }
.picker-selected { margin: 0; }

/* Two-way toggle showing one of two panels, pure CSS via #toggle-a/#toggle-b radios inside .toggle-host (rule 5 in the README). */
.toggle-group { display: flex; gap: 10px; margin-bottom: 15px; }
.toggle-group input {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    clip: rect(0, 0, 0, 0);
    overflow: hidden;
}
.toggle-group label { margin: 0; }
.toggle-group input:focus-visible + label { outline: 2px solid var(--c-primary); outline-offset: 2px; }
.toggle-panel-a, .toggle-panel-b { display: none; }
.toggle-host:has(#toggle-a:checked) .toggle-panel-a { display: block; }
.toggle-host:has(#toggle-b:checked) .toggle-panel-b { display: block; }
.toggle-host:has(#toggle-a:checked) label[for="toggle-a"],
.toggle-host:has(#toggle-b:checked) label[for="toggle-b"] { background: var(--c-primary); color: #fff; box-shadow: var(--shadow); }

/* checkboxes opt out of the block-label / full-width-input defaults */
.checkbox-group { display: flex; flex-wrap: wrap; gap: 5px 20px; }

label.checkbox {
    display: flex;
    align-items: center;
    gap: 5px;
    font-weight: 400;
    margin: 0;
}

label.checkbox input { width: auto; }

label.checkbox.checkbox-spaced { margin: 15px 0 5px; }

.badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 1000px;
    font-size: 10px;
    font-weight: 600;
    white-space: nowrap;
}

/* status badges (green/red are reserved for state, not categories) */
.badge-active { background: var(--c-success-bg); color: var(--c-success-text); }
.badge-inactive { background: var(--c-error-bg); color: var(--c-error-text); }
/* amber "needs attention" — same pair as the confirm-dialog icon */
.badge-warn { background: #fffbeb; color: #b45309; }

/* the categorical range: blues dark -> light, plus grey; pick with role_badge_class() so the mapping lives in one place */
.badge-navy  { background: var(--c-blue-900); color: var(--c-blue-100); }
.badge-blue  { background: var(--c-blue-700); color: var(--c-blue-50); }
.badge-steel { background: var(--c-blue-500); color: #fff; }
.badge-sky   { background: var(--c-blue-100); color: var(--c-blue-700); }
.badge-pale  { background: var(--c-blue-50); color: var(--c-blue-500); border: 1px solid var(--c-blue-100); }
.badge-grey  { background: #ececee; color: var(--c-text); }

.badge-list { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }

/* Groups two or more buttons together on one side of a .card-header, so the
   header's own space-between doesn't push them apart from each other. */
.header-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* ---------- shared tooltip (dark bubble on hover/focus) ---------- text carried on data-tooltip, not title=; opens above by default, .has-tip-below flips it under. */
.has-tip {
    position: relative;
    cursor: help;
}
.has-tip::after {
    content: attr(data-tooltip);
    position: absolute;
    left: 50%;
    bottom: calc(100% + 8px);
    transform: translateX(-50%) translateY(4px);
    background: #0e2438;
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .2px;
    text-transform: none;
    white-space: nowrap;
    padding: 6px 10px;
    border-radius: 6px;
    box-shadow: 0 8px 20px rgba(14, 36, 56, .28);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .14s ease, transform .14s ease, visibility .14s ease;
    z-index: 6;
}
.has-tip-below::after {
    top: calc(100% + 8px);
    bottom: auto;
    transform: translateX(-50%) translateY(-4px);
}
.has-tip:hover::after,
.has-tip:focus::after,
.has-tip:focus-visible::after {
    opacity: 1;
    visibility: visible;
}

/* Small inline cross-department flag icon; pairs with .has-tip for the tooltip. Colour comes from a flavour class (overlap_flag() in helpers.php). */
.flag-icon {
    display: inline-flex;
    vertical-align: middle;
}
.flag-icon svg { width: 15px; height: 15px; }
.flag-icon-support { color: #b45309; }
.flag-icon-donor   { color: var(--c-blue-700); }

/* ---------- user avatars (photo, or initials fallback — see avatar() in helpers.php) ---------- */

.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    border-radius: 50%;
    object-fit: cover;
    background: var(--c-blue-100);
    color: var(--c-blue-700);
    font-weight: 600;
    line-height: 1;
}

.avatar-sm { width: 28px; height: 28px; font-size: 11px; }
.avatar-md { width: 48px; height: 48px; font-size: 16px; }
.avatar-lg { width: 96px; height: 96px; font-size: 28px; }

/* ---------- alerts (toasts, corner-positioned, stack top to bottom) ---------- */

.alert-toasts {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-width: 360px;
    pointer-events: none;
}

/* .alert is the shared banner look, used for toasts and plain static banners; the slide-in animation lives on .alert-toasts .alert specifically. */
.alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border-radius: var(--radius);
    border-left: 4px solid currentColor;
}

.alert-toasts .alert {
    box-shadow: 0 4px 16px rgba(0, 0, 0, .16);
    pointer-events: auto;
    opacity: 0;
    transform: translateX(16px);
    transition: opacity .2s, transform .2s;
}

.alert-toasts .alert.is-visible { opacity: 1; transform: translateX(0); }

.alert-message { flex: 1; }

.alert-dismiss {
    background: none;
    border: none;
    padding: 0;
    line-height: 1;
    font-size: 16px;
    cursor: pointer;
    color: inherit;
    opacity: .6;
}
.alert-dismiss:hover { opacity: 1; }

.alert-success { background: var(--c-success-bg); color: var(--c-success-text); }
.alert-error { background: var(--c-error-bg); color: var(--c-error-text); }

/* ---------- dialogs (Nova.alert / Nova.confirm / Nova.prompt) ---------- */

body.dialog-open { overflow: hidden; }

.dialog-overlay {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 15px;
    background: rgba(58, 60, 63, .55);
    opacity: 0;
    transition: opacity .15s;
}

.dialog-overlay.open { opacity: 1; }

.dialog {
    width: min(420px, 100%);
    background: var(--c-surface);
    border-radius: var(--radius);
    box-shadow: 0 20px 50px rgba(58, 60, 63, .35);
    transform: translateY(10px) scale(.97);
    transition: transform .15s;
}

.dialog-overlay.open .dialog { transform: none; }

.dialog-body { padding: 25px 25px 4px; }

.dialog-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    font-size: 20px;
    font-weight: 700;
    margin-bottom: 15px;
}

.dialog-icon-alert,
.dialog-icon-prompt { background: var(--c-primary-tint); color: var(--c-primary-dark); }
.dialog-icon-confirm { background: #fffbeb; color: #b45309; }

.dialog-message {
    margin: 0;
    white-space: pre-line;   /* \n in the message becomes a line break, like native alert() */
    line-height: 1.5;
}

.dialog-body input { margin-top: 15px; }

.dialog-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 20px 25px 25px;
    flex: none; /* inside a .note-form (below), the footer never scrolls with the fields */
}

/* ---------- "note-form" dialog shape ---------- Native <dialog> with header band, scrollable middle, sticky footer; used well beyond notes, so it lives here not in one area's CSS. */
dialog.dialog {
    border: none;
    padding: 0;
    width: min(640px, 94vw);
    max-width: none;
    border-radius: var(--radius);
    overflow: hidden;
}

/* Wider variant for a dialog whose content is a table/list rather than a
   short set of form fields (e.g. the merch vest/shirt tracker). */
dialog.dialog.dialog-xl {
    width: min(1080px, 96vw);
}

dialog.dialog::backdrop {
    background: rgba(58, 60, 63, .55);
}

.note-form {
    display: flex;
    flex-direction: column;
    max-height: 88vh;
}

.note-form .card-header {
    flex: none;
}

.note-form .card-header h2 {
    margin: 0;
    font-size: 20px;
}

.note-form-fields {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 15px;
}

.note-form-meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 15px;
}

.note-form-meta>div {
    min-width: 0;
}

.note-form label:not(.checkbox) {
    display: block;
    font-weight: 600;
    font-size: 15px;
    color: var(--c-muted);
    margin: 0 0 4px;
}

.note-form-hint {
    margin: 5px 0 0;
    font-size: 15px;
}

.note-form .checkbox {
    margin: 0;
}

/* ---------- tables ---------- */

table { width: 100%; border-collapse: collapse; background: var(--c-surface); }
th, td { text-align: left; padding: 10px 20px; border-bottom: 1px solid var(--c-border); }
th {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--c-muted);
    background: #f8fbfd;
}
tbody tr { transition: background .1s; }
tbody tr:hover { background: var(--c-blue-50); }

/* inside the page card the table runs edge-to-edge; the footer's own border-top replaces the last row's line */
.page-card table tr:last-child td { border-bottom: 0; }

/* clickable column headings (server-side sorting) */
th .sort-link { color: inherit; text-decoration: none; }
th .sort-link:hover { color: var(--c-blue-700); }
th .sort-link.active { color: var(--c-blue-700); }
.sort-arrow { font-size: 10px; vertical-align: middle; }

/* A table cell must retain its table-cell display value — flex changes its box height relative to siblings, offsetting row borders. */
td.actions-cell { text-align: right; white-space: nowrap; }
td.actions-cell form { display: inline; margin-left: 5px; }
td.actions-cell > :first-child { margin-left: 0; }

/* Inline district + region edit form (same "no flex on a table cell" rule as .actions-cell above). */
.region-district-row { display: inline; }
.region-district-row select { margin-left: 6px; }

/* ---------- auth pages ---------- */

.auth-body {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    /* Night-sky photo with the 2wish star bottom-right; anchoring to right bottom keeps the logo corner always in view. */
    background: #060d1c url(../img/nova-bg.jpg) right bottom / cover no-repeat;
}

.auth-card {
    width: min(440px, 92vw);
    background: var(--c-surface);
    border-radius: var(--radius);
    padding: 30px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, .45);
}

.auth-brand { margin-bottom: 25px; }

.auth-brand img {
    display: block;
    width: min(240px, 75%);
    max-width: 100%;
    height: auto;
    margin: 0 auto;
}

.auth-tagline {
    font-family: Georgia, "Times New Roman", serif;
    font-style: italic;
    font-size: 15px;
    line-height: 1.55;
    color: var(--c-muted);
    text-align: center;
    text-wrap: balance;   /* splits the quote into evenly-filled lines */
    margin: 0 0 25px;
}

.auth-links {
    text-align: center;
    font-size: 15px;
    margin: 20px 0 0;
}

.twofa-qr { display: flex; justify-content: center; margin: 15px 0; }

.twofa-secret {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 20px;
    letter-spacing: 1px;
    text-align: center;
    background: var(--c-blue-50);
    border-radius: 5px;
    padding: 10px;
    user-select: all;
}

/* ---------- small screens ---------- */

@media (max-width: 800px) {
    /* keeps the desktop rule's z-index (40): as an overlay drawer it must stay above the content it slides over */
    .sidebar { position: fixed; inset: 0 auto 0 0; transform: translateX(-100%); transition: transform .2s, width .2s ease; }
    .sidebar.open { transform: none; }
}

/* short viewports (small laptops): tighten the shell's vertical rhythm instead of shrinking width-based breakpoints */
@media (max-height: 700px) {
    .sidebar-brand { padding: 10px 20px 8px; }
    .sidebar-user { padding: 10px 20px 0; }
    .nav-section-title { padding: 10px 20px 4px; }
    .nav-items a { padding: 7px 20px; }
    .topbar { padding: 8px 25px; }
    .footer { padding: 8px 25px; }
}

/* --- Tabs (shared component, code rule 4: reuse before duplicate) --- Horizontal tab strip, one .tab-panel at a time; [data-tabs] in app.js wires switching, roving tabindex, arrow keys, URL-hash deep link. */
.tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 5px;
    background: var(--c-bg);
    border: 1px solid var(--c-border);
    border-radius: calc(var(--radius) + 4px);
}

.tab {
    appearance: none;
    border: 1px solid transparent;
    background: none;
    padding: 8px 18px;
    font-family: var(--font-display);
    font-size: 15px;
    font-weight: 600;
    color: var(--c-muted);
    cursor: pointer;
    border-radius: var(--radius);
    transition: color .12s, background .12s, box-shadow .12s;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.tab:hover { color: var(--c-heading); background: var(--c-blue-50); }
.tab:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }

.tab.is-active {
    color: var(--c-heading);
    background: var(--c-surface);
    border-color: var(--c-border);
    box-shadow: var(--shadow);
}
.tab.is-active:hover { background: var(--c-surface); }

/* A small count pill inside a tab label (e.g. "Family 4"). */
.tab-count {
    font-size: 12px;
    font-weight: 600;
    min-width: 18px;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--c-blue-50);
    color: var(--c-blue-900);
}
.tab.is-active .tab-count { background: var(--c-blue-100, var(--c-blue-50)); }

.tab-panel { display: none; }
.tab-panel.is-active { display: block; margin-top: 20px; }
