@import url('../lib/fonts/fonts.css');

/* ===========================================================================
   Hall Bookings — "lodge minute book"

   Craft Freemasonry's midnight blue and antique gold, set in a classical serif
   for anything that names something and an institutional sans for anything you
   act on. Dignified rather than decorative: this is a booking ledger used by
   members of every age, often on a phone, sometimes in a badly lit room.
   =========================================================================== */

:root {
    /* Brand */
    --hb-navy-900: #081524;
    --hb-navy-800: #0d2135;
    --hb-navy-700: #143050;
    --hb-navy-600: #1d4470;
    --hb-navy-500: #2a5c93;

    --hb-gold-600: #8d6f1c;
    --hb-gold-500: #b08d24;
    --hb-gold-400: #c9a53c;
    --hb-gold-300: #e0c574;

    /* Surfaces — warm paper in light, deep ink in dark */
    --hb-paper: #f2efe7;
    --hb-surface: #ffffff;
    --hb-surface-sunk: #e9e5db;
    --hb-ink: #16202c;
    --hb-ink-soft: #5a6675;
    --hb-rule: #d9d3c6;

    /* The sidebar stays dark in both themes: it is the spine of the app. */
    --hb-rail-bg: var(--hb-navy-900);
    --hb-rail-fg: #b9c6d6;
    --hb-rail-fg-strong: #f2f5f9;
    --hb-rail-hover: rgba(255, 255, 255, .06);
    --hb-rail-active: rgba(201, 165, 60, .14);

    --hb-shadow-sm: 0 1px 2px rgba(8, 21, 36, .06), 0 1px 3px rgba(8, 21, 36, .04);
    --hb-shadow: 0 2px 4px rgba(8, 21, 36, .05), 0 8px 24px -12px rgba(8, 21, 36, .22);
    --hb-shadow-lg: 0 8px 20px -6px rgba(8, 21, 36, .18), 0 24px 60px -24px rgba(8, 21, 36, .35);

    --hb-radius: .5rem;
    --hb-radius-sm: .3rem;

    /* The bar across the floor of the app: the version strip under the rail and the footer beside
       it are both exactly this tall, so the two read as one continuous band. */
    --hb-bar-height: 3.25rem;

    --hb-display: 'Spectral', Georgia, 'Times New Roman', serif;
    --hb-sans: 'Public Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

:root[data-bs-theme="dark"] {
    --hb-paper: #070e17;
    --hb-surface: #0f1c2a;
    --hb-surface-sunk: #0a1522;
    --hb-ink: #e3eaf2;
    --hb-ink-soft: #93a3b6;
    --hb-rule: #1d2f43;

    --hb-rail-bg: #050c15;
    --hb-rail-fg: #93a3b6;
    --hb-rail-fg-strong: #f2f5f9;

    --hb-gold-500: #c9a53c;
    --hb-gold-600: #b08d24;

    --hb-shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
    --hb-shadow: 0 2px 4px rgba(0, 0, 0, .4), 0 10px 30px -14px rgba(0, 0, 0, .8);
    --hb-shadow-lg: 0 10px 30px -8px rgba(0, 0, 0, .6), 0 30px 70px -30px rgba(0, 0, 0, .9);
}

/* --- Bridge into Bootstrap so every component inherits the palette --------- */

:root,
:root[data-bs-theme="dark"] {
    --bs-body-bg: var(--hb-paper);
    --bs-body-color: var(--hb-ink);
    --bs-body-font-family: var(--hb-sans);
    --bs-body-font-size: .9375rem;
    --bs-body-line-height: 1.6;
    --bs-emphasis-color: var(--hb-ink);
    --bs-secondary-color: var(--hb-ink-soft);
    --bs-tertiary-bg: var(--hb-surface-sunk);
    --bs-border-color: var(--hb-rule);
    --bs-border-radius: var(--hb-radius);
    --bs-border-radius-sm: var(--hb-radius-sm);
    --bs-primary: var(--hb-navy-700);
    --bs-primary-rgb: 20, 48, 80;
    --bs-link-color: var(--hb-navy-600);
    --bs-link-hover-color: var(--hb-navy-500);
    --bs-heading-color: var(--hb-ink);
}

:root[data-bs-theme="dark"] {
    --bs-link-color: var(--hb-gold-400);
    --bs-link-hover-color: var(--hb-gold-300);
}

/* --- Base ---------------------------------------------------------------- */

body {
    background-color: var(--hb-paper);
    color: var(--hb-ink);
    font-family: var(--hb-sans);
    font-feature-settings: 'kern' 1, 'liga' 1;
    -webkit-font-smoothing: antialiased;
}

/* A whisper of grain so the light theme reads as paper rather than as a void. */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    opacity: .5;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.035'/%3E%3C/svg%3E");
}

:root[data-bs-theme="dark"] body::before { opacity: .28; }

h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
    font-family: var(--hb-display);
    font-weight: 600;
    letter-spacing: -.005em;
    color: var(--hb-ink);
}

/* Times and counts line up in columns when the figures are tabular. */
time, .tabular, .fc-list-event-time, .fc-timegrid-slot-label-cushion, .fc-col-header-cell-cushion {
    font-variant-numeric: tabular-nums;
}

/* Links carry their meaning in colour, not an underline. The hover colour shift and the
   focus ring keep the affordance for anyone not going by decoration alone. */
a, a:hover, a:focus, .alert-link, .btn-link, .nav-link, .dropdown-item {
    text-decoration: none;
}

a:hover { color: var(--bs-link-hover-color); }

::selection {
    background: var(--hb-gold-300);
    color: var(--hb-navy-900);
}

:focus-visible {
    outline: 2px solid var(--hb-gold-500);
    outline-offset: 2px;
    border-radius: var(--hb-radius-sm);
}

/* FocusOnNavigate parks focus on the page heading after every navigation, so a screen reader
   announces the new page instead of leaving the user where the old one was. That focus is the
   announcement, not an affordance: the heading carries tabindex="-1", cannot be tabbed to and
   does nothing when activated, so a ring around it is a box a sighted user cannot act on.
   Keep the focus, drop the decoration. Genuinely focusable things keep their ring above. */
h1[tabindex="-1"]:focus,
h1[tabindex="-1"]:focus-visible {
    outline: none;
}

/* --- Page furniture ------------------------------------------------------ */

.page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: .75rem;
    margin-bottom: 1.25rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--hb-rule);
}

.page-head h1 {
    margin: 0;
    font-size: 1.6rem;
    line-height: 1.2;
}

/* The hairline of gold under a page title is the one recurring flourish. It fades out rather than
   stopping, so the stated width is longer than the part that reads as a line. */
.page-head h1::after {
    content: '';
    display: block;
    width: 16rem;
    max-width: 100%;
    height: 2px;
    margin-top: .5rem;
    background: linear-gradient(90deg, var(--hb-gold-500), transparent);
}

.page-head .lede {
    margin: .35rem 0 0;
    color: var(--hb-ink-soft);
    font-size: .875rem;
    max-width: 62ch;
}

.eyebrow {
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--hb-ink-soft);
}

/* --- App shell ----------------------------------------------------------- */

.app-wrapper { position: relative; z-index: 1; }

.app-header {
    background: var(--hb-surface) !important;
    border-bottom: 1px solid var(--hb-rule);
    box-shadow: var(--hb-shadow-sm);
    backdrop-filter: saturate(140%) blur(6px);
}

.app-header .nav-link {
    color: var(--hb-ink-soft);
    border-radius: var(--hb-radius-sm);
    transition: color .15s ease, background-color .15s ease;
}

.app-header .nav-link:hover {
    color: var(--hb-ink);
    background: var(--hb-surface-sunk);
}

.app-sidebar {
    background: var(--hb-rail-bg) !important;
    border-right: 1px solid rgba(255, 255, 255, .05);
}

/* A thread of gold down the edge of the rail. */
.app-sidebar::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 1px;
    background: linear-gradient(180deg, transparent, rgba(201, 165, 60, .5) 22%, rgba(201, 165, 60, .5) 78%, transparent);
}

.sidebar-brand {
    background: transparent;
    border-bottom: 1px solid rgba(255, 255, 255, .07);
    padding: .35rem 0;
}

/* Brand, menu, version: a column so the version strip sits on the floor of the rail and the menu
   takes whatever is left. AdminLTE sizes .sidebar-wrapper with `calc(100vh - 3.5rem)`, which
   assumed the brand was the only other thing in here — overridden rather than re-calculated, so
   the strip's height is never hardcoded in two places. */
.app-sidebar {
    display: flex;
    flex-direction: column;
    height: 100dvh;
}

.sidebar-wrapper {
    height: auto;
    flex: 1 1 auto;

    /* Without this a flex item refuses to shrink below its content, and the menu would push the
       version strip off the bottom instead of scrolling. */
    min-height: 0;
}

.sidebar-version {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .3rem;

    /* Same token as .app-footer: the two are level by construction, not by coincidence. */
    height: var(--hb-bar-height);
    min-height: var(--hb-bar-height);
    padding: 0 .5rem;

    font-size: .75rem;
    letter-spacing: .04em;
    color: rgba(255, 255, 255, .45);
    border-top: 1px solid rgba(255, 255, 255, .07);
}

.app-main { position: relative; z-index: 1; }

/* Mirrors the pt-3 in the markup and nothing more. This was 3rem back when the footer sat in
   normal flow and content had to clear it; the footer is its own pinned row now, so that padding
   became dead space *inside* the scroller — enough that a page fitting the viewport still showed
   a scrollbar, and scrolling revealed only blank padding. */
.app-content { padding-bottom: 1rem; }

.app-footer {
    /* Flex rather than AdminLTE's padding plus a float, so the height is stated once and the two
       ends sit on the same baseline as the version strip next door. */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    height: var(--hb-bar-height);
    min-height: var(--hb-bar-height);
    padding: 0 1rem;
    background: transparent;
    border-top: 1px solid var(--hb-rule);
    color: var(--hb-ink-soft);
    font-size: .8125rem;
}

/* The footer stays on screen rather than scrolling away at the end of a long list. AdminLTE's
   shell is already a grid of header / main / footer, so pinning the wrapper to the viewport and
   scrolling the middle row keeps the header and footer put — no fixed positioning, no sidebar
   offsets to keep in step, and nothing overlapping the last row.

   dvh, not vh: on a phone vh counts the browser chrome that is not actually there. */
@media (min-width: 576px) {
    .app-wrapper {
        height: 100dvh;
        overflow: hidden;
    }

    /* Both lines earn their place. A `1fr` track is really minmax(auto, 1fr), and that auto floor
       is the item's min-content height — so without min-height:0 the middle row grows to fit the
       whole page and shoves the footer out of view. overflow-y then makes this the scroller.
       AdminLTE sets overflow itself, but not dependably enough to lean on. */
    .app-main {
        min-height: 0;
        overflow-y: auto;

        /* AdminLTE adds .75rem here as well. Two paddings stacked at the foot of a scroll area is
           how the viewport quietly loses height it never shows anything in. */
        padding-bottom: 0;
    }
}

/* On a phone the footer is decorative — the hall name beside it is already hidden at this
   width — and a permanent bar is a poor trade for the vertical space. Let the page scroll
   normally there instead. */
@media (max-width: 575.98px) {
    .app-footer { display: none; }
}

/* --- Brand mark ---------------------------------------------------------- */

.brand {
    display: flex;
    align-items: center;
    gap: .7rem;
    text-decoration: none;
    padding: .55rem .9rem;
    color: inherit;
}

.brand-emblem {
    flex: 0 0 auto;
    width: 2.1rem;
    height: 2.1rem;
    color: var(--hb-gold-400);
}

.brand-emblem img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: var(--hb-radius-sm);
}

.brand-text { min-width: 0; line-height: 1.15; }

.brand-name {
    display: block;
    font-family: var(--hb-display);
    font-weight: 600;
    font-size: 1.02rem;
    color: var(--hb-rail-fg-strong);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.brand-sub {
    display: block;
    font-size: .64rem;
    font-weight: 600;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--hb-gold-400);
}

/* --- Sidebar navigation -------------------------------------------------- */

.sidebar-menu .nav-link {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin: .1rem .55rem;
    padding: .5rem .7rem;
    border-radius: var(--hb-radius-sm);
    color: var(--hb-rail-fg);
    font-size: .875rem;
    font-weight: 500;
    transition: background-color .15s ease, color .15s ease;
}

.sidebar-menu .nav-link p { margin: 0; }

.sidebar-menu .nav-link .nav-icon {
    width: 1.15rem;
    font-size: 1rem;
    text-align: center;
    opacity: .85;
}

.sidebar-menu .nav-link:hover {
    background: var(--hb-rail-hover);
    color: var(--hb-rail-fg-strong);
}

.sidebar-menu .nav-item.active > .nav-link,
.sidebar-menu a.active .nav-link {
    background: var(--hb-rail-active);
    color: #fff;
    box-shadow: inset 2px 0 0 var(--hb-gold-400);
}

.sidebar-menu .nav-item.active > .nav-link .nav-icon { color: var(--hb-gold-300); opacity: 1; }

.nav-header {
    margin: 1.1rem .55rem .3rem;
    padding: 0 .7rem;
    font-size: .64rem;
    font-weight: 700;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: rgba(185, 198, 214, .55);
}

/* --- Cards --------------------------------------------------------------- */

.card {
    background: var(--hb-surface);
    border: 1px solid var(--hb-rule);
    border-radius: var(--hb-radius);
    box-shadow: var(--hb-shadow-sm);
    transition: box-shadow .18s ease, border-color .18s ease;
}

.card-header {
    background: transparent;
    border-bottom: 1px solid var(--hb-rule);
    padding: .8rem 1.1rem;
    font-family: var(--hb-display);
    font-weight: 600;
    font-size: 1rem;
    letter-spacing: -.005em;
}

.card-body { padding: 1.1rem; }

.card-footer {
    background: var(--hb-surface-sunk);
    border-top: 1px solid var(--hb-rule);
    padding: .75rem 1.1rem;
}

/* --- Buttons ------------------------------------------------------------- */

.btn {
    --bs-btn-font-weight: 600;
    --bs-btn-font-size: .875rem;
    --bs-btn-padding-y: .45rem;
    --bs-btn-padding-x: .9rem;
    --bs-btn-border-radius: var(--hb-radius-sm);
    letter-spacing: .01em;
    transition: transform .12s ease, box-shadow .15s ease, background-color .15s ease;
}

.btn:active { transform: translateY(1px); }

.btn-primary {
    --bs-btn-bg: var(--hb-navy-700);
    --bs-btn-border-color: var(--hb-navy-700);
    --bs-btn-hover-bg: var(--hb-navy-600);
    --bs-btn-hover-border-color: var(--hb-navy-600);
    --bs-btn-active-bg: var(--hb-navy-800);
    --bs-btn-active-border-color: var(--hb-navy-800);
    --bs-btn-disabled-bg: var(--hb-navy-700);
    --bs-btn-disabled-border-color: var(--hb-navy-700);
    box-shadow: var(--hb-shadow-sm);
}

.btn-outline-primary {
    --bs-btn-color: var(--hb-navy-600);
    --bs-btn-border-color: var(--hb-navy-600);
    --bs-btn-hover-bg: var(--hb-navy-700);
    --bs-btn-hover-border-color: var(--hb-navy-700);
    --bs-btn-active-bg: var(--hb-navy-800);
}

:root[data-bs-theme="dark"] .btn-outline-primary {
    --bs-btn-color: var(--hb-gold-400);
    --bs-btn-border-color: rgba(201, 165, 60, .5);
    --bs-btn-hover-bg: var(--hb-gold-600);
    --bs-btn-hover-border-color: var(--hb-gold-600);
    --bs-btn-hover-color: #0a1522;
}

/* The single gold button: reserved for confirming a booking. */
.btn-gold {
    --bs-btn-color: #1a1200;
    --bs-btn-bg: var(--hb-gold-400);
    --bs-btn-border-color: var(--hb-gold-500);
    --bs-btn-hover-color: #1a1200;
    --bs-btn-hover-bg: var(--hb-gold-300);
    --bs-btn-hover-border-color: var(--hb-gold-400);
    --bs-btn-active-bg: var(--hb-gold-500);
    --bs-btn-disabled-color: #1a1200;
    --bs-btn-disabled-bg: var(--hb-gold-400);
    --bs-btn-disabled-border-color: var(--hb-gold-500);
    box-shadow: var(--hb-shadow-sm);
}

/* --- Forms --------------------------------------------------------------- */

.form-label {
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--hb-ink-soft);
    margin-bottom: .3rem;
}

.form-control, .form-select {
    background-color: var(--hb-surface);
    border-color: var(--hb-rule);
    border-radius: var(--hb-radius-sm);
    padding: .5rem .7rem;
    font-size: .9375rem;
    color: var(--hb-ink);
    transition: border-color .15s ease, box-shadow .15s ease;
}

.form-control:focus, .form-select:focus {
    background-color: var(--hb-surface);
    border-color: var(--hb-gold-500);
    box-shadow: 0 0 0 3px rgba(201, 165, 60, .18);
    color: var(--hb-ink);
}

.form-text { color: var(--hb-ink-soft); font-size: .78rem; }

.form-check-input:checked {
    background-color: var(--hb-navy-700);
    border-color: var(--hb-navy-700);
}

:root[data-bs-theme="dark"] .form-check-input:checked {
    background-color: var(--hb-gold-500);
    border-color: var(--hb-gold-500);
}

.form-check-input:focus {
    border-color: var(--hb-gold-500);
    box-shadow: 0 0 0 3px rgba(201, 165, 60, .18);
}

.input-group-text {
    background: var(--hb-surface-sunk);
    border-color: var(--hb-rule);
    color: var(--hb-ink-soft);
}

/* --- Tables -------------------------------------------------------------- */

.table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--hb-ink);
    --bs-table-border-color: var(--hb-rule);
    --bs-table-hover-bg: var(--hb-surface-sunk);
    margin-bottom: 0;
}

.table > thead th {
    background: var(--hb-surface-sunk);
    border-bottom: 1px solid var(--hb-rule);
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--hb-ink-soft);
    padding: .6rem .9rem;
    white-space: nowrap;
}

.table > tbody td {
    padding: .7rem .9rem;
    vertical-align: middle;
    border-color: var(--hb-rule);
}

/* --- Badges -------------------------------------------------------------- */

.badge {
    font-weight: 600;
    font-size: .68rem;
    letter-spacing: .05em;
    padding: .34em .6em;
    border-radius: 999px;
    text-transform: uppercase;
}

.text-bg-primary { background-color: var(--hb-navy-700) !important; color: #fff !important; }
.text-bg-warning { background-color: #9a6b12 !important; color: #fff !important; }
.text-bg-success { background-color: #1f6b45 !important; color: #fff !important; }
.text-bg-danger  { background-color: #96261f !important; color: #fff !important; }
.text-bg-info    { background-color: var(--hb-navy-500) !important; color: #fff !important; }

/* --- Booking type colour swatch ---------------------------------------- */

.colour-swatch {
    display: inline-block;
    width: .9rem;
    height: .9rem;
    border-radius: 3px;
    vertical-align: -1px;
    border: 1px solid rgba(0, 0, 0, .25);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .3);
}

/* --- Toasts -------------------------------------------------------------- */

.toast-stack {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 1090;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    max-width: min(24rem, calc(100vw - 2rem));
}

.toast-stack .toast {
    border: 0;
    border-radius: var(--hb-radius);
    box-shadow: var(--hb-shadow-lg);
    border-left: 3px solid rgba(255, 255, 255, .55);
    animation: toast-in .22s cubic-bezier(.2, .8, .2, 1);
}

@keyframes toast-in {
    from { opacity: 0; transform: translateY(.6rem) scale(.98); }
    to   { opacity: 1; transform: none; }
}

/* On a phone there is no comfortable bottom-right, so they come down from the top. */
@media (max-width: 575.98px) {
    .toast-stack {
        left: .5rem;
        right: .5rem;
        top: .5rem;
        bottom: auto;
        max-width: none;
    }

    @keyframes toast-in {
        from { opacity: 0; transform: translateY(-.6rem); }
        to   { opacity: 1; transform: none; }
    }
}

/* --- Calendar ------------------------------------------------------------ */

.fc {
    --fc-border-color: var(--hb-rule);
    --fc-page-bg-color: transparent;
    --fc-neutral-bg-color: var(--hb-surface-sunk);
    --fc-today-bg-color: rgba(201, 165, 60, .09);
    --fc-list-event-hover-bg-color: var(--hb-surface-sunk);
    font-size: .85rem;
}

.fc .fc-toolbar-title {
    font-family: var(--hb-display);
    font-size: 1.25rem;
    font-weight: 600;
}

.fc .fc-col-header-cell-cushion {
    padding: .5rem .25rem;
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--hb-ink-soft);
    text-decoration: none;
}

.fc .fc-daygrid-day-number { font-size: .8rem; color: var(--hb-ink-soft); text-decoration: none; }
.fc .fc-day-today .fc-daygrid-day-number { color: var(--hb-gold-600); font-weight: 700; }

.fc .fc-button {
    background: var(--hb-surface);
    border-color: var(--hb-rule);
    color: var(--hb-ink);
    font-size: .78rem;
    font-weight: 600;
    text-transform: capitalize;
    padding: .35rem .65rem;
    box-shadow: none;
}

.fc .fc-button:hover { background: var(--hb-surface-sunk); border-color: var(--hb-rule); color: var(--hb-ink); }

.fc .fc-button-primary:not(:disabled).fc-button-active,
.fc .fc-button-primary:not(:disabled):active {
    background: var(--hb-navy-700);
    border-color: var(--hb-navy-700);
    color: #fff;
}

.fc .fc-button-primary:focus,
.fc .fc-button-primary:not(:disabled).fc-button-active:focus { box-shadow: 0 0 0 3px rgba(201, 165, 60, .25); }

.fc-event {
    cursor: pointer;
    padding: 1px 4px;
    border-radius: 3px;
    font-weight: 500;
    border-width: 1px;
}

/* Confirmed reads solid; pending is hatched AND dashed, so the difference
   survives colour blindness and a monochrome print-out. */
.fc-event.booking-pending {
    border-style: dashed !important;
    border-width: 2px !important;
    background-image: repeating-linear-gradient(45deg,
            rgba(255, 255, 255, .38) 0 5px,
            rgba(255, 255, 255, 0) 5px 10px);
}

.fc-event.booking-partial {
    border-style: dotted !important;
    border-width: 2px !important;
    background-image: repeating-linear-gradient(45deg,
            rgba(255, 255, 255, .2) 0 3px,
            rgba(255, 255, 255, 0) 3px 9px);
}

/* --- Event layout -------------------------------------------------------- */
/* Two ways to read a booking: stacked detail in week and day, one compact line
   in month. The colour is the block; these arrange the words on top of it. */
.fc-hb { overflow: hidden; }

.fc-hb-detail { display: flex; flex-direction: column; line-height: 1.2; }
.fc-hb-detail .fc-hb-time { font-weight: 700; }
.fc-hb-detail .fc-hb-lodge { font-weight: 600; }
.fc-hb-detail .fc-hb-title { font-weight: 400; }
.fc-hb-detail .fc-hb-rooms { font-size: .82em; opacity: .85; }

/* Month: everything on one line, the lodge name ellipsised rather than wrapping. */
.fc-hb-month { display: flex; align-items: baseline; gap: .3rem; white-space: nowrap; }
.fc-hb-month .fc-hb-time { font-weight: 700; }
.fc-hb-month .fc-hb-recur { flex: 0 0 auto; }
.fc-hb-month .fc-hb-lodge { overflow: hidden; text-overflow: ellipsis; }

@media (max-width: 767.98px) {
    .fc .fc-toolbar { flex-direction: column; gap: .5rem; }
    .fc .fc-toolbar-chunk { display: flex; gap: .25rem; }
}

/* --- Room picker --------------------------------------------------------- */

.room-tick {
    border: 1px solid var(--hb-rule);
    border-radius: var(--hb-radius-sm);
    padding: .6rem .8rem;
    background: var(--hb-surface);
    transition: border-color .15s ease, background-color .15s ease;
}

.room-tick:hover { border-color: var(--hb-navy-500); }

.room-tick.is-clashing {
    border-color: #96261f;
    background: rgba(150, 38, 31, .06);
}

.room-tick.is-contested {
    border-color: var(--hb-gold-500);
    background: rgba(201, 165, 60, .08);
}

.legend-dot {
    display: inline-block;
    width: .7rem;
    height: .7rem;
    border-radius: 50%;
    margin-right: .35rem;
    vertical-align: -1px;
}

/* --- Sign-in ------------------------------------------------------------- */

.auth-shell {
    min-height: 100dvh;
    display: grid;
    place-items: center;
    padding: 1.5rem;
    background:
        radial-gradient(1100px 620px at 50% -12%, rgba(29, 68, 112, .5), transparent 62%),
        linear-gradient(180deg, var(--hb-navy-900), #04090f);
    position: relative;
    overflow: hidden;
}

/* Faint compasses arc behind the card — atmosphere, not decoration. */
.auth-shell::after {
    content: '';
    position: absolute;
    width: 46rem;
    height: 46rem;
    border: 1px solid rgba(201, 165, 60, .1);
    border-radius: 50%;
    top: -18rem;
    pointer-events: none;
}

.auth-card {
    position: relative;
    width: min(26rem, 100%);
    background: var(--hb-surface);
    border: 1px solid var(--hb-rule);
    border-radius: calc(var(--hb-radius) * 1.4);
    box-shadow: var(--hb-shadow-lg);
    overflow: hidden;
    animation: auth-rise .4s cubic-bezier(.2, .8, .2, 1) both;
}

.auth-card::before {
    content: '';
    display: block;
    height: 3px;
    background: linear-gradient(90deg, var(--hb-gold-600), var(--hb-gold-300), var(--hb-gold-600));
}

@keyframes auth-rise {
    from { opacity: 0; transform: translateY(1rem); }
    to   { opacity: 1; transform: none; }
}

.auth-card .brand-emblem { width: 3.1rem; height: 3.1rem; color: var(--hb-gold-500); }

.auth-title {
    font-family: var(--hb-display);
    font-size: 1.45rem;
    font-weight: 600;
    margin: .75rem 0 .15rem;
}

.auth-sub { color: var(--hb-ink-soft); font-size: .85rem; margin-bottom: 1.5rem; }

.auth-rule {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin: 1.25rem 0 0;
    color: var(--hb-ink-soft);
    font-size: .72rem;
    letter-spacing: .1em;
    text-transform: uppercase;
}

.auth-rule::before, .auth-rule::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--hb-rule);
}

/* --- Empty states -------------------------------------------------------- */

/* Log entries are machine output: a monospaced face makes an id or a path scannable, and the
   exception keeps its own line breaks rather than being reflowed into a paragraph. */
.log-message {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .8125rem;
    word-break: break-word;
}

/* The whole row opens the detail panel, so it needs to look like it does something. */
.log-row { cursor: pointer; }

/* A modal rendered by a page sits inside .app-main, whose z-index makes it a stacking context — so
   the dialog is pinned at that layer and the header (1034) and sidebar (1038) paint over it, however
   high the modal's own 1055 is. ConfirmDialog sidesteps this by living in App.razor, outside the
   shell; a page's own dialog cannot, so the shell stands down for exactly as long as one is open.
   Scoped with :has so the ordinary layering, which the background depends on, is left alone. */
.app-main:has(.modal.show) { z-index: auto; }

/* A stat tile, not a bar: three counts whose magnitudes differ by orders of magnitude compare
   badly as bars, and the smallest of them is the one that matters. The label wears the status
   colour on its icon only — the value stays in ordinary ink, so identity never rests on colour and
   a day with four errors does not read as an emergency. */
/* Equal shares of the full width rather than a fixed column count: three tiles take a third each,
   and on the days a fourth appears they simply take a quarter each instead of leaving one stranded
   on a second row. Below the basis they wrap, because a third of a phone is not a readable tile. */
.stat-row {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

.stat-row > .stat-tile { flex: 1 1 11rem; }

.stat-tile {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .15rem;
    width: 100%;
    padding: .6rem .85rem;
    text-align: left;
    background: var(--hb-surface);
    border: 1px solid var(--hb-rule);
    border-radius: var(--hb-radius-sm);
    transition: border-color .15s ease, background-color .15s ease;
}

.stat-tile:hover { border-color: var(--hb-gold-500); }

.stat-tile.is-selected {
    border-color: var(--hb-gold-500);
    box-shadow: inset 0 0 0 1px var(--hb-gold-500);
}

.stat-tile-label {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .75rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--hb-ink-soft);
}

/* Proportional figures, not tabular: these stand alone rather than in a column, and tabular
   spacing makes a three-digit number look gappy at this size. */
.stat-tile-value {
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.1;
    color: var(--hb-ink);
}

/* Grid rows show the first couple of lines and the detail panel shows the rest. Only the display is
   clipped: searching runs server-side against the whole message, so a match still finds an entry
   whose matching words are past the fold. */
.log-message--clamped {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.log-exception {
    margin: .35rem 0 0;
    padding: .5rem .65rem;
    max-height: 18rem;
    overflow: auto;
    font-size: .75rem;
    background: var(--hb-surface-2, rgba(0, 0, 0, .2));
    border-radius: var(--hb-radius-sm);
    white-space: pre-wrap;
}

.empty-state {
    text-align: center;
    padding: 3rem 1.5rem;
    color: var(--hb-ink-soft);
}

.empty-state .bi { font-size: 2.25rem; color: var(--hb-gold-500); opacity: .8; }
.empty-state h2 { font-size: 1.1rem; margin: .75rem 0 .25rem; }
.empty-state p { max-width: 38ch; margin: 0 auto; font-size: .875rem; }

/* --- Tables collapse into cards on a phone ------------------------------- */

@media (max-width: 575.98px) {
    .table-stack thead { display: none; }

    .table-stack tbody tr {
        display: block;
        border: 1px solid var(--hb-rule);
        border-radius: var(--hb-radius);
        margin-bottom: .75rem;
        padding: .5rem;
        background: var(--hb-surface);
    }

    .table-stack tbody td {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 1rem;
        border: 0;
        padding: .35rem .25rem;
    }

    .table-stack tbody td::before {
        content: attr(data-label);
        font-size: .7rem;
        font-weight: 700;
        letter-spacing: .08em;
        text-transform: uppercase;
        color: var(--hb-ink-soft);
    }
}

/* --- Respect the OS motion preference ------------------------------------ */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
}

/* --- Drag to reorder ----------------------------------------------------- */

.drag-handle {
    cursor: grab;
    color: var(--bs-secondary-color);
    background: none;
    border: 0;
    padding: .25rem .4rem;
    line-height: 1;
    border-radius: .25rem;

    /* Without this a touch drag scrolls the page instead of moving the row. */
    touch-action: none;
}

.drag-handle:hover,
.drag-handle:focus-visible {
    color: var(--bs-body-color);
    background: var(--bs-secondary-bg);
}

.is-reordering {
    /* No text selection dragging along behind the row. */
    user-select: none;
}

.is-reordering .drag-handle {
    cursor: grabbing;
}

.drag-row.is-dragging > td {
    background: var(--bs-secondary-bg);
    opacity: .75;
}
