/*
 * Base components shared by every screen, direction "encre & contour" (2b, DESIGN.md).
 * Second deliverable of story 1.3. Tokens only: no literal colour, no shadow, no pill on buttons.
 * Focus ring, .on-dark, .icon and .visually-hidden come from base.css.
 *
 * Buttons (reference: handoff-2b/buttons.css). Shape carries the hierarchy, not colour:
 *   .btn--primary    ink fill       one per screen at most, the committing action
 *   .btn--accent     tomato fill    reserved for capture and submit
 *   .btn--secondary  2px field rule  secondary action of equal weight (Comment faire)
 *   .btn--vote       sun fill       Voter, on the event card: the only coloured action besides tomato
 *   .btn--danger     danger fill    confirms a destructive action
 *   .btn--icon       square         icon only (close, back, viewer), never a circle
 *   .btn--badge      circle         icon only, the badge of a friendship on a profile (story 3.9): the one round
 *                                  button, and « Demander en ami » the one tomato fill besides capture and submit —
 *                                  an exception decided by Raphael on the validated mockup (2026-10-31, mockup 3.9);
 *                                  its colours are the page's (friend-badge). .badge-round draws the same circle for
 *                                  a state that is no button (« Amis »).
 *   .btn--quiet      no box         tertiary (Refuser, Plus tard), 44px target kept; used alone, without .btn
 *   .link-quiet      underlined     quiet link (Signaler ce dossier)
 */

/* ===== Buttons ===== */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-5);
    border: 2px solid transparent;
    border-radius: var(--radius-sm);
    font: var(--font-button);
    font-weight: 800;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    /* Labels wrap rather than overflow at 200% text. */
    overflow-wrap: anywhere;
    transition: transform var(--motion-duration-fast) var(--motion-easing);
}

.btn--primary {
    background: var(--color-ink);
    color: var(--color-on-ink);
}

.btn--accent {
    min-height: 2.875rem;
    background: var(--color-primary);
    color: var(--color-on-primary);
}

/* Field-brown rule: lighter than ink, still 3:1 against the cream ground. */
.btn--secondary {
    background: var(--color-surface-card);
    border-color: var(--color-line-field);
    color: var(--color-ink);
}

.btn--vote {
    background: var(--color-sun);
    color: var(--color-ink);
}

.btn--danger {
    background: var(--color-danger);
    color: var(--color-on-primary);
}

.btn--block {
    width: 100%;
}

/* Tertiary action: plain text, no underline (the underline is reserved for links). */
.btn--quiet {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--touch-min);
    min-width: var(--touch-min);
    padding: var(--space-1) var(--space-3);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-ink-muted);
    font: var(--font-label);
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    overflow-wrap: anywhere;
}

/* Pressed: the box goes down by 1px instead of losing a shadow. */
.btn:active,
.btn--quiet:active {
    transform: translateY(1px);
}

/* Unavailable: stays readable, never dimmed; radius and type of .btn kept. Still focusable (aria-disabled). */
.btn[aria-disabled="true"],
.btn:disabled {
    background: var(--color-surface-sunken);
    border-color: transparent;
    color: var(--color-ink-muted);
    cursor: default;
    transform: none;
}

/* « Soumettre » (the tomato primary action) while unavailable: still recognisably the submit, in the tomato
   tints with a dashed tomato outline, and clearly not active; readable (on-primary-soft 6.9:1), never faded. */
.btn--accent[aria-disabled="true"] {
    border: 2px dashed var(--color-primary);
    background: var(--color-primary-soft);
    color: var(--color-on-primary-soft);
}

.btn--quiet[aria-disabled="true"] {
    cursor: default;
    transform: none;
}

/* Compact: a shorter run of padding, the 44px target kept (story 4.3: « Voir les ajouts » in « À valider »). */
.btn--compact {
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-4);
}

/* Icon-only button: square target, never a circle. */
.btn--icon {
    width: var(--touch-min);
    min-width: var(--touch-min);
    padding: 0;
    background: var(--color-surface-card);
    border-color: var(--color-line-strong);
    color: var(--color-ink);
}

/* The round badge (story 3.9, see the header): a 44px circle, its icon alone, its name hidden; no colour of its own. */
.btn--badge,
.badge-round {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: var(--touch-min);
    min-width: var(--touch-min);
    height: var(--touch-min);
    padding: 0;
    border: 2px solid transparent;
    border-radius: var(--radius-full);
}
.btn--badge .icon,
.badge-round .icon {
    width: 1.375rem;
    height: 1.375rem;
}
@media (forced-colors: active) {
    .btn--badge,
    .badge-round {
        border-color: CanvasText;
    }
}

/* The label box is trimmed to the cap height: Nunito keeps more room under the baseline than above
   the capitals, so centring the full line box made the text look low and the icon off its letters. */
.btn__label {
    text-box: trim-both cap alphabetic;
}

.btn__icon {
    width: 1.25rem;
    height: 1.25rem;
    flex: none;
}

/* Quiet link: muted underlined caption, 44px target. */
.link-quiet {
    display: inline-flex;
    align-items: center;
    min-height: var(--touch-min);
    color: var(--color-ink-muted);
    font: var(--font-caption);
    text-decoration: underline;
    text-underline-offset: 0.2em;
    overflow-wrap: anywhere;
}

/* A quiet link that submits a form (« Refuser »): a button drawn as the link. */
button.link-quiet {
    padding: 0;
    border: 0;
    background: none;
    text-align: left;
    cursor: pointer;
}

/* Reason shown next to an unavailable button. */
.disabled-reason {
    color: var(--color-ink-muted);
    font: var(--font-caption);
}

/* ===== Text field ===== */
.text-field {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
}

.text-field__label {
    color: var(--color-ink);
    font: var(--font-caption);
    font-weight: 800;
}

.text-field__input {
    width: 100%;
    min-width: 0;
    min-height: 3rem;
    padding: var(--space-2) var(--space-4);
    border: 2px solid var(--color-line-field);
    /* Same rounded square as the buttons. */
    border-radius: var(--radius-sm);
    background: var(--color-surface-card);
    color: var(--color-ink);
    /* 16px: no automatic zoom in Safari. */
    font: var(--font-input);
}

textarea.text-field__input {
    min-height: 7.5rem;
    padding-block: var(--space-3);
    resize: vertical;
}

.text-field__input::placeholder {
    color: var(--color-ink-muted);
    opacity: 1;
}

.text-field__input[aria-invalid="true"] {
    border-color: var(--color-danger);
}

/* Focus: a single 3px ink border instead of the border plus the outer ring; the padding loses 1px so the
   text does not move. The transparent outline stays visible in forced-colours mode. */
.text-field__input:focus,
.text-field__input:focus-visible {
    border: 3px solid var(--color-ink);
    padding: calc(var(--space-2) - 1px) calc(var(--space-4) - 1px);
    outline: 1px solid transparent;
    outline-offset: 0;
    box-shadow: none;
}

textarea.text-field__input:focus,
textarea.text-field__input:focus-visible {
    padding-block: calc(var(--space-3) - 1px);
}

/* Passages marked in a textarea (story 2.21, deliverable 4: what the AI doubts of in a transcription, text-marks): a
   layer behind the field draws its text in a transparent ink, each passage on the soft sun with a wavy underline, as
   the uncertain passages of a version of the story (.ai-status__uncertain) — without their padding, which would move
   the text. The field turns transparent while a passage is found, and takes its surface back when read-only. */
.text-field__frame {
    position: relative;
    display: flex;
    min-width: 0;
}

.text-field__frame > .text-field__input {
    position: relative;
    z-index: 1;
}

.text-field__marks {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    overflow: hidden;
    border-radius: var(--radius-sm);
    background: var(--color-surface-card);
    color: transparent;
    font: var(--font-input);
    white-space: pre-wrap;
    overflow-wrap: break-word;
    pointer-events: none;
}

.text-field__frame--marked > .text-field__input {
    /* iOS draws an inner inset of its own on a textarea: none, the padding said again, so the text sits where the
       layer draws it. */
    -webkit-appearance: none;
    appearance: none;
    padding: var(--space-3) var(--space-4);
    background: transparent;
}

.text-field__frame--marked > .text-field__input:focus,
.text-field__frame--marked > .text-field__input:focus-visible {
    padding: calc(var(--space-3) - 1px) calc(var(--space-4) - 1px);
}

/* Read-only (a recording being heard, story 2.21): its surface hides the layer, wherever the field is. */
.text-field__frame--marked > .text-field__input[readonly] {
    background: var(--color-surface-sunken);
}

.text-field__mark {
    border-radius: 0.25rem;
    background: var(--color-sun-soft);
    color: transparent;
    text-decoration: underline wavy var(--color-sun-border);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
}

@media (forced-colors: active) {
    .text-field__marks {
        forced-color-adjust: none;
        background: Canvas;
    }

    .text-field__mark {
        background: Mark;
    }
}

.text-field__help {
    color: var(--color-ink-muted);
    font: var(--font-caption);
}

/* Error: ink text with an icon, never carried by colour alone. */
.text-field__error {
    display: flex;
    align-items: flex-start;
    gap: var(--space-1);
    color: var(--color-ink);
    font: var(--font-caption);
    font-weight: 700;
}

.text-field__error .icon {
    color: var(--color-danger);
    margin-top: 0.0625rem;
}

/* ===== Bottom sheet and confirm sheet ===== */
.sheet {
    width: 100%;
    max-width: 100%;
    max-height: calc(100dvh - var(--space-6));
    margin: auto 0 0;
    padding: 0;
    border: 0;
    border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
    background: var(--color-surface-ground);
    color: var(--color-ink);
    overflow: hidden;
}

.sheet[open] {
    display: flex;
    flex-direction: column;
}

/* The page behind an open sheet does not scroll (iOS included). */
html:has(dialog.sheet[open]) {
    overflow: hidden;
}

.sheet::backdrop {
    background: var(--color-scrim);
}

/* The panel fills the dialog: a click on the dialog element itself is a click on the backdrop. */
.sheet__panel {
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding: var(--space-2) var(--margin-mobile) calc(var(--space-5) + env(safe-area-inset-bottom, 0px));
}

.sheet__handle {
    align-self: center;
    width: 2.5rem;
    height: 0.3125rem;
    margin-bottom: var(--space-2);
    border-radius: var(--radius-full);
    background: var(--color-line-strong);
}

/* The close button stays visible: only the body scrolls. */
.sheet__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    padding-bottom: var(--space-3);
}

.sheet__title {
    min-width: 0;
    padding-top: var(--space-2);
    font: var(--font-title-section);
}

/* Programmatic focus on the title (confirm sheet): no ring on a non-interactive target, as for the h1. */
.sheet__title[tabindex="-1"]:focus {
    outline: none;
    box-shadow: none;
}

.sheet__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    min-height: 0;
    overflow-y: auto;
    /* Room for the focus ring of the content. */
    padding: var(--space-1);
    margin: calc(-1 * var(--space-1));
}

/* A footer under the body (story 2.13: « Revoir l'audience »): always visible, the body scrolling above it. */
.sheet__footer {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: 0 0 auto;
    padding-top: var(--space-3);
    margin-top: var(--space-3);
    border-top: 1px solid var(--color-line);
}

.confirm-sheet__consequences {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding-left: var(--space-5);
    font: var(--font-body);
}

.confirm-sheet__actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* A choice before the confirmation (« Effacer aussi tout le champ « Ton récit » », story 2.21): a native checkbox and its
   label, the whole line a target of 44px at least. */
.confirm-sheet__option {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--touch-min);
    margin-bottom: var(--space-3);
    font: var(--font-body);
    overflow-wrap: anywhere;
    cursor: pointer;
}

.confirm-sheet__check {
    width: 1.375rem;
    height: 1.375rem;
    flex: none;
    margin: 0;
    accent-color: var(--color-primary);
}

@media (min-width: 64rem) {
    .sheet {
        width: min(32rem, calc(100% - 2 * var(--space-8)));
        max-height: calc(100dvh - 2 * var(--space-8));
        margin: auto;
        border-radius: var(--radius-xl);
    }

    .sheet__panel {
        padding: var(--space-5) var(--space-6) var(--space-6);
    }

    .sheet__handle {
        display: none;
    }
}

/* A centred modal on every width (story 2.12: the decision of a hero, « Qui verra ce dossier »): in the middle of the
   screen over the darkened page, rounded on its four corners, no handle. */
.sheet--centered {
    width: calc(100% - 2 * var(--margin-mobile));
    max-width: 32rem;
    max-height: calc(100dvh - 2 * var(--space-4));
    margin: auto;
    border-radius: var(--radius-xl);
}

.sheet--centered .sheet__panel {
    padding: var(--space-4) var(--space-4) var(--space-5);
}

@media (min-width: 64rem) {
    .sheet--centered .sheet__panel {
        padding: var(--space-5) var(--space-6) var(--space-6);
    }
}

/* ===== Toast ===== */
.toast-region {
    position: fixed;
    left: var(--margin-mobile);
    right: var(--margin-mobile);
    /* Above the bottom tab bar and its raised capture button. */
    bottom: calc(var(--tabbar-min-height) + 2rem + env(safe-area-inset-bottom, 0px));
    z-index: 20;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    max-width: min(100%, 30rem);
    min-height: var(--touch-min);
    padding: var(--space-1) var(--space-1) var(--space-1) var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--color-ink);
    color: var(--color-on-ink);
    font: var(--font-body);
    font-weight: 700;
    pointer-events: auto;
}

.toast__message {
    min-width: 0;
    padding-block: var(--space-2);
}

.toast__close {
    display: inline-grid;
    place-items: center;
    flex: none;
    width: var(--touch-min);
    min-height: var(--touch-min);
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-on-ink);
    cursor: pointer;
}

@media (min-width: 64rem) {
    .toast-region {
        left: calc(var(--sidenav-width) + var(--space-8));
        right: var(--space-8);
        bottom: var(--space-6);
    }
}

/* ===== System banner (network only) ===== */
/* The alert region stays in the page; while its card is hidden it takes no room, the gap of main included. */
.system-banner-region:has(> .system-banner[hidden]) {
    margin-bottom: calc(-1 * var(--space-5));
}

.system-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3);
    border-radius: var(--radius-lg);
    background: var(--color-ink);
    color: var(--color-on-ink);
}

.system-banner[hidden] {
    display: none;
}

.system-banner__message {
    flex: 1 1 12rem;
    min-width: 0;
    padding-inline-start: var(--space-1);
    font: var(--font-body);
    font-weight: 700;
    overflow-wrap: anywhere;
}

/* Outlined on ink: white rule and label; the white focus ring comes from .on-dark. Narrower padding so
   that the label keeps to one line at 320px and 200% text. */
.system-banner .system-banner__retry {
    padding-inline: var(--space-2);
    background: transparent;
    border-color: var(--color-on-ink);
    color: var(--color-on-ink);
}

/* ===== Serious message ===== */
/* The explicit title carries the meaning; the danger rule only underlines it. */
.serious-message {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-4) var(--space-4) var(--space-5);
    border-left: 6px solid var(--color-danger);
    border-radius: var(--radius-lg);
    background: var(--color-surface-card);
    color: var(--color-ink);
}

.serious-message__title {
    font: var(--font-title-section);
    overflow-wrap: anywhere;
}

.serious-message__text {
    font: var(--font-body);
}

.serious-message .btn {
    margin-top: var(--space-2);
}

/* ===== List row ===== */
.list-rows {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}
.list-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    min-height: 4rem;
    padding: var(--space-3);
    border-radius: var(--radius-lg);
    background: var(--color-surface-card);
}
.list-row__thumb {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 2.875rem;
    height: 2.875rem;
    border-radius: var(--radius-sm);
    background: var(--color-primary-soft);
    color: var(--color-on-primary-soft);
    font: var(--font-title-card);
}
/* A photo in the thumbnail (the first photo of a draft): cropped to fill it. */
.list-row__thumb--photo {
    object-fit: cover;
}
.list-row__body {
    display: flex;
    flex: 1 1 10rem;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
}
.list-row__title {
    font: var(--font-title-card);
    overflow-wrap: anywhere;
}
.list-row__meta {
    color: var(--color-ink-muted);
    font: var(--font-caption);
    overflow-wrap: anywhere;
}
.list-row__badge {
    flex: 0 0 auto;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-full);
    background: var(--color-surface-sunken);
    color: var(--color-ink);
    font: var(--font-badge);
}
.list-row__badge--done {
    background: var(--color-olive-soft);
    color: var(--color-olive);
}
/* A step not settled (« En modération », story 2.9): the colors of badge-draft. */
.list-row__badge--draft {
    background: var(--color-surface-sunken);
    color: var(--color-ink-muted);
}
/* Waiting for the heroes (« En attente », story 2.10): the colors of badge-pending. */
.list-row__badge--pending {
    background: var(--color-sun-soft);
    color: var(--color-on-sun-soft);
}
/* Refused by a hero, or withdrawn (« Refusé », « Retiré », story 2.11): the colors of badge-refused. */
.list-row__badge--refused {
    border: 2px solid var(--color-ink-muted);
    background: var(--color-surface-card);
    color: var(--color-ink);
}
.list-row__actions {
    display: flex;
    flex: 0 0 auto;
    /* A long label wraps instead of widening the row (320px, 200% text). */
    min-width: 0;
    max-width: 100%;
}
/* A row that opens a page: the title is the link, stretched over the whole card; the focus ring is drawn
   around the card. Actions of the row stay above the stretched link. */
.list-row--link {
    position: relative;
}
.list-row__link {
    color: inherit;
    text-decoration: none;
}
.list-row__link::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
}
.list-row__link:focus-visible {
    outline: none;
    box-shadow: none;
}
.list-row--link:has(.list-row__link:focus-visible) {
    outline: var(--focus-ring-width) solid var(--color-focus-ring);
    outline-offset: var(--focus-ring-offset);
    box-shadow: 0 0 0 var(--focus-halo-width) var(--color-focus-halo);
}
.list-row--link .list-row__actions {
    position: relative;
    z-index: 1;
}
.icon.list-row__chevron {
    flex: 0 0 auto;
    width: 1.25rem;
    height: 1.25rem;
    color: var(--color-ink-muted);
}

/* ===== Sibling nav ===== */
/* The foot of a fiche opened from a list (DESIGN.md sibling-nav, story 3.9, deliverable C): « 2 sur 3 » centred in a
   muted bold caption, above « ‹ Dossier précédent » and « Dossier suivant › », secondary buttons in two columns of
   equal width; the place of a missing one stays empty, so that « Dossier suivant › » is always on the right. Too narrow
   for two (320px with 200% text: measured on the bar itself), the two go one under the other and the empty place is
   taken away — no empty line, no gap. */
.sibling-nav {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-3) var(--space-4);
    border-top: 2px solid var(--color-line);
    background: var(--color-surface-card);
    container-type: inline-size;
}

.sibling-nav__position {
    margin: 0;
    color: var(--color-ink-muted);
    font: var(--font-caption);
    font-weight: 800;
    text-align: center;
}

.sibling-nav__pair {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2);
}

.sibling-nav__link {
    min-width: 0;
    padding-inline: var(--space-3);
}

@container (max-width: 15rem) {
    .sibling-nav__pair {
        grid-template-columns: minmax(0, 1fr);
    }

    .sibling-nav__slot {
        display: none;
    }
}

/* ===== State badge ===== */
/* A pill with its label always written (DESIGN.md badge-draft: « Brouillon », « En modération », story 2.9;
   badge-pending: « En attente », story 2.10; badge-refused: « Refusé », « Retiré », story 2.11). */
.badge {
    display: inline-block;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-full);
    font: var(--font-badge);
}

.badge-draft {
    background: var(--color-surface-sunken);
    color: var(--color-ink-muted);
}

.badge-pending {
    background: var(--color-sun-soft);
    color: var(--color-on-sun-soft);
}

/* Accepted by a hero (« Accepté », story 2.14, the block « Héros »): the colors of list-row__badge--done. */
.badge-done {
    background: var(--color-olive-soft);
    color: var(--color-olive);
}

.badge-refused {
    border: 2px solid var(--color-ink-muted);
    background: var(--color-surface-card);
    color: var(--color-ink);
}

/* ===== People strip ===== */
/* The fiche of a dossier (DESIGN.md people-strip, story 3.1): three cells of the same width — « Héros », « Témoins »,
   « Raconté par » —, each its role, its avatars (size-lg, overlapped with the border of the card when there are
   several, three at most) and its first names written in full. The whole cell is the target (a link to a page, or a
   button that opens the list of the people); a cell without target (an excluded member alone) is the same card,
   still; without any witness, no cell « Témoins » and the two others share the width (story 4.3). Heights are
   min-heights. Under 360px (320px, 200% text), the cells go one under the other. */
.people-strip {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}
.people-strip--two {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}
.people-strip__item,
.people-strip__item > .sheet-host {
    display: flex;
    min-width: 0;
}
.people-strip__item > .sheet-host {
    flex: 1 1 auto;
}
.people-strip__cell {
    container-type: inline-size;
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    width: 100%;
    min-width: 0;
    min-height: var(--touch-min);
    margin: 0;
    padding: var(--space-2);
    border: 0;
    border-radius: var(--radius-lg);
    background: var(--color-surface-card);
    color: var(--color-ink);
    font: inherit;
    text-align: center;
    text-decoration: none;
}
.people-strip__cell--target {
    cursor: pointer;
}
.people-strip__role,
.people-strip__none {
    color: var(--color-ink-muted);
    font: var(--font-caption);
    overflow-wrap: anywhere;
}
.people-strip__avatars {
    display: flex;
    justify-content: center;
    max-width: 100%;
}
/* size-lg, never wider than its cell (a narrow phone, text enlarged): measured on the cell (container units). Three
   stacked avatars take one and a half avatars: each one after the first shows a quarter of itself. */
.avatar.people-strip__avatar {
    --people-avatar: min(4rem, 100cqi);
    width: var(--people-avatar);
    height: var(--people-avatar);
    /* The initial follows the circle. */
    font-size: calc(var(--people-avatar) * 0.45);
    line-height: 1;
    overflow: hidden;
}
.people-strip__avatars--stacked .avatar.people-strip__avatar {
    --people-avatar: min(4rem, 66cqi);
    border: 3px solid var(--color-surface-card);
}
.people-strip__avatars--stacked .people-strip__avatar + .people-strip__avatar {
    margin-inline-start: calc(var(--people-avatar) * -0.75);
}
.people-strip__names {
    max-width: 100%;
    font: var(--font-label);
    overflow-wrap: anywhere;
}
@media (hover: hover) {
    .people-strip__cell--target:hover .people-strip__names {
        text-decoration: underline;
    }
}
@media (max-width: 22.4375rem) {
    .people-strip {
        grid-template-columns: minmax(0, 1fr);
    }
}
@media (forced-colors: active) {
    .people-strip__cell {
        border: 1px solid CanvasText;
    }
}

/* The list of the heroes or of the witnesses (story 3.1), in a bottom-sheet opened from their cell: a line per person,
   its avatar and its first name, a link to its page (Moi for the member itself), plain text for an excluded member. */
.people-sheet__list {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
}
.people-sheet__person {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--touch-min);
    padding-block: var(--space-2);
    color: var(--color-ink);
    text-decoration: none;
}
@media (hover: hover) {
    a.people-sheet__person:hover .people-sheet__name {
        text-decoration: underline;
    }
}
.people-sheet__name {
    min-width: 0;
    font: var(--font-label);
    overflow-wrap: anywhere;
}
/* ===== Audience frame ===== */
/* « Visible par » (DESIGN.md audience-frame, story 2.11): a flat frame under the people strip, without background,
   its legend, who sees the dossier (never the name of a group), a chevron; it opens the list of the people. */
.audience-frame {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-3);
    border: 2px solid var(--color-line);
    border-radius: var(--radius-lg);
    background: transparent;
    color: var(--color-ink);
    text-align: left;
    cursor: pointer;
}
.audience-frame__legend {
    /* « Visible une fois publié par » is long: it shrinks and wraps (320px, 200% text). */
    flex: 0 1 auto;
    min-width: 0;
    color: var(--color-ink-muted);
    font: var(--font-caption);
    overflow-wrap: anywhere;
}
.audience-frame__who {
    flex: 1 1 auto;
    min-width: 0;
    font: var(--font-label);
    overflow-wrap: anywhere;
}
.audience-frame__chevron {
    flex: 0 0 auto;
    width: 1.25rem;
    height: 1.25rem;
    color: var(--color-ink-muted);
}
@media (forced-colors: active) {
    .audience-frame {
        border-color: ButtonText;
    }
}

/* The list of who sees the dossier, in its centred modal (story 2.12): « Témoins », then « 12 admirateurs » and what
   they are, each a grid of round avatars, the first name under each. */
.audience-sheet__group {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
}
.audience-sheet__heading {
    margin: 0;
    color: var(--color-ink-muted);
    font: var(--font-caption);
    font-weight: 800;
}
.audience-sheet__hint {
    margin: 0;
    color: var(--color-ink-muted);
    font: var(--font-caption);
}
.audience-sheet__list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));
    gap: var(--space-3) var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}
.audience-sheet__person {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    min-width: 0;
    text-align: center;
}
.audience-sheet__name {
    max-width: 100%;
    font: var(--font-label);
    overflow-wrap: anywhere;
}
.audience-sheet__empty {
    margin: 0;
    font: var(--font-body);
}

/* « Revoir l'audience » (story 2.13, deliverable 2): the audience-sheet modal with what the hero may change — who
   will see the dossier said again at each change, the people of its audience each with a round « Retirer » cross on
   its avatar, its friends as person-picker chips and its groups as boxes; the foot stays under the scrolling body. */
.audience-review__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    min-width: 0;
}
.audience-review__summary {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-1) var(--space-2);
    margin: 0;
    padding: var(--space-2) var(--space-3);
    border: 2px solid var(--color-line);
    border-radius: var(--radius-lg);
}
.audience-review__legend {
    color: var(--color-ink-muted);
    font: var(--font-caption);
}
.audience-review__who {
    font: var(--font-label);
    overflow-wrap: anywhere;
}
.audience-review__role {
    color: var(--color-ink-muted);
    font: var(--font-caption);
}
.audience-review__admirer {
    position: relative;
}
.audience-review__admirer[hidden] {
    display: none;
}
/* The cross sits on the corner of the avatar, its target still 44px. */
.audience-review__remove {
    position: absolute;
    top: calc(-1 * var(--space-3));
    right: calc(50% - 2.25rem);
    border-radius: var(--radius-full);
}
.audience-review__remove .btn__icon {
    width: 1rem;
    height: 1rem;
}
.audience-review__panel {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
.audience-review__panel[hidden],
.audience-review [data-audience-review-target="friend"][hidden] {
    display: none;
}
.audience-review__groups {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
}
.audience-review__group-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}
.audience-review__group-count {
    color: var(--color-ink-muted);
    font: var(--font-caption);
}
.audience-review__note {
    margin: 0;
    color: var(--color-ink-muted);
    font: var(--font-caption);
}

/* ===== Contested block ===== */
/* A published dossier one of its heroes contests (DESIGN.md contested-block, badge-contested; story 2.12), under its
   title, to whoever sees it: ✋, « Contesté » outlined in ink, the heroes who contest it named. */
.contested-block {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 2px dashed var(--color-sun-border);
    border-radius: var(--radius-md);
    background: var(--color-sun-soft);
    color: var(--color-ink);
}
.contested-block__hand {
    flex: 0 0 auto;
    font-size: 1.25rem;
    line-height: 1.4;
}
.contested-block__body {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    min-width: 0;
}
.contested-block__text {
    margin: 0;
    font: var(--font-body);
    overflow-wrap: anywhere;
}
.badge-contested {
    display: inline-block;
    padding: var(--space-1) var(--space-2);
    border: 2px solid var(--color-ink);
    border-radius: var(--radius-xs);
    background: transparent;
    color: var(--color-ink);
    font: var(--font-badge);
    font-weight: 800;
}

/* ===== Filter selected ===== */
/* A filter applied to a list (DESIGN.md filter-chip-selected; story 3.6, deliverable 2): an ink pill « # la tente », 44px
   high, its ✕ a 44px link that removes it; the focus ring drawn outside it, on the page ground. Without label (a
   filter that tells nothing of itself), the pill reads « Filtre ». */
.filter-chip-selected {
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    min-height: var(--touch-min);
    padding-left: var(--space-3);
    border-radius: var(--radius-full);
    background: var(--color-ink);
    color: var(--color-on-ink);
    font: var(--font-label);
}
.filter-chip-selected__label {
    min-width: 0;
    overflow-wrap: anywhere;
}
.filter-chip-selected__prefix {
    opacity: 0.75;
}
.filter-chip-selected__remove {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    min-width: var(--touch-min);
    min-height: var(--touch-min);
    border-radius: var(--radius-full);
    color: inherit;
}
/* The pills of the search of the tab Dossiers (story 3.8, deliverable 2): a hero on the primary soft ground, its
   silhouette before its first name in bold; a tag on the olive soft ground, « #ski ». */
.filter-chip-selected--hero {
    background: var(--color-primary-soft);
    color: var(--color-on-primary-soft);
}
.filter-chip-selected--hero .filter-chip-selected__text {
    font-weight: 800;
}
.filter-chip-selected--tag {
    background: var(--color-olive-soft);
    color: var(--color-olive);
}
.filter-chip-selected__label {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
}
.icon.filter-chip-selected__icon {
    width: 1rem;
    height: 1rem;
    margin-right: var(--space-1);
    stroke-width: 2.4;
}
@media (forced-colors: active) {
    .filter-chip-selected {
        border: 2px solid CanvasText;
    }
}

/* ===== Hero responses ===== */
/* « Droit de réponse » / « Droits de réponse » on the fiche (DESIGN.md hero-response; story 3.4, deliverable 2): a card
   per hero on the card ground, an 8px sun rule, the avatar and « Ludovic, le héros », the response between quotation
   marks in the response type — distinct from the story and from the comments. The member's own one in its state, its
   field unfolded in place. */
.hero-responses {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    min-width: 0;
}
.hero-responses__title {
    margin: 0;
    font: var(--font-title-section);
}
.hero-response {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-4) var(--space-4) var(--space-5);
    border-left: 8px solid var(--color-sun);
    border-radius: var(--radius-xl);
    background: var(--color-surface-card);
    color: var(--color-ink);
    min-width: 0;
}
/* Focused only by the fiche (an anchor followed, back from « Envoyer »): its ring always shown. */
.hero-response:focus,
.hero-responses__own:focus {
    outline: var(--focus-ring-width) solid var(--color-focus-ring);
    outline-offset: var(--focus-ring-offset);
}
.hero-response__by {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font: var(--font-label);
}
.hero-response__text {
    margin: 0;
    font: var(--font-response);
    white-space: pre-line;
    overflow-wrap: anywhere;
}
.hero-response__status {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font: var(--font-caption);
}
.hero-response__failed {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
}
.hero-response__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}
.hero-responses__write {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-4);
    border: 2px dashed var(--color-line-strong);
    border-radius: var(--radius-xl);
}
.hero-responses__write p {
    margin: 0;
    font: var(--font-body);
}
.hero-responses__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    border: 2px solid var(--color-line);
    border-radius: var(--radius-xl);
}
.hero-responses__form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}
.hero-decision__response {
    min-width: 0;
}
.contested-block__link {
    display: inline-flex;
    align-items: center;
    min-height: var(--touch-min);
    color: var(--color-ink);
    font: var(--font-label);
    text-decoration: underline;
    text-underline-offset: 0.2em;
    overflow-wrap: anywhere;
}
@media (forced-colors: active) {
    .hero-response {
        border-left-color: CanvasText;
    }
}

/* ===== Tags ===== */
/* The tags of the fiche (DESIGN.md tag; story 3.6): card pills « # la tente », a 2px line border, 44px high, wrapping
   in a row; the member's own one still judged with « vérification… ». Under the list, never one of its items,
   « Ajouter un tag »: a dashed pill of the same size, which opens the picker-popover (the bottom sheet): the combobox
   and its suggestions as rows. */
.sheet-tags {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
}
/* What the section shows, a part of its own that the screen of a draft replaces in place (story 3.6, deliverable 3):
   laid out as if its children were the section's. */
.sheet-tags__shown {
    display: contents;
}
.sheet-tags__refused:empty {
    display: none;
}
.sheet-tags__refused {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.sheet-tags__add {
    display: flex;
    margin-top: var(--space-1);
}
.tag-list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}
.tag-list__item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
}
.tag {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--touch-min);
    max-width: 100%;
    padding: 0 var(--space-3);
    border: 2px solid var(--color-line);
    border-radius: var(--radius-full);
    background: var(--color-surface-card);
    color: var(--color-ink);
    font: var(--font-label);
    text-decoration: none;
    overflow-wrap: anywhere;
    cursor: pointer;
}
/* A tag still judged: the same pill, as plain text (no link). */
span.tag {
    cursor: default;
}
.tag__hash {
    color: var(--color-ink-muted);
}
.tag--add {
    border-style: dashed;
    border-color: var(--color-line-strong);
    background: transparent;
}
.tag-list__item--pending .tag,
.tag-list__item--failed .tag {
    border-style: dashed;
}
.tag-list__status {
    color: var(--color-ink-muted);
    font: var(--font-caption);
    font-style: italic;
}
.tag-picker__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-bottom: var(--space-4);
}
.tag-picker__options {
    display: flex;
    flex-direction: column;
    margin: var(--space-2) 0 0;
    padding: 0;
    list-style: none;
}
.tag-picker__options[hidden],
.tag-picker__form .text-field__error[hidden] {
    display: none;
}
.tag-picker__option {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--color-ink);
    font: var(--font-body);
    overflow-wrap: anywhere;
    cursor: pointer;
}
.tag-picker__option--create {
    font-weight: 700;
}
.tag-picker__option[aria-selected="true"] {
    background: var(--color-primary-soft);
    color: var(--color-on-primary-soft);
    outline: 2px solid var(--color-ink);
    outline-offset: -2px;
}
@media (forced-colors: active) {
    .tag-picker__option[aria-selected="true"] {
        outline-color: Highlight;
    }
}

/* ===== Rating ===== */
/* The rating on the fiche (DESIGN.md rating-stars; story 3.7): a card. « Ta note », five native radios, visually
   hidden but focusable and announced (« 4 étoiles »), each drawn as a star of 28px in a zone of 44px: filled up to the
   one chosen (sun, sun-strong outline), the others an outline only — the state told by the shape, not by the colour
   alone. Then « Ta note : 4 sur 5 », what the block says when a rating was not saved (in ink, never red), and the
   average in caption. */
.sheet-rating {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
    padding: var(--space-3) var(--space-3) var(--space-4);
    border-radius: var(--radius-xl);
    background: var(--color-surface-card);
    color: var(--color-ink);
}
.sheet-rating__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
    margin: 0;
}
.rating-stars {
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
}
.rating-stars__legend {
    padding: 0 0 0 var(--space-2);
    font: var(--font-title-card);
    font-weight: 900;
}
.rating-stars__row {
    display: flex;
    flex-wrap: wrap;
}
.rating-stars__star {
    position: relative;
    display: grid;
    place-items: center;
    width: var(--touch-min);
    height: var(--touch-min);
    border-radius: var(--radius-full);
    cursor: pointer;
}
.rating-stars__radio {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
    pointer-events: none;
}
.rating-stars__glyph {
    display: block;
    width: 28px;
    height: 28px;
    fill: var(--color-surface-card);
    stroke: var(--color-sun-strong);
    stroke-width: 2;
}
/* Filled up to the star chosen: itself and every star before it. */
.rating-stars__star:has(.rating-stars__radio:checked) .rating-stars__glyph,
.rating-stars__star:has(~ .rating-stars__star .rating-stars__radio:checked) .rating-stars__glyph {
    fill: var(--color-sun);
}
.rating-stars__star:has(.rating-stars__radio:focus-visible) {
    outline: var(--focus-ring-width) solid var(--color-focus-ring);
    outline-offset: calc(-1 * var(--focus-ring-width));
}
.sheet-rating__own {
    margin: 0;
    padding-left: var(--space-2);
    font: var(--font-body);
    font-weight: 800;
}
.sheet-rating__failed {
    margin: 0;
    padding-left: var(--space-2);
    color: var(--color-ink);
    font: var(--font-label);
}
.sheet-rating__failed[hidden] {
    display: none;
}
.sheet-rating__average {
    margin: 0;
    padding-left: var(--space-2);
    color: var(--color-ink-muted);
    font: var(--font-caption);
    overflow-wrap: anywhere;
}
.sheet-rating__glyph,
.list-row__rating-glyph {
    color: var(--color-sun-strong);
}
/* Forced colours: the stars filled in the text colour, the others an outline. */
@media (forced-colors: active) {
    .rating-stars__glyph {
        fill: Canvas;
        stroke: CanvasText;
        forced-color-adjust: none;
    }
    .rating-stars__star:has(.rating-stars__radio:checked) .rating-stars__glyph,
    .rating-stars__star:has(~ .rating-stars__star .rating-stars__radio:checked) .rating-stars__glyph {
        fill: CanvasText;
    }
    .rating-stars__star:has(.rating-stars__radio:focus-visible) {
        outline-color: Highlight;
    }
}

/* ===== Comments ===== */
/* « Commentaires » on the fiche (DESIGN.md comment; story 3.5): a list of comments, each the avatar (38px) and a card
   bubble whose corner towards the avatar is the small one, the first name in caption 800, the text in body. Under the
   list, « Ton commentaire »: a field and the round tomato button ↑ « Envoyer le commentaire ». */
.sheet-comments {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    min-width: 0;
}
.sheet-comments__title {
    margin: 0;
    font: var(--font-title-section);
}
.sheet-comments__empty {
    margin: 0;
    color: var(--color-ink-muted);
    font: var(--font-body);
}
.comment-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}
.comment-list:not(:has(> li)) {
    display: none;
}
/* The new comments held back under the list (story 3.5, deliverable 2): « N nouveaux commentaires », a secondary
   button, until a tap puts them at the end of the list. */
.comments-buffer {
    display: flex;
}
.comments-buffer[hidden] {
    display: none;
}
.comment {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    min-width: 0;
}
.comment--rejected {
    display: block;
}
.comment__bubble {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-xs) var(--radius-md) var(--radius-md) var(--radius-md);
    background: var(--color-surface-card);
    color: var(--color-ink);
}
.comment__author {
    margin: 0;
    color: var(--color-ink-muted);
    font: var(--font-caption);
    font-weight: 800;
    overflow-wrap: anywhere;
}
.comment__text {
    margin: 0;
    font: var(--font-body);
    white-space: pre-line;
    overflow-wrap: anywhere;
}
.comment__status {
    margin: 0;
    color: var(--color-ink-muted);
    font: var(--font-caption);
    font-style: italic;
}
.comment__failed {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
}
.comment-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
}
.comment-form__row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
}
.comment-form__input {
    flex: 1 1 auto;
    border-radius: var(--radius-full);
}
.comment-form__send {
    display: grid;
    flex: none;
    place-items: center;
    width: 2.875rem;
    height: 2.875rem;
    padding: 0;
    border: 0;
    border-radius: var(--radius-full);
    background: var(--color-primary);
    color: var(--color-on-primary);
    box-shadow: 0 4px 12px var(--color-shadow-primary);
    cursor: pointer;
}
.comment-form__send:active {
    transform: translateY(1px);
}
.comment-form__send .icon {
    width: 1.375rem;
    height: 1.375rem;
    stroke-width: 3;
}
.comment-form .text-field__error[hidden] {
    display: none;
}
@media (forced-colors: active) {
    .comment__bubble {
        border: 1px solid CanvasText;
    }
    .comment-form__send {
        border: 2px solid ButtonText;
    }
}

/* ===== Hero decision ===== */
/* « Ta décision » at the foot of the fiche (DESIGN.md hero-decision, decision-option; story 2.12): full-width options,
   card ground, ink, a 2px field rule, each its title and its consequence; « Refuser » after a double gap. None in
   tomato: nothing pushes. The decision already taken is current: an ink rule doubled inside and « Ton choix ». */
.hero-decision {
    display: flex;
    flex-direction: column;
    gap: var(--decision-gap);
    min-width: 0;
}
.hero-decision__title {
    margin: 0;
    font: var(--font-title-section);
}
.hero-decision__form {
    display: contents;
}
.hero-decision__refuse {
    margin-top: var(--decision-gap);
}
.decision-option {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
    width: 100%;
    min-height: 4rem;
    padding: var(--space-3) var(--space-4);
    border: 2px solid var(--color-line-field);
    border-radius: var(--radius-lg);
    background: var(--color-surface-card);
    color: var(--color-ink);
    text-align: left;
    cursor: pointer;
}
.decision-option:active {
    transform: translateY(1px);
}
/* The decision already taken: an ink outline over the rule, no change of size. */
.decision-option[aria-current="true"] {
    border-color: var(--color-ink);
    outline: 1px solid var(--color-ink);
    outline-offset: -3px;
}
/* The focus ring of every control, kept over the outline of the decision taken. */
.decision-option:focus-visible,
.decision-option[aria-current="true"]:focus-visible {
    outline: var(--focus-ring-width) solid var(--color-focus-ring);
    outline-offset: var(--focus-ring-offset);
}
.decision-option__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-1) var(--space-2);
}
.decision-option__title {
    font: var(--font-button);
    overflow-wrap: anywhere;
}
.decision-option__current {
    color: var(--color-ink);
    font: var(--font-caption);
    font-weight: 800;
}
.decision-option__consequence {
    color: var(--color-ink-muted);
    font: var(--font-caption);
    overflow-wrap: anywhere;
}
@media (forced-colors: active) {
    .decision-option,
    .contested-block,
    .badge-contested {
        border-color: ButtonText;
    }
    .decision-option[aria-current="true"] {
        border-color: Highlight;
        outline: 2px solid Highlight;
    }
    .decision-option:focus-visible,
    .decision-option[aria-current="true"]:focus-visible {
        outline: var(--focus-ring-width) solid CanvasText;
    }
}

@media (forced-colors: active) {
    .badge,
    .list-row__badge {
        border: 1px solid CanvasText;
    }
}

/* ===== Pending item (« À valider ») ===== */
/* A card with a sun rule on its left (DESIGN.md pending-item); decided in the row. */
.pending-item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    border: 2px solid var(--color-sun-border);
    border-left-width: 6px;
    border-radius: var(--radius-md);
    background: var(--color-surface-card);
}

/* Friend request row: the avatar of the requester, the sentence, then « Accepter » and « Refuser », which
   go under the sentence when the line is too narrow (320px, 200% text). The row of a dossier waiting for the
   member's decision (story 2.12, deliverable 3) is laid out the same: the avatar of its author, the sentence,
   then « Voir ». */
.friend-request-row__text,
.awaiting-decision-row__text {
    flex: 1 1 10rem;
    min-width: 0;
    font: var(--font-body);
    overflow-wrap: anywhere;
}

.friend-request-row__text b,
.awaiting-decision-row__text b {
    font-weight: 800;
}

.friend-request-row__actions,
.awaiting-decision-row__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
}

/* The row of a dossier whose additions wait for the member's decision (story 4.3): the thumbnail of the dossier and
   the sentence, under it the list of the additions, then « Voir les ajouts » alone, at the end of the line (under the
   sentence when the line is too narrow: 320px, 200% text). */
.awaiting-additions-row {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.awaiting-additions-row__line {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    min-width: 0;
}

.awaiting-additions-row__thumb {
    flex: 0 0 auto;
    width: 2.875rem;
    height: 2.875rem;
    border-radius: var(--radius-sm);
    object-fit: cover;
    background: var(--color-surface-sunken);
}

.awaiting-additions-row__text {
    flex: 1 1 10rem;
    min-width: 0;
    overflow-wrap: anywhere;
}

.awaiting-additions-row__sentence {
    margin: 0;
    font: var(--font-body);
}

.awaiting-additions-row__sentence b {
    font-weight: 800;
}

.awaiting-additions-row__list {
    margin: 0.125rem 0 0;
    color: var(--color-ink-muted);
    font: var(--font-caption);
    font-weight: 600;
}

.awaiting-additions-row__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--space-3);
}

/* ===== Avatar ===== */
/* The initial of the first name on one of the avatar colours (avatar--1 to avatar--5, stable for a member:
   AvatarExtension), no photo; always decorative, next to the first name. */
.avatar {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 2.875rem;
    height: 2.875rem;
    border-radius: var(--radius-full);
    color: var(--color-on-ink);
    font: var(--font-title-card);
    font-weight: 900;
}
.avatar--large {
    width: 4rem;
    height: 4rem;
    font: var(--font-headline);
}
/* A comment (DESIGN.md comment, story 3.5): 38px. */
.avatar--small {
    width: 2.375rem;
    height: 2.375rem;
    font: var(--font-label);
    font-weight: 900;
}

/* ===== Person picker chip ===== */
.person-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}
.person-chips[hidden] {
    display: none;
}
.person-chip {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    max-width: 100%;
    min-height: var(--touch-min);
    padding: var(--space-1) var(--space-3) var(--space-1) var(--space-1);
    border: 2px solid var(--color-line-field);
    border-radius: var(--radius-full);
    background: var(--color-surface-card);
    color: var(--color-ink);
    font: var(--font-label);
    cursor: pointer;
}
.person-chip__input {
    position: absolute;
    opacity: 0;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
}
.person-chip:has(.person-chip__input:checked),
.person-chip--selected {
    border-color: var(--color-on-primary-soft);
    background: var(--color-primary-soft);
    color: var(--color-on-primary-soft);
}
.person-chip:has(.person-chip__input:focus-visible) {
    outline: var(--focus-ring-width) solid var(--color-focus-ring);
    outline-offset: var(--focus-ring-offset);
}
.person-chip__avatar {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: var(--radius-full);
    color: var(--color-on-ink);
    font: var(--font-badge);
}
.avatar--1 { background: var(--color-avatar-1); }
.avatar--2 { background: var(--color-avatar-2); }
.avatar--3 { background: var(--color-avatar-3); }
.avatar--4 { background: var(--color-avatar-4); }
.avatar--5 { background: var(--color-avatar-5); }
.person-chip__name {
    min-width: 0;
    overflow-wrap: anywhere;
}
.person-chip__check {
    display: none;
}
.person-chip:has(.person-chip__input:checked) .person-chip__check {
    display: inline;
}
.person-chip .icon.person-chip__mark {
    width: 1rem;
    height: 1rem;
}

/* ===== Check field ===== */
.check-field {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--touch-min);
    color: var(--color-ink);
    font: var(--font-body);
    cursor: pointer;
}
.check-field__input {
    flex: 0 0 auto;
    width: 1.5rem;
    height: 1.5rem;
    margin: 0;
    accent-color: var(--color-ink);
}

/* ===== Invite rows (Admin > Invitations) ===== */
.invite-form {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-4);
}
.invite-form > .btn--block,
.invite-form__friends {
    align-self: stretch;
}
.invite-rows {
    display: flex;
    flex-direction: column;
    align-self: stretch;
    gap: var(--space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}
.invite-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-2);
    padding: var(--space-3);
    border-radius: var(--radius-lg);
    background: var(--color-surface-card);
}
.invite-row__fields {
    display: flex;
    flex: 1 1 12rem;
    flex-wrap: wrap;
    gap: var(--space-2);
    min-width: 0;
}
.invite-row__fields > .text-field {
    flex: 1 1 10rem;
}
.invite-row__error {
    display: flex;
    flex: 1 1 100%;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
}
.invite-form__friends {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
}
.invite-form__note {
    color: var(--color-ink);
    font: var(--font-body);
}

/* Windows high contrast: keep the sheet and the toast outlined once backgrounds are replaced. */
@media (forced-colors: active) {
    .system-banner,
    .sheet,
    .toast {
        border: 2px solid CanvasText;
    }

    .serious-message {
        border: 2px solid CanvasText;
        border-left-width: 6px;
    }

    .person-chip:has(.person-chip__input:checked),
    .person-chip--selected {
        border-width: 3px;
    }

    .btn[aria-disabled="true"],
    .btn:disabled {
        color: GrayText;
        border-color: GrayText;
    }
}

/* skeleton-line (story 2.5): a line of text still to come, drawn in the skeleton colour, breathing slowly
   (still for less motion, base.css). Always decorative: what is awaited is said in words next to it. */
.skeleton-line {
    display: block;
    width: 100%;
    height: 0.875rem;
    border-radius: var(--radius-full);
    background: var(--color-skeleton);
    animation: skeleton-breathe 1.6s ease-in-out infinite alternate;
}

.skeleton-line--short {
    width: 60%;
}

@keyframes skeleton-breathe {
    to {
        opacity: 0.45;
    }
}

@media (forced-colors: active) {
    .skeleton-line {
        background: GrayText;
    }
}

/* ===== Heading of « Nouveau dossier » (story 2.19) ===== */
/* The title, the badge « Brouillon » and the state of the save, on one line while they fit, wrapped otherwise. */
.dossier-heading {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
}

.dossier-heading > .page-title {
    margin-inline-end: var(--space-1);
}

/* The state of the save: words first, the dot only decorates them (never the colour alone). On a line of its own, under
   the title and the badge: its words change as the screen saves itself, and nothing moves with them. */
.save-state {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
    margin: 0;
    color: var(--color-ink-muted);
    font: var(--font-caption);
}

.save-state__dot {
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--radius-full);
    background: var(--color-line-strong);
}

.save-state--saved .save-state__dot {
    background: var(--color-olive);
}

.save-state--saving .save-state__dot {
    background: var(--color-sun-border);
}

.save-state--failed {
    color: var(--color-danger);
}

.save-state--failed .save-state__dot {
    background: var(--color-danger);
}

.save-state__retry:not([hidden]) {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

/* « Réessayer »: a link-like button, its touch target 44px high. */
.save-state__button {
    min-height: 44px;
    padding: 0 var(--space-1);
    border: 0;
    background: transparent;
    color: var(--color-primary-strong);
    font: var(--font-caption);
    font-weight: 800;
    text-decoration: underline;
    cursor: pointer;
}

/* The screen saves itself (new-dossier): « Enregistrer » is only for a browser without JavaScript. */
html[data-autosave] #dossier-save-button {
    display: none;
}

@media (forced-colors: active) {
    .save-state__dot {
        background: CanvasText;
    }
    .save-state__button {
        color: LinkText;
    }
}

/* ===== Round « i » and its window (components/_info_round.html.twig, info-round, story 4.3) =====
   The round sits at the right of its button, over it, a button of its own: a target of 44px, a visible round of 36px
   (the card ground, an ink « i », a rim of the strong line) whose rim turns while what is missing is a wait (data-busy:
   its top drawn tomato), fixed under prefers-reduced-motion. Its window is drawn over the page, above the button,
   right-aligned, ink ground, never changing the height of what holds it; its tip points at the round. Without
   JavaScript (no html[data-js]), the round is not shown and the panel stays in the flow as its page draws it. */
.info-round-host {
    position: relative;
    display: flex;
}

.info-round-host > .btn {
    flex: 1 1 auto;
    max-width: 100%;
}

.info-round {
    display: none;
}

html[data-js] .info-round-host:has(.info-round) > .btn {
    padding-inline-end: calc(var(--touch-min) + var(--space-2));
}

html[data-js] .info-round {
    position: absolute;
    top: 50%;
    right: 1px;
    display: grid;
    place-items: center;
    width: var(--touch-min);
    height: var(--touch-min);
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--color-ink);
    transform: translateY(-50%);
    cursor: pointer;
}

.info-round__mark {
    position: relative;
    display: grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--radius-full);
    background: var(--color-surface-card);
    font: var(--font-button);
    font-style: normal;
}

/* The rim, apart from the « i », so that it turns alone. */
.info-round__mark::before {
    content: "";
    position: absolute;
    inset: 0;
    border: 3px solid var(--color-line-strong);
    border-radius: inherit;
}

.info-round[data-busy] .info-round__mark::before {
    border-top-color: var(--color-primary);
}

@media (prefers-reduced-motion: no-preference) {
    .info-round[data-busy] .info-round__mark::before {
        animation: info-round-spin 1s linear infinite;
    }
}

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

html[data-js] .info-round__panel {
    position: absolute;
    right: var(--info-round-panel-right, 0px);
    bottom: calc(100% + var(--space-2));
    z-index: 6;
    box-sizing: border-box;
    width: min(20rem, calc(100% - 2 * var(--margin-mobile)));
    max-height: var(--info-round-panel-max-height, min(60vh, 28rem));
    margin: 0;
    padding: var(--space-2) var(--space-3);
    overflow-y: auto;
    border: 0;
    border-radius: var(--radius-sm);
    background: var(--color-ink);
    box-shadow: 0 8px 24px var(--color-scrim);
    color: var(--color-on-ink);
}

html[data-js] .info-round__panel:not([data-open]) {
    display: none;
}

html[data-js] .info-round__panel :focus-visible {
    outline-color: var(--color-focus-ring-on-dark);
    box-shadow: none;
}

@media (forced-colors: active) {
    .info-round__mark::before {
        border-color: CanvasText;
    }

    html[data-js] .info-round__panel {
        border: 2px solid CanvasText;
    }
}
