/* Sepiabox visual identity (docs/architecture.md): colours and typefaces. Desktop first; phones only need to work. */

/* Typefaces are served from wwwroot/fonts (SIL Open Font License, see the OFL.txt files), not a font CDN (D-040). */
@font-face {
    font-family: "Bricolage Grotesque";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("fonts/bricolage-grotesque/bricolage-grotesque-latin-500-normal.woff2") format("woff2");
}

@font-face {
    font-family: "Bricolage Grotesque";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("fonts/bricolage-grotesque/bricolage-grotesque-latin-700-normal.woff2") format("woff2");
}

/* ExtraBold for the wordmark and the big numbers. */
@font-face {
    font-family: "Bricolage Grotesque";
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src: url("fonts/bricolage-grotesque/bricolage-grotesque-latin-800-normal.woff2") format("woff2");
}

@font-face {
    font-family: "Figtree";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/figtree/figtree-latin-400-normal.woff2") format("woff2");
}

@font-face {
    font-family: "Figtree";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("fonts/figtree/figtree-latin-500-normal.woff2") format("woff2");
}

@font-face {
    font-family: "Figtree";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("fonts/figtree/figtree-latin-600-normal.woff2") format("woff2");
}

@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/ibm-plex-mono/ibm-plex-mono-latin-400-normal.woff2") format("woff2");
}

@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("fonts/ibm-plex-mono/ibm-plex-mono-latin-500-normal.woff2") format("woff2");
}
/* The design tokens (D-119, D-120; the reference is docs/design/redesign/css/tokens.css). The brand's colours keep their names in the
   comments: sepia is the brand, cyanotype the actions, developer (the darkest brown) the text, silver paper the background, mat the lines.
   Every text pair is at least 4.5:1 and every control border at least 3:1, in both themes (TokenContrastTests checks the values). The dark
   theme follows the system setting; [data-theme] is for a switch a later story may add. */
:root {
    color-scheme: light dark;

    /* type */
    --font-display: "Bricolage Grotesque", system-ui, sans-serif;
    --font-body: "Figtree", system-ui, sans-serif;
    --font-mono: "IBM Plex Mono", ui-monospace, monospace;
    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-md: 1rem;
    --text-lg: 1.125rem;
    --text-xl: 1.375rem;
    --text-2xl: 1.75rem;
    --text-3xl: 2.25rem;

    /* space, on a 4-point scale */
    --s-1: 4px;
    --s-2: 8px;
    --s-3: 12px;
    --s-4: 16px;
    --s-5: 24px;
    --s-6: 32px;
    --s-7: 48px;
    --s-8: 64px;

    /* shape */
    --r-sm: 6px;
    --r-md: 10px;
    --r-lg: 16px;
    --r-pill: 999px;
    --ring: 0 0 0 3px var(--focus);

    /* The sticky header's height, for anchored targets (#106): 0 where the header is not sticky (it wraps there, see .top). */
    --header-h: 0px;
    --content-max: 1440px;

    /* light */
    --bg: #eeece7; /* silver paper */
    --surface: #ffffff;
    --surface-2: #f7f5f1;
    --border: #ddd6cb; /* mat, lightened for hairlines */
    --control-border: #8c8174;
    --text: #231a14; /* developer */
    --text-2: #5c5048;
    --text-3: #6f6358;
    --accent: #1f4c74; /* cyanotype: actions */
    --accent-hover: #163a59;
    --accent-contrast: #ffffff;
    --accent-soft: #e3eaf1;
    --brand: #9a5a2c; /* sepia */
    --brand-soft: #f2e6da;
    --brand-ink: #7a4520;
    --danger: #a1301f;
    --success: #2d6a30;
    --warning: #8a5300;
    --focus: #1f4c7466;
    --stage: #16110d; /* the dark stage a photo is shown on, in both themes */
    --shadow-sm: 0 1px 2px rgb(35 26 20 / 0.08);
    --shadow-md: 0 4px 14px rgb(35 26 20 / 0.12);
    --shadow-pop: 0 12px 32px rgb(35 26 20 / 0.18), 0 2px 6px rgb(35 26 20 / 0.1);

    /* The names the pages already use, on the new tokens, so every page follows the theme with no markup change. */
    --sepia: var(--brand);
    --cyanotype: var(--accent);
    --cyanotype-hover: var(--accent-hover);
    --developer: var(--text);
    --silver-paper: var(--bg);
    --mat: var(--border);
    --text-secondary: var(--text-2);
    --chip: var(--accent-soft);
    --white: var(--surface);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #15110e;
        --surface: #1e1813;
        --surface-2: #261f19;
        --border: #352c24;
        --control-border: #7c7062;
        --text: #f2ede5;
        --text-2: #b9ada0;
        --text-3: #a09486;
        --accent: #8fbce3;
        --accent-hover: #b3d3ef;
        --accent-contrast: #0e2236;
        --accent-soft: #22364a;
        --brand: #e0a36b;
        --brand-soft: #33261b;
        --brand-ink: #e8b887;
        --danger: #f0907f;
        --success: #8bd48a;
        --warning: #f2b84b;
        --focus: #8fbce399;
        --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);
        --shadow-md: 0 4px 14px rgb(0 0 0 / 0.45);
        --shadow-pop: 0 12px 32px rgb(0 0 0 / 0.6), 0 0 0 1px #352c24;
    }
}

:root[data-theme="dark"] {
    --bg: #15110e;
    --surface: #1e1813;
    --surface-2: #261f19;
    --border: #352c24;
    --control-border: #7c7062;
    --text: #f2ede5;
    --text-2: #b9ada0;
    --text-3: #a09486;
    --accent: #8fbce3;
    --accent-hover: #b3d3ef;
    --accent-contrast: #0e2236;
    --accent-soft: #22364a;
    --brand: #e0a36b;
    --brand-soft: #33261b;
    --brand-ink: #e8b887;
    --danger: #f0907f;
    --success: #8bd48a;
    --warning: #f2b84b;
    --focus: #8fbce399;
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);
    --shadow-md: 0 4px 14px rgb(0 0 0 / 0.45);
    --shadow-pop: 0 12px 32px rgb(0 0 0 / 0.6), 0 0 0 1px #352c24;
}

@media (min-width: 960px) {
    :root {
        --header-h: 65px;
    }
}

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

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

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-body);
    font-size: var(--text-md);
    line-height: 1.55;
}

/* Anchored targets and the elements a script scrolls to are not hidden under the sticky header (#106). */
:target,
[id] {
    scroll-margin-top: calc(var(--header-h) + var(--s-3));
}

/* A theme chosen on the page (S-070) also sets native controls and scrollbars, which otherwise follow the system. */
:root[data-theme="light"] {
    color-scheme: light;
}

:root[data-theme="dark"] {
    color-scheme: dark;
}

/* Every interactive element shows where the keyboard is. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation: none !important;
        transition: none !important;
        scroll-behavior: auto !important;
    }
}

a {
    color: var(--cyanotype);
}

a:hover {
    color: var(--cyanotype-hover);
}

h1, h2, h3 {
    font-family: var(--font-display);
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.01em;
}

/* Base controls (S-066): one height, one radius, borders that reach 3:1. Rules for a class below this block win. */
input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="range"], [type="file"], [type="submit"], [type="button"]),
select,
textarea {
    min-height: 40px;
    padding: 0 var(--s-3);
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--control-border);
    border-radius: var(--r-sm);
    font: inherit;
}

textarea {
    padding: var(--s-2) var(--s-3);
}

input::placeholder,
textarea::placeholder {
    color: var(--text-3);
}

input:not([type="checkbox"], [type="radio"]):focus-visible,
select:focus-visible,
textarea:focus-visible {
    border-color: var(--accent);
    outline: none;
    box-shadow: var(--ring);
}

input[type="checkbox"],
input[type="radio"] {
    width: 18px;
    height: 18px;
    margin: 0;
    accent-color: var(--accent);
}

button {
    min-height: 40px;
    padding: 0 var(--s-4);
    background: var(--surface);
    color: var(--accent);
    border: 1px solid var(--control-border);
    border-radius: var(--r-md);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
}

button:hover:not(:disabled) {
    background: var(--accent-soft);
}

button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* The card: a surface with a hairline and a soft shadow. */
.card {
    padding: var(--s-5);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
}

h1:focus {
    outline: none;
}

/* An author display rule (flex, grid) beats the hidden attribute, so an element made visible by a script only would show without it. */
[hidden] {
    display: none !important;
}

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

.mono {
    font-family: var(--font-mono);
}

.muted {
    color: var(--text-secondary);
}

.small {
    font-size: 0.875rem;
}

/* The header (S-066): sticky, one row where it fits, pill links with the current page marked. */
.top {
    /* Relative, not static, so z-index applies: the "More" popover must paint above positioned page content (face circles, badges). */
    position: relative;
    z-index: 30;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2) var(--s-4);
    /* The header's row lines up with the content below it: the same maximum width and the same 24px gutter as main. */
    padding: var(--s-3) max(var(--s-5), calc((100% - var(--content-max)) / 2 + 24px));
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    backdrop-filter: saturate(1.4) blur(8px);
    border-bottom: 1px solid var(--border);
}

/* Sticky only where the header is one row (960 px and up): below that it wraps to two or three rows, and a sticky header of that height
   would cover a quarter of a phone's screen (and half of it with "More" open). There it scrolls away with the page. */
@media (min-width: 960px) {
    .top {
        position: sticky;
        top: 0;
    }
}

/* The mark is dark brown on a transparent ground: on the dark header it sits on a light rounded square, as in the mockups. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .brand img {
        padding: 3px;
        background: #f2ede5;
        border-radius: var(--r-sm);
        box-sizing: content-box;
    }
}

:root[data-theme="dark"] .brand img {
    padding: 3px;
    background: #f2ede5;
    border-radius: var(--r-sm);
    box-sizing: content-box;
}

.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-right: var(--s-2);
    color: var(--text);
    text-decoration: none;
}

/* The logo keeps its colours: the links' hover would turn "box" blue. */
.brand:hover {
    color: var(--developer);
}

/* The wordmark (sepiabox-brand): lowercase, ExtraBold, tight, "sepia" in the brand colour and "box" in developer. */
.wordmark {
    font-family: var(--font-display);
    font-size: 1.375rem;
    font-weight: 800;
    letter-spacing: -0.045em;
    line-height: 1;
}

.wordmark-sepia {
    color: var(--sepia);
}

main {
    max-width: var(--content-max);
    margin: 0 auto;
    padding: 16px 24px 48px;
}

.month-head {
    display: flex;
    align-items: baseline;
    gap: var(--s-3);
    margin: var(--s-6) 0 var(--s-3);
}

.month h2 {
    margin: 0;
    font-size: var(--text-xl);
}

/* The number of photos under a month's heading (S-068). */
.month-head .n {
    color: var(--text-2);
    font-size: var(--text-sm);
}

/* The occasions of a search by part of a photo are headed like months; the heading above them is quieter. */
.month h3 {
    margin: 32px 0 12px;
    font-family: var(--font-display);
    font-size: 1.25rem;
    line-height: 1.2;
}

.region-heading {
    margin: 16px 0 0;
    font-size: 1rem;
    font-weight: 600;
}

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
    gap: var(--s-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* A tile (S-068): rounded, the thumbnail zooms a little on hover, and a shade with the date comes up on hover and keyboard focus. */
.thumb {
    position: relative;
    display: block;
    aspect-ratio: 1;
    overflow: hidden;
    background: var(--surface-2);
    border-radius: var(--r-md);
    isolation: isolate;
}

.thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.25s ease;
}

.thumb::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, transparent 55%, rgb(0 0 0 / 0.55));
    opacity: 0;
    transition: opacity 0.15s;
    pointer-events: none;
}

.thumb .meta {
    position: absolute;
    right: var(--s-3);
    bottom: var(--s-2);
    left: var(--s-3);
    z-index: 2;
    color: #fff;
    font: 500 var(--text-xs) var(--font-mono);
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.15s, transform 0.15s;
}

.thumb:hover img {
    transform: scale(1.04);
}

.thumb:hover::after,
.thumb:focus-visible::after,
.thumb:hover .meta,
.thumb:focus-visible .meta {
    opacity: 1;
    transform: none;
}

.thumb:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
}

/* A finger has no hover: the date is not shown over the tile on a touch screen. */
@media (hover: none) {
    .thumb::after,
    .thumb .meta {
        opacity: 0;
    }
}

@media (max-width: 600px) {
    .grid {
        grid-template-columns: repeat(3, 1fr);
        gap: 3px;
    }

    .thumb {
        border-radius: var(--r-sm);
    }
}

.placeholder {
    display: grid;
    place-items: center;
    height: 100%;
    padding: 8px;
    font-size: 0.8rem;
    text-align: center;
}

.badge {
    position: absolute;
    right: 6px;
    bottom: 6px;
    padding: 1px 6px;
    background: var(--stage);
    color: #fff;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    border-radius: 2px;
}

.play {
    position: absolute;
    top: 50%;
    left: 50%;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    margin: -20px 0 0 -20px;
    /* The same brown, for browsers without color-mix. */
    background: rgb(35 26 20 / 0.7);
    color: #fff;
    border-radius: 50%;
    pointer-events: none;
}

.play svg {
    fill: currentColor;
}

/* The status line is always in the page, so a screen reader announces what ask.js puts in it (D-101); it is only clipped away
   while there is nothing to say. */
.ask-status {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
}

.ask.busy .ask-status,
.ask.failed .ask-status {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    white-space: normal;
    flex-basis: 100%;
    color: var(--cyanotype);
}

.ask.busy .ask-status::before {
    content: "";
    display: inline-block;
    width: 0.9em;
    height: 0.9em;
    margin-right: 0.5em;
    vertical-align: -0.1em;
    border: 2px solid currentcolor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: ask-spin 0.9s linear infinite;
}

@keyframes ask-spin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .ask.busy .ask-status::before {
        animation: none;
    }
}

.ask.failed .ask-status {
    color: var(--sepia);
}

.ask button[aria-disabled="true"] {
    opacity: 0.6;
    cursor: progress;
}

.button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* The question box and the filters (S-067, D-123): one panel; the question is a pill with its button inside, the filters are dropdown
   chips (details), toggle pills and one apply button. Everything works without JavaScript. */
.panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
}

.finder {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-3);
    padding: var(--s-4);
    margin: var(--s-2) 0 var(--s-4);
}

.ask {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--s-2);
    padding: 6px 6px 6px var(--s-4);
    background: var(--surface-2);
    border: 1px solid var(--control-border);
    border-radius: var(--r-pill);
}

.ask:focus-within {
    border-color: var(--accent);
    box-shadow: var(--ring);
}

.ask .icon {
    flex: none;
    width: 20px;
    height: 20px;
    stroke: var(--text-3);
    fill: none;
    stroke-width: 2;
}

.ask .ask-field {
    flex: 1;
    min-width: 0;
    display: block;
}

.ask input[type="search"] {
    width: 100%;
    min-width: 0;
    border: 0;
    background: none;
    color: var(--text);
    font: 400 var(--text-lg) var(--font-body);
    padding: 8px 0;
    outline: 0;
}

.ask input[type="search"]::placeholder {
    color: var(--text-3);
}

.ask .button {
    border-radius: var(--r-pill);
    min-height: 40px;
}

.finder .hint {
    margin: 0;
    font-size: var(--text-sm);
}

/* While a question is being understood, or failed, the status takes the hint's place under the pill. */
.ask.busy .ask-status,
.ask.failed .ask-status {
    position: absolute;
    top: calc(100% + var(--s-2));
    left: var(--s-4);
    flex-basis: auto;
}

.ask.busy ~ .hint,
.ask.failed ~ .hint {
    visibility: hidden;
}

.filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
}

.fchip {
    position: relative;
}

.fchip > summary {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 40px;
    max-width: 100%;
    padding: 0 var(--s-3) 0 var(--s-4);
    border: 1px solid var(--control-border);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--text);
    font: 500 var(--text-sm) var(--font-body);
    cursor: pointer;
    list-style: none;
}

.fchip > summary::-webkit-details-marker {
    display: none;
}

.fchip > summary:hover {
    background: var(--surface-2);
}

.fchip > summary .k {
    color: var(--text-2);
    font-weight: 500;
}

.fchip > summary .v {
    max-width: 14ch;
    overflow: hidden;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.fchip:not([data-set]) > summary .v {
    font-weight: 500;
}

.fchip > summary .caret {
    width: 14px;
    height: 14px;
    stroke: var(--text-2);
    fill: none;
    stroke-width: 2;
}

.fchip[data-set] > summary {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

.fchip[data-set] > summary .k {
    color: var(--accent);
}

.fchip[open] > summary {
    border-color: var(--accent);
    box-shadow: var(--ring);
}

.pop {
    position: absolute;
    z-index: 25;
    top: calc(100% + 8px);
    left: 0;
    min-width: 18rem;
    max-width: min(22rem, calc(100vw - 32px));
    max-height: 20rem;
    overflow: auto;
    padding: var(--s-2);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-pop);
}

.opt {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    min-height: 44px;
    padding: 0 var(--s-3);
    border-radius: var(--r-sm);
    cursor: pointer;
}

.opt:hover {
    background: var(--surface-2);
}

.opt span:not(.count):not(.avatar) {
    white-space: nowrap;
}

.opt input {
    flex: none;
    width: 18px;
    height: 18px;
    margin: 0;
    accent-color: var(--accent);
}

.opt:has(input:checked) {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
}

/* No counts yet (a follow-up, D-123): the slot is kept out of the markup. */
.pop-search {
    width: 100%;
    min-height: 40px;
    margin-bottom: var(--s-2);
    padding: 0 var(--s-3);
    font: inherit;
    border: 1px solid var(--control-border);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--text);
    box-sizing: border-box;
}

.pop-foot {
    position: sticky;
    bottom: calc(var(--s-2) * -1);
    display: flex;
    justify-content: space-between;
    gap: var(--s-2);
    margin: var(--s-2) calc(var(--s-2) * -1) calc(var(--s-2) * -1);
    padding: var(--s-2);
    background: var(--surface);
    border-top: 1px solid var(--border);
}

.pill {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    min-height: 40px;
    padding: 0 var(--s-4);
    border: 1px solid var(--control-border);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--text);
    font: 500 var(--text-sm) var(--font-body);
    cursor: pointer;
    text-decoration: none;
    box-sizing: border-box;
}

.pill:hover {
    background: var(--surface-2);
    color: var(--text);
}

.pill input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.pill:has(input:checked),
.pill[aria-current="page"] {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-contrast);
}

.pill:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.pill .icon {
    width: 16px;
    height: 16px;
    stroke: currentcolor;
    fill: none;
    stroke-width: 2;
}

.filters .chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
    flex: 1 1 100%;
}

.filters .spacer {
    flex: 1;
}

/* "On this day" and "No location" (S-083): a pair that wraps as one, a little tighter than the filters. */
.filters .shortcuts {
    display: inline-flex;
    flex: none;
    gap: var(--s-2);
}

.filters .pill.shortcut {
    padding: 0 var(--s-3);
}

.privacy {
    display: inline;
    margin-left: 0.4em;
}

.privacy > summary {
    display: inline;
    cursor: pointer;
    color: var(--accent, var(--cyanotype));
    text-decoration: underline;
}

.privacy > summary {
    list-style: none;
}

.privacy > summary::before {
    content: "\25B8  ";
}

.privacy[open] > summary::before {
    content: "\25BE  ";
}

.privacy[open] > span {
    display: block;
    margin-top: var(--s-1);
}

.pop-empty {
    margin: var(--s-2) var(--s-3);
    color: var(--text-muted, var(--text-secondary));
    font-size: var(--text-sm);
}

.sheet-foot,
button[data-close] {
    display: none;
}

.filters-js button[data-close] {
    display: inline-flex;
}

.filters .describe {
    display: inline-flex;
    align-items: center;
    order: 0;
}

.filters .describe > span {
    margin-right: var(--s-2);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    white-space: nowrap;
}

.filters .describe input[type="search"] {
    min-width: 14rem;
    min-height: 40px;
    padding: 0 var(--s-4);
    font: 500 var(--text-sm) var(--font-body);
    border: 1px solid var(--control-border);
    border-radius: var(--r-pill);
    background: var(--surface);
    color: var(--text);
    box-sizing: border-box;
}

/* People picker avatars (the face crop or the initials, S-067). */
.avatar {
    display: inline-grid;
    place-items: center;
    flex: none;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--brand-soft);
    color: var(--brand-ink);
    font: 700 var(--text-sm) var(--font-display);
    overflow: hidden;
    position: relative;
}

.avatar.md {
    width: 56px;
    height: 56px;
    font-size: 1.2rem;
}

.avatar.lg {
    width: 72px;
    height: 72px;
    font-size: 1.5rem;
}

.avatar.c2 {
    background: #dce7f1;
    color: #1f4c74;
}

.avatar.c3 {
    background: #e2ebdc;
    color: #3c5a2e;
}

.avatar.c4 {
    background: #f0dde3;
    color: #7a2e45;
}

.avatar.c5 {
    background: #ede4cf;
    color: #6b5313;
}

.avatar .avatar-photo {
    position: absolute;
    max-width: none;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .avatar.c2 {
        background: #22364a;
        color: #9cc6ea;
    }

    :root:not([data-theme="light"]) .avatar.c3 {
        background: #27361f;
        color: #b5d4a4;
    }

    :root:not([data-theme="light"]) .avatar.c4 {
        background: #40222c;
        color: #efb3c4;
    }

    :root:not([data-theme="light"]) .avatar.c5 {
        background: #3a3016;
        color: #e6d08c;
    }
}

:root[data-theme="dark"] .avatar.c2 {
    background: #22364a;
    color: #9cc6ea;
}

:root[data-theme="dark"] .avatar.c3 {
    background: #27361f;
    color: #b5d4a4;
}

:root[data-theme="dark"] .avatar.c4 {
    background: #40222c;
    color: #efb3c4;
}

:root[data-theme="dark"] .avatar.c5 {
    background: #3a3016;
    color: #e6d08c;
}

/* Phones: the popovers are bottom sheets and the filters wrap with no horizontal scroll. */
@media (max-width: 600px) {
    .fchip {
        position: static;
    }

    .pop {
        position: fixed;
        right: 0;
        bottom: 0;
        left: 0;
        top: auto;
        max-width: none;
        max-height: 72vh;
        padding-bottom: var(--s-4);
        border-radius: var(--r-lg) var(--r-lg) 0 0;
    }

    .ask .button {
        padding: 0 var(--s-3);
    }

    .ask input[type="search"] {
        font-size: var(--text-md);
    }

    .finder {
        padding: var(--s-3);
    }

    /* One scrolling row of chips: the popovers are fixed, so the scroll container does not clip them. */
    .filters .chips {
        flex-wrap: nowrap;
        overflow-x: auto;
        padding-bottom: var(--s-1);
        scrollbar-width: thin;
    }

    .filters .chips > * {
        flex: none;
    }

    .filters .describe {
        flex: 1 1 100%;
    }

    .filters .describe input[type="search"] {
        flex: 1;
        min-width: 0;
    }

    .sheet-foot {
        display: flex;
    }
}

/* A counting answer (S-026): the number first, large. */
.answer {
    margin: 0 0 24px;
}

.answer .number {
    margin: 0;
    color: var(--sepia);
    font-family: var(--font-display);
    font-size: 3rem;
    font-weight: 800;
    letter-spacing: -0.04em;
    line-height: 1.1;
}

.answer .sentence {
    margin: 4px 0 8px;
    font-size: 1.1rem;
}

.answer .ranking {
    border-collapse: collapse;
    min-width: 16rem;
}

.answer .ranking th,
.answer .ranking td {
    padding: 4px 12px 4px 0;
    text-align: left;
}

.answer .ranking td {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* What a search applies, each part removable: filters are set in the mono face on the cyanotype tint. */
.chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 0 0 16px;
    font-size: 0.9rem;
}

.chips ul {
    display: contents;
    list-style: none;
}

.chips li {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 4px 2px 10px;
    border-radius: 999px;
    background: var(--chip);
    color: var(--cyanotype);
    font-family: var(--font-mono);
    font-size: 0.85rem;
}

.chips li a {
    display: inline-grid;
    place-items: center;
    min-width: 24px;
    min-height: 24px;
    border-radius: 999px;
    text-decoration: none;
}

.more {
    margin-top: 32px;
}

.pages {
    display: flex;
    justify-content: space-between;
    margin-top: 32px;
}

.empty {
    margin-top: 48px;
}

.back {
    margin: 0 0 16px;
}

.detail {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 360px;
    gap: var(--s-5);
    align-items: start;
}

/* The photo on a dark stage, in both themes, with the arrows to the photos before and after it (S-068). */
.preview {
    position: relative;
    margin: 0;
    background: var(--stage);
    border-radius: var(--r-lg);
    overflow: hidden;
    min-height: 320px;
    color: #fff;
}

.nav-arrow {
    position: absolute;
    top: 50%;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    transform: translateY(-50%);
    border-radius: 50%;
    background: rgb(0 0 0 / 0.5);
    color: #fff;
}

.nav-arrow:hover {
    background: rgb(0 0 0 / 0.75);
    color: #fff;
}

/* On the dark stage the ring is light: the accent of the page would not show against it. */
.nav-arrow:focus-visible {
    outline: 3px solid #fff;
    outline-offset: 2px;
}

.nav-arrow.prev {
    left: var(--s-3);
}

.nav-arrow.next {
    right: var(--s-3);
}

.nav-arrow .icon {
    width: 22px;
    height: 22px;
    fill: none;
    stroke: currentcolor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* The side panel: a card for each part, the facts first. */
.side {
    display: grid;
    gap: var(--s-4);
}

.eyebrow {
    margin: 0 0 var(--s-1);
    color: var(--text-2);
    font: 500 var(--text-xs) var(--font-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.card h2 {
    margin: 0 0 var(--s-3);
    font-size: var(--text-lg);
}

.actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin: var(--s-4) 0 0;
}

/* The faces of the people in a photo (S-068): a round thumbnail for each, named ones ringed, unnamed ones dashed. */
.faces {
    display: grid;
    gap: var(--s-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* A face of the photo: its avatar and name, and the way to remove it on the right (S-068, S-078). */
.face-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-4);
}

.faces-form {
    margin: 0;
}

.people-card .face-note {
    margin: 0 0 var(--s-3);
}

.face {
    display: grid;
    justify-items: center;
    gap: 6px;
    width: 76px;
    color: var(--text);
    font-size: var(--text-sm);
    text-align: center;
    text-decoration: none;
}

.face .avatar {
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px var(--border);
}

.face.named:hover .avatar,
.face:focus-visible .avatar {
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}

.face.unnamed .avatar {
    background: none;
    border: 2px dashed var(--control-border);
    color: var(--text-2);
    box-shadow: none;
}

.face.unnamed .name {
    color: var(--accent);
    font-weight: 600;
}

.people-card .face {
    display: flex;
    align-items: center;
    justify-items: start;
    gap: var(--s-3);
    width: auto;
    min-height: 56px;
    text-align: left;
}

.face .name {
    max-width: 100%;
    overflow-wrap: anywhere;
}

.photo-albums {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* A source (S-068): its name, and its path cut short with the whole of it under "Show full path". */
.src {
    display: grid;
    gap: var(--s-2);
    padding: var(--s-3);
    background: var(--surface-2);
    border-radius: var(--r-md);
}

.src .path {
    overflow: hidden;
    color: var(--text-2);
    font-size: var(--text-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.src .path-full {
    display: block;
    margin-top: var(--s-2);
    color: var(--text-2);
    font-size: var(--text-xs);
    overflow-wrap: anywhere;
}

.src summary {
    color: var(--accent);
    cursor: pointer;
    font-size: var(--text-sm);
}

/* In the dark theme the stage is nearly the page's own colour: a hairline keeps it a frame, not a gap. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .stage {
        border: 1px solid var(--border);
    }
}

:root[data-theme="dark"] .stage {
    border: 1px solid var(--border);
}

@media (max-width: 600px) {
    .preview {
        min-height: 240px;
    }
}

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

    .preview {
        border-radius: var(--r-md);
    }
}

.preview img,
.preview video {
    display: block;
    width: 100%;
    height: auto;
}

/* The larger previews of an index-only photo (2048 px) must not push the facts off a tall screen. */
.preview img {
    max-height: 85vh;
    object-fit: contain;
}

.preview video {
    max-height: 80vh;
    background: var(--developer);
}

.player-error {
    margin: 0;
    padding: 12px 16px;
    font-weight: 600;
}

.player-error:empty {
    display: none;
}

.processing {
    margin: 0;
    padding: 16px;
    font-weight: 600;
}

.facts h1 {
    margin-top: 0;
    font-size: var(--text-xl);
}

.facts dl {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 4px 16px;
}

.facts dt {
    font-weight: 600;
}

.facts dd {
    margin: 0;
}

/* A caption is the family's own text: its line breaks stay, and a long word or address breaks rather than widening the page (S-052). */
.facts dt.caption-label,
.facts dd.caption {
    grid-column: 1 / -1;
}

.facts dd.caption {
    white-space: pre-line;
    overflow-wrap: anywhere;
}

/* Names and paths may break anywhere, but only when they do not fit (not mid-word in a sentence that does). */
.sources {
    display: grid;
    gap: var(--s-2);
    margin: 0;
    padding: 0;
    list-style: none;
    overflow-wrap: anywhere;
}

.source-name {
    font-weight: 600;
}

.source-link {
    display: inline-block;
    margin-left: 8px;
}

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    min-height: 40px;
    padding: 0 var(--s-4);
    background: var(--accent);
    color: var(--accent-contrast);
    text-decoration: none;
    border: 1px solid transparent;
    border-radius: var(--r-md);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

.button:hover:not(:disabled) {
    background: var(--accent-hover);
    color: var(--accent-contrast);
}

.top-end {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 16px;
    margin-left: auto;
}

/* Where the header has no room for the address (601 to 1180 px) it is the first line of the "More" list, read only. */
.user-in-more {
    display: none;
    padding: var(--s-2) var(--s-3);
    border-bottom: 1px solid var(--border);
    max-width: none;
}

@media (max-width: 1180px) and (min-width: 601px) {
    .top-end > .user {
        display: none;
    }

    .user-in-more {
        display: block;
    }
}

.user {
    color: var(--text-2);
    font-family: var(--font-mono);
    font-size: 0.875rem;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 11rem;
    white-space: nowrap;
}

.sign-out {
    margin: 0;
}

/* The language switch is a segmented control; it is a form post, so it works without JavaScript. */
.language {
    display: flex;
    gap: 0;
    margin: 0;
    padding: 2px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
}

.language button {
    min-height: 32px;
    padding: 0 var(--s-3);
    background: none;
    color: var(--text-2);
    border: 0;
    border-radius: var(--r-pill);
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
}

/* Up to 1180 px the switch says EN and PT; the button keeps the full name as its accessible name (aria-label). */
@media (max-width: 1180px) {
    .language button {
        font-size: 0;
    }

    .language button::before {
        content: attr(data-short);
        font-size: var(--text-sm);
    }
}

.language button:hover:not([aria-pressed="true"]) {
    background: var(--accent-soft);
    color: var(--accent);
}

.language button[aria-pressed="true"] {
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow-sm);
    cursor: default;
}

.sign-out button {
    min-height: 40px;
    padding: 0 var(--s-4);
    background: var(--surface);
    color: var(--accent);
    border: 1px solid var(--control-border);
    border-radius: var(--r-md);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

.auth {
    max-width: 420px;
    margin: 48px auto;
    padding: 24px;
    background: var(--white);
    border: 1px solid var(--mat);
    border-radius: 4px;
}

.auth .providers {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.auth .providers .sign-out {
    margin: 0;
}

.dev-login {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.dev-login input {
    padding: 8px;
    font: inherit;
    border: 1px solid var(--mat);
    border-radius: 4px;
}

@media (max-width: 800px) {
    .detail {
        grid-template-columns: 1fr;
    }

    main {
        padding: 12px 16px 32px;
    }
}

.language button,
.sign-out button {
    white-space: nowrap;
}

.error {
    padding: var(--s-3) var(--s-4);
    color: var(--danger);
    border: 1px solid var(--danger);
    border-radius: var(--r-md);
}

.nav-link {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
    padding: 0 var(--s-3);
    border-radius: var(--r-pill);
    color: var(--text);
    font-weight: 500;
    text-decoration: none;
}

.nav-link[aria-current="page"],
.nav-more-active {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
}

/* The pages used now and then, behind a disclosure that needs no JavaScript (D-102); not .more, which the "Show more" line of a search uses. */
.nav-more {
    position: relative;
}

.nav-more > summary {
    cursor: pointer;
    list-style: none;
}

.nav-more > summary::-webkit-details-marker {
    display: none;
}

.nav-more > summary::after {
    content: " ▾" / "";
}

.nav-more-current {
    font-weight: 400;
}

.nav-more-list {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 20;
    display: grid;
    gap: 2px;
    min-width: 12rem;
    max-width: calc(100vw - 32px);
    padding: var(--s-2);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-pop);
}

.nav-more-list .nav-link {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--s-3);
    border-radius: var(--r-sm);
}

/* On a phone the list opens in the flow of the header, not over the page, so it cannot run off the screen. */
@media (max-width: 600px) {
    .nav-more[open] {
        flex-basis: 100%;
    }

    .nav-more-list {
        position: static;
        margin-top: 8px;
        max-width: none;
        box-shadow: none;
    }
}

.nav-link:hover,
.nav-link:focus-visible {
    background: var(--surface-2);
    color: var(--text);
}

.nav-link[aria-current="page"]:hover,
.nav-more-active:hover {
    background: var(--accent-soft);
    color: var(--accent);
}

/* The people page (S-019): one card per person, the face cropped from its thumbnail by an inline style. */
.page-heading {
    margin: 0;
    font-size: var(--text-2xl);
}

/* The shared page parts (S-069, D-119): the head with its actions, a callout, a section title, and a titled panel. */
.page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    justify-content: space-between;
    gap: var(--s-3);
    margin: var(--s-2) 0 var(--s-5);
}

.page-head-text {
    min-width: 0;
}

.page-sub {
    margin: var(--s-1) 0 0;
}

.page-head-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
}

.callout {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    margin: 0 0 var(--s-5);
    padding: var(--s-4);
    border-radius: var(--r-md);
    background: var(--brand-soft);
    color: var(--text);
}

.section-title {
    display: flex;
    align-items: baseline;
    gap: var(--s-3);
    margin: var(--s-6) 0 var(--s-4);
}

.section-title h2 {
    margin: 0;
    font-size: var(--text-xl);
}

.section-title .n {
    color: var(--text-2);
    font-size: var(--text-sm);
}

.block {
    display: grid;
    gap: var(--s-3);
    margin: 0 0 var(--s-5);
    padding: var(--s-5);
    overflow-wrap: anywhere;
}

.block > h2,
.block > p {
    margin: 0;
}

.block > p {
    max-width: 72ch;
}

.export-request {
    justify-items: start;
}

.note {
    margin: 0 0 var(--s-5);
}

.people {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
    gap: var(--s-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.person {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.person .face {
    position: relative;
    display: block;
    aspect-ratio: 1;
    overflow: hidden;
    background: var(--mat);
    border-radius: 50%;
}

.person .face img {
    position: absolute;
    max-width: none;
}

/* A person's card on the people page (S-069): the avatar over the name and the count, the whole card one link. */
.person .face.card-face {
    display: grid;
    position: static;
    width: auto;
    aspect-ratio: auto;
    overflow: visible;
    background: none;
    border-radius: 0;
    justify-items: center;
}

.person-link {
    align-items: center;
    padding: var(--s-5) var(--s-3) var(--s-4);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
    text-align: center;
    transition: box-shadow 0.15s, transform 0.15s, border-color 0.15s;
}

.person-link:hover {
    border-color: var(--control-border);
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

.person .name {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    overflow-wrap: anywhere;
}

.person.unnamed .name {
    color: var(--accent);
}

.person .count {
    color: var(--text-2);
    font-size: var(--text-sm);
}

@media (prefers-reduced-motion: reduce) {
    .person-link:hover {
        transform: none;
    }
}

.person .name {
    font-weight: 600;
}

/* A person's page (S-020): the corrections above the faces. */
.person-link {
    display: flex;
    flex-direction: column;
    gap: 4px;
    color: inherit;
    text-decoration: none;
}

.person-edit {
    margin: var(--s-4, 16px) 0 var(--s-6, 24px);
    padding: var(--s-2, 8px) var(--s-5, 20px);
}

/* A labelled row: the name of the correction, its field, its button; a hint sits under the field. */
.person-edit .form-row {
    display: grid;
    grid-template-columns: 11rem minmax(0, 1fr) auto;
    gap: var(--s-2, 8px) var(--s-4, 16px);
    align-items: start;
    margin: 0;
    padding: var(--s-4, 16px) 0;
    border-top: 1px solid var(--border, var(--mat));
}

.person-edit > .form-row:first-child {
    border-top: 0;
}

.person-edit .form-label {
    padding-top: 9px;
    font-weight: 600;
}

.person-edit .field {
    display: flex;
    flex-direction: column;
    gap: var(--s-1, 4px);
    min-width: 0;
    padding-top: 0;
}

.person-edit .field input[type="text"],
.person-edit .field select {
    width: 100%;
    max-width: 28rem;
}

.person-edit .field > span,
.person-edit .field > .hint {
    margin: 0;
    padding-top: 9px;
}

.person-edit .field > select + .hint {
    padding-top: 0;
}

.person-edit .form-actions {
    margin-top: 0;
    display: flex;
    gap: var(--s-2, 8px);
}

.person-edit .me-linked {
    font-weight: 600;
}

/* What cannot be undone: below the corrections, in the danger style. */
.person-edit .danger-zone {
    margin-top: var(--s-2, 8px);
    border-top: 1px solid var(--danger, #a1301f);
}

.person-edit .danger-zone h3 {
    margin: var(--s-4, 16px) 0 0;
    color: var(--danger, #a1301f);
    font-size: 1rem;
}

.person-edit .danger-zone .form-row:first-of-type {
    border-top: 0;
}

.button.danger {
    background: var(--danger, #a1301f);
    border: 1px solid var(--danger, #a1301f);
    color: #fff;
}

.button.danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--danger, #a1301f) 85%, #000);
    color: #fff;
}

/* "Delete this face" (S-078): it sits on every face, so it is outlined; the solid red stays for the buttons that delete a
   whole person or the library's faces. The hover tints it and keeps the outline. */
.button.danger.face-delete {
    min-height: 32px;
    padding: 2px 8px;
    border: 1px solid var(--danger);
    background: transparent;
    color: var(--danger);
    font-size: var(--text-sm);
}

.button.danger.face-delete:hover:not(:disabled) {
    border-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, transparent);
    color: var(--danger);
}

/* On the photo's face rows, a finger-sized target, as the row's link is. */
.face-row .button.danger.face-delete {
    min-height: 44px;
    flex-shrink: 0;
}

.person-faces .button.small {
    min-height: 32px;
    padding: 2px 8px;
    font-size: var(--text-sm, 0.85rem);
}

.person-faces {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}

.confirm-panel .person .face.card-face {
    justify-items: start;
}

@media (max-width: 800px) {
    .person-edit .form-row {
        grid-template-columns: minmax(0, 1fr);
    }

    .person-edit .form-label {
        padding-top: 0;
    }

    .person-edit .field > span,
    .person-edit .field > .hint {
        padding-top: 0;
    }
}

.person-faces button {
    font: inherit;
    padding: 2px 6px;
    border: 1px solid var(--mat);
    border-radius: 4px;
    background: var(--white);
    color: var(--developer);
    cursor: pointer;
}

/* A note is a small card, like a toast: what just happened, in the flow of the page. */
.note {
    padding: var(--s-3) var(--s-4);
    border: 1px solid var(--border);
    border-left: 4px solid var(--accent);
    border-radius: var(--r-md);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
}

/* A merge deletes a person for good (D-125, #125): the one that goes looks like it, the one that stays has a ring, and the warning
   and the button are in the danger colour when the one that goes is named or is the user. */
.merge-people {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 16px;
}

.merge-people .person {
    width: 160px;
    padding: 8px;
    border: 2px solid transparent;
    border-radius: 8px;
}

.merge-people .person.merge-leaves {
    border-color: var(--danger, #a1301f);
    background: color-mix(in srgb, var(--danger, #a1301f) 8%, transparent);
}

.merge-people .person.merge-leaves .face {
    opacity: 0.6;
}

.merge-people .person.merge-stays {
    border-color: var(--accent, var(--cyanotype));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--cyanotype)) 25%, transparent);
}

.merge-people .merge-badge {
    align-self: flex-start;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 700;
}

.merge-people .merge-leaves .merge-badge {
    background: var(--danger, #a1301f);
    color: #fff;
}

.merge-people .merge-stays .merge-badge {
    background: var(--accent, var(--cyanotype));
    color: var(--accent-contrast, #fff);
}

.merge-arrow {
    font-size: 2rem;
    color: var(--text-secondary);
}

/* Phones: the two cards and the arrow do not fit a row, so they stack and the arrow points down at the one that stays. */
@media (max-width: 600px) {
    .merge-people {
        flex-direction: column;
        align-items: flex-start;
    }

    .merge-arrow {
        align-self: center;
        transform: rotate(90deg);
    }
}

.note.danger {
    border: 1px solid var(--danger, #a1301f);
    border-left-width: 4px;
    background: color-mix(in srgb, var(--danger, #a1301f) 10%, var(--white, transparent));
}

.note.danger strong {
    font-weight: 800;
}

.merge-flow .confirm {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
}

/* After a correction on a person's page (#106) the page jumps to the next face, which would leave the note off screen: it
   stays in view while the page is scrolled, and a face card lands below the note. */
.person-note {
    position: sticky;
    /* Below the sticky header from 960 px (--header-h is 0 under it). */
    top: calc(var(--header-h) + var(--s-2));
    z-index: 5;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}

.person-faces .person {
    scroll-margin-top: 96px;
}

/* What person-faces.js says after a face correction (S-078, S-088): a toast fixed at the bottom right, out of the flow, so
   the faces never move and the sticky header never covers it; clear of a phone's rounded corners and home bar. On a wide
   screen the note and Undo share one row; the close button is pinned at the top right in every state. */
.faces-toast {
    position: fixed;
    right: max(var(--s-4), env(safe-area-inset-right));
    bottom: max(var(--s-4), env(safe-area-inset-bottom));
    left: auto;
    z-index: 40;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: var(--s-2);
    max-width: min(560px, calc(100vw - 2 * var(--s-4)));
    padding: var(--s-2) calc(var(--s-1) + 32px + var(--s-1)) var(--s-2) var(--s-2);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface);
    box-shadow: var(--shadow-pop);
}

.faces-toast > div:not([hidden]) {
    flex: 1 1 auto;
    min-width: 0;
}

/* The note keeps its coloured edge and icon (the variant is never colour alone); the toast is its surface. */
.faces-toast .note {
    margin: 0;
    border-top: 0;
    border-right: 0;
    border-bottom: 0;
    box-shadow: none;
}

.faces-toast .faces-undo {
    flex: none;
    align-self: center;
}

.faces-toast .faces-close {
    position: absolute;
    top: var(--s-1);
    right: var(--s-1);
    min-width: 32px;
    min-height: 32px;
    font-size: 1.1rem;
    line-height: 1;
}

/* A finger gets a 44 px target for the close button. */
@media (pointer: coarse) {
    .faces-toast {
        padding-right: calc(var(--s-1) + 44px + var(--s-1));
    }

    .faces-toast .faces-close {
        min-width: 44px;
        min-height: 44px;
    }
}

/* On a phone the note takes the width and Undo goes under it, lined up with the note's text (edge, padding, icon, gap). */
@media (max-width: 600px) {
    .faces-toast {
        flex-wrap: wrap;
    }

    .faces-toast > div:not([hidden]) {
        flex-basis: 100%;
    }

    .faces-toast .faces-undo {
        margin-left: calc(4px + var(--s-4) + 20px + var(--s-3));
    }
}

/* While the toast shows, the page ends with room as tall as it (set by the script), so it never covers the last faces,
   and nothing inside a card moves. */
body.faces-toast-open main {
    padding-bottom: calc(var(--faces-toast-room, 0px) + var(--s-6));
}

/* A face put back because its correction failed (S-078, S-088): outlined, with the error under it, for a few seconds. */
.person-faces .person.face-failed,
.face-row.face-failed {
    outline: 2px solid var(--danger);
    outline-offset: 2px;
    border-radius: var(--r-md);
}

.person-faces .face-error,
.face-row .face-error {
    margin: 0;
    color: var(--danger);
}

/* On the photo's rows the error takes a line of its own under the face and its button, inside the outline. */
.face-row.face-failed {
    flex-wrap: wrap;
    padding: var(--s-1) var(--s-2);
}

.face-row .face-error {
    flex-basis: 100%;
}

.faces-hint {
    margin: 0 0 var(--s-3);
}

.faces-empty {
    margin: var(--s-3) 0;
}

/* The occasions page (S-023): a card per occasion, three thumbnails over its title. */
.occasions {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 20px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.occasion-link {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    height: 100%;
    padding: var(--s-3);
    color: inherit;
    text-decoration: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
    transition: border-color 0.15s, box-shadow 0.15s;
}

.occasion-link:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-md, var(--shadow-sm));
}

.occasion-body {
    display: grid;
    gap: var(--s-1);
}

.occasion-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
}

/* The type of an occasion, a small pill (not the video-duration .badge, which sits on a thumbnail). */
.occasion-type {
    padding: 1px var(--s-2);
    border-radius: var(--r-pill);
    background: var(--accent-soft);
    color: var(--accent);
    font-size: var(--text-sm);
    font-weight: 600;
}

.occasion-thumbs {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2px;
    min-height: 90px;
    background: var(--mat);
    border-radius: 2px;
    overflow: hidden;
}

.occasion-thumbs img {
    display: block;
    width: 100%;
    /* The img's height attribute would otherwise win over the aspect ratio and give tall strips. */
    height: auto;
    aspect-ratio: 1;
    object-fit: cover;
}

.occasion-thumbs .placeholder {
    grid-column: 1 / -1;
    min-height: 100px;
}

.occasion-filter {
    margin-bottom: 24px;
}

.occasion-link:focus-visible {
    outline: 3px solid var(--cyanotype);
    outline-offset: 4px;
    border-radius: 2px;
}

.occasion-link:hover .occasion-title {
    color: var(--accent);
}

.occasion-title {
    font-family: var(--font-display);
    font-weight: 600;
}

/* The storage page (S-028): a table like a count's ranking, and the assets under it in the timeline's grid. */
.stat-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: var(--s-4);
    margin: 0 0 var(--s-5);
}

.stat-card {
    display: grid;
    align-content: start;
    gap: var(--s-2);
    padding: var(--s-5);
}

.stat-card h2,
.stat-card p {
    margin: 0;
}

.stat-card .stat-line {
    font-family: var(--font-display);
    font-size: var(--text-xl, 1.5rem);
    font-weight: 700;
    line-height: 1.2;
}

/* The totals are a table of five narrow columns: a card as wide as they need, not the page. */
.storage-totals {
    max-width: 56rem;
}

.storage-totals table {
    border-collapse: collapse;
}

.storage-totals th,
.storage-totals td {
    padding: 4px 16px 4px 0;
    text-align: left;
}

.storage-totals thead th:not(:first-child) {
    text-align: right;
}

.storage-totals td {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.storage-totals tfoot th,
.storage-totals tfoot td {
    border-top: 1px solid var(--mat);
    font-weight: 700;
}

.storage-assets {
    margin: 0 0 8px;
    padding: 0;
    list-style: none;
}

/* A drive's name can be long and unbroken: wrap it anywhere and keep three lines (the full list is the title). */
.storage-assets p {
    margin: 4px 0 0;
    overflow-wrap: anywhere;
}

.storage-assets .clamp {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
}

.storage-assets .size {
    font-variant-numeric: tabular-nums;
}

/* A segmented control (S-075): one pill with a segment for each list, the one shown filled, each with its count. */
.organize-views {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 2px;
    margin: 0 0 var(--s-3);
    padding: 3px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
}

.organize-views .seg {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    min-height: 36px;
    padding: 0 var(--s-4);
    border-radius: var(--r-pill);
    color: var(--text-2);
    font-weight: 600;
    text-decoration: none;
}

.organize-views a.seg:hover {
    background: var(--accent-soft);
    color: var(--text);
}

.organize-views .seg.active {
    background: var(--accent);
    color: var(--accent-contrast);
}

.organize-views .seg-count {
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    opacity: 0.85;
}

/* The action stays at the bottom of the window while a long list is checked (S-075). */
.organize-bar {
    position: sticky;
    bottom: 0;
    z-index: 5;
    display: flex;
    justify-content: flex-end;
    margin: var(--s-5) 0 0;
    padding: var(--s-3) var(--s-4);
    background: var(--surface);
    border-top: 1px solid var(--border);
    box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.08);
}

.organize-form .organize-submit {
    margin: 0;
}

.organize-form .grid {
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--s-3);
}

.bulk {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 8px 0 16px;
}

.burst h2 {
    margin: 0;
    font-size: var(--text-lg);
}

.organize-item {
    position: relative;
    display: grid;
    gap: 2px;
}

.organize-item input {
    width: 22px;
    height: 22px;
    margin: 0;
}

.organize-item .check {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    cursor: pointer;
}

.sharpest-badge {
    position: absolute;
    top: 6px;
    left: 6px;
    padding: 1px 8px;
    background: var(--developer);
    color: var(--white);
    font-size: 0.8rem;
    border-radius: 2px;
    pointer-events: none;
}

/* The connected accounts (S-037): one block per account, its counts and its actions. */
.connections {
    display: grid;
    gap: 16px;
    margin: 0 0 16px;
    padding: 0;
    list-style: none;
}

.connection {
    padding: var(--s-4);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
}

.connection p {
    margin: 0 0 8px;
}

/* A connection is a card of its own, with its icon and its actions to the right (S-076). */
.connection,
.service-card {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--s-4);
    align-items: start;
    padding: var(--s-4) var(--s-5);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
}

.service-card {
    margin: 0 0 var(--s-5);
}

.service-icon {
    width: 40px;
    height: 40px;
    padding: 8px;
    box-sizing: border-box;
    background: var(--accent-soft);
    border-radius: var(--r-md);
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.6;
    stroke-linejoin: round;
}

.connection-body h2,
.connection-body p {
    margin: 0 0 var(--s-1);
}

.connection-side {
    display: grid;
    justify-items: end;
    gap: var(--s-2);
    max-width: 22rem;
}

/* Disconnecting only removes the stored access: an outlined danger button; solid red stays for deleting data. */
.connection-actions .button.danger {
    background: transparent;
    border-color: var(--danger);
    color: var(--danger);
}

.connection-actions .button.danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--danger) 10%, transparent);
    color: var(--danger);
}

.connection-side .connection-actions {
    justify-content: flex-end;
}

.disconnect-hint {
    margin: 0;
    text-align: right;
}

.connect-more {
    margin: 0;
}

@media (max-width: 800px) {
    .connection,
    .service-card {
        grid-template-columns: auto minmax(0, 1fr);
    }

    .connection-side {
        grid-column: 1 / -1;
        justify-items: start;
        max-width: none;
    }

    .disconnect-hint {
        text-align: left;
    }
}

.connection-account {
    overflow-wrap: anywhere;
}

.connection-account .status {
    margin-left: 8px;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 0.8rem;
    background: var(--accent-soft);
    color: var(--accent);
}

.connection-account .status-expired {
    background: transparent;
    border: 1px solid var(--danger);
    color: var(--danger);
}

.connection .counts span {
    margin-right: 16px;
}

.connection-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 12px;
    align-items: center;
}

.button.secondary {
    background: var(--surface);
    color: var(--accent);
    border-color: var(--control-border);
}

.button.secondary:hover:not(:disabled) {
    background: var(--accent-soft);
    color: var(--accent);
}

.button.ghost {
    background: none;
    color: var(--accent);
    border-color: transparent;
}

.button.ghost:hover:not(:disabled) {
    background: var(--accent-soft);
    color: var(--accent);
}

/* Ignoring strangers' faces (#107): the confirmation says what is deleted, and the way out is the first thing in reach. */
.confirm .button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 40px;
    box-sizing: border-box;
}

.confirm {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-top: 16px;
}

.ignore-warning {
    font-size: 1.125rem;
}

.ignore-effects {
    margin: 8px 0;
    padding-left: 20px;
}

.ignore-cloud {
    margin: 8px 0;
}

.ignore-cloud summary {
    cursor: pointer;
    min-height: 24px;
}

.ignore-crops {
    margin: 12px 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
    gap: 8px;
}

.ignore-more {
    margin: 4px 0 12px;
}

.ignore-faces a,
.ignore-many a,
.ignore-person a,
.delete-face-data a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 8px;
    box-sizing: border-box;
}

/* Ignoring the unnamed faces of a photo deletes data too, so it is drawn as the rows' own ignore links are (S-068). */
.people-card .ignore-faces a {
    padding-left: 0;
    color: var(--danger);
}

.ignore-many a.button {
    line-height: normal;
    display: inline-flex;
    align-items: center;
    min-height: 40px;
}

/* A long album name must not widen the bar or the page (S-052): the select shrinks to its cell and shows an ellipsis. */
.filters label {
    min-width: 0;
    max-width: 100%;
}

.filters select {
    min-width: 0;
    max-width: min(100%, 20rem);
}

/* The disclosure triangle stays (display: list-item), and the line reads as a link with a 44 px target. */
.more-albums summary {
    display: list-item;
    cursor: pointer;
    padding: 12px 0;
    color: var(--cyanotype);
    text-decoration: underline;
}

.block .note {
    margin: 0;
}

/* Lists of things on the settings-like pages (S-069): saved places, exports, deletions and the sources, one small card each. */
.saved-places,
.exports,
.face-deletions,
.connections {
    display: grid;
    gap: var(--s-3);
    margin: 0 0 var(--s-5);
    padding: 0;
    list-style: none;
}

.saved-place,
.export {
    padding: var(--s-4);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
}

.saved-place {
    display: grid;
    gap: var(--s-2);
    overflow-wrap: anywhere;
}

.saved-place h2 {
    margin: 0;
    font-size: var(--text-lg);
}

.export {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2) var(--s-4);
}

.block label:not(:has(input[type="checkbox"], input[type="radio"])) {
    display: grid;
    gap: var(--s-1);
}

.block form {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: var(--s-3);
}

.block table {
    width: 100%;
}

@media (max-width: 600px) {
    .block {
        padding: var(--s-4);
    }
}

svg.icon {
    width: 20px;
    height: 20px;
    flex: none;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.callout .icon {
    width: 20px;
    height: 20px;
    flex: none;
}

/* Wide screens (D-126): the content grows to 1760px from 1600px; text and forms keep a readable measure. */
@media (min-width: 1600px) {
    :root {
        --content-max: 1760px;
    }
}

.measure,
.page-sub,
.empty-help {
    max-width: 72ch;
}

.form-col {
    max-width: 720px;
}

/* Form primitives (S-069): a field is its label above, the control, a helper line and an error line; a row puts two or three side by side. */
.field {
    display: grid;
    gap: var(--s-1);
    min-width: 0;
}

.field > .field-label {
    font-weight: 600;
    font-size: var(--text-sm);
}

.field input:not([type="checkbox"], [type="radio"]),
.field select {
    width: 100%;
    min-height: 40px;
}

.field-help {
    margin: 0;
    color: var(--text-2);
    font-size: var(--text-sm);
}

.field-error {
    margin: 0;
    color: var(--danger);
    font-size: var(--text-sm);
    font-weight: 600;
}

.field:has(.field-error) input,
.field:has(.field-error) select {
    border-color: var(--danger);
}

.form-stack {
    display: grid;
    gap: var(--s-4);
    align-items: start;
}

.block form.form-stack {
    display: grid;
}

.form-row {
    display: grid;
    align-items: start;
    grid-template-columns: 1fr;
    gap: var(--s-3) var(--s-4);
}

@media (min-width: 700px) {
    .form-row {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .form-row.three {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

/* An option card (S-069): a checkbox with a title and a line of help in one clickable card, for an option that needs explaining. */
.option-card {
    display: flex;
    align-items: baseline;
    gap: var(--s-3);
    padding: var(--s-4);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface-2);
    cursor: pointer;
}

.option-card:has(input:checked) {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.option-card:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.option-card input {
    flex: none;
    width: 20px;
    height: 20px;
    margin: 0;
}

.option-text {
    display: grid;
    gap: var(--s-1);
}

.form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2) var(--s-3);
    margin-top: var(--s-2);
}

/* The primary action is the first in the markup (so the first Tab stop and Enter), then the secondary ones, then a link. */
.form-actions > .button:first-child {
    order: -1;
}

.button.danger {
    border-color: var(--danger);
    background: var(--danger);
    color: var(--surface);
}

.button.danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--danger) 85%, black);
    border-color: transparent;
    color: var(--surface);
}

/* Notes in three kinds (S-069): a banner under the header with a coloured edge and an icon. */
.note {
    display: flex;
    align-items: center;
    gap: var(--s-3);
}

.note .icon {
    flex: none;
}

/* A one-line note does not stretch across a wide screen: it keeps the text measure. */
.note {
    max-width: 72ch;
}

.note-success {
    border-left-color: var(--success);
}

.note-success .icon {
    color: var(--success);
}

.note-warning {
    border-left-color: var(--warning);
}

.note-warning .icon {
    color: var(--warning);
}

.note-info {
    border-left-color: var(--accent);
}

.note-info .icon {
    color: var(--accent);
}

.note-error {
    border-left-color: var(--danger);
    background: var(--surface);
}

.note-error .icon {
    color: var(--danger);
}

/* An empty state (S-069; S-070 puts it on the pages). */
.empty-state {
    display: grid;
    justify-items: center;
    gap: var(--s-3);
    margin: var(--s-7, 48px) auto;
    max-width: 480px;
    padding: var(--s-6);
    text-align: center;
}

.empty-state .empty-icon {
    width: 56px;
    height: 56px;
    stroke-width: 1.4;
    color: var(--text-3);
}

.empty-title {
    margin: 0;
    font-size: var(--text-xl);
}

.empty-help {
    margin: 0;
}

/* Places (S-071): a saved place as a card with its edit form folded away. */
.saved-place .links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin: 0;
}

.saved-place.is-home {
    border-color: var(--accent);
}

.home-mark {
    color: var(--accent);
    font-size: var(--text-sm);
    font-weight: 600;
}

.place-edit > summary {
    width: fit-content;
    color: var(--accent);
    cursor: pointer;
    font-weight: 600;
}

.place-edit[open] > summary {
    margin-bottom: var(--s-3);
}

.chip {
    display: inline-block;
    padding: 0 var(--s-2);
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: var(--text-sm);
    font-weight: 600;
    vertical-align: middle;
}

.saved-place h3 {
    margin: 0;
    font-size: var(--text-lg);
}

.confirm-delete {
    margin: 0 0 var(--s-5);
}

.confirm-delete .confirm {
    margin-top: var(--s-3);
}

.edit-refusal {
    margin: 0 0 var(--s-3);
}

@media (min-width: 1280px) {
    .saved-places {
        grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
        align-items: start;
    }
}

/* Settings and Export (S-072): section navigation, the danger zone, history tables and the biometric option. */
.settings-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--s-4);
    align-items: start;
}

@media (min-width: 960px) {
    .settings-layout {
        grid-template-columns: 220px minmax(0, 1fr);
        gap: var(--s-6);
    }

    .settings-nav {
        position: sticky;
        top: calc(var(--header-h) + var(--s-4));
        flex-direction: column;
    }
}

.settings-nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-1);
}

.settings-nav a {
    padding: var(--s-2) var(--s-3);
    border-radius: var(--r-md);
    color: var(--text);
    font-weight: 600;
    text-decoration: none;
}

.settings-nav a:hover {
    background: var(--accent-soft);
    color: var(--accent);
}

.settings-nav .danger-link {
    color: var(--danger);
}

.settings-sections {
    min-width: 0;
    max-width: 880px;
}

.danger-zone {
    border-color: var(--danger);
}

.danger-zone > h2 {
    color: var(--danger);
}

.table-wrap {
    overflow-x: auto;
    margin: 0 0 var(--s-4);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface);
}

.face-deletions,
.exports {
    display: table;
    width: 100%;
    margin: 0;
    padding: 0;
    border-collapse: collapse;
    list-style: none;
}

.face-deletions th,
.face-deletions td,
.exports th,
.exports td {
    padding: var(--s-3) var(--s-4);
    border-bottom: 1px solid var(--border);
    text-align: left;
    vertical-align: top;
}

.face-deletions th,
.exports th {
    background: var(--surface-2);
    color: var(--text-2);
    font-size: var(--text-sm);
    font-weight: 600;
    white-space: nowrap;
}

.face-deletions tbody tr:last-child td,
.exports tbody tr:last-child td {
    border-bottom: 0;
}

.exports .action-stack {
    display: grid;
    gap: var(--s-1);
    justify-items: start;
}

.exports .error {
    padding: 0;
    border: 0;
}

.face-deletions .when,
.exports .when {
    white-space: nowrap;
}

.face-deletions .who {
    min-width: 14rem;
    overflow-wrap: normal;
}

.exports tr {
    display: table-row;
    padding: 0;
    border: 0;
    box-shadow: none;
}

.status-badge {
    display: inline-block;
    padding: 2px var(--s-3);
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--control-border);
    color: var(--text);
    font-size: var(--text-sm);
    font-weight: 600;
    white-space: nowrap;
}

.status-badge.status-done {
    border-color: var(--success);
    color: var(--success);
}

.status-badge.status-failed {
    border-color: var(--danger);
    color: var(--danger);
}

.status-badge.status-requested,
.status-badge.status-running {
    border-color: var(--warning);
    color: var(--warning);
}

.option-card.sensitive {
    border-color: var(--warning);
}

.sensitive-tag {
    width: fit-content;
    padding: 0 var(--s-2);
    border-radius: var(--r-sm, 4px);
    background: var(--surface);
    border: 1px solid var(--warning);
    color: var(--warning);
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Sign-in and No access (S-073): the brand card, centred on a calm sepia ground, with the provider buttons and the privacy line. */
body:has(.auth-main) {
    min-height: 100vh;
    background: radial-gradient(ellipse at top, var(--brand-soft), var(--bg) 70%) no-repeat fixed;
}

/* The card carries the brand on these pages, so the header does not repeat it. */
.top-bare .brand {
    display: none;
}

/* The mark is dark brown on a transparent ground: on the dark card it sits on a light rounded square, as in the header. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .auth-brand img {
        padding: 6px;
        background: #f2ede5;
        border-radius: var(--r-md);
        box-sizing: content-box;
    }
}

:root[data-theme="dark"] .auth-brand img {
    padding: 6px;
    background: #f2ede5;
    border-radius: var(--r-md);
    box-sizing: content-box;
}

/* A long label such as "Sign out and sign in with another account" wraps instead of widening the page. */
.auth .sign-out button {
    width: 100%;
    min-height: 44px;
    white-space: normal;
}

.auth-main {
    display: grid;
    align-content: start;
    justify-items: center;
    min-height: calc(100vh - 120px);
    padding-top: var(--s-7, 48px);
}

.auth-card {
    display: grid;
    gap: var(--s-4);
    width: 100%;
    max-width: 440px;
    margin: 0;
    padding: var(--s-6);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-md);
    text-align: center;
}

.auth-card > h1,
.auth-card > p {
    margin: 0;
}

.auth-card h1 {
    font-size: var(--text-2xl);
}

.auth-brand {
    display: grid;
    justify-items: center;
    gap: var(--s-2);
}

.auth-wordmark {
    font-size: var(--text-xl);
}

.auth-card .providers {
    display: grid;
    gap: var(--s-3);
}

.button.provider {
    justify-content: center;
}

.provider-icon {
    width: 20px;
    height: 20px;
}

.auth-privacy {
    padding-top: var(--s-3);
    border-top: 1px solid var(--border);
}

/* The theme switch (S-077): the header's one button, and Settings' three choices as a segmented control, as the language switch. */
.theme {
    display: inline-flex;
    margin: 0;
}

.theme-menu > summary.theme-cycle {
    display: inline-grid;
    place-items: center;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 1px solid var(--control-border);
    border-radius: 50%;
    background: var(--surface);
    color: var(--text);
}

.theme-menu > summary.theme-cycle::after {
    content: none;
}

.theme-menu > summary.theme-cycle:hover {
    background: var(--accent-soft);
    color: var(--accent);
}

.theme-list .theme {
    display: grid;
    gap: 2px;
}

.theme-item {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    width: 100%;
    min-height: 44px;
    padding: 0 var(--s-3);
    border: 0;
    border-radius: var(--r-sm);
    background: none;
    color: var(--text);
    text-align: left;
}

.theme-item:hover {
    background: var(--accent-soft);
    color: var(--accent);
}

.theme-item[aria-pressed="true"] {
    font-weight: 600;
}

.theme-check {
    margin-left: auto;
    color: var(--accent);
}

.theme:not(:has(.theme-item)) {
    display: inline-flex;
    width: fit-content;
    padding: 2px;
    border: 1px solid var(--control-border);
    border-radius: 999px;
    background: var(--surface);
}

.theme:not(:has(.theme-item)) button {
    min-height: 36px;
    padding: 0 var(--s-4);
    border: 0;
    border-radius: 999px;
    background: none;
    color: var(--text);
}

.theme:not(:has(.theme-item)) button[aria-pressed="true"] {
    background: var(--accent);
    color: var(--accent-contrast);
}

/* Loading skeletons (S-070): grey shapes where the content will be, pulsing gently. */
.skeleton {
    display: block;
    border-radius: var(--r-md);
    background: linear-gradient(90deg, var(--border) 25%, color-mix(in srgb, var(--control-border) 45%, var(--border)) 50%, var(--border) 75%);
    background-size: 200% 100%;
    animation: skeleton-pulse 1.4s ease-in-out infinite;
}

.skeleton-card {
    aspect-ratio: 4 / 3;
}

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

.skeleton-lines {
    display: grid;
    gap: var(--s-3);
    max-width: 720px;
}

.skeleton-line {
    height: 18px;
}

.skeleton-line:nth-child(odd) {
    width: 92%;
}

@keyframes skeleton-pulse {
    from {
        background-position: 200% 0;
    }

    to {
        background-position: -200% 0;
    }
}

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

/* The photo page (S-082): the photo and "Same day" in the main column, the side panel beside both. The second row takes
   what the panel adds below the photo, so the card stays right under it. */
.detail {
    grid-template-areas:
        "photo side"
        "day side";
    grid-template-rows: auto 1fr;
}

.detail > .preview {
    grid-area: photo;
}

/* A grid item is as wide as its content by default: without min-width the strip's nine tiles would widen the page on a phone. */
.detail > .same-day {
    grid-area: day;
    align-self: start;
    min-width: 0;
}

.detail > .side {
    grid-area: side;
}

/* One column: the panel's cards join the page's grid so "Same day" can come after the facts. This is the visual order
   only; the DOM, the tab order and screen readers keep photo, same day, then the panel (S-082, DaVinci's sketch). */
@media (max-width: 900px) {
    .detail {
        grid-template-areas: none;
        grid-template-rows: none;
    }

    .detail > .preview,
    .detail > .same-day,
    .detail > .side {
        grid-area: auto;
    }

    .detail > .side {
        display: contents;
    }

    .detail > .side > .facts {
        order: 1;
    }

    .detail > .same-day {
        order: 2;
    }

    .detail > .side > :not(.facts) {
        order: 3;
    }
}

/* The heading and "See all" share the first row; the subtitle runs under both, so its date never breaks on a phone. */
.same-day-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    gap: var(--s-1) var(--s-3);
    margin: 0 0 var(--s-3);
}

.same-day-head h2 {
    margin: 0;
}

.same-day-sub {
    grid-column: 1 / -1;
    margin: 0;
    font-size: var(--text-sm);
}

.same-day-all {
    font-weight: 600;
}

/* Nine columns across the main column, whatever the day has, so two or three tiles keep the size of nine, on the left. On a
   phone 64 px tiles scroll sideways inside the card rather than shrinking. The padding leaves room for the ring and the
   focus outline (the scroll box would clip them); the negative margin lines the tiles up with the heading. */
.day-strip {
    display: grid;
    grid-template-columns: repeat(9, minmax(48px, 1fr));
    gap: var(--s-2);
    margin: 0 -6px;
    padding: 6px;
    position: relative;
    overflow-x: auto;
    list-style: none;
    scroll-snap-type: x proximity;
    scroll-padding-inline: 6px;
}

@media (max-width: 600px) {
    .day-strip {
        grid-template-columns: none;
        grid-auto-flow: column;
        grid-auto-columns: 64px;
    }
}

.day-item {
    display: grid;
    gap: var(--s-1);
    justify-items: center;
    scroll-snap-align: start;
}

.day-tile {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 1;
    overflow: hidden;
    background: var(--surface-2);
    border-radius: var(--r-sm);
}

.day-tile img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.day-tile .placeholder {
    display: grid;
    place-items: center;
    height: 100%;
    color: var(--text-2);
    font-size: var(--text-xs);
}

.day-tile .badge {
    right: 2px;
    bottom: 2px;
    padding: 0 3px;
    font-size: 0.62rem;
}

a.day-tile:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
}

/* This photo: ringed in the accent, its time in bold; not a link. */
.day-item.current .day-tile {
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}

.day-time {
    color: var(--text-2);
    font: 500 var(--text-xs) var(--font-mono);
}

.day-item.current .day-time {
    color: var(--text);
    font-weight: 700;
}

.same-day-alone {
    margin: 0;
}

.same-day-years {
    margin-top: var(--s-4);
    padding-top: var(--s-4);
    border-top: 1px solid var(--border);
}

.same-day-years h3 {
    margin: 0 0 var(--s-2);
    font-size: var(--text-sm);
    font-weight: 600;
}

.year-pills {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.year-pill {
    font-variant-numeric: tabular-nums;
}

/* "More actions" next to the download (S-082): the header's disclosure, opening under its button. */
.detail-actions {
    align-items: flex-start;
}

/* Its list opens under the button and to the right of its left edge: the button sits at the left of the facts card, so
   aligned to its right edge (as the header's is) the list would run over the photo. */
.detail-more .nav-more-list {
    right: auto;
    left: 0;
    min-width: 16rem;
}

/* The photo's place row (S-083): the name a person gave it, the city, and a quiet line saying who set it and how precisely.
   "No location · Set location" is one line. */
.location-row {
    display: grid;
    gap: 2px;
}

.location-row .location-by,
.location-row .location-replaced {
    font-size: var(--text-sm);
}

/* A note with an action at its end (Notice's Action, S-083's Undo): the text takes the room, the action keeps its size. */
.note-with-action > span:not(.note-action) {
    flex: 1 1 auto;
}

.note-action {
    flex: none;
}

.note-action:empty {
    display: none;
}

.note-action form {
    margin: 0;
}

.location-note {
    flex-wrap: wrap;
}

/* "Set location" (S-083): three options in one form, each a group with its own legend, one radio choice across them. */
.location-panel {
    max-width: 720px;
}

.location-lead,
.location-now {
    margin: 0 0 var(--s-4);
    text-wrap: pretty;
}

.location-now {
    margin-top: calc(-1 * var(--s-2));
    color: var(--text-2);
}

.location-error {
    margin: 0 0 var(--s-4);
}

.location-error:focus {
    outline: none;
}

.location-error:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
    border-radius: var(--r-md);
}

.location-error .note {
    margin: 0;
}

/* One group under the other: a panel's forms are otherwise a row of fields (.block form). */
.block form.location-form {
    display: grid;
    align-items: stretch;
    gap: var(--s-4);
}

.location-option {
    display: grid;
    gap: var(--s-2);
    margin: 0;
    padding: var(--s-3) var(--s-4) var(--s-4);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    min-width: 0;
}

.location-option legend {
    padding: 0 var(--s-1);
    font-weight: 600;
}

.location-choices {
    display: grid;
    gap: var(--s-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* A whole row to press, a finger's height. */
.location-choice {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px var(--s-2);
    min-height: 44px;
    padding: var(--s-2) var(--s-2);
    border-radius: var(--r-sm);
    cursor: pointer;
}

@media (hover: hover) {
    .location-choice:hover {
        background: var(--surface-2);
    }
}

.location-choice input {
    align-self: center;
    margin: 0;
    accent-color: var(--accent);
}

/* The radios are drawn with the controls' border colour, so an unchecked one shows on a dark surface too. */
.location-choice input[type="radio"] {
    appearance: none;
    display: grid;
    place-content: center;
    flex: none;
    width: 18px;
    height: 18px;
    border: 2px solid var(--control-border);
    border-radius: 50%;
    background: var(--surface);
}

.location-choice input[type="radio"]::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    transform: scale(0);
    background: var(--accent);
}

.location-choice input[type="radio"]:checked {
    border-color: var(--accent);
}

.location-choice input[type="radio"]:checked::before {
    transform: scale(1);
}

.location-choice input[type="radio"]:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
}

@media (forced-colors: active) {
    .location-choice input[type="radio"]::before {
        background: CanvasText;
    }
}

.location-choice-name {
    font-weight: 500;
}

.location-choice-where {
    font-size: var(--text-sm);
}

.location-field-label {
    font-size: var(--text-sm);
}

.location-city-search {
    display: flex;
    gap: var(--s-2);
}

.location-city-search input {
    flex: 1 1 auto;
    min-width: 0;
}

.location-city-search .button {
    flex: none;
}

.location-option .hint {
    margin: 0;
}

.location-city-results:empty {
    display: none;
}

.location-city-results:focus {
    outline: none;
}

.location-name {
    width: 100%;
}

/* Linking the name to the other photos with it: its own block, just above the buttons. */
.location-link {
    padding: var(--s-2) var(--s-3);
    border-radius: var(--r-md);
    background: var(--surface-2);
}

.location-remove {
    padding-top: var(--s-3);
    border-top: 1px solid var(--border);
}

.location-remove-button {
    min-height: 36px;
}

@media (pointer: coarse) {
    .location-remove-button {
        min-height: 44px;
    }
}

/* On a phone the groups are not boxes within the panel's box: their legends head them, with the room given to the choices. */
@media (max-width: 600px) {
    .location-option {
        padding: 0;
        border: 0;
        border-radius: 0;
    }

    .location-option legend {
        padding: 0 0 var(--s-1);
    }
}

/* The occasion block in the facts card (S-084): a quiet part with its heading, the occasion's name and line, its type
   as a soft pill, and the ⋯ menu of corrections at its top right. */
.occasion-block {
    display: grid;
    gap: var(--s-1);
    margin: var(--s-4) 0 0;
    padding-top: var(--s-4);
    border-top: 1px solid var(--border);
}

.occasion-block-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
}

.occasion-part-of {
    margin: 0;
    color: var(--text-2);
    font-size: var(--text-sm);
}

.occasion-name {
    font-weight: 600;
    text-wrap: pretty;
}

.occasion-meta,
.occasion-none {
    margin: 0;
    text-wrap: pretty;
}

.occasion-meta {
    font-size: var(--text-sm);
}

.occasion-type {
    justify-self: start;
    padding: 2px var(--s-2);
    border-radius: 999px;
    background: var(--brand-soft);
    color: var(--brand-ink);
    font-size: var(--text-sm);
}

.occasion-add {
    justify-self: start;
}

.occasion-menu > summary.occasion-menu-button {
    display: inline-grid;
    place-content: center;
    width: 44px;
    height: 36px;
    border: 1px solid var(--control-border);
    border-radius: var(--r-sm);
    color: var(--text);
    font-size: 1.25rem;
    line-height: 1;
}

.occasion-menu > summary.occasion-menu-button::after {
    content: none;
}

@media (hover: hover) {
    .occasion-menu > summary.occasion-menu-button:hover {
        background: var(--surface-2);
    }
}

/* Over the card at every width, from the button's right edge: inside the card's head it has no room to open in the flow. */
.occasion-menu .nav-more-list {
    position: absolute;
    right: 0;
    left: auto;
    max-width: calc(100vw - 48px);
    box-shadow: var(--shadow-pop);
}

@media (pointer: coarse) {
    .occasion-menu > summary.occasion-menu-button {
        height: 44px;
    }
}

/* The occasion pages (S-084) share the location page's layout: one form, groups of choices, the buttons last. */
.occasion-panel {
    max-width: 720px;
}

.occasion-lead,
.occasion-now {
    margin: 0 0 var(--s-4);
    text-wrap: pretty;
}

.occasion-now {
    color: var(--text-2);
}

.block form.occasion-form {
    display: grid;
    align-items: stretch;
    gap: var(--s-4);
}

.occasion-search {
    display: grid;
    gap: var(--s-2);
}

/* "Use the automatic grouping again" on the occasion's page. */
.occasion-reset {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2) var(--s-4);
    margin: 0 0 var(--s-5);
}

.occasion-reset p {
    margin: 0;
}

/* A family's name can be one long word (S-084): it breaks rather than run off the page. */
.page-heading,
.occasion-name,
.occasion-title,
.occasion-choices .location-choice-name {
    overflow-wrap: anywhere;
}

/* The menu's entries on one line each, and the heading keeps its place beside the button when the menu is open. */
.occasion-menu .nav-more-list {
    min-width: 17rem;
}

.occasion-menu[open] {
    flex-basis: auto;
}

/* An occasion over days that are not consecutive (S-084): each day a heading over its photos. */
.occasion-day {
    margin-bottom: var(--s-5);
}

/* The block ends the facts with room before the Download button, which belongs to the photo, not to the occasion. */
.occasion-block {
    margin-bottom: var(--s-5);
}

/* The corrections of the whole occasion above, those of this photo alone below. */
.nav-more-separator {
    margin: var(--s-1) 0;
    border: 0;
    border-top: 1px solid var(--border);
}

/* The rename field: its label, the input and the hint as one group. */
.occasion-field {
    display: grid;
    gap: var(--s-2);
}

.occasion-field .hint {
    margin: 0;
}

.occasion-field input[aria-invalid="true"] {
    border-color: var(--danger);
}

/* What a merge keeps, read before the confirm. */
.occasion-review {
    margin: 0;
    padding: var(--s-3) var(--s-4);
    border-left: 3px solid var(--brand);
    background: var(--surface-2);
    text-wrap: pretty;
}

.occasion-question {
    font-weight: 500;
}

.occasion-done-links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2) var(--s-5);
    margin: var(--s-4) 0 0;
}

/* Thirteen types read as two columns once there is room. */
@media (min-width: 640px) {
    .occasion-types .location-choices {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        column-gap: var(--s-4);
    }
}

/* "Create occasion" (S-085): wider than the other occasion pages, for the day's grid. Its own names for what it shares
   with the location page's look. */
.occasion-panel.occasion-new {
    max-width: 960px;
}

.occasion-error-box {
    width: fit-content;
    max-width: 100%;
    margin: 0 0 var(--s-4);
}

.occasion-error-box:focus {
    outline: none;
}

.occasion-error-box:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
    border-radius: var(--r-md);
}

.occasion-error-box .note {
    margin: 0;
}

.occasion-field-label {
    font-size: var(--text-sm);
}

.occasion-name-input {
    width: 100%;
    max-width: 32rem;
}

.occasion-type-select {
    width: 100%;
    max-width: 20rem;
}

.occasion-group {
    display: grid;
    gap: var(--s-2);
    min-width: 0;
    margin: 0;
    padding: var(--s-3) var(--s-4) var(--s-4);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
}

.occasion-group legend {
    padding: 0 var(--s-1);
    font-weight: 600;
}

.occasion-group .hint,
.occasion-new-only {
    margin: 0;
}

/* The count and its hint on one side, "Select all" and "Clear selection" on the other; stacked when narrow. */
.occasion-day-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--s-2) var(--s-4);
    scroll-margin-top: var(--s-4);
}

.occasion-day-count {
    margin: 0;
    font-weight: 600;
}

.occasion-day-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
}

/* The day's photos and videos: square tiles with the tick at their corner, the time and, if any, the occasion they are
   in under them. An unticked tile is dimmed and loses its frame; the tick itself always says which. A ticked tile that
   is in another occasion says it moves here. */
.occasion-day-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: var(--s-3);
    margin: var(--s-2) 0 0;
    padding: 0;
    list-style: none;
}

@media (min-width: 640px) {
    .occasion-day-grid {
        grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
    }
}

.day-pick {
    position: relative;
    display: grid;
    gap: var(--s-1);
    min-width: 0;
    cursor: pointer;
}

.day-pick input[type="checkbox"] {
    position: absolute;
    top: 6px;
    left: 6px;
    z-index: 1;
    width: 20px;
    height: 20px;
    margin: 0;
    accent-color: var(--brand);
    box-shadow: 0 0 0 2px var(--surface);
}

.day-pick-thumb {
    position: relative;
    display: block;
    aspect-ratio: 1;
    overflow: hidden;
    background: var(--surface-2);
    border-radius: var(--r-sm);
    outline: 3px solid transparent;
    outline-offset: -3px;
}

.day-pick-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.day-pick-thumb .placeholder {
    color: var(--text-2);
    font-size: var(--text-xs);
}

.day-pick input:checked + .day-pick-thumb {
    outline-color: var(--brand);
}

.day-pick input:not(:checked) + .day-pick-thumb img {
    opacity: 0.55;
}

/* Focus is a ring outside the tile, so the ticked frame stays. */
.day-pick input:focus-visible + .day-pick-thumb {
    box-shadow: 0 0 0 3px var(--surface), 0 0 0 6px var(--focus);
}

.day-pick-time {
    color: var(--text-2);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
}

.day-pick-in,
.day-pick-moves,
.day-pick-estimated {
    justify-self: start;
    max-width: 100%;
    padding: 1px var(--s-2);
    border-radius: var(--r-sm);
    font-size: var(--text-xs);
    overflow-wrap: anywhere;
}

.day-pick-in {
    background: var(--brand-soft);
    color: var(--brand-ink);
}

.day-pick-moves {
    display: none;
    background: var(--brand);
    color: var(--surface);
}

.day-pick-estimated {
    padding: 0;
    color: var(--text-2);
}

.day-pick input:checked ~ .day-pick-in {
    display: none;
}

.day-pick input:checked ~ .day-pick-moves {
    display: inline;
}

/* On a phone the actions are a column, the main one first and every one the whole width, as S-084's confirm reads. */
@media (max-width: 600px) {
    .occasion-group {
        padding: 0;
        border: 0;
        border-radius: 0;
    }

    .occasion-group legend {
        padding: 0 0 var(--s-1);
    }

    .occasion-new-actions {
        flex-direction: column-reverse;
        align-items: stretch;
    }

    .occasion-new-actions .button {
        width: 100%;
        justify-content: center;
    }
}

@media (forced-colors: active) {
    .day-pick input:checked + .day-pick-thumb {
        outline-color: Highlight;
    }
}

/* A button that reads as the header's back link ("Back to the day", S-085): it posts the selection back. */
.link-button {
    min-height: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent);
    font: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}

/* "Create occasion with 0": marked unavailable by the script, still read and focusable. */
.occasion-new-actions [data-create][aria-disabled="true"] {
    opacity: 0.55;
    cursor: not-allowed;
}

/* Selection mode (S-086): "Select" before the month groups (and, while selecting, "Selecting · Cancel" in its place),
   then every tile a checkbox's label, its box white with a dark ring in both themes, ticked tiles drawn in with a frame
   and a bigger check; "Select all" per month (with selection.js); the action bar at the end of the form, which sticks
   to the bottom of the window once the script runs, and on a phone is one compact row. */
.selection-start {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--s-2);
    min-height: 36px;
    margin: var(--s-4) 0 calc(-1 * var(--s-3));
}

.selection-start.selecting {
    color: var(--text-2);
    font-size: var(--text-sm);
}

.month-head {
    flex-wrap: wrap;
}

.month-head .month-select {
    margin-left: auto;
    align-self: center;
    white-space: nowrap;
}

.select-tile {
    position: relative;
    display: block;
    cursor: pointer;
}

.select-tile input[type="checkbox"] {
    position: absolute;
    top: 8px;
    left: 8px;
    z-index: 2;
    display: grid;
    place-content: center;
    width: 24px;
    height: 24px;
    margin: 0;
    border-radius: 6px;
    background: #fff;
    box-shadow: 0 0 0 2px #231a14, 0 0 0 4px rgb(255 255 255 / 0.7);
    appearance: none;
    cursor: pointer;
}

.select-tile input[type="checkbox"]:checked {
    width: 28px;
    height: 28px;
    background: var(--brand);
    box-shadow: 0 0 0 2px #fff;
}

.select-tile input[type="checkbox"]:checked::after {
    content: "";
    width: 12px;
    height: 7px;
    border: solid #fff;
    border-width: 0 0 3px 3px;
    transform: translateY(-2px) rotate(-45deg);
}

.select-tile .thumb {
    outline: 3px solid transparent;
    outline-offset: -3px;
    transition: transform 120ms ease;
}

.select-tile input:checked + .thumb {
    outline-color: var(--brand);
    transform: scale(0.94);
}

/* Focus is a ring outside the tile, so the ticked frame stays. */
.select-tile input:focus-visible + .thumb {
    box-shadow: 0 0 0 3px var(--surface), 0 0 0 6px var(--focus);
}

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

/* A set-aside tile in a view that lists them. */
.select-badge {
    position: absolute;
    right: 6px;
    bottom: 6px;
    z-index: 2;
    padding: 1px 6px;
    border-radius: var(--r-sm);
    background: var(--stage);
    color: #fff;
    font-size: var(--text-xs);
}

.selection-skip:not(:focus) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.selection-skip:focus {
    display: inline-block;
    margin: var(--s-2) 0;
}

.selection-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3) var(--s-4);
    margin: var(--s-6) 0 0;
    padding: var(--s-3) var(--s-4);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface);
}

.selection-bar:focus {
    outline: none;
}

/* With the script: at the bottom of the window while the grid scrolls under it; in the flow, it never covers the last row. */
.selection-live .selection-bar {
    position: sticky;
    bottom: max(var(--s-3), env(safe-area-inset-bottom));
    z-index: 5;
    box-shadow: var(--shadow-pop);
}

.selection-status {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    column-gap: var(--s-2);
    min-width: 0;
}

.selection-count {
    margin: 0;
    font-weight: 600;
}

.selection-limit {
    grid-column: 1 / -1;
    margin: 0;
}

.selection-over .selection-count {
    color: var(--danger);
}

.selection-close {
    display: none;
}

.selection-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
}

.selection-wide {
    display: contents;
}

.selection-more {
    display: none;
}

.selection-actions [aria-disabled="true"] {
    opacity: 0.55;
    cursor: not-allowed;
}

/* A phone: one compact row under the count; "More" opens upwards over the grid. */
@media (max-width: 600px) {
    .selection-bar {
        gap: var(--s-2);
        padding: var(--s-2) var(--s-3);
    }

    .selection-status {
        width: 100%;
    }

    .selection-limit {
        display: none;
    }

    .selection-over .selection-limit {
        display: block;
    }

    .selection-close {
        display: inline-grid;
        place-content: center;
        width: 36px;
        height: 36px;
        border-radius: var(--r-sm);
        color: var(--text);
        font-size: 1.5rem;
        text-decoration: none;
    }

    .selection-actions {
        flex-wrap: nowrap;
        width: 100%;
    }

    .selection-actions .button {
        min-height: 40px;
        white-space: nowrap;
    }

    .selection-actions > [data-selection-action] {
        flex: 1 1 auto;
        justify-content: center;
    }

    .selection-wide,
    .selection-cancel,
    .selection-actions [data-selection-page] {
        display: none;
    }

    .selection-more {
        position: relative;
        display: block;
    }

    .selection-more > summary {
        list-style: none;
    }

    .selection-more > summary::-webkit-details-marker {
        display: none;
    }

    .selection-more-list {
        position: absolute;
        right: 0;
        bottom: calc(100% + 8px);
        z-index: 20;
        display: grid;
        gap: var(--s-2);
        min-width: 14rem;
        padding: var(--s-2);
        border: 1px solid var(--border);
        border-radius: var(--r-md);
        background: var(--surface);
        box-shadow: var(--shadow-pop);
    }
}

@media (forced-colors: active) {
    .select-tile input:checked + .thumb {
        outline-color: Highlight;
    }

    .select-tile input[type="checkbox"] {
        appearance: auto;
    }
}

/* Set aside and Bring back for a selection (S-086). */
.selection-panel {
    max-width: 720px;
}

.selection-links {
    margin-top: var(--s-4);
}

/* A selection's tile on "Create occasion" (S-086): where it moves from, always shown (it is not ticked again). */
.day-pick-from {
    justify-self: start;
    max-width: 100%;
    padding: 1px var(--s-2);
    border-radius: var(--r-sm);
    background: var(--brand-soft);
    color: var(--brand-ink);
    font-size: var(--text-xs);
    overflow-wrap: anywhere;
}

.occasion-selection-title {
    margin: 0;
    font-size: var(--text-md);
    font-weight: 600;
}

/* "Or create a new occasion with these" under the picker's list, apart from its confirm (S-086). */
.occasion-new-from-selection {
    margin: 0;
}

@media (max-width: 600px) {
    .occasion-form .confirm {
        flex-direction: column-reverse;
        align-items: stretch;
    }

    .occasion-form .confirm .button,
    .occasion-new-from-selection .button {
        width: 100%;
        justify-content: center;
    }
}
