/*
 * The `hidden` attribute loses to any rule that sets `display`, and several
 * things here are display: flex or grid — which is how the countdown's hide
 * button quietly stopped working. This puts the attribute back in charge for
 * everything, so hiding an element is never a question of specificity.
 */
[hidden] { display: none !important; }

/*
 * The whole app is dark, and the browser needs telling.
 *
 * Without this, everything the browser draws itself stays light: the little
 * calendar glyph inside a date field, the picker that drops out of it, the
 * scrollbars. A date input styled dark by hand but left light here is the
 * worst of both — a black box with a black icon in it.
 */
:root { color-scheme: dark; }

/*
 * Every text field and dropdown, in one place.
 *
 * This should have existed from the start. There was no shared rule, so each
 * form carried its own copy of the same five declarations — and a form that
 * forgot got the browser's default, which on this background is a white slab.
 * That is exactly how the calendar shipped.
 *
 * `:where()` keeps the specificity at zero, so every existing per-form rule
 * still wins and nothing already on screen moves. Checkboxes, radios and file
 * inputs are left alone: they are not boxes you type in, and giving them a
 * padded frame would make a tick look like a text field.
 */
input:where(:not([type="checkbox"], [type="radio"], [type="file"])),
select {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    border-radius: 8px;
    padding: 9px 12px;
    font-family: inherit;
    font-size: 0.95rem;
    outline: none;
    /* Or a `width: 100%` box is 100% *plus* its padding and border. */
    box-sizing: border-box;
}

input:where(:not([type="checkbox"], [type="radio"], [type="file"])):focus,
select:focus { border-color: #8a2be2; }

/*
 * The list a dropdown drops open is drawn by the operating system, and on
 * Windows that is a pale grey however dark the control above it is. Two places
 * had already discovered this and carried their own copy of this rule; here it
 * is once, so the next dropdown added does not have to find out the same way.
 */
select option { background: #1a1030; color: #ffffff; }

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    height: 100dvh;
    margin: 0;
    color: #ffffff;
    background-color: #080312;
    overflow: hidden;
    position: relative;
}

.aurora-bg {
    position: absolute;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    overflow: hidden;
    z-index: -1;
    filter: blur(90px);
}

.orb { position: absolute; border-radius: 50%; opacity: 0.6; }
.orb-1 { width: 70vw; height: 70vw; background-color: #ffb6c1; top: -20%; left: -10%; animation: float1 25s infinite linear; }
.orb-2 { width: 80vw; height: 80vw; background-color: #8a2be2; bottom: -30%; right: -20%; animation: float2 30s infinite linear; }
.orb-3 { width: 50vw; height: 50vw; background-color: #ff1493; top: 20%; left: 20%; animation: float3 20s infinite linear; }
.orb-4 { width: 90vw; height: 90vw; background-color: #191970; top: -10%; right: 10%; animation: float4 35s infinite linear; }

@keyframes float1 { 0% { transform: translate(0, 0); } 33% { transform: translate(30vw, 15vh); } 66% { transform: translate(10vw, 40vh); } 100% { transform: translate(0, 0); } }
@keyframes float2 { 0% { transform: translate(0, 0); } 33% { transform: translate(-30vw, -20vh); } 66% { transform: translate(-50vw, 10vh); } 100% { transform: translate(0, 0); } }
@keyframes float3 { 0% { transform: translate(0, 0); } 50% { transform: translate(40vw, -10vh); } 100% { transform: translate(0, 0); } }
@keyframes float4 { 0% { transform: translate(0, 0); } 50% { transform: translate(-20vw, 30vh); } 100% { transform: translate(0, 0); } }

.countdown-container {
    text-align: center;
    background: rgba(255, 255, 255, 0.05);
    width: 90%;
    max-width: 600px;
    box-sizing: border-box;
    padding: 50px 30px 40px 30px;
    border-radius: 15px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
    border: 1px solid rgba(255, 255, 255, 0.1);
    position: relative;
    z-index: 10;
    backdrop-filter: blur(10px);
}

.lang-select {
    position: absolute;
    top: 15px;
    right: 15px;
    background: rgba(255, 255, 255, 0.1);
    color: #a8b2d1;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 5px;
    padding: 5px 10px;
    font-size: 0.9rem;
    cursor: pointer;
    outline: none;
    z-index: 20;
}

.lang-select:focus { border-color: #ffb6c1; }
.lang-select option { background: #1e1e2f; color: #ffffff; }

.toggle-timer-btn {
    position: absolute;
    top: 15px;
    left: 15px;
    background: rgba(255, 255, 255, 0.1);
    color: #ffffff;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 5px;
    padding: 5px 8px;
    font-size: 1rem;
    cursor: pointer;
    outline: none;
    z-index: 20;
    transition: all 0.2s ease;
}
.toggle-timer-btn:hover {
    border-color: #ffb6c1;
    background: rgba(255, 255, 255, 0.2);
}

h1 {
    font-size: 1.5rem;
    margin-bottom: 30px;
    margin-top: 10px;
    color: #a8b2d1;
    font-weight: normal;
}

#timer {
    display: flex;
    justify-content: center;
    gap: 20px;
    color: #ffb6c1;
    text-shadow: 0 0 10px rgba(255, 182, 193, 0.6), 0 0 20px rgba(255, 182, 193, 0.3);
    font-variant-numeric: tabular-nums;
}

.time-block { display: flex; align-items: baseline; gap: 5px; }
.num { font-size: 4rem; font-weight: bold; letter-spacing: 2px; }
.unit { font-size: 1.2rem; color: #a8b2d1; font-weight: normal; text-shadow: none; }

.button-group {
    display: flex;
    justify-content: center;
    gap: 15px;
    margin-top: 35px;
    flex-wrap: wrap;
}

button {
    padding: 12px 24px;
    font-size: 1rem;
    color: #1e1e2f;
    background-color: #ffb6c1;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-weight: bold;
    transition: all 0.2s ease;
    box-shadow: 0 0 10px rgba(255, 182, 193, 0.3);
    touch-action: manipulation;
    position: relative; /* Crucial for positioning the NEW badge */
}

button:hover { background-color: #ffc0cb; transform: scale(1.05); }
button:disabled { opacity: 0.5; cursor: default; transform: none; }

/* The Heart Button Styling */
#cookieBtn {
    background-color: #ff6b81;
    color: white;
    box-shadow: 0 0 10px rgba(255, 107, 129, 0.4);
}
#cookieBtn:hover { background-color: #ff4757; }

#addBtn { background-color: #8a2be2; color: white; box-shadow: 0 0 10px rgba(138, 43, 226, 0.3); }
#addBtn:hover { background-color: #9b4dca; }

/* The Pulsing NEW Badge */
.new-badge {
    display: none; /* Hidden by default */
    position: absolute;
    top: -10px;
    right: -10px;
    background-color: #ffeb3b;
    color: #1e1e2f;
    font-size: 0.75rem;
    padding: 4px 8px;
    border-radius: 12px;
    font-weight: 900;
    letter-spacing: 1px;
    box-shadow: 0 0 10px rgba(255, 235, 59, 0.8);
    animation: pulseBadge 1.5s infinite;
    z-index: 50;
}

/* The Rotating Spinner */
.spinner {
    display: inline-block;
    animation: spinSync 1s linear infinite;
    margin-left: 6px;
    font-weight: bold;
}

@keyframes spinSync {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

@keyframes pulseBadge {
    0% { transform: scale(1); }
    50% { transform: scale(1.15); }
    100% { transform: scale(1); }
}

.input-section {
    display: none;
    margin-top: 25px;
    flex-direction: column;
    gap: 10px;
    align-items: center;
}

textarea {
    width: 90%;
    height: 60px;
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.3);
    color: white;
    border-radius: 8px;
    padding: 10px;
    font-family: inherit;
    resize: none;
    outline: none;
}

textarea:focus { border-color: #8a2be2; }

#submitBtn { margin-top: 0; padding: 8px 20px; background-color: #64ffda; color: #1e1e2f; }

#message {
    margin-top: 25px;
    font-size: 1.2rem;
    color: #ffffff;
    font-style: italic;
    min-height: 25px;
    text-shadow: 0 2px 4px rgba(0,0,0,0.5);
    padding: 0 10px;
    unicode-bidi: plaintext;
}

.particle {
    position: fixed;
    border-radius: 3px;
    pointer-events: none;
    z-index: 9999;
    top: 0;
    left: 0;
    opacity: 1;
}

/* ---------------------------------------------------------------
   The passphrase gate. The jar is private; the countdown is not,
   so only the jar controls hide when signed out.
   --------------------------------------------------------------- */

.signed-out .needs-auth { display: none !important; }

/* Lists, album and admin are private in their entirety — every one of them is
   a `.view.page`. Marking each control inside them `needs-auth` would work
   until the day somebody adds one and forgets. Only the jar's public countdown
   survives being signed out, and that lives in the header. */
.signed-out .view.page { display: none !important; }
.signed-in #authGate { display: none; }

#authGate {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    margin-top: 32px;
}

#authPrompt {
    color: #a8b2d1;
    font-size: 0.95rem;
}

#authPassphrase {
    width: 70%;
    max-width: 280px;
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.3);
    color: white;
    border-radius: 8px;
    padding: 10px 12px;
    font-family: inherit;
    font-size: 1rem;
    text-align: center;
    outline: none;
}

#authPassphrase:focus { border-color: #ff6b81; }

/* Matches #cookieBtn — the two are the jar's way in, and read as one family. */
#authSubmit {
    background-color: #ff6b81;
    color: white;
    padding: 10px 22px;
    box-shadow: 0 0 10px rgba(255, 107, 129, 0.4);
}
#authSubmit:hover { background-color: #ff4757; }

#authError {
    color: #ff8fa3;
    font-size: 0.9rem;
    min-height: 1.2em;
}

/* Shown while /api/session is still in flight. A passphrase box sitting there
   during the check reads as "you were signed out" — the opposite of what is
   usually happening, since most loads end up signed in. */
#authChecking {
    display: none;
    align-items: center;
    gap: 10px;
    color: #a8b2d1;
    font-size: 0.95rem;
    /* Roughly the height of the form it replaces, so the panel doesn't jump. */
    min-height: 44px;
}

.checking #authChecking { display: flex; }
.checking #authPrompt,
.checking #authPassphrase,
.checking #authSubmit,
.checking #authError { display: none; }

.auth-spinner {
    width: 18px;
    height: 18px;
    border: 2px solid rgba(255, 182, 193, 0.25);
    border-top-color: #ff6b81;
    border-radius: 50%;
    animation: spinSync 0.8s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
    /* Still turns — a frozen ring says "stuck" — just slowly enough not to nag. */
    .auth-spinner { animation-duration: 2.4s; }
}

/* Edit / delete affordances on the note currently on screen. */
.note-actions {
    display: none;
    justify-content: center;
    gap: 10px;
    margin-top: 12px;
}

.note-actions.visible { display: flex; }

.note-actions button {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.15);
    color: #a8b2d1;
    box-shadow: none;
    padding: 5px 12px;
    font-size: 0.95rem;
    font-weight: normal;
}

.note-actions button:hover {
    background: rgba(255, 255, 255, 0.18);
    transform: scale(1.05);
}

#noteAuthor {
    display: block;
    margin-top: 10px;
    font-size: 0.85rem;
    font-style: normal;
    color: #a8b2d1;
    opacity: 0.85;
}

#whoami {
    margin-top: 28px;
    font-size: 0.8rem;
    color: #a8b2d1;
    opacity: 0.6;
}

#signOutBtn {
    background: none;
    border: none;
    box-shadow: none;
    color: inherit;
    font: inherit;
    font-weight: normal;
    text-decoration: underline;
    padding: 0 4px;
    cursor: pointer;
}

#signOutBtn:hover { background: none; transform: none; color: #ffb6c1; }

@media (max-width: 600px) {
    .countdown-container { padding: 40px 15px 30px 15px; }
    h1 { font-size: 1.2rem; margin-top: 25px; }
    #timer { gap: 8px; }
    .num { font-size: 2.2rem; }
    .unit { font-size: 0.9rem; }
    .button-group { margin-top: 25px; }
    button { padding: 10px 20px; }
}

/* ---------------------------------------------------------------
   Navigation shared by every page. The admin link is hidden unless
   the session says so — cosmetic only; the API returns 403 anyway.
   --------------------------------------------------------------- */

#siteNav {
    display: flex;
    justify-content: center;
    gap: 18px;
    margin-top: 24px;
    font-size: 0.9rem;
}

#siteNav a {
    color: #a8b2d1;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    padding-bottom: 2px;
    transition: color 0.2s ease, border-color 0.2s ease;
}

#siteNav a:hover,
#siteNav a:focus-visible { color: #ffb6c1; border-bottom-color: #ffb6c1; }

.admin-only { display: none; }
.is-admin .admin-only { display: inline; }

/* ---------------------------------------------------------------
   The admin page. A document to be scanned, not a card to be read,
   so it scrolls and sits wider than the countdown.
   --------------------------------------------------------------- */

body.admin-page,
body.lists-page {
    display: block;
    height: auto;
    min-height: 100dvh;
    overflow-y: auto;
}

.page-container {
    position: relative;
    z-index: 10;
    width: min(92%, 760px);
    margin: 0 auto;
    padding: 40px 0 80px;
}

.page-header { text-align: center; }
.page-header h1 { font-size: 1.6rem; color: #a8b2d1; font-weight: normal; margin: 0 0 4px; }

.panel {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    padding: 24px;
    margin-top: 28px;
    text-align: center;
    backdrop-filter: blur(10px);
}
.panel a { color: #ffb6c1; }

/* Who has been in lately, above the log. Two lines, so it stays a header to
   the list rather than competing with it. */
.visits {
    list-style: none;
    margin: 0 0 14px;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 8px 22px;
}

.visit { display: flex; align-items: baseline; gap: 8px; font-size: 0.88rem; }
.visit-who { color: #fff; font-weight: 600; }
.visit-when { color: #a8b2d1; }
.visit-when.never { color: #6f6790; font-style: italic; }
.visit small { color: #6f6790; font-size: 0.78rem; }

.filters {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: flex-end;
    justify-content: center;
    margin: 28px 0 8px;
}

/* ------------------------------------------- testing, told apart from using
 *
 * Activity inside a quiet period is filed rather than deleted: hidden by
 * default, and clearly marked when the switch brings it back. See
 * netlify/functions/_lib/quiet.mts for what opens one. */
.testing-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.8rem;
    color: #8479a0;
    cursor: pointer;
}
.testing-toggle input { accent-color: #8a2be2; cursor: pointer; }

.quiet-notice {
    margin: 8px auto 0;
    max-width: 40em;
    padding: 8px 12px;
    border-radius: 8px;
    background: rgba(255, 184, 107, 0.12);
    border: 1px solid rgba(255, 184, 107, 0.4);
    color: #ffd7ab;
    font-size: 0.82rem;
    text-align: center;
}

.event.while-testing { opacity: 0.7; }
.event-testing {
    margin-inline-start: 8px;
    padding: 1px 7px;
    border-radius: 999px;
    background: rgba(255, 184, 107, 0.18);
    color: #ffb86b;
    font-size: 0.62rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.filters label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #8479a0;
}

.filters select {
    background: rgba(0, 0, 0, 0.35);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 6px;
    padding: 7px 10px;
    font-family: inherit;
    font-size: 0.9rem;
    outline: none;
}
.filters select:focus { border-color: #8a2be2; }

button.ghost, a.ghost {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: #d8d2e8;
    box-shadow: none;
    font-weight: normal;
    padding: 8px 16px;
    font-size: 0.9rem;
}
button.ghost:hover, a.ghost:hover { background: rgba(255, 255, 255, 0.16); color: #fff; }

/* ------------------------------------------------------- the video call
 *
 * On the home page under the jar's own buttons. An anchor rather than a button
 * because it goes somewhere, and `a.ghost` rules do not cover it — this one has
 * a look of its own, since it is the only thing on the page that leaves it. */
.meeting-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 18px auto 0;
    padding: 9px 18px;
    border-radius: 999px;
    background: rgba(45, 140, 255, 0.14);
    border: 1px solid rgba(90, 165, 255, 0.45);
    color: #cfe3ff;
    font-size: 0.92rem;
    text-decoration: none;
    transition: background 0.18s ease, border-color 0.18s ease;
}
.meeting-link:visited { color: #cfe3ff; }
.meeting-link:hover { background: rgba(45, 140, 255, 0.26); border-color: #5aa5ff; color: #fff; }
.meeting-link:focus-visible { outline: 2px solid #8a2be2; outline-offset: 3px; }

/* The vault sits under the call link and does something completely different,
   so it does not get to look the same. Green rather than blue, and quieter:
   this one is a shortcut you reach for occasionally, not the thing the page is
   for. The `.meeting-link` rules above carry the shape; only colour changes. */
.vault-link,
.vault-link:visited {
    margin-top: 10px;
    background: rgba(60, 190, 130, 0.12);
    border-color: rgba(90, 210, 150, 0.4);
    color: #bfe9d4;
    font-size: 0.86rem;
}
.vault-link:hover { background: rgba(60, 190, 130, 0.24); border-color: #5ad296; color: #fff; }
.meeting-link-icon { font-size: 1.05rem; line-height: 1; }

@media (prefers-reduced-motion: reduce) {
    .meeting-link { transition: none; }
}

/* Who is in there. Green because the only state it ever has is "somebody is",
   and it is hidden the rest of the time. */
.call-presence {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    margin: 8px 0 0;
    font-size: 0.84rem;
    color: #8ce8c8;
}
.call-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #64ffda;
    box-shadow: 0 0 8px rgba(100, 255, 218, 0.8);
    animation: callPulse 2.4s ease-in-out infinite;
}
@keyframes callPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
    .call-dot { animation: none; }
}

/* The editor for it, on the settings page. */
.meeting-edit { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.meeting-edit input { flex: 1 1 260px; min-width: 0; }

/* The test buttons, which only one person gets. Their own block so the lock
   has a heading to sit on rather than landing on a bare row of buttons. */
.notify-tests-block { margin-top: 18px; }

/* On a device count the lock sits inline in a line of small grey text, so it
   has to be quieter than the one on a heading. */
.notify-person small.admin-gated .admin-mark { margin-inline-start: 4px; font-size: 0.9em; }

/* The lock on a panel only one person can open. The same glyph the nav uses,
   so a lock means one thing across the whole site. */
.admin-mark { margin-inline-start: 8px; font-size: 0.8em; opacity: 0.85; }

.status { min-height: 1.3em; margin: 8px 0 0; font-size: 0.9rem; color: #a8b2d1; text-align: center; }
.status.ok { color: #64ffda; }
.status.warn { color: #ffb86b; }

.events { list-style: none; margin: 20px 0 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }

.event {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-left: 3px solid #8a2be2;
    border-radius: 10px;
    padding: 14px 16px;
    backdrop-filter: blur(10px);
}

.event.undone { opacity: 0.6; border-left-color: #5a5a70; }

.event-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; font-size: 0.95rem; }
.event-head strong { color: #ffb6c1; font-weight: 600; }
.event-head time { margin-left: auto; font-size: 0.78rem; color: #8479a0; white-space: nowrap; }

.text-before, .text-after {
    display: flex;
    gap: 8px;
    align-items: baseline;
    margin-top: 8px;
    font-size: 0.92rem;
    line-height: 1.5;
}

.text-label {
    flex: none;
    width: 2.4em;
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #8479a0;
    padding-top: 0.25em;
}

.text-before q { color: #b3a8cd; text-decoration: line-through; text-decoration-color: rgba(255,255,255,0.25); }
.text-after q { color: #fff; }
.text-before q, .text-after q { quotes: none; unicode-bidi: plaintext; }

.event-summary {
    margin: 8px 0 0;
    font-size: 0.9rem;
    color: #d8d2e8;
    line-height: 1.45;
    unicode-bidi: plaintext;
}

.event-foot { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.event-foot button.ghost { padding: 5px 12px; font-size: 0.82rem; }
.undone-note { font-size: 0.8rem; color: #8479a0; }

#loadMoreBtn { display: block; margin: 20px auto 0; }

.admin-page #authGate { max-width: 420px; margin-left: auto; margin-right: auto; }
.admin-page #whoami { text-align: center; }

@media (max-width: 600px) {
    .page-container { padding: 28px 0 60px; }
    .event-head time { margin-left: 0; width: 100%; }
}

/* ---------------------------------------------------------------
   The todo lists.
   --------------------------------------------------------------- */

.lists-page .page-header { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.lists-page #authGate { max-width: 420px; margin-left: auto; margin-right: auto; }
.lists-page #whoami { text-align: center; }
.lists-page .lang-select { position: static; margin-top: 4px; }

.hint { color: #8479a0; font-size: 0.82rem; margin: 6px 0 0; text-align: center; }
.hint.warn { color: #ffb86b; }
.hint.ok { color: #64ffda; }

/* The per-device notification switch. */
.notify-box {
    margin-top: 26px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
}
.notify-box .hint { max-width: 34ch; line-height: 1.5; }

button.ghost.small { padding: 6px 12px; font-size: 0.82rem; }
button.ghost.tiny, a.ghost.tiny { padding: 3px 8px; font-size: 0.78rem; border-radius: 6px; }

/* The list switcher. Chips wrap rather than scroll — there will never be
   enough lists for a scroller to be worth the hidden-content problem. */
.list-strip {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    margin-top: 24px;
}

.list-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    background: rgba(255, 255, 255, 0.07);
    border: 1px solid rgba(255, 255, 255, 0.16);
    color: #d8d2e8;
    box-shadow: none;
    font-weight: normal;
    font-size: 0.9rem;
    padding: 8px 14px;
    border-radius: 999px;
}

.list-chip:hover { background: rgba(255, 255, 255, 0.14); color: #fff; transform: none; }
.list-chip.open {
    background: rgba(138, 43, 226, 0.35);
    border-color: #8a2be2;
    color: #fff;
}
.list-chip.locked { opacity: 0.75; }
.list-chip.archived { opacity: 0.5; font-style: italic; }

.chip-count {
    background: rgba(0, 0, 0, 0.35);
    border-radius: 999px;
    padding: 1px 7px;
    font-size: 0.74rem;
}
.chip-lock { font-size: 0.8rem; }

.new-list, .new-todo {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    justify-content: center;
    margin-top: 16px;
}

.new-list input[type="text"],
.new-todo input[type="text"],
.link-form input,
.todo-edit,
.todo-details {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    border-radius: 8px;
    padding: 9px 12px;
    font-family: inherit;
    font-size: 0.95rem;
    outline: none;
    /* Without this a `width: 100%` box is 100% *plus* its padding and border,
       so the notes field hangs 26px past the panel holding it. */
    box-sizing: border-box;
}

.new-list input[type="text"]:focus,
.new-todo input[type="text"]:focus,
.link-form input:focus,
.todo-edit:focus,
.todo-details:focus { border-color: #8a2be2; }

.new-todo { margin-top: 0; }
.new-todo input[type="text"] { flex: 1 1 240px; }

.private-toggle, .archived-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: #a8b2d1;
    font-size: 0.85rem;
    cursor: pointer;
}
.archived-toggle { margin-top: 24px; justify-content: center; width: 100%; }

.list-panel { text-align: left; }

.list-panel-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 10px;
    margin-bottom: 16px;
}
.list-panel-head h2 { margin: 0; font-size: 1.15rem; color: #fff; font-weight: 600; }
.counts { font-size: 0.8rem; color: #8479a0; }
.list-panel-actions { margin-left: auto; display: flex; gap: 6px; }

.todos { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }

.todo {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 10px;
    padding: 8px 10px;
}

/* Neighbours slide to open a gap; the dragged row follows the pointer. The
   transition is deliberately not on .dragging, or the row would lag behind
   the finger by its own animation. */
.todo { transition: transform 0.16s ease; }

.todo.dragging {
    border-color: #8a2be2;
    background: rgba(138, 43, 226, 0.18);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
    position: relative;
    z-index: 5;
    transition: none;
}

/* Held for one frame while a dropped row's transform and its real position are
   swapped, so the landing is instant instead of being animated twice. */
.todos.settling .todo { transition: none; }

@media (prefers-reduced-motion: reduce) {
    .todo { transition: none; }
}

.todo-row { display: flex; align-items: center; gap: 10px; }

.drag-handle {
    flex: none;
    border-radius: 6px;
    color: #6f6790;
    cursor: grab;
    font-size: 1.1rem;
    line-height: 1;
    padding: 4px 2px;
    /* Without this the browser claims the gesture for scrolling and the drag
       never starts on a phone. */
    touch-action: none;
}
.todo.dragging .drag-handle { cursor: grabbing; }
.drag-handle:focus-visible { outline: 2px solid #8a2be2; outline-offset: 2px; }

.todo-tick { flex: none; width: 20px; height: 20px; accent-color: #8a2be2; cursor: pointer; }

.todo-title {
    flex: 1;
    font-size: 0.98rem;
    line-height: 1.45;
    cursor: text;
    unicode-bidi: plaintext;
    /* Long unbroken links pasted as titles must not widen the page. */
    overflow-wrap: anywhere;
}
.todo-title:focus-visible { outline: 2px solid #8a2be2; outline-offset: 3px; border-radius: 4px; }

.todo.done { opacity: 0.55; }
.todo.done .todo-title { text-decoration: line-through; text-decoration-color: rgba(255,255,255,0.3); }

.todo-edit { flex: 1; }
.todo-actions { flex: none; display: flex; gap: 4px; align-items: center; }

.todo-detail {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 4px 6px 4px;
    margin-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.todo-details { width: 100%; resize: vertical; line-height: 1.5; }

.attachments { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px; }

.attachment {
    display: flex;
    align-items: center;
    gap: 8px;
    background: rgba(0, 0, 0, 0.25);
    border-radius: 8px;
    padding: 6px 10px;
    font-size: 0.88rem;
    max-width: 100%;
}
.attachment a { color: #ffb6c1; overflow-wrap: anywhere; }
.attachment.image { flex-direction: column; align-items: flex-start; }

/* Standing in for a link that is still on its way. A quiet pulse rather than a
   spinner: it is one line of text, and a spinner per link would be a fidget. */
.attachment.skeleton {
    pointer-events: none;
    padding: 0;
    background: none;
}
.attachment.skeleton span {
    display: block;
    width: 132px;
    height: 29px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.07);
    animation: pulseSync 1.4s ease-in-out infinite;
}

@keyframes pulseSync {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
    .attachment.skeleton span { animation-duration: 4s; }
}

/* The pictures on a todo, in the same uniform grid the album uses. Narrower
   columns: a detail panel is not a gallery, and these are usually a photo of a
   receipt or a screenshot rather than something to linger over. */
.pic-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
    gap: 8px;
}
.pic-grid:empty { display: none; }

/* What a row is carrying, said before you open it. */
.carries { font-size: 0.8rem; margin-left: 3px; line-height: 1; }
.carries-count {
    margin-left: 4px;
    font-variant-numeric: tabular-nums;
    opacity: 0.75;
}

/* The thumbnail is a button, so it needs stripping back to being a picture. */
.thumb {
    padding: 0;
    border: none;
    background: none;
    box-shadow: none;
    border-radius: 8px;
    cursor: zoom-in;
    line-height: 0;
    display: block;
}
.thumb:hover { transform: none; }
.thumb:hover img { filter: brightness(1.08); }
.thumb:focus-visible { outline: 2px solid #8a2be2; outline-offset: 3px; }

.attachment img {
    max-width: min(320px, 100%);
    max-height: 320px;
    border-radius: 8px;
    display: block;
}

/* ---------------------------------------------------------------
   The notification switches.
   --------------------------------------------------------------- */

.settings-heading {
    margin: 24px 0 6px;
    font-size: 1.3rem;
    color: #fff;
    font-weight: 600;
}

.settings-lede {
    color: #a8b2d1;
    font-size: 0.9rem;
    line-height: 1.6;
    max-width: 62ch;
    margin: 0 auto 18px;
}

.settings-panel { text-align: left; margin-bottom: 16px; }

.settings-subheading {
    margin: 0 0 10px;
    font-size: 0.78rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #8479a0;
    font-weight: 600;
}

/* Was a footer box on every page; now it is the first thing in Settings, so it
   sits left with everything else rather than centring itself. */
.settings-panel .notify-box { margin-top: 0; align-items: flex-start; }
.settings-panel .notify-box .hint { text-align: left; max-width: 46ch; }

.notify-table {
    width: 100%;
    border-collapse: collapse;
    text-align: left;
}

.notify-table th, .notify-table td {
    padding: 10px 8px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    vertical-align: top;
}

.notify-table tr:first-child th {
    border-bottom-color: rgba(255, 255, 255, 0.18);
    font-size: 0.78rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #8479a0;
}

/* The two name columns are narrow and fixed; the description takes the rest. */
.notify-person { width: 92px; text-align: center; }
.notify-person span { display: block; color: #fff; text-transform: none; letter-spacing: 0; font-size: 0.95rem; }
.notify-person small { display: block; color: #6f6790; font-size: 0.7rem; letter-spacing: 0; text-transform: none; }
.notify-person small.none { color: #ffb86b; }

.notify-what { font-weight: normal; }
.notify-label { display: block; color: #d8d2e8; font-size: 0.95rem; }
.notify-what small { display: block; color: #8479a0; font-size: 0.8rem; line-height: 1.5; margin-top: 2px; }

.notify-table td { text-align: center; }
.notify-table input[type="checkbox"] {
    width: 18px;
    height: 18px;
    accent-color: #8a2be2;
    cursor: pointer;
}

.notify-tests { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }

/* ------------------------------------------------------------- contacts */

.contacts-body { display: flex; flex-direction: column; gap: 30px; text-align: left; }

.contact-section-heading {
    margin: 0 0 12px;
    font-size: 0.78rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #8479a0;
    font-weight: 600;
}

.contact-card {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    padding: 14px 16px;
    margin-bottom: 12px;
}

/* The one card that is actually them, rather than somebody they know. */
.contact-card.self { border-color: rgba(255, 182, 193, 0.35); }

.contact-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.contact-head h3 { margin: 0; font-size: 1.05rem; color: #fff; }
.contact-relation { color: #8479a0; font-size: 0.85rem; }
.contact-tools { margin-inline-start: auto; display: flex; gap: 4px; }

.contact-field {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 7px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.field-label {
    flex: none;
    width: 88px;
    color: #6f6790;
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.field-value { flex: 1; color: #d8d2e8; overflow-wrap: anywhere; margin: 0; }

/* A number or an address you can tap. It has to look the same as the field it
   replaced, so :visited is pinned too — otherwise ringing somebody once turns
   their number purple for good. */
a.field-value,
a.field-value:visited { color: #d8d2e8; text-decoration: none; }
a.field-value:hover,
a.field-value:focus-visible { color: #fff; text-decoration: underline; }

/* The button this page exists for. Kept quiet until you are near it. */
.copy { flex: none; }
.copy.copied { color: #64ffda; border-color: rgba(100, 255, 218, 0.5); opacity: 1; }

.contact-addresses { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }

.contact-address {
    background: rgba(0, 0, 0, 0.22);
    border-radius: 8px;
    padding: 8px 10px;
}
.address-head { display: flex; align-items: center; gap: 6px; }

/* An anchor that sits in a row of buttons. The size and the frame come from
   `a.ghost.tiny` alongside the real ones; only the parts an anchor does not
   get for free are here. */
.map-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
}

/* One height for every small control on a card, set by a *number* rather than
   left to `normal`. `normal` derives the line box from the metrics of whatever
   font ends up drawing the glyph, so a colour emoji and a text glyph in
   otherwise identical boxes come out different heights. A number does not
   depend on the glyph at all, which is the only way these four line up. */
.contact-card .ghost.tiny { line-height: 1.45; }
/* Pinned against the browser's own link colours: unstyled, this went blue and
   then stayed purple once opened, both of which vanish into the background.
   Same value the neighbouring buttons take from `.ghost`. */
.map-link, .map-link:visited { color: #d8d2e8; }
.map-link:hover, .map-link:focus-visible { color: #fff; }

.address-label { flex: 1; color: #ffb6c1; font-size: 0.85rem; font-weight: 600; }
.address-text { margin: 4px 0 0; color: #d8d2e8; white-space: pre-wrap; overflow-wrap: anywhere; }

.contact-form, .address-form {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}
.contact-form input, .address-form input {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    border-radius: 8px;
    padding: 9px 12px;
    font-family: inherit;
    font-size: 0.95rem;
    outline: none;
    box-sizing: border-box;
    flex: 1 1 160px;
}
.contact-form input:focus, .address-form input:focus { border-color: #8a2be2; }
/* The address itself is the long one, and wants a line of its own. */
.address-text-input { flex: 1 1 100%; }
.new-contact { margin-top: 10px; }

@media (max-width: 600px) {
    .field-label { width: 100%; }
    .contact-field { flex-wrap: wrap; }
}

/* -------------------------------------------------- the devices signed up */

.notify-devices { display: flex; flex-direction: column; gap: 22px; }

.device-group h4 {
    margin: 0 0 8px;
    font-size: 0.95rem;
    font-weight: 600;
    color: #fff;
}

.device-list { list-style: none; margin: 0 0 12px; padding: 0; display: flex; flex-direction: column; gap: 8px; }

.device {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(0, 0, 0, 0.22);
}

.device-what { flex: 1; min-width: 0; }
.device-label { display: block; color: #d8d2e8; font-size: 0.9rem; }
.device small { display: block; color: #8479a0; font-size: 0.78rem; margin-top: 2px; }

/* Square, so it reads as a close rather than as a word cut short. */
.device-forget {
    flex: none;
    width: 28px;
    height: 28px;
    padding: 0;
    line-height: 1;
    color: #8479a0;
}
.device-forget:hover:not(:disabled) { color: #ff9d9d; border-color: rgba(255, 120, 120, 0.6); }

/* Never reached, or failing. The one thing worth spotting in this list. */
.device.suspect { border-color: rgba(255, 184, 107, 0.45); }
.device.suspect small { color: #ffb86b; }

.device-group .danger { border-color: rgba(255, 120, 120, 0.4); color: #ff9d9d; }
.device-group .danger:hover:not(:disabled) { border-color: rgba(255, 120, 120, 0.75); }

@media (max-width: 600px) {
    .notify-table th, .notify-table td { padding: 8px 4px; }
    .notify-person { width: 64px; }
    .notify-what small { display: none; }
}


/* ---------------------------------------------------------------
   The kit.
   --------------------------------------------------------------- */

.kit-filters {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-top: 24px;
}

.kit-filter {
    background: rgba(255, 255, 255, 0.07);
    border: 1px solid rgba(255, 255, 255, 0.16);
    color: #d8d2e8;
    box-shadow: none;
    font-weight: normal;
    font-size: 0.9rem;
    padding: 7px 18px;
    border-radius: 999px;
}
.kit-filter:hover { background: rgba(255, 255, 255, 0.14); color: #fff; transform: none; }
.kit-filter.on { background: rgba(138, 43, 226, 0.35); border-color: #8a2be2; color: #fff; }

.kit-form input[type="text"] { flex: 1 1 200px; }

.kit-assignee {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    border-radius: 8px;
    padding: 9px 10px;
    font-family: inherit;
    font-size: 0.9rem;
}

.kit-panel { text-align: left; margin-top: 18px; }

.kit-head { display: flex; align-items: baseline; gap: 12px; min-height: 28px; }
.kit-head .counts { flex: 1; }

.kit-list { margin-top: 12px; }

.kit-item {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-left: 3px solid var(--priority-colour, transparent);
    border-radius: 10px;
    padding: 8px 10px;
    transition: transform 0.16s ease;
}

/* Priority is a stripe down the edge rather than a badge in the row: it has to
   be readable at a glance while scanning, and it is not what you tap.
 *
 * One declaration serves both the stripe on a row and the swatch in the legend
 * that explains it, so the two cannot drift apart — a legend that has quietly
 * stopped matching the list is worse than no legend.
 *
 * Only `important` is allowed to shout. The rest step down in order, which is
 * the point: what is loud should be the short list. */
.kit-item.important, .kit-legend-row.important { --priority-colour: #ff6b9d; }
.kit-item.normal, .kit-legend-row.normal { --priority-colour: #82aaff; }
.kit-item.optional, .kit-legend-row.optional { --priority-colour: #6a5f88; }
.kit-item.basic, .kit-legend-row.basic { --priority-colour: rgba(255, 255, 255, 0.07); }

.kit-item.optional { opacity: 0.85; }

/* The everyday things. Nobody but you sees these rows, and there are usually a
   lot of them, so they read as background rather than as a list to work
   through. */
.kit-item.basic { opacity: 0.72; }
.kit-item.basic .kit-title { font-size: 0.92rem; }
.kit-priority.basic { opacity: 0.55; }

.kit-item.dragging {
    border-color: #8a2be2;
    background: rgba(138, 43, 226, 0.18);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
    position: relative;
    z-index: 5;
    transition: none;
}
.kit-list.settling .kit-item { transition: none; }

@media (prefers-reduced-motion: reduce) {
    .kit-item { transition: none; }
}

.kit-row { display: flex; align-items: center; gap: 10px; }
.kit-tick { flex: none; width: 20px; height: 20px; accent-color: #8a2be2; cursor: pointer; }

.kit-title {
    flex: 1;
    font-size: 0.98rem;
    line-height: 1.45;
    cursor: text;
    unicode-bidi: plaintext;
    overflow-wrap: anywhere;
}
.kit-title:focus-visible { outline: 2px solid #8a2be2; outline-offset: 3px; border-radius: 4px; }

.kit-edit {
    flex: 1;
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    border-radius: 8px;
    padding: 7px 10px;
    font-family: inherit;
    font-size: 0.95rem;
    outline: none;
}
.kit-edit:focus { border-color: #8a2be2; }

/* Packed, not gone: struck through and dimmed, still there to change your mind
   about, and moved to the end of its own group by the view. */
.kit-item.packed { opacity: 0.5; }
.kit-item.packed .kit-title { text-decoration: line-through; text-decoration-color: rgba(255,255,255,0.35); }

.kit-actions { flex: none; display: flex; gap: 4px; align-items: center; }

/* Dropdowns rather than tap-to-cycle: three answers is one too many to guess
   your way around. Styled down to the size of the buttons they replaced. */
.kit-select {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: #d8d2e8;
    border-radius: 6px;
    padding: 3px 4px;
    font-family: inherit;
    font-size: 0.78rem;
    outline: none;
    cursor: pointer;
    max-width: 96px;
}
.kit-select:hover { border-color: rgba(255, 255, 255, 0.35); color: #fff; }
.kit-select:focus { border-color: #8a2be2; }
.kit-select option { background: #1a1030; color: #d8d2e8; }

.kit-who.nobody { opacity: 0.6; }

/* A secret is still an item on the list — same row, same tick, same place in
   the order. Only its words are missing.
 *
 * It used to say so with a cyan left border, which stopped working twice over:
 * that border now carries the priority, so a secret both lost its own colour
 * and had to compete with three others for attention. The hatch is the marker
 * instead — nothing else on this page uses one, so it reads as "sealed" at a
 * glance and costs the stripe nothing. */
.kit-item.secret {
    background-image: repeating-linear-gradient(
        135deg,
        rgba(100, 255, 218, 0.08) 0 5px,
        rgba(100, 255, 218, 0) 5px 11px
    );
    border-color: rgba(100, 255, 218, 0.38);
    /* After `border-color`, so the priority keeps its own stripe. */
    border-left-color: var(--priority-colour, transparent);
}

.kit-title.hidden-title {
    /* Teal rather than grey: it is sealed, not disabled. */
    color: #7fbfb2;
    font-style: italic;
    cursor: default;
    letter-spacing: 0.02em;
}
.kit-hide { filter: grayscale(1); opacity: 0.55; }
.kit-hide.on { filter: none; opacity: 1; }

.kit-secret-toggle { white-space: nowrap; }

/* Sits under the filters, quiet: it is a "let me look at my own pile" switch,
   not one of the three things the page is actually organised by. */
.kit-basics-toggle {
    margin-top: 12px;
    justify-content: center;
    width: 100%;
    font-size: 0.8rem;
    opacity: 0.75;
}
.kit-basics-toggle:hover { opacity: 1; }
.kit-priority.important { color: #ff6b9d; border-color: rgba(255, 107, 157, 0.4); }
.kit-priority.optional { opacity: 0.65; }
.kit-remove:hover { color: #ff6b9d; }

/* -------------------------------------------- a secret, no longer a secret
 *
 * A dialog rather than a positioned overlay, like the lightbox: showModal()
 * puts it in the browser's top layer, which is above everything on the page
 * without anybody having to pick a z-index and hope.
 */
.reveal {
    border: none;
    padding: 0;
    background: transparent;
    max-width: 100vw;
    max-height: 100dvh;
    margin: auto;
}
.reveal::backdrop { background: rgba(8, 3, 18, 0.72); backdrop-filter: blur(4px); }

.reveal-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 28px 32px;
    border-radius: 18px;
    background: rgba(30, 20, 55, 0.96);
    border: 1px solid rgba(255, 182, 193, 0.4);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
    text-align: center;
    animation: revealIn 0.42s cubic-bezier(0.2, 1.3, 0.4, 1);
}

.reveal-cheer { margin: 0; font-size: 2.6rem; line-height: 1; animation: revealPop 0.9s ease; }
.reveal-box h2 { margin: 0; font-size: 1.15rem; color: #ffb6c1; }
.reveal-who { margin: 0; font-size: 0.85rem; color: #a8b2d1; }

.reveal-names {
    list-style: none;
    margin: 4px 0 6px;
    padding: 0;
    display: grid;
    gap: 6px;
}
.reveal-names li {
    font-size: 1rem;
    color: #fff;
    background: rgba(255, 255, 255, 0.07);
    border-radius: 8px;
    padding: 6px 14px;
    overflow-wrap: anywhere;
}

@keyframes revealIn {
    from { opacity: 0; transform: scale(0.86) translateY(12px); }
    to { opacity: 1; transform: none; }
}
@keyframes revealPop {
    0% { transform: scale(0.4) rotate(-12deg); }
    60% { transform: scale(1.18) rotate(6deg); }
    100% { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .reveal-box, .reveal-cheer { animation: none; }
}

/* ------------------------------------------------- what the stripes mean
 *
 * Folded shut by default. Four levels are only obvious to whoever chose the
 * words, and the answer to that is a legend you can open once — not four
 * labels permanently taking up room in every row. */
.kit-legend {
    margin: 0 0 14px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.18);
}

.kit-legend summary {
    padding: 8px 12px;
    cursor: pointer;
    font-size: 0.8rem;
    color: #a8b2d1;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 8px;
}
.kit-legend summary::-webkit-details-marker { display: none; }
.kit-legend summary:hover { color: #fff; }
/* The triangle, drawn rather than inherited, so it turns with the panel in
   both writing directions. */
.kit-legend summary::before {
    content: "›";
    font-size: 1rem;
    line-height: 1;
    transition: transform 0.15s ease;
}
.kit-legend[open] summary::before { transform: rotate(90deg); }

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

.kit-legend-row {
    display: grid;
    grid-template-columns: 4px 1fr;
    gap: 10px;
    align-items: start;
}
/* The same stripe a row wears, stood on end and given room to be seen. */
.kit-legend-row::before {
    content: "";
    align-self: stretch;
    min-height: 2.2em;
    border-radius: 2px;
    background: var(--priority-colour);
}
.kit-legend-row strong {
    display: block;
    color: #fff;
    font-size: 0.85rem;
    font-weight: 600;
}
.kit-legend-row span { color: #8479a0; font-size: 0.78rem; line-height: 1.45; }

@media (prefers-reduced-motion: reduce) {
    .kit-legend summary::before { transition: none; }
}

@media (max-width: 600px) {
    .kit-panel { padding: 16px 12px; }
    /* The buttons wrap under the name rather than squeezing it to nothing. */
    .kit-row { flex-wrap: wrap; }
    .kit-title { flex: 1 1 100%; order: -1; padding-left: 30px; }
    .kit-actions { margin-left: auto; }
}


/* ---------------------------------------------------------------
   Flights on a meeting.
   --------------------------------------------------------------- */

/* The forecast. Read at a glance while looking at the kit, so it is a row of
   small columns rather than a table of numbers. */
.meeting-weather {
    margin: 18px 0;
    padding: 12px 14px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    background: rgba(0, 0, 0, 0.18);
}

.weather-days {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    justify-content: flex-start;
}

.weather-day {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    min-width: 58px;
}

.weather-when { font-size: 0.72rem; color: #8479a0; text-transform: uppercase; letter-spacing: 0.06em; }
.weather-date { font-size: 0.72rem; color: #6f6790; white-space: nowrap; font-variant-numeric: tabular-nums; }
.weather-sky { font-size: 1.3rem; line-height: 1.2; }
.weather-degrees { font-size: 0.82rem; color: #d8d2e8; font-variant-numeric: tabular-nums; }
.weather-rain { font-size: 0.7rem; color: #64b5ff; font-variant-numeric: tabular-nums; }

.meeting-weather .hint { text-align: left; margin: 0; }

.meeting-flights {
    margin: 18px 0;
    padding: 14px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    background: rgba(0, 0, 0, 0.18);
    text-align: left;
}

.flights { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }

.flight {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 12px;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.05);
}

.flight-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; }

/* ---------------------------------------------------------------
   Where we're staying. Deliberately the same shape as the flights panel
   above it — they answer the two halves of the same question.
   --------------------------------------------------------------- */

.meeting-stays {
    margin: 18px 0;
    padding: 14px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    background: rgba(0, 0, 0, 0.18);
    text-align: left;
}

.stays-heading {
    margin: 0 0 10px;
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #b9aed4;
}

.stays { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-direction: column; gap: 10px; }

.stay {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 12px;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.05);
    /* Which kind of place it is, read before any of the words are. */
    border-inline-start: 3px solid var(--stay-colour, rgba(255, 255, 255, 0.2));
}
.stay.airbnb  { --stay-colour: #ff6b81; }
.stay.hotel   { --stay-colour: #5aa5ff; }
.stay.friends { --stay-colour: #64ffda; }
.stay.other   { --stay-colour: #b9aed4; }

.stay-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; }
.stay-mark { font-size: 1rem; line-height: 1; }

.stay-name { color: #fff; font-size: 0.95rem; font-weight: 600; }
a.stay-name { text-decoration: none; border-bottom: 1px dashed rgba(255, 182, 193, 0.45); }
a.stay-name:hover { color: #ffb6c1; border-bottom-color: #ffb6c1; }

.stay-when {
    margin: 0;
    font-size: 0.82rem;
    color: #ffb6c1;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.stay-address, .stay-door {
    margin: 0;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 0.85rem;
    color: #d8d2e8;
}

.stay-label {
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #8479a0;
}

/* A door code is read off a screen at a door. It gets the room to be legible
   at arm's length, and the spacing so 0 and O are not the same shape. */
.stay-code {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 1rem;
    letter-spacing: 0.12em;
    color: #fff;
    background: rgba(255, 255, 255, 0.08);
    border-radius: 6px;
    padding: 2px 8px;
}

.stay-notes {
    margin: 2px 0 0;
    font-size: 0.85rem;
    line-height: 1.5;
    color: #b9aed4;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.stay-actions { display: flex; gap: 6px; margin-top: 6px; }

/* ------------------------------------------------------------ the form */

.stay-form {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.1);
}

/* Two to a line where there is room, stacked on a phone. */
.stay-form-row { display: flex; flex-wrap: wrap; gap: 8px; }
.stay-form-row > * { flex: 1 1 160px; min-width: 0; }

.stay-field { display: flex; flex-direction: column; gap: 4px; }
.stay-field > span {
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #8479a0;
}

.stay-form input, .stay-form select, .stay-form textarea { width: 100%; }
.stay-form-buttons { display: flex; gap: 8px; }

.flight-leg {
    font-size: 0.72rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #8479a0;
}

.flight-number {
    font-size: 1.05rem;
    font-weight: 600;
    color: #64ffda;
    text-decoration: none;
    letter-spacing: 0.02em;
}
.flight-number:hover { text-decoration: underline; }

.flight-airline { font-size: 0.85rem; color: #a8b2d1; }

/* Who is on it, changeable in place. Sits between the leg and the number, so
   the line reads "Out — Maria — LO156". */
.flight-who {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: #d8d2e8;
    border-radius: 6px;
    padding: 2px 4px;
    font-family: inherit;
    font-size: 0.78rem;
    outline: none;
    cursor: pointer;
}
.flight-who:hover { border-color: rgba(255, 255, 255, 0.35); color: #fff; }
.flight-who:focus { border-color: #8a2be2; }
.flight-who option { background: #1a1030; color: #d8d2e8; }

/* The times are what you scan this block for, so they read like data: aligned
   digits, and brighter than the label above them. */
.flight-when {
    margin: 0;
    font-size: 0.95rem;
    color: #d8d2e8;
    font-variant-numeric: tabular-nums;
}

.flight-problem { text-align: left; margin: 2px 0 0; }
.flight-actions { display: flex; gap: 6px; margin-top: 4px; }

.flight-form { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; align-items: center; }
.flight-form input, .flight-form select {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    border-radius: 8px;
    padding: 7px 10px;
    font-family: inherit;
    font-size: 0.88rem;
    outline: none;
}
.flight-form input:focus, .flight-form select:focus { border-color: #8a2be2; }
#flightNumber { flex: 0 1 110px; text-transform: uppercase; }
.meeting-flights .hint { text-align: left; }


/* ------------------------------------------------ the full-size picture view */

.lightbox {
    border: none;
    padding: 0;
    background: transparent;
    max-width: 100vw;
    max-height: 100dvh;
    width: 100vw;
    height: 100dvh;
    overflow: auto;
    /* Centres the picture, and lets the caption and button sit under it. */
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    color: #d8d2e8;
}

/* `display` has to switch with the open state, or the dialog is laid out even
   while closed — and a flex dialog ignores the browser's own hiding. */
.lightbox[open] { display: flex; }

.lightbox::backdrop {
    background: rgba(8, 3, 18, 0.94);
    backdrop-filter: blur(6px);
}

/* The picture springs back when a swipe was not meant. No `touch-action` here
   on purpose: setting it would stop the browser interfering with a drag, and
   would take pinch-to-zoom with it. */
.lightbox img { transition: transform 0.18s ease; }
.lightbox img.swiping { transition: none; }

@media (prefers-reduced-motion: reduce) {
    .lightbox img { transition: none; }
}

.lightbox img {
    max-width: 94vw;
    max-height: 78dvh;
    object-fit: contain;
    border-radius: 10px;
    cursor: zoom-in;
}

/* Tapping again drops the fit and shows the picture at its own size; the
   dialog scrolls, which is what looking closely at a photo actually needs. */
.lightbox.zoomed { justify-content: flex-start; align-items: flex-start; }
.lightbox.zoomed img { max-width: none; max-height: none; cursor: zoom-out; border-radius: 0; }

.lightbox-caption {
    margin: 0;
    font-size: 0.9rem;
    color: #a8b2d1;
    text-align: center;
    max-width: 90vw;
    unicode-bidi: plaintext;
}
.lightbox.zoomed .lightbox-caption, .lightbox.zoomed #lightboxClose { display: none; }

.link-form { display: flex; flex-wrap: wrap; gap: 6px; }
.link-form input { flex: 1 1 160px; font-size: 0.88rem; padding: 7px 10px; }

/*
 * The finished ones, and the button that reveals them.
 *
 * The button had no top margin, so it sat flush against the last todo and
 * read as part of the list rather than as something below it. The list itself
 * has `margin: 18px 0 0`, so this matches the gap above the list — the block
 * ends up evenly spaced top and bottom instead of hanging off the last row.
 */
#toggleDoneBtn { margin-top: 16px; }

.done-list { margin-top: 10px; }

.locked-panel { text-align: center; }
.locked-title { font-size: 1.1rem; color: #fff; margin: 0 0 8px; }
.locked-panel p:last-child { color: #8479a0; font-size: 0.9rem; margin: 0; }

@media (max-width: 600px) {
    .list-panel { padding: 16px 12px; }
    .list-panel-actions { margin-left: 0; width: 100%; }
    .attachment img { max-width: 100%; }
}


/* ===============================================================
   The single-page shell.

   One document now: a header that never moves, and views swapped
   underneath it. `body` scrolls, since the views are documents.
   =============================================================== */

body {
    display: block;
    height: auto;
    min-height: 100dvh;
    overflow-y: auto;
}

.app {
    position: relative;
    z-index: 10;
    width: min(94%, 780px);
    margin: 0 auto;
    padding: 18px 0 80px;
}

.site-header { text-align: center; }

.header-top {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

/* The toggle used to float in the card's corner; in a flex bar it just sits. */
.site-header .toggle-timer-btn { position: static; }
.site-header .lang-select { position: static; }

#siteNav { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }
#siteNav a.active { color: #ffb6c1; border-bottom-color: #ffb6c1; }

#mainTitle { margin: 6px 0 10px; }

/* The countdown: full size on the jar, one line everywhere else. */
#timer.compact {
    display: block;
    font-size: 1.15rem;
    letter-spacing: 1px;
    font-variant-numeric: tabular-nums;
    margin: 0;
    min-height: 0;
}
#timer.finished { color: #ff6b6b; text-shadow: 0 0 10px rgba(255, 107, 107, 0.8); }

.meeting-location {
    margin: 6px 0 0;
    color: #ffb6c1;
    font-size: 0.95rem;
    letter-spacing: 0.04em;
}

/* Views. `hidden` is what the router toggles; the rest is layout. */
.view[hidden] { display: none; }
.view { animation: viewIn 0.18s ease-out; }
.view.page { text-align: left; }

@keyframes viewIn {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .view { animation: none; }
}

/* The jar keeps its centred card feel without being a card. */
#viewJar { text-align: center; }

.note-occasion {
    margin: 22px 0 -8px;
    color: #ffb6c1;
    font-size: 0.9rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

/* --------------------------------------------------- meetings editor */

.meeting-form {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 12px;
    margin-top: 20px;
}
.meeting-form label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #8479a0;
}
.meeting-form label.wide { grid-column: 1 / -1; }
.meeting-form input, .meeting-form textarea {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    border-radius: 8px;
    padding: 9px 12px;
    font-family: inherit;
    font-size: 0.95rem;
    outline: none;
}
.meeting-form input:focus, .meeting-form textarea:focus { border-color: #8a2be2; }
.meeting-form-actions { grid-column: 1 / -1; display: flex; gap: 8px; align-items: center; }

.meetings { list-style: none; margin: 24px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }

.meeting {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-left: 3px solid #8a2be2;
    border-radius: 10px;
    padding: 14px 16px;
}
.meeting.past { opacity: 0.6; border-left-color: #5a5a70; }
.meeting.now { border-left-color: #64ffda; }

.meeting-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.meeting-head strong { color: #fff; font-size: 1.02rem; }
.meeting-phase {
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #8479a0;
    margin-left: auto;
}
.meeting-when { margin: 6px 0 0; font-size: 0.88rem; color: #a8b2d1; }
.meeting-description { margin: 8px 0 0; font-size: 0.9rem; color: #b3a8cd; }
.meeting-actions { display: flex; gap: 6px; margin-top: 12px; }

/* ------------------------------------------------------- the album */

.album-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 14px;
    margin-top: 24px;
}

.album-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    padding: 10px;
    color: #d8d2e8;
    box-shadow: none;
    font-weight: normal;
    text-align: center;
}
.album-card:hover { background: rgba(255, 255, 255, 0.1); transform: translateY(-2px); }
.album-card.upcoming { border-color: rgba(100, 255, 218, 0.4); }
.album-card strong { color: #fff; font-size: 0.95rem; }
/* The date and length are what you actually scan an album by, so they read
   as information rather than as a footnote. */
.album-when {
    font-size: 0.84rem;
    color: #ffb6c1;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.album-cover {
    aspect-ratio: 4 / 3;
    border-radius: 8px;
    overflow: hidden;
    background: rgba(0, 0, 0, 0.35);
    display: flex;
    align-items: center;
    justify-content: center;
}
.album-cover img { width: 100%; height: 100%; object-fit: cover; }
.album-cover-empty { font-size: 2rem; opacity: 0.5; }

.album-detail { text-align: left; }
.album-detail h2 { margin: 12px 0 4px; color: #fff; font-size: 1.2rem; }
.album-detail > p { margin: 0 0 10px; }

/* Same treatment inside a meeting: bigger than the body text, and coloured. */
#albumWhen {
    font-size: 1rem;
    color: #ffb6c1;
    font-weight: 600;
    letter-spacing: 0.02em;
    margin: 0 0 12px;
}

#albumDescription {
    font-size: 1rem;
    line-height: 1.6;
    color: #e8e2f5;
    font-style: italic;
    border-left: 2px solid rgba(255, 182, 193, 0.5);
    padding-left: 12px;
    unicode-bidi: plaintext;
}

/* A grid of equal frames. Pictures are squares of the same size whatever shape
   they were taken in; a written excerpt is a different kind of thing, so it
   spans the row rather than pretending to be a tile. */
.album-media {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 10px;
    margin: 18px 0;
    align-items: start;
}

/* A tile is a `figure` on the card panel and a `button` in the album, and a
   `figure` carries the browser's own `margin: 1em 40px`. Inside a 120px grid
   cell that leaves almost nothing, which is exactly how the card's pictures
   came to be a quarter the size of the same pictures everywhere else. */
.media-tile { margin: 0; }

.album-tile,
.pic-tile {
    position: relative;
    aspect-ratio: 1;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 10px;
    overflow: hidden;
    background: rgba(0, 0, 0, 0.3);
    box-shadow: none;
    cursor: zoom-in;
    display: block;
}

.album-tile:hover, .pic-tile:hover { transform: none; border-color: rgba(255, 182, 193, 0.5); }
.album-tile:focus-visible, .pic-tile:focus-visible { outline: 2px solid #8a2be2; outline-offset: 2px; }

.album-tile img, .pic-tile img {
    width: 100%;
    height: 100%;
    /* Fills the frame and crops, so a portrait and a landscape sit side by
       side without one of them letterboxed. */
    object-fit: cover;
    display: block;
    opacity: 1;
    transition: opacity 0.25s ease;
}

.album-tile.loading img, .pic-tile.loading img,
.album-tile.skeleton img, .pic-tile.skeleton img { opacity: 0; }
.album-tile.skeleton, .pic-tile.skeleton { cursor: default; }
.album-tile.broken, .pic-tile.broken { border-style: dashed; }

/* Centred in the frame the picture is about to fill. */
.tile-spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 20px;
    height: 20px;
    margin: -10px 0 0 -10px;
    border: 2px solid rgba(255, 182, 193, 0.25);
    border-top-color: #ff6b81;
    border-radius: 50%;
    animation: spinSync 0.8s linear infinite;
}

.album-tile:not(.loading):not(.skeleton) .tile-spinner,
.pic-tile:not(.loading):not(.skeleton) .tile-spinner { display: none; }

@media (prefers-reduced-motion: reduce) {
    .tile-spinner { animation-duration: 2.4s; }
}

.tile-flag {
    position: absolute;
    top: 5px;
    inset-inline-end: 5px;
    color: #ffb6c1;
    font-size: 0.9rem;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}

.tile-reactions {
    position: absolute;
    bottom: 4px;
    inset-inline-start: 5px;
    font-size: 0.8rem;
    background: rgba(8, 3, 18, 0.7);
    border-radius: 999px;
    padding: 1px 7px;
}

.album-item.text {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    gap: 8px;
    background: rgba(0, 0, 0, 0.25);
    border-radius: 10px;
    padding: 12px 14px;
}
.album-item.text.promoted { outline: 1px solid rgba(255, 182, 193, 0.55); outline-offset: 2px; }
.album-item blockquote {
    margin: 0;
    font-size: 0.95rem;
    line-height: 1.55;
    color: #e8e2f5;
    unicode-bidi: plaintext;
}
.album-caption { margin: 0; font-size: 0.85rem; color: #a8b2d1; }
.album-by { font-size: 0.72rem; color: #8479a0; }
.album-item-actions { display: flex; flex-direction: column; gap: 8px; }
.album-item-buttons { display: flex; flex-wrap: wrap; gap: 6px; }
.album-promote.on { color: #ffb6c1; border-color: rgba(255, 182, 193, 0.5); }

/* The picture's own controls, shown under it once it is open — there is no
   room for them on a thumbnail, and nowhere better to put them. */
.lightbox-extras {
    display: flex;
    flex-direction: column;
    gap: 10px;
    align-items: center;
    max-width: 90vw;
}
.lightbox.zoomed .lightbox-extras { display: none; }

/* Big enough to hit with a thumb, and out of the way of the picture. */
.lightbox-arrow {
    position: fixed;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    background: rgba(8, 3, 18, 0.72);
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: #fff;
    font-size: 1.8rem;
    line-height: 1;
    padding: 10px 16px;
    border-radius: 999px;
    box-shadow: none;
}

.lightbox-arrow:hover {
    background: rgba(8, 3, 18, 0.92);
    border-color: #ffb6c1;
    transform: translateY(-50%);
}

.lightbox-arrow.prev { left: 10px; }
.lightbox-arrow.next { right: 10px; }

/* At full size the picture is what you are scrolling; arrows over it would sit
   in the way of the thing you zoomed in to look at. */
.lightbox.zoomed .lightbox-arrow { display: none; }

.album-add { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 10px; }
.album-add textarea {
    flex: 1 1 220px;
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    border-radius: 8px;
    padding: 9px 12px;
    font-family: inherit;
    font-size: 0.95rem;
    outline: none;
    resize: vertical;
}
.album-add textarea:focus { border-color: #8a2be2; }

@media (max-width: 600px) {
    .app { padding: 12px 0 60px; }
    #timer.compact { font-size: 1rem; }
    .album-item img { max-width: 100%; }
}


/* ---------------------------------------------------------------
   The admin tools, which are a set of tabs rather than one page.
   --------------------------------------------------------------- */

.admin-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin: 20px 0 4px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

.admin-tabs a {
    color: #8479a0;
    text-decoration: none;
    font-size: 0.9rem;
    padding: 8px 16px;
    border-radius: 8px 8px 0 0;
    /* Reserves the active border on every tab, so selecting one does not
       shift the row by a pixel. */
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

.admin-tabs a:hover { color: #d8d2e8; background: rgba(255, 255, 255, 0.05); }

.admin-tabs a.active {
    color: #ffb6c1;
    border-bottom-color: #ffb6c1;
    background: rgba(255, 255, 255, 0.06);
}

.admin-tabs a:focus-visible { outline: 2px solid #8a2be2; outline-offset: -2px; }


/* ---------------------------------------------------------------
   Reactions. Two people, so a name is worth more than a count.
   --------------------------------------------------------------- */

.reactions-holder { margin-top: 14px; }

.reactions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    justify-content: center;
    position: relative;
}

.album-item .reactions { justify-content: flex-start; }

button.reaction {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.16);
    color: #d8d2e8;
    box-shadow: none;
    font-weight: normal;
    font-size: 0.82rem;
    padding: 4px 10px;
    border-radius: 999px;
    line-height: 1.4;
}

button.reaction:hover { background: rgba(255, 255, 255, 0.16); transform: none; }

/* Yours, so you can see at a glance which ones pressing again would remove. */
button.reaction.mine {
    background: rgba(255, 107, 129, 0.22);
    border-color: rgba(255, 107, 129, 0.55);
    color: #fff;
}

button.reaction.add { padding: 4px 9px; color: #8479a0; }

/* All eight on one line, on the narrowest phone too. That only works if the
   emoji themselves are allowed to shrink, so the size is tied to the viewport
   rather than fixed — and `nowrap` makes a second row impossible rather than
   merely unlikely. */
.reaction-palette {
    display: flex;
    flex-wrap: nowrap;
    justify-content: center;
    gap: clamp(1px, 0.6vw, 4px);
    max-width: 96vw;
    background: rgba(20, 12, 34, 0.96);
    border: 1px solid rgba(255, 255, 255, 0.16);
    border-radius: 999px;
    padding: 5px 7px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}

.reaction-palette button.reaction {
    padding: 2px clamp(3px, 1.1vw, 7px);
    font-size: clamp(0.85rem, 3.4vw, 1.05rem);
    flex: 0 0 auto;
}

/* A log line that goes somewhere should look like it does. */
a.event-summary { color: #d8d2e8; text-decoration: none; display: block; }
a.event-link { border-bottom: 1px dashed rgba(255, 182, 193, 0.4); }
a.event-link:hover { color: #fff; border-bottom-color: #ffb6c1; }


/* --------------------------------------------------------------
   Meetings: the place under the name, and what has not happened yet.
   -------------------------------------------------------------- */

/* Only ever rendered when a meeting has a name of its own, so it never just
   repeats the title. */
.album-where {
    font-size: 0.78rem;
    color: #a8b2d1;
    letter-spacing: 0.02em;
}

.album-where-line {
    margin: 0 0 6px;
    font-size: 0.95rem;
    color: #a8b2d1;
}

.album-card.upcoming { border-color: rgba(100, 255, 218, 0.45); }
.album-card.now { border-color: #64ffda; }

/* Sat on the cover rather than under the title: it is a state, not a label. */
/* ---------------------------------------------------------------
   Hold to peek. One panel reached by a hover, a focus or a long press —
   see lib/peek.js, which owns when each of those counts.
   --------------------------------------------------------------- */

/* The panel lies over the whole card, so it needs something to lie over. */
.album-card { position: relative; }

.peek-panel {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow-y: auto;
    padding: 14px;
    border-radius: 12px;
    background: rgba(10, 4, 22, 0.94);
    color: #e8e2f5;
    font-size: 0.86rem;
    line-height: 1.5;
    text-align: center;
    /* A description is a paragraph somebody wrote; it keeps its shape. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    animation: peekIn 0.14s ease-out;
}

@keyframes peekIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .peek-panel { animation: none; }
}

/* The card lifts on hover, which under a panel reads as the panel wobbling. */
.album-card.peeking:hover { transform: none; }

/* Says there is something to hold for. Bottom-end, because the phase flag has
   the top-start corner and two marks in one corner is neither of them. */
.peek-mark {
    position: absolute;
    bottom: 6px;
    inset-inline-end: 6px;
    font-size: 0.72rem;
    line-height: 1;
    padding: 3px 5px;
    border-radius: 999px;
    background: rgba(8, 3, 18, 0.82);
    opacity: 0.85;
}

/* A long press on a phone otherwise offers to select the title or save the
   cover picture, on top of the panel that press was asking for. */
.album-card { -webkit-touch-callout: none; }

.album-flag {
    position: absolute;
    top: 6px;
    inset-inline-start: 6px;
    background: rgba(8, 3, 18, 0.82);
    color: #64ffda;
    border: 1px solid rgba(100, 255, 218, 0.5);
    border-radius: 999px;
    padding: 2px 9px;
    font-size: 0.68rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.album-cover { position: relative; }

.album-upcoming {
    display: inline-block;
    margin: 0 0 8px;
    color: #64ffda;
    border: 1px solid rgba(100, 255, 218, 0.5);
    border-radius: 999px;
    padding: 2px 12px;
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.meeting-form-title {
    grid-column: 1 / -1;
    margin: 0;
    font-size: 1.05rem;
    color: #fff;
    font-weight: 600;
}

.meeting-form { margin-bottom: 8px; }
.meeting-form .hint { grid-column: 1 / -1; text-align: start; }

.meeting-detail-actions { display: flex; gap: 8px; margin: 12px 0 4px; }

/* ============================================================== the calendar

   A month grid, six rows always. Five-row months exist and a grid that
   changes height as you page through the year makes everything below it jump —
   including the day you were reaching for. */

.cal-head {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    margin-bottom: 12px;
    flex-wrap: wrap;
}
.cal-month {
    margin: 0;
    font-size: 1.15rem;
    color: #fff;
    min-width: 8.5em;
    text-align: center;
}

.cal-weekdays, .cal-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
}
.cal-weekdays {
    margin-bottom: 4px;
    font-size: 0.72rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: #8479a0;
    text-align: center;
}

.cal-cell {
    /* A button so the whole square is one tap target and the keyboard can
       reach it — the day panel is the only way to read a busy day. */
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    min-height: 74px;
    padding: 4px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    box-shadow: none;
    font-weight: normal;
    text-align: start;
    overflow: hidden;
}
.cal-cell:hover { background: rgba(255, 255, 255, 0.09); }
.cal-cell.outside { opacity: 0.4; }
.cal-cell.today { border-color: rgba(255, 107, 129, 0.7); }
.cal-cell.chosen { background: rgba(255, 107, 129, 0.16); border-color: #ff6b81; }

.cal-date { font-size: 0.78rem; color: #d8d2e8; }
.cal-cell.today .cal-date { color: #ff6b81; font-weight: 600; }

.cal-chip {
    font-size: 0.62rem;
    line-height: 1.35;
    padding: 1px 4px;
    border-radius: 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    background: rgba(255, 107, 129, 0.28);
    color: #ffd9df;
}
/* The three kinds are told apart by colour, not by a word: there is no room
   for a word in a square this size. The panel underneath names them. */
.cal-chip.kind-meeting { background: rgba(100, 255, 218, 0.22); color: #b8fff0; }
.cal-chip.kind-flight { background: rgba(130, 170, 255, 0.24); color: #d6e2ff; }
.cal-chip.kind-card { background: rgba(255, 184, 107, 0.22); color: #ffd7ab; }
.cal-chip.kind-habit { background: rgba(180, 140, 255, 0.22); color: #ded0ff; }
.cal-chip.skipped { text-decoration: line-through; opacity: 0.55; }

/* Done is struck through rather than removed. A habit that vanished when it
   was ticked would leave a calendar that cannot tell "done" from "never
   asked for" — which is the one distinction the dots on the board are made of. */
.cal-chip.done { text-decoration: line-through; opacity: 0.5; }

.cal-more { font-size: 0.6rem; color: #8479a0; }

.cal-day { text-align: start; }
.cal-day-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.cal-day-head h3 { margin: 0; font-size: 1rem; color: #fff; }

.cal-entries { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }

.cal-entry {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 10px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-inline-start: 3px solid rgba(255, 107, 129, 0.75);
    border-radius: 8px;
}
.cal-entry.kind-meeting { border-inline-start-color: rgba(100, 255, 218, 0.75); }
.cal-entry.kind-flight { border-inline-start-color: rgba(130, 170, 255, 0.75); }
.cal-entry.kind-card { border-inline-start-color: rgba(255, 184, 107, 0.75); }
.cal-entry.kind-habit { border-inline-start-color: rgba(180, 140, 255, 0.75); }
.cal-entry.skipped { opacity: 0.6; }
.cal-entry.skipped .cal-title { text-decoration: line-through; }
.cal-entry.done { opacity: 0.55; }
.cal-entry.done .cal-title { text-decoration: line-through; }

.cal-when {
    flex: none;
    min-width: 3.4em;
    font-size: 0.82rem;
    color: #d8d2e8;
    font-variant-numeric: tabular-nums;
}
.cal-entry-body { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cal-title { color: #fff; font-size: 0.92rem; }
.cal-detail { color: #a8b2d1; font-size: 0.78rem; }
.cal-tags { color: #8479a0; font-size: 0.72rem; }
.cal-entry-actions { flex: none; display: flex; gap: 6px; align-items: center; }

.cal-form { text-align: start; display: grid; gap: 10px; }
.cal-form h3 { margin: 0; font-size: 1.05rem; color: #fff; }
.cal-form label { display: flex; flex-direction: column; gap: 4px; font-size: 0.82rem; color: #a8b2d1; }
.cal-form .private-toggle { flex-direction: row; align-items: center; gap: 8px; }
.cal-form-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.cal-form .hint { text-align: start; }

/* Two squares per row on a phone would be unreadable, but seven narrow ones
   with the chips hidden is worse: below this width the chips become dots. */
@media (max-width: 560px) {
    .cal-cell { min-height: 56px; }
    .cal-chip {
        font-size: 0;
        line-height: 0;
        height: 5px;
        border-radius: 999px;
        padding: 0;
    }
    .cal-more { font-size: 0.55rem; }
}

/* ------------------------------------------------ the unfinished features */

.feature-grid { display: grid; gap: 12px; text-align: start; }
.feature-row {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    padding-bottom: 10px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.feature-row:last-child { border-bottom: none; padding-bottom: 0; }
.feature-what { flex: 1; min-width: 12em; display: flex; flex-direction: column; }
.feature-what small { color: #8479a0; font-size: 0.75rem; }
.feature-switch {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.82rem;
    color: #d8d2e8;
}

/* ================================================================ the boards

   Columns scroll sideways rather than stacking. A board is a picture of where
   everything is, and a phone that shows one column at a time is a list — which
   is what the lists page already is. */

.board-kind {
    display: block;
    font-size: 0.62rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: #8479a0;
}

.board-panel { margin-top: 20px; }

.board-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.board-head h2 { margin: 0; font-size: 1.1rem; color: #fff; }
.board-head-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.board-columns {
    display: flex;
    gap: 12px;
    overflow-x: auto;
    padding-bottom: 8px;
    align-items: flex-start;
}

.board-column {
    flex: 0 0 260px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    padding: 12px;
    text-align: start;
}
/* The three that do something get a thread of colour at the top; a plain
   column deliberately gets none, because it means nothing in particular. */
.board-column.role-planning { border-top: 2px solid rgba(130, 170, 255, 0.6); }
.board-column.role-progress { border-top: 2px solid rgba(255, 184, 107, 0.6); }
.board-column.role-done { border-top: 2px solid rgba(100, 255, 218, 0.6); }

.board-column-head {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.board-column-head h3 { margin: 0; font-size: 0.95rem; color: #fff; flex: 1; min-width: 0; }

.column-role {
    font-size: 0.6rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    border-radius: 999px;
    padding: 1px 8px;
    border: 1px solid currentColor;
}
.column-role.role-planning { color: #82aaff; }
.column-role.role-progress { color: #ffb86b; }
.column-role.role-done { color: #64ffda; }

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

.board-card {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 8px;
    padding: 8px 10px;
    display: grid;
    gap: 6px;
}
.board-card.done { opacity: 0.6; }
.board-card.done .board-card-title { text-decoration: line-through; }

.board-card-title {
    background: none;
    border: none;
    box-shadow: none;
    padding: 0;
    font-weight: normal;
    font-size: 0.9rem;
    color: #fff;
    text-align: start;
}
.board-card-title:hover { color: #ff6b81; background: none; }

.board-card-facts { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.board-card-facts .due {
    font-size: 0.7rem;
    border-radius: 999px;
    padding: 1px 8px;
    background: rgba(255, 255, 255, 0.08);
    color: #a8b2d1;
}
/* The whole reason a due date is on a closed card: the late ones have to
   catch the eye before they are read. */
.board-card-facts .due.soon { background: rgba(255, 184, 107, 0.2); color: #ffb86b; }
.board-card-facts .due.late { background: rgba(255, 107, 129, 0.24); color: #ff9aab; }
.board-card-facts .who { font-size: 0.7rem; color: #d8d2e8; }
.board-card-facts .carries { font-size: 0.68rem; color: #8479a0; }
.board-card-facts .carries.blocked { color: #ff9aab; }

.board-card-move { display: flex; gap: 6px; justify-content: space-between; }

.board-add { display: flex; gap: 6px; }
.board-add input { flex: 1; min-width: 0; }

/* -------------------------------------------------------------- one card */

.card-panel { text-align: start; }
.card-panel-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.card-panel-head h3 { margin: 0; font-size: 1.05rem; color: #fff; }
/* The pictures and the button that adds them are one thing, so they sit
   together: the shared album grid's 18px bottom margin left them looking like
   two unrelated parts of the panel. */
.card-panel .album-media { margin: 0 0 10px; }

.card-panel h4 {
    margin: 18px 0 8px;
    font-size: 0.8rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: #8479a0;
}

.card-meta { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; }
.card-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.78rem; color: #a8b2d1; }
.card-field small { color: #8479a0; font-size: 0.7rem; }

/* A bare `select` inherits the shared field rule, which is sized for something
   you type a sentence into. These are one word wide and sit in a row of them. */
.card-field select {
    background-color: rgba(0, 0, 0, 0.35);
    border-color: rgba(255, 255, 255, 0.2);
    color: #fff;
    padding: 7px 10px;
    font-size: 0.9rem;
    cursor: pointer;
}

/*
 * The two boxes you have to save by hand.
 *
 * `align-items: start` matters: the default stretches every child to the width
 * of the widest, which is what made a Save button wider than the box it saves.
 * The box is told to fill the row; the button is left at its own size.
 */
.card-details, .card-blocked {
    display: flex;
    flex-direction: column;
    align-items: start;
    gap: 8px;
    margin-top: 14px;
}
.card-details textarea, .card-blocked textarea {
    width: 100%;
    resize: vertical;
}

/* Nothing on screen said whether what you typed was stored. Now an unsaved
   change is visible in both places at once — the box you changed and the
   button that would save it — and the button is dead when there is nothing to
   save, so a disabled Save means "already saved". */
textarea.dirty { border-color: #ffb86b; background: rgba(255, 184, 107, 0.07); }
button.ghost.dirty {
    color: #ffb86b;
    border-color: rgba(255, 184, 107, 0.55);
    background: rgba(255, 184, 107, 0.12);
}
button.ghost.dirty:hover { background: rgba(255, 184, 107, 0.2); color: #ffd0a0; }

/* The tick on a checklist row and on a habit.
 *
 * It is a `button` rather than a checkbox because it carries a ✓ of its own and
 * has to be tappable at the size of a thumb. That means it inherits the big
 * pink button at the top of this sheet unless it is told otherwise — which is
 * how a tick came to render as a pink slab wider than the row it sits in. */
.check {
    flex: none;
    width: 22px;
    height: 22px;
    padding: 0;
    display: grid;
    place-items: center;
    font-size: 0.85rem;
    line-height: 1;
    font-weight: bold;
    color: #64ffda;
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 6px;
    box-shadow: none;
}
/* The inherited hover scales by 1.05, which on a 22px box next to text reads
   as a wobble rather than as a response. */
.check:hover { background: rgba(138, 43, 226, 0.35); border-color: #8a2be2; transform: none; }
.check:focus-visible { outline: 2px solid #8a2be2; outline-offset: 2px; }

.habit.done > .check,
.check-row.done .check {
    background: rgba(100, 255, 218, 0.15);
    border-color: rgba(100, 255, 218, 0.5);
}

.checks { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.check-row { display: flex; align-items: center; gap: 8px; font-size: 0.88rem; color: #d8d2e8; }
.check-row.done { opacity: 0.6; }
.check-row.done span { text-decoration: line-through; }
.check-row span { flex: 1; min-width: 0; }

/* "One more thing" sat flush against the last item, reading as part of the
   list rather than as the way to add to it. */
.card-checks .new-todo { margin-top: 14px; }

/* The heading turns when there is something in the box, so a card that is
   stuck says so from the top of the panel rather than only from its own text. */
.card-panel h4.is-blocked { color: #ff9aab; }

.card-comments { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }

/*
 * One comment: who, then what, with the time tucked bottom-right and the bin
 * top-right. The old row stacked a full-width "Amit · 14 Aug 19:20" line, the
 * text, and a full-size Remove button underneath — three rows of furniture for
 * one sentence of talk.
 */
.card-comment {
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    padding: 6px 10px 4px;
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    column-gap: 8px;
}
/* Who spoke and what they said were the same colour, so a run of comments read
   as one block of text with names scattered through it. The name is the label
   and takes the accent; the words are what you are reading and take the light. */
.card-comment .who {
    grid-column: 1;
    color: #ffb6c1;
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}
.card-comment p {
    grid-column: 1 / -1;
    margin: 2px 0 0;
    color: #ebe7f5;
    font-size: 0.92rem;
    line-height: 1.45;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
.card-comment .when {
    grid-column: 1 / -1;
    justify-self: end;
    color: #6f6790;
    font-size: 0.68rem;
}

/* A bin rather than the word "Remove": it sits on every one of your own
   comments, and the word was as wide as some of the messages.
 *
 * It wears `ghost tiny` like the kit's own remove button, so it reads as a
 * button rather than as a stray glyph — and it stays neutral on hover. Red is
 * for something that has gone wrong, and removing your own comment has not. */
.card-comment .comment-drop {
    grid-column: 2;
    grid-row: 1;
    width: 26px;
    height: 24px;
    padding: 0;
    display: grid;
    place-items: center;
    font-size: 0.72rem;
    line-height: 1;
    opacity: 0.75;
}
.card-comment .comment-drop:hover { opacity: 1; transform: none; }

/* ---------------------------------------------------------------- habits */

.habits-panel { text-align: start; }
.habit-list { display: grid; gap: 8px; }

.habit {
    display: flex;
    align-items: center;
    gap: 10px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 8px;
    padding: 8px 10px;
}
.habit.done { border-color: rgba(100, 255, 218, 0.4); }
.habit.done .habit-title { text-decoration: line-through; opacity: 0.7; }

.habit-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: none;
    border: none;
    box-shadow: none;
    padding: 0;
    font-weight: normal;
    text-align: start;
}
/* The `background` was reset here but not the `transform`, so the inherited
   button's 5% grow was still running: pointing at a habit swelled its text and
   pushed it over the tick beside it. A row in a list does not respond by
   getting bigger — it lights up. */
.habit-body:hover { background: none; transform: none; }
.habit-body:hover .habit-title { color: #ffb6c1; }
.habit-body:focus-visible { outline: 2px solid #8a2be2; outline-offset: 2px; border-radius: 6px; }
.habit-title { color: #fff; font-size: 0.92rem; }
.habit-body small { color: #8479a0; font-size: 0.74rem; }

/* The point of the whole habits board: a row of dots you can read at a
   glance without counting anything. */
.habit-dots { margin: 14px 0 6px; }
.habit-dot-strip { display: flex; flex-wrap: wrap; gap: 4px; }
.habit-dot {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: rgba(255, 107, 129, 0.55);
}
.habit-dot.done { background: #64ffda; }
.habit-dot.current { outline: 2px solid rgba(255, 255, 255, 0.5); outline-offset: 1px; }

.habit-settings { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }

@media (max-width: 560px) {
    .board-column { flex-basis: 82vw; }
}

/* ------------------------------------------------------- marks in the nav

   Two of them, and the difference is the point: 🔒 is a page that is one
   person's by design, 🚧 is one that simply is not finished. The second
   disappears by itself once the page is shared. */

.nav-mark {
    font-size: 0.7em;
    margin-inline-start: 4px;
    /* Sits with the text rather than on the baseline, where a glyph this size
       drags the whole link down by a pixel and the row stops looking level. */
    vertical-align: 0.08em;
    opacity: 0.85;
}

/* ---------------------------------------------------------- video embeds

   A link to YouTube, TikTok, Instagram or Facebook gets a player underneath
   it. Nothing is fetched from any of them until the button is pressed — see
   lib/embeds.js for why that matters more than the convenience does. */

.attachment.has-embed {
    flex-direction: column;
    align-items: stretch;
    flex-basis: 100%;
}

.embed {
    position: relative;
    margin-top: 8px;
    width: 100%;
    max-width: 480px;
    border-radius: 10px;
    overflow: hidden;
    background: rgba(0, 0, 0, 0.35);
    border: 1px solid rgba(255, 255, 255, 0.12);
}

/* The two shapes these places actually publish. A reel forced into 16:9 is
   letterboxed to a sliver; a talk forced into 9:16 wastes most of a phone. */
.embed.shape-wide { aspect-ratio: 16 / 9; }
.embed.shape-tall { aspect-ratio: 9 / 16; max-width: 320px; }

.embed iframe {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}

.embed-play {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 16px;
    background: none;
    border: none;
    box-shadow: none;
    color: #d8d2e8;
    font-weight: normal;
    text-align: center;
}
.embed-play:hover { background: rgba(255, 255, 255, 0.06); color: #fff; transform: none; }

.embed-label { font-size: 0.95rem; }
.embed-play small { font-size: 0.72rem; color: #8479a0; max-width: 26ch; line-height: 1.4; }

/* A hint of each place's own colour, enough to tell them apart at a glance
   without pasting four logos into the page. */
.embed.provider-youtube { border-color: rgba(255, 0, 51, 0.35); }
.embed.provider-tiktok { border-color: rgba(105, 201, 208, 0.35); }
.embed.provider-instagram { border-color: rgba(221, 42, 123, 0.35); }
.embed.provider-facebook { border-color: rgba(24, 119, 242, 0.35); }
