/**
 * Page-scoped styles
 *
 * What is left after the component layer: the shapes that genuinely belong to
 * one screen. This replaces the eleven files under pages/, which between them
 * carried 796 `!important` declarations and 409 literal hex colours because
 * each was fighting the others over `.btn`, `.card` and `.card-header`.
 *
 * Nothing here restates a component. If a rule below starts to look like one,
 * it belongs upstairs.
 */

/* ============================================
   SHARED PAGE CHROME
   ============================================ */

.page-header {
    position: relative;
    margin-bottom: var(--space-8);
}

.header-content,
.header-title-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.header-nav-actions,
.header-primary-actions,
.header-actions,
.nav-actions-left,
.nav-actions-right,
.action-buttons {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.header-nav-actions {
    margin-bottom: var(--space-5);
}

.event-title {
    color: var(--color-cream);
    font-family: var(--font-display);
    font-size: 2.625rem;   /* 42px */
    font-weight: var(--font-weight-normal);
    letter-spacing: var(--tracking-display);
    line-height: var(--line-height-display);
}

.event-subtitle {
    max-width: 60ch;
    color: var(--text-on-dark-muted);
    font-size: var(--font-size-md);
}

.event-details,
.event-stats {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.header-icon,
.icon-inline,
.icon-inline-sm,
.icon-inline-lg,
.icon-inline-xl {
    display: inline-block;
    vertical-align: -0.15em;
    object-fit: contain;
}

.icon-inline-sm { width: 16px; height: 16px; }
.icon-inline { width: 20px; height: 20px; }
.icon-inline-lg { width: 26px; height: 26px; }
.icon-inline-xl { width: 34px; height: 34px; }
.header-icon { width: 44px; height: 44px; }

.return-section {
    display: flex;
    justify-content: center;
    margin-top: var(--space-10);
}

/* The exchange countdown sits beside the date. The red it carried inline was
   1.6:1 on teal, and gold on the tinted meta chip is 4.4:1, so it takes the
   surface's own text colour. */
#countdown-timer {
    color: var(--surface-text);
    font-weight: var(--font-weight-semibold);
}

/* ============================================
   SIGN IN AND CREATE ACCOUNT

   Two columns at 1440: a brand panel and the form. One column below 900.
   ============================================ */

.auth-layout {
    display: grid;
    grid-template-columns: 1fr 560px;
    align-items: center;
    gap: var(--space-16);
    min-height: 100vh;
    max-width: 1200px;
    margin: 0 auto;
    /*
     * The horizontal gutter is .container's, not its own.
     *
     * Both shells cap at 1200px, but this used --space-10 all round against
     * .container's --space-md, so signing in shifted the content's left edge by
     * 24px at desk widths. The vertical breathing room stays; the side gutter
     * matches, so the page does not step sideways when you cross between them.
     * Below 960 they already agreed.
     */
    padding: var(--space-10) var(--space-md);
}

.auth-brand {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    color: var(--color-cream);
}

.auth-brand h1,
.auth-brand .auth-brand-headline {
    font-family: var(--font-display);
    font-size: var(--display-xl);
    font-weight: var(--font-weight-normal);
    letter-spacing: var(--tracking-display);
    line-height: var(--line-height-display);
}

.auth-brand p {
    max-width: 46ch;
    color: var(--text-on-dark-muted);
    font-size: var(--font-size-md);
}

.auth-brand-scene {
    display: flex;
    align-items: flex-end;
    gap: var(--space-4);
    margin-top: var(--space-6);
    /* The three figures share one ground line and shrink together, so the
       scene never pushes the page sideways on a phone. */
    max-width: 100%;
}

.auth-brand-scene img {
    height: auto;
    min-width: 0;
    max-width: 100%;
    object-fit: contain;
}

@media (max-width: 640px) {
    .auth-brand-scene {
        gap: var(--space-2);
    }

    .auth-brand-scene img:nth-child(1) { width: 84px; }
    .auth-brand-scene img:nth-child(2) { width: 132px; }
    .auth-brand-scene img:nth-child(3) { width: 44px; }
}

.auth-brand-steps {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    margin-top: var(--space-4);
}

.auth-brand-step {
    display: flex;
    align-items: flex-start;
    gap: var(--space-4);
}

.auth-brand-step-number {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    border: 1px solid var(--border-on-dark);
    border-radius: var(--radius-full);
    color: var(--color-gold);
    font-weight: var(--font-weight-bold);
}

.auth-container {
    width: 100%;
    max-width: 560px;
    justify-self: end;
}

/*
 * One card and nothing else.
 *
 * .auth-layout is `1fr 560px` — a brand column and a form column. Unsubscribe
 * has no brand, and dropped both wrappers, so its card became the first grid
 * item and took the ILLUSTRATION column: 495px wide, pinned to the left third
 * at 1440, with the entire right half of the page empty. A page reached from an
 * email, and the one screen in the flow nobody had ever screenshotted.
 */
.auth-layout-solo {
    grid-template-columns: minmax(0, 560px);
    justify-content: center;
    /*
     * The mark and the card are one group, vertically centred together.
     *
     * .auth-layout inherits align-items: center with min-height: 100vh, which
     * is right for a single item and wrong for two: the mark took its own row
     * track and floated 350px above the card with nothing between them.
     * align-content centres the pair; the gap is what separates them.
     */
    align-content: center;
    gap: var(--space-6);
}

/*
 * Used by exactly one page — confirm-participation — whose container is a
 * centred 560px column and whose card centres all of its own contents. Without
 * justify-content the mark sat hard against the left edge, 251px off the card's
 * centre line, reading as a stray santa floating above the corner.
 */
.logo-section {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    margin-bottom: var(--space-6);
}

.logo-section img {
    width: 56px;
    height: 56px;
    margin: 0;
    object-fit: contain;
}

@media (max-width: 960px) {
    .auth-layout {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-8);
        padding: var(--space-8) var(--space-4);
    }

    .auth-brand h1,
    .auth-brand .auth-brand-headline {
        font-size: var(--display-md);
    }

    .auth-container {
        justify-self: stretch;
        max-width: none;
    }
}

/* ============================================
   EVENTS DASHBOARD
   ============================================ */

.dashboard-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-8);
    margin-bottom: var(--space-8);
}

.create-event-toggle {
    /* Its last pixel row and the card's first cream row were adjacent. */
    margin-bottom: var(--space-5);
    background: var(--color-red-dark);
    color: var(--text-on-dark);
    box-shadow: var(--shadow-cta);
}

.create-event-toggle:hover {
    background: var(--color-red-dark-hover);
    color: var(--text-on-dark);
}

.event-card {
    position: relative;
    display: flex;
    flex-direction: column;
    background: var(--card-face);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
    color: var(--text-on-light);
    overflow: hidden;
}

.event-card > * {
    min-width: 0;
}

.event-card-header {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-5) var(--space-5) var(--space-4);
    border-bottom: 1px solid var(--border-on-light-hairline);
}

.event-card-title {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-display);
    font-size: var(--display-sm);
    font-weight: var(--font-weight-normal);
    letter-spacing: var(--tracking-display);
    color: var(--text-on-light);
    overflow-wrap: anywhere;
}

.event-card-date {
    color: var(--text-on-light-muted);
    font-size: var(--font-size-sm);
}

.event-card-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    padding: var(--space-5);
}

.event-action-btn {
    align-self: flex-start;
}

/* Title takes the slack; the status pill holds the right edge and never wraps
   under it. */
.event-card-header-row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
}

/* One clay render per card, varying by event — the thing that makes a row of
   cards tellable apart before you read any of them. */
.event-card-mark {
    flex: 0 0 auto;
    width: 42px;
    height: 42px;
    object-fit: contain;
}

.event-card-header-row .event-card-title {
    flex: 1 1 auto;
}

.event-card-header-row .badge {
    flex: 0 0 auto;
    margin-top: 2px;
    margin-left: auto;
}

.event-card-people {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-1);
}

/*
 * The card's footer names the next action. It is a full-width row rather than a
 * button because it is the whole bottom edge of the card, and the arrow says
 * which way it goes.
 */
.event-card-next {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: auto;
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--border-on-light-hairline);
    color: var(--color-red-dark);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
    transition: background-color var(--duration-fast) var(--easing);
}

.event-card-next:hover,
.event-card-next:focus-visible {
    background: var(--color-red-wash);
    color: var(--color-red-dark);
    text-decoration: none;
}

/* The clay renders are illustration, not iconography: one scene per card at a
   size where it reads, rather than a swinging bauble in the corner. */
.card-decoration {
    position: absolute;
    top: var(--space-4);
    right: var(--space-4);
    width: 44px;
    height: 44px;
    object-fit: contain;
    opacity: 0.9;
    pointer-events: none;
}

/* ============================================
   CREATE EVENT
   ============================================ */

.page-create-event .card {
    max-width: 720px;
}

/* ============================================
   MANAGE EVENT
   ============================================ */

.participants-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--space-4);
}

.participant-card {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4);
    border: 1px solid var(--border-on-light-hairline);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.5);
}

.participant-number {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-full);
    background: var(--color-teal-dark);
    color: var(--text-on-dark);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-bold);
}

.participant-meta {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.participant-name {
    font-weight: var(--font-weight-semibold);
    overflow-wrap: anywhere;
}

.participant-email {
    color: var(--text-on-light-muted);
    font-size: var(--font-size-sm);
    overflow-wrap: anywhere;
}

.participant-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-left: auto;
}

.meta-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--border-on-dark);
    border-radius: var(--radius-full);
    color: var(--color-cream);
    font-size: var(--font-size-sm);
}

.exclusions-matrix th,
.exclusions-matrix td {
    text-align: center;
    vertical-align: middle;
    white-space: nowrap;
}

.exclusions-matrix .matrix-corner,
.exclusions-matrix .matrix-header {
    background: rgba(26, 77, 77, 0.06);
    font-weight: var(--font-weight-semibold);
}

/*
 * The progress tracker: the six-step stepper and the card naming the single
 * next action. It used to carry its own <style> block inside the PHP partial,
 * with eight `!important` declarations and its own green palette.
 */
/*
 * Space above the tracker.
 *
 * It carried a margin-bottom and nothing on top, and relied on whatever the
 * block above happened to bring. The deadline alert brings one; the
 * draw-failure card does not, so on that state the panel's cream edge ended
 * 4px above the "Event Progress" heading and the two collided.
 */
.progress-tracker {
    margin-top: var(--space-5);
    display: grid;
    grid-template-columns: minmax(0, 1fr) 288px;
    align-items: start;
    gap: var(--space-8);
    margin-bottom: var(--space-10);
}

.next-step-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-5);
    border-radius: var(--radius-card);
    background: var(--card-face);
    box-shadow: var(--shadow-card);
    color: var(--text-on-light);
}

.next-step-card .eyebrow {
    color: var(--color-gold-ink);
}

.next-step-title {
    font-family: var(--font-display);
    font-size: var(--display-sm);
    font-weight: var(--font-weight-normal);
    letter-spacing: var(--tracking-display);
}

.next-step-description {
    color: var(--text-on-light-muted);
    font-size: var(--font-size-sm);
}

.next-step-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.next-step-actions form {
    margin: 0;
}

@media (max-width: 1024px) {
    .progress-tracker {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ============================================
   EMAIL TEMPLATES
   ============================================ */

.template-editor-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 680px;
    gap: var(--space-6);
    align-items: start;
}

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

.language-group,
.language-preview-card {
    margin-bottom: var(--space-5);
}

/* Language tabs are pills carrying the ISO code, the language name and the
   number of participants who will get that template. */
.language-code {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 28px;
    height: 20px;
    margin-right: var(--space-2);
    padding: 0 var(--space-1);
    border-radius: var(--radius-sm);
    background: var(--color-warning-tint);
    color: var(--color-gold-ink);
    font-size: 0.6875rem;
    font-weight: var(--font-weight-bold);
    letter-spacing: 0.04em;
}

/* ============================================
   PREVIEW AND SEND
   ============================================ */

.participants-accordion-card .participant-row {
    border-bottom: 1px solid var(--border-on-light-hairline);
}

.participants-accordion-card .participant-row:last-child {
    border-bottom: none;
}

.participant-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-5);
}

.participant-info {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}

.participant-language {
    color: var(--text-on-light-muted);
    font-size: var(--font-size-sm);
}

.participant-toggle-icon {
    display: inline-flex;
    flex: 0 0 auto;
    color: var(--surface-text-muted);
    transform: rotate(-90deg);
    transition: transform var(--duration-base) var(--easing);
}

.participant-toggle-icon.is-open {
    transform: rotate(0deg);
}

.participant-preview {
    display: none;
    padding: 0 var(--space-5) var(--space-5);
}

.participant-preview.expanded {
    display: block;
}

.preview-header,
.preview-label {
    margin-bottom: var(--space-2);
    color: var(--text-on-light-muted);
    font-size: 0.71875rem;
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
}

.preview-meta {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-bottom: var(--space-3);
    font-size: var(--font-size-sm);
}

.email-preview-container {
    border: 1px solid var(--border-on-light-hairline);
    border-radius: var(--radius-md);
    background: var(--color-white);
    overflow: auto;
}

.preview-body {
    padding: var(--space-4);
    color: var(--text-on-light);
}

.preview-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

.recipient-info {
    color: var(--text-on-light-muted);
    font-size: var(--font-size-sm);
}

/* A blurred receiver name is a courtesy, not a secret: it is in the markup and
   one click away, and the control says so. */
.hidden-receiver,
.receiver-blurred {
    color: var(--color-red-dark);
    font-weight: var(--font-weight-semibold);
    text-decoration: underline dotted;
    text-underline-offset: 3px;
}

/* ============================================
   PUBLIC WISHLIST
   ============================================ */

.wishlist-header .event-title,
.wishlist-header h1 {
    font-size: var(--display-lg);
}

.wishlist-header .event-details {
    justify-content: center;
}

/*
 * The reader's own wish sits at the top in an editor, marked by a gold top
 * border; everyone else's follow in a grid.
 */
.wish-own {
    margin-bottom: var(--space-8);
    border-top: 4px solid var(--color-gold);
}

.wish-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 22px;
    /*
     * Stretch, not start. Wishes run from one line to a paragraph plus a list
     * plus a link card, so `align-items: start` left every row with a different
     * bottom edge — 339px beside 146px beside 146px in the first row alone —
     * which reads as unfinished rather than as deliberate masonry.
     */
    align-items: stretch;
}

@media (max-width: 1024px) {
    .wish-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
    .wish-grid { grid-template-columns: minmax(0, 1fr); }
}

/* A dashed card closes the grid with the count of people still to write one. */
.wish-outstanding {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 120px;
    padding: var(--space-6);
    border: 1px dashed var(--border-on-dark);
    border-radius: var(--radius-card);
    color: var(--text-on-dark-muted);
    font-size: var(--font-size-sm);
    text-align: center;
}

.wish-body {
    display: flex;
    flex-direction: column;
    /* A paragraph break has to beat the line pitch to read as one. At
       --space-2 the gap between two paragraphs was 8px against a 23px line
       pitch, so consecutive paragraphs ran together and the space before a
       bullet list was the same as the space between two sentences. */
    gap: var(--space-4);
}

.wish-paragraph {
    overflow-wrap: anywhere;
}

.wish-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding-left: var(--space-5);
    list-style: disc;
}

/* A link card is built from the URL text alone. The server never fetches the
   page it points at. */
/*
 * The link card is a card, not a hyperlink.
 *
 * The `text-decoration: none` below is specificity 0-1-0 and loses to
 * `:is(.card, …) a:not(.btn)` at 0-2-1 in 4-components-cards.css, so both the
 * host line and the path line rendered underlined and the one piece of designed
 * chrome in the wish renderer read as a raw link in a box. Naming the ancestor
 * matches that specificity and wins on order.
 */
.card .wish-link-card,
.wish-link-card {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--space-3);
    border: 1px solid var(--border-on-light);
    border-radius: var(--radius-md);
    text-decoration: none;
}

.wish-link-card:hover {
    background: var(--surface-hover);
}

.wish-link-host {
    color: var(--surface-link);
    font-weight: var(--font-weight-semibold);
}

.wish-link-path {
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
    overflow-wrap: anywhere;
}

/* ============================================
   CONFIRM PARTICIPATION
   ============================================ */

.confirmation-layout {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 70vh;
    padding: var(--space-10) var(--space-4);
}

.confirmation-container {
    width: 100%;
    max-width: 560px;
}

.confirmation-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    padding: var(--space-8);
    border-radius: var(--radius-xl);
    background: var(--card-face);
    box-shadow: var(--shadow-modal);
    color: var(--text-on-light);
    text-align: center;
}

/*
 * The status disc.
 *
 * Three things were wrong here, and they are the same three that were wrong
 * with .alert-icon one component over. The base painted WARNING amber, so any
 * state without a modifier said "something went wrong". There was no .error
 * rule at all, though two views emit the class — so the failure state on both
 * pages a participant reaches from an email rendered in amber while
 * --color-danger-tint sat unused. And nothing set `color`, so the glyph, which
 * strokes in currentColor, inherited body ink on every variant.
 *
 * The base is neutral now: a completed action that is neither good news nor bad
 * — declining an invitation — gets that, rather than borrowing the accept
 * state's green and becoming indistinguishable from it.
 */
.confirmation-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    margin: 0 auto;
    border-radius: var(--radius-full);
    background: var(--color-info-tint);
    color: var(--text-on-light-muted);
    font-size: 2rem;
}

.confirmation-icon.success {
    background: var(--color-success-tint);
    color: var(--color-success);
}

.confirmation-icon.error {
    background: var(--color-danger-tint);
    color: var(--surface-danger);
}

.confirmation-icon.warning {
    background: var(--color-warning-tint);
    color: var(--color-gold-ink);
}

.confirmation-message {
    color: var(--text-on-light);
    font-size: var(--font-size-md);
}

/* No vertical margins in here: .confirmation-card is a flex column with a gap,
   and gaps do not collapse with margins — they add, which is how one column
   ended up with 20px, 40px and 60px between successive panels. */
.confirmation-details,
.confirmation-info {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4);
    border-radius: var(--radius-md);
    background: rgba(26, 77, 77, 0.05);
    text-align: left;
}

.confirmation-details strong {
    color: var(--color-gold-ink);
}

.confirmation-footer {
    /* Outside the card, on the teal ground. */
    margin-top: var(--space-6);
    color: var(--text-on-dark-muted);
    font-size: var(--font-size-sm);
    text-align: center;
}

/* ============================================
   ADMIN OVERVIEW

   No artwork: it is an instrument panel.
   ============================================ */

.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-8);
}

.stats-grid .stat-card {
    padding: var(--space-5);
    border: none;
    border-radius: var(--radius-card);
    background: var(--card-face);
    box-shadow: var(--shadow-card);
}

.stat-card-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
}

.section-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

/* A span is inline by default, so width and height would not apply and an
   image inside it at 100% would render at its natural size — which is how a
   26px mark became a 700px santa. */
.section-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
}

.section-icon img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.section-title {
    color: var(--color-cream);
    font-family: var(--font-display);
    font-size: var(--display-sm);
    font-weight: var(--font-weight-normal);
    letter-spacing: var(--tracking-display);
}

.table-card {
    margin-bottom: var(--space-8);
    padding: var(--space-2) 0;
    border-radius: var(--radius-card);
    background: var(--card-face);
    box-shadow: var(--shadow-card);
    color: var(--text-on-light);
    /* The organiser table has six columns; it scrolls inside the card rather
       than pushing the page sideways on a phone. */
    overflow-x: auto;
}

.stats-badge {
    margin-left: auto;
}

.id-col { width: 64px; }

/*
 * ...but not once the table has stacked.
 *
 * The width above is a column width for a real table. In the stacked layout
 * each td becomes its own flex row, so a 64px cap made the ::before label fill
 * the whole cell and pushed the value onto a second line — the ID row was the
 * only one of five that did not read "LABEL … value". The participants table
 * has no fixed-width cell, which is why the pattern looked safe to reuse.
 */
@media (max-width: 799px) {
    .table-stack .id-col {
        width: auto;
    }
}
.email-col { min-width: 200px; }

@media (max-width: 768px) {
    .dashboard-header,
    .page-header-split {
        flex-direction: column;
        align-items: stretch;
    }

    .event-title {
        font-size: var(--display-md);
    }

    .wishlist-header .event-title,
    .wishlist-header h1 {
        font-size: var(--display-md);
    }

    .stats-row {
        flex-wrap: wrap;
    }
}

/* ============================================
   DRAW FAILURE

   The card that replaces "Failed to create pairings". It carries the red left
   edge because the organiser is the one who has to act, and it is the only
   place in the product that lists rules with their source.
   ============================================ */

.draw-failure {
    border-left: 3px solid var(--color-red);
}

.draw-failure-eyebrow {
    color: var(--color-danger);
}

.draw-failure-title {
    margin-bottom: var(--space-2);
    font-family: var(--font-display);
    font-size: var(--display-sm);
    font-weight: var(--font-weight-normal);
    letter-spacing: var(--tracking-display);
}

.draw-failure-diagnosis {
    max-width: 62ch;
    margin-bottom: var(--space-5);
    color: var(--surface-text-muted);
}

/* The rules sit in their own box: they are evidence, not prose. */
.draw-failure-rules {
    margin-bottom: var(--space-5);
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--border-on-light-hairline);
    border-radius: var(--radius-md);
    background: var(--surface-hover);
}

.draw-failure-rules-title {
    margin-bottom: var(--space-3);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
}

.draw-failure-rules ul {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.draw-failure-rules li {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-2);
    font-size: var(--font-size-md);
}

/* Where the rule came from, so the organiser knows which screen to open. */
.draw-failure-source {
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
}

.draw-failure-source::before {
    content: '— ';
}

.draw-failure-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
}

/* A bounced recipient is the row that needs the organiser, so it is findable
   without reading every pill: the same tint the participants table uses for a
   row waiting on someone. */
.participant-row-bounced {
    background: var(--color-danger-tint);
}

.delivery-reason {
    display: block;
    margin-top: var(--space-1);
    color: var(--surface-danger);
    font-size: var(--font-size-sm);
    text-align: right;
}

/* ============================================
   BOUNCE BAR

   On the teal ground, between the stat grid and the body of the admin
   overview. It is the one thing on that page that is about something going
   wrong, so it is the one thing that is red.
   ============================================ */

.bounce-bar {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    margin-bottom: var(--space-6);
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--border-on-dark);
    border-radius: var(--radius-lg);
    background: rgba(158, 44, 26, 0.14);
}

.bounce-bar-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-full);
    background: rgba(255, 196, 190, 0.2);
    color: var(--color-red-pale);
    font-weight: var(--font-weight-bold);
}

.bounce-bar-text {
    flex: 1 1 auto;
    min-width: 0;
}

.bounce-bar-headline {
    margin: 0;
    color: var(--text-on-dark);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
}

.bounce-bar-detail {
    margin: 2px 0 0;
    color: var(--text-on-dark-muted);
    font-size: var(--font-size-sm);
}

/* ============================================
   MANAGE EVENT: BODY SPLIT

   The table is what you work in; the pairings and the exclusion rules are what
   you glance at. Stacked full-width, the rules sat three screens below the
   people they are about.
   ============================================ */

.manage-event-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 356px;
    gap: var(--space-5);
    align-items: start;
}

.manage-event-main,
.manage-event-side {
    min-width: 0;
}

/*
 * The sidebar may only appear once the main column can still hold the table.
 *
 * This collapsed at 1024, so from 1025 up the 356px side rail took its share
 * and handed the main column 617px for a participants table whose min-content
 * is 706px — clipping the row-actions column at every width from 1025 to 1179
 * (1080p at a scale factor, 1152x864, half of a 2560 display). Measured: 1180
 * is the first width where the main column reaches 760px and the table fits,
 * so the rail waits until the container is at its full 1200px width.
 */
@media (max-width: 1199px) {
    .manage-event-body {
        grid-template-columns: minmax(0, 1fr);
    }
}

/*
 * .progress-tracker is a two-column grid (stepper, next-step card), so a third
 * child would take the second column and squeeze the stepper into 288px. The
 * head is a row of its own.
 */
.progress-tracker-head {
    grid-column: 1 / -1;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}

.progress-tracker-title {
    font-family: var(--font-display);
    font-size: var(--display-sm);
    font-weight: var(--font-weight-normal);
    letter-spacing: var(--tracking-display);
    color: var(--surface-text);
}

.progress-tracker-count {
    color: var(--surface-text-muted);
    font-size: var(--font-size-md);
}

.progress-tracker-count strong {
    color: var(--color-gold);
}

/* The pairings list, in the side column. */
.pairings-note {
    margin-bottom: var(--space-4);
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
}

.pairing-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.pairing-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3);
    border-radius: var(--radius-md);
    background: var(--surface-hover);
    font-size: var(--font-size-sm);
}

.pairing-item .pairing-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.exclusion-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--font-size-md);
}

.exclusion-separator {
    display: inline-flex;
    align-items: center;
    padding: 0 var(--space-2);
    color: var(--color-danger);
    vertical-align: middle;
}

/* ============================================
   ROW MENU
   ============================================ */

.row-menu-cell {
    width: 44px;
    text-align: right;
}

.row-menu {
    position: relative;
    display: inline-block;
}

.row-menu-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: none;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--surface-text-muted);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--easing);
}

.row-menu-trigger:hover,
.row-menu-trigger[aria-expanded='true'] {
    background: var(--surface-hover);
    color: var(--surface-text);
}

/*
 * The panel is hidden by the `hidden` attribute, and this rule has to respect
 * it. `display: flex` on a bare class beats the user-agent's
 * `[hidden] { display: none }`, so the only thing keeping these nine panels
 * shut was Bootstrap's reboot re-asserting it with !important — i.e. a CDN this
 * product does not control. Block that request and every row menu on the
 * participants table paints open at once, which is exactly what happened to the
 * first run of the landing-page screenshot capture.
 */
.row-menu-panel[hidden] {
    display: none;
}

.row-menu-panel {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: var(--z-dropdown);
    display: flex;
    flex-direction: column;
    min-width: 180px;
    padding: var(--space-2);
    border: 1px solid var(--border-on-light-hairline);
    border-radius: var(--radius-md);
    background: var(--card-face);
    box-shadow: var(--shadow-modal);
    text-align: left;
}

.row-menu-item {
    display: block;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--surface-text);
    font-family: var(--font-body);
    font-size: var(--font-size-md);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.row-menu-item:hover,
.row-menu-item:focus-visible {
    background: var(--surface-hover);
    text-decoration: none;
}

.row-menu-item-danger {
    color: var(--color-danger);
}

/* A date under a status pill: it may wrap, the pill may not. */
.cell-note {
    display: block;
    margin-top: var(--space-1);
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
}

/* The ask: a labelled panel the reader can decide from, and two answers. */
.confirmation-details {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    /* No bottom margin: .confirmation-card is a flex column with its own gap,
       and a gap does not collapse with a margin — it adds. That is how one
       column ended up with 20px, 40px and 60px between successive panels. */
    margin: 0;
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--border-on-light-hairline);
    border-radius: var(--radius-md);
    background: var(--color-white);
    text-align: left;
}

.confirmation-details > div {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
}

/*
 * Stack the label over its value on a phone.
 *
 * Held as two columns at every width, "Answer by" wrapped to two lines while
 * its value right-aligned and broke, stranding a lone "PM" under
 * "December 5, 2026 · 11:59" against a ragged two-line label. Five rows became
 * nine lines. This page is reached from an email, so most of its readers are
 * on exactly this width.
 */
@media (max-width: 480px) {
    .confirmation-details > div {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-1);
    }

    .confirmation-details dd {
        text-align: left;
    }
}

.confirmation-details dt {
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
}

.confirmation-details dd {
    margin: 0;
    font-weight: var(--font-weight-semibold);
    text-align: right;
}

.confirmation-actions {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: var(--space-3);
}

/*
 * Stacked, they have to agree.
 *
 * flex-wrap drops these onto separate lines below 480px and each keeps its own
 * content width — measured 155px and 163px, centred, so both edges staggered.
 * Two stacked buttons that do not line up read as a mistake.
 */
@media (max-width: 480px) {
    .confirmation-actions {
        flex-direction: column;
        align-items: stretch;
    }
}

.confirmation-reassurance {
    margin-top: var(--space-4);
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
}

.confirmation-next {
    /* Same reason as .confirmation-details: the card's flex gap already
       separates these, and a margin on top of it adds rather than collapses. */
    margin: 0;
    text-align: left;
}

.confirmation-next .btn {
    margin-top: var(--space-4);
}

/* ============================================
   LANGUAGE TABS

   Pills, in the design system. They were Bootstrap nav-tabs restyled by a
   page-level <style> block using six raw Bootstrap greys — the exact shape the
   eleven page stylesheets took, growing back inside a view.
   ============================================ */

.language-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: 0 0 var(--space-5);
    padding: 0;
    list-style: none;
    border: none;
}

.language-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    height: 38px;
    padding: 0 var(--space-4);
    border: 1px solid var(--border-on-light);
    border-radius: var(--radius-full);
    background: transparent;
    color: var(--surface-text-muted);
    font-family: var(--font-body);
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--easing),
                color var(--duration-fast) var(--easing);
}

.language-tab:hover {
    color: var(--surface-text);
}

.language-tab.active {
    border-color: transparent;
    background: var(--color-gold);
    color: var(--color-on-gold);
}

/*
 * .language-code is a gold-tinted chip, which on the gold pill composites to
 * 4.34:1 — under the 4.5 it needs. On the active tab it drops the tint and
 * takes --color-on-gold, the one ink the token file guarantees against gold.
 */
.language-tab.active .language-code {
    background: transparent;
    color: var(--color-on-gold);
}

/* ============================================
   PREVIEW & SEND: LIST AND PREVIEW

   It was eighteen accordions, each holding a full email, so the page was
   eighteen emails tall and no two were ever comparable.
   ============================================ */

.send-layout {
    display: grid;
    /*
     * Wider than the 400px it replaced, which is the whole point.
     *
     * At 400 the list truncated names mid-word and wrapped bounce reasons over
     * three ragged lines. My first attempt at this wrote minmax(340px, 30%),
     * which is WORSE: .container caps at 1200px, so 30% resolves to ~346px and
     * the max governs the track. The percentage can never reach its own
     * minimum inside this container, so the "range" was a fixed 346px and the
     * names truncated at 1440 as well as at 1200.
     */
    grid-template-columns: minmax(420px, 38%) minmax(0, 1fr);
    gap: var(--space-5);
    align-items: start;
}

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

.send-list-card { position: sticky; top: var(--space-5); }

.send-filter {
    display: inline-flex;
    padding: 3px;
    border-radius: var(--radius-full);
    background: rgba(26, 77, 77, 0.06);
}

.send-filter-option {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    height: 30px;
    padding: 0 var(--space-3);
    border: none;
    border-radius: var(--radius-full);
    background: transparent;
    color: var(--surface-text-muted);
    font-family: var(--font-body);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
}

.send-filter-option[aria-pressed='true'] {
    background: var(--color-teal-dark);
    color: var(--text-on-dark);
}

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

.send-list-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-3) var(--space-5);
    border: none;
    border-bottom: 1px solid var(--border-on-light-hairline);
    border-left: 3px solid transparent;
    background: transparent;
    color: inherit;
    font-family: var(--font-body);
    text-align: left;
    cursor: pointer;
}

.send-list-row:hover { background: var(--surface-hover); }

/* The selected row is what ties the list to the panel beside it. */
.send-list-row.is-selected {
    border-left-color: var(--color-red-dark);
    background: var(--color-red-wash);
}

.send-list-row-bounced { background: var(--color-danger-tint); }

.send-list-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.send-list-name { font-weight: var(--font-weight-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.send-list-meta { color: var(--surface-text-muted); font-size: var(--font-size-sm); }
/*
 * The status column takes what it needs and no more. A long bounce reason —
 * "The mail server refused the message" — was expanding it until the name
 * beside it truncated and its meta line wrapped to three.
 */
.send-list-status {
    flex: 0 1 auto;
    max-width: 42%;
    text-align: right;
}
.send-list-empty { padding: var(--space-5); margin: 0; }

.send-preview { min-width: 0; }

.send-preview-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
}

.send-preview-title {
    font-family: var(--font-display);
    font-size: var(--display-sm);
    font-weight: var(--font-weight-normal);
    letter-spacing: var(--tracking-display);
    color: var(--surface-text);
}

.send-preview-meta {
    margin-bottom: var(--space-4);
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
}

/* ============================================
   ADMIN OVERVIEW: BODY

   The tables are the record; the panels beside them are the read on it.
   ============================================ */

.admin-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 380px;
    gap: var(--space-5);
    align-items: start;
}

.admin-main, .admin-side { min-width: 0; }

@media (max-width: 1024px) {
    .admin-body { grid-template-columns: minmax(0, 1fr); }
}

.panel-sub {
    margin-bottom: var(--space-4);
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
}

.funnel-row { margin-bottom: var(--space-4); }
.funnel-row:last-child { margin-bottom: 0; }

.funnel-label {
    margin-bottom: var(--space-2);
    font-size: var(--font-size-md);
}

.funnel-rail {
    height: 6px;
    border-radius: var(--radius-full);
    background: rgba(26, 77, 77, 0.1);
    overflow: hidden;
}

.funnel-fill { height: 100%; border-radius: var(--radius-full); }
.funnel-fill-gold { background: var(--color-gold); }

/* Red marks the stage where the organiser has stopped short of sending. */
.funnel-fill-red { background: var(--color-red-dark); }

.activity-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--font-size-md);
}

.activity-list li {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
}

.activity-dot {
    flex: 0 0 auto;
    width: 6px;
    height: 6px;
    border-radius: var(--radius-full);
    background: var(--surface-text-muted);
}

.activity-dot-event { background: var(--color-success); }
.activity-dot-participant { background: var(--color-gold); }

.activity-when {
    display: block;
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
}

/*
 * The one panel in this column the design keeps on the dark treatment, so the
 * ratings read as a different kind of thing from the operational panels above.
 */
.card-dark {
    padding: var(--space-5) var(--space-6);
    border: 1px solid var(--border-on-dark);
    border-radius: var(--radius-card);
    background: rgba(247, 243, 232, 0.07);
}

.card-dark-title {
    font-family: var(--font-display);
    font-size: var(--display-sm);
    color: var(--text-on-dark);
}

.card-dark-sub {
    margin-bottom: var(--space-4);
    color: var(--text-on-dark-muted);
    font-size: var(--font-size-sm);
}

/*
 * The feedback rows sit on a .card now, not a .card-dark.
 *
 * The panel was the only dark one in a rail of two cream siblings, so it moved
 * to the shared card. Its ink had to move with it: cream-on-dark text and a
 * light gold numeral rendered at ratio 1.01 and 1.55 against the cream face —
 * the label was invisible and the rating nearly so.
 */
.feedback-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-2) 0;
    color: var(--text-on-light-muted);
    font-size: var(--font-size-md);
}

.feedback-row strong {
    color: var(--color-gold-ink);
    font-size: var(--font-size-lg);
}

/* One reminder rule: the switch, what it does, and who is due it today. */
.reminder-rule {
    margin-bottom: var(--space-4);
    padding: var(--space-4);
    border-radius: var(--radius-md);
    background: var(--surface-hover);
}

.reminder-rule-switch {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
}

.reminder-rule-desc {
    margin-top: var(--space-2);
    color: var(--surface-text-muted);
    font-size: var(--font-size-sm);
}

/* The schedule, shown before it runs. */
.reminder-rule-due {
    margin-top: var(--space-1);
    color: var(--surface-text);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
}
