@font-face {
  font-family: 'Archivo';
  src: asset-url("/assets/archivo/Archivo-700-latin-7a918f31.woff2") format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Archivo';
  src: asset-url("/assets/archivo/Archivo-700-latin-ext-4e7e2dd1.woff2") format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Lora';
  src: asset-url("/assets/lora/Lora-Regular-c8978b37.ttf") format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Lora';
  src: asset-url("/assets/lora/Lora-Italic-b47ea162.ttf") format('truetype');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Lora';
  src: asset-url("/assets/lora/Lora-Medium-fafc359f.ttf") format('truetype');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Lora';
  src: asset-url("/assets/lora/Lora-MediumItalic-57058366.ttf") format('truetype');
  font-weight: 500;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Lora';
  src: asset-url("/assets/lora/Lora-SemiBold-f1f8d504.ttf") format('truetype');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Lora';
  src: asset-url("/assets/lora/Lora-SemiBoldItalic-35113b97.ttf") format('truetype');
  font-weight: 600;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Lora';
  src: asset-url("/assets/lora/Lora-Bold-696a1365.ttf") format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Lora';
  src: asset-url("/assets/lora/Lora-BoldItalic-fdc5c9f9.ttf") format('truetype');
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
    /* Apple-style grouped surfaces: a quiet grey ground, white cards, near-black ink */
    :root {
        color-scheme: light;
        --background: 0 0% 100%;
        --foreground: 240 3% 12%;         /* #1D1D1F */
        --card: 0 0% 100%;
        --card-foreground: 240 3% 12%;
        --popover: 0 0% 100%;
        --popover-foreground: 240 3% 12%;
        --primary: 240 3% 12%;
        --primary-foreground: 0 0% 100%;
        --secondary: 240 6% 96%;
        --secondary-foreground: 240 3% 12%;
        --muted: 240 6% 96%;              /* quiet fills */
        --muted-foreground: 240 2% 44%;   /* #6E6E73 */
        --accent: 240 6% 96%;
        --accent-foreground: 240 3% 12%;
        --destructive: 356 100% 42%;      /* #D70015 */
        --destructive-foreground: 0 0% 100%;
        --border: 240 6% 90%;             /* hairlines */
        --input: 240 6% 83%;
        --ring: 211 100% 50%;
        --gold: 38 72% 40%;               /* from the Sanjola mark: "next" and the calendar leaf */
        /* Voice sections, high → low. Validated categorical palette (colour-blind safe on adjacent pairs);
           always shown with the voice name next to it, never colour alone. */
        --att-present: #1baf7a; --att-late: #eda100; --att-excused: #2a78d6; --att-absent: #d94f45;
        --voice-1: #2a78d6; --voice-2: #eb6834; --voice-3: #1baf7a;
        --voice-4: #eda100; --voice-5: #e87ba4; --voice-6: #008300;
        --radius: 0.5rem;
    }

    .dark {
        /* Native widgets — the audio player above all — follow the theme instead of
           sitting as a white pill on a black page */
        color-scheme: dark;
        --background: 0 0% 0%;
        --foreground: 240 11% 96%;
        --card: 240 3% 11%;               /* #1C1C1E */
        --card-foreground: 240 11% 96%;
        --popover: 240 3% 11%;
        --popover-foreground: 240 11% 96%;
        --primary: 240 11% 96%;
        --primary-foreground: 240 3% 11%;
        --secondary: 240 3% 18%;
        --secondary-foreground: 240 11% 96%;
        --muted: 240 3% 18%;              /* #2C2C2E */
        --muted-foreground: 240 3% 60%;   /* #98989D */
        --accent: 240 3% 18%;
        --accent-foreground: 240 11% 96%;
        --destructive: 3 100% 61%;        /* #FF453A */
        --destructive-foreground: 0 0% 100%;
        --border: 240 3% 20%;
        --input: 240 3% 26%;
        --ring: 211 100% 60%;
        --gold: 42 72% 58%;
        --att-present: #24c489; --att-late: #f0b021; --att-excused: #4a94ea; --att-absent: #e4655b;
        --voice-1: #3987e5; --voice-2: #d95926; --voice-3: #199e70;
        --voice-4: #c98500; --voice-5: #d55181; --voice-6: #008300;
    }

    * {
        @apply border-border;
    }

    body {
        @apply bg-background text-foreground font-sans;
        font-feature-settings: "rlig" 1, "calt" 1;
        letter-spacing: -0.006em;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }

    :focus-visible {
        outline: 2px solid hsl(var(--ring));
        outline-offset: 2px;
    }

    @media (prefers-reduced-motion: reduce) {
        *, *::before, *::after {
            animation-duration: 0.01ms !important;
            transition-duration: 0.01ms !important;
        }
    }
}

/* ── Notification shrink animation ── */
@keyframes shrink {
    from { width: 100%; }
    to   { width: 0%; }
}

/* ── Scrollbar hide ── */
/* No scrollbars anywhere: every scrolling surface in the app keeps its gesture and
   loses its gutter, so nothing shifts when a list grows past its box. */
*, .no-scrollbar {
    -ms-overflow-style: none;
    scrollbar-width: none;
}
*::-webkit-scrollbar, .no-scrollbar::-webkit-scrollbar { display: none; }

@layer components {
    /* ── Page typography ── */
    /* Large title, set like the heading of a hymnal */
    .page-title {
        font-family: 'Lora', Georgia, serif;
        font-size: 2rem;
        font-weight: 600;
        letter-spacing: -0.02em;
        line-height: 1.15;
    }
    @media (min-width: 768px) {
        .page-title { font-size: 2.5rem; }
    }

    .page-eyebrow {
        font-size: 0.8125rem;
        font-weight: 500;
        color: hsl(var(--muted-foreground));
    }

    /* A quiet marker for a section: a coloured dot and a plain word */
    /* A quiet label above a section or inside a card. No marker: the weight carries it. */
    .section-dot {
        display: block;
        font-size: 0.8125rem;
        font-weight: 500;
        color: hsl(var(--muted-foreground));
    }

    .section-title {
        font-size: 1.125rem;
        font-weight: 600;
        letter-spacing: -0.01em;
        color: hsl(var(--foreground));
    }

    .back-link {
        display: inline-flex;
        align-items: center;
        gap: 0.375rem;
        font-size: 0.875rem;
        color: hsl(var(--muted-foreground));
        transition: color 150ms;
    }
    .back-link:hover { color: hsl(var(--foreground)); }

    /* ── Rows ── */
    .row-link {
        display: flex;
        align-items: center;
        gap: 1rem;
        padding: 1rem 1.25rem;
        background: hsl(var(--card));
        transition: background-color 120ms ease;
        cursor: pointer;
    }
    .row-link:hover { background: hsl(var(--accent) / 0.5); }

    /* ── Forms ── */
    .field-label {
        display: block;
        font-size: 0.875rem;
        font-weight: 600;
        color: hsl(var(--foreground));
    }

    .field-hint {
        font-size: 0.8125rem;
        line-height: 1.45;
        color: hsl(var(--muted-foreground));
    }

    .field-input {
        display: flex;
        width: 100%;
        border-radius: 0.5rem;
        border: 1px solid hsl(var(--input));
        background: hsl(var(--card));
        padding: 0.5625rem 0.75rem;
        font-size: 1rem;
        font-family: inherit;
        color: hsl(var(--foreground));
        transition: border-color 150ms, box-shadow 150ms;
        outline: none;
        line-height: 1.5;
    }
    .field-input::placeholder { color: hsl(var(--muted-foreground) / 0.6); }
    .field-input:focus {
        border-color: hsl(var(--ring));
        box-shadow: 0 0 0 3px hsl(var(--ring) / 0.15);
    }
    .field-input-sm { padding: 0.375rem 0.625rem; font-size: 0.875rem; }
    .field-input-lg { padding: 0.75rem 1rem; font-size: 1.0625rem; }

    .field-select {
        appearance: none;
        cursor: pointer;
        padding-right: 2rem;
        text-overflow: ellipsis;
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
        background-repeat: no-repeat;
        background-position: right 0.625rem center;
    }
    .field-select.is-set { border-color: hsl(var(--foreground) / 0.45); }

    .file-input {
        display: block;
        width: 100%;
        font-size: 0.875rem;
        color: hsl(var(--muted-foreground));
    }
    .file-input::file-selector-button {
        margin-right: 0.875rem;
        padding: 0.5rem 1rem;
        border-radius: 0.5rem;
        border: 1px solid hsl(var(--border));
        background: hsl(var(--background));
        color: hsl(var(--foreground));
        font-family: inherit;
        font-size: 0.875rem;
        font-weight: 500;
        cursor: pointer;
        transition: background-color 150ms;
    }
    .file-input::file-selector-button:hover { background: hsl(var(--accent)); }

    .form-footer {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1rem;
        padding-top: 1.5rem;
        border-top: 1px solid hsl(var(--border) / 0.6);
    }

    /* Long forms keep Save within thumb reach, above the mobile tab bar */
    .form-footer.is-sticky {
        position: sticky;
        bottom: calc(env(safe-area-inset-bottom, 0px) + 5.5rem);
        z-index: 30;
        margin-inline: -1.25rem;
        padding: 0.875rem 1.25rem;
        background: hsl(var(--background) / 0.9);
        -webkit-backdrop-filter: blur(12px);
        backdrop-filter: blur(12px);
    }
    @media (min-width: 768px) {
        .form-footer.is-sticky { bottom: 0; }
    }

    /* ── Filter chips: a native <select> laid over a chip, so phones open their own picker ── */
    .chip-row {
        display: flex;
        gap: 0.5rem;
        overflow-x: auto;
        margin-inline: -1.25rem;
        padding: 0.125rem 1.25rem;
        scrollbar-width: none;
    }
    .chip-row::-webkit-scrollbar { display: none; }
    @media (min-width: 1024px) {
        .chip-row.lg-inline { margin-inline: 0; padding-inline: 0; }
    }

    .filter-chip {
        position: relative;
        display: inline-flex;
        flex-shrink: 0;
        align-items: center;
        gap: 0.375rem;
        height: 2.125rem;
        padding: 0 0.75rem;
        border-radius: 0.5rem;
        border: 1px solid hsl(var(--border));
        background: hsl(var(--card));
        font-size: 0.875rem;
        color: hsl(var(--foreground) / 0.85);
        white-space: nowrap;
        transition: background-color 120ms, color 120ms, border-color 120ms;
    }
    .filter-chip:hover { background: hsl(var(--accent) / 0.6); }
    .filter-chip:focus-within { box-shadow: 0 0 0 3px hsl(var(--ring) / 0.35); }
    .filter-chip.is-set {
        background: hsl(var(--foreground));
        border-color: hsl(var(--foreground));
        color: hsl(var(--background));
    }
    .filter-chip-select {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        opacity: 0;
        cursor: pointer;
        font-size: 16px; /* no zoom-on-focus on iOS */
    }

    /* The invitation panel opens when it is wanted, and stays out of the way otherwise */
    .invite-panel > summary::-webkit-details-marker { display: none; }
    .invite-panel > summary:hover { background: hsl(var(--accent) / 0.35); }
    .invite-panel[open] .invite-caret { transform: rotate(180deg); }
    .invite-caret { transition: transform 150ms; }

    /* Native disclosure: no JavaScript, keyboard and screen readers get it for free.
       Everything starts closed; the caret is a lucide chevron that turns as it opens. */
    .disclosure > summary,
    .disclosure-group > summary {
        cursor: pointer;
        list-style: none;
        user-select: none;
    }
    .disclosure > summary::-webkit-details-marker,
    .disclosure-group > summary::-webkit-details-marker { display: none; }
    .disclosure-caret { transition: transform 150ms ease; opacity: 0.55; }
    .disclosure[open] > summary .disclosure-caret,
    .disclosure-group[open] > summary .disclosure-caret { transform: rotate(90deg); opacity: 1; }

    /* The section's own line: quiet, like every other eyebrow on the page */
    .disclosure-head {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        padding: 0.125rem 0;
        border-radius: 0.5rem;
    }
    .disclosure-head:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 3px; }

    /* One audience per row, inside the card: name left, what it holds right */
    .disclosure-row {
        display: flex;
        align-items: center;
        gap: 0.625rem;
        padding: 0.8125rem 1rem;
        transition: background-color 120ms;
    }
    .disclosure-row:hover { background: hsl(var(--accent) / 0.4); }
    .disclosure-row:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }
    .disclosure-group[open] > .disclosure-row { background: hsl(var(--accent) / 0.3); }
    .disclosure-name {
        flex: 1;
        min-width: 0;
        font-size: 0.9375rem;
        color: hsl(var(--foreground));
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .disclosure-count {
        font-size: 0.75rem;
        font-variant-numeric: tabular-nums;
        color: hsl(var(--muted-foreground));
        min-width: 1.25rem;
        text-align: right;
    }

    /* Open rows sit on a slightly recessed ground, so the nesting reads without indentation */
    .disclosure-body {
        border-top: 1px solid hsl(var(--border) / 0.7);
        background: hsl(var(--muted) / 0.35);
    }
    .disclosure-body > * + * { border-top: 1px solid hsl(var(--border) / 0.6); }

    .list-scroll {
        max-height: min(70vh, 46rem);
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    [data-nested-row][hidden] { display: none; }

    /* ── Choice chips (multi-select checkboxes) ── */
    .chip { display: inline-flex; cursor: pointer; }
    .chip[hidden] { display: none; }
    .chip-face {
        display: inline-flex;
        align-items: center;
        border: 1px solid hsl(var(--border));
        border-radius: 0.5rem;
        padding: 0.3125rem 0.8125rem;
        font-size: 0.875rem;
        line-height: 1.25rem;
        color: hsl(var(--foreground) / 0.8);
        background: hsl(var(--card));
        user-select: none;
        transition: background-color 120ms, color 120ms, border-color 120ms;
    }
    .chip:hover .chip-face { color: hsl(var(--foreground)); border-color: hsl(var(--foreground) / 0.35); }
    /* A chip face used on its own as a link (public profile: the choir's social links) */
    a.chip-face { gap: 0.375rem; }
    a.chip-face:hover { color: hsl(var(--foreground)); border-color: hsl(var(--foreground) / 0.35); }
    .peer:checked + .chip-face {
        background: hsl(var(--foreground));
        border-color: hsl(var(--foreground));
        color: hsl(var(--background));
    }
    .peer:focus-visible + .chip-face { box-shadow: 0 0 0 3px hsl(var(--ring) / 0.4); }

    /* ── Attendance charts: plain bars, one measure, no decoration ── */
    .chart-scroll {
        overflow-x: auto;
        scrollbar-width: none;
        margin-inline: -0.25rem;
        padding: 0 0.25rem 0.25rem;
    }
    .chart-scroll::-webkit-scrollbar { display: none; }
    .chart-columns { display: flex; align-items: flex-end; gap: 0.375rem; min-height: 11rem; }
    .chart-column {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 0.375rem;
        flex: 0 0 2.25rem;
        text-decoration: none;
    }
    .chart-track {
        display: flex;
        align-items: flex-end;
        width: 100%;
        height: 8rem;
        border-radius: 0.25rem;
        background: hsl(var(--secondary) / 0.8);
        overflow: hidden;
    }
    .chart-bar {
        display: block;
        width: 100%;
        border-radius: 0.25rem;
        background: var(--att-present);
        transition: background-color 120ms;
    }
    .chart-column:hover .chart-bar { filter: brightness(0.92); }
    .chart-value { font-size: 0.6875rem; font-variant-numeric: tabular-nums; color: hsl(var(--muted-foreground)); }
    .chart-tick { font-size: 0.625rem; color: hsl(var(--muted-foreground)); white-space: nowrap; }

    .chart-seg {
        position: relative;
        flex: 1 1 0;
        min-width: 0.5rem;
        height: 100%;
        border-radius: 0.1875rem;
        background: hsl(var(--foreground) / 0.06);
    }

    /* A year of rolls, one square per day */
    .activity-grid { display: flex; gap: 3px; padding-bottom: 0.25rem; }
    .activity-months { display: flex; gap: 3px; }
    .activity-month {
        width: 0.75rem;
        font-size: 0.625rem;
        line-height: 1;
        color: hsl(var(--muted-foreground));
        white-space: nowrap;
        overflow: visible;
    }
    .activity-week { display: grid; grid-template-rows: repeat(7, 0.75rem); gap: 3px; }
    .activity-cell {
        position: relative;
        width: 0.75rem;
        height: 0.75rem;
        border-radius: 0.1875rem;
        background: hsl(var(--foreground) / 0.06);
    }
    .activity-cell.is-outside { background: none; }
    .activity-cell:hover { outline: 1px solid hsl(var(--foreground) / 0.35); outline-offset: 1px; }
    .activity-cell.is-outside:hover { outline: none; }

    /* One colour per state: every present cell reads the same, on every row */
    .att-present { background: var(--att-present); }
    .att-late { background: var(--att-late); }
    .att-excused { background: var(--att-excused); }
    .att-absent { background: var(--att-absent); }
    .att-blank {
        background: repeating-linear-gradient(45deg, transparent, transparent 3px,
                    hsl(var(--foreground) / 0.10) 3px, hsl(var(--foreground) / 0.10) 4px);
    }

    .chart-key { width: 0.75rem; height: 0.75rem; border-radius: 0.1875rem; display: inline-block; }

    /* Hover tells you which event it is — no click needed */
    .chart-tip {
        position: absolute;
        bottom: calc(100% + 0.5rem);
        left: 50%;
        transform: translateX(-50%) translateY(0.25rem);
        z-index: 30;
        max-width: 16rem;
        width: max-content;
        padding: 0.375rem 0.625rem;
        border-radius: 0.375rem;
        background: hsl(var(--foreground));
        color: hsl(var(--background));
        font-size: 0.75rem;
        line-height: 1.25;
        white-space: normal;
        opacity: 0;
        pointer-events: none;
        transition: opacity 120ms, transform 120ms;
    }
    .chart-seg:hover .chart-tip,
    .chart-column:hover .chart-tip,
    .chart-cell:hover .chart-tip,
    .chart-seg:focus-visible .chart-tip {
        opacity: 1;
        transform: translateX(-50%) translateY(0);
    }
    .chart-column { position: relative; }

    .chart-row { display: flex; align-items: center; gap: 0.75rem; }
    .chart-row-name { flex: 0 0 9rem; font-size: 0.8125rem; color: hsl(var(--foreground)); }
    .chart-row-track {
        display: flex;
        gap: 2px;
        flex: 1;
        height: 1.125rem;
        overflow: visible;
    }
    .chart-row-track .chart-bar { height: 100%; }
    .chart-row-value { flex: 0 0 2.75rem; text-align: right; font-size: 0.8125rem; color: hsl(var(--muted-foreground)); }
    @media (max-width: 640px) {
        .chart-row-name { flex-basis: 6.5rem; }
    }

    /* ── Roll call: one segmented control per member, on a single line ── */
    .roll-track {
        display: flex;
        overflow-x: auto;
        scrollbar-width: none;
        margin-inline: -0.25rem;
        padding: 0.125rem 0.25rem;
    }
    .roll-track::-webkit-scrollbar { display: none; }
    .roll-options {
        display: inline-flex;
        flex-shrink: 0;
        align-items: center;
        gap: 0.125rem;
        padding: 0.125rem;
        border: 1px solid hsl(var(--border));
        border-radius: 0.5rem;
        background: hsl(var(--secondary) / 0.7);
    }
    .roll-options label { display: inline-flex; }
    .roll-option {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        padding: 0.25rem 0.625rem;
        border-radius: 0.375rem;
        font-size: 0.8125rem;
        line-height: 1.25rem;
        white-space: nowrap;
        color: hsl(var(--muted-foreground));
        cursor: pointer;
        user-select: none;
        transition: background-color 120ms, color 120ms;
    }
    .roll-option:hover { color: hsl(var(--foreground)); }
    .peer:checked + .roll-option {
        background: hsl(var(--card));
        color: hsl(var(--foreground));
        box-shadow: 0 1px 2px hsl(var(--foreground) / 0.08);
    }
    .peer:checked + .roll-option.is-clear { background: none; box-shadow: none; color: hsl(var(--muted-foreground)); }
    .peer:focus-visible + .roll-option { box-shadow: 0 0 0 3px hsl(var(--ring) / 0.4); }

    /* ── Buttons ── */
    .btn-primary, .btn-outline, .btn-ghost, .btn-danger {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 0.5rem;
        padding: 0.5rem 1rem;
        border-radius: 0.5rem;
        border: 1px solid transparent;
        font-size: 0.9375rem;
        font-weight: 500;
        line-height: 1.25rem;
        white-space: nowrap;
        cursor: pointer;
        transition: background-color 150ms, color 150ms, border-color 150ms;
    }
    .btn-primary { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); font-weight: 500; }
    .btn-primary:hover { background: hsl(var(--primary) / 0.88); }
    .btn-outline { border-color: hsl(var(--border)); background: hsl(var(--card)); color: hsl(var(--foreground)); }
    .btn-outline:hover { background: hsl(var(--accent) / 0.6); }
    .btn-ghost { color: hsl(var(--muted-foreground)); padding-left: 0.25rem; padding-right: 0.25rem; }
    .btn-ghost:hover { color: hsl(var(--foreground)); }
    .btn-danger { border-color: hsl(var(--destructive) / 0.4); color: hsl(var(--destructive)); background: transparent; }
    .btn-danger:hover { background: hsl(var(--destructive)); border-color: hsl(var(--destructive)); color: hsl(var(--destructive-foreground)); }
    .btn-block { width: 100%; padding-top: 0.75rem; padding-bottom: 0.75rem; }
    .btn-ghost.btn-block { padding-left: 1.125rem; padding-right: 1.125rem; }

    .icon-button {
        height: 2.25rem;
        width: 2.25rem;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border-radius: 0.5rem;
        color: hsl(var(--muted-foreground));
        transition: background-color 150ms, color 150ms;
    }
    .icon-button:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
    .icon-button.is-danger:hover { background: hsl(var(--destructive) / 0.1); color: hsl(var(--destructive)); }

    .danger-zone {
        display: flex;
        flex-direction: column;
        gap: 1rem;
        padding: 1.25rem;
        border-radius: 0.5rem;
        border: 1px solid hsl(var(--destructive) / 0.25);
    }
    @media (min-width: 640px) {
        .danger-zone { flex-direction: row; align-items: center; justify-content: space-between; }
    }

    .divider {
        display: flex;
        align-items: center;
        gap: 0.75rem;
        font-size: 0.8125rem;
        color: hsl(var(--muted-foreground));
    }
    .divider::before, .divider::after {
        content: "";
        flex: 1;
        height: 1px;
        background: hsl(var(--border));
    }

    /* ── Badges & pills ── */
    .badge-muted, .badge-outline, .badge-solid {
        display: inline-flex;
        align-items: center;
        border-radius: 0.5rem;
        padding: 0.125rem 0.5rem;
        font-size: 0.6875rem;
        font-weight: 500;
        line-height: 1rem;
        white-space: nowrap;
    }
    .badge-muted { background: hsl(var(--muted)); color: hsl(var(--muted-foreground)); }
    .badge-outline { border: 1px solid hsl(var(--border)); color: hsl(var(--muted-foreground)); }
    .badge-solid { background: hsl(var(--foreground)); color: hsl(var(--background)); min-width: 1.125rem; justify-content: center; padding: 0 0.3125rem; }

    .filter-pill {
        display: inline-flex;
        align-items: center;
        gap: 0.375rem;
        border-radius: 0.5rem;
        border: 1px solid hsl(var(--foreground) / 0.25);
        padding: 0.25rem 0.625rem;
        font-size: 0.8125rem;
        color: hsl(var(--foreground));
        transition: background-color 120ms;
    }
    .filter-pill:hover { background: hsl(var(--accent)); }

    .tag-link {
        display: inline-flex;
        align-items: center;
        border-radius: 0.5rem;
        border: 1px solid hsl(var(--border));
        padding: 0.25rem 0.75rem;
        font-size: 0.875rem;
        color: hsl(var(--foreground) / 0.85);
        transition: background-color 120ms, border-color 120ms;
    }
    .tag-link:hover { background: hsl(var(--accent) / 0.6); border-color: hsl(var(--foreground) / 0.3); }

    .detail-link {
        text-decoration: underline;
        text-decoration-color: hsl(var(--border));
        text-underline-offset: 4px;
        transition: text-decoration-color 150ms;
    }
    .detail-link:hover { text-decoration-color: hsl(var(--foreground)); }

    /* ── Voices ── */
    .voice-badge {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 1.75rem;
        height: 1.5rem;
        padding: 0 0.375rem;
        border-radius: 0.5rem;
        border: 1px solid hsl(var(--border));
        font-size: 0.6875rem;
        font-weight: 600;
        letter-spacing: 0.02em;
        color: hsl(var(--foreground) / 0.8);
    }

    /* The key sits under the bar, each item as wide as its own segment, so a section's
       name is centred on the colour it belongs to. Too narrow to read on a phone, so
       there it falls back to a plain wrapping list. */
    .voice-keys { display: flex; flex-wrap: wrap; gap: 0.25rem 1.25rem; }
    @media (min-width: 640px) {
        .voice-keys { flex-wrap: nowrap; gap: 2px; }
        .voice-keys > .voice-key { flex-basis: 0; justify-content: center; }
    }

    .voice-key {
        display: inline-flex;
        align-items: center;
        justify-content: flex-start;
        gap: 0.4375rem;
        flex: 0 1 auto;
        min-width: 0;
        padding-top: 0.375rem;
        font-size: 0.8125rem;
        line-height: 1.25rem;
        color: hsl(var(--muted-foreground));
        transition: color 120ms;
    }
    .voice-key:hover { color: hsl(var(--foreground)); }
    .voice-key.is-active { color: hsl(var(--foreground)); font-weight: 500; }
    .voice-key.is-active .voice-key-dot { box-shadow: 0 0 0 3px hsl(var(--foreground) / 0.12); }
    .voice-key-dot { width: 0.5rem; height: 0.5rem; border-radius: 9999px; flex-shrink: 0; }
    .voice-key-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .voice-key-count { font-variant-numeric: tabular-nums; font-weight: 600; color: hsl(var(--foreground)); }

    /* The section's own colour, carried by its name rather than a dot */
    .voice-name-0 { color: var(--voice-1); }
    .voice-name-1 { color: var(--voice-2); }
    .voice-name-2 { color: var(--voice-3); }
    .voice-name-3 { color: var(--voice-4); }
    .voice-name-4 { color: var(--voice-5); }
    .voice-name-5 { color: var(--voice-6); }

    .voice-tone-0 { background: var(--voice-1); }
    .voice-tone-1 { background: var(--voice-2); }
    .voice-tone-2 { background: var(--voice-3); }
    .voice-tone-3 { background: var(--voice-4); }
    .voice-tone-4 { background: var(--voice-5); }
    .voice-tone-5 { background: var(--voice-6); }

    /* Stacked section bar: 2px surface gaps between segments, 4px rounded ends */
    .voice-bar { display: flex; gap: 2px; height: 0.625rem; }
    .voice-bar > span { border-radius: 2px; min-width: 4px; }
    .voice-bar > span:first-child { border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
    .voice-bar > span:last-child { border-top-right-radius: 4px; border-bottom-right-radius: 4px; }

    /* ── Grouped form rows: label left, value right, hairlines between ── */
    .form-card {
        border-radius: 0.5rem;
        border: 1px solid hsl(var(--border) / 0.8);
        background: hsl(var(--card));
        overflow: hidden;
    }
    .form-card > * + * { border-top: 1px solid hsl(var(--border) / 0.7); }

    .form-row {
        display: flex;
        align-items: center;
        gap: 0.75rem;
        min-height: 3rem;
        padding: 0.5rem 1rem;
    }
    .form-row:focus-within { background: hsl(var(--accent) / 0.35); }
    .row-label {
        width: 6rem;
        flex-shrink: 0;
        font-size: 0.9375rem;
        color: hsl(var(--foreground));
    }
    @media (min-width: 640px) { .row-label { width: 8rem; } }

    .row-input {
        flex: 1;
        min-width: 0;
        border: 0;
        background: transparent;
        padding: 0.25rem 0;
        font-family: inherit;
        font-size: 0.9375rem;
        color: hsl(var(--foreground));
        text-align: right;
        outline: none;
    }
    .row-input::placeholder { color: hsl(var(--muted-foreground) / 0.7); }
    .row-input.is-title { text-align: left; font-size: 1.0625rem; font-weight: 600; }
    .row-input.is-select {
        appearance: none;
        cursor: pointer;
        text-align: right;
        direction: rtl;
        padding-right: 1.125rem;
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
        background-repeat: no-repeat;
        background-position: right center;
    }
    .row-input.is-select > option { direction: ltr; }
    .row-stacked { display: block; padding: 0.75rem 1rem; }
    .row-stacked .row-input { text-align: left; }

    /* ── Segmented control (iOS) ── */
    .segmented {
        display: inline-grid;
        grid-auto-flow: column;
        grid-auto-columns: 1fr;
        gap: 0.125rem;
        padding: 0.1875rem;
        border-radius: 0.5rem;
        background: hsl(var(--muted));
    }
    .segmented > a {
        padding: 0.375rem 1rem;
        border-radius: 0.375rem;
        font-size: 0.8125rem;
        font-weight: 500;
        text-align: center;
        white-space: nowrap;
        color: hsl(var(--foreground) / 0.7);
        transition: background-color 150ms, color 150ms;
    }
    .segmented > a:hover { color: hsl(var(--foreground)); }
    .segmented > a[aria-current="page"] {
        background: hsl(var(--card));
        color: hsl(var(--foreground));
        box-shadow: 0 1px 2px rgb(0 0 0 / 0.08), 0 0 0 0.5px rgb(0 0 0 / 0.04);
    }

    /* ── Calendar leaf: the date of an event, like a page torn from a liturgical calendar ── */
    .date-leaf {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 0.125rem;
        width: 3.5rem;
        height: 3.75rem;
        flex-shrink: 0;
        border-radius: 0.5rem;
        background: hsl(var(--card));
        border: 1px solid hsl(var(--border));
    }
    .date-leaf-month { font-size: 0.625rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: hsl(var(--gold)); line-height: 1; }
    .date-leaf-day { font-family: 'Lora', Georgia, serif; font-size: 1.5rem; font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; color: hsl(var(--foreground)); }
    .date-leaf-weekday { font-size: 0.625rem; color: hsl(var(--muted-foreground)); line-height: 1; }
    .date-leaf.is-lg { width: 4.5rem; height: 5rem; }
    .date-leaf.is-lg .date-leaf-day { font-size: 2rem; }
    .date-leaf.is-lg .date-leaf-month, .date-leaf.is-lg .date-leaf-weekday { font-size: 0.6875rem; }

    /* ── Event status ── */
    .status-badge {
        font-size: 0.75rem;
        font-weight: 500;
        line-height: 1.25rem;
        white-space: nowrap;
        color: hsl(var(--muted-foreground));
    }
    .status-next { color: hsl(var(--gold)); font-weight: 600; }
    .status-cancelled { color: hsl(var(--destructive) / 0.85); }

    /* ── Cards ── */
    .event-card {
        display: flex;
        gap: 1rem;
        padding: 1rem;
        border-radius: 0.5rem;
        background: hsl(var(--card));
        border: 1px solid hsl(var(--border) / 0.8);
        transition: background-color 120ms, box-shadow 150ms;
    }
    .event-card:hover { box-shadow: 0 2px 10px rgb(0 0 0 / 0.06); }

    /* Public pages share the signed-in canvas, so both sides of the app line up */
    .public-shell {
        max-width: 72rem;
        margin-inline: auto;
        padding-inline: 1.25rem;
    }
    @media (min-width: 1536px) {
        .public-shell { max-width: 80rem; }
    }

    /* The calendar as a board: one column per month. The whole board scrolls in both
       directions — down through a month, across to the next — and each month name stays
       pinned while its own column is in view. Columns keep their width whether there is
       one month or twelve. */
    .month-board {
        display: flex;
        align-items: flex-start;
        gap: 1rem;
        max-height: min(72vh, 52rem);
        overflow: auto;
        overscroll-behavior: contain;
        scroll-snap-type: x proximity;
        margin-inline: -1.25rem;
        padding: 0 1.25rem 0.5rem;
    }
    .month-board > * {
        flex: 0 0 min(23rem, 86vw);
        scroll-snap-align: start;
    }
    @media (min-width: 768px) {
        .month-board { margin-inline: 0; padding-inline: 0.125rem; }
    }
    .month-head {
        position: sticky;
        top: 0;
        z-index: 1;
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 1rem;
        padding: 0.375rem 0 0.5rem;
        background: hsl(var(--background));
    }
    .month-column { display: grid; gap: 0.75rem; align-content: start; }

    .event-card.is-cancelled .event-card-title { text-decoration: line-through; color: hsl(var(--muted-foreground)); }

    .program { counter-reset: program; }
    .program > [data-nested-row]:not([hidden]), .program > .program-row { counter-increment: program; }
    .program-number::before {
        content: counter(program);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 1.5rem;
        height: 1.5rem;
        border-radius: 9999px;
        background: hsl(var(--muted));
        font-size: 0.75rem;
        font-weight: 600;
        font-variant-numeric: tabular-nums;
        color: hsl(var(--foreground) / 0.75);
    }

    /* ── Price table ──────────────────────────────────────────────────────────
       One markup, two homes: the landing and a choir's settings. Both prices are
       in the page and the `plan-period` Stimulus controller shows one, so the
       monthly/yearly switch never costs a request. */
    .plan-switch { display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 0.125rem; padding: 0.1875rem; border-radius: 0.5rem; background: hsl(var(--muted)); }
    .plan-switch > button {
        padding: 0.4375rem 1.125rem; border-radius: 0.375rem; font-size: 0.8125rem; font-weight: 500;
        white-space: nowrap; color: hsl(var(--foreground) / 0.7); transition: background-color 150ms, color 150ms;
    }
    .plan-switch > button:hover { color: hsl(var(--foreground)); }
    .plan-switch > button[aria-current="page"] {
        background: hsl(var(--card)); color: hsl(var(--foreground));
        box-shadow: 0 1px 2px rgb(0 0 0 / 0.08), 0 0 0 0.5px rgb(0 0 0 / 0.04);
    }

    .plan-grid { display: grid; gap: 1rem; align-items: stretch; }
    @media (min-width: 680px) { .plan-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
    @media (min-width: 1100px) { .plan-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

    .plan-card {
        display: grid; grid-template-rows: auto auto auto 1fr auto; gap: 1rem; padding: 1.5rem;
        border: 1px solid hsl(var(--border)); border-radius: var(--radius); background: hsl(var(--card));
    }
    .plan-card.is-featured { border-color: hsl(var(--gold) / 0.65); box-shadow: 0 0 0 1px hsl(var(--gold) / 0.35); }
    .plan-card.is-current { border-color: hsl(var(--foreground)); }
    .plan-name { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
    .plan-card.is-featured .plan-name { color: hsl(var(--gold)); }
    .plan-tagline { font-size: 0.875rem; line-height: 1.55; color: hsl(var(--muted-foreground)); }
    @media (min-width: 680px) { .plan-tagline { min-height: 2.7em; } }
    .plan-price { font-size: 1.75rem; font-weight: 600; letter-spacing: -0.025em; color: hsl(var(--foreground)); white-space: nowrap; }
    .plan-per { font-size: 0.8125rem; font-weight: 400; color: hsl(var(--muted-foreground)); }
    .plan-saving { font-size: 0.8125rem; font-weight: 500; color: hsl(var(--gold)); margin-top: 0.1875rem; }
    .plan-features { display: grid; align-content: start; border-top: 1px solid hsl(var(--border)); }
    .plan-feature { padding: 0.5625rem 0; font-size: 0.875rem; line-height: 1.5; color: hsl(var(--foreground) / 0.88); }
    .plan-feature + .plan-feature { border-top: 1px solid hsl(var(--border) / 0.7); }
    .plan-foot { font-size: 0.8125rem; font-weight: 600; color: hsl(var(--foreground)); }

    /* ── Landing ──────────────────────────────────────────────────────────────
       The landing is the only page allowed its own skin, because it has to
       welcome people who have never seen the app. It keeps the app's ground
       (--background) and its type rule (system sans, Lora italic for titles),
       and adds three warm hues taken from the photographs themselves —
       --lp-flame, --lp-leaf, --lp-berry — alongside the brand's --gold.
       The product's own data palette (--voice-1…6, --att-*) is untouched.

       The lattice is a whisper: one 88px module at 2% ink, pinned to
       calc(50% - 528px) so its lines fall on .lp-shell's rails
       (66rem = 12 modules). It is texture, not structure. */
    body.landing { background: hsl(var(--background)); }
    /* The app's dark theme is true black, which makes a whole marketing page feel
       like a hole. On the landing only, lift the surfaces to a soft charcoal. The
       tokens are redefined on <body>, so the shared navbar follows along. */
    .dark body.landing, .dark .landing-page {
        --background: 240 6% 8%;
        --card: 240 6% 12%;
        --muted: 240 6% 14%;
        --border: 240 5% 20%;
        --input: 240 5% 26%;
    }

    .landing-page {
        --lp-cell: 96px;
        --lp-flame: 14 72% 48%;     /* the orange choir shirts, the congas */
        --lp-leaf: 164 70% 26%;     /* the green drape behind a choir */
        --lp-berry: 344 55% 36%;    /* the maroon caps and wax print */
        overflow-x: clip;
        background-color: hsl(var(--background));
        /* A true 12px grid, ruled the same in both directions and kept faint
           enough to read as paper rather than as lines. 12 divides the shell
           (84rem = 112 columns), the wide shell (96rem = 128) and the 672px
           origin, so every column rule lands on the shell's own edges. */
        background-image:
            linear-gradient(to right, hsl(var(--foreground) / 0.026) 0 1px, transparent 1px),
            linear-gradient(to bottom, hsl(var(--foreground) / 0.026) 0 1px, transparent 1px);
        background-size: calc(var(--lp-cell) / 8) calc(var(--lp-cell) / 8);
        background-position: calc(50% - 672px) 0;
    }
    .dark .landing-page { background-image:
            linear-gradient(to right, hsl(var(--foreground) / 0.035) 0 1px, transparent 1px),
            linear-gradient(to bottom, hsl(var(--foreground) / 0.035) 0 1px, transparent 1px); }

    /* 84rem = 1344px = fourteen modules, so the lattice lines fall on the edges */
    .lp-shell { max-width: 84rem; margin-inline: auto; padding-inline: 1.25rem; }
    @media (min-width: 720px) { .lp-shell { padding-inline: 3rem; } }
    @media (min-width: 1600px) { .lp-shell { max-width: 96rem; padding-inline: 4rem; } }
    .lp-narrow { max-width: 48rem; }

    /* ── Type ── */
    .lp-eyebrow {
        font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.14em;
        text-transform: uppercase; color: hsl(var(--muted-foreground));
    }
    /* Archivo, bold and upright. Lora and italics stay inside the app; the
       landing speaks in its own, plainer voice. */
    .lp-display, .lp-head {
        font-family: 'Archivo', system-ui, sans-serif; font-weight: 700; font-style: normal;
        color: hsl(var(--foreground)); text-wrap: balance;
    }
    .lp-display { font-size: clamp(2.375rem, 5.6vw, 4rem); line-height: 1.02; letter-spacing: -0.035em; }
    .lp-head { font-size: clamp(1.75rem, 3.4vw, 2.5rem); line-height: 1.1; letter-spacing: -0.028em; }
    .lp-lead { font-size: clamp(1.0625rem, 1.6vw, 1.1875rem); line-height: 1.6; color: hsl(var(--muted-foreground)); text-wrap: pretty; }
    .lp-body { font-size: 1.0625rem; line-height: 1.65; color: hsl(var(--muted-foreground)); text-wrap: pretty; }
    .lp-note { font-size: 0.8125rem; line-height: 1.6; color: hsl(var(--muted-foreground)); }

    /* One hue per band: the eyebrow and its rule carry it, nothing else */
    .is-flame { color: hsl(var(--lp-flame)); }
    .is-leaf { color: hsl(var(--lp-leaf)); }
    .is-berry { color: hsl(var(--lp-berry)); }
    .is-gold { color: hsl(var(--gold)); }
    .dark .is-flame { color: hsl(14 80% 62%); }
    .dark .is-leaf { color: hsl(164 55% 45%); }
    .dark .is-berry { color: hsl(344 62% 62%); }

    /* ── Buttons ── */
    .lp-cta {
        display: inline-flex; align-items: center; justify-content: center;
        padding: 0.8125rem 1.625rem; border-radius: var(--radius);
        background: hsl(var(--gold)); color: #fff; font-size: 0.9375rem; font-weight: 600;
        transition: filter 150ms, transform 150ms;
    }
    .dark .lp-cta { color: hsl(var(--background)); }
    .lp-cta:hover { filter: brightness(1.08); }
    .lp-cta:active { transform: translateY(1px); }
    .lp-second {
        display: inline-flex; align-items: center; justify-content: center;
        padding: 0.8125rem 1.5rem; border-radius: var(--radius);
        border: 1px solid hsl(var(--foreground) / 0.2); background: hsl(var(--card));
        font-size: 0.9375rem; font-weight: 600; color: hsl(var(--foreground));
        transition: background-color 150ms, border-color 150ms;
    }
    .lp-second:hover { background: hsl(var(--muted)); border-color: hsl(var(--foreground) / 0.35); }

    /* Footer */
    .lp-foot-link { font-size: 0.9375rem; color: hsl(var(--muted-foreground)); transition: color 150ms; }
    .lp-foot-link:hover { color: hsl(var(--foreground)); }
    .lp-langs { display: grid; gap: 0.375rem; justify-items: start; }
    .lp-lang { font-size: 0.9375rem; color: hsl(var(--muted-foreground)); transition: color 150ms; }
    .lp-lang:hover { color: hsl(var(--foreground)); text-decoration: underline; text-underline-offset: 3px; }
    .lp-lang[aria-current="true"] { color: hsl(var(--foreground)); font-weight: 600; }
    .lp-credits > summary { cursor: pointer; list-style: none; font-size: 0.75rem; color: hsl(var(--muted-foreground) / 0.8); }
    .lp-credits > summary::-webkit-details-marker { display: none; }
    .lp-credits > summary:hover { color: hsl(var(--foreground)); }
    .lp-credits p { font-size: 0.75rem; line-height: 1.6; color: hsl(var(--muted-foreground) / 0.8); margin-top: 0.375rem; }

    /* ── Photographs: real choirs, not product shots ── */

    /* The screenshots in the hero: a tab strip, then the frame they sit in.
       Every screenshot is in the page and `app-shots` shows one. */
    .shot-bar {
        display: flex; align-items: center; gap: 0.75rem;
        padding: 0.5rem 0.625rem; border-bottom: 1px solid hsl(var(--border));
        background: hsl(var(--muted) / 0.5);
    }
    .shot-tabs { display: flex; gap: 0.125rem; overflow-x: auto; scrollbar-width: none; flex: 1 1 auto; }
    .shot-tabs::-webkit-scrollbar { display: none; }
    .shot-tabs > button {
        padding: 0.5rem 1rem; border-radius: 0.625rem; font-size: 0.875rem; font-weight: 500;
        white-space: nowrap; color: hsl(var(--muted-foreground));
        border: 1px solid transparent;
        transition: background-color 150ms, color 150ms;
    }
    .shot-tabs > button:hover { color: hsl(var(--foreground)); }
    .shot-tabs > button[aria-selected="true"] {
        background: hsl(var(--card)); color: hsl(var(--foreground)); font-weight: 600;
        border-color: hsl(var(--border));
        box-shadow: 0 1px 2px hsl(var(--foreground) / 0.06);
    }
    .shot-join {
        display: inline-flex; align-items: center; gap: 0.375rem; flex: none;
        padding: 0.5rem 1rem; border-radius: 0.625rem;
        border: 1px solid hsl(var(--border)); background: hsl(var(--card));
        font-size: 0.875rem; font-weight: 600; color: hsl(var(--foreground)); white-space: nowrap;
        box-shadow: 0 1px 2px hsl(var(--foreground) / 0.06);
        transition: background-color 150ms;
    }
    .shot-join:hover { background: hsl(var(--muted)); }
    @media (max-width: 720px) { .shot-join { display: none; } }
    .shot-frame {
        border: 1px solid hsl(var(--border)); border-radius: 0.875rem; overflow: hidden;
        background: hsl(var(--card));
        box-shadow: 0 1px 2px hsl(var(--foreground) / 0.06);
    }
    /* Whatever a capture's proportions, the frame keeps one shape and shows the
       top of the page, where the interface is. 16:10 captures fit exactly.
       :where() keeps this at zero specificity so a hidden image stays hidden. */
    .shot-frame :where(img) { display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: cover; object-position: top center; }
    /* The light and dark captures swap on the theme class, not on a media query */
    .shot-dark { display: none; }
    .dark .shot-light { display: none; }
    .dark .shot-dark { display: block; }
    .dark .shot-frame { box-shadow: 0 0 0 1px hsl(var(--foreground) / 0.08); }
    /* Nothing behind the hero but the ground and the lattice */
    .lp-hero-plain { text-align: center; }
    .lp-hero-plain .lp-display, .lp-hero-plain .lp-lead { margin-inline: auto; }

    /* ── What the app holds: one bordered board, four across and two down ── */
    .lp-board { border: 1px solid hsl(var(--border)); border-radius: 0.875rem; overflow: hidden; background: hsl(var(--card)); display: grid; grid-template-columns: minmax(0, 1fr); }
    @media (min-width: 620px) { .lp-board { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
    @media (min-width: 1040px) { .lp-board { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
    .lp-cell-item { padding: 1.5rem; border-top: 1px solid hsl(var(--border)); border-left: 1px solid hsl(var(--border)); }
    .lp-cell-item:first-child { border-top: 0; }
    @media (min-width: 620px) { .lp-cell-item:nth-child(-n+2) { border-top: 0; } .lp-cell-item:nth-child(odd) { border-left: 0; } }
    @media (min-width: 1040px) { .lp-cell-item:nth-child(-n+4) { border-top: 0; } .lp-cell-item:nth-child(odd) { border-left: 1px solid hsl(var(--border)); } .lp-cell-item:nth-child(4n+1) { border-left: 0; } }
    .lp-cell-item svg { margin-bottom: 0.875rem; }
    .lp-cell-title { font-size: 0.9375rem; font-weight: 600; color: hsl(var(--foreground)); }
    .lp-cell-body { font-size: 0.875rem; line-height: 1.55; color: hsl(var(--muted-foreground)); margin-top: 0.375rem; }


    /* ── A section below the hero: a centred lede, then one panel built with the
       same care as the hero's frame. The panels are real markup from the app's
       own vocabulary, so they translate and never drift — but they are held to
       the frame's standard: one border, one radius, one quiet lift. ── */
    .lp-sep { position: relative; }
    .lp-sep::before {
        content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
        width: 100vw; height: 1px; background: hsl(var(--border));
    }

    .lp-lede { max-width: 44rem; margin-inline: auto; text-align: center; }
    .lp-lede .lp-body { margin-inline: auto; }

    .panel {
        border: 1px solid hsl(var(--border)); border-radius: 0.875rem; overflow: hidden;
        background: hsl(var(--card)); box-shadow: 0 1px 2px hsl(var(--foreground) / 0.06);
    }
    .panel-head {
        display: flex; align-items: center; gap: 0.875rem; flex-wrap: wrap;
        padding: 0.875rem 1.25rem; border-bottom: 1px solid hsl(var(--border));
        background: hsl(var(--muted) / 0.45);
    }
    .panel-title { font-size: 0.9375rem; font-weight: 600; color: hsl(var(--foreground)); }
    .panel-sub { font-size: 0.8125rem; color: hsl(var(--muted-foreground)); }
    .panel-tag { margin-left: auto; font-size: 0.75rem; font-weight: 600; color: hsl(var(--gold)); }
    .panel-body { padding: 1.25rem; }
    @media (min-width: 720px) { .panel-body { padding: 1.5rem 1.75rem; } }
    .panel-split { display: grid; }
    @media (min-width: 900px) { .panel-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
    .panel-split > * + * { border-top: 1px solid hsl(var(--border)); }
    @media (min-width: 900px) { .panel-split > * + * { border-top: 0; border-left: 1px solid hsl(var(--border)); } }
    .panel-label { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }

    /* Searching the repertoire */
    .q-field {
        display: flex; align-items: center; gap: 0.625rem; padding: 0.75rem 0.9375rem;
        border: 1px solid hsl(var(--input)); border-radius: var(--radius);
        background: hsl(var(--background)); font-size: 0.9375rem; color: hsl(var(--muted-foreground));
    }
    .q-chips { display: flex; flex-wrap: wrap; gap: 0.375rem; margin-top: 0.75rem; }
    .q-chip { padding: 0.3125rem 0.6875rem; border-radius: 0.5rem; border: 1px solid hsl(var(--border)); font-size: 0.8125rem; color: hsl(var(--muted-foreground)); }
    .q-chip.is-on { border-color: transparent; background: hsl(var(--lp-leaf)); color: #fff; font-weight: 500; }
    .dark .q-chip.is-on { background: hsl(164 55% 34%); }

    /* A song, laid out the way songs/_song lays it out */
    .srow { display: flex; gap: 0.875rem; padding: 0.9375rem 1.25rem; align-items: flex-start; }
    @media (min-width: 720px) { .srow { padding: 0.9375rem 1.75rem; } }
    .srow + .srow { border-top: 1px solid hsl(var(--border) / 0.7); }
    .srow-icon { display: grid; place-items: center; width: 2.5rem; height: 2.5rem; border-radius: var(--radius); background: hsl(var(--muted)); flex: none; }
    .srow-title { font-size: 1rem; font-weight: 600; color: hsl(var(--foreground)); }
    .srow-badge { margin-left: 0.5rem; font-size: 0.6875rem; font-weight: 500; padding: 0.125rem 0.4375rem; border-radius: 0.375rem; border: 1px solid hsl(var(--border)); color: hsl(var(--muted-foreground)); white-space: nowrap; }
    .srow-meta { display: block; font-size: 0.8125rem; color: hsl(var(--muted-foreground)); margin-top: 0.1875rem; }
    .srow-tags { display: flex; flex-wrap: wrap; gap: 0.3125rem; margin-top: 0.5625rem; }
    .srow-tag { font-size: 0.6875rem; padding: 0.1875rem 0.5rem; border-radius: 0.375rem; background: hsl(var(--muted)); color: hsl(var(--muted-foreground)); }

    /* The programme of a celebration: a numbered sequence, because it is one */
    .prog-line { display: grid; grid-template-columns: 1.375rem 9rem minmax(0, 1fr); gap: 0.25rem 1rem; padding: 0.8125rem 1.25rem; align-items: baseline; }
    @media (min-width: 720px) { .prog-line { padding: 0.8125rem 1.75rem; } }
    @media (max-width: 640px) { .prog-line { grid-template-columns: 1.375rem minmax(0, 1fr); } .prog-line > .prog-song { grid-column: 2; } }
    .prog-line + .prog-line { border-top: 1px solid hsl(var(--border) / 0.7); }
    .prog-num { font-size: 0.75rem; font-variant-numeric: tabular-nums; color: hsl(var(--muted-foreground) / 0.8); }
    .prog-cue { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: hsl(var(--lp-flame)); }
    .dark .prog-cue { color: hsl(14 80% 62%); }
    .prog-song { font-size: 1rem; color: hsl(var(--foreground)); }
    .prog-who { display: block; font-size: 0.8125rem; color: hsl(var(--muted-foreground)); margin-top: 0.1875rem; }

    /* The roster beside the balance of the sections */
    .prow { display: flex; align-items: center; gap: 0.75rem; padding: 0.6875rem 0; }
    .prow + .prow { border-top: 1px solid hsl(var(--border) / 0.6); }
    .prow-face { width: 2rem; height: 2rem; border-radius: 999px; background: hsl(var(--muted)); display: grid; place-items: center; font-size: 0.6875rem; font-weight: 700; color: hsl(var(--muted-foreground)); flex: none; }
    .prow-name { font-size: 0.9375rem; color: hsl(var(--foreground)); }
    .prow-role { display: block; font-size: 0.75rem; color: hsl(var(--muted-foreground)); }
    .prow-voices { margin-left: auto; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.25rem; }

    /* A term of Sundays, one row per singer */
    .rollgrid { display: grid; gap: 0.5rem 1.25rem; }
    @media (min-width: 620px) { .rollgrid { grid-template-columns: 10rem minmax(0, 30rem); align-items: center; } }
    .rollgrid-name { font-size: 0.8125rem; color: hsl(var(--foreground)); }
    .rollrow { display: grid; grid-template-columns: repeat(20, minmax(0, 1fr)); gap: 0.25rem; }
    .rollrow > span { aspect-ratio: 1; border-radius: 0.1875rem; }

    /* The file a choir arrives with, and what each of its columns becomes */
    .map-file { display: flex; align-items: center; gap: 0.625rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.8125rem; color: hsl(var(--foreground)); }

    /* A photograph on the landing is shown whole and sharp, in its own column,
       never under the words: nothing is veiled, dimmed or blurred. */
    .lp-shot { border-radius: var(--radius); overflow: hidden; border: 1px solid hsl(var(--border)); background: hsl(var(--muted)); }
    .lp-shot img { display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }


    /* ── Cards ── */
    .lp-card {
        border: 1px solid hsl(var(--border)); border-radius: var(--radius);
        background: hsl(var(--card)); overflow: hidden;
        box-shadow: 0 1px 2px hsl(var(--foreground) / 0.06), 0 22px 44px -30px hsl(var(--foreground) / 0.45);
    }
    .dark .lp-card { box-shadow: 0 0 0 1px hsl(var(--foreground) / 0.06); }

    .lp-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2.5rem; align-items: center; }
    @media (min-width: 940px) {
        .lp-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4.5rem; }
        .lp-row.is-flipped > :first-child { order: 2; }
    }
    .lp-row-text { display: grid; gap: 1rem; align-content: start; }

    /* ── The feuille de messe ── */
    .sheet-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; padding: 0.9375rem 1.25rem; border-bottom: 1px solid hsl(var(--border)); }
    .sheet-choir { font-size: 0.9375rem; font-weight: 600; color: hsl(var(--foreground)); }
    .sheet-when { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.75rem; color: hsl(var(--muted-foreground)); }
    .sheet-next { width: 0.4375rem; height: 0.4375rem; border-radius: 999px; background: hsl(var(--gold)); box-shadow: 0 0 0 3px hsl(var(--gold) / 0.2); flex: none; }
    .sheet-line { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.125rem; padding: 0.5625rem 1.25rem; }
    @media (min-width: 420px) { .sheet-line { grid-template-columns: 7rem minmax(0, 1fr); gap: 0.875rem; align-items: baseline; } }
    .sheet-line + .sheet-line { border-top: 1px solid hsl(var(--border) / 0.7); }
    .sheet-cue { font-size: 0.625rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: hsl(var(--lp-flame)); }
    .dark .sheet-cue { color: hsl(14 80% 62%); }
    .sheet-song { font-size: 0.9375rem; color: hsl(var(--foreground)); }
    .sheet-who { display: block; font-size: 0.75rem; color: hsl(var(--muted-foreground)); }

    /* ── A cahier line ── */
    .song-filters { display: flex; flex-wrap: wrap; gap: 0.375rem; padding: 0.875rem 1.125rem; border-bottom: 1px solid hsl(var(--border)); background: hsl(var(--muted) / 0.6); }
    .song-filter { display: inline-flex; align-items: center; padding: 0.1875rem 0.5625rem; border-radius: 0.375rem; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); font-size: 0.75rem; color: hsl(var(--muted-foreground)); }
    .song-filter.is-on { border-color: transparent; background: hsl(var(--lp-leaf)); color: #fff; font-weight: 500; }
    .song-row { display: grid; gap: 0.1875rem; padding: 0.875rem 1.125rem; }
    .song-row + .song-row { border-top: 1px solid hsl(var(--border) / 0.7); }
    .song-name { font-size: 0.9375rem; font-weight: 500; color: hsl(var(--foreground)); }
    .song-meta { font-size: 0.8125rem; color: hsl(var(--muted-foreground)); }
    .song-meta > * + *::before { content: "·"; margin: 0 0.4375rem; opacity: 0.5; }

    /* ── The pupitres, each its own colour and its real headcount ── */
    .staff { display: grid; gap: 0.75rem; padding: 1.125rem 1.25rem; }
    .staff-row { display: grid; gap: 0.3125rem; }
    .staff-label { display: flex; align-items: baseline; gap: 0.5rem; }
    .staff-name { font-size: 0.75rem; font-weight: 600; }
    .staff-count { font-size: 0.75rem; font-variant-numeric: tabular-nums; color: hsl(var(--muted-foreground)); }
    .staff-track { height: 0.5rem; border-radius: 999px; background: hsl(var(--foreground) / 0.07); }
    .staff-line { display: block; height: 100%; border-radius: 999px; transform-origin: left center; }
    .staff-0 { color: var(--voice-1); } .staff-0 .staff-line { background: var(--voice-1); }
    .staff-1 { color: var(--voice-2); } .staff-1 .staff-line { background: var(--voice-2); }
    .staff-2 { color: var(--voice-3); } .staff-2 .staff-line { background: var(--voice-3); }
    .staff-3 { color: var(--voice-4); } .staff-3 .staff-line { background: var(--voice-4); }
    .staff-4 { color: var(--voice-5); } .staff-4 .staff-line { background: var(--voice-5); }
    .staff-5 { color: var(--voice-6); } .staff-5 .staff-line { background: var(--voice-6); }

    /* ── The roll ── */
    .roll { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 0.3125rem; max-width: 21rem; }
    .roll-cell { aspect-ratio: 1; border-radius: 0.3125rem; }
    .roll-legend { display: flex; flex-wrap: wrap; gap: 0.375rem 1.125rem; }
    .roll-key { display: inline-flex; align-items: center; gap: 0.4375rem; font-size: 0.8125rem; color: hsl(var(--muted-foreground)); }
    .roll-swatch { width: 0.625rem; height: 0.625rem; border-radius: 0.1875rem; }

    /* ── The mapping an import really is ── */
    .mapping-row { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: 0.75rem; align-items: center; padding: 0.6875rem 1.125rem; }
    .mapping-row + .mapping-row { border-top: 1px solid hsl(var(--border) / 0.7); }
    .mapping-from { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.8125rem; color: hsl(var(--muted-foreground)); }
    .mapping-arrow { font-size: 0.75rem; color: hsl(var(--lp-berry)); }
    .dark .mapping-arrow { color: hsl(344 62% 62%); }
    .mapping-to { font-size: 0.875rem; font-weight: 500; color: hsl(var(--foreground)); text-align: right; }

    /* ── What a choir keeps, on a quiet tinted ground ── */
    .lp-tinted { background: hsl(var(--lp-leaf) / 0.05); border-block: 1px solid hsl(var(--lp-leaf) / 0.14); }
    .dark .lp-tinted { background: hsl(var(--lp-leaf) / 0.1); border-color: hsl(var(--lp-leaf) / 0.22); }
    .lp-keeps { display: grid; gap: 2rem 3rem; }
    @media (min-width: 640px) { .lp-keeps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
    @media (min-width: 940px) { .lp-keeps { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
    .lp-term { font-size: 1rem; font-weight: 600; color: hsl(var(--foreground)); }
    .lp-def { font-size: 0.9375rem; line-height: 1.6; color: hsl(var(--muted-foreground)); margin-top: 0.25rem; }
    /* Each part of the app introduces itself with a piece of its own content */
    .spec { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3125rem; min-height: 1.625rem; margin-bottom: 0.875rem; }
    .spec-tag {
        font-size: 0.75rem; padding: 0.1875rem 0.5625rem; border-radius: 0.375rem;
        border: 1px solid hsl(var(--border)); background: hsl(var(--card)); color: hsl(var(--muted-foreground));
    }
    .spec-tag.is-solid { border-color: transparent; background: hsl(var(--foreground)); color: hsl(var(--background)); font-weight: 500; }
    .spec-url { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.75rem; color: hsl(var(--muted-foreground)); }
    .spec-voice { font-size: 0.75rem; font-weight: 500; padding: 0.1875rem 0.5625rem; border-radius: 0.375rem; }
    /* One per voice, high to low. A voice is never shown by colour alone: the
       name is always inside the badge. */
    .spec-voice-0 { color: var(--voice-1); background: color-mix(in srgb, var(--voice-1) 13%, transparent); }
    .spec-voice-1 { color: var(--voice-2); background: color-mix(in srgb, var(--voice-2) 13%, transparent); }
    .spec-voice-2 { color: var(--voice-3); background: color-mix(in srgb, var(--voice-3) 13%, transparent); }
    .spec-voice-3 { color: var(--voice-4); background: color-mix(in srgb, var(--voice-4) 15%, transparent); }
    .spec-voice-4 { color: var(--voice-5); background: color-mix(in srgb, var(--voice-5) 15%, transparent); }
    .spec-voice-5 { color: var(--voice-6); background: color-mix(in srgb, var(--voice-6) 13%, transparent); }
    .spec-roll { display: flex; gap: 0.1875rem; }
    .spec-roll > span { width: 0.875rem; height: 0.875rem; border-radius: 0.25rem; }

    /* ── Incipits and the litany of languages, over a photograph ── */
    /* The one band with a photograph as its ground. It is the only place on the
       page where type sits on an image, so the veil is flat and heavy enough to
       read against, and the picture is never blurred. */
    .lp-invert { position: relative; isolation: isolate; background: #100c0a; color: #fff; }
    .lp-invert > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; }
    .lp-invert::before { content: ""; position: absolute; inset: 0; z-index: -1; background: rgb(14 10 8 / 0.74); }
    .lp-invert .lp-display, .lp-invert .lp-head { color: #fff; }
    .lp-invert .lp-eyebrow, .lp-invert .lp-lead, .lp-invert .lp-body, .lp-invert .lp-note { color: rgb(255 255 255 / 0.7); }

    .incipit { display: block; }
    .incipit + .incipit { margin-top: 1.25rem; }
    .incipit-tongue { display: block; font-size: 0.625rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; color: hsl(41 86% 66%); margin-bottom: 0.25rem; }
    .incipit-line { font-family: 'Archivo', system-ui, sans-serif; font-weight: 700; font-size: clamp(1.5rem, 4.2vw, 2.625rem); line-height: 1.12; letter-spacing: -0.03em; color: #fff; }
    .tongues { display: flex; flex-wrap: wrap; column-gap: 1.125rem; row-gap: 0.375rem; }
    .tongue { font-size: clamp(0.9375rem, 1.5vw, 1.0625rem); color: rgb(255 255 255 / 0.62); }
    .tongue.is-app { color: hsl(41 86% 66%); font-weight: 700; }

    /* ── Prices ── */
    .price-card { display: flex; flex-direction: column; gap: 0.4375rem; padding: 1.375rem; }
    .price-card.is-featured { border-color: hsl(var(--gold) / 0.6); box-shadow: 0 0 0 1px hsl(var(--gold) / 0.35), 0 22px 44px -30px hsl(var(--gold) / 0.55); }
    .price-amount { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.02em; color: hsl(var(--foreground)); white-space: nowrap; }
    .price-per { font-size: 0.8125rem; font-weight: 400; color: hsl(var(--muted-foreground)); }
    .price-tagline { font-size: 0.875rem; line-height: 1.6; color: hsl(var(--muted-foreground)); }

    @media (prefers-reduced-motion: no-preference) {
        .reveal { opacity: 0; transform: translateY(16px); transition: opacity 700ms cubic-bezier(0.16, 1, 0.3, 1), transform 700ms cubic-bezier(0.16, 1, 0.3, 1); }
        .reveal.is-in { opacity: 1; transform: none; }
        /* The page's one orchestrated moment: six pupitres entering like a canon */
        .staff-line { transform: scaleX(0); }
        .is-in .staff-line { animation: staff-in 900ms cubic-bezier(0.16, 1, 0.3, 1) forwards; animation-delay: calc(var(--i) * 110ms); }
        @keyframes staff-in { to { transform: none; } }
        /* Its echo: the litany arriving name by name */
        .tongue { opacity: 0; animation: tongue-in 560ms cubic-bezier(0.16, 1, 0.3, 1) forwards; animation-play-state: paused; }
        .is-in .tongue { animation-play-state: running; animation-delay: calc(var(--i) * 26ms); }
        @keyframes tongue-in { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
    }
}
