/*
 * Tibius Transit — feuille de style produit.
 *
 * Elle ne redéclare aucune valeur du Design System : toutes les couleurs, tous
 * les rayons, toutes les durées viennent des jetons `--tds-*` servis depuis
 * /vendor/tibius-design-system.
 *
 * L'OSSATURE n'est pas ici non plus (0.34.1) : en-tête, navigation, zone compte,
 * pied, en-tête de page, états, bandeaux, badges, boutons et champs sont rendus
 * par la structure commune du Design System (`tibius-structure.css`, classes
 * `tds-*`). Cette feuille ne porte que ce qui est propre à Transit — signes,
 * thermomètre, passages, carte — et la mise en page de ses écrans.
 *
 * Une seule famille de couleurs échappe aux jetons : celle des signes de ligne.
 * Elle est fournie ligne par ligne, en variables locales `--line-paper` et
 * `--line-ink`, par le référentiel de l'opérateur — jamais écrite ici. Aucune
 * couleur d'opérateur n'habille l'interface : c'est une règle du contrat de
 * signalétique, et la voir violée reviendrait à trouver un hexadécimal de
 * ligne dans ce fichier.
 */

/* ── Fondations ──────────────────────────────────────────────────────────── */

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

html {
    font-family: var(--tds-font-family-sans);
    background: var(--tds-surface-canvas);
    color: var(--tds-text-primary);
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    font-size: var(--tds-font-size-body);
    line-height: var(--tds-line-height-body);
}

h1, h2, h3 { line-height: var(--tds-line-height-heading); margin: 0; }
h1 { font-size: var(--tds-font-size-heading2); letter-spacing: var(--tds-letter-spacing-tight); }
h2 { font-size: var(--tds-font-size-heading3); }
h3 { font-size: var(--tds-font-size-heading4); }
p { margin: 0; }

a { color: var(--tds-brand-link); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--tds-brand-action-hover); }

:where(a, button, input, summary):focus-visible {
    outline: var(--tds-focus-width) solid var(--tds-border-focus);
    outline-offset: var(--tds-focus-offset);
    border-radius: var(--tds-radius-sm);
}

/* Les chiffres se comparent en colonne : heures, attentes, numéros de quai. */
.tabular { font-variant-numeric: tabular-nums; }

/* ── Gabarit d'application ───────────────────────────────────────────────── */

/*
 * Le gabarit est celui de la structure commune : `tds-header`, `tds-content`
 * (largeur standard déclarée, gouttières des jetons), `tds-footer`. Il ne reste
 * ici que ce que Transit pose DANS ces blocs.
 */

/* Le retour du parcours de connexion, au-dessus du contenu de la page. */
.session-notice { margin-block-end: var(--tds-space-6); }

/* Le retour du signalement contextuel (« Signalement envoyé · SIG-… »), à côté de son lien, au pied. */
.report-note { margin-inline-start: var(--tds-space-2); }

/* ── Surfaces ────────────────────────────────────────────────────────────── */

/* La surface est `tds-card` ; ici, seulement son titre. */
.card__title { font-size: var(--tds-font-size-heading4); margin-bottom: var(--tds-space-3); }

.section { margin-top: var(--tds-space-10); }
.section:first-child { margin-top: 0; }
.section__title {
    font-size: var(--tds-font-size-heading3); margin-bottom: var(--tds-space-4);
    padding-bottom: var(--tds-space-2);
    border-bottom: var(--tds-border-width-default) solid var(--tds-border-default);
}

/* ── Signes de ligne ─────────────────────────────────────────────────────── */
/*
 * Trois signes, dans cet ordre : marque de mode, numéro, barre de destination.
 * Ils partagent la même hauteur sur une rangée — d'où `--sign-size`, qui ne se
 * réduit pas avec la fenêtre : un signe de ligne garde une taille lisible.
 */

.line-sign {
    --sign-size: 2rem;
    display: inline-flex; align-items: center; gap: var(--tds-space-2);
    flex: 0 0 auto;
}

.line-sign__mode,
.line-sign__number {
    height: var(--sign-size);
    min-width: var(--sign-size);
    display: inline-flex; align-items: center; justify-content: center;
    padding-inline: 0.375rem;
    /*
     * 1,25 rem en gras : c'est ce qui fait du numéro un « texte de grande
     * taille » au sens des règles de contraste, et donc ce qui autorise le
     * seuil de 3:1 appliqué à l'import. Le baisser sans revoir le seuil
     * casserait la lisibilité des signes.
     */
    font-size: 1.25rem;
    font-weight: var(--tds-font-weight-bold);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    /*
     * Capitales et chiffres n'ont pas de jambage : les centrer sur leur boîte
     * de ligne les fait paraître bas. La compensation est en em, et se
     * recalibre si la fonte change.
     */
    text-indent: 0;
}
.line-sign__mode > span,
.line-sign__number > span { transform: translateY(-0.045em); }

.line-sign__mode {
    border-radius: var(--tds-radius-full);
    background: var(--tds-surface-sunken);
    color: var(--tds-text-secondary);
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    font-size: 1rem;
}

/* Un mode sans lettre publiée porte son libellé : le disque ne s'invente pas. */
.line-sign__mode--label {
    border-radius: var(--tds-radius-role-pill);
    padding-inline: var(--tds-space-3);
    font-size: var(--tds-font-size-label);
    text-transform: uppercase;
    letter-spacing: var(--tds-letter-spacing-label);
}

.line-sign__number {
    border-radius: var(--tds-radius-role-control);
    /* Aucune couleur publiée : le neutre du thème, pas une couleur choisie. */
    background: var(--line-paper, var(--tds-surface-sunken));
    color: var(--line-ink, var(--tds-text-primary));
    border: var(--tds-border-width-default) solid var(--line-paper, var(--tds-border-strong));
}

.line-sign--lg { --sign-size: 2.75rem; }
.line-sign--lg .line-sign__number { font-size: 1.625rem; }

.operator-mention {
    display: inline-flex; align-items: center;
    font-size: var(--tds-font-size-caption);
    font-weight: var(--tds-font-weight-medium);
    color: var(--tds-text-muted);
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    border-radius: var(--tds-radius-role-pill);
    padding: 0.125rem var(--tds-space-2);
    white-space: nowrap;
}

/* ── Rangées de lignes ───────────────────────────────────────────────────── */

.line-list { list-style: none; margin: 0; padding: 0; }
.line-list > li + li { border-top: var(--tds-border-width-default) solid var(--tds-border-default); }

.line-row {
    display: flex; align-items: center; gap: var(--tds-space-3);
    padding: var(--tds-space-3) var(--tds-space-2);
    /* La cible tactile ne dépend pas de la hauteur visuelle des signes. */
    min-height: var(--tds-control-height-md);
    text-decoration: none; color: inherit;
    border-radius: var(--tds-radius-role-control);
}
.line-row:hover { background: var(--tds-action-ghost-hover); }

.line-row__body { min-width: 0; flex: 1 1 auto; display: flex; align-items: center; gap: var(--tds-space-3); }

/* La barre de destination : pleine, une seule ligne, troncature en fin. */
.destination-bar {
    flex: 1 1 auto; min-width: 0;
    background: var(--tds-surface-sunken);
    border-radius: var(--tds-radius-role-control);
    padding: 0 var(--tds-space-3);
    height: 2rem; display: flex; align-items: center;
    color: var(--tds-text-primary);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-size: var(--tds-font-size-body-small);
}

.line-row__meta { flex: 0 0 auto; display: flex; align-items: center; gap: var(--tds-space-2); }
.line-row__meta--service { flex-wrap: wrap; min-width: 0; max-width: 100%; }
.line-row__next-service { font-size: var(--tds-font-size-body-small); color: var(--tds-text-secondary); overflow-wrap: anywhere; }

/*
 * Rangée de ligne, en étroit.
 *
 * La destination NE descend PAS : la charte veut « une barre pleine, une seule
 * ligne, troncature en fin », et c'est aussi ce qui garde une liste de 89
 * lignes parcourable au pouce. C'est la mention d'opérateur qui passe dessous,
 * quand il y en a une — elle ne disparaît pas.
 */
@media (max-width: 34rem) {
    .line-row { flex-wrap: wrap; }
    .line-row__meta { flex-basis: 100%; order: 3; }
}

/*
 * ... SAUF quand la relation EST l'identité de la ligne.
 *
 * La règle ci-dessus a été écrite pour un réseau urbain, et elle y est juste :
 * le NUMÉRO identifie la ligne, la destination la qualifie, et tronquer une
 * qualification ne fait rien perdre.
 *
 * Chez un opérateur ferroviaire, le signe ne désigne aucune ligne — 374 lignes
 * portent « IC » dans le jeu SNCB — et c'est la relation qui distingue. La
 * tronquer à 375 px rendait « Bruxelles-Midi ↔ Rotterdam Centraal (NL) »
 * indiscernable de « Bruxelles-Midi ↔ Rotterdam ... » : deux relations, une
 * seule chaîne lisible. C'est de l'information essentielle masquée.
 *
 * La barre passe donc à la ligne, sur toute la largeur, et sa hauteur suit son
 * contenu. Le déclencheur n'est pas le mode ni l'opérateur : c'est la même
 * mesure qui retire la grille de signes, posée par le gabarit.
 */
.line-row--relation .destination-bar {
    white-space: normal;
    height: auto;
    min-height: var(--tds-control-height-sm, 2rem);
    padding-block: var(--tds-space-1);
}

@media (max-width: 34rem) {
    .line-row--relation .destination-bar { flex-basis: 100%; order: 2; }
}

/* ── Tableau des passages ────────────────────────────────────────────────── */

.departures { list-style: none; margin: 0; padding: 0; }
.departures > li + li { border-top: var(--tds-border-width-default) solid var(--tds-border-default); }

.departure {
    display: flex; align-items: center; gap: var(--tds-space-3);
    padding: var(--tds-space-3) var(--tds-space-2);
    min-height: var(--tds-control-height-md);
}

/* L'attente se lit en premier ; la destination qualifie, elle ne domine pas. */
.departure__wait {
    flex: 0 0 auto; min-width: 6.5rem;
    font-size: var(--tds-font-size-body-large);
    font-weight: var(--tds-font-weight-semibold);
    font-variant-numeric: tabular-nums;
}
.departure__clock { display: block; font-size: var(--tds-font-size-caption); font-weight: var(--tds-font-weight-regular); color: var(--tds-text-muted); }
.departure__body { min-width: 0; flex: 1 1 auto; display: flex; align-items: center; gap: var(--tds-space-3); }

/*
 * Les mentions d'une rangée de passage — voie, numéro de course, opérateur —
 * restent EN LIGNE. Elles réutilisaient `.line-row__meta`, qui passe sur une
 * seconde ligne en étroit : la rangée de passage y gagnait une troisième ligne
 * et une hauteur irrégulière d'un passage à l'autre. Trouvé en recette le
 * 27/08/2026.
 */
.departure__meta { flex: 0 0 auto; display: flex; align-items: center; gap: var(--tds-space-2); }

/*
 * Rangée de passage, en étroit : l'attente reste seule à gauche, le signe et la
 * destination descendent sous elle.
 *
 * Le signe et la barre sont frères DANS `__body` : c'est ce qui fait que la
 * barre, à `flex-basis: 100%`, passe réellement sous le signe. Sortir le signe
 * de ce conteneur laisserait la barre seule à se replier — donc au même
 * endroit, et la règle serait tenue sur le papier seulement. La recette du
 * 27/08/2026 a trouvé exactement ce défaut sur la rangée de ligne.
 */
@media (max-width: 34rem) {
    /*
     * Une grille, et non un `flex-wrap`. En flex, la largeur réelle de
     * l'attente décide du repli : « 1 minute » et « 2 minutes » ne font pas la
     * même largeur, et la rangée passait de deux à trois lignes d'un passage à
     * l'autre. La recette du 27/08/2026 a mesuré des hauteurs de 100, 112 et
     * 158 pixels dans une même liste. Une grille rend la hauteur indépendante
     * du texte de l'attente.
     */
    .departure {
        display: grid;
        grid-template-columns: minmax(6.5rem, auto) 1fr;
        column-gap: var(--tds-space-3);
        row-gap: var(--tds-space-2);
        align-items: start;
    }
    .departure__wait { grid-column: 1; grid-row: 1; }
    .departure__body { grid-column: 2; grid-row: 1; flex-wrap: wrap; }
    .departure__body .destination-bar { flex-basis: 100%; order: 2; }
    .departure__meta { grid-column: 2; grid-row: 2; flex-wrap: wrap; }
}

.quality-note {
    display: inline-flex; align-items: center; gap: var(--tds-space-2);
    font-size: var(--tds-font-size-body-small);
    color: var(--tds-text-secondary);
}

/* ── Parcours ────────────────────────────────────────────────────────────── */

.route-stops { list-style: none; margin: 0; padding: 0; }
.route-stops li {
    display: flex; align-items: center; gap: var(--tds-space-3);
    padding: var(--tds-space-2) 0;
    min-height: var(--tds-control-height-sm);
}
.route-stops__rank {
    flex: 0 0 auto; width: 2rem; text-align: end;
    font-variant-numeric: tabular-nums;
    color: var(--tds-text-muted); font-size: var(--tds-font-size-caption);
}
.route-stops__mark {
    flex: 0 0 auto; width: 0.75rem; height: 0.75rem;
    border-radius: var(--tds-radius-full);
    /* Le thermomètre garde le signe de sa ligne sur toute sa longueur. */
    background: var(--line-paper, var(--tds-border-strong));
    border: var(--tds-border-width-strong) solid var(--tds-surface-default);
    box-shadow: 0 0 0 1px var(--line-paper, var(--tds-border-strong));
}
.route-stops li:last-child .route-stops__mark,
.route-stops li:first-child .route-stops__mark { width: 1rem; height: 1rem; }

/* ── Recherche ───────────────────────────────────────────────────────────── */

.search-form { display: flex; gap: var(--tds-space-2); flex-wrap: wrap; align-items: flex-end; }
.search-form__field { flex: 1 1 16rem; min-width: 0; }

/* Les champs et les boutons sont `tds-field` et `tds-button` ; un champ de date est `tds-field--compact`. */

/* ── Listes de faits ─────────────────────────────────────────────────────── */

/* Une fiche — paires libellé / valeur — est `tds-kv`. */

.chip-row { display: flex; flex-wrap: wrap; gap: var(--tds-space-2); }
.chip {
    display: inline-flex; align-items: center; gap: var(--tds-space-2);
    padding: var(--tds-space-1) var(--tds-space-3);
    border-radius: var(--tds-radius-role-pill);
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    background: var(--tds-surface-default);
    font-size: var(--tds-font-size-body-small);
    text-decoration: none; color: var(--tds-text-primary);
    min-height: var(--tds-control-height-sm);
}
.chip:hover { background: var(--tds-action-ghost-hover); color: var(--tds-text-primary); }

/*
 * Le décompte d'une pastille, et le troisième endroit où le même défaut sort.
 *
 * `--tds-text-muted` tient largement sur le fond de page. Sur une surface
 * distincte, il ne tient plus : la 0.6.0 l'avait mesuré à 4,303 sur une
 * surface creusée, la 0.7.0 sur l'onglet de sens courant, et la sonde de la
 * 0.8.0 le retrouve à **4,435 pour un seuil de 4,5** dans les pastilles de
 * filtre de `/reseau`, en sombre, aux quatre largeurs.
 *
 * Le remède est celui des deux fois précédentes, et non un nouveau : le jeton
 * SECONDAIRE. Le décompte reste plus discret que le libellé qu'il accompagne,
 * et il redevient lisible.
 */
.chip .count-note { color: var(--tds-text-secondary); }

.table-scroll { overflow-x: auto; }

/* ── Mouvement réduit ────────────────────────────────────────────────────── */

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

/* ── Utilitaires de mise en page ─────────────────────────────────────────── */
/*
 * Ces quelques classes existent pour que les vues n'aient AUCUN attribut
 * `style`. Ce n'est pas une préférence esthétique : la politique de sécurité
 * de contenu interdit les styles en ligne, sans exception ni valeur `unsafe`.
 * Une vue qui en poserait un verrait sa mise en forme silencieusement ignorée.
 */

.stack-top { margin-top: var(--tds-space-4); }
.title-row { display: flex; align-items: center; gap: var(--tds-space-4); flex-wrap: wrap; }
.count-note { color: var(--tds-text-muted); font-size: var(--tds-font-size-body-small); font-weight: var(--tds-font-weight-regular); }
/*
 * Le même texte, sur une surface CREUSÉE, ne tient plus son seuil.
 *
 * Mesuré à 320 px sur `/lignes/sncb` : `--tds-text-muted` posé sur
 * `--tds-surface-sunken` donne **4,303 pour un seuil de 4,5**, sur 36 rangées.
 * Le même jeton passe largement sur le fond de page — c'est la SURFACE qui
 * change, pas la couleur du texte.
 *
 * C'est la sœur exacte du défaut n° 3 de la 0.5.0, où c'était la TAILLE rendue
 * qui changeait sous une condition qu'on croyait tenir. La leçon est la même :
 * un seuil se vérifie sur le rendu, jamais sur l'intention.
 *
 * `--tds-text-secondary` mesure 5,632 sur cette surface, et conserve la
 * hiérarchie — il reste en retrait du texte primaire, qui est à 14,135.
 */
.destination-bar .count-note { color: var(--tds-text-secondary); }
.card__meta { color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); margin-bottom: var(--tds-space-4); }

/* ═══════════════════════════════════════════════════════════════════════════
 * 0.1.1 — ce que le référentiel théorique savait déjà dire
 * ═══════════════════════════════════════════════════════════════════════════ */

/* ── Disque de service de remplacement ───────────────────────────────────── */
/*
 * « T » sur « BUS » : le mode nominal au-dessus, celui qui assure en dessous.
 * Le disque garde sa taille de rangée — un signe ne rétrécit pas — et les deux
 * marques se partagent sa hauteur. Rien n'y est porté par la couleur : les deux
 * lettres sont écrites, et l'étiquette du signe dit la phrase entière.
 */
.line-sign__mode--relief {
    flex-direction: column;
    justify-content: center;
    gap: 0;
    line-height: 1;
    padding-inline: 0.25rem;
    min-width: calc(var(--sign-size) + 0.25rem);
}
.line-sign__mode-primary { font-size: 0.9em; font-weight: var(--tds-font-weight-bold); }
.line-sign__mode-relief {
    font-size: 0.5em;
    font-weight: var(--tds-font-weight-semibold);
    letter-spacing: var(--tds-letter-spacing-label);
    /*
     * `--tds-text-muted` mesurait 4,30 sur cette surface, à 7 px : sous le
     * seuil du texte courant, et cette marque EST une information — elle dit
     * quel mode assure le service.
     */
    color: var(--tds-text-secondary);
}

/* ── Grille de signes ────────────────────────────────────────────────────── */
/*
 * Tout un service en un coup d'œil. Une LISTE, pas une nappe : l'ordre de
 * lecture, le comptage et la tabulation viennent de la structure. Les colonnes
 * se remplissent d'elles-mêmes, donc la grille se resserre sur un téléphone
 * sans qu'aucune ligne n'en soit retirée.
 */
.line-grid {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: var(--tds-space-2);
}
.line-grid__cell {
    display: inline-flex; align-items: center;
    min-height: var(--tds-control-height-md);
    padding: var(--tds-space-1) var(--tds-space-2);
    border-radius: var(--tds-radius-role-control);
    text-decoration: none;
}
.line-grid__cell:hover { background: var(--tds-action-ghost-hover); }

.line-grid-group + .line-grid-group { margin-top: var(--tds-space-5); }
.line-grid-group__title {
    display: flex; align-items: center; gap: var(--tds-space-2); flex-wrap: wrap;
    font-size: var(--tds-font-size-label);
    font-weight: var(--tds-font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tds-letter-spacing-label);
    color: var(--tds-text-secondary);
    margin-bottom: var(--tds-space-2);
}

.service-note {
    color: var(--tds-text-secondary);
    font-size: var(--tds-font-size-body-small);
    margin-bottom: var(--tds-space-3);
    max-width: var(--tds-content-width-reading);
}

/* ── Étiquettes ──────────────────────────────────────────────────────────── */
/* Une étiquette d'état est un `tds-badge` : le mot porte le sens, le ton le redit. */

.service-badges {
    list-style: none; margin: var(--tds-space-3) 0 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: var(--tds-space-2);
}

/* ── Bascule de sens et choix de parcours ────────────────────────────────── */
/*
 * Pas un bouton d'inversion : le référentiel publie quatre parcours par ligne
 * en moyenne, et certains opérateurs n'en publient aucun sens. Chaque sens est
 * un LIEN vers une adresse, donc partageable, et `aria-current` dit lequel est
 * ouvert sans que la couleur ait à le porter seule.
 */
.direction-switch { margin-bottom: var(--tds-space-4); }
.direction-switch ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--tds-space-2); }
.direction-switch a {
    display: flex; align-items: center; gap: var(--tds-space-3);
    min-height: var(--tds-control-height-md);
    padding: var(--tds-space-2) var(--tds-space-3);
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    border-radius: var(--tds-radius-role-control);
    text-decoration: none; color: var(--tds-text-primary);
}
.direction-switch a:hover { background: var(--tds-action-ghost-hover); }
.direction-switch a[aria-current="page"] {
    border-color: var(--tds-brand-action);
    border-inline-start-width: var(--tds-border-width-accent);
    background: var(--tds-brand-subtle);
    font-weight: var(--tds-font-weight-semibold);
}
.direction-switch__way { flex: 0 0 auto; color: var(--tds-text-muted); }
/*
 * Le sens COURANT se pose sur `--tds-brand-subtle`, et le texte discret n'y
 * tient plus son seuil : `--tds-text-muted` y mesure **4,435 en sombre** pour
 * un seuil de 4,5, contre 7,724 pour `--tds-text-secondary`.
 *
 * C'est la troisième occurrence de la même règle, et elle mérite d'être
 * nommée : **un texte discret ne se pose pas sur une surface teintée.** Les
 * deux autres sont `.destination-bar` sur surface creusée (4,303) et la
 * mention d'opérateur d'une perturbation sur surface d'avertissement (2,965).
 * Le jeton du texte ne change pas d'une surface à l'autre ; c'est la surface
 * qui bouge sous lui, et le seuil se vérifie sur le rendu.
 */
.direction-switch a[aria-current="page"] .direction-switch__way,
.direction-switch a[aria-current="page"] .count-note { color: var(--tds-text-secondary); }
.direction-switch__label { flex: 1 1 auto; min-width: 0; }

.variant-switch { margin-bottom: var(--tds-space-4); }
.variant-switch__title {
    font-size: var(--tds-font-size-body-small);
    color: var(--tds-text-secondary);
    margin-bottom: var(--tds-space-2);
}
.variant-switch ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--tds-space-2); }
.variant-switch .chip[aria-current="page"] {
    border-color: var(--tds-brand-action);
    background: var(--tds-brand-subtle);
    font-weight: var(--tds-font-weight-semibold);
}

/* ── Parcours : corps d'arrêt, correspondances, arrêts non desservis ─────── */
.route-stops li { align-items: flex-start; }
.route-stops__body { min-width: 0; flex: 1 1 auto; display: grid; gap: var(--tds-space-1); }
.route-stops__name { font-weight: var(--tds-font-weight-medium); }
.route-stops__tags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--tds-space-2); }
.route-stops__timetable {
    font-size: var(--tds-font-size-caption);
    color: var(--tds-brand-link);
}

/*
 * Un arrêt que ce parcours ne dessert pas. Il reste DANS le document, avec son
 * nom, son lien et une étiquette écrite — la mise en retrait ne fait que
 * confirmer ce que l'étiquette dit déjà. Le retirer laisserait croire que la
 * ligne n'y passe jamais ; le masquer en étroit reviendrait au défaut de
 * l'ancien site, qui supprimait ses colonnes sous 450 px.
 */
.route-stops__skipped .route-stops__name { color: var(--tds-text-muted); }
.route-stops__skipped .route-stops__mark {
    background: var(--tds-surface-default);
    box-shadow: 0 0 0 1px var(--tds-border-default);
}

.connections { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--tds-space-1); }
/*
 * Une correspondance est un lien vers une autre ligne : elle passe le plancher
 * de cible, sans que le signe grossisse. Mesuré avant : 28 px.
 */
.connections__link, .connections__more {
    display: inline-flex; align-items: center;
    min-block-size: calc(var(--tds-space-8));
    padding: 0.125rem var(--tds-space-1);
    border-radius: var(--tds-radius-sm);
    text-decoration: none;
}
.connections__link:hover, .connections__more:hover { background: var(--tds-action-ghost-hover); }
/*
 * Le signe d'une correspondance, et la condition qui rend son contraste valide.
 *
 * Le produit applique aux couleurs de ligne un seuil de 3:1, et ce seuil est
 * MOTIVÉ : le numéro est rendu en gras à 1,25 rem au moins, ce qui en fait un
 * « texte de grande taille » au sens des règles de contraste. La 0.4.1 va
 * jusqu'à faire échouer un test si cette déclaration disparaît.
 *
 * Cette liste la contournait. Elle réduisait le même numéro à 0,875 rem — 14 px
 * — où le seuil applicable redevient 4,5:1. Mesuré sur les couleurs réelles :
 * 3,10 pour le « 2 », 3,22 pour le « S2 », 3,43 pour le « 74 ». Toutes passaient
 * le seuil qu'on croyait appliquer, et aucune celui qui s'appliquait vraiment.
 *
 * Le signe reprend donc une taille qui tient la condition — 1,25 rem en gras —
 * et le disque suit. La correspondance grandit un peu ; elle cesse d'être une
 * exception silencieuse à la règle que tout le reste du produit respecte.
 */
.connections .line-sign { --sign-size: 1.75rem; gap: var(--tds-space-1); }
.connections .line-sign__mode { font-size: 0.8125rem; }
.connections .line-sign__number { font-size: 1.25rem; }
.connections .line-sign__mode-primary { font-size: 0.85em; }
/*
 * `min-height: 1.5rem` valait 24 px sur la HAUTEUR seule. Mesuré à 320 px sur
 * `/lignes/stib/metro-1` : « +1 » fait 21,5 px de large, « +3 » 23,4 — sous le
 * minimum sur l'axe qu'on n'avait pas contraint. Trois cibles par écran.
 *
 * Le plancher s'exprime désormais sur les deux axes, et avec le jeton du
 * Design System plutôt qu'avec un `rem` littéral qui disait la même chose sans
 * dire pourquoi.
 */
.connections__more {
    font-size: var(--tds-font-size-caption);
    font-weight: var(--tds-font-weight-medium);
    color: var(--tds-text-secondary);
    display: inline-flex; align-items: center; justify-content: center;
    min-block-size: var(--tds-space-6);
    min-inline-size: var(--tds-space-6);
}

.destination-bar--link { text-decoration: none; color: var(--tds-text-primary); }
.destination-bar--link:hover { text-decoration: underline; }
.operator-mention--link { text-decoration: none; color: var(--tds-brand-link); }
.operator-mention--link:hover { background: var(--tds-action-ghost-hover); }

/* ── Horaire théorique complet ───────────────────────────────────────────── */
/*
 * La grille heure × minutes, quand une seule ligne est demandée. Le modèle
 * historique est bon : l'œil balaie une colonne d'heures et trouve sa minute.
 * Ce qui change, c'est que les heures SANS service y figurent, écrites, plutôt
 * que d'être une rangée noire dont il faut deviner le sens.
 */
.timetable { width: 100%; border-collapse: collapse; }
.timetable th, .timetable td {
    text-align: start; vertical-align: top;
    padding: var(--tds-space-2) var(--tds-space-2);
    border-bottom: var(--tds-border-width-default) solid var(--tds-border-default);
}
.timetable thead th {
    font-size: var(--tds-font-size-label);
    text-transform: uppercase;
    letter-spacing: var(--tds-letter-spacing-label);
    color: var(--tds-text-secondary);
}
.timetable__hour {
    white-space: nowrap;
    width: 1%;
    font-weight: var(--tds-font-weight-semibold);
    color: var(--tds-text-secondary);
}
.timetable__minutes { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--tds-space-1); }
/*
 * La minute est portée par le `li` lui-même, sans conteneur intermédiaire : une
 * journée de pôle en compte plus de trois mille, et chaque balise évitée s'y
 * multiplie d'autant.
 */
.timetable__minutes > li {
    display: inline-flex; align-items: baseline;
    padding: 0.125rem var(--tds-space-1);
    border-radius: var(--tds-radius-sm);
    background: var(--tds-surface-sunken);
    font-variant-numeric: tabular-nums;
    font-weight: var(--tds-font-weight-medium);
}
/* La marque de destination : une LETTRE, jamais une couleur seule. */
.timetable__mark {
    font-size: 0.6875rem;
    font-weight: var(--tds-font-weight-bold);
    color: var(--tds-text-secondary);
    margin-inline-start: 0.0625rem;
}
.timetable__empty td, .timetable__empty th { background: var(--tds-surface-sunken); }
/* Mesuré à 4,30 en muet : cette phrase dit qu'aucun train ne part à cette
   heure, ce qui est le contraire d'une décoration. */
.timetable__none { color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); }

.timetable-hour { margin-top: var(--tds-space-5); }
.timetable-hour__title {
    font-size: var(--tds-font-size-label);
    text-transform: uppercase;
    letter-spacing: var(--tds-letter-spacing-label);
    color: var(--tds-text-secondary);
    margin-bottom: var(--tds-space-2);
}

.legend { list-style: none; margin: var(--tds-space-2) 0 0; padding: 0; display: grid; gap: var(--tds-space-1); }
.legend > li { display: flex; align-items: baseline; gap: var(--tds-space-2); font-size: var(--tds-font-size-body-small); }

.date-picker { display: flex; gap: var(--tds-space-2); flex-wrap: wrap; align-items: flex-end; }
.date-picker__field { flex: 1 1 14rem; min-width: 0; }
.date-steps { margin-top: var(--tds-space-3); }
.date-steps ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--tds-space-2); }

/* ── Densité mobile ──────────────────────────────────────────────────────── */
/*
 * Ce qui se resserre ici se REPLIE : rien n'est retiré du document. L'ancien
 * site masquait purement ses colonnes de corridor sous 450 px — soixante
 * cellules sur soixante en `display:none` — et perdait la seule information que
 * cet écran apportait. C'est exactement ce que le contrat 1.6.1 interdit.
 */
@media (max-width: 34rem) {
    .timetable th, .timetable td { padding-inline: var(--tds-space-1); }
    .direction-switch a { flex-wrap: wrap; }
    .route-stops__tags { gap: var(--tds-space-1); }
}

/* ── Horaire : un bloc par ligne ─────────────────────────────────────────── */
.timetable-block { margin-top: var(--tds-space-6); }
.timetable-block__head {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: var(--tds-space-2);
    margin-bottom: var(--tds-space-2);
}

/* ── Temps réel ──────────────────────────────────────────────────────────────
 *
 * Aucune couleur littérale, et aucune information portée par la seule couleur.
 * Chaque état s'écrit — « temps réel », « théorique », « périmé » — et la
 * teinte ne fait que confirmer ce qui est déjà lisible. C'est la règle du
 * contrat de signalétique, et c'est aussi ce qui rend l'écran utilisable en
 * monochrome, en fort contraste, et à voix haute.
 *
 * La hauteur des rangées ne bouge PAS quand un passage change d'état : la
 * ligne d'état est toujours rendue, y compris pour un horaire théorique. Sans
 * cela, l'arrivée d'une mesure ferait sauter tout ce qui est en dessous — au
 * moment précis où le voyageur lit.
 */

.departure__state {
    display: block;
    font-size: var(--tds-font-size-caption);
    font-weight: var(--tds-font-weight-regular);
    color: var(--tds-text-muted);
    min-height: 1.25em;
}
.departure__state--live { color: var(--tds-status-success); font-weight: var(--tds-font-weight-semibold); }
.departure__state--stale { color: var(--tds-status-warning); font-weight: var(--tds-font-weight-semibold); }

/*
 * La marque qui précède l'état. Elle est décorative — `aria-hidden` — et ne
 * porte jamais seule le sens : un point plein pour une mesure, un cercle vide
 * pour un horaire planifié. Elle se distingue par sa FORME autant que par sa
 * couleur.
 */
.departure__mark { font-style: normal; margin-inline-end: var(--tds-space-1); }

.departure__delay {
    font-variant-numeric: tabular-nums;
    font-weight: var(--tds-font-weight-semibold);
}
.departure__delay--late { color: var(--tds-status-warning); }
.departure__delay--early { color: var(--tds-status-info); }

/*
 * La suppression et le changement de voie, ajoutés en 0.9.0.
 *
 * Tous deux sont ÉCRITS en toutes lettres — « Supprimé », « au lieu de la
 * voie 4 » — et la couleur ne fait que confirmer. C'est la règle de
 * signalétique du produit depuis la 0.2.0 : un écran en niveaux de gris, ou lu
 * par une personne daltonienne, doit rester exact.
 *
 * La suppression emprunte le jeton de danger, le changement de voie celui
 * d'alerte : l'un empêche le voyage, l'autre déplace le voyageur de trente
 * mètres. Les rendre identiques rendrait la hiérarchie illisible le jour où
 * les deux tombent sur la même rangée.
 */
.departure__cancelled {
    color: var(--tds-status-danger);
    font-weight: var(--tds-font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.departure__platform-change {
    color: var(--tds-status-warning);
    font-weight: var(--tds-font-weight-semibold);
}

/*
 * Un passage supprimé garde son heure LISIBLE et la barre.
 *
 * Il ne disparaît pas de la liste : le voyageur doit pouvoir reconnaître que
 * c'est bien son train de 18:33 qui saute. Le retirer le laisserait chercher
 * un passage qui n'est plus là, sans jamais savoir pourquoi.
 */
.departure--cancelled .departure__time {
    text-decoration: line-through;
    text-decoration-thickness: 1px;
    color: var(--tds-text-secondary);
}

.departure__notice {
    display: inline-flex; align-items: center;
    padding: var(--tds-space-1) var(--tds-space-2);
    border-radius: var(--tds-radius-role-pill);
    border: var(--tds-border-width-default) solid var(--tds-status-warning);
    background: var(--tds-status-warning-subtle);
    font-size: var(--tds-font-size-caption);
    color: var(--tds-text-primary);
}

/* ── Prochains passages — 0.39.0 ─────────────────────────────────────────── */
/*
 * L'écran qui consomme le contrat réutilise les rangées de la fiche d'arrêt
 * (`.departures`, `.departure*`), les pastilles (`.chip`) et les signes : un
 * passage se lit partout de la même façon. Ce qui suit n'est que ce qui lui
 * est propre — un résultat d'arrêt qui se distingue de ses homonymes, et une
 * liste de choix qui reste utilisable à 482 entrées sur un téléphone.
 */

.stop-results > li { margin: 0; }
.stop-result {
    display: grid; gap: var(--tds-space-1);
    padding: var(--tds-space-3) var(--tds-space-2);
    min-height: var(--tds-control-height-md);
    text-decoration: none; color: inherit;
    border-radius: var(--tds-radius-role-control);
}
.stop-result:hover { background: var(--tds-action-ghost-hover); }
.stop-result:focus-visible {
    outline: var(--tds-focus-width) solid var(--tds-border-focus);
    outline-offset: var(--tds-focus-offset);
}
.stop-result__name { font-weight: var(--tds-font-weight-semibold); color: var(--tds-brand-link); overflow-wrap: anywhere; }
.stop-result__aka { font-size: var(--tds-font-size-body-small); color: var(--tds-text-secondary); overflow-wrap: anywhere; }
.stop-result__meta {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--tds-space-2);
    font-size: var(--tds-font-size-caption); color: var(--tds-text-secondary);
}

[data-passages-results] { margin-top: var(--tds-space-3); }

/* La recherche en cours : les résultats précédents s'estompent, sans disparaître. */
[data-passages-results].is-loading { opacity: 0.6; }

.passages-header { margin-bottom: var(--tds-space-3); }
.departure__clock.is-repeat { display: none; }
.passages-selection { display: grid; gap: var(--tds-space-2); margin-bottom: var(--tds-space-4); }
.passages-selection__lead { margin: 0; color: var(--tds-text-secondary); }
.passages-selection__note { margin: 0; }
.passages-selection__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--tds-space-2) var(--tds-space-3); }
.choice-chips { list-style: none; margin: 0; padding: 0; }
.choice-chip { padding-inline-end: var(--tds-space-1); }
.choice-chip__remove {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: var(--tds-control-height-sm); min-height: var(--tds-control-height-sm);
    margin-block: calc(-1 * var(--tds-space-1));
    border-radius: var(--tds-radius-role-pill);
    text-decoration: none; color: var(--tds-text-secondary);
    font-size: var(--tds-font-size-body-large);
}
.choice-chip__remove:hover { background: var(--tds-action-ghost-hover); color: var(--tds-text-primary); }

.passages-hints { margin: var(--tds-space-2) 0 0; padding-inline-start: var(--tds-space-4); display: grid; gap: var(--tds-space-1); }

/* Le choix des services. */
.passages-choice { display: grid; gap: var(--tds-space-5); }
.passages-choice__filter { max-width: 32rem; }
.choice-group { border: 0; margin: 0; padding: 0; min-width: 0; }
.choice-group__legend { font-size: var(--tds-font-size-heading4); font-weight: var(--tds-font-weight-semibold); padding: 0; margin-bottom: var(--tds-space-2); }
.choice-more { display: grid; gap: var(--tds-space-2); justify-items: start; }
.choice-more .choice-group__legend { margin: 0; }
.choice-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--tds-space-1); }
.choice-list--columns { grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr)); column-gap: var(--tds-space-4); }
.choice-list > li[hidden] { display: none; }
.choice-routes { list-style: none; margin: 0 0 var(--tds-space-2); padding-inline-start: calc(var(--tds-space-6) + var(--tds-space-2)); display: grid; gap: 0; }
.choice {
    display: flex; align-items: center; gap: var(--tds-space-3);
    min-height: var(--tds-control-height-md);
    padding: var(--tds-space-1) var(--tds-space-2);
    border-radius: var(--tds-radius-role-control);
    cursor: pointer;
}
.choice:hover { background: var(--tds-action-ghost-hover); }
.choice input { width: 1.25rem; height: 1.25rem; flex: 0 0 auto; margin: 0; accent-color: var(--tds-brand-primary); }
.choice__text { min-width: 0; overflow-wrap: anywhere; }
.choice__hint { display: block; font-size: var(--tds-font-size-caption); color: var(--tds-text-secondary); }

/*
 * L'envoi reste à portée de pouce : la liste d'une grande gare se parcourt
 * longtemps, et remonter tout en haut pour valider serait la première chose
 * qu'un voyageur abandonnerait.
 */
.passages-choice__actions {
    position: sticky; bottom: 0; z-index: 1;
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--tds-space-2);
    padding: var(--tds-space-3) 0;
    background: var(--tds-surface-canvas);
    border-top: var(--tds-border-width-default) solid var(--tds-border-default);
}
.passages-choice__count { flex-basis: 100%; margin: 0; }

/* ── Perturbations ───────────────────────────────────────────────────────── */

.disruptions { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--tds-space-3); }
.disruption {
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    border-inline-start: var(--tds-border-width-accent) solid var(--tds-status-warning);
    background: var(--tds-status-warning-subtle);
    border-radius: var(--tds-radius-role-surface);
    padding: var(--tds-space-4);
    color: var(--tds-text-primary);
}
/*
 * Une SECTION de l'état du réseau — 0.24.0.
 *
 * Un troisième niveau de titre à l'intérieur d'une section, et non trois
 * sections de premier rang : « Maintenant », « Prochainement » et « Travaux
 * installés » sont trois réponses à une même question, et les mettre au même
 * rang que « Le réseau » ferait de l'accueil un tableau de supervision.
 *
 * Le titre ne porte pas de filet : celui de la section le porte déjà, et deux
 * filets à deux niveaux découperaient l'écran plus qu'ils ne le structurent.
 */
.section-group { margin-top: var(--tds-space-6); }
.section-group:first-of-type { margin-top: var(--tds-space-4); }
.section-group__title {
    font-size: var(--tds-font-size-body);
    font-weight: var(--tds-font-weight-semibold);
    margin-bottom: var(--tds-space-2);
    color: var(--tds-text-primary);
}

.disruption__text { margin: 0; white-space: pre-line; }

/*
 * La nature en tête d'entrée, la cause à côté — 0.33.0.
 *
 * Sur le fond teinté d'avertissement, le gris « muted » de `.count-note` ne
 * tient pas le contraste en sombre — 2,97 mesuré par la sonde géométrique. Le
 * texte secondaire le tient, comme pour les autres mentions de l'entrée.
 */
.disruption__head { margin: 0 0 var(--tds-space-1); display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--tds-space-2); }
.disruption .count-note { color: var(--tds-text-secondary); }

/*
 * L'état temporel d'une rangée, porté par l'accent de bordure.
 *
 * Il ne tient JAMAIS à la couleur seule : l'état est écrit dans le titre de la
 * section qui contient la rangée, et la couleur ne fait que redire ce que le
 * texte dit déjà. C'est la règle du produit depuis la 0.4.1 — un état qui ne
 * se lit qu'en couleur ne se lit pas.
 *
 * Une perturbation installée prend le neutre plutôt que l'avertissement : elle
 * reste vraie, elle a cessé d'être une alerte, et lui garder l'orange du reste
 * ferait de tout l'écran une alerte — ce qui n'alerte plus de rien.
 */
.disruption[data-state="longue-duree"] {
    border-inline-start-color: var(--tds-border-strong);
    background: var(--tds-surface-sunken);
}
.disruption[data-state="prevue"] {
    border-inline-start-color: var(--tds-status-info);
    background: var(--tds-status-info-subtle);
}
.disruption__meta {
    display: flex; flex-wrap: wrap; gap: var(--tds-space-2);
    margin-top: var(--tds-space-2);
    font-size: var(--tds-font-size-body-small);
    color: var(--tds-text-secondary);
}
/*
 * Le lien de détail est une CIBLE, pas une métadonnée.
 *
 * Mesuré à 37,1 × 21,7 px sur les cinquante rangées de l'écran global : sous
 * le minimum de 24 px, et cinquante fois. C'est exactement la classe de défaut
 * trouvée en 0.5.0 — liens « Horaire » du thermomètre à 18,6 px, destinations
 * d'un horaire à 18 px — et elle revient dès qu'un lien vit dans une rangée de
 * petit texte.
 *
 * Le plancher est `--tds-space-6`, soit 24 px : exactement la solution que la
 * 0.5.0 a posée sur `.thermometer__timetable`, et pour la même raison. Une
 * liste dense ne peut pas prendre 44 px — cinquante boutons enterreraient les
 * messages qu'ils accompagnent — mais elle ne peut pas rester sous le minimum.
 * La cible est atteinte par la marge intérieure, sans grossir le texte.
 */
.disruption__meta a {
    display: inline-flex; align-items: center;
    min-block-size: var(--tds-space-6);
    padding-inline: var(--tds-space-1);
    margin-inline: calc(-1 * var(--tds-space-1));
    border-radius: var(--tds-radius-sm);
    /*
     * L'encre de la SURFACE, pas celle de la marque.
     *
     * Une perturbation se pose sur `--tds-status-warning-subtle`, et
     * `--tds-brand-link` y mesure **2,945 en sombre** pour un seuil de 4,5 —
     * le lien serait le texte le moins lisible d'un écran qui en porte
     * cinquante. `--tds-text-primary` y mesure 8,988.
     *
     * C'est exactement la règle que la 0.5.0 a posée sur `.notice a` en
     * corrigeant son défaut n° 5, au même ratio de 2,95. Elle est étendue ici
     * plutôt que redécouverte : les surfaces d'état portent leur propre encre,
     * et un lien souligné reste un lien sans dépendre de sa teinte.
     */
    color: var(--tds-text-primary);
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}
.disruption__meta a:hover { background: var(--tds-action-ghost-hover); text-decoration-thickness: 2px; }

/*
 * Une mention d'opérateur sur la même surface : `--tds-text-muted` y mesure
 * **2,965 en sombre** pour un seuil de 4,5, contre 5,164 pour
 * `--tds-text-secondary`. Le retrait visuel est conservé — le texte primaire
 * est à 8,988 —, seul le plancher est atteint.
 */
.disruption .operator-mention { color: var(--tds-text-secondary); }

.disruption__scopes { display: flex; flex-wrap: wrap; gap: var(--tds-space-2); margin-top: var(--tds-space-2); }
/*
 * Le même message dans une autre langue. Mesuré à 2,97 en sombre : c'est le
 * texte d'une perturbation, et quelqu'un le lit parce que c'est sa langue.
 */
.disruption__other { font-size: var(--tds-font-size-caption); color: var(--tds-text-secondary); }

/* ── Véhicules observés ──────────────────────────────────────────────────── */

.vehicles { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--tds-space-2); }
.vehicle {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--tds-space-2);
    padding: var(--tds-space-2) 0;
    border-bottom: var(--tds-border-width-default) solid var(--tds-border-default);
    min-height: var(--tds-control-height-sm);
}
.vehicle:last-child { border-bottom: 0; }
.vehicle__place { font-weight: var(--tds-font-weight-semibold); }
.vehicle__progress { color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); }
.vehicle__unknown { color: var(--tds-text-muted); font-size: var(--tds-font-size-body-small); font-style: italic; }

/* ═══════════════════════════════════════════════════════════════════════════
 * 0.5.0 — le thermomètre de ligne
 * ═══════════════════════════════════════════════════════════════════════════ */

/*
 * Un parcours qui se lit comme un parcours.
 *
 * La 0.4.1 rendait une suite de pastilles sans rien entre elles. L'axe est ce
 * qui manquait, et il est dessiné PAR LES RANGÉES : chaque arrêt porte le
 * segment qui monte vers sa marque et celui qui en descend. Il n'y a donc
 * aucune hauteur totale à calculer, aucun positionnement absolu, et un parcours
 * de trois cents gares ne coûte pas une règle de plus qu'un parcours de trois.
 *
 * Ce n'est ni un composant de métro ni un composant de train. Rien ici ne
 * connaît d'opérateur ni de mode.
 */

.thermometer {
    /*
     * L'axe emprunte la couleur de la ligne quand elle en publie une, et le
     * neutre du thème sinon — exactement comme un signe. Aucune valeur
     * littérale : `--line-paper` vient de la feuille engendrée depuis le
     * référentiel publié.
     */
    --thermo-rail: var(--line-paper, var(--tds-border-strong));
    --thermo-rail-width: var(--tds-border-width-strong);
    --thermo-gutter: 1.5rem;
    --thermo-mark: 0.75rem;
    /* Marque des points remarquables : extrémités du parcours, position courante. */
    --thermo-mark-lg: 1.125rem;
    /*
     * Ce dont une grande marque doit remonter pour rester centrée sur la même
     * ligne de texte que les petites. Mesuré sans lui : 2,99 px de décrochage
     * aux deux terminus.
     */
    --thermo-mark-lg-shift: calc((var(--thermo-mark) - var(--thermo-mark-lg)) / 2);
    /*
     * Où la marque tombe sur la rangée : au milieu de la PREMIÈRE ligne du nom,
     * et non au milieu de la rangée — une rangée qui porte des correspondances
     * est trois fois plus haute que sa voisine, et une marque centrée sur elle
     * décrocherait de l'axe. Le calcul part du padding de rangée et de
     * l'interligne du corps de texte, tous deux publiés.
     */
    --thermo-lead: calc(
        var(--tds-space-2)
        + (1em * var(--tds-line-height-body) - var(--thermo-mark)) / 2
    );

    list-style: none; margin: 0; padding: 0;
}

/*
 * La marge verticale est portée par le CORPS, jamais par la rangée.
 *
 * Mesuré : avec `padding-block` sur la rangée, l'axe s'arrêtait 8 px avant le
 * bas et reprenait 8 px après le haut de la suivante — 16 px de trou entre
 * chaque arrêt, soit exactement le défaut qu'on venait corriger. L'axe doit
 * traverser la rangée entière ; c'est le texte qui respire, pas le trait.
 */
.thermometer__stop {
    display: flex; align-items: stretch; gap: var(--tds-space-3);
}

/* ── L'axe ───────────────────────────────────────────────────────────────── */

.thermometer__axis {
    flex: 0 0 var(--thermo-gutter);
    display: flex; flex-direction: column; align-items: center;
    align-self: stretch;
}

/*
 * Deux segments et une marque. Le segment du haut a la hauteur qui amène la
 * marque au bon endroit ; celui du bas prend tout le reste de la rangée, quelle
 * qu'elle soit. C'est ce qui rend l'axe continu d'un arrêt au suivant sans que
 * personne n'ait à connaître la hauteur des rangées.
 */
.thermometer__axis::before,
.thermometer__axis::after {
    content: '';
    inline-size: var(--thermo-rail-width);
    background: var(--thermo-rail);
    flex: 0 0 auto;
}
.thermometer__axis::before { block-size: var(--thermo-lead); }
.thermometer__axis::after { flex: 1 1 auto; block-size: 0; }

/* L'axe commence au premier arrêt et s'arrête au dernier : il ne dépasse pas. */
.thermometer__stop:first-child .thermometer__axis::before,
.thermometer__stop:last-child .thermometer__axis::after { background: transparent; }

.thermometer__mark {
    flex: 0 0 auto;
    inline-size: var(--thermo-mark); block-size: var(--thermo-mark);
    border-radius: var(--tds-radius-full);
    background: var(--thermo-rail);
    /*
     * La marque se détache de l'axe par un liseré de la couleur du fond : sans
     * lui, une pastille de la même teinte que son trait disparaît dedans.
     */
    box-shadow: 0 0 0 var(--tds-border-width-strong) var(--tds-surface-canvas);
}

/*
 * Les deux extrémités du parcours dessiné. Une marque plus grande ET creuse :
 * la forme les distingue autant que la taille.
 *
 * La condition porte sur la POSITION dans la liste, et non sur `is_terminus` :
 * ce que la marque dit est « l'axe commence ici », ce qui est vrai de la
 * première rangée quoi que la donnée pense de l'arrêt. Le mot « Terminus »,
 * lui, reste écrit à côté et vient de la donnée seule.
 */
.thermometer__stop:first-child .thermometer__mark,
.thermometer__stop:last-child .thermometer__mark {
    inline-size: var(--thermo-mark-lg); block-size: var(--thermo-mark-lg);
    margin-block-start: var(--thermo-mark-lg-shift);
    background: var(--tds-surface-canvas);
    border: var(--tds-border-width-strong) solid var(--thermo-rail);
    box-shadow: none;
}

/*
 * Un arrêt que ce parcours ne dessert pas. Il reste DANS le document, avec son
 * nom, son lien et son étiquette écrite : le retirer laisserait croire que la
 * ligne n'y passe jamais, et le masquer en étroit reviendrait au défaut de
 * l'ancien site, qui supprimait ses colonnes sous 450 px.
 *
 * L'axe, lui, se met en pointillé à cet endroit : le trait dit « la ligne
 * traverse sans s'arrêter », ce qu'aucune pastille ne savait dire.
 */
.thermometer__stop[data-served="non"] .thermometer__mark {
    background: var(--tds-surface-canvas);
    border: var(--tds-border-width-default) dashed var(--tds-border-strong);
    box-shadow: none;
}
.thermometer__stop[data-served="non"] .thermometer__name { color: var(--tds-text-muted); }

/* ── Progression, quand elle est démontrée ───────────────────────────────── */

/*
 * `data-progress` n'est posé que lorsqu'UN SEUL véhicule est situé sur ce
 * parcours : il y a alors une seule lecture possible de « déjà passé ». Avec
 * deux véhicules il y a deux progressions, et en choisir une serait faux — le
 * gabarit ne pose alors aucun de ces attributs, et le thermomètre reste
 * purement théorique. C'est aussi son état permanent chez un opérateur sans
 * temps réel.
 */
.thermometer__stop[data-progress="passed"] {
    --thermo-rail: var(--tds-border-default);
}
.thermometer__stop[data-progress="passed"] .thermometer__name { color: var(--tds-text-secondary); }

/*
 * La position courante. Un halo, pas seulement une teinte — et le mot
 * « Position actuelle » est écrit dans la rangée.
 */
.thermometer__stop[data-progress="current"] .thermometer__mark,
.thermometer__stop[data-vehicle="oui"] .thermometer__mark {
    inline-size: var(--thermo-mark-lg); block-size: var(--thermo-mark-lg);
    margin-block-start: var(--thermo-mark-lg-shift);
    background: var(--thermo-rail);
    box-shadow:
        0 0 0 var(--tds-border-width-strong) var(--tds-surface-canvas),
        0 0 0 calc(var(--tds-border-width-strong) + 3px) var(--tds-brand-subtle);
}
.thermometer__stop[data-progress="current"] .thermometer__name,
.thermometer__stop[data-vehicle="oui"] .thermometer__name {
    font-weight: var(--tds-font-weight-bold);
}

/* ── Le corps d'un arrêt ─────────────────────────────────────────────────── */

.thermometer__body {
    min-width: 0; flex: 1 1 auto;
    display: grid; gap: var(--tds-space-1);
    padding-block: var(--tds-space-2);
    align-content: start;
}

/*
 * Le nom d'un arrêt ne se tronque jamais : il EST son identité, et il n'y a
 * rien d'autre pour le désigner. Il passe donc à la ligne, y compris au milieu
 * d'un mot composé assez long pour ne pas tenir à 320 px.
 */
.thermometer__name {
    font-weight: var(--tds-font-weight-medium);
    overflow-wrap: anywhere;
}

/*
 * Le nom et ce qui le qualifie partagent une rangée qui se replie. Empilés,
 * trente arrêts occupaient trois lignes chacun et le parcours cessait de se
 * parcourir — le reproche exact fait à l'écran de la 0.4.1.
 */
.thermometer__head {
    display: flex; flex-wrap: wrap; align-items: baseline;
    column-gap: var(--tds-space-3); row-gap: var(--tds-space-1);
    min-width: 0;
}

.thermometer__tags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--tds-space-2); max-width: 100%; }
.thermometer__tag { max-width: 100%; white-space: normal; }

/*
 * Le raccourci vers l'horaire de CETTE ligne à CET arrêt — la meilleure
 * trouvaille de navigation de l'ancien site, et elle ne demande aucun temps
 * réel.
 *
 * Mesuré à 320 px avant correction : 18,6 px de haut. Le Design System publie
 * `--tds-touch-target-web` à 44 px, et c'est la cible d'une action ou d'un
 * repère de navigation. Une affordance secondaire répétée à chaque rangée d'une
 * liste dense ne peut pas la prendre — trente boutons de 44 px enterreraient le
 * parcours qu'ils accompagnent — mais elle ne peut pas non plus rester sous le
 * minimum. Le plancher est donc `--tds-space-6`, soit 24 px : la plus petite
 * cible que les règles d'accessibilité admettent, atteinte par la marge
 * intérieure et sans grossir le texte.
 */
.thermometer__timetable {
    font-size: var(--tds-font-size-caption);
    color: var(--tds-brand-link);
    display: inline-flex; align-items: center;
    min-block-size: var(--tds-space-6);
    padding-inline: var(--tds-space-1);
    margin-inline: calc(-1 * var(--tds-space-1));
    border-radius: var(--tds-radius-sm);
}
.thermometer__timetable:hover { background: var(--tds-action-ghost-hover); }

/*
 * Un arrêt supprimé garde son nom lisible et le barre : le voyageur doit
 * pouvoir le RECONNAÎTRE pour comprendre que c'est le sien qui saute. Le
 * masquer ou le griser jusqu'à l'illisible retirerait l'information au moment
 * où elle compte le plus.
 */
.thermometer__stop[data-realtime="skipped"] .thermometer__name {
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}

.thermometer__stop[data-realtime="skipped"] .thermometer__time {
    text-decoration: line-through;
    color: var(--tds-text-secondary);
}

@media (max-width: 26rem) {
    .thermometer { --thermo-gutter: 1.25rem; }
    .thermometer__stop { gap: var(--tds-space-2); }
    .thermometer__tags { gap: var(--tds-space-1); }
}

/* ── Accueil : la recherche en tête, un bloc par opérateur ───────────────── */

/*
 * La recherche n'est pas une section de l'accueil : c'est son action.
 *
 * La 0.4.1 la rangeait sous un titre « Chercher », au même rang que
 * « Opérateurs » — un rang d'inventaire. Elle prend ici la forme d'un contrôle
 * de tête : posée sur une surface, plus large, immédiatement après le chapô.
 */
.search-lead {
    display: flex; gap: var(--tds-space-3); flex-wrap: wrap; align-items: flex-end;
    padding: var(--tds-space-5);
    background: var(--tds-brand-subtle);
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    border-radius: var(--tds-radius-role-surface);
    margin-block-end: 0;
}
.search-lead__field { flex: 1 1 20rem; min-width: 0; }

/*
 * Un opérateur, et ce qu'il fait rouler. Séparé du suivant par un trait plutôt
 * que par une carte : trois opérateurs feraient trois cartes empilées, et
 * l'accueil deviendrait la « longue succession de cartes » qu'il ne doit pas
 * être.
 */
.operator-block + .operator-block {
    margin-block-start: var(--tds-space-8);
    padding-block-start: var(--tds-space-8);
    border-block-start: var(--tds-border-width-default) solid var(--tds-border-default);
}

.operator-block__title { font-size: var(--tds-font-size-heading4); margin-block-end: var(--tds-space-1); }
.operator-block__title a { text-decoration: none; color: var(--tds-text-primary); }
.operator-block__title a:hover { color: var(--tds-brand-link); text-decoration: underline; }

.operator-block__meta {
    color: var(--tds-text-secondary);
    font-size: var(--tds-font-size-body-small);
    margin-block-end: var(--tds-space-3);
}

/* ── Horaire en liste : quand la grille ne comprime rien ─────────────────── */

/*
 * Une relation à huit départs par jour n'a rien à faire dans une grille de
 * vingt-quatre heures. Elle devient une liste de départs, qui gagne au passage
 * la place d'écrire la course et la voie — les deux choses qu'on lit sur un
 * écran de quai et que la grille ne pouvait pas porter.
 */
.timetable--list th,
.timetable--list td {
    text-align: start;
    vertical-align: baseline;
    padding-block: var(--tds-space-2);
}

.timetable__departure { white-space: nowrap; font-weight: var(--tds-font-weight-semibold); }

/*
 * Un départ au-delà de minuit appartient au jour de service de la veille. Les
 * DEUX heures sont écrites : « 01h14 » et « 25:14 — le lendemain ». Convertir
 * naïvement l'une en l'autre ferait disparaître le dernier passage de la nuit
 * de l'horaire où on le cherche.
 */
.timetable__overflow {
    display: block;
    font-size: var(--tds-font-size-caption);
    font-weight: var(--tds-font-weight-regular);
    color: var(--tds-text-muted);
}

/* ── Cibles : le plancher, là où la mesure l'a trouvé franchi ────────────── */

/*
 * Un titre qui est aussi un lien. Mesuré à 320 px : 23 px de haut, sous le
 * plancher — l'interligne d'un `h3` à cette largeur ne suffit pas seul.
 */
.operator-block__title a {
    display: inline-flex; align-items: center;
    min-block-size: var(--tds-space-6);
}

/*
 * La destination d'un départ, dans un horaire en liste. Mesurée à 18 px, et
 * répétée cent cinq fois sur l'horaire d'un grand nœud : c'est la porte vers
 * le parcours, et elle doit se viser au doigt.
 */
.timetable--list td a {
    display: inline-flex; align-items: center;
    min-block-size: var(--tds-space-6);
}

/* ── 0.6.0 — l'heure sur un thermomètre de course ────────────────────────── */

/*
 * L'heure vient AVANT le nom, et sur sa propre colonne.
 *
 * C'est ce qu'un voyageur balaie : sur une course, il cherche « quand », pas
 * « où » — il sait déjà où il monte. Une largeur minimale fixe évite que la
 * colonne respire d'une rangée à l'autre et rende la lecture verticale
 * impossible ; `ch` la donne en chiffres, ce qui est exactement l'unité utile
 * pour un nombre à chiffres de largeur égale.
 *
 * Elle est en `--tds-text-primary` et non en secondaire : mesuré, le seuil
 * applicable à 0,875 rem est 4,5:1, et c'est le défaut n° 3 de la 0.5.0 —
 * la condition qui justifie le seuil de 3:1 contournée par une taille plus
 * petite. Une heure n'est pas une métadonnée, c'est l'information.
 */
.thermometer__time {
    flex: 0 0 auto;
    min-width: 5.5ch;
    font-weight: var(--tds-font-weight-semibold);
    color: var(--tds-text-primary);
}
.thermometer__time-secondary { display: block; font-weight: var(--tds-font-weight-regular); }

/* ── 0.6.0 — pagination ──────────────────────────────────────────────────── */

/*
 * Trois repères, jamais une barre de numéros : douze cibles tactiles ne
 * tiennent pas à 320 px, et la phrase dit déjà où l'on est.
 *
 * `space-between` sur trois éléments centrerait la position ; le `gap` et
 * `flex-wrap` la laissent passer sous les liens en écran étroit plutôt que de
 * les comprimer sous leur cible minimale.
 */
.pager {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--tds-space-3);
    margin-top: var(--tds-space-5);
}
.pager__position {
    margin: 0;
    color: var(--tds-text-secondary);
    font-size: var(--tds-font-size-body-small);
}

/* ── 0.6.0 — l'onglet ou le filtre courant ───────────────────────────────── */

/*
 * La même marque que `variant-switch`, généralisée aux rangées de jetons.
 *
 * Le défaut n° 4 de la 0.5.0 était un indicateur de page courante mesuré à
 * 2,22:1 en sombre. La règle est donc reprise telle quelle plutôt que
 * réinventée : bordure de marque, fond subtil, graisse — trois marques dont
 * aucune ne repose sur la seule couleur, et un contraste de texte inchangé
 * puisque la couleur du texte ne bouge pas.
 */
.chip-row .chip[aria-current="page"] {
    border-color: var(--tds-brand-action);
    background: var(--tds-brand-subtle);
    font-weight: var(--tds-font-weight-semibold);
}

/* ── 0.7.0 — le numéro de course d'un horaire est une cible ──────────────── */

/*
 * Un numéro court est un lien court. Mesuré par la sonde géométrique à 375 px
 * sur l'horaire du plus grand pôle ferroviaire : le train « 50 » occupait
 * **19,5 × 24,8 px**, sous le minimum sur l'axe qu'on n'avait pas contraint.
 *
 * C'est le troisième endroit du produit où le même défaut apparaît — après les
 * correspondances repliées et le fil d'Ariane portant un numéro de ligne — et
 * la règle est toujours la même : **un plancher se pose sur les DEUX axes.**
 */
.timetable__course {
    display: inline-flex; align-items: center; justify-content: center;
    min-inline-size: var(--tds-space-6);
    min-block-size: var(--tds-space-6);
    border-radius: var(--tds-radius-sm);
}
.timetable__course:hover { background: var(--tds-action-ghost-hover); }

/* ── 0.8.0 — la carte ────────────────────────────────────────────────────── */

/*
 * Une carte Transit est un SVG rendu par le serveur, posé dans un cadre du
 * produit. Elle n'introduit AUCUNE grammaire visuelle nouvelle : le cadre est
 * celui d'une section, le fond est le fond enfoncé du thème, la note de
 * qualité est celle qu'on lit déjà sous un horaire.
 *
 * La seule couleur qui n'est pas un jeton est celle du tracé, et elle vient
 * d'où viennent toutes les couleurs d'opérateur : du référentiel, en variable
 * locale posée sur l'élément. C'est la même règle que les signes de ligne, pour
 * la même raison — un hexadécimal d'opérateur dans ce fichier serait une
 * violation du contrat de signalétique.
 */
.map {
    margin: var(--tds-space-4) 0 0;
    display: flex;
    flex-direction: column;
    gap: var(--tds-space-3);
}

.map__canvas {
    inline-size: 100%;
    /*
     * Une carte ne prend jamais plus qu'un écran.
     *
     * Le cadre suit la forme de son contenu — voir MapFrame — et une ligne
     * nord-sud produit donc un cadre haut. À `inline-size: 100%` sur un large
     * écran, cela ferait une carte plus haute que la page. Le plafond la ramène
     * dans l'écran ; `preserveAspectRatio` la centre, sans la déformer.
     */
    max-block-size: 72vh;
    /*
     * La hauteur suit le viewBox, jamais une valeur fixe : une carte à hauteur
     * imposée se déforme ou se rogne, et c'est exactement le défaut qu'une
     * carte coupée produit. `preserveAspectRatio` fait le reste.
     */
    block-size: auto;
    display: block;
    background: var(--tds-surface-sunken);
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    border-radius: var(--tds-radius-role-surface);
}

/*
 * Le tracé.
 *
 * `--line-paper` est la couleur du disque de la ligne, servie par la feuille
 * des signes engendrée depuis le référentiel — la même variable, le même
 * mécanisme, la même règle qu'un signe. Sans elle, le tracé prend le gris fort
 * du thème : une ligne sans couleur publiée n'en reçoit pas une au hasard.
 *
 * L'épaisseur est en unités de VIEWBOX, donc elle grandit et rétrécit avec la
 * carte. C'est ce qu'on veut : à 320 px de large, un trait de 3 unités reste
 * proportionnellement le même trait, alors qu'un `vector-effect` non mis à
 * l'échelle deviendrait un cheveu sur grand écran et une barre sur téléphone.
 */
/*
 * La gaine.
 *
 * Elle est tracée sous chaque ligne, dans une couleur de TEXTE du thème — donc
 * contrastée avec le fond dans les deux modes. Sans elle, une ligne publiée dans un
 * blanc cassé par son opérateur serait invisible sur le fond enfoncé, et le
 * contraste d'un objet graphique porteur de sens ne dépendrait plus que du bon
 * vouloir du transporteur.
 *
 * C'est le procédé des cartes de réseau imprimées, et il vaut ici pour
 * exactement la même raison.
 */
.map__casing {
    stroke: var(--tds-text-secondary);
    stroke-width: 6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.map__casing--context { stroke-width: 4; opacity: 0.45; }

.map__trace {
    /*
     * `--line-paper` vient de /css/signes.css, engendrée depuis le référentiel
     * publié, exactement comme pour un disque de ligne. Aucune couleur
     * d'opérateur ne traverse ce fichier ni un gabarit : c'est le contrat de
     * signalétique, et la politique de sécurité de contenu interdit de toute
     * façon un `style=` en ligne.
     */
    stroke: var(--line-paper, var(--tds-border-strong));
    stroke-width: 4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/*
 * Le contexte : plus fin, plus pâle, et DERRIÈRE. Il ne se distingue pas que
 * par la couleur — l'épaisseur seule suffirait à le reconnaître en niveaux de
 * gris, ce qui est le test qu'on s'impose.
 */
.map__trace--context {
    stroke-width: 2.5;
    opacity: 0.45;
}

/*
 * Une marque d'arrêt tient DANS la gaine, elle ne la recouvre pas.
 *
 * Mesuré en regardant le tram 92 : à trois unités de rayon et 1,5 de contour,
 * une marque faisait 7,5 unités de diamètre contre 6 pour la gaine. Trente-
 * quatre marques enfilées sur un tracé de trois cents unités le cachaient
 * entièrement — la carte montrait un collier de perles, pas une ligne.
 *
 * Le rayon est posé ici et non dans le gabarit : c'est une décision de rendu,
 * et le gabarit n'a pas à connaître l'épaisseur du trait qu'il ne dessine pas.
 */
.map__stop {
    r: 2;
    fill: var(--tds-surface-default);
    stroke: var(--tds-text-secondary);
    stroke-width: 1.2;
}

/* Une extrémité est PLEINE : la forme porte la différence, pas la teinte. */
.map__end {
    r: 5;
    fill: var(--tds-text-primary);
    stroke: var(--tds-surface-default);
    stroke-width: 2;
}

.map__caption {
    display: flex;
    flex-direction: column;
    gap: var(--tds-space-2);
    margin: 0;
}

/*
 * Les extrémités écrites.
 *
 * Origine et terminus, dans cet ordre, séparés par une flèche. La flèche est
 * décorative : le sens est déjà porté par l'ordre de lecture de la liste et par
 * le libellé que chaque rangée annonce à un lecteur d'écran.
 */
.map__ends {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tds-space-2);
    font-size: var(--tds-font-size-body-small);
}

.map__ends li { display: inline-flex; align-items: center; gap: var(--tds-space-2); }

.map__ends li[data-role="terminus"]::before {
    content: "→";
    color: var(--tds-text-muted);
}

.map__ends a {
    display: inline-flex;
    align-items: center;
    /* Le plancher sur les DEUX axes, comme partout ailleurs depuis la 0.7.0. */
    min-block-size: var(--tds-space-6);
    padding-inline: var(--tds-space-1);
    border-radius: var(--tds-radius-sm);
}

.map__ends a:hover { background: var(--tds-action-ghost-hover); }

/*
 * La carte du réseau.
 *
 * Elle est plus haute que large sur téléphone et plus large que haute sur
 * écran : un réseau urbain est à peu près carré, une carte de 620 unités de
 * haut occuperait tout un écran de téléphone. La contrainte porte donc sur la
 * hauteur MAXIMALE, exprimée en unités de vue, et le viewBox s'y adapte seul.
 */
.map--network .map__canvas { max-block-size: 78vh; }

.map--network .map__trace { stroke-width: 2.5; }
.map--network .map__casing { stroke-width: 4; }

@media (max-width: 640px) {
    .map__canvas { max-block-size: 66vh; }
    .map--network .map__canvas { max-block-size: 62vh; }
}

/*
 * ── LA CARTE SITUÉE : LE FOND DU SOCLE, ET LA BASCULE ──────────────────────
 *
 * Deux rendus de la même carte cohabitent dans le document : le SVG que le
 * serveur a dessiné, et le moteur de navigateur qui la repeint sur un vrai
 * plan de ville. Les règles ci-dessous ne font QUE choisir lequel se voit.
 *
 * L'ordre est ce qui rend l'amélioration progressive vraie plutôt que
 * proclamée : l'état par défaut du document est le SVG visible et le moteur
 * absent. Rien de conditionnel n'est écrit dans le gabarit ; c'est le RUNTIME
 * qui pose `data-tmaps-state`, et il ne le pose que s'il a réellement peint.
 *
 * Sans JavaScript, l'attribut ne change jamais de valeur — et la carte reste
 * exactement celle de la 0.10.0.
 */
.map__engine {
    /*
     * Aucune hauteur tant que rien n'a peint. Un conteneur qui réserverait sa
     * place laisserait un trou gris sur toute page dont le moteur ne démarre
     * pas — et ce trou serait sous le SVG, qui lui s'affiche déjà.
     */
    display: none;
    inline-size: 100%;
    border-radius: var(--tds-radius-role-surface);
    overflow: hidden;
    background: var(--tds-surface-sunken);
}

/*
 * Peint : le moteur prend la place, le SVG s'efface.
 *
 * Le SVG n'est PAS retiré du document — il est masqué. `display: none` le
 * retire aussi de l'arbre d'accessibilité, ce qui est voulu : la même carte
 * annoncée deux fois est une carte annoncée une fois de trop, et le runtime
 * écrit son propre compte rendu dans la zone d'annonce.
 */
.map[data-tmaps-state="ready"] .map__engine { display: block; block-size: 62vh; max-block-size: 620px; }
.map[data-tmaps-state="ready"] .map__canvas { display: none; }

/* Une carte de réseau mérite plus de hauteur : c'est une ville entière. */
.map--network[data-tmaps-state="ready"] .map__engine { block-size: 78vh; max-block-size: 760px; }

/*
 * Le moteur n'a pas démarré. Rien à faire — le SVG est déjà là, visible, et
 * n'a jamais cessé de l'être. Seule la note apparaît, pour que le lecteur
 * sache qu'il regarde le repli et pourquoi.
 */
.map__fallback-notice {
    margin: 0;
    padding: var(--tds-space-2) var(--tds-space-3);
    background: var(--tds-surface-sunken);
    border-radius: var(--tds-radius-role-surface);
    color: var(--tds-text-secondary);
    font-size: var(--tds-font-size-body-small);
}

/*
 * La mention du fond : servie par le SERVEUR, dans le document, avant tout
 * script. Une mention qui dépendrait de WebGL disparaîtrait le jour où le
 * moteur tombe — le jour, précisément, où la licence continue de s'appliquer.
 */
.map__attribution {
    margin: 0;
    color: var(--tds-text-muted);
    font-size: var(--tds-font-size-label);
}

.map__attribution a { color: inherit; }

/*
 * Les contrôles du moteur, ramenés dans le thème.
 *
 * `maplibre-gl.css` peint en dur du blanc et des ombres : sur le mode sombre du
 * Design System, les boutons deviendraient des rectangles blancs. Les jetons
 * les rattachent à la surface du produit, dans les deux modes.
 */
.map__engine .maplibregl-ctrl-group {
    background: var(--tds-surface-default);
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    box-shadow: none;
}

.map__engine .maplibregl-ctrl-group button + button { border-block-start-color: var(--tds-border-default); }
.map__engine .maplibregl-ctrl-scale {
    background: var(--tds-surface-default);
    border-color: var(--tds-border-strong);
    color: var(--tds-text-secondary);
}

@media (max-width: 640px) {
    /*
     * Sur téléphone, la carte manipulable garde une hauteur utilisable : plus
     * basse qu'un écran — la page doit rester scrollable autour d'elle — mais
     * assez haute pour qu'un déplacement du doigt ait un sens.
     */
    .map[data-tmaps-state="ready"] .map__engine { block-size: 58vh; }
    .map--network[data-tmaps-state="ready"] .map__engine { block-size: 62vh; }
}

/*
 * Les boutons du moteur sont trop petits pour un doigt.
 *
 * Mesuré au navigateur à 375 px : 29 × 29 px, soit la valeur par défaut de
 * `maplibre-gl.css`. Le Design System pose une cible tactile à
 * `--tds-touch-target-web`, et un contrôle de zoom n'a aucune raison d'y
 * échapper — c'est le seul moyen d'agrandir la carte sur un écran où le
 * pincement n'est pas toujours praticable d'une main.
 *
 * Le critère est le POINTEUR et non la largeur : une tablette large se pilote
 * au doigt, un petit portable à la souris.
 */
@media (pointer: coarse) {
    .map__engine .maplibregl-ctrl-group button {
        inline-size: var(--tds-touch-target-web);
        block-size: var(--tds-touch-target-web);
    }
}

/*
 * ── LES MARQUES D'ARRÊT DU MOTEUR ─────────────────────────────────────────
 *
 * Elles ne sont PAS peintes dans le canevas : le moteur les pose en HTML,
 * par-dessus. Elles n'héritent donc de rien, et sans ces règles elles sont ce
 * qu'un `<button>` est par défaut — mesuré le 2026-08-31 : 16 × 6 px, bordure
 * `outset`, et le gris de formulaire du navigateur. Trente-trois rectangles
 * gris sur un plan de ville.
 *
 * La feuille du socle en porte l'équivalent, mais elle habille son
 * LABORATOIRE — en-tête, panneaux, pied — et ses classes `.map` entreraient en
 * collision avec celles de ce produit. Ce qui est repris ici est donc l'INTENTION
 * et non le fichier : une pastille cerclée de la surface du thème, exactement
 * ce que `.map__end` fait dans le SVG, pour que les deux rendus se ressemblent.
 */
.tmaps-marker {
    --tmaps-marker-colour: var(--tds-text-primary);
    display: grid;
    place-items: center;
    /*
     * La cible tactile fait la taille recommandée, la pastille reste petite.
     * Un arrêt de métro qui demanderait un doigt de 44 px de précision serait
     * inatteignable ; une cible de 10 px le serait tout autant.
     */
    inline-size: var(--tds-touch-target-web);
    block-size: var(--tds-touch-target-web);
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

.tmaps-marker__dot {
    inline-size: 10px;
    block-size: 10px;
    border-radius: var(--tds-radius-role-pill);
    background: var(--tmaps-marker-colour);
    /*
     * La gaine claire, et pour la même raison que celle des tracés : une
     * marque posée sur un plan gris a besoin d'un bord qui la détache, et ce
     * bord doit venir du thème et non de l'opérateur.
     */
    box-shadow: 0 0 0 2px var(--tds-surface-default);
}

.tmaps-marker:focus-visible {
    outline: var(--tds-focus-width) solid var(--tds-border-focus);
    outline-offset: var(--tds-focus-offset);
    border-radius: var(--tds-radius-role-pill);
}

/*
 * Personne n'a demandé d'animation.
 *
 * Le moteur anime ses transitions de zoom par défaut. Une carte qui glisse
 * n'est pas une décoration pour qui souffre de troubles vestibulaires.
 */
@media (prefers-reduced-motion: reduce) {
    .map__engine .maplibregl-canvas { transition: none; }
}

/*
 * Ce qu'on affiche quand il n'y a rien à afficher.
 *
 * Un opérateur sans `shapes.txt` n'est pas une panne : c'est un état du jeu de
 * données. L'écran le DIT, par l'état commun `tds-state--unavailable` posé dans
 * sa section, et ne dessine surtout pas une droite entre deux gares pour combler.
 */

/* ═══════════════════════════════════════════════════════════════════════════
 * 0.28.0 — les courses actives d'une ligne
 * ═══════════════════════════════════════════════════════════════════════════ */

/*
 * Une course, sur l'axe du thermomètre.
 *
 * La FORME dit la nature de la position, et le texte la redit : pleine pour une
 * position observée, cerclée pour une progression déduite d'heures annoncées,
 * creuse et pointillée pour l'horaire publié, rayée pour une donnée ancienne.
 * Aucune n'est portée par la teinte seule — la teinte est celle de la ligne,
 * comme l'axe, et une ligne sans couleur prend le neutre du thème.
 *
 * La cible atteint le plancher de 24 px par sa marge intérieure, comme le lien
 * « Horaire » de la même rangée depuis la 0.5.0 : quinze véhicules sur une ligne
 * ne peuvent pas prendre 44 px chacun sans enterrer les arrêts qu'ils situent.
 */
.thermometer__runs {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--tds-space-1) var(--tds-space-2);
}

/*
 * Parties vers l'arrêt suivant : décalées sous le nom, là où l'axe descend. Le
 * trait de gauche prolonge visuellement l'axe jusqu'à elles — c'est ce qui les
 * lit « entre » deux arrêts, et non « à » celui du dessus.
 */
.thermometer__runs--between {
    margin-block-start: var(--tds-space-1);
    padding-inline-start: var(--tds-space-3);
    border-inline-start: var(--tds-border-width-strong) dotted var(--thermo-rail);
}

.run {
    --run-ink: var(--line-paper, var(--tds-text-primary));
    display: inline-flex; align-items: center; gap: var(--tds-space-1);
    min-block-size: var(--tds-space-6);
    padding: 0 var(--tds-space-2) 0 var(--tds-space-1);
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    border-radius: var(--tds-radius-role-pill);
    background: var(--tds-surface-default);
    color: var(--tds-text-primary);
    font: inherit;
    font-size: var(--tds-font-size-caption);
    font-weight: var(--tds-font-weight-medium);
    cursor: pointer;
}
.run:hover { background: var(--tds-action-ghost-hover); }
.run:focus-visible {
    outline: var(--tds-focus-width) solid var(--tds-border-focus);
    outline-offset: var(--tds-focus-offset);
}
.run[aria-pressed="true"] {
    border-color: var(--tds-border-focus);
    box-shadow: 0 0 0 var(--tds-border-width-default) var(--tds-border-focus);
    font-weight: var(--tds-font-weight-semibold);
}

.run__glyph {
    flex: 0 0 auto;
    inline-size: var(--tds-space-3); block-size: var(--tds-space-3);
    border-radius: var(--tds-radius-full);
    background: var(--run-ink);
    box-shadow: 0 0 0 var(--tds-border-width-default) var(--tds-text-primary);
}
.run__text { white-space: nowrap; }

/* 0.37.0 — la position est publiée, l'arrêt est calculé : la marque reste
   pleine (mesurée) et se cerne d'un pointillé. Jamais la couleur seule. */
[data-nature="calculated"] > .run__glyph {
    outline: var(--tds-border-width-default) dotted var(--tds-text-primary);
    outline-offset: var(--tds-border-width-strong);
}

[data-nature="deduced"] > .run__glyph {
    background: var(--tds-surface-default);
    box-shadow:
        inset 0 0 0 var(--tds-border-width-strong) var(--run-ink, var(--tds-text-primary)),
        0 0 0 var(--tds-border-width-default) var(--tds-text-primary);
}
[data-nature="scheduled"] > .run__glyph {
    background: transparent;
    box-shadow: none;
    border: var(--tds-border-width-default) dashed var(--tds-text-secondary);
}
[data-nature="cancelled"] > .run__glyph {
    background: transparent;
    box-shadow: none;
    border: var(--tds-border-width-strong) solid var(--tds-status-danger);
}
[data-stale="oui"] > .run__glyph {
    background: repeating-linear-gradient(45deg, var(--tds-text-secondary) 0 2px, transparent 2px 4px);
    box-shadow: 0 0 0 var(--tds-border-width-default) var(--tds-text-secondary);
}
.run[data-stale="oui"] { color: var(--tds-text-secondary); border-style: dashed; }

/* Au-delà de trois courses au même endroit : un compteur qui se déplie. */
.run-cluster > summary {
    display: inline-flex; align-items: center;
    min-block-size: var(--tds-space-6);
    padding-inline: var(--tds-space-2);
    border-radius: var(--tds-radius-role-pill);
    background: var(--tds-status-info-subtle);
    color: var(--tds-text-primary);
    font-size: var(--tds-font-size-caption);
    font-weight: var(--tds-font-weight-semibold);
    cursor: pointer;
}
.run-cluster[open] > summary { margin-block-end: var(--tds-space-1); }
.run-cluster[open] { display: flex; flex-wrap: wrap; gap: var(--tds-space-1) var(--tds-space-2); }

.run-legend {
    list-style: none; margin: var(--tds-space-3) 0 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: var(--tds-space-2) var(--tds-space-4);
    font-size: var(--tds-font-size-caption);
    color: var(--tds-text-secondary);
}
.run-legend li { display: inline-flex; align-items: center; gap: var(--tds-space-1); }
.run-legend .run__glyph { --run-ink: var(--tds-text-primary); }

.run-detail {
    margin-block-start: var(--tds-space-3);
    padding: var(--tds-space-3) var(--tds-space-4);
    border: var(--tds-border-width-default) solid var(--tds-border-focus);
    border-radius: var(--tds-radius-role-surface);
    background: var(--tds-surface-raised);
}
.run-detail p { margin: 0; }
.run-detail__title {
    font-size: var(--tds-font-size-caption);
    font-weight: var(--tds-font-weight-semibold);
    color: var(--tds-text-secondary);
}
.run-detail__label { font-weight: var(--tds-font-weight-semibold); overflow-wrap: anywhere; }
.run-detail__actions { display: flex; flex-wrap: wrap; gap: var(--tds-space-2); margin-block-start: var(--tds-space-2) !important; }

.run-list { margin: var(--tds-space-1) 0 0; padding-inline-start: var(--tds-space-5); font-size: var(--tds-font-size-body-small); }
.run-list__title { margin: 0; font-weight: var(--tds-font-weight-semibold); font-size: var(--tds-font-size-body-small); }

/*
 * ── Les perturbations sur le thermomètre — 0.29.0 ───────────────────────────
 *
 * Trois signes, et aucun n'est porté par la teinte seule :
 *
 *   - une FORME sur la rangée : un losange plein pour un arrêt que la source
 *     rattache à ce sens, un losange pointillé quand le sens n'est pas établi ;
 *   - un TRAIT hachuré parallèle à l'axe, qui relie des arrêts cités à la
 *     suite. Il ne remplace pas l'axe et ne le colore pas : la ligne passe
 *     toujours, et le trait dit « cités », pas « fermé » ;
 *   - un MOT, en tête de chaque suite, et une phrase complète pour le lecteur
 *     d'écran à chaque rangée.
 *
 * La teinte est le jeton d'avertissement du Design System, déjà celui des
 * perturbations du produit.
 */
.stop-disruption {
    display: inline-flex; align-items: center; gap: var(--tds-space-1);
    min-block-size: var(--tds-space-6);
    padding-inline: var(--tds-space-2) var(--tds-space-2);
    border-radius: var(--tds-radius-role-pill);
    background: var(--tds-status-warning-subtle);
    color: var(--tds-text-primary);
    font-size: var(--tds-font-size-caption);
    font-weight: var(--tds-font-weight-semibold);
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}
a.stop-disruption:hover { text-decoration-thickness: 2px; }
a.stop-disruption:focus-visible {
    outline: var(--tds-focus-width) solid var(--tds-border-focus);
    outline-offset: var(--tds-focus-offset);
}
.stop-disruption--quiet { min-block-size: 0; padding: 0; background: transparent; }

.stop-disruption__glyph {
    flex: 0 0 auto;
    inline-size: 0.6rem; block-size: 0.6rem;
    transform: rotate(45deg);
    background: var(--tds-status-warning);
    box-shadow: 0 0 0 var(--tds-border-width-default) var(--tds-text-primary);
}
.thermometer__stop[data-disruption="incertain"] .stop-disruption__glyph,
.disruption-legend [data-settled="non"] .stop-disruption__glyph {
    background: transparent;
    box-shadow: none;
    border: var(--tds-border-width-strong) dotted var(--tds-status-warning);
}

.thermometer__stop[data-disruption] .thermometer__axis {
    --disruption-band: repeating-linear-gradient(to bottom, var(--tds-status-warning) 0 3px, transparent 3px 6px);
    background-image: var(--disruption-band);
    background-repeat: no-repeat;
    background-size: var(--tds-border-width-accent) 100%;
    background-position: 0 0;
}
.thermometer__stop[data-disruption-edge="start"] .thermometer__axis { background-position: 0 var(--thermo-lead); }
.thermometer__stop[data-disruption-edge="end"] .thermometer__axis { background-size: var(--tds-border-width-accent) calc(var(--thermo-lead) + var(--thermo-mark)); }
.thermometer__stop[data-disruption-edge="single"] .thermometer__axis {
    background-position: 0 var(--thermo-lead);
    background-size: var(--tds-border-width-accent) var(--thermo-mark);
}

.disruption-legend__band {
    inline-size: var(--tds-border-width-accent); block-size: var(--tds-space-4);
    background-image: repeating-linear-gradient(to bottom, var(--tds-status-warning) 0 3px, transparent 3px 6px);
}

/* La liste des perturbations du sens affiché, rafraîchie avec le thermomètre. */
.line-disruptions {
    margin-block: 0 var(--tds-space-3);
    padding: var(--tds-space-3) var(--tds-space-4);
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    border-inline-start: var(--tds-border-width-accent) solid var(--tds-status-warning);
    border-radius: var(--tds-radius-role-surface);
    background: var(--tds-status-warning-subtle);
    font-size: var(--tds-font-size-body-small);
    color: var(--tds-text-primary);
}
.line-disruptions:focus { outline: none; }
.line-disruptions__title { margin: 0; font-weight: var(--tds-font-weight-semibold); }
.line-disruptions__list { margin: var(--tds-space-2) 0 0; padding-inline-start: var(--tds-space-4); display: grid; gap: var(--tds-space-2); }
.line-disruptions__list li { overflow-wrap: anywhere; }
.line-disruptions__list li:target { outline: var(--tds-focus-width) solid var(--tds-border-focus); outline-offset: var(--tds-space-1); }
.line-disruptions a,
.line-disruption a { color: var(--tds-text-primary); display: inline-flex; align-items: center; min-block-size: var(--tds-space-6); }
.line-disruptions .count-note { margin: var(--tds-space-2) 0 0; color: var(--tds-text-secondary); }

.line-disruption__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--tds-space-2); margin: 0 0 var(--tds-space-2); font-size: var(--tds-font-size-body-small); }
.line-disruption__where { margin: var(--tds-space-2) 0 0; padding-inline-start: var(--tds-space-4); font-size: var(--tds-font-size-body-small); overflow-wrap: anywhere; }
.line-disruption:target { outline: var(--tds-focus-width) solid var(--tds-border-focus); outline-offset: var(--tds-space-1); }

/* Le compteur de ce qui roule, dans le choix du sens. */
.direction-switch__live {
    display: inline-flex; align-items: center; justify-content: center;
    min-inline-size: var(--tds-space-5);
    padding-inline: var(--tds-space-1);
    border-radius: var(--tds-radius-role-pill);
    background: var(--tds-status-info-subtle);
    color: var(--tds-text-primary);
    font-size: var(--tds-font-size-caption);
    font-weight: var(--tds-font-weight-semibold);
}

/* Sur la carte : la même course, la même forme. */
.map__run circle {
    fill: var(--line-paper, var(--tds-text-primary));
    stroke: var(--tds-surface-default);
    stroke-width: 2;
    cursor: pointer;
}
.map__run[data-nature="scheduled"] circle,
.map__run[data-nature="deduced"] circle {
    fill: var(--tds-surface-default);
    stroke: var(--line-paper, var(--tds-text-primary));
    stroke-width: 3;
}
.map__run[data-nature="scheduled"] circle { stroke-dasharray: 3 2; }
.map__run[data-stale="oui"] { opacity: var(--tds-state-disabled-opacity); }
.map__run[data-selected="oui"] circle {
    stroke: var(--tds-border-focus);
    stroke-width: 4;
}

/*
 * ── L'Admin — 0.33.0, incrément 6 ─────────────────────────────────────────
 *
 * Plus dense que les pages publiques, avec la même hiérarchie : état, problème,
 * contexte, puis le détail technique replié. Les états sont des `tds-badge` —
 * succès, attention, danger, neutre — écrits en toutes lettres, et le symbole qui
 * les précède ne fait que les redire.
 */
.admin-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--tds-space-4); grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr)); }
.admin-card__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--tds-space-2); margin-bottom: var(--tds-space-2); }
.admin-card__head .card__title { margin: 0; }
.admin-incident { border-inline-start: var(--tds-border-width-accent) solid var(--tds-status-warning); padding-inline-start: var(--tds-space-3); margin-bottom: var(--tds-space-3); }
.admin-incident p { margin: 0; }
.admin-incident[data-level="echec"] { border-inline-start-color: var(--tds-status-danger); }
.admin-incident[data-level="suspendu"] { border-inline-start-color: var(--tds-border-default); }

/* Listes métier groupées : même taille de signe et contraste que la signalétique. */
.line-groups { display: grid; gap: var(--tds-space-3); width: 100%; }
.line-groups__title { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--tds-space-2); margin-block: 0 var(--tds-space-2); }
.line-groups__items { display: flex; flex-wrap: wrap; gap: var(--tds-space-2); list-style: none; margin: 0; padding: 0; }
.line-groups__items > li { max-width: 100%; }
.line-groups__link { display: inline-flex; align-items: center; gap: var(--tds-space-2); max-width: 100%; overflow-wrap: anywhere; }
/* Le lien courant est trop peu contrasté sur le fond d'annonce en sombre.
   Les noms restent en texte primaire ; les numéros gardent leur couleur publiée. */
.disruption .line-groups__link { color: var(--tds-text-primary); }
.line-groups__more { margin-top: var(--tds-space-2); }
.line-groups__collapse { display: none; }
.line-groups__more[open] .line-groups__expand { display: none; }
.line-groups__more[open] .line-groups__collapse { display: inline; }

/* ── Précision : un sujet, son contexte, puis la lecture transport ────────── */
/* Le filet de marque appartient à tds-header--precision. La couleur Transit
 * reste distincte des signes de ligne et des états de temps réel. */
.transit-subject {
    padding-block-end: var(--tds-space-4);
    border-bottom: var(--tds-border-width-default) solid var(--tds-border-default);
    overflow-wrap: anywhere;
}
.transit-entry {
    margin-block-end: var(--tds-space-8);
    align-items: start;
}
.transit-entry .transit-subject { margin-block-end: 0; }
.transit-entry .search-lead { padding: var(--tds-space-4); }
.transit-context {
    padding: var(--tds-space-3);
    background: var(--tds-brand-subtle);
    color: var(--tds-text-primary);
    border-radius: var(--tds-radius-role-surface);
}
.direction-switch a { background: var(--tds-surface-default); }
.transit-services {
    padding-block: var(--tds-space-2);
    padding-inline: var(--tds-space-3);
    background: var(--tds-brand-subtle);
    border-radius: var(--tds-radius-role-control);
    width: fit-content;
    max-width: 100%;
}
.transit-freshness {
    padding: var(--tds-space-3);
    border-inline-start: var(--tds-border-width-accent) solid var(--tds-brand-primary);
    background: var(--tds-brand-subtle);
    color: var(--tds-text-primary);
}

/* Favoris personnels : mêmes actions et espacement sur toutes les fiches. */
.favorite-action { display: inline-block; margin-block: var(--tds-space-2); margin-inline-end: var(--tds-space-2); }
.favorites-list { list-style: none; padding: 0; }
.favorites-list > li { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; padding-block: 1rem; border-bottom: 1px solid var(--tds-border-default); }
.favorites-list a { overflow-wrap: anywhere; }

/* Relations : deux étapes lisibles, puis une rangée structurée par ligne. */
.relation-journey {
    list-style: none; padding: 0; margin: 0 0 var(--tds-space-6);
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    border: var(--tds-border-width-default) solid var(--tds-border-default);
    border-radius: var(--tds-radius-role-surface); overflow: hidden;
}
.relation-journey > li { display: grid; align-content: start; gap: var(--tds-space-2); padding: var(--tds-space-4); min-width: 0; }
.relation-journey > li + li { border-inline-start: var(--tds-border-width-default) solid var(--tds-border-default); }
.relation-journey [aria-current="step"] { background: var(--tds-surface-sunken); }
.relation-journey__label, .relation-line__meta { color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); }
.relation-journey__name { font-size: var(--tds-font-size-heading3); font-weight: var(--tds-font-weight-semibold); overflow-wrap: anywhere; }
.relation-search { max-width: 48rem; }
.relation-search h2 { margin-bottom: var(--tds-space-4); }
.search-status { min-height: 1.5em; margin-block: var(--tds-space-2); color: var(--tds-text-secondary); }
[data-live-search] ~ [aria-busy="true"], #relation-results[aria-busy="true"], #search-results[aria-busy="true"] { opacity: 0.6; }
.relation-lines { list-style: none; margin: 0; padding: 0; border-block: var(--tds-border-width-default) solid var(--tds-border-default); }
.relation-line { display: flex; align-items: center; gap: var(--tds-space-4); padding: var(--tds-space-5) var(--tds-space-2); }
.relation-line + .relation-line { border-top: var(--tds-border-width-default) solid var(--tds-border-default); }
.relation-line__main { flex: 1; min-width: 0; }
.relation-line__identity { display: flex; align-items: center; gap: var(--tds-space-3); font-weight: var(--tds-font-weight-semibold); text-decoration: none; overflow-wrap: anywhere; }
.relation-line__identity:hover { text-decoration: underline; }
.relation-line__meta { margin-block: var(--tds-space-2); }
.relation-directions { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--tds-space-2) var(--tds-space-4); overflow-wrap: anywhere; }
.relation-directions li { padding-inline-start: var(--tds-space-3); border-inline-start: var(--tds-border-width-strong) solid var(--tds-border-default); }
.relation-pagination { display: flex; justify-content: space-between; flex-wrap: wrap; gap: var(--tds-space-3); }
@media (max-width: 40rem) {
    .relation-journey { grid-template-columns: minmax(0, 1fr); }
    .relation-journey > li + li { border-inline-start: 0; border-top: var(--tds-border-width-default) solid var(--tds-border-default); }
    .relation-line { align-items: flex-start; flex-direction: column; }
}
.relation-line__schedule { display: grid; gap: var(--tds-space-3); justify-items: start; max-width: 22rem; overflow-wrap: anywhere; }
.service-availability { margin-block: var(--tds-space-5); }
.service-availability__next { font-weight: var(--tds-font-weight-semibold); }
.service-calendar { border: var(--tds-border-width-default) solid var(--tds-border-default); padding: var(--tds-space-3); border-radius: var(--tds-radius-role-surface); max-width: 42rem; }
.service-calendar summary { cursor: pointer; font-weight: var(--tds-font-weight-semibold); padding-block: var(--tds-space-2); }
.service-calendar__nav { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--tds-space-2); margin-block: var(--tds-space-3); }
.service-calendar__grid { width: 100%; table-layout: fixed; border-collapse: collapse; text-align: center; }
.service-calendar__grid th { font-size: var(--tds-font-size-body-small); color: var(--tds-text-secondary); padding-block: var(--tds-space-2); }
.service-calendar__grid td { padding: var(--tds-space-1); }
.service-calendar__grid a, .service-calendar__off { display: flex; align-items: center; justify-content: center; min-height: var(--tds-control-height-md); border-radius: var(--tds-radius-role-control); }
.service-calendar__grid a { background: var(--tds-surface-sunken); font-weight: var(--tds-font-weight-semibold); }
.service-calendar__grid [aria-current="date"] { outline: var(--tds-border-width-strong) solid var(--tds-border-strong); }
.service-calendar__off { text-decoration: line-through; color: var(--tds-text-secondary); }

.service-calendar__scroll { overflow-x: auto; }
.service-calendar__grid { min-width: 17em; }

.section__title { overflow-wrap: anywhere; }

/* Favoris : le compte se propose uniquement après le geste. */
.favorite-toggle { gap: var(--tds-space-2); white-space: nowrap; }
.favorite-mark { font-size: 1.4em; line-height: 1; }
.favorite-toggle--saved .favorite-mark { color: var(--tds-brand-primary); }

/* Tableau ferroviaire : mêmes colonnes d’une rangée à l’autre. */
.train-eyebrow, .train-columns { color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); }
.train-eyebrow { display: block; margin-block-end: var(--tds-space-2); }
.train-shortcuts { margin-block: var(--tds-space-4); }
.train-shortcuts summary, .train-edit summary { cursor: pointer; padding-block: var(--tds-space-3); }
.train-shortcuts__list { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: var(--tds-space-2); list-style: none; padding: 0; }
.train-shortcuts__list a { display: block; padding: var(--tds-space-3); background: var(--tds-surface-default); border: 1px solid var(--tds-border-default); border-radius: var(--tds-radius-role-control); }
.train-journey { display: grid; grid-template-columns: minmax(0,1fr) auto; row-gap: var(--tds-space-3); border: 1px solid var(--tds-border-default); border-radius: var(--tds-radius-role-surface); background: var(--tds-surface-default); padding: var(--tds-space-5); margin-block: var(--tds-space-5); }
.train-journey__stops { grid-column: 1 / -1; display: grid; grid-template-columns: minmax(0,1fr) auto minmax(0,1fr); gap: var(--tds-space-4); align-items: center; }
.train-journey__stops strong { font-size: var(--tds-font-size-heading3); overflow-wrap: anywhere; display: block; }
.train-invert { font-size: 1.5rem; }
.train-journey__actions { grid-column: 2; grid-row: 2; align-self: start; position: relative; z-index: 1; }
.train-edit { grid-column: 1 / -1; grid-row: 2; }
.train-edit > summary { max-width: calc(100% - 8rem); }
.train-selection { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: var(--tds-space-3); margin-block: var(--tds-space-3); }
.train-selection fieldset { min-width: 0; margin: 0; padding: var(--tds-space-3); border: 1px solid var(--tds-border-default); }
.train-selection > .train-field, .train-selection > p { grid-column: 1 / -1; }
.train-field { margin-block: var(--tds-space-2); }
.train-action { justify-self: start; }
.train-toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--tds-space-3); flex-wrap: wrap; }
.train-modes { display: inline-flex; flex-wrap: wrap; gap: var(--tds-space-1); padding: var(--tds-space-1); border: 1px solid var(--tds-border-default); border-radius: var(--tds-radius-role-control); background: var(--tds-surface-default); }
.train-modes a { padding: var(--tds-space-2) var(--tds-space-3); border-radius: var(--tds-radius-role-control); text-decoration: none; color: var(--tds-text-secondary); font-weight: var(--tds-font-weight-semibold); }
.train-modes [aria-current="page"] { background: var(--tds-brand-subtle); color: var(--tds-text-primary); box-shadow: inset 0 -2px var(--tds-brand-primary); }
.train-reading-note { font-size: var(--tds-font-size-body-small); color: var(--tds-text-secondary); margin-block: var(--tds-space-3) var(--tds-space-5); }
.train-board { background: var(--tds-surface-default); border: 1px solid var(--tds-border-default); border-radius: var(--tds-radius-role-surface); margin-block: var(--tds-space-5); overflow: hidden; }
.train-board__heading { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: var(--tds-space-3); padding: var(--tds-space-4) var(--tds-space-5); }
.train-board__heading h2 { font-size: var(--tds-font-size-heading3); margin: 0; overflow-wrap: anywhere; }
.train-board__updated { color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); }
.train-columns, .train-entry { display: grid; grid-template-columns: minmax(6.5rem,1fr) minmax(0,4fr) minmax(3rem,.6fr); column-gap: var(--tds-space-4); align-items: center; padding: var(--tds-space-4) var(--tds-space-5); }
.train-columns { background: var(--tds-surface-sunken); border-block: 1px solid var(--tds-border-default); padding-block: var(--tds-space-2); }
.train-board__scroll { position: relative; overflow-x: auto; overscroll-behavior-inline: contain; }
.train-departures, .train-columns { min-width: 16.5rem; }
.train-departures { list-style: none; padding: 0; margin: 0; }
.train-entry + .train-entry { border-block-start: 1px solid var(--tds-border-default); }
.train-entry > * { min-width: 0; overflow-wrap: anywhere; }
.train-entry__time time { display: block; font-size: 1.4rem; font-variant-numeric: tabular-nums; font-weight: var(--tds-font-weight-semibold); }
.train-entry small { display: block; color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); margin-block-start: var(--tds-space-1); }
.train-entry__destination > a, .train-entry__destination > strong { display: inline-flex; align-items: center; gap: var(--tds-space-2); font-weight: var(--tds-font-weight-semibold); text-decoration: none; color: var(--tds-text-primary); }
.train-entry__destination > a:hover { text-decoration: underline; }
.train-entry__meta { display: flex; flex-wrap: wrap; gap: var(--tds-space-1) var(--tds-space-3); color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); margin-block-start: var(--tds-space-1); }
.train-platform { display: inline-flex; min-width: 2.5em; min-height: 2.25em; padding: var(--tds-space-1); align-items: center; justify-content: center; border: 1px solid var(--tds-border-default); background: var(--tds-surface-sunken); border-radius: var(--tds-radius-role-control); font-weight: var(--tds-font-weight-semibold); font-variant-numeric: tabular-nums; }
.train-platform--changed { border-width: 2px; border-color: var(--tds-status-warning); }
.train-entry__status { margin-block-start: var(--tds-space-2); display: grid; justify-items: start; gap: var(--tds-space-1); }
.train-category { font-weight: var(--tds-font-weight-semibold); color: var(--tds-text-primary); }
.train-delay { font-size: var(--tds-font-size-body-small); font-weight: var(--tds-font-weight-semibold); }
.train-delay--late { color: var(--tds-status-danger); }
.train-quality { color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); }
.train-entry--cancelled time { text-decoration: line-through; }
.train-board__footer { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--tds-space-3); border-block-start: 1px solid var(--tds-border-default); padding: var(--tds-space-3) var(--tds-space-5); color: var(--tds-text-secondary); font-size: var(--tds-font-size-body-small); }
.train-sources { max-width: 100%; }
.train-sources p { overflow-wrap: anywhere; }
.train-sources summary { cursor: pointer; }
.train-board__empty { padding: var(--tds-space-5); }
@media (max-width: 40rem) {
    .train-journey { padding: var(--tds-space-3); }
    .train-journey__stops { grid-template-columns: minmax(0,1fr) auto; gap: var(--tds-space-3); }
    .train-journey__stops > div:last-child { grid-column: 1; }
    .train-invert { grid-column: 2; grid-row: 1 / span 2; }
    .train-shortcuts__list, .train-selection { grid-template-columns: minmax(0,1fr); }
    .train-columns { display: none; }
    .train-entry { grid-template-columns: minmax(4.8rem,1fr) minmax(0,2.3fr) minmax(2.5rem,.5fr); gap: var(--tds-space-2); padding: var(--tds-space-4) var(--tds-space-3); align-items: start; }
    .train-board__heading, .train-board__footer { padding-inline: var(--tds-space-3); }
}

/* Le lockup suit le même mode explicite que les jetons du bandeau. */
:root .header-lockup--dark { display: none; }
[data-mode="dark"] .header-lockup--light { display: none; }
[data-mode="dark"] .header-lockup--dark { display: block; }

/* Suggestions de gares : restent dans le flux pour les petits écrans. */
.train-station-options { list-style: none; padding: 0; margin: var(--tds-space-1) 0; max-height: 16rem; overflow-y: auto; border: 1px solid var(--tds-border-default); border-radius: var(--tds-radius-role-control); background: var(--tds-surface-default); }
.train-station-options [role="option"] { padding: var(--tds-space-3); cursor: pointer; overflow-wrap: anywhere; }
.train-station-options [aria-selected="true"], .train-station-options [role="option"]:hover { background: var(--tds-brand-subtle); }

/* Repères communs : le mode et la nature de l'heure restent visibles partout. */
.transport-icon { width: 1.35rem; height: 1.35rem; flex: 0 0 auto; color: var(--tds-text-secondary); }
.time-state { display: inline-flex; align-items: center; gap: var(--tds-space-1); color: var(--tds-text-muted); font-size: var(--tds-font-size-caption); font-weight: var(--tds-font-weight-regular); }
.time-state--live { color: var(--tds-status-success); }
.time-state--stale { color: var(--tds-status-warning); }
.time-state__clock { width: 1rem; height: 1rem; flex: 0 0 auto; }
.departure__wait > .time-state { display: flex; }
.departure__wait > a, .train-entry__time > a { color: inherit; text-decoration: none; }
.departure__wait > a:hover, .train-entry__time > a:hover { text-decoration: underline; }
.train-entry__meta .line-sign { --sign-size: 1.8rem; }
.train-entry__meta .line-sign__number { font-size: 1.25rem; }
.train-entry__meta .line-sign__mode { font-size: .875rem; }

.train-journey__stops strong a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: .15em; }
.thermometer__time > .time-state { display: inline-flex; margin-inline-start: var(--tds-space-1); vertical-align: middle; }

/* Matériel annoncé, dans le flux et lisible à toute largeur. */
.material-units { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); gap: .75rem; padding-inline-start: 1.5rem; }
.material-unit { padding: .75rem; border: var(--tds-border-width-default) solid var(--tds-border-default); border-radius: var(--tds-radius-sm); overflow-wrap: anywhere; }
.material-unit__detail { display: block; margin-top: .25rem; }

/* Les originaux restent séparés ; la réduction d’affichage conserve leurs proportions. */
.material-unit__figure { margin: 0 0 var(--tds-space-3); }
.material-unit__image { display: block; max-width: 100%; height: auto; margin-block: var(--tds-space-2); image-rendering: pixelated; }
.material-unit__figure figcaption { font-size: var(--tds-font-size-body-small); color: var(--tds-text-secondary); }
.material-unit__placeholder { margin-top: 0; }
.illustration-credits { padding-inline-start: var(--tds-space-5); }
.illustration-credits li { margin-block: var(--tds-space-3); overflow-wrap: anywhere; }
.material-unit__figure figcaption a, .illustration-credits a { color: var(--tds-text-primary); text-decoration: underline; }
