/* ==========================================================================
   Castle CRM — stylesheet
   --------------------------------------------------------------------------
   Hand-written, no framework, no build step. Staff look at this for eight
   hours a shift, so it optimises for legibility and low fatigue over visual
   impact, with the castle-purple accent used only to mark action and state.

   Colour carries meaning only alongside a label or an icon, never alone.
   Contrast targets WCAG AA: 4.5:1 for body text, 3:1 for large text and UI
   boundaries. Light and dark are both first-class.
   ========================================================================== */

/* ---------------------------------------------------------------- Tokens */

:root {
    /* Brand */
    --brand:        #6b4ea8;
    --brand-dark:   #553d87;
    --brand-light:  #f3effa;
    --brand-ring:   rgba(107, 78, 168, .35);

    /* Neutrals */
    --ink:          #1b1f26;
    --ink-soft:     #3d454f;
    --muted:        #5b6472;
    /* Darker than it looks like it wants to be: .muted, .empty, breadcrumbs
       and card meta are all real text in this colour, so it has to clear
       4.5:1 against every surface it sits on, not merely look subdued. The
       old #8b95a3 managed 3.0:1 on white. */
    --faint:        #68717f;
    --line:         #e3e7ec;
    --line-strong:  #cdd4dc;
    /* The visible boundary of a control. WCAG 1.4.11 asks 3:1 for that, which
       a hairline the colour of a divider (--line-strong, 1.5:1) does not meet
       — and an input whose edge you cannot find is not a cosmetic problem. */
    --field-border: #828b98;
    /* Text and icons ON a saturated fill: brand buttons, danger buttons, the
       brand mark. White in light mode; in dark mode the fills are pastel, so
       white on them fails and near-black is correct. */
    --on-accent:    #ffffff;
    --bg:           #f6f7f9;
    --panel:        #ffffff;
    --panel-soft:   #fafbfc;

    /* Semantic — reserved for state, never decoration */
    --ok:           #0f7b4f;
    --ok-bg:        #e8f6ef;
    --warn:         #8a5a00;
    --warn-bg:      #fff4e5;
    --bad:          #b42318;
    --bad-bg:       #fdeceb;
    --info:         #1c5fa8;
    --info-bg:      #e8f1fb;

    /* Geometry */
    --radius:       10px;
    --radius-sm:    6px;
    /* Every single-line form control, and the buttons that sit beside one. */
    --control-h:    2.5rem;
    --shadow:       0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .06);
    --shadow-lg:    0 4px 6px -2px rgba(16, 24, 40, .05), 0 12px 16px -4px rgba(16, 24, 40, .08);

    /* Rhythm */
    --gap:          1rem;
    --gap-lg:       1.5rem;
    --sidebar-w:    15.5rem;
}

/* Dark theme: applied by preference, and overridable per user. The
   :not([data-theme="light"]) guard lets an explicit light choice win over a
   dark-mode device. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --brand:       #9d7fd4;
        --brand-dark:  #b49ae0;
        --brand-light: #241d33;
        --brand-ring:  rgba(157, 127, 212, .4);

        --ink:         #e8eaed;
        --ink-soft:    #c9ced6;
        --muted:       #a8b0bb;
        --faint:       #8a93a1;
        --line:        #2d333d;
        --line-strong: #3c444f;
        --field-border: #767f8d;
        --on-accent:   #14171c;
        --bg:          #14171c;
        --panel:       #1c2027;
        --panel-soft:  #22262e;

        --ok:          #4ade80;
        --ok-bg:       #11291f;
        --warn:        #fbbf24;
        --warn-bg:     #2a2113;
        --bad:         #f87171;
        --bad-bg:      #2a1716;
        --info:        #7dd3fc;
        --info-bg:     #11222e;

        --shadow:      0 1px 2px rgba(0, 0, 0, .3);
        --shadow-lg:   0 8px 24px rgba(0, 0, 0, .35);
    }
}

:root[data-theme="dark"] {
    --brand:       #9d7fd4;
    --brand-dark:  #b49ae0;
    --brand-light: #241d33;
    --brand-ring:  rgba(157, 127, 212, .4);

    --ink:         #e8eaed;
    --ink-soft:    #c9ced6;
    --muted:       #a8b0bb;
    --faint:       #8a93a1;
    --line:        #2d333d;
    --line-strong: #3c444f;
    --field-border: #767f8d;
    --on-accent:   #14171c;
    --bg:          #14171c;
    --panel:       #1c2027;
    --panel-soft:  #22262e;

    --ok:          #4ade80;
    --ok-bg:       #11291f;
    --warn:        #fbbf24;
    --warn-bg:     #2a2113;
    --bad:         #f87171;
    --bad-bg:      #2a1716;
    --info:        #7dd3fc;
    --info-bg:     #11222e;

    --shadow:      0 1px 2px rgba(0, 0, 0, .3);
    --shadow-lg:   0 8px 24px rgba(0, 0, 0, .35);
}

/* ------------------------------------------------------------------ Reset */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    /* System stack: no web-font fetch, no layout shift, no third-party request */
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                 "Helvetica Neue", Arial, sans-serif;
    /* 15px at a default 16px root, but expressed in rem so that a reader who
       has raised their browser's default font size gets larger text here too.
       A px value here would pin the body text and quietly ignore the first
       accommodation most people with low vision ever set. */
    font-size: .9375rem;
    line-height: 1.55;
    color: var(--ink);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0; font-weight: 650; letter-spacing: -.011em; line-height: 1.3; }
h1 { font-size: 1.3rem; }
h2 { font-size: 1.05rem; }
h3 { font-size: .95rem; }

/* A card's own heading, when it is not inside .card-head, is followed
   directly by a label or a paragraph; without this the two touch. */
.card > h2:first-child { margin-bottom: .85rem; }

p { margin: 0 0 .75rem; }
p:last-child { margin-bottom: 0; }

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }
/* A link sitting inside running text is underlined, not only coloured: a
   reader who cannot tell purple from grey still has to find it (WCAG 1.4.1).
   Links that stand alone (a table's guest names, the sidebar, buttons) keep
   colour alone, because nothing around them is text to confuse them with. */
:where(p, dd, .breadcrumbs, .page-lede, .empty, .field-hint, .health-warning, .card-sub) > a:not(.btn) {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: .18em;
}

ul, ol { margin: 0 0 .75rem; padding-left: 1.25rem; }

code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .85em; }
/* For a DNS value or a long token in a narrow table cell: break anywhere
   rather than forcing the whole page to scroll sideways. */
code.wrap { overflow-wrap: anywhere; word-break: break-word; }

/* Focus is always visible. Removing it would make the app unusable by
   keyboard, which several of these screens are designed around. */
:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
    border-radius: 2px;
}

::selection { background: var(--brand-ring); }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
}

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.skip-link {
    position: absolute; top: -3rem; left: .5rem; z-index: 100;
    padding: .5rem 1rem; background: var(--brand); color: var(--on-accent);
    border-radius: var(--radius-sm);
}
.skip-link:focus { top: .5rem; }

.muted { color: var(--faint); }

/* ------------------------------------------------------------------ Shell */

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

.sidebar {
    flex: 0 0 var(--sidebar-w);
    width: var(--sidebar-w);
    background: var(--nav-bg);
    border-right: 0;
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    color: var(--nav-ink);
    overflow: hidden;
}

.sidebar-brand { padding: 1.1rem 1rem; border-bottom: 1px solid var(--nav-line); position: relative; }

.brand-link { display: flex; gap: .65rem; align-items: center; color: #fff; }
.brand-link:hover { text-decoration: none; }

.brand-mark {
    flex: 0 0 2.2rem; height: 2.2rem;
    display: grid; place-items: center;
    background: var(--brand); color: var(--on-accent); border-radius: var(--radius-sm);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .3);
}
/* The venue's own mark fills the square; it carries its own colours. */
.brand-mark img { width: 100%; height: 100%; border-radius: inherit; display: block; }

.brand-text { display: flex; flex-direction: column; min-width: 0; }
.brand-text strong { font-size: .95rem; letter-spacing: -.01em; }
.brand-text small { font-size: .75rem; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.sidebar-nav { flex: 1 1 auto; overflow-y: auto; padding: .75rem .5rem; position: relative; z-index: 1; }

.nav-heading {
    margin: 1rem .5rem .35rem;
    font-size: .68rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: .07em; color: var(--nav-faint);
}

.nav-list { list-style: none; margin: 0; padding: 0; }

.nav-link {
    display: flex; gap: .6rem; align-items: center;
    padding: .45rem .55rem; margin-bottom: 1px;
    border-radius: var(--radius-sm);
    color: var(--nav-ink); font-size: .95rem; font-weight: 500;
    position: relative;
}
.nav-link:hover { background: var(--bg); color: var(--ink); text-decoration: none; }
.nav-link .icon { flex: 0 0 auto; color: var(--faint); }

.nav-link.is-current {
    background: var(--brand-light); color: var(--brand-dark); font-weight: 600;
}
.nav-link.is-current .icon { color: var(--brand); }

/* A laptop screen: tighter rows, so more of the menu fits without scrolling. */
@media (min-width: 60rem) and (max-height: 960px) {
    .sidebar-nav { padding-block: .5rem; }
    .nav-link { padding-block: .34rem; }
    .nav-heading { margin-top: .75rem; }
}

.sidebar-foot .btn-icon.is-current { color: var(--brand); background: var(--brand-light); }

.sidebar-foot {
    padding: .6rem;
    display: flex; gap: .5rem; align-items: center;
    position: relative;
    z-index: 1;
    background: rgba(0, 0, 0, .14);
}

.user-chip {
    flex: 1 1 auto; min-width: 0;
    display: flex; gap: .55rem; align-items: center;
    padding: .35rem; border-radius: var(--radius-sm); color: #fff;
}
.user-chip:hover { background: var(--bg); text-decoration: none; }

.avatar {
    flex: 0 0 1.9rem; height: 1.9rem; border-radius: 50%;
    background: var(--brand-light); color: var(--brand-dark);
    display: grid; place-items: center;
    font-size: .72rem; font-weight: 700;
}

.user-text { display: flex; flex-direction: column; min-width: 0; }
.user-text strong { font-size: .82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-text small { font-size: .7rem; color: var(--faint); }

.logout-form { flex: 0 0 auto; margin: 0; }

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

.topbar {
    display: flex; gap: .75rem; align-items: center;
    padding: .7rem 1.25rem;
    background: var(--panel); border-bottom: 1px solid var(--line);
    position: sticky; top: 0; z-index: 20;
}

.page-title { flex: 1 1 auto; min-width: 0; font-size: 1.45rem; text-transform: uppercase; letter-spacing: .02em; }

.topbar-right { display: flex; gap: .5rem; align-items: center; }

.nav-toggle { display: none; }

.content { flex: 1 1 auto; padding: 1.25rem; max-width: 80rem; width: 100%; }

/* ----------------------------------------------------------- Bare layout */

body.bare {
    display: grid; place-items: center; min-height: 100vh; padding: 1.5rem 1rem;
    /* The Castle's royal ground behind sign-in and every guest page, with the
       mark's orange and blue glowing from opposite corners. Not `fixed`: a
       fixed background stops at the first screen on a long form in some
       browsers, and iOS ignores it. The colour underneath carries on. */
    background-color: var(--royal);
    background-image:
        radial-gradient(90% 70% at 85% 0%, rgba(235, 149, 33, .28) 0%, transparent 60%),
        radial-gradient(80% 60% at 0% 100%, rgba(7, 124, 199, .30) 0%, transparent 60%),
        linear-gradient(160deg, var(--royal-deep) 0%, var(--royal) 60%, #4a138f 100%);
}

.bare-main { width: 100%; max-width: 24rem; }

.bare-card {
    background: var(--panel); border: 0;
    border-radius: var(--radius); padding: 2rem; box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .55);
    position: relative;
}

.bare-brand {
    display: flex; gap: .6rem; align-items: center; justify-content: center;
    margin-bottom: 1.5rem;
}
.bare-brand .brand-mark { flex: 0 0 2.4rem; height: 2.4rem; }

.bare-title { text-align: center; font-size: 1.7rem; margin-bottom: .35rem; text-transform: uppercase; letter-spacing: .02em; }
.bare-sub { text-align: center; color: var(--muted); font-size: .9rem; margin-bottom: 1.5rem; }
.bare-foot { text-align: center; font-size: .85rem; color: var(--muted); margin-top: 1.25rem; }

/* ------------------------------------------------------------------ Cards */

.card {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--radius); padding: 1.25rem;
    margin-bottom: var(--gap); box-shadow: var(--shadow);
}

.card-accent { border-left: 3px solid var(--brand); }
.card-danger { border-color: var(--bad); }
/* A card after a form starts clear of the form's buttons, which otherwise sit
   on its top edge — the delete card under an edit form most of all. */
form:not(.card) + .card { margin-top: calc(var(--gap) * 1.5); }
.card-danger h2 { color: var(--bad); }

.card-head {
    display: flex; gap: var(--gap); align-items: flex-start; justify-content: space-between;
    margin-bottom: 1rem; flex-wrap: wrap;
}
.card-sub { color: var(--muted); font-size: .9rem; margin: .2rem 0 0; }
/* The title block takes the room and wraps its own text, so a long subtitle
   no longer pushes the card's button down onto a line of its own; the button
   stays top-right, as on every other card. On a phone it still drops below. */
.card-head > div:first-child:not(:only-child) { flex: 1 1 18rem; min-width: 0; }
.card-head > div:first-child:not(:only-child) ~ .btn { flex: none; }
/* A heading's subtitle belongs under it, not floated to the far side of the
   card where the actions go. */
.card-head > h2 + .card-sub { flex-basis: 100%; order: 3; margin-top: calc(.2rem - var(--gap)); }

.greeting { font-size: 1.05rem; color: var(--muted); margin-bottom: var(--gap); }

/* Today, at the top of the dashboard: the day, the hours, the parties. */
.today-when { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; }
.today-when .icon { color: var(--faint); }
.today-bit { display: inline-flex; align-items: center; gap: .3rem; white-space: nowrap; }
.today-sep { color: var(--line-strong); margin-inline: .2rem; }
.today-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.today-empty { margin: .25rem 0 0; color: var(--muted); }
.today-list { list-style: none; margin: .5rem 0 0; padding: 0; }
.today-party {
    display: grid; gap: .25rem 1rem; align-items: center;
    grid-template-columns: 6.5rem minmax(0, 2fr) minmax(0, 1.2fr) 7rem;
    padding: .7rem 0; border-top: 1px solid var(--line);
}
.today-party small { display: block; color: var(--faint); font-size: .78rem; }
.today-time strong { font-size: .95rem; }
.today-who a { font-weight: 600; }
.today-count { text-align: right; }
.today-allergy { color: var(--warn); font-weight: 600; white-space: nowrap; }
.today-allergy .icon { vertical-align: -.15em; }
@media (max-width: 38rem) {
    .today-party { grid-template-columns: 5rem minmax(0, 1fr); }
    .today-room, .today-count { grid-column: 2; text-align: left; }
}
.page-lede { color: var(--muted); margin-bottom: var(--gap); max-width: 46rem; }

.page-actions {
    display: flex; gap: var(--gap); align-items: flex-start; justify-content: space-between;
    margin-bottom: var(--gap); flex-wrap: wrap;
}

.breadcrumbs { font-size: .85rem; color: var(--faint); margin-bottom: .75rem; }

/*
 * A row of filter links that reads as a set of tabs.
 *
 * The selected one is marked by a SOLID BACKGROUND and bolder text, not by
 * colour alone — `.is-active` has to be legible to somebody who cannot tell
 * this blue from this grey, and on the Text Club list it is the only thing
 * saying which slice of the list is on screen.
 */
.tab-bar {
    display: flex; flex-wrap: wrap; gap: .25rem;
    padding: .2rem; border-radius: var(--radius-sm);
    background: var(--bg); border: 1px solid var(--line);
}
.tab {
    padding: .3rem .7rem; border-radius: calc(var(--radius-sm) - .1rem);
    font-size: .82rem; font-weight: 600; color: var(--muted);
    white-space: nowrap; text-decoration: none;
}
.tab:hover { background: var(--panel-soft); color: var(--text); text-decoration: none; }
.tab.is-active { background: var(--panel); color: var(--text); box-shadow: var(--shadow); }
.tab:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

.grid-2 { display: grid; gap: var(--gap); align-items: start; }
@media (min-width: 60rem) { .grid-2 { grid-template-columns: 1.15fr .85fr; } }

.stack { display: flex; flex-direction: column; gap: var(--gap); }
.stack-lg { display: flex; flex-direction: column; gap: var(--gap); }

/*
 * A COLUMN MAY BE NARROWER THAN ITS WIDEST UNBREAKABLE LINE.
 *
 * A grid or flex item's automatic minimum size is its content's min-content
 * width, and `white-space: nowrap` has no soft wrap opportunities — so its
 * min-content width is the WHOLE line. One such line anywhere inside a column
 * therefore sets a floor under the column, the column sets one under the grid,
 * and the page scrolls sideways. `overflow: hidden` and `text-overflow:
 * ellipsis` do not change that: they need a width to ellipsize against, and
 * the floor is why they never get one.
 *
 * MEASURED on the dashboard at 390px: scrollWidth 487, traced by hiding
 * elements one at a time to `.playbook-tagline` — a one-line, ellipsised
 * tagline inside a card inside `.grid-2`. `min-width: 0` on the item removes
 * the floor, the card takes the track's width, and the ellipsis finally has
 * something to work with.
 *
 * It is set on the containers' children rather than on that one tagline
 * because the next nowrap line somebody adds will be somewhere else.
 */
.grid-2 > *, .stack > *, .stack-lg > * { min-width: 0; }

/* The stack's gap spaces its cards; their own margin would double it. */
.stack > .card, .stack-lg > .card { margin-bottom: 0; }
.grid-2 > .stack, .grid-2 > .stack-lg { margin-bottom: var(--gap); }

/* Two peer cards side by side end level: the shorter grows to meet the
   taller, and in a column of cards the last one takes up the difference. */
.grid-pair { align-items: stretch; }
.grid-pair > .stack > .card:last-child, .grid-pair > .stack-lg > .card:last-child { flex: 1 1 auto; }

/* A short card beside a long list stays in view while the list scrolls. A
   card as tall as the row has nowhere to move, so only the shorter sticks. */
@media (min-width: 60rem) {
    .grid-aside > * { position: sticky; top: calc(4.25rem + env(safe-area-inset-top, 0px)); }
}

/* A card's last line does not add space under itself: the card's padding is
   the space. */
.card > :last-child, .card > form:last-child > :last-child, .card > .stat-row:last-child { margin-bottom: 0; }

.empty { color: var(--faint); font-style: italic; padding: .5rem 0; }

/* A card with nothing in it but the empty line is a whole page with nothing
   to show: given room and a quiet tray icon, so it reads as "all clear"
   rather than as something that failed to load. */
.card > .empty:only-child {
    display: flex; flex-direction: column; align-items: center; gap: .65rem;
    padding: 2.25rem 1rem; text-align: center;
    font-style: normal; font-size: .95rem; color: var(--muted);
}
.card > .empty:only-child::before {
    content: ""; width: 2rem; height: 2rem; opacity: .55;
    background: var(--brand);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 12h-6l-2 3h-4l-2-3H2'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 12h-6l-2 3h-4l-2-3H2'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* ------------------------------------------------------------------ Stats */

.stat-row {
    display: grid; gap: var(--gap); margin-bottom: var(--gap);
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
/* Inside a card the row is narrower, and its tiles are part of the card
   rather than cards of their own: tighter, flatter, no orphan on a line. */
.card .stat-row { grid-template-columns: repeat(auto-fit, minmax(9.25rem, 1fr)); gap: .75rem; }
.card .stat { box-shadow: none; background: var(--panel-soft); }

.stat {
    display: flex; flex-direction: column; gap: .15rem;
    background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--radius); padding: .9rem 1rem;
    color: var(--ink); box-shadow: var(--shadow);
    position: relative;
    overflow: hidden;
    padding-top: 1.05rem;
}
a.stat:hover { border-color: var(--brand); text-decoration: none; }

.stat-label { font-size: .78rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--faint); }
.stat-value { font-size: 2.1rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.1; color: var(--royal); }
.stat-note { font-size: .78rem; color: var(--muted); }
.stat-pending .stat-value { color: var(--faint); }
/*
 * A count that is a finding rather than a fact — refusals in an import.
 *
 * The LABEL carries the colour, not the number: a figure in red reads as an
 * error in the figure itself, where what is being said is "this many rows need
 * your attention". The number stays in the ordinary ink so it is still the
 * most legible thing in the tile.
 */
.stat-warn .stat-label { color: var(--warn); }
.stat-warn { border-color: var(--warn); }

/*
 * A progress bar for work that runs in the background.
 *
 * No percentage is drawn inside it: the caller prints the figure beside it in
 * words, because a bar with text in it is unreadable at the width a phone
 * gives it. `role="img"` with an aria-label is how the value reaches a screen
 * reader — a div has no implicit value, and `role="progressbar"` would need
 * aria-valuenow kept in step by script this app does not run.
 */
.bar {
    height: .5rem; border-radius: 999px; overflow: hidden;
    background: var(--bg); border: 1px solid var(--line);
    margin: .6rem 0 .3rem;
}
.bar-fill {
    height: 100%; background: var(--brand);
    transition: width .3s ease;
}
@media (prefers-reduced-motion: reduce) {
    .bar-fill { transition: none; }
}

/* -------------------------------------------------------------- Checklist */

.progress-ring {
    flex: 0 0 auto; width: 3rem; height: 3rem; border-radius: 50%;
    display: grid; place-items: center;
    background: var(--brand-light); color: var(--brand-dark);
    font-size: .8rem; font-weight: 700;
}

.checklist { list-style: none; margin: 0; padding: 0; }
.checklist li {
    display: flex; gap: .7rem; align-items: flex-start;
    padding: .65rem 0; border-bottom: 1px solid var(--line);
}
.checklist li:last-child { border-bottom: 0; padding-bottom: 0; }

.checklist-mark { flex: 0 0 auto; color: var(--faint); line-height: 1; margin-top: .1rem; }
.checklist li.is-done .checklist-mark { color: var(--ok); }

.checklist-body { display: flex; flex-direction: column; min-width: 0; }
.checklist-label { font-weight: 600; font-size: .9rem; }
.checklist li.is-done .checklist-label { color: var(--muted); font-weight: 500; }
.checklist-detail { font-size: .82rem; color: var(--muted); }

/* --------------------------------------------------------------- Playbooks */

.playbook-list { list-style: none; margin: 0; padding: 0; }
.playbook-list li {
    display: flex; gap: .75rem; align-items: center;
    padding: .55rem 0; border-bottom: 1px solid var(--line);
}
.playbook-list li:last-child { border-bottom: 0; }

.playbook-number {
    flex: 0 0 auto; font-size: .72rem; font-weight: 700;
    color: var(--faint); font-variant-numeric: tabular-nums;
}
.playbook-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.playbook-name { font-size: .88rem; font-weight: 600; color: var(--ink); }
.playbook-tagline { font-size: .78rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.playbook-grid {
    display: grid; gap: var(--gap);
    grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}

.playbook-card { display: flex; flex-direction: column; gap: .5rem; margin: 0; }
.playbook-card.is-active { border-left: 3px solid var(--ok); }
.playbook-card-head { display: flex; align-items: center; justify-content: space-between; }
.playbook-number-lg {
    font-size: 1.5rem; font-weight: 700; color: var(--faint);
    font-variant-numeric: tabular-nums; line-height: 1;
}
.playbook-card-title { font-size: .98rem; }
.playbook-card-tagline { color: var(--muted); font-size: .85rem; flex: 1 1 auto; }
.playbook-card-meta { font-size: .78rem; color: var(--faint); }

/* ------------------------------------------------------------------ Health */

.health-list { margin: 0; }
.health-row {
    display: flex; justify-content: space-between; gap: var(--gap);
    padding: .5rem 0; border-bottom: 1px solid var(--line);
}
.health-row:last-child { border-bottom: 0; }
.health-row dt { color: var(--muted); font-size: .85rem; white-space: nowrap; }
/* A long status ("Safe Mode — redirected to the owner") wraps on the right
   with its dot held to the first line, instead of squeezing the label onto
   two lines and leaving the dot floating between them. */
.health-row dd {
    margin: 0; font-size: .85rem; font-weight: 600; text-align: right;
    display: flex; gap: .4rem; align-items: flex-start; justify-content: flex-end; min-width: 0;
}
.health-row dd .dot { flex: 0 0 auto; margin-top: .4em; }
/* A warning under a row is a row of its own, so the list stays a list. */
.health-row-warning { padding: 0; border-bottom: 0; }
.health-row-warning dd.health-warning { display: block; flex: 1; text-align: left; font-weight: 400; }

.health-warning {
    background: var(--warn-bg); color: var(--warn);
    padding: .6rem .75rem; border-radius: var(--radius-sm);
    font-size: .82rem; margin: .5rem 0;
}

.dot { width: .5rem; height: .5rem; border-radius: 50%; flex: 0 0 auto; }
.badge .dot { display: inline-block; margin-right: .3rem; vertical-align: .05em; }
.dot-ok   { background: var(--ok); }
.dot-warn { background: var(--warn); }
.dot-bad  { background: var(--bad); }

/* --------------------------------------------------------------- Timeline */

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
    display: flex; gap: .75rem; padding: .4rem 0;
    border-bottom: 1px solid var(--line); font-size: .83rem;
}
.timeline li:last-child { border-bottom: 0; }
.timeline-when { flex: 0 0 6.5rem; color: var(--faint); }
.timeline-what { flex: 1 1 auto; min-width: 0; color: var(--muted); }

/* --------------------------------------------------------------- Controls */

.btn {
    display: inline-flex; gap: .4rem; align-items: center; justify-content: center;
    padding: .5rem 1rem; border: 1px solid transparent; border-radius: var(--radius-sm);
    background: var(--brand); color: var(--on-accent);
    font: inherit; font-size: .88rem; font-weight: 700;
    cursor: pointer; text-decoration: none; white-space: nowrap;
    box-shadow: 0 1px 2px rgba(22, 0, 122, .18);
}
.btn:hover { background: var(--brand-dark); text-decoration: none; color: var(--on-accent); }
.btn:disabled { opacity: .55; cursor: not-allowed; }

.btn-ghost { background: transparent; color: var(--ink-soft); border-color: var(--field-border); box-shadow: none; }
.btn-ghost:hover { background: var(--bg); color: var(--ink); border-color: var(--faint); }

.btn-danger { background: var(--bad); color: var(--on-accent); }
.btn-danger:hover { background: var(--bad); filter: brightness(.9); }

.btn-sm { padding: .3rem .65rem; font-size: .8rem; }
.btn-block { width: 100%; }

/* A destructive action that is not the primary one on the row: red text on a
   ghost button rather than a solid red block, so a table of them does not read
   as a wall of danger. */
.btn-danger-text { color: var(--bad); }
.btn-danger-text:hover { background: var(--bad-bg); color: var(--bad); border-color: var(--bad); }

/* Buttons sitting side by side in a table cell. */
.row-actions { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; white-space: nowrap; }
/* On the cell itself the flex box stopped it being a cell: its border and
   height came loose from the rest of the row, so every Edit button sat on a
   line of its own. A cell stays a cell and spaces its buttons with margins. */
td.row-actions { display: table-cell; }
td.row-actions > * + * { margin-left: .4rem; }
.inline-form { display: inline; margin: 0; }

.btn-icon {
    display: inline-grid; place-items: center;
    width: 2rem; height: 2rem; padding: 0;
    background: transparent; border: 0; border-radius: var(--radius-sm);
    color: var(--faint); cursor: pointer;
}
.btn-icon:hover { background: var(--bg); color: var(--ink); }

/* ------------------------------------------------------------------ Forms */

.form .field { margin-bottom: 1rem; }
.card > .field:last-child, .card > .field-row:last-child { margin-bottom: 0; }

.field label {
    display: block; margin-bottom: .3rem;
    font-size: .85rem; font-weight: 600; color: var(--ink-soft);
}

.field input[type=text], .field input[type=email], .field input[type=password],
.field input[type=number], .field input[type=date], .field input[type=time],
.field input[type=tel], .field input[type=url], .field input[type=search],
.field select, .field textarea {
    width: 100%; padding: .5rem .65rem;
    border: 1px solid var(--field-border); border-radius: var(--radius-sm);
    background: var(--panel); color: var(--ink); font: inherit; font-size: .9rem;
}

/* Every select looks like one of ours, inside a .field or not. :where()
   keeps this at zero specificity, so the .field rules above still win. */
:where(select:not([multiple])) {
    padding: .35rem 2rem .35rem .65rem; min-height: 2.1rem;
    border: 1px solid var(--field-border); border-radius: var(--radius-sm);
    background: var(--panel); color: var(--ink); font: inherit; font-size: .88rem;
}
:where(select:not([multiple])):focus { border-color: var(--brand); outline: 2px solid var(--brand-ring); outline-offset: 0; }
.card-head .form-inline { gap: .5rem; align-items: center; }

/* One height for every single-line control. A browser draws a select a few
   pixels taller than a text input with the same padding, and in a row aligned
   on its bottom edge that difference surfaced as labels sitting at different
   heights — the contacts filter's "Search" label rode above "Stage" and
   "Owner". Textareas are left to grow. */
.field input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=hidden]), .field select {
    min-height: var(--control-h);
}

/* A filter bar's buttons stand beside those controls, so they match them
   rather than sitting short at the baseline. */
.filter-bar .field-actions .btn { min-height: var(--control-h); }

.field input:focus, .field select:focus, .field textarea:focus {
    border-color: var(--brand); outline: 2px solid var(--brand-ring); outline-offset: 0;
}

.field input[aria-invalid="true"] { border-color: var(--bad); }
.field input[type=color] { width: 4.5rem; padding: .2rem; cursor: pointer; }

.field-hint { font-size: .78rem; color: var(--muted); margin: .25rem 0 0; }
.field-error { font-size: .8rem; color: var(--bad); margin: .25rem 0 0; font-weight: 500; }

.field-row { display: grid; gap: var(--gap); }
@media (min-width: 38rem) { .field-row { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); } }

.field-check {
    display: grid; grid-template-columns: auto 1fr; gap: .25rem .6rem;
    align-items: start; margin-bottom: .75rem;
}
.field-check input { margin-top: .3rem; }
.field-check label { margin: 0; font-weight: 500; }
.field-check .field-hint { grid-column: 2; }
/* The other way of writing it, used on twenty forms: a label wrapping its
   checkbox. Without these the label took the narrow first column and the
   hint ran down the side of it. */
.field-check > label:first-child {
    grid-column: 1 / -1; display: flex; gap: .6rem; align-items: flex-start;
}
.field-check > label:first-child input { margin-top: .3rem; flex: 0 0 auto; }
.field-check > label:first-child ~ .field-hint { grid-column: 1 / -1; padding-left: 1.6rem; }

.form-actions {
    display: flex; gap: .6rem; align-items: center; flex-wrap: wrap;
    margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--line);
}
.form-hint { font-size: .8rem; color: var(--muted); }

.form-section-title { margin: 1.5rem 0 .5rem; font-size: .9rem; }
.form-section-divided { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid var(--line); }

/*
 * A SECONDARY FORM BEHIND ONE LINE.
 *
 * A record page is read far more often than it is written to. Recording a
 * payment, adding a party guest or cancelling are each one click away rather
 * than six always-open fields, which is what turned a one-screen booking into
 * a 3,300px scroll. The summary is the action's own name, so the page still
 * says everything that can be done here.
 */
.disclosure { margin-top: 1rem; padding-top: .85rem; border-top: 1px solid var(--line); }
.disclosure > summary {
    display: inline-flex; align-items: center; gap: .45rem; list-style: none;
    cursor: pointer; font-size: .88rem; font-weight: 600; color: var(--brand);
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::before {
    content: "+"; display: inline-grid; place-items: center;
    width: 1.15rem; height: 1.15rem; border-radius: 999px;
    background: var(--brand-light); font-size: .85rem; line-height: 1;
}
.disclosure[open] > summary::before { content: "\2212"; }
.disclosure[open] > summary { margin-bottom: .85rem; }
.disclosure > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--radius-sm); }
.disclosure-danger > summary { color: var(--bad); }
.disclosure-danger > summary::before { background: var(--bad-bg); }
.disclosure .form-actions { margin-top: .25rem; padding-top: 0; border-top: 0; }

/* One field and its button on one line, for a form that is a single question. */
.inline-fields { display: flex; gap: .6rem; align-items: flex-end; flex-wrap: wrap; }
.inline-fields .field { flex: 1 1 9rem; margin: 0; }
.inline-fields > .btn { flex: 0 0 auto; min-height: var(--control-h); }

/* A titled part of a card, set apart from what is above it. */
.card-section { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.card-section > h3 { margin: 0 0 .6rem; }

.page-buttons { display: flex; gap: .6rem; flex-wrap: wrap; align-items: center; }

/* Automations, one folding section per playbook. */
.automation-group { border-top: 1px solid var(--line); padding: .75rem 0; }
.automation-group > summary {
    display: flex; gap: .75rem; align-items: baseline; flex-wrap: wrap;
    cursor: pointer; list-style: none;
}
.automation-group > summary::-webkit-details-marker { display: none; }
.automation-group > summary::before { content: "\25B8"; color: var(--muted); width: .8rem; }
.automation-group[open] > summary::before { content: "\25BE"; }
.automation-group-name { font-weight: 650; }
.automation-group[open] > summary { margin-bottom: .5rem; }

/* Reports: the six families side by side on a wide screen. */
.report-families { display: grid; gap: var(--gap); align-items: stretch; }
@media (min-width: 70rem) { .report-families { grid-template-columns: 1fr 1fr; } }
.report-families > .card { margin-bottom: 0; }
.report-csv {
    display: inline-flex; gap: .3rem; align-items: center; white-space: nowrap;
    font-size: .78rem; color: var(--muted); text-decoration: none;
}
.report-csv:hover { color: var(--brand); }

/* Pipeline board. Stacked on a phone; on a wide screen one row in stage
   order, scrolling sideways, so "Date Held" never sits under "New Inquiry". */
.board { display: grid; gap: var(--gap); align-items: start; }
@media (min-width: 60rem) {
    .board {
        grid-auto-flow: column; grid-auto-columns: minmax(15rem, 1fr);
        overflow-x: auto; padding-bottom: .5rem; scroll-snap-type: x proximity;
        position: relative;
        /* Scroll shadows: a soft edge on whichever side has more stages off
           screen, so a board wider than the window says so. The two page-
           coloured covers scroll with the content and hide each shadow once
           that end is reached. */
        background:
            linear-gradient(to right, var(--bg) 30%, transparent) 0 0 / 2.5rem 100% no-repeat local,
            linear-gradient(to left, var(--bg) 30%, transparent) 100% 0 / 2.5rem 100% no-repeat local,
            radial-gradient(farthest-side at 0 50%, rgba(20, 0, 60, .16), transparent) 0 0 / .9rem 100% no-repeat scroll,
            radial-gradient(farthest-side at 100% 50%, rgba(20, 0, 60, .16), transparent) 100% 0 / .9rem 100% no-repeat scroll;
    }
    .board-column { scroll-snap-align: start; }
}
.board-column { border-top: 3px solid var(--brand); margin-bottom: 0; }
.board-card { border-top: 1px solid var(--line); padding-top: .6rem; margin-top: .6rem; }
.board-move > summary {
    cursor: pointer; list-style: none; font-size: .8rem; font-weight: 600; color: var(--brand);
    margin-top: .3rem;
}
.board-move > summary::-webkit-details-marker { display: none; }
.board-move > summary::after { content: " \25BE"; }
.board-move[open] > summary { margin-bottom: .4rem; }
.board-move .inline-form { display: flex; gap: .4rem; flex-wrap: wrap; }
.board-move select { min-width: 0; flex: 1 1 8rem; }

/* Dashboard: the birthdays card. */
.birthday-windows { display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.birthday-window { display: flex; flex-direction: column; gap: .15rem; padding: .85rem 1rem; border-radius: var(--radius-sm); background: var(--brand-light); }
.birthday-window .stat-value { color: var(--brand); }
/* On the brand tint --faint falls just short of 4.5:1, so the label takes --muted. */
.birthday-window .stat-label { color: var(--muted); }
/* On a phone the three windows stay one row of three: a glance, not a scroll. */
@media (max-width: 40rem) {
    .birthday-windows { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .5rem; }
    .birthday-window { padding: .6rem .65rem; }
    .birthday-window .stat-value { font-size: 1.6rem; }
    .birthday-window .stat-label { font-size: .68rem; }
    .birthday-window .stat-note { font-size: .72rem; }
}
.birthday-week { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem .75rem; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); }
.birthday-week li { font-size: .9rem; }

/* A short list of people to choose one from: the whole row is the target. */
.pick-list { border: 0; padding: 0; margin: .75rem 0 0; display: grid; gap: .4rem; }
.pick {
    display: grid; grid-template-columns: auto 1fr; gap: .6rem; align-items: start;
    padding: .55rem .75rem; border: 1px solid var(--line); border-radius: var(--radius-sm);
    cursor: pointer;
}
.pick:hover { background: var(--panel-soft); }
.pick:has(input:checked) { border-color: var(--brand); background: var(--brand-light); }
.pick input { margin-top: .25rem; }

.filter-bar {
    display: grid; gap: .75rem;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    align-items: end;
}
.filter-bar .field { margin: 0; }
.field-actions { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; }

/* ------------------------------------------------------------ Permissions */

.permission-group {
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    padding: .75rem 1rem 1rem; margin: 0 0 .75rem;
}
.permission-group legend {
    padding: 0 .4rem; font-size: .78rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: .05em; color: var(--faint);
}

.permission-row {
    display: grid; grid-template-columns: auto 1fr auto auto;
    gap: .6rem; align-items: center;
    padding: .3rem 0; border-bottom: 1px solid var(--line);
}
.permission-row:last-child { border-bottom: 0; }
.permission-row label { margin: 0; font-weight: 500; font-size: .85rem; }
.permission-row select { padding: .2rem .4rem; font-size: .78rem; width: auto; }
.permission-row.is-blocked { opacity: .5; }
.permission-blocked { color: var(--faint); line-height: 1; }

/* ----------------------------------------------------------------- Tables */

/*
 * `position: relative` for the reason spelled out on `.table-scroll` below:
 * without it a `.sr-only` caption or action label inside a table wider than
 * the phone escapes this scroll container and drags the whole page sideways.
 * MEASURED on `/settings/users` at 390px — scrollWidth 424 — AFTER the same
 * fix had been made to `.table-scroll` and this second wrapper was missed.
 */
.table-wrap {
    overflow-x: auto; position: relative;
    margin: 0 -1.25rem; padding: 0 1.25rem;
}

.table { width: 100%; border-collapse: collapse; font-size: .87rem; }

.table th {
    text-align: left; padding: .45rem .6rem;
    font-size: .72rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: .05em; color: var(--faint);
    border-bottom: 1px solid var(--line-strong); white-space: nowrap;
}

.table td { padding: .55rem .6rem; border-bottom: 1px solid var(--line); vertical-align: top; }
/* A row header (`<th scope="row">`) is the first cell of a row, not a column
   heading: it reads as a cell, with the weight that says what the row is.
   Styled like a column heading it shouted the row's name in small capitals
   and squeezed every other column. */
.table tbody th {
    padding: .55rem .6rem; border-bottom: 1px solid var(--line); vertical-align: top;
    font-size: inherit; font-weight: 600; text-transform: none; letter-spacing: normal;
    color: var(--ink); white-space: normal;
}
.table tbody tr:last-child td, .table tbody tr:last-child th { border-bottom: 0; }
.table tbody tr:hover { background: var(--panel-soft); }

body[data-density="comfortable"] .table td { padding: .8rem .6rem; }

.td-sub { display: block; font-size: .78rem; font-weight: 400; color: var(--muted); }
.td-right { text-align: right; }
/* `.table th` sets text-align and outranks a bare class, so a right-aligned
   number column kept a left-aligned heading. */
.table th.td-right { text-align: right; }
.row-muted td { opacity: .6; }

/*
 * A cell holding something long and unbreakable — a URL, a token prefix.
 *
 * `anywhere` rather than `break-all`: it breaks only when the line genuinely
 * will not fit, so a URL that does fit is left whole and readable.
 */
.cell-wrap { word-break: break-word; overflow-wrap: anywhere; max-width: 22rem; }

/*
 * A cell of buttons. They wrap on a narrow screen rather than stretching the
 * table past the viewport, because `.table-scroll` scrolls horizontally and a
 * row whose actions are off to the right is a row whose actions are invisible.
 */
.cell-actions { display: flex; flex-wrap: wrap; gap: .35rem; justify-content: flex-end; }

.card-actions { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }

/*
 * One condition in a segment's rule builder.
 *
 * A bordered block rather than a run of loose fields: a segment with six
 * conditions is otherwise eighteen controls in a column with nothing saying
 * which three belong together. The fields lay out in a row where there is
 * room and stack on a phone.
 */
.segment-rule {
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    padding: .75rem; margin: 0 0 .6rem;
    display: flex; flex-wrap: wrap; gap: .6rem; align-items: flex-end;
}
.segment-rule .field { flex: 1 1 11rem; margin: 0; }

/* The plain-English rendering of a segment's rules, in a table cell. */
.segment-rules { font-size: .84rem; color: var(--muted); max-width: 28rem; }

.change-list { list-style: none; margin: .4rem 0 0; padding: 0; font-size: .78rem; }
.change-list li { padding: .1rem 0; }

.pagination {
    display: flex; gap: .75rem; align-items: center; justify-content: center;
    margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--line);
}
.pagination-info { font-size: .82rem; color: var(--muted); }

/* --------------------------------------------------------------- Feedback */

.flash-stack { margin-bottom: var(--gap); display: flex; flex-direction: column; gap: .5rem; }

.alert {
    display: flex; gap: .6rem; align-items: flex-start;
    padding: .7rem .85rem; border-radius: var(--radius-sm);
    font-size: .87rem; border-left: 3px solid;
}
.alert p { margin: 0; flex: 1 1 auto; }
.alert .icon { flex: 0 0 auto; margin-top: .1rem; }
/* An alert on the page (not in the flash stack) is spaced like a card, so
   whatever follows does not sit on its edge. */
.content > .alert { margin-bottom: var(--gap); }

.alert-success { background: var(--ok-bg);   color: var(--ok);   border-color: var(--ok); }
.alert-error   { background: var(--bad-bg);  color: var(--bad);  border-color: var(--bad); }
.alert-warning { background: var(--warn-bg); color: var(--warn); border-color: var(--warn); }
.alert-info    { background: var(--info-bg); color: var(--info); border-color: var(--info); }

.alert-close {
    flex: 0 0 auto; background: none; border: 0; cursor: pointer;
    color: inherit; opacity: .6; font-size: 1.1rem; line-height: 1; padding: 0 .2rem;
}
.alert-close:hover { opacity: 1; }

.badge {
    display: inline-block; padding: .1rem .45rem;
    border-radius: 999px; font-size: .72rem; font-weight: 650; white-space: nowrap;
    /* Neutral unless a variant says otherwise: a bare .badge (a contact's
       stage, "System", "Add-on") otherwise reads as stray bold text. */
    background: var(--brand-light); color: var(--ink-soft);
}
.badge-ok     { background: var(--ok-bg);   color: var(--ok); }
.badge-warn   { background: var(--warn-bg); color: var(--warn); }
.badge-bad    { background: var(--bad-bg);  color: var(--bad); }
.badge-muted  { background: var(--bg);      color: var(--faint); border: 1px solid var(--line); }
/* The top 5% of the year's spenders: the venue's gold, on royal. */
.badge-vip    { background: var(--royal); color: var(--sun); letter-spacing: .06em; }

.pill {
    display: inline-flex; gap: .35rem; align-items: center;
    padding: .25rem .6rem; border-radius: 999px;
    font-size: .76rem; font-weight: 600; white-space: nowrap;
}
.pill-warn { background: var(--warn-bg); color: var(--warn); }
.pill-bad  { background: var(--bad-bg);  color: var(--bad); }
a.pill:hover { text-decoration: none; filter: brightness(.96); }

/* ------------------------------------------------------------ Definitions */

.definition-list { margin: 0; }
.definition-list > div { padding: .6rem 0; border-bottom: 1px solid var(--line); }
.definition-list > div:last-child { border-bottom: 0; }
.definition-list dt {
    font-size: .75rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: .05em; color: var(--faint); margin-bottom: .2rem;
}
.definition-list dd { margin: 0; font-size: .88rem; }

/* -------------------------------------------------------- Requirement list */

ul.checks { list-style: none; margin: 0; padding: 0; }
ul.checks li {
    display: flex; gap: .7rem; align-items: flex-start;
    padding: .7rem 0; border-bottom: 1px solid var(--line);
}
ul.checks li:last-child { border-bottom: 0; }

.mark {
    flex: 0 0 1.4rem; height: 1.4rem; border-radius: 50%;
    display: grid; place-items: center; margin-top: .05rem;
}
.mark-ok   { background: var(--ok-bg);   color: var(--ok); }
.mark-warn { background: var(--warn-bg); color: var(--warn); }
.mark-fail { background: var(--bad-bg);  color: var(--bad); }

.check-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.check-label { font-weight: 600; font-size: .88rem; }
.check-detail { font-size: .82rem; color: var(--muted); }
.check-fix { font-size: .82rem; color: var(--warn); margin-top: .2rem; }

/* ----------------------------------------------------------------- Settings */

.settings-grid {
    display: grid; gap: var(--gap);
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
}

/* The top of a guest's page: who they are and how to reach them, at a glance. */
.contact-hero {
    display: flex; flex-wrap: wrap; align-items: center; gap: 1rem;
    margin-bottom: var(--gap);
}
.avatar-lg { flex: 0 0 3.5rem; width: 3.5rem; height: 3.5rem; font-size: 1.15rem; }
.contact-hero-text { flex: 1 1 18rem; min-width: 0; }
.contact-hero h1 { margin: 0; font-size: 1.6rem; letter-spacing: -.02em; line-height: 1.2; }
.contact-hero-meta {
    display: flex; flex-wrap: wrap; align-items: center; gap: .35rem 1rem;
    margin: .35rem 0 0; font-size: .9rem; overflow-wrap: anywhere;
}
.contact-hero-actions { display: flex; flex-wrap: wrap; gap: .5rem; }

/* The guest search that sits above a family <select>. */
.contact-search { margin-bottom: .4rem; }

.settings-group + .settings-group { margin-top: 1.75rem; }
.settings-group-heading {
    margin: 0 0 .65rem; font-size: .75rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: .07em; color: var(--faint);
}

.settings-tile {
    display: flex; gap: .75rem; align-items: center; margin: 0;
    color: var(--ink);
}
.settings-tile:hover { border-color: var(--brand); text-decoration: none; }

.settings-icon {
    flex: 0 0 2.4rem; height: 2.4rem; border-radius: var(--radius-sm);
    background: var(--brand-light); color: var(--brand-dark);
    display: grid; place-items: center;
}
.settings-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.settings-body strong { font-size: .9rem; }
.settings-body span { font-size: .8rem; color: var(--muted); }
.settings-tile > .icon { color: var(--faint); flex: 0 0 auto; }

/*
 * A checklist of features or facts, used on a contact's record. It outlived
 * the "arrives in Phase N" placeholder page it was written for: every module
 * that page advertised has shipped, so the page, its route and its controller
 * are gone rather than left telling a reader that Campaigns is still coming
 * while Campaigns sits in the nav beside it.
 */
.feature-list {
    list-style: none; margin: 0 auto 1.25rem; padding: 0;
    max-width: 30rem; text-align: left;
}
.feature-list li {
    display: flex; gap: .55rem; align-items: flex-start;
    padding: .35rem 0; font-size: .87rem;
}
.feature-list .icon { color: var(--ok); flex: 0 0 auto; margin-top: .15rem; }

/* A guardian's children: who, when they next turn a year older, and the
   allergy that has to be on the run sheet. The countdown is coloured inside
   a month, because that is when the birthday playbook is working on them. */
.child-list { list-style: none; margin: 0 0 .75rem; padding: 0; }
.child-list li {
    display: flex; flex-wrap: wrap; gap: .25rem .6rem; align-items: baseline;
    padding: .45rem 0; border-bottom: 1px solid var(--line); font-size: .87rem;
}
.child-list li:last-child { border-bottom: 0; }
.child-when { color: var(--muted); white-space: nowrap; }
.child-soon { color: var(--brand-dark); font-weight: 600; white-space: nowrap; }

/* --------------------------------------------------------------- Error page */

.error-page { text-align: center; padding: 1rem 0; }
.error-status {
    font-size: 3rem; font-weight: 700; color: var(--brand);
    line-height: 1; margin: 0 0 .5rem; font-variant-numeric: tabular-nums;
}
.error-message { color: var(--muted); margin: .5rem 0 1.5rem; }
.error-detail { text-align: left; margin: 1.5rem 0; font-size: .8rem; }
.error-detail pre {
    background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm);
    padding: .75rem; overflow-x: auto; position: relative; max-height: 20rem;
}
.error-actions { margin-top: 1rem; }

/* ----------------------------------------------------------------- Code */

/*
 * ONE DEFINITION. There were two, 500 lines apart, differing only in padding
 * and whether they set a margin — so what a code block actually looked like
 * was whichever cascaded last, and editing the first one changed nothing.
 */
/* A waiver, read before it is signed: the venue's wording in the page's
   own type, wrapped, and scrolling inside its box rather than stretching
   the form. Focusable so the keyboard can scroll it. */
.waiver-text {
    margin: .25rem 0 1.25rem; padding: 1rem 1.1rem;
    max-height: 18rem; overflow-y: auto; position: relative;
    white-space: pre-wrap; overflow-wrap: anywhere;
    font-family: var(--font-text); font-size: .95rem; line-height: 1.6;
    background: var(--panel-soft); border: 1px solid var(--line); border-radius: var(--radius-sm);
}

.code-block {
    background: var(--bg); border: 1px solid var(--line);
    border-radius: var(--radius-sm); padding: .8rem;
    overflow-x: auto; position: relative; margin: .5rem 0; font-size: .82rem;
}
.code-inline {
    background: var(--bg); border: 1px solid var(--line);
    padding: .05rem .3rem; border-radius: 3px; font-size: .8em; color: var(--muted);
}

/* -------------------------------------------------------------- Calendar */

/* Month and week are real tables — a calendar IS tabular data, and a screen
   reader announces "Saturday 11 July, 3 bookings" from one while it announces
   nothing useful from a grid of divs. These rules only lay the table out. */

/* View, the period, and a jump to a date: three groups on one line that wrap
   as groups on a phone. It used to borrow the filter bar's grid, which put
   the date picker in a column of its own with its button stacked beneath. */
.cal-toolbar {
    display: flex; flex-wrap: wrap; gap: .75rem 1.5rem;
    align-items: center; justify-content: space-between;
    margin-bottom: var(--gap);
}
.cal-toolbar-group { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; margin: 0; }
.cal-toolbar .btn { min-height: var(--control-h); }
.cal-toolbar .field input[type=date] { width: auto; }
.cal-toolbar-title { margin: 0 .4rem; font-size: 1.05rem; min-width: 9.5rem; text-align: center; }

.cal-month, .cal-week { table-layout: fixed; }
/* The month and week grids are followed by a card (subscribing); without
   this the two touch. */
.table-wrap.cal-wrap {
    margin: 0 0 var(--gap); padding: 0;
    /* Framed like every other panel on the page, not a bare grid on the page. */
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
    box-shadow: var(--shadow); overflow: hidden;
}
.cal-wrap .table { margin: 0; }
.cal-month th, .cal-week th { text-align: left; font-size: .78rem; }
/* The day names are abbreviations with their full name on hover; the
   browser's dotted underline made every column heading look like a link. */
.cal-month th abbr, .cal-week th abbr { text-decoration: none; }

.cal-cell {
    vertical-align: top; height: 6.5rem; padding: .35rem;
    border: 1px solid var(--line); background: var(--panel);
}
.cal-week .cal-cell { height: 18rem; }

.cal-outside { background: var(--panel-soft); }
.cal-outside .cal-date { color: var(--faint); font-weight: 500; }
/* Today's date as a filled marker, the way every calendar app shows it. */
.cal-today .cal-date {
    min-width: 1.6rem; padding: .1rem .4rem; border-radius: 999px; text-align: center;
    background: var(--brand); color: var(--on-accent);
}
.cal-today .cal-date:hover, .cal-today .cal-date:focus-visible { color: var(--on-accent); }

.cal-today { outline: 2px solid var(--brand); outline-offset: -2px; }

.cal-date {
    display: inline-block; font-size: .8rem; font-weight: 700;
    color: var(--muted); text-decoration: none; margin-bottom: .2rem;
}
.cal-date:hover, .cal-date:focus-visible { color: var(--brand-dark); }

.cal-chip {
    display: block; font-size: .74rem; line-height: 1.25;
    padding: .18rem .35rem; margin-bottom: .18rem; border-radius: var(--radius-sm);
    background: var(--brand-light); color: var(--brand-dark);
    text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cal-chip:hover, .cal-chip:focus-visible { background: var(--brand); color: var(--on-accent); }

/* The status colours double up with the badge palette on purpose: a venue
   learns one vocabulary, not two. */
.cal-inquiry, .cal-held           { background: var(--warn-bg); color: var(--warn); }
.cal-confirmed, .cal-deposit_paid { background: var(--ok-bg);   color: var(--ok); }
.cal-paid, .cal-completed         { background: var(--ok-bg);   color: var(--ok); }
.cal-cancelled, .cal-no_show      { background: var(--bad-bg);  color: var(--bad); }

/* Struck through AND in the failure colour: colour alone is not a signal, and
   a cancelled party must not read as a live one to somebody who cannot tell
   red from green. */
.cal-off del { text-decoration-thickness: 1px; }

.cal-more { display: block; font-size: .72rem; color: var(--muted); padding: 0 .35rem; }

.cal-lanes th[scope="row"] { width: 14rem; vertical-align: top; }
.cal-lanes .cal-chip { display: inline-block; margin-right: .3rem; white-space: normal; }

/* ------------------------------------------------------------------- Inbox */


/* An unread row has to be findable at a glance down a list of forty, and
   weight is the signal rather than colour: the queue is read by whoever is on
   shift, and a tint alone fails for anyone who cannot distinguish it. The left
   edge carries it too, so the cue survives a greyscale print. */
.inbox-unread > th,
.inbox-unread > td { font-weight: 600; }
.inbox-unread > th:first-child { box-shadow: inset 3px 0 0 0 var(--brand); }

.inbox-preview { color: var(--muted); max-width: 32rem; }

.inbox-thread { list-style: none; display: flex; flex-direction: column; gap: .75rem; }

.inbox-msg {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--radius); padding: .85rem 1rem; box-shadow: var(--shadow);
    /* Outbound sits to the right of centre, inbound to the left — the shape
       every messaging app has taught people to read without a legend. */
    margin-left: 2.5rem;
}
.inbox-msg.is-inbound { margin-left: 0; margin-right: 2.5rem; border-left: 3px solid var(--brand); }

/* A note is staff talking to each other, and it must not be mistaken for
   something the guest can see. Dashed, because a solid tint reads as another
   kind of message. */
.inbox-msg.is-note {
    background: var(--panel-soft); border-style: dashed;
    border-left: 3px solid var(--warn);
}

.inbox-msg-meta {
    display: flex; gap: .5rem; align-items: baseline; flex-wrap: wrap;
    font-size: .82rem; margin-bottom: .35rem;
}

.inbox-msg-body { white-space: pre-wrap; overflow-wrap: anywhere; }

.inbox-list { list-style: none; display: flex; flex-direction: column; gap: .5rem; }
.inbox-list > li { display: flex; flex-direction: column; gap: .1rem; font-size: .9rem; }

/* The canned replies are reference text, so they scroll rather than pushing
   the composer off the screen. */
.inbox-canned { max-height: 14rem; overflow-y: auto; font-size: .85rem; }

@media (max-width: 38rem) {
    .inbox-msg { margin-left: 0; }
    .inbox-msg.is-inbound { margin-right: 0; }
}

/* ------------------------------------------------------------- Text Club */

/* The honeypot. Positioned off-screen rather than `display: none`, because
   the simpler bots check for that and skip the field — which is exactly the
   behaviour this is trying to catch. It is also `aria-hidden` with
   `tabindex="-1"` in the markup, so a screen reader never announces it and
   a keyboard user cannot land in it: a labelled field somebody is asked to
   fill in is a trap for the wrong person. */
.honeypot {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* The disclosure a guest agrees to. Deliberately not small print: this is
   the wording their consent rests on, and the whole reason the venue may
   text them. */
.club-consent {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--bg-sunk);
    padding: .85rem 1rem;
    margin-bottom: var(--gap);
    font-size: .82rem;
    color: var(--muted);
}

.club-consent p { margin: 0 0 .5rem; }
.club-consent p:last-child { margin-bottom: 0; }
.club-consent strong { color: var(--ink); }

.club-wording {
    font-style: italic;
    padding-left: .75rem;
    border-left: 2px solid var(--line);
}

/* An offer as it will land on a phone, shown at a phone's width so the line
   breaks are the real ones. */
.club-preview {
    max-width: 22rem;
    padding: .7rem .85rem;
    border-radius: 1rem;
    background: var(--accent-soft, var(--bg-sunk));
    border: 1px solid var(--line);
    font-size: .88rem;
    line-height: 1.45;
}

.club-month { border-top: 1px solid var(--line); padding: .6rem 0; }
.club-month:first-of-type { border-top: 0; }
.club-month > summary { cursor: pointer; font-weight: 600; font-size: .88rem; }
.club-month[open] > summary { margin-bottom: .75rem; }
.club-month .field:last-child { margin-bottom: 0; }

.club-calendar { list-style: none; margin: 0; padding: 0; font-size: .85rem; }
.club-calendar li {
    display: flex;
    justify-content: space-between;
    gap: .75rem;
    padding: .4rem 0;
    border-bottom: 1px solid var(--line);
}
.club-calendar li:last-child { border-bottom: 0; }

/* --------------------------------------------------------- Public booking */

/* The only `bare` page that needs two columns' worth of room: a package
   picker, a slot grid and a price breakdown do not fit the 24rem card that
   sign-in was designed around. Opt-in, so sign-in and the error pages are
   untouched. */
.bare-main.is-wide { max-width: 46rem; }

.book-step { margin-bottom: var(--gap-lg); }
.book-step + .book-step { border-top: 1px solid var(--line); padding-top: var(--gap-lg); }

/* Each step's link lands on the next step's heading, with room above it. */
/* A waiver child: name and age on one line, the age only as wide as an age. */
.minor-row { margin-bottom: .6rem; }
.minor-row:first-child { margin-top: .75rem; }
.minor-inputs { display: grid; grid-template-columns: minmax(0, 1fr) 5.5rem; gap: .5rem; }

.book-step-title { font-size: 1rem; margin-bottom: .75rem; scroll-margin-top: 1rem; }

.book-packages { list-style: none; padding: 0; display: grid; gap: .5rem; }

.book-package > a {
    display: flex; gap: .75rem; align-items: baseline; justify-content: space-between;
    padding: .7rem .85rem; border: 1px solid var(--line-strong);
    border-radius: var(--radius); text-decoration: none; color: var(--ink);
}
.book-package > a:hover, .book-package > a:focus-visible { border-color: var(--brand); }

/* The chosen package carries a thick left edge as well as the brand colour:
   colour alone is not a signal, and this is the answer to "which party" that
   every figure further down the page depends on. */
.book-package.is-chosen > a {
    border-color: var(--brand); border-left-width: 4px;
    background: var(--brand-light); color: var(--brand-dark);
}

.book-package-price { font-size: .85rem; color: var(--muted); white-space: nowrap; }
.book-package-price small { display: block; font-size: .78rem; }
.book-package.is-chosen .book-package-price { color: var(--brand-dark); }

.book-package-note { font-size: .88rem; color: var(--muted); margin-top: .6rem; }

.book-includes { margin: .6rem 0 0 1.1rem; font-size: .85rem; color: var(--muted); }

.book-day-form .form-actions { margin-top: .25rem; }

.book-avail-note { font-size: .9rem; margin-bottom: .6rem; }
.book-avail-note.is-refused { color: var(--warn); }

.book-slots {
    list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .6rem;
}

.book-slot {
    display: inline-block; padding: .45rem .7rem; border-radius: var(--radius-sm);
    border: 1px solid var(--field-border); text-decoration: none;
    color: var(--ink); font-size: .9rem; min-height: 2.75rem; line-height: 1.8;
}
.book-slot:hover, .book-slot:focus-visible { border-color: var(--brand); }
.book-slot small { display: block; font-size: .75rem; color: var(--muted); line-height: 1.2; }

/* Ticked as well as filled, for the same reason as the package above. */
.book-slot.is-chosen {
    background: var(--brand); border-color: var(--brand); color: var(--on-accent);
    font-weight: 700;
}
.book-slot.is-chosen small { color: var(--on-accent); }

.book-quote { margin-bottom: .6rem; }

@media (max-width: 38rem) {
    .book-package > a { flex-direction: column; gap: .2rem; }
    .book-package-price { white-space: normal; }
}

/* ------------------------------------------------------------- Responsive */

@media (max-width: 60rem) {
    .sidebar {
        position: fixed; inset: 0 auto 0 0; z-index: 40;
        transform: translateX(-100%); transition: transform .2s ease;
        box-shadow: var(--shadow-lg);
    }
    .sidebar.is-open { transform: translateX(0); }
    .nav-toggle { display: inline-grid; }
    .content { padding: 1rem; }
    .table-wrap { margin: 0 -1rem; padding: 0 1rem; }
}

@media (max-width: 38rem) {
    body { font-size: .875rem; }
    .card { padding: 1rem; }
    .stat-row { grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); }
    .permission-row { grid-template-columns: auto 1fr; }
    .permission-row select { grid-column: 2; justify-self: start; }
    .timeline-when { flex-basis: 5rem; }
}

/* ------------------------------------------------------- Automation steps */

/*
 * The step list is the automation builder's main surface: a numbered sequence
 * with its controls beside each step and the edit form folded away under it.
 * Numbered because the ORDER is the thing being edited, and a list that only
 * implies its order makes "move this up" a guess.
 */
.step-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }

.step-item {
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    padding: .75rem .9rem; background: var(--surface-2, transparent);
}
.step-item.is-off { opacity: .6; }

.step-head { display: flex; gap: .75rem; align-items: flex-start; }

.step-number {
    flex: 0 0 auto; width: 1.6rem; height: 1.6rem; border-radius: 50%;
    display: grid; place-items: center;
    background: var(--accent-soft, var(--line)); color: var(--text);
    font-size: .8rem; font-weight: 700;
}

.step-text { display: grid; gap: .15rem; min-width: 0; }

.step-actions {
    display: flex; gap: .35rem; flex-wrap: wrap; align-items: center;
    margin: .6rem 0 0; padding-left: 2.35rem;
}
.step-actions form { margin: 0; }

.step-edit { margin: .5rem 0 0; padding-left: 2.35rem; }
.step-edit > summary {
    cursor: pointer; font-size: .82rem; color: var(--muted);
    padding: .2rem 0;
}
.step-edit > summary:hover { color: var(--text); }

.stacked-form { display: grid; gap: .25rem; margin: .75rem 0 0; }
.stacked-form .field { margin: 0 0 .75rem; }

.form-inline { display: flex; gap: .75rem; align-items: flex-start; flex-wrap: wrap; }
.form-inline .field { margin: 0; }

/* A checkbox or radio with its words beside it. The class was named in three
   views and defined nowhere, so each label took the bold block style of a
   field label: hint text in bold, running on from its heading, inside the
   browser's own fieldset frame. */
.field label.check {
    display: flex; gap: .55rem; align-items: flex-start;
    margin: 0 0 .4rem; font-weight: 400; color: var(--ink); cursor: pointer;
}
.field label.check input { flex: 0 0 auto; margin-top: .2rem; }
.field label.check strong { font-weight: 600; }
.field label.check .field-hint { display: block; margin-top: .15rem; }

/* A choice between kinds of thing (what is in this file?) reads as cards, so
   the one picked is obvious at a glance. Checkbox lists stay plain. */
.field label.check:has(input[type=radio]) {
    padding: .6rem .75rem; border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.field label.check:has(input[type=radio]:checked) { border-color: var(--brand); background: var(--brand-light); }

fieldset.field { border: 0; padding: 0; margin: 0; min-width: 0; }
fieldset.field > legend { padding: 0; margin-bottom: .45rem; font-size: .85rem; font-weight: 600; color: var(--ink-soft); }

.fieldset {
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    padding: .75rem 1rem; margin: 0 0 .9rem;
}
.fieldset legend {
    padding: 0 .4rem; font-size: .78rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: .05em; color: var(--faint);
}

.list-plain { margin: .35rem 0 0; padding-left: 1.1rem; display: grid; gap: .3rem; }

/*
 * An automation's dry-run result.
 *
 * Capped and scrollable: a preview of a long sequence is twenty lines, and
 * without this the button that produced it is pushed off the screen, so the
 * obvious next move — change something and run it again — is the one that
 * cannot be seen.
 */
.preview-result { margin-top: .75rem; max-height: 22rem; overflow-y: auto; }

.rule { border: 0; border-top: 1px solid var(--line); margin: 1rem 0; }

@media (max-width: 38rem) {
    /* The indent under the step number costs more than it gives on a phone. */
    .step-actions, .step-edit { padding-left: 0; }
}

/* ------------------------------------------------------------- Reputation */

/* A definition list written as bare dt/dd pairs rather than with the wrapper
   div `.definition-list > div` expects. Both spellings read the same, so both
   get the same separators instead of one of them quietly losing them. */
.definition-list > dd { padding-bottom: .6rem; border-bottom: 1px solid var(--line); margin-bottom: .6rem; }
.definition-list > dd:last-child { border-bottom: 0; margin-bottom: 0; }

/* Stars. The off state is a real element rather than a lighter colour on the
   same glyphs, so the rating is legible at a glance and still readable by a
   screen reader through the aria-label on the wrapper. --faint clears 4.5:1,
   which matters because these ARE the content on the review wall. */
.rating { color: #b8860b; letter-spacing: .05em; white-space: nowrap; }
.rating-off { color: var(--line-strong); }
.rating-lg { font-size: 1.3rem; }

.is-overdue td { background: var(--bad-bg); }

/* Chips: a filter bar of links, one of them current. */
.chip-bar { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: var(--gap); }

.chip {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .3rem .7rem; border-radius: 999px;
    border: 1px solid var(--field-border); background: var(--panel);
    font-size: .82rem; font-weight: 600; color: var(--ink-soft);
}
.chip:hover { background: var(--bg); text-decoration: none; color: var(--ink); }
.chip.is-on { background: var(--brand); border-color: var(--brand); color: var(--on-accent); }
.chip-count { font-size: .74rem; opacity: .75; font-variant-numeric: tabular-nums; }
/* Overdue is the one count that should catch the eye. */
.chip-count-bad { opacity: 1; color: var(--bad); font-weight: 700; }
.chip.is-on .chip-count-bad { color: inherit; }

/* A review, in the inbox and on the wall. */
.review-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .9rem; }

.review-item {
    border: 1px solid var(--line); border-radius: var(--radius);
    padding: .85rem 1rem; background: var(--panel-soft);
}
.review-item > p { margin: .4rem 0 .6rem; }

.review-meta { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; font-size: .86rem; }

.review-reply {
    margin: .5rem 0 0; padding: .6rem .85rem;
    border-left: 3px solid var(--brand); background: var(--panel);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.review-reply p { margin: 0 0 .25rem; font-size: .88rem; }
.review-reply footer { font-size: .78rem; }

.review-actions { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.review-actions form { margin: 0; }

.review-write { margin-top: .6rem; }
.review-write > summary { cursor: pointer; font-size: .85rem; font-weight: 600; color: var(--brand); }

/* The NPS chart: one stacked column per day or week. No library, no canvas —
   three divs whose heights are percentages of the day's answers. */
.nps-chart {
    display: flex; align-items: flex-end; gap: .25rem;
    overflow-x: auto; position: relative; padding: .5rem 0 0;
}

.nps-col { display: grid; gap: .2rem; justify-items: center; min-width: 2.1rem; }

.nps-bar {
    display: flex; flex-direction: column-reverse; justify-content: flex-start;
    width: 1.5rem; height: 7rem;
    background: var(--bg); border-radius: var(--radius-sm); overflow: hidden;
}

.nps-seg { display: block; width: 100%; }
.nps-promoter  { background: var(--ok); }
.nps-passive   { background: var(--warn); }
.nps-detractor { background: var(--bad); }

.nps-score { font-size: .8rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.nps-label { font-size: .68rem; color: var(--faint); white-space: nowrap; }

/* One settings block per survey. */
.setting-block { padding: .9rem 0; border-bottom: 1px solid var(--line); }
.setting-block:last-child { border-bottom: 0; }
.setting-block h3 { margin: 0 0 .2rem; font-size: 1rem; }

.inline-weight { display: inline-flex; align-items: center; gap: .4rem; font-size: .8rem; color: var(--muted); }
.inline-weight input { width: 4.5rem; }

/* ------------------------------------------- The guest's feedback page */

/* Tap targets first: this is answered on a phone, one-handed, by somebody
   who is doing something else. Every button clears 44px in both directions
   even at the 0-10 scale's eleven columns. */
.survey-form { margin: 1.25rem 0 0; }
.survey-scale { border: 0; margin: 0; padding: 0; }

.survey-buttons {
    display: grid; gap: .4rem;
    grid-template-columns: repeat(auto-fit, minmax(2.75rem, 1fr));
}

/* Six across on a phone, so the eleven answers of an NPS scale fall as 0-5
   and 6-10 rather than 0-4, 5-9 and a lone 10 on a third row — MEASURED at
   390px, where auto-fit chose five and the "Extremely likely" label ended up
   over empty space. */
@media (max-width: 30rem) {
    .survey-buttons { grid-template-columns: repeat(6, 1fr); }
    .survey-btn { padding: .5rem .1rem; }
}

.survey-btn {
    min-height: 2.9rem; padding: .5rem .2rem;
    font-family: var(--font-display); font-size: 1.15rem; font-weight: 700;
    background: var(--panel); color: var(--ink);
    border: 1px solid var(--field-border); border-radius: var(--radius-sm);
    cursor: pointer;
}
.survey-btn:hover { background: var(--brand-light); border-color: var(--brand); }
.survey-btn:focus-visible { outline: 3px solid var(--brand-ring); outline-offset: 2px; }

.survey-buttons.is-stars { grid-template-columns: 1fr; }
.survey-buttons.is-stars .survey-btn { color: #b8860b; font-size: 1.15rem; letter-spacing: .1em; }

.survey-ends {
    display: flex; justify-content: space-between; gap: 1rem;
    margin-top: .45rem; font-size: .76rem; color: var(--muted);
}

.survey-cta { margin: 1.25rem 0; }
.btn-lg { padding: .7rem 1.2rem; font-size: 1rem; }
.survey-comment { margin-top: 1.25rem; }

/* ------------------------------------------------- The public review wall */

.review-wall-head { display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; justify-content: center; font-size: .95rem; }

.review-wall-list { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: 1rem; }
@media (min-width: 40rem) { .review-wall-list { grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); } }

.review-wall-item {
    border: 1px solid var(--line); border-radius: var(--radius);
    padding: 1.1rem 1.2rem; background: var(--panel-soft);
    display: flex; flex-direction: column;
}
.review-wall-item blockquote { margin: .5rem 0; flex: 1 1 auto; font-size: 1.02rem; }
.review-wall-item blockquote p { margin: 0; }
.review-wall-item footer { font-size: .82rem; display: flex; flex-wrap: wrap; gap: .4rem; }

/* The embed layout: transparent, so the widget takes the colour of the page
   it is dropped into rather than painting a white box on a dark site. */
body.embed { background: transparent; padding: 0; }
body.embed .review-wall { padding: .25rem; }

/* ------------------------------------------------------------- API tokens */

/* The one-shot reveal. Monospaced and selectable in one gesture: a token is
   64 hex characters and the only thing anybody does with it is copy it. */
/*
 * A credential shown once, to be read and copied by eye.
 *
 * `.field input.token-reveal`, NOT `.token-reveal` — which is what this was,
 * and it never applied. `.field input[type=text]` sets `font: inherit`, a
 * shorthand that resets family along with everything else, and it outranks a
 * single class. So every API token and every signing secret this application
 * has ever shown was rendered in the proportional UI font, where `l`, `1` and
 * `I` are the same picture and so are `0` and `O`. For 64 hex characters that
 * somebody is checking against another screen, that is the one thing the
 * styling had to get right. MEASURED in a browser, not read off the file.
 */
.field input.token-reveal {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .85rem; letter-spacing: .02em;
}

.ability-groups {
    display: grid; gap: .9rem; margin: .5rem 0 0;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
.ability-group h3 {
    margin: 0 0 .3rem; font-size: .78rem; text-transform: uppercase;
    letter-spacing: .04em; color: var(--muted);
}
.ability-group .check { display: flex; gap: .4rem; align-items: flex-start; font-size: .88rem; }

.code-block code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

/* ---------------------------------------------------------------- Reports */

/* The picker. A report is listed by the QUESTION it answers, with the name
   above it in the role of a label — "Lead time" means nothing to a new
   manager, "How far ahead do people book?" means something to everybody. */
.report-list { list-style: none; margin: 0; padding: 0; }

.report-list > li {
    display: flex; align-items: center; gap: .75rem;
    padding: .55rem 0; border-bottom: 1px solid var(--line);
}
.report-list > li:last-child { border-bottom: 0; }

.report-link { flex: 1 1 auto; display: grid; gap: .1rem; text-decoration: none; }
.report-link:hover .report-name { text-decoration: underline; }
.report-name { font-weight: 600; color: var(--text); }
.report-question { font-size: .85rem; color: var(--muted); }

.saved-reports { list-style: none; margin: 0; padding: 0; }
.saved-reports > li {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
    padding: .45rem 0; border-bottom: 1px solid var(--line);
}
.saved-reports > li:last-child { border-bottom: 0; }
.saved-reports form { margin: 0 0 0 auto; }
.saved-report-dead { display: inline-flex; flex-wrap: wrap; align-items: center; gap: .4rem; }

/* The period picker. A row on a desktop, a stack on a phone; no inline
   handler anywhere, because the CSP forbids inline script and a select that
   submits on change cannot be used with a keyboard. */
.report-window {
    display: flex; flex-wrap: wrap; gap: .75rem; align-items: flex-end;
    padding: .75rem 0; border-bottom: 1px solid var(--line); margin-bottom: .75rem;
}
.report-window .field { margin: 0; flex: 0 1 11rem; }
.report-window .field-actions { flex: 1 1 14rem; }
.report-window .field-hint { margin: .25rem 0 0; }

.report-period { display: flex; align-items: center; gap: .4rem; font-size: .88rem; color: var(--muted); }
.report-period svg { flex: 0 0 auto; }

/* The trend: one bar per day, same approach as the NPS chart — a div whose
   height is a percentage. No library, no canvas, no third-party request.

   The columns GROW to fill the card. Twelve bars at a fixed width left a
   chart hugging the left third of a wide card with two thirds of white space
   beside it, which reads as a rendering fault rather than as a short month. */
.report-chart {
    display: flex; align-items: flex-end; gap: .2rem;
    overflow-x: auto; position: relative; padding: .5rem 0 0;
}

/* The baseline. Without a track behind each bar, this is what a day with
   almost nothing on it sits on. */
.report-chart .report-bar { border-bottom: 1px solid var(--line); }

/* The COLUMN grows to fill the card; the BAR inside it is capped and
   centred. Capping the column instead left twelve bars occupying the left
   half of a wide card, which reads as a chart that failed to draw rather
   than as a month with twelve trading days in it. */
.report-col {
    display: grid; gap: .2rem; justify-items: center;
    flex: 1 1 0; min-width: 1.5rem;
}

/* NO TRACK BEHIND THE BAR. A filled column above each bar reads as a target
   the day fell short of, which is a claim this chart is not making and the
   opposite of the ACTUALS ONLY rule; at ninety bars it also turned the panel
   into stripes. The baseline under the chart is what gives a near-zero day
   something to sit on. */
.report-bar {
    display: flex; flex-direction: column-reverse;
    width: 100%; max-width: 2.2rem; height: 7rem;
    background: transparent;
}
.report-fill {
    display: block; width: 100%; background: var(--brand);
    border-radius: 2px 2px 0 0;
}
.report-bar-label {
    font-size: .66rem; color: var(--faint); white-space: nowrap;
    min-height: 1em;
}

/* Figures right-aligned and tabular so a column of money lines up on the
   decimal point, which is the only way a column of money can be scanned. */
.table-report .num-col { text-align: right; }
.table-report .num { font-variant-numeric: tabular-nums; }
/* The first column is the row's name (a day, a package): one line, so a phone
   shows a row per line and the numbers scroll beside it. */
.table-report tbody > tr > :first-child { white-space: nowrap; }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }
.table-report tfoot th,
.table-report tfoot td { font-weight: 700; border-top: 2px solid var(--line); }

/* A wide table scrolls inside its card rather than pushing the page sideways.
   The fade on the right edge is the only thing that says so on a phone, where
   there is no scrollbar until you touch it. */
.table-scroll {
    overflow-x: auto;
    /*
     * A CONTAINING BLOCK, and it is load-bearing.
     *
     * `.sr-only` is `position: absolute`, and a `<caption class="sr-only">`
     * or an `<span class="sr-only">Actions</span>` inside a table that is
     * wider than the phone is laid out at its static position — out past the
     * right edge. Without a positioned ancestor here, the nearest one is the
     * page, so that 1px span joins the PAGE's scrollable overflow and the
     * whole layout scrolls sideways: MEASURED at 390px, scrollWidth 482
     * against a 390 viewport, with every row button pushed off screen at
     * x=623 and up. The table itself was contained correctly the whole time;
     * it was the invisible label that escaped.
     */
    position: relative;
    -webkit-overflow-scrolling: touch;
    background:
        linear-gradient(to right, var(--panel) 30%, transparent) left / 2rem 100% no-repeat,
        linear-gradient(to left, var(--panel) 30%, transparent) right / 2rem 100% no-repeat,
        radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, .12), transparent) left / .6rem 100% no-repeat,
        radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, .12), transparent) right / .6rem 100% no-repeat;
    background-attachment: local, local, scroll, scroll;
}

.delta { font-weight: 700; white-space: nowrap; }
.delta-up   { color: var(--ok); }
.delta-down { color: var(--bad); }
.delta-flat { color: var(--muted); }
/* On a Venue tile the colour says whether the change is welcome, which is not
   always the direction: more comps given away is a fall worth seeing. */
.delta-good { color: var(--ok); }
.delta-bad  { color: var(--bad); }

@media (max-width: 40rem) {
    .report-window .field,
    .report-window .field-actions { flex: 1 1 100%; }

    /* MEASURED at 390px: "$26,326.64" at the full stat size overflowed its
       own card. Money is the longest thing these tiles ever hold. */
    .report-page .stat-value,
    .stat-row .stat-value { font-size: 1.15rem; overflow-wrap: anywhere; }

    .report-chart { gap: .1rem; }
    .report-col { min-width: .9rem; }
}

/* The ninety-day strip on the picker. A sparkline of divs, same approach as
   the NPS chart — no library, no canvas, no third-party request. */
.pulse-row {
    display: grid; gap: .75rem;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}

.pulse { display: grid; gap: .15rem; }
.pulse-label { font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.pulse-total { font-size: 1.3rem; font-variant-numeric: tabular-nums; }

.pulse-spark {
    display: flex; align-items: flex-end; gap: 1px;
    height: 2.6rem; margin: .25rem 0;
}
.pulse-bar { flex: 1 1 0; min-width: 1px; background: var(--brand); border-radius: 1px; }

.pulse-note { font-size: .72rem; color: var(--faint); }

/* ------------------------------------------------------------------ Print */

@media print {
    .sidebar, .topbar, .form-actions, .btn, .skip-link { display: none !important; }
    body { background: #fff; color: #000; font-size: 11pt; }
    .card { border: 1px solid #999; box-shadow: none; break-inside: avoid; }
    .content { padding: 0; max-width: none; }
    a { color: #000; text-decoration: underline; }
}

/* ------------------------------------------------------------------ Hours */

.hours-table td.field { margin: 0; }
.hours-table td, .hours-table tbody th { vertical-align: middle; }
.hours-table .field input[type=time] { width: auto; min-width: 8.5rem; }
.hours-closed {
    display: inline-flex; gap: .35rem; align-items: center;
    font-size: .85rem; color: var(--ink-soft); cursor: pointer;
}

/* -------------------------------------------------------- Message templates */

/* The editor on the left, what a guest sees on the right; stacked on a
   narrower screen, preview second. */
.template-layout {
    display: grid; gap: var(--gap); align-items: start;
    grid-template-columns: minmax(0, 1fr) minmax(0, 27rem);
}
.template-side { min-width: 0; }
.template-preview {
    display: block; width: 100%; height: 36rem;
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    /* The email's own background, whatever the app's theme. */
    background: #fff;
}
/* A text is a bubble or two, not a letter. */
.template-preview-sms { height: 15rem; }
.field textarea.code-input {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .78rem; line-height: 1.5; tab-size: 2;
}
.tag-list { list-style: none; margin: .5rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .35rem; }
.tag-list code {
    display: inline-block; padding: .15rem .4rem; border-radius: var(--radius-sm);
    background: var(--panel-soft); border: 1px solid var(--line); font-size: .76rem;
}

@media (max-width: 70rem) {
    .template-layout { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   The Castle's own look
   --------------------------------------------------------------------------
   Taken from thecastlefuncenter.com and the venue's mark, so the CRM reads as
   the Castle's rather than as a template: Asap Condensed for headings and
   Karla for text (the website's own pairing), the royal indigo of the site's
   dark sections behind the navigation, and the blue-purple-orange of the
   mark as one signature stripe. The battlement edge is the one ornament.

   Everything below restyles; nothing changes what a screen does. The fonts
   are served from this application (SIL Open Font License, see
   assets/fonts/OFL-*.txt), so there is still no third-party request.
   ========================================================================== */

@font-face { font-family: "Karla"; font-weight: 400; font-style: normal; font-display: swap;
    src: url("../fonts/karla-latin-400-normal.woff2") format("woff2"); }
@font-face { font-family: "Karla"; font-weight: 400; font-style: italic; font-display: swap;
    src: url("../fonts/karla-latin-400-italic.woff2") format("woff2"); }
@font-face { font-family: "Karla"; font-weight: 500; font-style: normal; font-display: swap;
    src: url("../fonts/karla-latin-500-normal.woff2") format("woff2"); }
@font-face { font-family: "Karla"; font-weight: 600; font-style: normal; font-display: swap;
    src: url("../fonts/karla-latin-600-normal.woff2") format("woff2"); }
@font-face { font-family: "Karla"; font-weight: 700; font-style: normal; font-display: swap;
    src: url("../fonts/karla-latin-700-normal.woff2") format("woff2"); }
@font-face { font-family: "Asap Condensed"; font-weight: 600; font-style: normal; font-display: swap;
    src: url("../fonts/asap-condensed-latin-600-normal.woff2") format("woff2"); }
@font-face { font-family: "Asap Condensed"; font-weight: 700; font-style: normal; font-display: swap;
    src: url("../fonts/asap-condensed-latin-700-normal.woff2") format("woff2"); }
@font-face { font-family: "Asap Condensed"; font-weight: 800; font-style: normal; font-display: swap;
    src: url("../fonts/asap-condensed-latin-800-normal.woff2") format("woff2"); }

:root {
    --font-text:    "Karla", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
    --font-display: "Asap Condensed", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;

    /* The website's royal indigo, and the mark's three colours. */
    --royal:        #35007a;
    --royal-deep:   #16007a;
    --royal-night:  #12043a;
    --mark-blue:    #077cc7;
    --mark-purple:  #843794;
    --mark-orange:  #eb9521;
    --sun:          #f5e900;
    --castle-stripe: linear-gradient(90deg, var(--mark-blue), var(--mark-purple) 50%, var(--mark-orange));

    /* The navigation is always royal, in either theme: it is the venue's
       colour, not a surface. Its text is measured against #16007a. */
    --nav-bg:       linear-gradient(180deg, var(--royal-deep) 0%, var(--royal) 100%);
    --nav-ink:      rgba(255, 255, 255, .86);
    --nav-faint:    rgba(255, 255, 255, .62);
    --nav-hover:    rgba(255, 255, 255, .09);
    --nav-current:  rgba(255, 255, 255, .16);
    --nav-line:     rgba(255, 255, 255, .13);

    --radius:       12px;
    --radius-sm:    8px;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --nav-bg: linear-gradient(180deg, var(--royal-night) 0%, #1d0b4d 100%);
    }
}
:root[data-theme="dark"] {
    --nav-bg: linear-gradient(180deg, var(--royal-night) 0%, #1d0b4d 100%);
}

/* A battlement edge, drawn once and used as a mask: a wall-top of merlons. */
:root {
    --battlement: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='10' viewBox='0 0 24 10'%3E%3Cpath d='M0 10V0h8v4h8V0h8v10z' fill='black'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------ Typography */

body { font-family: var(--font-text); font-size: 1rem; }

h1, h2, h3, h4,
.page-title, .stat-value, .bare-title, .brand-text strong {
    font-family: var(--font-display);
    font-weight: 700;
    letter-spacing: 0;
    /* A condensed face sets its spaces narrow; at heading sizes "Good
       morning" otherwise reads as one word. */
    word-spacing: .12em;
}
h1 { font-size: 1.6rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.05rem; }

/* Small capitals labels — the website's condensed uppercase voice. */
.nav-heading, .stat-label, .settings-group-heading, .table thead th, .detail-list dt {
    font-family: var(--font-display);
    font-weight: 600;
    letter-spacing: .08em;
}

/* The castle wall: merlons along the top of the account strip. */
.sidebar-foot::before {
    content: ""; position: absolute; left: 0; right: 0; top: -10px; height: 10px;
    background: rgba(0, 0, 0, .14);
    -webkit-mask: var(--battlement) left top / 24px 10px repeat-x;
    mask: var(--battlement) left top / 24px 10px repeat-x;
}

/* The mark's three colours, once, under the venue's name. */
.sidebar-brand::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 3px;
    background: var(--castle-stripe);
}
.brand-text strong { font-size: 1.2rem; text-transform: uppercase; letter-spacing: .02em; line-height: 1.1; }
.brand-text small { color: var(--nav-faint); }
.nav-link .icon { color: var(--nav-faint); }
.nav-link:hover { background: var(--nav-hover); color: #fff; }
.nav-link:hover .icon { color: #fff; }
.nav-link.is-current { background: var(--nav-current); color: #fff; font-weight: 700; }
.nav-link.is-current .icon { color: var(--sun); }
/* A pennant of the mark's colours beside the page you are on. */
.nav-link.is-current::before {
    content: ""; position: absolute; left: -.5rem; top: .3rem; bottom: .3rem; width: 4px;
    border-radius: 0 4px 4px 0; background: linear-gradient(180deg, var(--mark-blue), var(--mark-orange));
}
.sidebar :focus-visible { outline-color: #fff; }
.user-chip:hover { background: var(--nav-hover); }
.user-text small { color: var(--nav-faint); }
.sidebar .avatar { background: var(--sun); color: var(--royal-deep); }
.sidebar-foot .btn-icon { color: var(--nav-ink); }
.sidebar-foot .btn-icon:hover { background: var(--nav-hover); color: #fff; }
.sidebar-foot .btn-icon.is-current { background: var(--nav-current); color: var(--sun); }
.topbar::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
    background: var(--castle-stripe); opacity: .85;
}

/* ------------------------------------------------------------ Cards, stats */

.card, .stat { border-radius: var(--radius); }
.stat::before {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px;
    background: var(--castle-stripe); opacity: .9;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .stat-value, :root:not([data-theme="light"]) .venue-ytd-value, :root:not([data-theme="light"]) .venue-week-value, :root:not([data-theme="light"]) .castle-guest-value, :root:not([data-theme="light"]) .party-card-time strong, :root:not([data-theme="light"]) .venue-today .so-far-value { color: var(--ink); }
}
:root[data-theme="dark"] .stat-value, :root[data-theme="dark"] .venue-ytd-value, :root[data-theme="dark"] .venue-week-value, :root[data-theme="dark"] .castle-guest-value, :root[data-theme="dark"] .party-card-time strong, :root[data-theme="dark"] .venue-today .so-far-value { color: var(--ink); }
.btn:active { transform: translateY(1px); }

/* ---------------------------------------------- Today: the dashboard banner */

.today-card {
    position: relative;
    color: #fff; border: 0;
    background:
        radial-gradient(120% 140% at 100% 0%, rgba(235, 149, 33, .35) 0%, transparent 55%),
        linear-gradient(135deg, var(--royal-deep) 0%, var(--royal) 55%, #5a1aa8 100%);
    box-shadow: 0 10px 30px -12px rgba(22, 0, 122, .55);
    padding-top: 1.6rem;
    margin-top: 4px;
}
/* The wall-top: merlons standing up from the banner's upper edge. */
.today-card::before {
    content: ""; position: absolute; left: 12px; right: 12px; top: -4px; height: 10px;
    background: var(--castle-stripe);
    -webkit-mask: var(--battlement) left top / 24px 10px repeat-x;
    mask: var(--battlement) left top / 24px 10px repeat-x;
}
.today-card h2 { font-size: 2rem; font-weight: 700; letter-spacing: .015em; color: #fff; }
.today-card .card-sub, .today-card .today-when .icon { color: rgba(255, 255, 255, .82); }
.today-card a { color: #fff; }
.today-card .today-who a { color: var(--sun); }
.today-card .today-party { border-top-color: rgba(255, 255, 255, .16); }
.today-card .today-party small, .today-card .today-empty { color: rgba(255, 255, 255, .78); }
.today-card .today-empty a { color: var(--sun); font-weight: 700; }
.today-card .today-allergy { color: #ffd166; }
.today-card .btn { background: #fff; color: var(--royal-deep); }
.today-card .btn:hover { background: var(--sun); color: var(--royal-deep); }
.today-card .btn-ghost { background: transparent; color: #fff; border-color: rgba(255, 255, 255, .55); }
.today-card .btn-ghost:hover { background: rgba(255, 255, 255, .12); color: #fff; }
.today-card .badge-muted { background: rgba(255, 255, 255, .14); color: #fff; border-color: transparent; }
.today-card :focus-visible { outline-color: #fff; }

/* ------------------------------------------------------- Sign-in and guests */

.bare-card::before {
    content: ""; position: absolute; left: 12px; right: 12px; top: -4px; height: 10px;
    background: var(--castle-stripe);
    -webkit-mask: var(--battlement) left top / 24px 10px repeat-x;
    mask: var(--battlement) left top / 24px 10px repeat-x;
}
.bare-brand .brand-mark { flex-basis: 3rem; height: 3rem; }
/* Text that sits on the royal ground rather than on the card. */
body.bare .bare-main > .bare-foot, body.bare .bare-main > p { color: rgba(255, 255, 255, .85); }
body.bare .bare-main > .bare-foot a, body.bare .bare-main > p a { color: #fff; }

/* ------------------------------------------------------------------ Venue */

.venue-tabs {
    display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: var(--gap);
}
.venue-tab {
    padding: .45rem .9rem; border-radius: 999px;
    font-family: var(--font-display); font-weight: 600; font-size: .95rem; letter-spacing: .02em;
    color: var(--ink-soft); background: var(--panel); border: 1px solid var(--line);
}
.venue-tab:hover { border-color: var(--brand); color: var(--brand-dark); text-decoration: none; }
.venue-tab.is-current { background: var(--brand); border-color: var(--brand); color: var(--on-accent); }

.venue-period, .venue-filter {
    display: flex; flex-wrap: wrap; gap: .75rem; align-items: flex-end;
}
.venue-period .field, .venue-filter .field { margin: 0; min-width: 10rem; }
.venue-period-line { flex-basis: 100%; margin: 0; font-size: .88rem; color: var(--muted); }
.venue-filter { margin-bottom: 1rem; }

.venue-stat .stat-value { font-size: 1.9rem; }

/* The dashboard's week at the venue: four figures beside a fortnight of bars. */
.venue-week { position: relative; overflow: hidden; }
.venue-week::before {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px;
    background: var(--castle-stripe);
}
.venue-week-body { display: grid; gap: 1rem 1.5rem; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); align-items: end; }
.venue-week-figures { list-style: none; margin: 0; padding: 0; display: grid; gap: .9rem 1.25rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.venue-week-figures li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .15rem .45rem; }
.venue-week-label { flex-basis: 100%; font-family: var(--font-display); font-weight: 600; font-size: .76rem; letter-spacing: .08em; text-transform: uppercase; color: var(--faint); }
.venue-week-value { font-family: var(--font-display); font-size: 1.45rem; color: var(--royal); }
.venue-week-figures li.is-lead .venue-week-value { font-size: 2.1rem; }
.venue-week-spark { display: flex; align-items: flex-end; gap: 3px; height: 6.5rem; padding-bottom: .2rem; border-bottom: 1px solid var(--line-strong); }
.venue-week-bar { flex: 1 1 0; min-width: 4px; max-width: 24px; border-radius: 4px 4px 0 0; background: var(--brand); opacity: .55; }
.venue-week-bar:last-child { opacity: 1; }
.venue-week-bar:hover { opacity: 1; }
.venue-week-leaders { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; margin: 1rem 0 0; padding-top: .8rem; border-top: 1px solid var(--line); font-size: .9rem; color: var(--muted); }
.venue-week-leaders .icon { vertical-align: -.2em; margin-right: .3rem; color: var(--brand); }
@media (max-width: 48rem) { .venue-week-body { grid-template-columns: 1fr; } }

/* The year so far: one band of four figures under the period's tiles. */
.venue-ytd h2 { margin-bottom: .6rem; }
.venue-ytd-list {
    list-style: none; margin: 0; padding: 0;
    display: grid; gap: .75rem 1.5rem; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
}
.venue-ytd-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .5rem; }
.venue-ytd-label { flex-basis: 100%; font-family: var(--font-display); font-weight: 600; font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; color: var(--faint); }
.venue-ytd-value { font-family: var(--font-display); font-size: 1.5rem; color: var(--royal); }
.venue-ytd-list small { color: var(--faint); }

/* The best days: a short podium. */
.best-days { list-style: none; margin: 0; padding: 0; }
.best-days li { display: flex; align-items: center; gap: .75rem; padding: .55rem 0; border-top: 1px solid var(--line); }
.best-days li:first-child { border-top: 0; }
.best-days-rank {
    flex: 0 0 1.8rem; height: 1.8rem; border-radius: 50%; display: grid; place-items: center;
    font-family: var(--font-display); font-weight: 700; background: var(--brand-light); color: var(--brand-dark);
}
.best-days li:first-child .best-days-rank { background: var(--sun); color: var(--royal-deep); }
.best-days-date { flex: 1 1 auto; }
.best-days-date small { display: block; }

/* A column chart: one series in the venue's purple on hairline gridlines. */
.vchart { margin: 0; }
/* On a phone the chart keeps a readable size and scrolls sideways in its card. */
.vchart-scroll { overflow-x: auto; position: relative; }
.vchart svg { display: block; width: 100%; min-width: 34rem; height: auto; overflow: visible; }
.vchart-compact svg { min-width: 0; }
.vchart-grid { stroke: var(--line); stroke-width: 1; }
.vchart-axis { stroke: var(--line-strong); stroke-width: 1; }
.vchart-tick, .vchart-label { fill: var(--faint); font-size: .6875rem; font-family: var(--font-text); }
.vchart-bar { fill: var(--brand); }
.vchart-hit { fill: transparent; }
.vchart-col:hover .vchart-bar, .vchart-col:focus .vchart-bar { fill: var(--brand-dark); }
.vchart-col:hover .vchart-hit { fill: var(--brand-light); }
.vchart-compare { fill: none; stroke: var(--faint); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; opacity: .8; }
.vchart-legend { display: flex; flex-wrap: wrap; gap: 1rem; margin: .4rem 0 0; font-size: .82rem; color: var(--muted); }
.vchart-legend > span { display: inline-flex; align-items: center; gap: .4rem; }
.vchart-key { display: inline-block; }
.vchart-key-bar { width: .7rem; height: .7rem; border-radius: 2px; background: var(--brand); }
.vchart-key-line { width: 1.1rem; height: 2px; background: var(--faint); }
.vchart-table { margin-top: .5rem; font-size: .88rem; }
.vchart-table summary { cursor: pointer; color: var(--brand); }

/* The weekday-by-hour grid: the venue's purple mixed into the surface in six
   steps, light (quiet) to dark (busiest). */
.heat-wrap { position: relative; }
.heat { border-collapse: separate; border-spacing: 2px; width: 100%; font-size: .75rem; }
.heat th { font-weight: 600; color: var(--faint); text-align: center; padding: .1rem; }
.heat tbody th { text-align: left; padding-right: .4rem; }
.heat td { height: 1.6rem; min-width: 1.4rem; border-radius: 4px; }
.heat-0 { background: var(--panel-soft); }
.heat-1 { background: color-mix(in oklab, var(--brand) 12%, var(--panel)); }
.heat-2 { background: color-mix(in oklab, var(--brand) 26%, var(--panel)); }
.heat-3 { background: color-mix(in oklab, var(--brand) 42%, var(--panel)); }
.heat-4 { background: color-mix(in oklab, var(--brand) 60%, var(--panel)); }
.heat-5 { background: color-mix(in oklab, var(--brand) 80%, var(--panel)); }
.heat-6 { background: var(--brand); }
.heat-key { display: flex; align-items: center; gap: .3rem; font-size: .78rem; color: var(--faint); margin-top: .5rem; }
.heat-swatch { display: inline-block; width: 1.1rem; height: .7rem; border-radius: 3px; }

/* A ranked list: name, a bar for size, the figure and its share, the change. */
.rank { list-style: none; margin: 0; padding: 0; container-type: inline-size; }
.rank-row {
    display: grid; align-items: center; gap: .2rem .9rem;
    grid-template-columns: minmax(0, 1.3fr) minmax(4rem, 1fr) auto 3.6rem;
    padding: .5rem .4rem; border-top: 1px solid var(--line);
}
.rank-row:first-child { border-top: 0; }
.rank-row.is-current { background: var(--brand-light); border-radius: var(--radius-sm); }
.rank-name { min-width: 0; overflow-wrap: anywhere; font-weight: 600; }
.rank-bar { height: .55rem; border-radius: 4px; background: var(--panel-soft); }
.rank-fill { display: block; height: 100%; border-radius: 4px; background: var(--brand); }
.rank-value { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rank-value small { display: block; color: var(--faint); font-size: .76rem; }
.rank-delta { text-align: right; font-size: .82rem; white-space: nowrap; }
/* In a narrow column the bar goes under the name, so a name like
   "Redemption Game Readers" keeps one line instead of three. */
@container (max-width: 34rem) {
    .rank-row { grid-template-columns: minmax(0, 1fr) auto 3.4rem; row-gap: .35rem; }
    .rank-name { grid-column: 1; grid-row: 1; }
    .rank-bar { grid-column: 1; grid-row: 2; }
    .rank-value { grid-column: 2; grid-row: 1 / span 2; }
    .rank-delta { grid-column: 3; grid-row: 1 / span 2; }
}
.rank-rest .rank-name { font-weight: 500; color: var(--muted); }

.table td.is-warn { color: var(--warn); font-weight: 700; }
.table tr.is-current td { background: var(--brand-light); }

/* The hover tooltip for chart marks (assets/js/app.js). */
.chart-tip {
    position: fixed; z-index: 60; pointer-events: none;
    padding: .35rem .6rem; border-radius: var(--radius-sm);
    background: var(--ink); color: var(--panel);
    font-size: .8rem; font-weight: 600; box-shadow: var(--shadow-lg);
    max-width: 18rem;
}


/* ---------------------------------------------------- At the Castle (guest) */

.castle-guest { position: relative; overflow: hidden; }
.castle-guest::before {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px;
    background: var(--castle-stripe);
}
.castle-guest-figures {
    display: grid; gap: 1rem 2rem; align-items: end; margin-bottom: 1.25rem;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.castle-guest-figures > div { display: flex; flex-direction: column; gap: .15rem; }
.castle-guest-label {
    font-family: var(--font-display); font-weight: 600; font-size: .76rem;
    letter-spacing: .08em; text-transform: uppercase; color: var(--faint);
}
.castle-guest-value { font-family: var(--font-display); font-size: 1.6rem; color: var(--royal); }
.castle-guest-figures > .castle-guest-years { flex-direction: row; align-items: flex-end; gap: .4rem; height: 4.5rem; }
.castle-guest-year { display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; min-width: 2.2rem; }
.castle-guest-year-bar { display: block; width: 1.4rem; border-radius: 4px 4px 0 0; background: var(--brand); }
.castle-guest-year small { font-size: .72rem; color: var(--faint); margin-top: .2rem; }
.castle-guest-subhead { margin: 0 0 .5rem; font-size: 1rem; }
.castle-guest-cards { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.castle-guest-card-figures { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; color: var(--muted); }
.castle-guest-card-figures strong { font-family: var(--font-display); font-size: 1.15rem; color: var(--ink); }

/* ------------------------------------------------- Did a campaign work? */

.campaign-impact { position: relative; overflow: hidden; }
.campaign-impact::before {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px;
    background: var(--castle-stripe);
}
.campaign-impact-headline { font-size: 1.05rem; margin: .75rem 0 1rem; }
.campaign-impact-headline .delta { font-family: var(--font-display); font-size: 1.4rem; margin-right: .3rem; }
.campaign-impact-groups { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); margin-bottom: .75rem; }
.campaign-impact-group { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: .9rem 1rem; }
.campaign-impact-group.is-lead { border-color: var(--brand); background: var(--brand-light); }
.campaign-impact-group h3 { margin: 0 0 .5rem; font-size: 1rem; }
.campaign-impact-group dl { margin: 0; display: grid; gap: .35rem; }
.campaign-impact-group dl > div { display: flex; justify-content: space-between; gap: 1rem; }
.campaign-impact-group dt { color: var(--muted); }
.campaign-impact-group dd { margin: 0; font-weight: 700; font-variant-numeric: tabular-nums; }
.campaign-impact-group dd small { font-weight: 500; color: var(--faint); }

/* ------------------------------------------------------- Search, top bar */

.topbar-search {
    position: relative; flex: 0 1 24rem; min-width: 0; margin: 0;
    display: flex; align-items: center; gap: .4rem;
    padding: 0 .6rem; height: 2.3rem;
    background: var(--bg); border: 1px solid var(--line-strong); border-radius: 999px;
}
.topbar-search:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); background: var(--panel); }
.topbar-search .icon { flex: 0 0 auto; color: var(--faint); }
.topbar-search input {
    flex: 1 1 auto; min-width: 0; height: 100%; padding: 0;
    border: 0; background: transparent; box-shadow: none; font: inherit; font-size: .92rem; color: var(--ink);
}
.topbar-search-key {
    flex: 0 0 auto; font-family: var(--font-text); font-size: .7rem; color: var(--faint);
    border: 1px solid var(--line-strong); border-radius: 4px; padding: .05rem .3rem;
}
.quick-results {
    position: absolute; top: calc(100% + .4rem); left: 0; right: 0; z-index: 40;
    max-height: 70vh; overflow-y: auto; padding: .35rem;
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
}
.quick-results-heading {
    margin: .4rem .5rem .2rem; font-family: var(--font-display); font-weight: 600;
    font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--faint);
}
.quick-results a { display: block; padding: .45rem .55rem; border-radius: var(--radius-sm); color: var(--ink); text-decoration: none; }
.quick-results a small { display: block; color: var(--muted); font-size: .78rem; }
.quick-results a:hover, .quick-results a.is-active { background: var(--brand-light); }
.quick-results a.is-active span { color: var(--brand-dark); font-weight: 700; }
.quick-results-empty { margin: .5rem; color: var(--muted); font-size: .88rem; }
@media (max-width: 60rem) { .topbar-search-key { display: none; } .topbar-search { flex-basis: 14rem; } }
@media (max-width: 38rem) { .page-title { display: none; } .topbar-search { flex: 1 1 auto; } }

.search-page-form { display: flex; flex-wrap: wrap; gap: .75rem; align-items: flex-end; }
.search-page-form .field { flex: 1 1 20rem; margin: 0; }
.search-results { list-style: none; margin: 0; padding: 0; }
.search-results li { padding: .55rem 0; border-top: 1px solid var(--line); }
.search-results li:first-child { border-top: 0; }
.search-results a { font-weight: 700; }
.search-results small { display: block; color: var(--muted); }

/* The Monday report shown as the email it is, on the email's own paper. */
.weekly-preview { border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
.weekly-callout { display: flex; align-items: center; gap: 1rem; margin-bottom: var(--gap); color: var(--ink); text-decoration: none; }
.weekly-callout:hover { border-color: var(--brand); text-decoration: none; }

/* ------------------------------------------------- Party day, on a phone */

.party-hero {
    display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-end; justify-content: space-between;
    margin-bottom: var(--gap); padding: 1.1rem 1.25rem; border-radius: var(--radius);
    color: #fff; background: linear-gradient(135deg, var(--royal-deep) 0%, var(--royal) 60%, #5a1aa8 100%);
}
.party-hero-main { min-width: 0; }
.party-hero h2 { margin: 0; font-size: 1.8rem; color: #fff; }
.party-hero h2 small { font-family: var(--font-text); font-size: .95rem; font-weight: 500; color: rgba(255, 255, 255, .8); margin-left: .35rem; }
.party-hero-when { margin: .25rem 0 .5rem; color: rgba(255, 255, 255, .85); }
.party-hero-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.party-hero-actions .btn-ghost { color: #fff; border-color: rgba(255, 255, 255, .55); }
.party-hero-actions .btn-ghost:hover { background: rgba(255, 255, 255, .12); color: #fff; }
.party-hero :focus-visible { outline-color: #fff; }
.party-phase {
    display: inline-block; padding: .2rem .7rem; border-radius: 999px;
    font-family: var(--font-display); font-weight: 700; letter-spacing: .03em;
}
.party-phase-soon { background: rgba(255, 255, 255, .16); color: #fff; }
.party-phase-now  { background: var(--sun); color: var(--royal-deep); }
.party-phase-done { background: rgba(255, 255, 255, .1); color: rgba(255, 255, 255, .75); }

.party-allergy {
    display: flex; gap: .75rem; align-items: center; margin-bottom: var(--gap);
    padding: .9rem 1.1rem; border-radius: var(--radius);
    background: var(--bad-bg); color: var(--bad); border: 2px solid var(--bad);
}
.party-allergy strong { display: block; font-size: 1.15rem; }
.party-allergy span { display: block; }

.party-progress-head { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
.party-progress { height: .55rem; margin: .5rem 0 .9rem; border-radius: 999px; background: var(--panel-soft); overflow: hidden; }
.party-progress span { display: block; height: 100%; border-radius: 999px; background: var(--castle-stripe); }

.party-guests { list-style: none; margin: 0; padding: 0; }
.party-guest { display: flex; align-items: center; gap: .85rem; padding: .6rem 0; border-top: 1px solid var(--line); }
.party-guest:first-child { border-top: 0; }
.party-guest-toggle { margin: 0; flex: 0 0 auto; }
/* A target a thumb can hit without looking twice: 48px. */
.party-check {
    width: 3rem; height: 3rem; border-radius: 12px; cursor: pointer;
    display: grid; place-items: center;
    border: 2px solid var(--field-border); background: var(--panel); color: var(--on-accent);
}
.party-check:hover { border-color: var(--brand); }
.party-guest.is-in .party-check { background: var(--ok); border-color: var(--ok); }
.party-guest-body { flex: 1 1 auto; min-width: 0; }
.party-guest-body strong { font-size: 1.05rem; margin-right: .35rem; }
.party-guest-meta { display: block; font-size: .82rem; color: var(--muted); margin-top: .15rem; }
.party-guest.is-in .party-guest-body strong { color: var(--muted); }
.party-call {
    flex: 0 0 auto; width: 2.75rem; height: 2.75rem; border-radius: 50%;
    display: grid; place-items: center; background: var(--brand-light); color: var(--brand-dark);
}
.party-call:hover { background: var(--brand); color: var(--on-accent); }

/* The day's parties: one tappable card each. */
.party-card { display: flex; align-items: center; gap: 1rem; color: var(--ink); text-decoration: none; }
.party-card:hover { border-color: var(--brand); text-decoration: none; }
.party-card > .icon { flex: 0 0 auto; color: var(--faint); }
.party-card-time { flex: 0 0 5.5rem; display: flex; flex-direction: column; }
.party-card-time strong { font-family: var(--font-display); font-size: 1.25rem; color: var(--royal); }
.party-card-time small { color: var(--faint); }
.party-card-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .3rem; }
.party-card-title { font-weight: 700; font-size: 1.08rem; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.party-card-meta { color: var(--muted); font-size: .88rem; }
.party-card-allergy { color: var(--bad); font-weight: 700; font-size: .9rem; }
.party-card-allergy .icon { vertical-align: -.15em; }
.party-card-progress { display: flex; flex-direction: column; }
.party-card-progress .party-progress { display: block; margin: .2rem 0 .15rem; max-width: 18rem; }
.party-card-progress small { color: var(--muted); }
.party-card-phase { padding: .1rem .55rem; border-radius: 999px; font-family: var(--font-display); font-size: .82rem; letter-spacing: .03em; }
.party-card-phase-soon { background: var(--brand-light); color: var(--brand-dark); }
.party-card-phase-now  { background: var(--sun); color: var(--royal-deep); }
.party-card-phase-done { background: var(--panel-soft); color: var(--faint); }

/* ------------------------------------------------- Guests in the CRM twice */

.dup-pair { position: relative; }
.dup-head { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-bottom: .75rem; }
.dup-sides { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.dup-side { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: .9rem 1rem; display: flex; flex-direction: column; gap: .6rem; }
.dup-name { font-weight: 700; font-size: 1.05rem; }
.dup-facts { margin: 0; display: grid; gap: .25rem; font-size: .88rem; }
.dup-facts > div { display: grid; grid-template-columns: 8.5rem 1fr; gap: .5rem; }
.dup-facts dt { color: var(--faint); }
.dup-facts dd { margin: 0; overflow-wrap: anywhere; }
.dup-dismiss { margin: .75rem 0 0; }

/* ------------------------------------------------- Staff guide and welcome */

.guide-hero {
    position: relative; color: #fff; border: 0; padding-top: 1.6rem; margin-top: 4px;
    background:
        radial-gradient(120% 140% at 100% 0%, rgba(7, 124, 199, .35) 0%, transparent 55%),
        linear-gradient(135deg, var(--royal-deep) 0%, var(--royal) 60%, #5a1aa8 100%);
    box-shadow: 0 10px 30px -12px rgba(22, 0, 122, .55);
}
.guide-hero::before, .welcome-card::before {
    content: ""; position: absolute; left: 12px; right: 12px; top: -4px; height: 10px;
    background: var(--castle-stripe);
    -webkit-mask: var(--battlement) left top / 24px 10px repeat-x;
    mask: var(--battlement) left top / 24px 10px repeat-x;
}
.guide-hero h2 { font-size: 2rem; font-weight: 700; color: #fff; }
.guide-hero .card-sub { color: rgba(255, 255, 255, .82); }
.guide-find {
    display: flex; align-items: center; gap: .5rem; margin-top: 1rem; max-width: 34rem;
    background: #fff; color: #3d3550; border-radius: 999px; padding: .2rem .4rem .2rem 1rem;
}
.guide-find input { border: 0; background: transparent; flex: 1; font: inherit; font-size: 1rem; padding: .55rem 0; color: #1d1530; min-width: 0; }
/* The pill shows focus (the sun ring below), so the field inside does not draw a second one. */
.guide-find input:focus, .guide-find input:focus-visible { box-shadow: none; border: 0; outline-color: transparent; }
.guide-find:focus-within { box-shadow: 0 0 0 3px var(--sun); }
.guide-jump { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1rem; }
.guide-jump a {
    display: inline-flex; align-items: center; gap: .35rem; padding: .35rem .8rem; border-radius: 999px;
    background: rgba(255, 255, 255, .12); color: #fff; font-size: .85rem; font-weight: 600;
}
.guide-jump a:hover { background: rgba(255, 255, 255, .22); text-decoration: none; }
.guide-jump .icon { color: var(--sun); }

.guide-body { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr)); gap: var(--gap); align-items: start; }
.guide-body > .card { margin-bottom: 0; }
.guide-section { scroll-margin-top: 5rem; }
.guide-section-head { display: flex; gap: .85rem; align-items: flex-start; margin-bottom: .75rem; }
.guide-section-head h2 { font-size: 1.3rem; }
.guide-section-icon {
    flex: none; display: grid; place-items: center; width: 2.4rem; height: 2.4rem; border-radius: 10px;
    background: var(--brand-light); color: var(--brand);
}
.guide-item { border-top: 1px solid var(--line); }
.guide-item summary {
    cursor: pointer; list-style: none; padding: .7rem 1.6rem .7rem 0; position: relative;
    font-weight: 600; color: var(--ink);
}
.guide-item summary::-webkit-details-marker { display: none; }
.guide-item summary::after {
    content: "+"; position: absolute; right: .25rem; top: .55rem; font-size: 1.2rem; color: var(--brand); font-weight: 700;
}
.guide-item[open] summary::after { content: "–"; }
.guide-item summary:hover { color: var(--brand); }
.guide-item[open] { padding-bottom: .9rem; }
.guide-steps { margin: 0 0 .8rem; padding-left: 1.4rem; color: var(--ink-soft); }
.guide-steps li { margin-bottom: .35rem; line-height: 1.5; padding-left: .3rem; }
.guide-steps li::marker { color: var(--brand); font-weight: 700; font-family: var(--font-display); }
.guide-steps + .btn .icon { vertical-align: -.15em; }
.guide-none { grid-column: 1 / -1; color: var(--muted); }
.guide-foot { color: var(--muted); font-size: .9rem; margin-top: var(--gap); text-align: center; }
kbd {
    font-family: ui-monospace, monospace; font-size: .8em; padding: .1em .45em; border-radius: 5px;
    border: 1px solid var(--line-strong); border-bottom-width: 2px; background: var(--panel); color: var(--ink);
}

.welcome-card { position: relative; margin-top: 4px; padding-top: 1.6rem; border-color: var(--line); }
.welcome-head { display: flex; gap: var(--gap); justify-content: space-between; align-items: flex-start; flex-wrap: wrap; }
.welcome-head h2 { font-size: 1.6rem; }
.welcome-tiles {
    list-style: none; margin: 1rem 0 0; padding: 0;
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: .75rem;
}
.welcome-tile {
    display: flex; flex-direction: column; gap: .3rem; height: 100%; padding: 1rem;
    border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); color: var(--ink-soft);
    font-size: .9rem; transition: border-color .15s, transform .15s;
}
.welcome-tile strong { color: var(--ink); font-family: var(--font-display); font-size: 1.1rem; }
.welcome-tile:hover { border-color: var(--brand); transform: translateY(-2px); text-decoration: none; }
.welcome-tile-icon {
    display: grid; place-items: center; width: 2.2rem; height: 2.2rem; border-radius: 10px; margin-bottom: .2rem;
    background: linear-gradient(135deg, var(--royal), #5a1aa8); color: var(--sun);
}
.welcome-guide { display: flex; gap: .5rem; align-items: flex-start; margin-top: 1rem; color: var(--muted); font-size: .9rem; }
.welcome-guide .icon { flex: none; margin-top: .15em; color: var(--brand); }
.link-button {
    background: none; border: 0; padding: 0; font: inherit; color: var(--brand); font-weight: 600;
    cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
}
.link-button:hover { color: var(--brand-dark); }

/* ------------------------------------------------------- Presentation mode */

.present-pill {
    display: inline-flex; align-items: center; gap: .5rem; padding: .2rem .25rem .2rem .7rem;
    border-radius: 999px; background: var(--royal); color: #fff; font-size: .8rem; font-weight: 700;
}
.present-pill span { display: inline-flex; align-items: center; gap: .35rem; }
.present-pill .icon { color: var(--sun); }
.present-pill button {
    border: 0; border-radius: 999px; padding: .2rem .65rem; cursor: pointer;
    background: var(--sun); color: var(--royal-deep); font: inherit; font-weight: 700;
}
.present-pill button:hover { filter: brightness(.95); }
/* A frame in the mark's colours, so nobody forgets the room can see it. */
body[data-presenting] .topbar::after { height: 4px; opacity: 1; }

/* ------------------------------------------------------------ Today so far */

.so-far { display: flex; flex-direction: column; gap: .9rem; }
.so-far-figures { display: flex; flex-wrap: wrap; gap: .75rem 2rem; align-items: flex-start; }
.so-far-figures > div { display: flex; flex-direction: column; gap: .1rem; }
.so-far-figures small { color: var(--faint); font-size: .8rem; }
.so-far-label { font-family: var(--font-display); font-weight: 600; font-size: .76rem; letter-spacing: .08em; text-transform: uppercase; color: var(--faint); }
.so-far-value { font-family: var(--font-display); font-size: 2.4rem; line-height: 1; color: var(--royal); }
.so-far-num { font-family: var(--font-display); font-size: 1.45rem; line-height: 1.1; color: var(--ink); }
.so-far-pace { display: flex; gap: .45rem; align-items: flex-start; margin: 0; font-weight: 600; }
.so-far-pace .icon { flex: none; margin-top: .2em; }
.so-far-pace-up .icon { color: var(--ok); }
.so-far-pace-down .icon { color: var(--warn); }
.so-far-pace-flat .icon { color: var(--brand); }
.so-far-pace span { font-weight: 400; color: var(--muted); }
.so-far-pace span::first-line { font-weight: 600; color: var(--ink); }

.so-far-chart { margin: 0; }
.so-far-bars { display: flex; align-items: stretch; gap: 4px; height: 7rem; border-bottom: 1px solid var(--line-strong); }
.so-far-hour { position: relative; flex: 1 1 0; min-width: 6px; display: flex; align-items: flex-end; }
.so-far-bar { display: block; width: 100%; border-radius: 4px 4px 0 0; background: var(--brand); }
/* The hour still going: hatched, because it is not finished. */
.so-far-now .so-far-bar { background: repeating-linear-gradient(135deg, var(--brand) 0 4px, transparent 4px 7px); box-shadow: inset 0 0 0 1.5px var(--brand); }
/* A usual day, as a mark at its height: there to measure against, not to read first. */
.so-far-usual { position: absolute; left: -1px; right: -1px; height: 2px; border-radius: 2px; background: var(--ink-soft); opacity: .55; }
.so-far-later .so-far-usual { opacity: .3; }
.so-far-hour:hover .so-far-bar { filter: brightness(1.1); }
.so-far-axis { display: flex; justify-content: space-between; align-items: center; gap: 1rem; margin-top: .35rem; font-size: .78rem; color: var(--faint); }
.so-far-legend { display: inline-flex; gap: 1rem; }
.so-far-legend span { display: inline-flex; align-items: center; gap: .35rem; }
.so-far-key-bar { display: inline-block; width: .7rem; height: .7rem; border-radius: 2px; background: var(--brand); }
.so-far-key-usual { display: inline-block; width: .9rem; height: 2px; border-radius: 2px; background: var(--ink-soft); opacity: .7; }

.so-far-lists { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: 1rem 2rem; }
.so-far-lists h3 { font-size: 1rem; margin-bottom: .4rem; }
.so-far-lists ol { margin: 0; padding: 0; list-style: none; counter-reset: so-far; }
.so-far-lists li { display: flex; gap: .5rem; align-items: baseline; padding: .35rem 0; border-top: 1px solid var(--line); counter-increment: so-far; }
.so-far-lists li::before { content: counter(so-far); font-family: var(--font-display); font-weight: 700; color: var(--brand); width: 1.1rem; }
.so-far-lists li span { flex: 1; }
.so-far-lists li small { color: var(--faint); }
.so-far-updated { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; margin: 0; font-size: .82rem; color: var(--faint); }
.so-far-updated a { margin-left: auto; font-weight: 600; }
.so-far-updated.is-stale { color: var(--warn); }

/* In the dashboard's royal banner. */
.today-card .so-far-compact { margin: .25rem 0 .5rem; padding: 1rem 0 .25rem; border-top: 1px solid rgba(255, 255, 255, .16); }
.today-card .so-far-label, .today-card .so-far-figures small, .today-card .so-far-axis, .today-card .so-far-updated { color: rgba(255, 255, 255, .72); }
.today-card .so-far-value, .today-card .so-far-num { color: #fff; }
.today-card .so-far-value { color: var(--sun); }
.today-card .so-far-pace span { color: rgba(255, 255, 255, .8); }
.today-card .so-far-pace span::first-line { color: #fff; }
.today-card .so-far-pace .icon { color: var(--sun); }
.today-card .so-far-bars { height: 4.5rem; border-bottom-color: rgba(255, 255, 255, .25); }
.today-card .so-far-bar, .today-card .so-far-key-bar { background: var(--sun); }
.today-card .so-far-now .so-far-bar { background: repeating-linear-gradient(135deg, var(--sun) 0 4px, transparent 4px 7px); box-shadow: inset 0 0 0 1.5px var(--sun); }
.today-card .so-far-usual, .today-card .so-far-key-usual { background: #fff; }
.today-card .so-far-updated a { color: var(--sun); }
.today-card .so-far-updated.is-stale { color: #ffd38a; }

/* A money breakdown: the amounts line up on the right edge, digit under digit. */
.table-money td { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.segment-uncounted { white-space: nowrap; }
/* The name stands on its own line, so it reads as a link rather than as a
   purple word in the middle of the row's description. */
.segment-name { display: block; width: fit-content; }


/* ------------------------------------------------------------------ two-step sign-in */

/* A code is read digit by digit, so it gets a monospaced, spaced-out box. */
.otp-input { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 1.25rem; letter-spacing: .2em; max-width: 14rem; }
.code-help { font-size: .85rem; color: var(--muted); }
.code-help > summary { cursor: pointer; color: var(--brand); }
.code-help p { margin-top: .5rem; }

.two-factor-steps { display: grid; gap: 1.25rem; padding-left: 1.25rem; margin: 0; }
.two-factor-steps > li::marker { font-weight: 700; color: var(--brand); }
/* White behind the code in every theme: a scanner needs dark on light. */
.two-factor-qr { display: block; width: fit-content; margin: .75rem 0 .25rem; padding: .5rem; background: #fff; border-radius: var(--radius-sm); border: 1px solid var(--line); }
.two-factor-qr svg { display: block; width: 12.5rem; height: auto; max-width: 100%; }
.two-factor-key { font-size: .95rem; letter-spacing: .06em; overflow-wrap: anywhere; }
.two-factor-confirm { max-width: 20rem; margin-top: .6rem; }

.recovery-codes { list-style: none; padding: 0; margin: 0 0 1rem; display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: .5rem; }
.recovery-codes code { display: block; padding: .5rem .75rem; text-align: center; font-size: 1rem; letter-spacing: .06em; background: var(--panel-soft); border: 1px solid var(--line); border-radius: var(--radius-sm); }

/* The minute's warning before an idle page signs itself out. */
.idle-warning {
    position: fixed; z-index: 60; left: 50%; transform: translateX(-50%);
    bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
    width: min(30rem, calc(100% - 2rem));
    display: flex; gap: .75rem; align-items: center;
    padding: .85rem 1rem; border-radius: var(--radius); border: 1px solid var(--warn);
    background: var(--warn-bg); color: var(--warn); box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, .2));
}
.idle-warning p { margin: 0; flex: 1; font-size: .88rem; }

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

/* A list that has to read on a phone: below 40rem each row is a block, its
   first cell the heading and every other value under its column's name
   (copied onto the cell by app.js). Empty cells and action cells keep their
   place without a label. */
@media (max-width: 40rem) {
    .table-stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
    .table-stack, .table-stack tbody { display: block; width: 100%; }
    .table-stack tbody tr {
        display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .55rem 1rem;
        padding: .85rem 0; border-bottom: 1px solid var(--line);
    }
    .table-stack tbody tr:last-child { border-bottom: 0; }
    .table-stack tbody > tr > * { display: block; padding: 0; border: 0; min-width: 0; white-space: normal; text-align: left; overflow-wrap: anywhere; }
    .table-stack tbody > tr > :first-child { grid-column: 1 / -1; font-size: 1rem; }
    .table-stack tbody > tr > [data-label]:not(:first-child)::before {
        content: attr(data-label); display: block; margin-bottom: .1rem;
        font-size: .68rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--faint);
    }
    .table-stack tbody > tr > :last-child:has(> form, > .btn) { grid-column: 1 / -1; }
    .table-stack tbody > tr > :last-child:has(> form, > .btn)::before { content: none; }
}
