/* ==========================================================================
   Inter, served from this host.

   Nothing on a page is fetched from another origin — no font CDN, no script
   host, no remote image — so the app works on an isolated network and leaks no
   visitor request to a third party.

   One variable file per subset covers every weight the site uses (Google
   serves the same file for 400 through 800). Tamil is not in Inter and falls
   back to the reader's own Tamil face, exactly as it did with the hosted copy.
   ========================================================================== */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('../fonts/inter-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('../fonts/inter-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* ==========================================================================
   Palette — taken from the logo, not invented.

   The mark is deep navy and gold on white, so those are the two accents and
   the page is light behind them. Every colour in the stylesheet comes from a
   token here; nothing hardcodes a hex or an rgba() of its own, which is what
   makes a change of theme a change to this block.

   The overlay tokens matter more than they look. A dark theme lightens a
   surface by laying white over it at low alpha, and that trick inverts on a
   light background — white on white is nothing at all. They are ink at low
   alpha instead, so a raised surface reads as raised either way.
   ========================================================================== */
:root {
    --bg: #f3f6fb;
    --bg-2: #e9eff8;
    --surface: #ffffff;
    --surface-strong: #ffffff;
    --surface-muted: #f6f8fc;
    --border: #dbe3ef;
    --border-strong: #c3d0e4;
    --text: #12233d;
    --muted: #5b6c88;

    /* The logo's two colours. Navy leads; gold is the highlight, never a
       large field — at size it reads as caution rather than quality. */
    --accent: #1b3f7a;
    --accent-strong: #14305e;
    --accent-soft: #e8eefa;
    --accent-2: #b8862b;
    --accent-2-soft: #fbf3e2;
    --accent-3: #b45309;

    --success: #12704a;
    --success-soft: #e6f4ee;
    --danger: #b42318;
    --danger-soft: #fdeceb;

    /* Text that sits on a filled accent. */
    --on-accent: #ffffff;

    /* Ink at low alpha — see the note above. */
    --overlay-soft: rgba(18, 35, 61, 0.04);
    --overlay: rgba(18, 35, 61, 0.07);
    --overlay-strong: rgba(18, 35, 61, 0.12);

    --shadow: 0 12px 30px rgba(18, 35, 61, 0.08);
    --shadow-lg: 0 20px 45px rgba(18, 35, 61, 0.14);

    /* Native select popups are painted by the OS, so they need opaque colours. */
    --option-bg: #ffffff;
    --option-text: #12233d;
    --option-active-bg: #1b3f7a;
    --option-active-text: #ffffff;
    color-scheme: light;
}

* {
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
}

body {
    margin: 0;
    font-family: 'Inter', sans-serif;
    color: var(--text);
    background:
        radial-gradient(circle at top left, rgba(27, 63, 122, 0.07), transparent 32%),
        radial-gradient(circle at top right, rgba(184, 134, 43, 0.07), transparent 28%),
        linear-gradient(135deg, var(--bg), var(--bg-2));
    min-height: 100vh;
}

body::before {
    content: '';
    position: fixed;
    inset: 0;
    pointer-events: none;
    background-image: radial-gradient(var(--overlay-soft) 1px, transparent 1px);
    background-size: 26px 26px;
    opacity: 0.5;
}

img {
    max-width: 100%;
    display: block;
}

.page-shell {
    min-height: 100vh;
    padding: 24px 20px 60px;
}

.topbar {
    max-width: 1240px;
    margin: 0 auto 30px;
    padding: 18px 22px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    /*
     * Opaque, and deliberately WITHOUT backdrop-filter.
     *
     * A filter — backdrop or otherwise — makes an element the containing
     * block for any position:fixed descendant. The notification drawer lives
     * inside this bar, so a blur here pins it to the header instead of the
     * viewport: it stops reaching the edge of the screen and loses its full
     * height. At this opacity the blur bought nothing anyway.
     */
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    box-shadow: var(--shadow);
    /*
     * The .reveal sections below use transform, which makes each one its own
     * stacking context painted after the header. Without an explicit z-index
     * here the open account/notification dropdowns render behind the page.
     */
    position: relative;
    z-index: 100;
}

.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 800;
    letter-spacing: 0.03em;
    color: var(--text);
    text-decoration: none;
}

.brand-mark {
    /* Artwork on white, sitting on a white bar: no chip and no drop shadow,
       which on a light theme only muddies the mark. */
    width: 40px;
    height: 40px;
    flex: 0 0 auto;
    border-radius: 10px;
    background: transparent;
    object-fit: contain;
}

.topnav {
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
}

.topnav a {
    color: var(--muted);
    text-decoration: none;
    font-weight: 600;
    transition: color 0.3s ease, transform 0.3s ease;
}

.topnav a:hover {
    color: var(--text);
    transform: translateY(-2px);
}

.topnav a[aria-current="page"] {
    color: var(--accent);
}

/*
 * Grouped menu: "College" and "Mess" each open a small panel of their own
 * screens. <details> supplies the toggle; main.js adds the one-open-at-a-time,
 * outside-click and Escape behaviour shared with the account dropdowns.
 */
.nav-group {
    position: relative;
}

.nav-group > summary {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--muted);
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    transition: color 0.3s ease;
}

.nav-group > summary::-webkit-details-marker {
    display: none;
}

.nav-group > summary:hover,
.nav-group[open] > summary {
    color: var(--text);
}

/* The group whose page is open reads the same as an active link. */
.nav-group > summary.is-current {
    color: var(--accent);
}

.nav-group-caret {
    font-size: 0.7em;
    transition: transform 0.25s ease;
}

.nav-group[open] .nav-group-caret {
    transform: rotate(180deg);
}

.nav-group-menu {
    position: absolute;
    left: 0;
    top: calc(100% + 10px);
    z-index: 120;
    min-width: 190px;
    display: grid;
    gap: 2px;
    padding: 8px;
    border-radius: 14px;
    /* Opaque, not translucent: the panel overlaps page content. */
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
}

.nav-group-menu a {
    padding: 8px 10px;
    border-radius: 10px;
    font-size: 0.92rem;
    white-space: nowrap;
}

.nav-group-menu a:hover {
    background: var(--overlay);
    /* The parent rule lifts links on hover; inside a panel that just jitters. */
    transform: none;
}

.nav-group-menu a[aria-current="page"] {
    background: rgba(27, 63, 122, 0.14);
}

.user-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-radius: 999px;
    background: var(--overlay);
    border: 1px solid var(--overlay);
    color: var(--text);
    font-size: 0.92rem;
    font-weight: 600;
}

.user-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--accent), var(--accent-2));
    box-shadow: 0 0 0 3px rgba(27, 63, 122, 0.2);
}

.container {
    max-width: 1240px;
    margin: 0 auto;
}

.hero {
    display: grid;
    gap: 18px;
    grid-template-columns: 1fr;
    align-items: start;
    margin-bottom: 20px;
}

.hero-card,
.card,
.metric-card,
.role-card,
.list-card,
.panel {
    background: var(--surface);
    border: 1px solid var(--border);
    backdrop-filter: blur(20px);
    border-radius: 24px;
    box-shadow: var(--shadow);
}

.hero-card {
    padding: 18px 20px;
    position: relative;
    overflow: hidden;
}

.hero-card::after {
    content: '';
    position: absolute;
    inset: auto -40px -40px auto;
    width: 180px;
    height: 180px;
    background: radial-gradient(circle, rgba(184, 134, 43, 0.10), transparent 70%);
    border-radius: 50%;
    pointer-events: none;
}

.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-radius: 999px;
    background: var(--overlay);
    color: var(--accent);
    font-size: 0.9rem;
    font-weight: 600;
    margin-bottom: 16px;
}

.hero h1 {
    font-size: clamp(1.5rem, 3vw, 2.2rem);
    line-height: 1.2;
    margin: 0 0 8px;
}

.hero p {
    color: var(--muted);
    font-size: 0.98rem;
    line-height: 1.6;
    margin-bottom: 14px;
}

.button-row {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
}

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 16px;
    border-radius: 999px;
    border: 1px solid transparent;
    font-weight: 700;
    text-decoration: none;
    transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.btn:hover {
    transform: translateY(-2px);
}

.btn-primary {
    background: linear-gradient(135deg, var(--accent), var(--accent-strong));
    color: var(--on-accent);
    box-shadow: 0 12px 24px rgba(27, 63, 122, 0.18);
}

.btn-secondary {
    background: var(--overlay);
    color: var(--text);
    border-color: var(--border);
}

.pill {
    display: inline-flex;
    align-items: center;
    padding: 7px 10px;
    border-radius: 999px;
    background: rgba(27, 63, 122, 0.16);
    color: var(--accent);
    font-size: 0.84rem;
    font-weight: 700;
}

.story-card {
    padding: 18px;
    border-radius: 18px;
    background: var(--surface);
    border: 1px solid var(--border);
}

.story-card h3 {
    margin: 0 0 8px;
    font-size: 1.05rem;
}

.story-card p {
    margin: 0 0 10px;
    color: var(--muted);
    line-height: 1.6;
}

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

.form-card h3 {
    margin-top: 0;
}

.hero-side {
    padding: 24px;
}

.hero-stack {
    display: grid;
    gap: 16px;
}

.feature-chip {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    border-radius: 16px;
    background: var(--overlay-soft);
    color: var(--muted);
}

.feature-chip strong {
    color: var(--text);
    display: block;
}

.section-title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    margin: 36px 0 18px;
}

.section-title h2 {
    margin: 0;
    font-size: 1.3rem;
}

.section-title a {
    color: var(--accent);
    text-decoration: none;
    font-weight: 600;
}

.grid-3,
.role-grid,
.dashboard-grid,
.stat-grid {
    display: grid;
    gap: 18px;
}

.grid-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.role-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.dashboard-grid {
    grid-template-columns: 1.1fr 0.9fr;
    align-items: start;
}

.dashboard-shell {
    display: grid;
    gap: 20px;
    grid-template-columns: minmax(0, 1.7fr) minmax(280px, 0.8fr);
    align-items: start;
}

.dashboard-main,
.dashboard-sidebar {
    display: grid;
    gap: 16px;
}

.sidebar-card {
    padding: 18px;
    background: var(--surface);
    border: 1px solid var(--border);
    backdrop-filter: blur(20px);
    border-radius: 20px;
    box-shadow: var(--shadow);
}

.sidebar-card h3 {
    margin: 0 0 12px;
    font-size: 1rem;
}

.status-list {
    display: grid;
    gap: 10px;
}

.status-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 0;
    border-bottom: 1px solid var(--overlay);
    color: var(--muted);
    font-size: 0.95rem;
}

.status-row:last-child {
    border-bottom: 0;
}

.status-success,
.status-warn,
.status-info {
    display: inline-flex;
    align-items: center;
    padding: 5px 8px;
    border-radius: 999px;
    font-size: 0.78rem;
    font-weight: 700;
}

.status-success {
    background: rgba(27, 63, 122, 0.18);
    color: var(--accent);
}

.status-warn {
    background: var(--accent-2-soft);
    color: #8a6318;
}

.status-info {
    background: rgba(184, 134, 43, 0.18);
    color: #cab5ff;
}

.activity-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    gap: 10px;
}

.activity-list li {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 0;
    border-bottom: 1px solid var(--overlay);
    color: var(--muted);
    font-size: 0.94rem;
}

.activity-list li:last-child {
    border-bottom: 0;
}

.activity-list strong {
    color: var(--text);
}

.quick-links {
    display: grid;
    gap: 8px;
}

.quick-links a {
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 12px;
    border-radius: 12px;
    background: var(--overlay-soft);
    color: var(--text);
    text-decoration: none;
    font-weight: 600;
}

.stat-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

.role-card,
.metric-card,
.list-card,
.panel {
    padding: 22px;
    transition: transform 0.3s ease, border-color 0.3s ease;
}

.role-card:hover,
.metric-card:hover,
.list-card:hover,
.panel:hover {
    transform: translateY(-4px);
    border-color: rgba(27, 63, 122, 0.4);
}

.role-card h3 {
    margin: 0 0 8px;
    font-size: 1.1rem;
}

.role-card p,
.metric-card p,
.list-card p,
.panel p {
    color: var(--muted);
    line-height: 1.6;
    margin: 0;
}

.role-card ul {
    padding-left: 18px;
    color: var(--muted);
    line-height: 1.7;
}

.metric-card .value {
    font-size: 1.8rem;
    font-weight: 800;
    margin: 4px 0 6px;
}

.metric-card .delta {
    color: var(--accent);
    font-weight: 600;
}

.list-card .list-item {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    align-items: center;
    padding: 12px 0;
    border-bottom: 1px solid var(--overlay);
}

.list-card .list-item:last-child {
    border-bottom: 0;
}

.badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px 10px;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 700;
    background: var(--overlay);
    color: var(--text);
}

.progress-track {
    height: 8px;
    border-radius: 999px;
    background: var(--overlay);
    overflow: hidden;
    margin-top: 10px;
}

.progress-bar {
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--accent), var(--accent-2));
}

.timeline {
    display: grid;
    gap: 12px;
    margin-top: 14px;
}

.timeline-item {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 12px;
    border-radius: 14px;
    background: var(--overlay-soft);
}

.timeline-item .dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    margin-top: 6px;
    flex: 0 0 auto;
    background: linear-gradient(135deg, var(--accent), var(--accent-3));
    box-shadow: 0 0 0 3px rgba(27, 63, 122, 0.2);
}

.timeline-item strong {
    display: block;
    margin-bottom: 4px;
}

.list-card h3,
.panel h3 {
    margin: 0 0 10px;
    font-size: 1.1rem;
}

.form-grid {
    display: grid;
    gap: 16px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.field-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.field-group label {
    font-size: 0.95rem;
    color: var(--muted);
    font-weight: 600;
}

.required-mark {
    color: var(--accent-3);
    margin-left: 2px;
}

.field-optional {
    font-weight: 500;
    opacity: 0.75;
}

/*
 * A validation message, printed by Html::fieldError() directly beneath the
 * input it belongs to rather than collected into one banner at the top — the
 * user should not have to work out which field a complaint refers to.
 */
.field-error {
    color: var(--danger);
    font-size: 0.88rem;
    font-weight: 600;
    line-height: 1.35;
}

.field-error::before {
    content: '⚠ ';
}

/* The matching state on the control itself, so the pair reads as one unit. */
.field-group input[aria-invalid="true"],
.field-group select[aria-invalid="true"],
.field-group textarea[aria-invalid="true"] {
    border-color: rgba(180, 35, 24, 0.55);
    background: var(--danger-soft);
}

.field-group input[aria-invalid="true"]:focus,
.field-group select[aria-invalid="true"]:focus,
.field-group textarea[aria-invalid="true"]:focus {
    outline: 2px solid rgba(180, 35, 24, 0.25);
    border-color: rgba(180, 35, 24, 0.7);
}

/* Spacing for a banner that sits directly under the page hero. */
.alert-top {
    margin-top: 16px;
}

.field-group input,
.field-group select,
.field-group textarea {
    width: 100%;
    padding: 12px 14px;
    border-radius: 14px;
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    transition: border-color 0.25s ease, box-shadow 0.25s ease, background 0.25s ease;
}

.field-group select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-color: var(--surface);
    background-image: linear-gradient(45deg, transparent 50%, rgba(18, 35, 61, 0.6) 50%),
                      linear-gradient(135deg, rgba(18, 35, 61, 0.6) 50%, transparent 50%);
    background-position: calc(100% - 18px) calc(50% - 3px), calc(100% - 14px) calc(50% - 3px);
    background-size: 6px 6px, 6px 6px;
    background-repeat: no-repeat;
    padding-right: 40px;
    color: var(--text);
    border-color: var(--border-strong);
}

.field-group select:hover {
    background-color: var(--surface-muted);
}

.field-group select::-ms-expand {
    display: none;
}

/*
 * The open dropdown list is drawn by the browser/OS, which ignores translucent
 * colours and inherited text colour. Options therefore need fully opaque
 * background/foreground pairs, and the highlighted row needs the inset
 * box-shadow fallback because Chrome ignores background-color on :checked.
 */
select option,
.field-group select option {
    background-color: var(--option-bg);
    color: var(--option-text);
}

select option:hover,
select option:focus,
select option:checked,
select option[selected],
.field-group select option:hover,
.field-group select option:focus,
.field-group select option:checked,
.field-group select option[selected] {
    background-color: var(--option-active-bg);
    color: var(--option-active-text);
    box-shadow: 0 0 0 100px var(--option-active-bg) inset;
}

select option:disabled {
    color: var(--muted);
}

.field-group input:focus,
.field-group select:focus,
.field-group textarea:focus {
    outline: 2px solid rgba(27, 63, 122, 0.25);
    border-color: rgba(27, 63, 122, 0.45);
    background-color: var(--overlay-strong);
}

.table-wrap {
    overflow-x: auto;
}

.table-wrap table {
    width: 100%;
    border-collapse: collapse;
    min-width: 620px;
}

.table-wrap th,
.table-wrap td {
    padding: 12px 10px;
    text-align: left;
    border-bottom: 1px solid var(--border);
}

.table-empty {
    text-align: center;
    padding: 18px 10px;
    color: var(--muted);
}

.row-actions {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

.inline-form {
    display: inline;
    margin: 0;
}

.btn-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 7px 12px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--overlay-soft);
    color: var(--text);
    font: inherit;
    font-size: 0.84rem;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.btn-action:hover {
    transform: translateY(-1px);
}

.btn-edit:hover {
    background: rgba(27, 63, 122, 0.16);
    border-color: rgba(27, 63, 122, 0.45);
    color: var(--accent);
}

.btn-delete:hover {
    background: var(--danger-soft);
    border-color: rgba(180, 35, 24, 0.35);
    color: var(--danger);
}

.btn-action:focus-visible {
    outline: 2px solid rgba(27, 63, 122, 0.5);
    outline-offset: 2px;
}

.row-actions-note {
    color: var(--muted);
    font-size: 0.84rem;
}

/* Dining hall chooser ------------------------------------------------------ */

.hall-choices {
    display: grid;
    gap: 16px;
    margin-top: 18px;
}

.hall-group {
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 14px 16px 16px;
    margin: 0;
}

.hall-group legend {
    padding: 0 8px;
    color: var(--accent);
    font-weight: 700;
    font-size: 0.95rem;
}

.hall-option {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 12px;
    border-radius: 14px;
    border: 1px solid transparent;
    background: var(--overlay-soft);
    margin-top: 8px;
    cursor: pointer;
}

.hall-option:hover {
    background: var(--overlay);
}

.hall-option.is-selected,
.hall-option:has(input:checked) {
    border-color: rgba(27, 63, 122, 0.45);
    background: rgba(27, 63, 122, 0.1);
}

.hall-option input {
    margin-top: 4px;
    width: auto;
    accent-color: var(--accent);
    flex: 0 0 auto;
}

.hall-option-body {
    display: grid;
    gap: 6px;
    min-width: 0;
}

.hall-tags {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.hall-notes {
    color: var(--muted);
    font-size: 0.88rem;
}

.alert-warning {
    background: var(--accent-2-soft);
    border-color: rgba(184, 134, 43, 0.35);
    color: #8a6318;
}

.badge-paid {
    background: var(--success-soft);
    color: var(--success);
}

.badge-unpaid {
    background: var(--accent-2-soft);
    color: #8a6318;
}

.cell-mono {
    font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
    font-size: 0.86rem;
}

.settings-note {
    margin-top: 14px;
    padding: 10px 12px;
    border-radius: 12px;
    background: var(--overlay-soft);
    color: var(--muted);
    font-size: 0.9rem;
}

.checkbox-label {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text);
    font-weight: 600;
}

.checkbox-label input {
    width: auto;
    accent-color: var(--accent);
}

/* Filter tabs -------------------------------------------------------------- */

.filter-tabs {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}

.filter-tab {
    padding: 8px 14px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--overlay-soft);
    color: var(--muted);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 600;
}

.filter-tab.is-active {
    background: linear-gradient(135deg, var(--accent), var(--accent-strong));
    border-color: transparent;
    color: var(--on-accent);
}

/* Account and notification menus ------------------------------------------- */

.topbar-account {
    display: flex;
    align-items: center;
    gap: 10px;
}

.notification-menu,
.account-menu {
    position: relative;
}

.notification-menu summary,
.account-menu summary {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-radius: 999px;
    background: var(--overlay);
    border: 1px solid var(--overlay);
    color: var(--text);
    font-size: 0.92rem;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
}

.notification-menu summary::-webkit-details-marker,
.account-menu summary::-webkit-details-marker {
    display: none;
}

.notification-menu summary {
    position: relative;
}

.notification-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--accent-3);
    color: var(--on-accent);
    font-size: 0.7rem;
    font-weight: 800;
    display: grid;
    place-items: center;
}

.account-name {
    max-width: 140px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.account-dropdown {
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    z-index: 120;
    min-width: 260px;
    max-width: min(320px, calc(100vw - 32px));
    padding: 12px;
    border-radius: 16px;
    /* Opaque, not translucent: the dropdown overlaps page content. */
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
}

.topbar-account details[open] > summary {
    background: rgba(27, 63, 122, 0.16);
    border-color: rgba(27, 63, 122, 0.4);
}

/* ==========================================================================
   The notification drawer.

   A panel over the page rather than a page of its own: reading a notification
   is a glance, and a separate screen costs the reader whatever they were doing.
   It is the <details> the bell already toggled, so it still opens with no
   JavaScript; only the presentation changed from a dropdown to a drawer.
   ========================================================================== */
.drawer-backdrop {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: rgba(18, 35, 61, 0.32);
    backdrop-filter: blur(2px);
    animation: drawer-fade 0.25s ease;
}

.notification-drawer {
    position: fixed;
    top: 0;
    right: 0;
    z-index: 210;
    width: min(400px, 100vw);
    /* dvh follows a phone browser as its address bar hides, so the panel
       stays flush to the bottom edge; vh is the fallback for older ones. */
    height: 100vh;
    height: 100dvh;
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border-left: 1px solid var(--border);
    box-shadow: -30px 0 60px rgba(18, 35, 61, 0.18);
    animation: drawer-in 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes drawer-in {
    from { transform: translateX(100%); }
    to { transform: translateX(0); }
}

@keyframes drawer-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .notification-drawer,
    .drawer-backdrop {
        animation: none;
    }
}

.drawer-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 22px 22px 16px;
    border-bottom: 1px solid var(--border);
    /* The gold hairline is the one place the brand's second colour appears in
       the chrome; a full gold band would read as a warning. */
    box-shadow: inset 0 3px 0 var(--accent-2);
}

.drawer-head strong {
    display: block;
    font-size: 1.05rem;
    letter-spacing: 0.01em;
}

.drawer-count {
    display: block;
    margin-top: 2px;
    color: var(--muted);
    font-size: 0.84rem;
}

.drawer-close {
    border: 0;
    background: transparent;
    color: var(--muted);
    font-size: 1.5rem;
    line-height: 1;
    padding: 2px 6px;
    border-radius: 8px;
    cursor: pointer;
}

.drawer-close:hover {
    background: var(--overlay);
    color: var(--text);
}

.drawer-bulk {
    padding: 14px 22px 0;
}

.drawer-body {
    flex: 1;
    overflow-y: auto;
    padding: 14px 16px 22px;
}

.drawer-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 6px;
}

.drawer-list li {
    padding: 12px 12px 10px;
    border-radius: 14px;
    border: 1px solid transparent;
    transition: background 0.2s ease, border-color 0.2s ease;
}

.drawer-list li:hover {
    background: var(--surface-muted);
    border-color: var(--border);
}

/* Unread carries a gold edge rather than a filled block: the eye finds it just
   as fast and the list stays calm. */
.drawer-list li.is-unread {
    background: var(--accent-soft);
    border-color: var(--border);
    box-shadow: inset 3px 0 0 var(--accent-2);
}

.drawer-item {
    display: grid;
    gap: 3px;
    color: var(--muted);
    text-decoration: none;
    font-size: 0.88rem;
    line-height: 1.45;
}

.drawer-item strong {
    color: var(--text);
    font-size: 0.94rem;
}

.drawer-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 8px;
}

.drawer-when {
    color: var(--muted);
    font-size: 0.78rem;
    margin-right: auto;
}

.drawer-actions form {
    margin: 0;
}

.drawer-actions button {
    border: 0;
    background: transparent;
    padding: 0;
    color: var(--accent);
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
}

.drawer-actions button:hover {
    text-decoration: underline;
}

.notification-empty {
    margin: 0;
    color: var(--muted);
    font-size: 0.88rem;
}

.account-dropdown {
    display: grid;
    gap: 6px;
}

.account-role {
    color: var(--accent);
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.account-email {
    color: var(--muted);
    font-size: 0.85rem;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--overlay);
    word-break: break-all;
}

.account-dropdown a {
    padding: 8px;
    border-radius: 10px;
    color: var(--text);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 600;
}

.account-dropdown a:hover {
    background: var(--overlay);
}

.account-signout {
    color: var(--accent-3) !important;
}

/* Notification list -------------------------------------------------------- */

.notification-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 10px;
}

.notification-item {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 14px;
    border-radius: 16px;
    background: var(--overlay-soft);
    border: 1px solid var(--overlay);
    flex-wrap: wrap;
}

.notification-item.is-unread {
    border-color: rgba(27, 63, 122, 0.35);
    background: rgba(27, 63, 122, 0.08);
}

.notification-icon {
    font-size: 1.2rem;
    line-height: 1.4;
}

.notification-body {
    flex: 1 1 240px;
    min-width: 0;
}

.notification-body p {
    margin: 4px 0 6px;
    color: var(--muted);
    line-height: 1.5;
}

.notification-meta {
    color: var(--muted);
    font-size: 0.82rem;
}

.notification-meta a {
    color: var(--accent);
}

/* Visible to screen readers only — used for table captions and search labels. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.alert {
    padding: 12px 14px;
    border-radius: 14px;
    margin-bottom: 16px;
    border: 1px solid transparent;
    line-height: 1.5;
}

.alert-error {
    background: var(--danger-soft);
    border-color: rgba(180, 35, 24, 0.28);
    color: var(--danger);
}

.alert-success {
    background: var(--success-soft);
    border-color: rgba(18, 112, 74, 0.28);
    color: var(--success);
}

.table-search {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
}

.table-search input {
    padding: 10px 14px;
    border-radius: 999px;
    border: 1px solid var(--border-strong);
    background-color: var(--surface);
    color: var(--text);
    font: inherit;
    min-width: 0;
    flex: 1 1 200px;
}

.table-search input:focus {
    outline: 2px solid rgba(27, 63, 122, 0.25);
    border-color: rgba(27, 63, 122, 0.45);
}

.user-signout {
    color: var(--accent);
    text-decoration: none;
    font-weight: 700;
    margin-left: 4px;
}

.pagination {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 8px;
    margin-top: 14px;
    flex-wrap: wrap;
}

.pagination span[aria-disabled="true"] {
    opacity: 0.4;
}

.pagination-summary {
    border: 0;
    color: var(--muted);
    font-size: 0.88rem;
    min-width: 0;
    padding-left: 6px;
}

/* A single page still states its count, so a short table reads as paged
   rather than as one that fetched everything. There is nowhere to page to,
   so it is a caption and not a control: no border, no pill. */
/* The outcome of the payment just made: what happened, the reference to
   quote, and the amount. Coloured by result, with the same status palette
   the rest of the interface uses. */
.payment-outcome {
    border-top: 3px solid var(--border-strong);
}

.payment-outcome.is-success {
    border-top-color: var(--success);
}

.payment-outcome.is-warning {
    border-top-color: var(--accent-3);
}

.payment-outcome.is-error {
    border-top-color: var(--danger);
}

/* Label above value, wrapping into as many columns as fit — a reference is
   long and must not be squeezed onto a second line mid-string. */
.payment-outcome-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 18px 26px;
    margin: 20px 0 0;
}

.payment-outcome-facts dt {
    font-size: 0.72rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 4px;
}

.payment-outcome-facts dd {
    margin: 0;
    font-size: 1rem;
    word-break: break-word;
}

/* The paste-able diagnostic report: a block of text, kept monospaced and
   scrollable rather than wrapped, so column alignment survives the copy. */
.diagnostic-report {
    margin: 0;
    padding: 16px 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--surface-muted);
    color: var(--text);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem;
    line-height: 1.55;
    overflow-x: auto;
    white-space: pre;
    -webkit-user-select: all;
    user-select: all;
}

.pagination-single .pagination-summary {
    border: 0;
    padding-left: 0;
    height: auto;
}

.pagination a,
.pagination span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    height: 36px;
    padding: 0 10px;
    border-radius: 999px;
    border: 1px solid var(--overlay-strong);
    color: var(--text);
    text-decoration: none;
    font-size: 0.92rem;
}

.pagination .active {
    background: linear-gradient(135deg, var(--accent), var(--accent-strong));
    color: var(--on-accent);
    border-color: transparent;
    font-weight: 700;
}

.table-wrap th {
    color: var(--accent);
    font-size: 0.86rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    background: var(--surface-muted);
    border-bottom: 1px solid var(--border-strong);
    white-space: nowrap;
}

/* Rows lift on hover, which on a dense report is most of the readability. */
.table-wrap tbody tr:hover {
    background: var(--accent-soft);
}

.site-footer {
    max-width: 1240px;
    margin: 0 auto;
    padding: 20px 0 10px;
    text-align: center;
    color: var(--muted);
    font-size: 0.95rem;
}

.reveal {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity 0.6s ease, transform 0.6s ease;
}

.reveal.is-visible {
    opacity: 1;
    transform: translateY(0);
}

/*
 * A list panel that is fetching its next page. Dimming it marks the wait and
 * blocking pointer events stops a second request queueing behind the first.
 * Declared after .reveal.is-visible so it wins on a panel that is both.
 */
[data-ajax-table].is-loading {
    opacity: 0.55;
    pointer-events: none;
    transition: opacity 0.15s ease;
}

@media (max-width: 980px) {
    .hero,
    .dashboard-grid {
        grid-template-columns: 1fr;
    }

    .grid-3,
    .role-grid,
    .stat-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 700px) {
    .page-shell {
        padding: 18px 14px 45px;
    }

    .topbar {
        border-radius: 24px;
        padding: 16px;
        flex-direction: column;
        align-items: flex-start;
        gap: 10px;
    }

    .user-pill {
        width: 100%;
        justify-content: flex-start;
    }

    /* Stacked header: anchor the dropdowns to the bar itself so they cannot
       hang off the edge of a narrow screen. */
    .topbar-account {
        width: 100%;
        justify-content: space-between;
    }

    /*
     * The nav wraps to full width here, so a floating panel would sit over the
     * links beneath it. Let the group expand in place instead — the menu simply
     * pushes the rest of the nav down, which is what there is room for.
     */
    .topnav {
        width: 100%;
        flex-direction: column;
        gap: 10px;
    }

    .nav-group,
    .nav-group > summary {
        width: 100%;
    }

    .nav-group > summary {
        justify-content: space-between;
    }

    .nav-group-menu {
        position: static;
        margin-top: 8px;
        box-shadow: none;
    }

    .notification-menu,
    .account-menu {
        position: static;
    }

    .notification-dropdown,
    .account-dropdown {
        left: 14px;
        right: 14px;
        max-width: none;
        min-width: 0;
    }

    .account-name {
        max-width: 45vw;
    }

    .topnav {
        gap: 10px;
    }

    .hero-card,
    .hero-side,
    .role-card,
    .metric-card,
    .list-card,
    .panel,
    .sidebar-card {
        padding: 18px;
    }

    .grid-3,
    .role-grid,
    .stat-grid,
    .dashboard-shell {
        grid-template-columns: 1fr;
    }

    .section-title {
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
    }
}

/* Company links in the footer. They wrap onto as many rows as the screen
   needs, so the row never forces a horizontal scroll on a phone. */
.footer-links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 8px 18px;
    margin-bottom: 14px;
}

.footer-links a {
    color: var(--muted);
    text-decoration: none;
    font-weight: 500;
    padding: 4px 2px;
}

.footer-links a:hover,
.footer-links a:focus-visible,
.footer-links a[aria-current="page"] {
    color: var(--accent);
}

/* Long-form copy: the company and policy pages. Sets the vertical rhythm the
   marketing panels never needed, and caps the measure so lines stay readable
   on a wide desktop. */
.legal-copy {
    max-width: 78ch;
}

.legal-copy h3 {
    margin: 26px 0 12px;
    font-size: 1.12rem;
    color: var(--text);
}

.legal-copy h3:first-child {
    margin-top: 0;
}

.legal-copy p,
.legal-copy li {
    color: var(--muted);
    line-height: 1.7;
}

.legal-copy p {
    margin: 0 0 12px;
}

.legal-copy ul,
.legal-copy ol {
    margin: 0 0 12px;
    padding-left: 20px;
}

.legal-copy li + li {
    margin-top: 8px;
}

.legal-copy a {
    color: var(--accent);
}

/* The founder's sign-off at the foot of the company story. */
.legal-signoff {
    margin-top: 22px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
    color: var(--text);
    font-weight: 600;
}

.legal-signoff span {
    display: block;
    margin-top: 2px;
    color: var(--muted);
    font-weight: 400;
    font-size: 0.9rem;
}

/* "Last updated" and similar notes above a policy. */
.legal-meta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.85rem;
    color: var(--muted);
}

/* Contact details: one column on a phone, two from tablet up. */
.contact-grid {
    display: grid;
    gap: 16px;
    grid-template-columns: 1fr;
}

.contact-item {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.contact-item strong {
    color: var(--text);
    font-size: 0.95rem;
}

.contact-item span,
.contact-item a {
    color: var(--muted);
    font-size: 0.95rem;
    line-height: 1.6;
    word-break: break-word;
}

.contact-item a {
    color: var(--accent);
    text-decoration: none;
}

.contact-item a:hover,
.contact-item a:focus-visible {
    text-decoration: underline;
}

@media (min-width: 600px) {
    .contact-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Marks a block whose final copy has not been supplied yet. */
.content-pending {
    border: 1px dashed var(--border);
    border-radius: 14px;
    padding: 14px 16px;
    color: var(--muted);
    font-size: 0.9rem;
    background: var(--overlay-soft);
}

/* ==========================================================================
   Client banner slider (index.php)

   The viewport is a scroll-snap container, so a finger swipe and a keyboard
   both move it with no JavaScript at all. main.js adds `is-enhanced`, which is
   what reveals the arrows and dots — until then the page shows no control that
   would do nothing.

   Slide artwork arrives at whatever ratio the client sent, so the frame owns
   the height and the image is cropped into it with object-fit.
   ========================================================================== */
.client-slider {
    --slide-height: 240px;
    position: relative;
    border-radius: 20px;
    overflow: hidden;
    border: 1px solid var(--border);
    background: var(--surface-strong);
    box-shadow: var(--shadow);
}

/* The banner lives inside the hero card, between the eyebrow and the headline.
   The card already spaces the eyebrow above it, so this only has to clear the
   headline below. */
.hero-card .client-slider {
    margin-bottom: 18px;
}

.slider-viewport {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* The dots sit over the artwork, so the native bar would only clutter it. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.slider-viewport::-webkit-scrollbar {
    display: none;
}

.slider-viewport:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -4px;
}

.slide {
    position: relative;
    flex: 0 0 100%;
    margin: 0;
    scroll-snap-align: center;
    height: var(--slide-height);
}

.slide img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Reads the caption over any photograph: dark at the foot, clear at the top. */
.slide::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        180deg,
        rgba(7, 17, 31, 0.15) 0%,
        rgba(7, 17, 31, 0.05) 35%,
        rgba(7, 17, 31, 0.82) 100%
    );
    pointer-events: none;
}

.slide-caption {
    position: absolute;
    z-index: 2;
    left: 18px;
    right: 18px;
    bottom: 46px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    color: #ffffff;
}

.slide-tag {
    align-self: flex-start;
    padding: 4px 11px;
    border-radius: 999px;
    /* Over a photograph, not over the page — see .slide-caption. */
    background: rgba(7, 17, 31, 0.62);
    border: 1px solid rgba(255, 255, 255, 0.3);
    backdrop-filter: blur(6px);
    color: #ffffff;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.5);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.slide-caption strong {
    font-size: 1.05rem;
    line-height: 1.3;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.55);
}

.slide-place {
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.78);
}

/* Controls appear only once main.js has wired them up. */
.slider-arrow,
.slider-dots {
    display: none;
}

.client-slider.is-enhanced .slider-dots {
    display: flex;
}

.slider-dots {
    position: absolute;
    z-index: 3;
    left: 0;
    right: 0;
    bottom: 14px;
    justify-content: center;
    align-items: center;
    gap: 8px;
}

.slider-dot {
    width: 8px;
    height: 8px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.45);
    cursor: pointer;
    transition: width 0.25s ease, background 0.25s ease;
}

.slider-dot:hover {
    background: rgba(255, 255, 255, 0.75);
}

.slider-dot[aria-current="true"] {
    width: 22px;
    /* Over a photo, so it marks itself out with opacity, not with the page
       accent: navy among translucent whites disappeared. */
    background: #ffffff;
}

.slider-dot:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.slider-arrow {
    position: absolute;
    z-index: 3;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    height: 40px;
    display: none;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.28);
    background: rgba(7, 17, 31, 0.55);
    backdrop-filter: blur(6px);
    color: #ffffff;
    font-size: 1rem;
    cursor: pointer;
    transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.slider-arrow:hover {
    background: rgba(7, 17, 31, 0.8);
    /* Gold edge for the brand, white chevron so it stays legible. */
    border-color: var(--accent-2);
    color: #ffffff;
}

.slider-arrow:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.slider-prev {
    left: 12px;
}

.slider-next {
    right: 12px;
}

/* Tablet up: taller frame, and the arrows earn their place once there is a
   pointer to use them. On a phone the swipe already does the job. */
@media (min-width: 600px) {
    .client-slider {
        --slide-height: 360px;
        border-radius: 22px;
    }

    .client-slider.is-enhanced .slider-arrow {
        display: flex;
    }

    .slide-caption {
        left: 26px;
        right: 26px;
        bottom: 52px;
        gap: 6px;
    }

    .slide-caption strong {
        font-size: 1.45rem;
    }

    .slide-place {
        font-size: 0.95rem;
    }
}

@media (min-width: 1024px) {
    .client-slider {
        --slide-height: 460px;
    }

    .slide-caption {
        left: 34px;
        bottom: 58px;
    }

    .slide-caption strong {
        font-size: 1.75rem;
    }

    .slider-arrow {
        width: 46px;
        height: 46px;
    }
}

/* Someone who has asked for less motion gets no automatic advance (main.js)
   and no animated jump between slides. */
@media (prefers-reduced-motion: reduce) {
    /* The banner lives inside the hero card, between the eyebrow and the headline.
   The card already spaces the eyebrow above it, so this only has to clear the
   headline below. */
.hero-card .client-slider {
    margin-bottom: 18px;
}

.slider-viewport {
        scroll-behavior: auto;
    }

    .slider-dot {
        transition: none;
    }
}

/* Leadership portraits on the home page. One column on a phone, side by side
   from tablet up. The photographs are 4:3 and include the desk name plate, so
   the frame keeps that ratio rather than cropping it away. */
.leader-grid {
    display: grid;
    gap: 18px;
    grid-template-columns: 1fr;
}

.leader-card {
    overflow: hidden;
    border-radius: 18px;
    border: 1px solid var(--border);
    background: var(--surface-strong);
    box-shadow: var(--shadow);
}

.leader-card img {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

.leader-name {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 14px 16px 16px;
}

.leader-name strong {
    color: var(--text);
    font-size: 1.05rem;
}

.leader-name span {
    color: var(--muted);
    font-size: 0.9rem;
}

/* The full logo where it stands on its own — the sign-in page. */
.brand-lockup {
    display: block;
    width: 132px;
    max-width: 40%;
    height: auto;
    margin: 0 auto 18px;
    padding: 10px;
    border-radius: 20px;
    background: var(--surface);
    box-shadow: var(--shadow);
}

@media (min-width: 600px) {
    .leader-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .brand-lockup {
        width: 150px;
    }
}

/* Profile photographs. The image and the initials fallback are the same shape
   and the same size, so a list keeps its rhythm whether or not a given account
   has uploaded one. Sizes are in rem so they follow the page's text scale. */
.avatar {
    flex: 0 0 auto;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 999px;
    object-fit: cover;
    background: var(--surface-strong);
    border: 1px solid var(--border);
}

.avatar-empty {
    background: var(--surface-muted);
    display: grid;
    place-items: center;
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--muted);
    text-transform: uppercase;
    user-select: none;
}

.avatar-sm {
    width: 1.9rem;
    height: 1.9rem;
    font-size: 0.7rem;
}

.avatar-lg {
    width: 5.5rem;
    height: 5.5rem;
    font-size: 1.6rem;
}

/* Name and photograph as one unit — used in table cells and the account chip. */
.identity {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.identity-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The Mess ID, under the name it belongs to. Monospaced digits so a column
   of them lines up and a transposed pair is easy to spot, and quiet enough
   that the name is still what the eye lands on first. */
.identity-id {
    display: block;
    margin-top: 2px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.74rem;
    letter-spacing: 0.06em;
    color: var(--muted);
}

/* The upload control: what is on file now, sitting beside the picker. */
.photo-field {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
}

/* What is on file, at a size that reads as a photograph rather than as an
   icon. The caption matters as much as the picture: a circle of initials is
   otherwise indistinguishable from a photo that failed to load. */
.photo-preview {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    margin: 0;
    flex: 0 0 auto;
}

.photo-preview figcaption {
    font-size: 0.72rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    white-space: nowrap;
}

.photo-field-controls {
    display: flex;
    flex-direction: column;
    gap: 10px;
    flex: 1 1 240px;
    min-width: 0;
}

.photo-field input[type="file"] {
    min-width: 0;
    max-width: 100%;
}

@media (max-width: 599px) {
    /* Stacked on a phone: 5.5rem of photo beside a file input leaves the input
       too narrow to show the chosen filename. */
    .photo-field {
        flex-direction: column;
        align-items: flex-start;
    }

    .photo-field-controls {
        width: 100%;
    }
}

.photo-remove {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--muted);
    font-size: 0.86rem;
    white-space: nowrap;
}

.photo-remove input {
    width: auto;
    margin: 0;
}

@media (min-width: 600px) {
    .avatar-lg {
        width: 6.5rem;
        height: 6.5rem;
    }
}

/* The row of controls that belongs to a table: the status tabs on the left,
   the download of exactly what those controls are showing on the right. It
   sits inside the panel so the two read as one thing, and wraps to two lines
   on a phone rather than squeezing the tabs. */
.table-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin: 14px 0 6px;
}

.table-toolbar .filter-tabs {
    margin: 0;
}

.btn-sm {
    padding: 8px 14px;
    font-size: 0.86rem;
}

@media (max-width: 599px) {
    .table-toolbar {
        align-items: stretch;
    }

    .table-toolbar .btn-sm {
        justify-content: center;
    }
}

/* ==========================================================================
   The premium pass.

   Nothing here changes what a page says or does — it is spacing, weight and
   restraint. Four ideas, applied consistently:

     1. Air. Cards breathe more, sections sit further apart. Crowding is the
        single thing that makes an interface feel cheap.
     2. One gold hairline per surface, never a gold field. The brand's second
        colour signs a card; used broadly it reads as a warning.
     3. Layered shadow instead of one flat drop — a near shadow for the edge
        and a far one for the lift, which is how a real card sits on paper.
     4. Type that knows its job: display sizes tighten, small labels widen
        into tracked capitals.

   It comes last in the file deliberately, so each rule is a considered
   override of the working styles above rather than a rewrite of them.
   ========================================================================== */

.page-shell {
    padding: 26px 22px 72px;
}

/* Sections need room from each other before anything else reads as premium. */
.section-title {
    margin: 44px 0 20px;
}

.section-title h2 {
    font-size: 1.35rem;
    letter-spacing: -0.015em;
}

.hero h1 {
    font-size: clamp(1.6rem, 3.1vw, 2.35rem);
    letter-spacing: -0.02em;
    line-height: 1.18;
}

.hero p {
    font-size: 1rem;
    line-height: 1.65;
    max-width: 68ch;
}

/* The eyebrow becomes a tracked, capitalised label — the cheapest way to make
   a heading block look considered. */
.eyebrow {
    padding: 7px 13px;
    background: var(--accent-soft);
    border: 1px solid var(--border);
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.hero-card,
.card,
.metric-card,
.role-card,
.list-card,
.form-card,
.story-card,
.panel {
    box-shadow:
        0 1px 2px rgba(18, 35, 61, 0.05),
        0 14px 34px rgba(18, 35, 61, 0.07);
}

.hero-card {
    padding: 30px 32px;
    /* The gold signature: one hairline along the top edge of the page's
       leading card, and nowhere else on it. */
    border-top: 2px solid var(--accent-2);
    background:
        linear-gradient(180deg, var(--surface-muted), var(--surface) 120px);
}

.panel,
.form-card {
    padding: 26px;
}

.metric-card,
.role-card {
    padding: 22px;
}

/* Figures are the thing people look at on a dashboard, so they get the weight
   and the tightest tracking on the page. */
.metric-card .value {
    letter-spacing: -0.03em;
}

.metric-card p {
    letter-spacing: 0.01em;
}

.metric-card .delta {
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.btn {
    padding: 13px 22px;
    letter-spacing: 0.015em;
    transition: transform 0.25s ease, box-shadow 0.25s ease, background 0.25s ease;
}

.btn-primary {
    box-shadow:
        0 1px 2px rgba(18, 35, 61, 0.12),
        0 10px 24px rgba(27, 63, 122, 0.22);
}

.btn-primary:hover {
    box-shadow:
        0 2px 4px rgba(18, 35, 61, 0.14),
        0 16px 32px rgba(27, 63, 122, 0.28);
}

.btn-secondary {
    background: var(--surface);
    border-color: var(--border-strong);
}

.btn-secondary:hover {
    background: var(--surface-muted);
    border-color: var(--accent);
    color: var(--accent);
}

.btn-sm {
    padding: 9px 16px;
}

/* Rows want height before they want anything else: a dense table is the other
   thing that reads as cheap. */
.table-wrap th,
.table-wrap td {
    padding: 15px 12px;
}

.table-wrap th {
    font-size: 0.72rem;
    letter-spacing: 0.11em;
}

.table-wrap tbody tr {
    transition: background 0.18s ease;
}

.field-group label {
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--muted);
}

.field-group input,
.field-group select,
.field-group textarea {
    padding: 13px 15px;
}

.field-group input:focus,
.field-group select:focus,
.field-group textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* The bar the whole app sits under. A hairline and a whisper of shadow, so it
   reads as a fixed edge rather than a floating pill. */
.topbar {
    padding: 14px 24px;
    box-shadow:
        0 1px 2px rgba(18, 35, 61, 0.04),
        0 10px 30px rgba(18, 35, 61, 0.06);
}

.brand span {
    letter-spacing: 0.08em;
    font-size: 0.98rem;
}

.badge {
    letter-spacing: 0.03em;
}

.pill {
    letter-spacing: 0.04em;
    font-size: 0.78rem;
    text-transform: uppercase;
    background: var(--accent-soft);
    border: 1px solid var(--border);
}

.site-footer {
    padding: 34px 0 12px;
    border-top: 1px solid var(--border);
    margin-top: 40px;
}

@media (min-width: 1024px) {
    .hero-card {
        padding: 38px 40px;
    }

    .panel,
    .form-card {
        padding: 30px;
    }
}

@media (max-width: 599px) {
    .page-shell {
        padding: 16px 14px 48px;
    }

    .hero-card {
        padding: 22px 20px;
    }

    .panel,
    .form-card {
        padding: 20px 16px;
    }
}

/* The filter controls that sit in a panel header.
 *
 * These used to style the text input and leave the selects to the browser, so
 * a report panel showed a themed search box beside two default dropdowns. They
 * are the same control in the same row and now look it: the pill shape of the
 * input, the chevron the form selects draw, and one shared focus ring. */
.table-search select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding: 10px 38px 10px 14px;
    border-radius: 999px;
    border: 1px solid var(--border-strong);
    background-color: var(--surface);
    background-image: linear-gradient(45deg, transparent 50%, rgba(18, 35, 61, 0.6) 50%),
                      linear-gradient(135deg, rgba(18, 35, 61, 0.6) 50%, transparent 50%);
    background-position: calc(100% - 18px) calc(50% - 3px), calc(100% - 14px) calc(50% - 3px);
    background-size: 6px 6px, 6px 6px;
    background-repeat: no-repeat;
    color: var(--text);
    font: inherit;
    cursor: pointer;
    transition: border-color 0.25s ease, box-shadow 0.25s ease, background-color 0.25s ease;
}

.table-search select:hover {
    background-color: var(--surface-muted);
}

.table-search input,
.table-search select {
    min-height: 42px;
}

/* One focus ring for every control on the page, filter row included. */
.table-search input:focus,
.table-search select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Clear sits in the same row as Apply and matches its height, but reads
   quieter: it undoes a search rather than running one, so it should never be
   the control the eye lands on first. It only exists while there is something
   to clear — see Html::searchClear(). */
.table-search-clear {
    display: inline-flex;
    align-items: center;
    min-height: 42px;
    background: transparent;
    color: var(--muted);
    border-color: var(--border);
}

.table-search-clear:hover,
.table-search-clear:focus-visible {
    background: var(--overlay-soft);
    color: var(--text);
    border-color: var(--border-strong);
}

/* The Excel download: the icon carries the file type, so the label does not
   have to spell it out twice. */
.btn-icon {
    width: 1.05em;
    height: 1.05em;
    flex: 0 0 auto;
    display: block;
}

/* ==========================================================================
   A table that grows downwards, not sideways.

   The roster carried twelve columns and could only ever scroll horizontally,
   which puts the columns people act on — the rebate field among them — off the
   right-hand edge. It now shows six, and the rest of each record sits in a
   <details> inside the name cell: opening one makes that row taller. No
   JavaScript is involved, so it works exactly the same with scripting off.
   ========================================================================== */
.table-roomy {
    /* The shared 620px minimum is what forced the scrollbar. With six columns
       there is nothing to force. */
    min-width: 0;
}

.table-roomy td {
    vertical-align: top;
}

.row-more {
    margin-top: 6px;
}

.row-more > summary {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--accent);
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    cursor: pointer;
    list-style: none;
}

.row-more > summary::-webkit-details-marker {
    display: none;
}

.row-more > summary::after {
    content: '▾';
    font-size: 0.9em;
    transition: transform 0.2s ease;
}

.row-more[open] > summary::after {
    transform: rotate(180deg);
}

/* Label above value, so a long email or college name wraps instead of pushing
   the column wider — the thing this whole arrangement exists to avoid. */
.row-more dl {
    margin: 8px 0 2px;
    display: grid;
    grid-template-columns: auto;
    gap: 6px 0;
    padding: 10px 12px;
    border-radius: 12px;
    background: var(--surface-muted);
    border: 1px solid var(--border);
}

.row-more dt {
    color: var(--muted);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
}

.row-more dd {
    margin: 0 0 4px;
    font-size: 0.88rem;
    word-break: break-word;
}

@media (min-width: 600px) {
    .row-more dl {
        grid-template-columns: max-content 1fr;
        gap: 6px 16px;
        align-items: baseline;
    }

    .row-more dd {
        margin-bottom: 0;
    }
}

/* The rebate control, styled like every other field on the site rather than
   left as a bare browser input in a table cell. */
.rebate-form {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
}

.rebate-form input {
    width: 5.5rem;
    flex: 0 0 auto;
    padding: 9px 10px;
    border-radius: 12px;
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 0.9rem;
    text-align: center;
    transition: border-color 0.25s ease, box-shadow 0.25s ease;
}

.rebate-form input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.rebate-form .btn-sm {
    padding: 8px 12px;
}

/* The claimable figure sits under the control, always — it is the number the
   bill is actually worked out from. */
.rebate-note {
    display: block;
    margin-top: 5px;
    font-size: 0.76rem;
    color: var(--muted);
}

.rebate-note strong {
    color: var(--accent);
}

.rebate-note.is-none {
    color: var(--muted);
}

/* Typed but not yet saved: the arithmetic is right, the record is not.
   Gold rather than navy, so a pending figure never reads as settled. */
.rebate-note.is-preview,
.rebate-note.is-preview strong {
    color: var(--accent-2);
}

/* Student sign-in notice */
.login-notice[open] {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    margin: 0;
    padding: 16px;
    border: 0;
    background: rgba(18, 35, 61, 0.45);
    color: #12233d;
}

.login-notice-card {
    width: min(560px, 100%);
    max-height: 100%;
    overflow-y: auto;
    padding: 24px 20px;
    background: #ffffff;
    border: 1px solid #dbe3ef;
    border-top: 2px solid #b8862b;
    border-radius: 18px;
    box-shadow: 0 20px 45px rgba(18, 35, 61, 0.14);
}

.login-notice-card h2 {
    margin: 0 0 14px;
    font-size: 1.25rem;
    color: #1b3f7a;
}

.login-notice-card p {
    margin: 0 0 12px;
    line-height: 1.6;
}

.login-notice-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 20px;
}

@media (min-width: 600px) {
    .login-notice-card {
        padding: 30px 32px;
    }
}
