/* ============================================================================
   Upchin — site vitrine public. Autonome, fonctionne en ltr et rtl.

   Charte « Heure Bleue » — miroir web de la charte iOS. Sources de vérité :
     apps/upchin-ios/Upchin/UI/UpchinColors.swift        (palette)
     apps/upchin-ios/Upchin/UI/DesignSystem.swift        (espacements, rayons)
     apps/upchin-ios/Upchin/docs/COULEUR_HEURE_BLEUE_REPRISE.md  (§4, §5)

   Invariants de marque (§4) : UN SEUL bleu structurel, le saphir. Le turquoise
   #6E86D8 est un ornement. L'or #C5A872 est rare. Les accents des 4 concepts
   sont GELÉS pour les orbes de l'app et ne sont pas repris ici.

   PARTI PRIS — SIMPLICITÉ RÉPÉTÉE (voir la section « Landing » plus bas).
   Deux fonds seulement, trois dispositions réutilisées, une seule forme de
   carte, des images posées à plat. La propreté vient de la RÉPÉTITION, pas de
   l'effet. Titres de section CENTRÉS, corps de texte aligné à gauche dans une
   colonne centrée. Le geste fort est unique : la grille de l'année.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   Inter (variable, auto-hébergée, latin/latin-ext) pour TOUT, titres compris —
   décision du PO le 2026-09-10 : Spectral retirée (« enlève Spectral du titre »).
   Serif dessiné pour la lecture à l'écran : registre adulte et calme, à
   l'opposé d'une app à badges. La pile système reste pour l'UI (libellés,
   boutons) — deux familles franchement distinctes, pas trois.
   ------------------------------------------------------------------------ */
/* ESSAI (2026-09-10, demande du PO) : Inter en police de lecture, la police
   de calai.app. Fonte VARIABLE (100–900) auto-hébergée, latin + latin-ext.
   `?font=system` sur n'importe quelle page repasse sur la pile système pour
   comparer sur Mac, où « système » = SF. Voir .font-system plus bas. */
@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("/site-assets/fonts/inter-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
        U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
        U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

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

:root {
    /* --- Palette brute « Heure Bleue » (miroir UpchinColors.swift) --------- */
    --upn-sapphire: #2247c2;
    --upn-night: #16308f;
    /* ⚠️ VALEUR CORRIGÉE d'après brand-extraction/variables.css (source officielle,
       2026-08-16). J'avais dérivé #6e86d8 du Swift : c'était le turquoise
       RÉTROGRADÉ de la note de reprise. Le token officiel du site est le
       turquoise vif. Ornement seul — bords, liserés, badges — JAMAIS un aplat. */
    --upn-turquoise: #27c6e6;
    --upn-lagoon: #0f7fa0;      /* aplats d'accent (toggles, CTA sombre) */
    --upn-azure-bright: #66b5ff; /* accent TEXTE sur surface sombre */
    --upn-warm: #b4552e;        /* terracotta : le « trop ». L'app ne fait JAMAIS de rouge d'alarme — règle de charte */
    --upn-warning: #f59e0b;     /* « à configurer » */
    --upn-ice: #dee6fa;
    --upn-pantone: #e9eff9;
    --upn-white: #ffffff;
    --upn-gold: #c5a872;
    --upn-graphite: #0e1b3f;
    --upn-charcoal: #50608c;
    --upn-piste: #dce3f3;
    --upn-encre-3: #7a8399;     /* 3,3:1 : états DÉSACTIVÉS seuls, jamais du texte à lire */
    --upn-sur-profond: #f2f5fd;

    /* Seule valeur absente de la palette iOS — assumée. Employée en APLAT sur
       les deux bandes sombres (hero, gain IRL) et sur le pied de page. */
    --upn-nuit: #0b1a4d;

    /* --- Légende de la grille de l'année ---------------------------------
       Reprise LITTÉRALE de YearGrid.swift : c'est la légende d'une figure de
       données, pas une palette d'interface. Comme les orbes, ces valeurs sont
       gelées et ne suivent pas les tokens (d'où le turquoise vif, absent du
       reste du site). Les modifier ferait mentir la figure. */
    --year-screen: #d98b82;     /* écran perdu (corail)     */
    --year-reclaimed: #27c6e6;  /* récupérable (turquoise)  */
    --year-free: #9fb8e6;       /* temps libre (bleu doux)  */
    --year-work: #e2e6ee;       /* travail (gris clair)     */

    /* --- Rôles sémantiques ------------------------------------------------ */
    --bg: var(--upn-pantone);
    --surface: var(--upn-white);
    --deep: var(--upn-night);
    --ink: var(--upn-graphite);
    /* Un cran plus sombre que le charcoal (#50608C) : les textes de carte sont
       sur bleu glace, pas sur blanc — le contraste de calai (gris #4A4552 sur
       blanc) demande cet ajustement. Teinte propre au site. */
    --muted: #3f4d78;
    --ink-tertiary: var(--upn-encre-3);
    --accent: var(--upn-sapphire);
    --border: var(--upn-piste);
    --border-soft: var(--upn-ice);
    --on-deep: var(--upn-sur-profond);

    /* --- Familles ---------------------------------------------------------- */
    /* Une seule famille : le token d'affichage reste pour la graisse et
       l'interlettrage des titres, pas pour une autre police. */
    --font-display: var(--font-ui);
    --font-system: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --font-ui: "Inter", var(--font-system);

    /* --- Échelle typographique (base 17 px = le corps de l'app) ------------
       Recalée sur calai.app, MESURÉE dans le navigateur (styles calculés, 1280
       et 390) : h1 52, titres de section 48 (36 sur mobile), titres de carte
       20, corps 16–18, petit texte 14, et un h1 qui ne rétrécit PAS sur mobile.
       Nos titres de section étaient à 38 et 26, nos titres de carte à 17, le
       h1 tombait à 40 sur mobile : la hiérarchie était écrasée sous le hero.
       Tout en Inter depuis le 2026-09-10 : le h1 est revenu sur la référence
       (44–52), plus de compensation d'œil. */
    --text-xs: 0.875rem;     /* 14 — petit : pied de page, pastilles, kicker */
    --text-sm: 0.9375rem;    /* 15 — texte de carte (calai 14 sur blanc ; +1 sur glace) */
    --text-base: 1rem;       /* 16 — corps, navigation, boutons */
    --text-lg: 1.125rem;     /* 18 — accroche */
    --text-md: 1.25rem;      /* 20 — h3 de carte */
    --text-xl: clamp(1.875rem, 2.8vw, 2.25rem);   /* 30–36 — h2 de sous-section */
    --text-2xl: clamp(2.25rem, 4vw, 3rem);        /* 36–48 — h2 de section */
    --text-3xl: 3.25rem;                          /* 52 — h1, mobile COMPRIS (calai) */

    /* Interlignes par rôle, copiés de calai (styles calculés, 2026-09-11). */
    --lh-h1: 1.25;
    --lh-h2: 1.15;
    --lh-sub: 1.2;
    --lh-h3: 1.4;
    --lh-lead: 1.56;
    --lh-body: 1.5;
    --lh-card: 1.47;
    --lh-small: 1.43;
    /* Sous un h2 de section : 64 (calai), 48 sur mobile. */
    --gap-title: 64px;

    /* --- Espacement (base 4, miroir DesignSystem.Spacing) ------------------ */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    --space-7: 48px;
    --space-8: 80px;

    /* Aurore claire — LE ciel de la marque, repris tel quel de
       brand-extraction/variables.css. Le guide le désigne comme « parfait en
       hero de site ». Non employé pour l'instant : notre hero est sombre. */
    --upn-aurora-light:
        radial-gradient(ellipse 460px 460px at 50% -6%, rgba(34, 71, 194, 0.26), transparent),
        linear-gradient(to bottom,
            rgba(176, 195, 240, 0.55) 0%, rgba(176, 195, 240, 0) 34%,
            rgba(163, 185, 233, 0) 62%, rgba(163, 185, 233, 0.50) 100%);

    /* Grain — bruit fractal généré en SVG, embarqué en data URI (aucune requête,
       aucune image à maintenir). Un aplat de couleur pure se lit comme un
       remplissage CSS ; le même aplat grainé se lit comme une surface. C'est
       toute la différence entre « plat » et « fin ». Posé très bas en opacité,
       il ne doit jamais devenir visible en tant que texture. */
    --grain: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='140' height='140' filter='url%28%23n%29' opacity='0.45'/></svg>");

    /* --- Rayons (miroir DesignSystem.Radius) ------------------------------- */
    --radius-sm: 10px;
    --radius: 16px;
    --radius-lg: 24px;

    /* Ombres de marque (guide §4 / variables.css) : douces et BLEUTÉES, jamais
       noires dures. Je les avais supprimées par principe anti-« kit SaaS » ; la
       charte les revendique — c'est ce qui manquait de finesse aux cartes. */
    --shadow-glass: 0 6px 20px rgba(34, 71, 194, 0.12);
    --shadow-relief: 0 3px 7px rgba(22, 48, 143, 0.10);
    --radius-pill: 999px;

    /* --- Layout ------------------------------------------------------------ */
    --max: 68ch;         /* colonne de lecture — sous 80 caractères */
    --max-wide: 1180px;
    --gutter: clamp(24px, 6vw, 64px);
}

* {
    box-sizing: border-box;
}

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

body {
    margin: 0;
    font-family: var(--font-ui);
    font-size: var(--text-base);
    color: var(--ink);
    background: var(--surface);
    line-height: var(--lh-body);
}

/* Même cadre que le chrome : la colonne de lecture se cale sur le BORD GAUCHE
   de la marque et du pied de page, au lieu d'être centrée dans la fenêtre
   (le titre démarrait 220 px plus à droite que le logo). La longueur de ligne
   reste bornée par les `max-width` posés sur le texte lui-même, ci-dessous. */
.container {
    max-width: var(--max-wide);
    margin: 0 auto;
    padding-inline: var(--gutter);
}

.site-page p,
.site-page ul,
.site-page ol,
.site-page blockquote {
    max-width: var(--max);
}

.container-wide {
    max-width: var(--max-wide);
    margin: 0 auto;
    padding-inline: var(--gutter);
}

a:focus-visible,
button:focus-visible,
summary:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
    border-radius: var(--space-1);
}

.skip-link {
    position: absolute;
    inset-inline-start: -9999px;
    top: var(--space-2);
    z-index: 10;
    background: var(--deep);
    color: var(--on-deep);
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-sm);
    text-decoration: none;
    font-weight: 600;
}

.skip-link:focus {
    inset-inline-start: var(--space-2);
}

/* ============================================================================
   En-tête. Sur l'ACCUEIL il se pose dans le ciel du premier mouvement :
   transparent, texte clair. Partout ailleurs (blog, pages) le fond est blanc,
   donc il redevient opaque et sombre — sans quoi il serait blanc sur blanc.
   ========================================================================== */

/* La barre doit se LIRE comme une bande, pas se dissoudre dans le ciel : fond
   dense, filet bas, et elle reste collée en haut au défilement. Sans cela il
   n'y avait aucune démarcation entre la navigation et le hero. */
.site-header {
    /* FIXE et hors du flux (PO, 2026-09-11 : « translucide avec du blur ») :
       la page passe DESSOUS, c'est ce qui donne quelque chose à flouter. Les
       pages compensent avec padding-top: var(--header-h) (hero et .site-main).
       Fond à 50 % d'opacité + flou d'arrière-plan. */
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 20;
    border-bottom: 1px solid var(--border);
    background: color-mix(in srgb, var(--surface) 50%, transparent);
    -webkit-backdrop-filter: saturate(1.4) blur(16px);
    backdrop-filter: saturate(1.4) blur(16px);
}

/* Sur l'accueil la barre flotte sur le hero nuit : nuit à 50 %, même flou. */
.is-landing .site-header {
    background: rgba(6, 16, 46, 0.5);
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

/* Hauteur mesurée de la barre (logo 68 px + padding) : sert d'offset aux pages. */
:root {
    --header-h: 117px;
}

.site-main {
    padding-top: var(--header-h);
}

/* Sur l'accueil, c'est le hero qui absorbe l'offset pour que son fond nuit
   monte jusqu'au bord de l'écran, sous la barre. */
.is-landing .site-main {
    padding-top: 0;
}

/* En prévisualisation la barre revient dans le flux (le bandeau est au-dessus). */
.is-preview .site-header {
    position: sticky;
}

.is-preview .site-main {
    padding-top: 0;
}

/* Repli sans color-mix : fond opaque plutôt qu'une barre transparente. */
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
    .site-header { background: var(--surface); }
}

/* En prévisualisation, l'en-tête reste dans le flux : superposé, il masquerait
   le bandeau « PRÉVISUALISATION ». */
.is-preview .site-header {
    position: static;
}


.site-header__inner {
    display: flex;
    align-items: center;
    gap: var(--space-5);
    padding-block: var(--space-5);
    flex-wrap: wrap;
}

.site-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--ink);
    text-decoration: none;
    font-family: var(--font-display);
    font-weight: 700;
    /* Taille FIXE : le nom dans l'en-tête n'est pas un titre de section, il
       ne doit pas suivre l'échelle d'affichage. */
    font-size: 1.625rem;
    letter-spacing: 0;
}

.is-landing .site-brand {
    color: var(--surface);
}

.site-brand__mark {
    width: 68px;
    height: 68px;
    flex: none;
}

/* Le fichier logo est teinté Night : sur le ciel de l'accueil il disparaîtrait.
   Le filtre le repasse en blanc plein — un seul fichier à maintenir. */
.is-landing .site-brand__mark {
    filter: brightness(0) invert(1);
}

.site-nav {
    margin-inline-start: auto;
    display: flex;
    align-items: center;
    gap: var(--space-5);
}

/* Langue + CTA groupés à l'extrémité : la barre a un début (la marque), un
   milieu (la navigation) et une fin (l'action) — c'est ce qui lui donne corps. */
.site-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.btn--sm {
    /* calai, bouton secondaire : 14 / 500, 8 × 16 → 38 px. */
    padding: 8px 16px;
    font-size: var(--text-xs);
    line-height: var(--lh-small);
}

.site-nav a {
    color: var(--muted);
    text-decoration: none;
    font-weight: 500;
    font-size: var(--text-base);
}

.is-landing .site-nav a {
    color: var(--on-deep);
}

.site-nav a:hover {
    text-decoration: underline;
}

/* Sélecteur de langue — repli natif <details> : le site vise seize locales,
   une pastille par langue déborde dès la cinquième. Aucun JS. */
.site-lang {
    position: relative;
}

.site-lang__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    list-style: none;
    color: var(--ink);
    font-size: var(--text-sm);
    font-weight: 500;
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
}

.is-landing .site-lang__toggle {
    color: var(--on-deep);
    border-color: rgba(255, 255, 255, 0.35);
}

.is-landing .site-lang[open] .site-lang__toggle {
    background: rgba(255, 255, 255, 0.14);
}

.site-lang__toggle::-webkit-details-marker {
    display: none;
}

.site-lang__toggle::after {
    content: "";
    width: 0.4em;
    height: 0.4em;
    border-inline-end: 1.5px solid currentColor;
    border-block-end: 1.5px solid currentColor;
    transform: rotate(45deg) translate(-0.1em, -0.1em);
}

.site-lang[open] .site-lang__toggle {
    background: var(--bg);
}

.site-lang__menu {
    position: absolute;
    inset-inline-end: 0;
    top: calc(100% + var(--space-2));
    min-width: 190px;
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-2);
    box-shadow: 0 12px 32px rgba(11, 26, 77, 0.24);
}

.site-lang__item {
    color: var(--ink);
    text-decoration: none;
    font-size: var(--text-base);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
}

.site-lang__item:hover {
    background: var(--bg);
}

.site-lang__item.is-active {
    font-weight: 700;
    color: var(--accent);
}

/* ============================================================================
   Typographie
   ========================================================================== */

h1,
h2,
h3 {
    font-family: var(--font-display);
    font-weight: 700;
    line-height: var(--lh-h2);
    letter-spacing: 0;   /* calai : 0 partout, sauf le kicker en capitales */
    color: var(--ink);
    text-wrap: balance;
}

h1 {
    font-size: var(--text-2xl);
    margin: 0 0 var(--space-5);
}

h2 {
    font-size: var(--text-xl);
    margin: var(--space-7) 0 var(--space-4);
    max-width: 20ch;
}

h3 {
    font-family: var(--font-ui);
    font-weight: 600;
    font-size: var(--text-md);
    line-height: var(--lh-h3);
    letter-spacing: 0;
    margin: 0 0 var(--space-4);
}

p {
    margin: 0 0 var(--space-4);
    max-width: var(--max);
}

a {
    color: var(--accent);
    text-underline-offset: 3px;
}

img {
    max-width: 100%;
    height: auto;
}

figure {
    margin: var(--space-6) 0;
}

figcaption {
    color: var(--muted);
    font-size: var(--text-sm);
    margin-top: var(--space-2);
}

blockquote {
    margin: var(--space-6) 0;
    padding-inline-start: var(--space-5);
    border-inline-start: 2px solid var(--accent);
    font-family: var(--font-display);
    font-size: var(--text-lg);
    color: var(--ink);
}

blockquote cite {
    display: block;
    font-family: var(--font-ui);
    font-size: var(--text-sm);
    margin-top: var(--space-3);
    font-style: normal;
    color: var(--muted);
}

ul {
    padding-inline-start: 1.2em;
}

li {
    margin-bottom: var(--space-2);
}

hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin: var(--space-7) 0;
}

.site-page {
    padding-block: var(--space-8);
}

.site-page > h1,
.page__header h1 {
    font-size: var(--text-2xl);
    max-width: 20ch;
}

.site-page h3 {
    max-width: 32ch;
}

/* ============================================================================
   Boutons — un seul geste : plein saphir, angles francs sauf la pilule.
   Pas d'ombre portée générique, pas de soulèvement au survol.
   ========================================================================== */

.btn {
    display: inline-block;
    /* Dégradé primaire de la marque — variables.css `--upn-gradient-primary`. */
    background: linear-gradient(135deg, var(--upn-night), var(--upn-sapphire));
    color: var(--surface);
    text-decoration: none;
    /* calai : 16 / 500 / 1,5, 12 × 24 → 50 px de haut. */
    padding: 12px 24px;
    border-radius: var(--radius-pill);
    font-family: var(--font-ui);
    font-weight: 500;
    font-size: var(--text-base);
    line-height: var(--lh-body);
    transition: background-color 0.15s ease;
}

.btn:hover {
    background: var(--upn-night);
}

@media (prefers-reduced-motion: reduce) {
    * {
        transition: none !important;
        animation: none !important;
    }
}

/* ============================================================================
   Blocs hors landing
   ========================================================================== */

.block {
    margin: 0 0 var(--space-5);
}

.block-cta {
    margin: var(--space-6) 0;
}

.hero-inner {
    display: flex;
    gap: var(--space-7);
    align-items: center;
    flex-wrap: wrap;
}

.hero-text {
    flex: 1 1 340px;
}

.hero-subtitle {
    font-size: var(--text-lg);
    color: var(--muted);
}

.hero-cta {
    margin: var(--space-6) 0 var(--space-5);
}

.hero-badges {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    padding: 0;
    margin: 0;
}

.hero-badges li {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--muted);
}

.block--stat {
    padding-block: var(--space-6);
}

/* « Chiffres : monospacedDigit sur tout ce qui compte » (guide de marque §3). */
.stat-value,
.irl-value,
.year-note,
.pricing-price {
    font-variant-numeric: tabular-nums;
}

.stat-value {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0;
    color: var(--accent);
    margin: 0;
}

.stat-label {
    font-weight: 600;
    font-size: var(--text-lg);
    margin: var(--space-3) 0 0;
}

.stat-note {
    color: var(--muted);
    font-size: var(--text-sm);
    margin: var(--space-2) 0 0;
}

/* --- Grilles de cartes : un REGISTRE réglé, pas un jeu de cartes ---------
   Les filets encodent la séparation ; ni bordure complète, ni ombre, ni coin
   arrondi, ni survol animé — c'est ce quatuor qui produit l'effet « kit ». */

/* DEUX colonnes fixes, pas d'auto-fit : les grilles du site comptent 2 ou 4
   entrées, et l'auto-fit les cassait en 3+1 — une orpheline sur sa ligne.
   Deux colonnes donnent 2×1 et 2×2, toujours pleines. */
.feature-grid {
    display: grid;
}

/* UNE seule forme de carte, partagée avec les témoignages. La mise en page ne
   dépend pas de la présence d'une icône (le champ est facultatif). */
.feature-card,
.testimonial {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
    box-shadow: var(--shadow-relief);
}

.feature-card p {
    margin: 0;
    color: var(--muted);
    font-size: var(--text-sm);
    line-height: var(--lh-card);
}

.feature-icon {
    display: block;
    font-size: var(--text-lg);
    line-height: 1;
    margin-bottom: var(--space-3);
}

/* --- Témoignages : des citations, pas des cartes ------------------------- */

.testimonials {
    display: grid;
}

/* Colonne flex + `margin-top: auto` : les attributions se calent sur une même
   ligne de base quelle que soit la longueur de la citation. */
.testimonial {
    margin: 0;
    display: flex;
    flex-direction: column;
}

/* Dans une carte, le filet vertical de citation fait doublon. */
.testimonial blockquote {
    border: 0;
    padding-inline-start: 0;
    font-size: var(--text-base);
}

.testimonial blockquote {
    margin: 0 0 var(--space-4);
}

/* UNE seule règle : deux blocs séparés se neutralisaient (le `margin: 0` du
   second effaçait le `margin-top: auto` du premier, à spécificité égale). */
.testimonial figcaption {
    font-family: var(--font-ui);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink);
    margin: auto 0 0;
}

/* Garde d'honnêteté : secondaire par la graisse, jamais par un gris trop pâle
   (--ink-tertiary tombe sous le seuil AA) — la mention doit rester lisible. */
.testimonial-detail {
    font-weight: 400;
    color: var(--muted);
}

/* --- FAQ ----------------------------------------------------------------- */

.faq {
    margin-top: var(--space-6);
}

.faq-item {
    border-top: 1px solid var(--border);
}

.faq-item summary {
    cursor: pointer;
    font-weight: 600;
    padding-block: var(--space-4);
}

.faq-item p {
    margin: 0 0 var(--space-4);
    color: var(--muted);
}

/* --- Galerie ------------------------------------------------------------- */

.gallery {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-4);
    margin-top: var(--space-6);
}

.gallery-item {
    margin: 0;
}

.gallery-item img {
    width: 100%;
    border-radius: var(--radius);
}

.gallery-item figcaption {
    font-size: var(--text-xs);
}

/* --- Gain IRL ------------------------------------------------------------ */

/* Gain IRL : texte à gauche, planche à droite — même partition que l'année. */
.irl-lead {
    color: var(--muted);
}

.irl-value {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 700;
    letter-spacing: 0;
    color: var(--accent);
    margin: var(--space-4) 0 var(--space-2);
}

.irl-caption {
    color: var(--muted);
    font-size: var(--text-sm);
}

.irl-cta {
    margin-top: var(--space-6);
}

/* --- Tarifs -------------------------------------------------------------- */

.pricing {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-5);
    margin-top: var(--space-6);
}

.pricing-card {
    border-top: 2px solid var(--border);
    padding-block: var(--space-5);
}

/* Plan mis en avant : l'OR, seul emploi sur le site (§4 « or rare »). */
.pricing-card.is-featured {
    border-top-color: var(--upn-gold);
}

.pricing-badge {
    display: inline-block;
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--deep);
    background: var(--upn-gold);
    border-radius: var(--radius-pill);
    padding: var(--space-1) var(--space-3);
    margin-bottom: var(--space-2);
}

.pricing-price {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 700;
    margin: var(--space-2) 0;
}

.pricing-detail {
    color: var(--muted);
    font-size: var(--text-sm);
    margin: 0;
}

.pricing-note {
    color: var(--muted);
    font-size: var(--text-sm);
    margin-top: var(--space-5);
}

/* ============================================================================
   L'ANNÉE EN JOURS — le geste unique de la page.
   Reprise de YearGrid.swift : 1 case = 1 jour de 16 h éveillées.
   ========================================================================== */

.year-text h2 {
    margin-top: 0;
}

.year-lead {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    line-height: 1.5;
}

.year-legend {
    list-style: none;
    padding: 0;
    margin: var(--space-6) 0 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-5);
}

.year-legend__item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font-size: var(--text-sm);
}

.year-legend__item::before {
    content: "";
    width: 0.85em;
    height: 0.85em;
    border-radius: 3px;
    background: var(--year-work);
}

.year-legend__item.is-screen::before { background: var(--year-screen); }
.year-legend__item.is-reclaimed::before { background: var(--year-reclaimed); }
.year-legend__item.is-free::before { background: var(--year-free); }
.year-legend__item.is-work::before { background: var(--year-work); }

.year-note {
    margin: var(--space-5) 0 0;
    font-size: var(--text-sm);
}

/* `width: 100%` est INDISPENSABLE, pas décoratif : `.year-grid` est un élément
   de grille. Le caler à droite par une marge auto (ou par `justify-self` seul)
   supprime l'étirement et le dimensionne sur son contenu — or dix-huit colonnes
   `1fr` de cases vides ont une largeur intrinsèque nulle, donc largeur 0, donc
   `aspect-ratio` → hauteur 0, et la figure disparaît sans erreur. Une largeur
   définie d'abord, l'alignement ensuite. */
.year-grid {
    display: grid;
    grid-template-columns: repeat(18, minmax(0, 1fr));
    gap: clamp(2px, 0.3vw, 3px);
    width: 100%;
    max-width: 430px;
    justify-self: end;
}

/* La figure est désormais sur fond CLAIR, comme dans l'app : la palette
   littérale de YearGrid.swift y retrouve son rôle d'origine (le gris du
   « travail » recule au lieu d'avancer). Plus aucune adaptation n'est
   nécessaire — c'est un gain de fidélité, pas une concession. */

.year-cell {
    aspect-ratio: 1;
    border-radius: 2px;
    background: var(--year-work);
}

.year-cell.is-screen { background: var(--year-screen); }
.year-cell.is-reclaimed { background: var(--year-reclaimed); }
.year-cell.is-free { background: var(--year-free); }

/* ============================================================================
   Landing — DEUX fonds, TROIS dispositions.

   Réécrit par SIMPLIFICATION, après analyse chiffrée de calai.app (référence
   du projet). Cette section portait une « descente » de trois dégradés
   enchaînés, des modes de fusion `screen`/`multiply` et des masques
   elliptiques. La référence, elle, tient sur DEUX aplats, des images posées à
   plat, une seule forme de carte et trois dispositions répétées — sa propreté
   vient de la RÉPÉTITION, pas de l'effet. La sophistication compensait ici
   l'absence d'images produit ; elle ne la remplaçait pas.

   Les trois seules dispositions admises :
     A. texte à gauche / média à droite   — hero, année en jours, gain IRL
     B. titre centré + rangée de cartes   — outils, vie privée, science, avis
     C. titre centré + colonne étroite    — accroche, clôture
   ========================================================================== */

.landing .block {
    margin: 0;
    /* ⚠️ Ce padding s'applique EN HAUT ET EN BAS : entre deux sections l'espace
       réel est donc le DOUBLE. À 115 px il produisait 230 px de vide entre
       chaque bande — le double de la fourchette de la référence (96-176 px au
       total). Ramené à ~70, il donne ~140 px entre sections. Mesuré par
       tools/grid-audit.py, section RYTHME VERTICAL. */
    /* calai : 96 à 176 par section. Bandes claires à 96 (64 sur mobile). */
    padding-block: clamp(64px, 7.5vw, 96px);
    background-color: var(--bg);
    background-image: var(--grain);
    background-blend-mode: soft-light;
}

/* ---------------------------------------------------------------------------
   GRILLE DE 12 COLONNES — conservée : c'est elle qui empêche l'« escalier »
   (chaque section découpant la largeur à sa façon). Voir tools/grid-audit.py.
   ------------------------------------------------------------------------ */
.landing .block-inner {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--space-5);
    max-width: var(--max-wide);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

.landing .block-inner > * {
    grid-column: 1 / span 7;
}

/* Disposition C — titre de section CENTRÉ sur toute la largeur, puis colonne
   de lecture étroite et centrée sous lui. */
.landing .block-inner > h2 {
    grid-column: 1 / -1;
    justify-self: center;
    text-align: center;
    /* 26ch : « Quatre angles pour réduire ton temps d'écran. » tient sur deux
       lignes à 48 px ; à 18ch il en prenait trois. */
    max-width: 26ch;
    font-size: var(--text-2xl);
    margin-bottom: var(--gap-title);
}

.landing .block-inner > :first-child { margin-top: 0; }
.landing .block-inner > :last-child { margin-bottom: 0; }

/* Les conteneurs internes reprennent le MÊME gabarit de colonnes : leurs
   enfants s'alignent sur la grille du parent, sans `subgrid`. */
.landing .hero-inner,
.landing .year,
.landing .irl,
.landing .feature-grid,
.landing .testimonials {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--space-5);
    gap: var(--space-5);
    align-items: center;
}

/* Disposition A — texte colonnes 1→6, média colonnes 8→12. Toujours. */
.landing .hero-text,
.landing .irl-inner,
.landing .year-text {
    grid-column: 1 / span 6;
}

.landing .hero-media,
.landing .year-grid,
.landing .irl-media {
    grid-column: 8 / span 5;
}

/* Gain IRL SANS média (Back IRL, décision du PO) : le texte seul sur les
   colonnes 1→6 laissait la moitié droite de la bande vide. Sans image, le
   bloc devient une déclaration centrée sur 6 colonnes, comme un titre de
   section — la bande garde son poids sans rien inventer à droite. */
.landing .irl:not(.has-media) .irl-inner {
    grid-column: 4 / span 6;
    text-align: center;
}

/* Dans une déclaration centrée, la « valeur » est une seconde ligne forte,
   pas un second titre : un cran SOUS le h2 (36 → 30–36), sinon la hiérarchie
   s'inverse (« Sans rouge. Sans sermon. » sortait plus gros que son titre). */
.landing .irl:not(.has-media) .irl-value {
    font-size: var(--text-xl);
    max-width: 28ch;
}

.landing .irl:not(.has-media) h2,
.landing .irl:not(.has-media) .irl-value,
.landing .irl:not(.has-media) .irl-lead {
    /* h2 et value portent un max-width (20ch) : sans margin auto, la boîte
       reste à gauche de la colonne et le titre paraît décalé. */
    margin-inline: auto;
}

@media (max-width: 800px) {
    .landing .irl:not(.has-media) .irl-inner {
        grid-column: 1 / -1;
    }
}

/* Disposition B — cartes de 4 colonnes : trois par rangée, toujours pleines. */
.landing .feature-grid,
.landing .testimonials {
    align-items: stretch;
    row-gap: var(--space-5);
}

.landing .feature-card,
.landing .testimonial {
    grid-column: span 4;
}

/* La largeur d'une carte dépend du NOMBRE de cartes, sinon une rangée de
   quatre laisse une orpheline seule sur la ligne suivante (3 + 1).
   Défaut : trois par rangée. Deux entrées ⇒ moitié/moitié. Quatre ⇒ 2×2. */
.landing .feature-grid:not(.has-device):has(.feature-card:nth-child(2):last-child) .feature-card,
.landing .feature-grid:not(.has-device):has(.feature-card:nth-child(4)) .feature-card {
    grid-column: span 6;
}

/* Disposition « téléphone + liste » (référence : « What does it include? »).
   Le châssis occupe les colonnes 1→5 et s'étend sur les rangées des cartes ;
   les cartes s'empilent en colonnes 7→12, une par rangée. `span 4` couvre le
   maximum de cartes d'un registre ; des rangées implicites vides sont sans
   effet. */
.landing .feature-grid.has-device {
    align-items: center;
    row-gap: var(--space-4);
}

.landing .feature-grid.has-device .feature-device {
    grid-column: 1 / span 5;
    grid-row: 1 / span 4;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    /* Le cadre PNG porte déjà ~5,5 % de vide sous le corps : un petit écart suffit. */
    gap: var(--space-2);
}

/* Points de carrousel sous le téléphone (site.js) : un par écran. Cible
   tactile de 28 px, pastille de 8 ; l'active porte le saphir, comme la carte
   active, et s'allonge — l'état se lit sans couleur. */
.feature-dots {
    display: flex;
    gap: var(--space-1);
}

.feature-dot {
    width: 28px;
    height: 28px;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
    display: grid;
    place-items: center;
}

.feature-dot::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: var(--charcoal, #50608c);
    opacity: 0.35;
    transition: width 0.2s ease, opacity 0.2s ease, background-color 0.2s ease;
}

.feature-dot:hover::before {
    opacity: 0.7;
}

.feature-dot.is-active::before {
    width: 22px;
    background: var(--accent);
    opacity: 1;
}

.feature-dot:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
    border-radius: 999px;
}

.landing .feature-grid.has-device .feature-card {
    grid-column: 7 / span 6;
}

/* Carte-bouton : mêmes surface, filet, rayon et ombre que la carte ; on ne
   retire que ce qu'un <button> ajoute (fonte, alignement, largeur). L'état
   actif porte le saphir en filet — l'unique bleu d'action de la charte. */
.feature-card--switch {
    font: inherit;
    text-align: start;
    width: 100%;
    color: inherit;
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.feature-card--switch:hover {
    border-color: var(--upn-turquoise);
}

.feature-card--switch.is-active {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent), var(--shadow-relief);
}

.feature-card--switch:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
}

/* La permutation d'écran se fait en fondu court ; respecté si l'utilisateur
   demande moins d'animation (la règle globale annule toute transition). */
.feature-grid.has-device .device__screen {
    transition: opacity 0.2s ease;
}

/* Une capture dans la section gain IRL : centrée dans sa colonne comme le hero. */
.landing .irl-media:has(.device) {
    display: flex;
    justify-content: center;
}

/* Une section irl_gain qui montre l'app EN MODE SOMBRE se pose sur le fond
   CLAIR (la référence fait de même) : le téléphone sombre y ressort, et deux
   bandes nuit consécutives — mode sombre puis Back IRL — ne fusionnent plus en
   un seul bloc. Ciblé sur le châssis, pas sur la clé de bloc. */
.landing .block--irl_gain:has(.device) {
    background-color: var(--bg);
    background-image: var(--grain);
    color: var(--ink);
    padding-block: clamp(64px, 7.5vw, 96px);
}

.landing .block--irl_gain:has(.device) h2 {
    color: var(--ink);
}

.landing .block--irl_gain:has(.device) .irl-lead {
    color: var(--muted);
}

/* À la charnière, le hero garde ses deux colonnes mais le texte prend 7/12 :
   à 820 px la colonne 6/12 ne fait que 341 px et le paragraphe du hero s'y
   étalait sur cinq lignes. À 7/12 elle fait 402 px. Le bord GAUCHE reste
   colonne 1 : l'alignement inter-sections tient.
   (Les TROIS pastilles, elles, passent encore en 2 + 1 à cette largeur — c'est
   un renvoi à la ligne naturel d'un flux de trois éléments, pas le défaut des
   deux pastilles d'autrefois.) */
@media (max-width: 1000px) {
    .landing .hero-text {
        grid-column: 1 / span 7;
    }
}

@media (max-width: 900px) {
    .landing .feature-card,
    .landing .testimonial {
        grid-column: span 6;
    }
}

/* Seuil d'empilement à 800 px, PAS 720 : en dessous, la colonne de texte (6/12)
   tombe sous 322 px et les pastilles du hero débordent même compactées.
   Mesuré aux largeurs 760/820/900/1000/1280, pas estimé. */
@media (max-width: 800px) {
    .landing .block-inner > *,
    .landing .block-inner > h2,
    .landing .hero-text,
    .landing .irl-inner,
    .landing .year-text,
    .landing .hero-media,
    .landing .year-grid,
    .landing .irl-media,
    .landing .feature-card,
    .landing .testimonial {
        grid-column: 1 / -1;
    }

    .landing .feature-grid.has-device .feature-device {
        grid-column: 1 / -1;
        grid-row: auto;
    }

    /* Plus spécifique que `.landing .feature-card` ci-dessus : sans cette
       ligne la règle bureau (7 → 12) gagnait et les cartes restaient sur la
       moitié droite sous le téléphone. */
    .landing .feature-grid.has-device .feature-card {
        grid-column: 1 / -1;
    }

}

/* --- Fond nº 1 : les bandes sombres --------------------------------------
   Aplats, plus de dégradé enchaîné : le hero ouvre, le gain IRL referme. */

.landing .block--hero,
.landing .block--irl_gain {
    background-color: var(--upn-nuit);
    background-image:
        radial-gradient(90% 70% at 12% -20%, rgba(86, 124, 214, 0.13), transparent 58%),
        var(--grain);
    background-blend-mode: normal, soft-light;
    color: var(--on-deep);
    /* Les bandes COLORÉES n'ont pas de « vide entre sections » : leur padding est
       interne au fond, il ne s'additionne visuellement à rien. Elles restent donc
       plus généreuses que les bandes claires, qui se suivent et se cumulent. */
    /* Bandes nuit : 128 (calai, témoignages 160, feature 256 ramenée à 128). */
    padding-block: clamp(80px, 10vw, 128px);
}

.is-landing:not(.is-preview) .block--hero {
    padding-top: calc(clamp(80px, 10vw, 128px) + var(--header-h));
}

.landing .block--hero h1,
.landing .block--irl_gain h2,
.landing .block--irl_gain .irl-value {
    color: var(--surface);
}

.landing .irl h2 {
    line-height: var(--lh-sub);
}

.landing .block--hero .hero-subtitle,
.landing .block--irl_gain .irl-lead,
.landing .block--irl_gain .irl-caption {
    color: var(--upn-ice);
}

.landing .block--hero .btn,
.landing .block--irl_gain .btn {
    background: var(--surface);
    color: var(--deep);
}

.landing .block--hero .btn:hover,
.landing .block--irl_gain .btn:hover {
    background: var(--upn-ice);
}

.landing .block--hero a:focus-visible,
.landing .block--irl_gain a:focus-visible {
    outline-color: var(--surface);
}

/* Hero : 52 / 1,25 / 700, interlettrage 0 — les valeurs calculées de calai. */
.landing .hero-text h1 {
    font-size: var(--text-3xl);
    line-height: var(--lh-h1);
    letter-spacing: 0;
    margin-bottom: var(--space-4);
}

.landing .block--hero .hero-subtitle {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    margin-bottom: var(--space-5);
    line-height: var(--lh-lead);
}

/* Paragraphe d'appui : c'est lui qui donne au hero sa densité — auparavant la
   bande faisait 560 px pour 260 px de contenu. Copy assemblée à partir de
   formulations déjà présentes dans l'app, aucune affirmation nouvelle. */
.landing .block--hero .hero-lead {
    font-size: var(--text-base);
    color: var(--upn-ice);
    margin-bottom: var(--space-6);
    line-height: var(--lh-body);
}

.landing .block--hero .hero-cta {
    margin: 0;
}

.landing .block--hero .hero-badges li {
    color: var(--on-deep);
    /* Étiquettes, pas du texte de lecture : taille FIXE, sinon les trois
       pastilles ne tiennent plus sur une ligne dans la colonne du hero. */
    font-size: var(--text-xs);
    font-weight: 500;
    padding: var(--space-2) var(--space-4);
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: var(--radius-pill);
    line-height: var(--lh-small);
}

.landing .block--irl_gain .irl-value {
    font-size: var(--text-2xl);
    line-height: var(--lh-h2);
}

/* --- Images : posées à plat -----------------------------------------------
   Plus de `mix-blend-mode` ni de masque elliptique. Un coin arrondi, comme la
   référence. Les planches sont sur fond noir : sur la bande nuit elles se
   fondent d'elles-mêmes, sans artifice. */
/* Les planches sont sur fond noir : sans filet elles font un trou dans la
   bande marine. Une hairline claire les pose au lieu de les laisser flotter. */
/* Enfant DIRECT : les images du châssis (.device__screen / .device__frame) sont
   positionnées par le châssis, cette règle ne doit pas les toucher. */
.landing .irl-media > img {
    display: block;
    width: 100%;
    border-radius: var(--radius);
    border: 1px solid rgba(255, 255, 255, 0.12);
}

/* --- Le châssis d'appareil ----------------------------------------------
   C'est le cadre PNG de la marque (upchin-ios, brand-extraction/assets/mockups/
   iphone-frame.png, guide §8 bis) : 887 × 1774, écran et extérieur TRANSPARENTS.
   La capture est posée DERRIÈRE dans l'ouverture, le cadre vient après dans le
   DOM et peint par-dessus, sans z-index. L'ouverture a été MESURÉE dans le
   canal alpha (les cotes du guide étaient fausses de 20 px en haut) :
   x 96, y 114, 693 × 1529 → en % du cadre, seule forme valable puisqu'il est
   fluide. Ses coins sont déjà arrondis (r ≈ 100 px) : aucun border-radius sur
   la capture, ses coins carrés passent sous le châssis. Le cadre dessine sa
   propre Dynamic Island, qui recouvre exactement celle de la capture.
   Le rapport de l'ouverture (0,453) diffère de 1,5 % de celui des captures
   (0,4603) : object-fit: cover rogne une dizaine de pixels en hauteur, invisible. */
.landing .hero-media {
    display: flex;
    justify-content: center;
}

.device {
    /* Piloté par la hauteur. L'écran ne fait que 78 % de la largeur du cadre
       (0,39 × la hauteur), d'où une hauteur plus généreuse que l'ancien châssis
       CSS pour garder une capture lisible. */
    --device-h: clamp(480px, 66vh, 680px);

    position: relative;
    height: var(--device-h);
    aspect-ratio: 887 / 1774;
}

/* L'ombre est portée par un pseudo-élément calé sur la silhouette du corps,
   MESURÉE dans l'alpha (seuil 50 %) : marges 4,57 % haut, 6,99 % côtés,
   5,47 % bas, coins r ≈ 150 px au 887 (19,7 % / 9,4 % de la boîte). Un
   premier réglage à l'œil (boîte plus large, coins plus petits) laissait
   l'ombre dépasser aux quatre coins sur fond clair. Étalement NÉGATIF : la
   forme de l'ombre reste à l'intérieur du corps, elle ne peut plus le
   déborder. PAS de drop-shadow sur le cadre : son ouverture est transparente,
   le flou de l'ombre viendrait baver SUR l'écran. */
.device::before {
    content: "";
    position: absolute;
    inset: 4.6% 7% 5.5% 7%;
    border-radius: 19.7% / 9.4%;
    box-shadow: 0 34px 70px -14px rgba(4, 10, 34, 0.5);
}

.device__frame {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

.device__screen {
    position: absolute;
    left: 10.823%;
    top: 6.426%;
    width: 78.129%;
    height: 86.19%;
    display: block;
    object-fit: cover;
    /* Les coins de l'ouverture sont ronds (r ≈ 100 px au 887) et le coin de sa
       boîte englobante tombe SUR le bord extérieur du corps : sans arrondi, le
       coin carré de la capture dépasse du téléphone. Rayon un peu plus grand
       que l'ouverture, la différence se cache sous le liseré. */
    border-radius: 15% / 6.8%;
}

/* --- Le plan pleine largeur ------------------------------------------------
   La SEULE section qui sort de la grille de 12 colonnes, et le seul endroit du
   site où la couleur apparaît. C'est le pendant émotionnel de la grille de
   l'année : celle-ci prouve le coût, celui-là le fait ressentir.
   L'image porte l'unique touche de couleur de tout le corpus de la marque
   (la robe rouge) — d'où la ligne « Regarde encore. » qui la rime.
   Fond nuit : le plan est sur fond clair, il s'y détache franchement. */
.landing .block--statement {
    padding-block: 0;
    background: var(--upn-nuit);
}

.landing .block--statement .block-inner {
    display: block;
    max-width: none;
    padding-inline: 0;
}

.statement {
    margin: 0;
}

/* Le plan est en 4:3 : servi pleine largeur il faisait 960 px de haut, soit deux
   à trois fois la hauteur des autres sections — une rupture de géométrie, pas un
   effet. Recadré en BANDE cinématographique : la femme en rouge occupe la moitié
   verticale de l'image, un recadrage centré la conserve entière. */
.statement img {
    display: block;
    width: 100%;
    height: clamp(320px, 46vh, 560px);
    object-fit: cover;
    object-position: center;
}

.statement-line {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 700;
    letter-spacing: 0;
    color: var(--surface);
    text-align: center;
    margin: 0;
    padding-block: var(--space-7);
    padding-inline: var(--gutter);
}

/* --- Disposition C : accroche et clôture, colonne étroite centrée ---------
   La COLONNE est centrée dans la page, mais le TEXTE reste aligné à gauche :
   au-delà de deux lignes, un paragraphe centré devient pénible à lire, son
   bord gauche se déplaçant à chaque ligne. Seul le titre est centré. */
.landing .block--markdown .block-inner > * {
    grid-column: 4 / span 6;
}

/* Ce bloc @media DOIT suivre la règle ci-dessus : même spécificité, c'est
   l'ordre qui tranche. Placé dans le bloc ≤ 800 général (plus haut dans le
   fichier), il perdait, et à 390 px les déclarations restaient sur les
   colonnes 4 → 9 : quatorze caractères par ligne. */
@media (max-width: 800px) {
    .landing .block--markdown .block-inner > * {
        grid-column: 1 / -1;
    }
}

.landing .block--markdown .block-inner > h2 {
    grid-column: 1 / -1;
}

.landing .block--markdown p {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    line-height: var(--lh-lead);
    color: var(--muted);
    /* Une déclaration centrée : le paragraphe suit le titre, il ne colle pas à
       gauche d'une colonne centrée. */
    text-align: center;
}

/* La ligne en gras d'une déclaration : encre pleine, un cran plus grande. */
.landing .block--markdown p strong {
    color: var(--ink);
    font-weight: 600;
    font-size: var(--text-md);
}

.landing .block--cta {
    text-align: center;
}

.landing .block--cta .block-inner > * {
    grid-column: 1 / -1;
    /* Le <p> générique porte une mesure (max-width en ch) : centré dans sa
       colonne, pas dans sa boîte, sinon le badge tombe à gauche du centre. */
    max-width: none;
    justify-self: center;
}

.landing .block--markdown + .block--cta {
    padding-top: 0;
}

/* ---- film : bande nuit, texte à gauche, film à droite dans un cadre --------
   Le film du PO (upchin-sequence.mp4, 1280 × 1116, N&B, fondu au noir aux
   deux bouts donc boucle sans raccord) est presque carré : posé à plat dans
   la colonne média, avec le rayon et le liseré des planches illustrées, PAS
   en fond pleine largeur (un cover aurait rogné la scène et couvert l'enfant
   sous le texte). L'affiche est la même image SOUS la vidéo : elle prend le
   relais en mouvement réduit. Disposition A comme le hero. */

.landing .block--film {
    background-color: var(--upn-nuit);
    background-image:
        radial-gradient(90% 70% at 12% -20%, rgba(86, 124, 214, 0.13), transparent 58%),
        var(--grain);
    background-blend-mode: normal, soft-light;
    color: #fff;
    padding-block: clamp(80px, 10vw, 128px);
}

.landing .block--film .film__body {
    grid-column: 1 / span 6;
    align-self: center;
}

.landing .film__media {
    grid-column: 8 / span 5;
    position: relative;
    border-radius: var(--radius-lg);
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: #000;
    /* Rapport du film : la boîte garde sa hauteur avant le chargement. */
    aspect-ratio: 1280 / 1116;
}

.landing .film__poster,
.landing .film__video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.landing .film__video {
    z-index: 1;
}

.landing .block--film h2 {
    margin: 0 0 var(--space-4);
    color: #fff;
    text-align: left;
    max-width: 16ch;
}

.landing .block--film p {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    line-height: var(--lh-lead);
    color: rgba(233, 239, 249, .82);
    max-width: 30ch;
}

.landing .block--film .block-cta {
    margin: var(--space-6) 0 0;
    text-align: left;
    max-width: none;
}

@media (max-width: 800px) {
    .landing .block--film .film__body,
    .landing .film__media {
        grid-column: 1 / -1;
    }

    .landing .film__media {
        margin-top: var(--space-6);
    }
}

/* Dans le hero, le cadre remplit la colonne média (flex centré) et plafonne
   à la largeur qu'aurait le châssis, pour garder le pli identique. */
.landing .hero-media .film__media {
    width: 100%;
    max-width: calc(var(--device-h, 600px) * 1280 / 1116);
}

@media (prefers-reduced-motion: reduce) {
    .landing .film__video {
        display: none;
    }
}

/* --- Badge App Store (partiel _cta) ----------------------------------------
   Artwork officiel d'Apple, inchangé, en SVG : hauteur 50 px dans les bandes
   (la même que le bouton qu'il remplace), 40 px dans l'en-tête — le minimum
   d'Apple. Le ratio du badge est 126.5 / 40. */
.store-badge {
    display: inline-block;
    line-height: 0;
    border-radius: 8px;
}

.store-badge img {
    display: block;
    height: 50px;
    width: auto;
}

.store-badge--sm img {
    height: 40px;
}

.store-badge:hover img {
    opacity: 0.9;
}

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

/* L'en-tête porte le badge (blanc sur l'accueil nuit, noir ailleurs) dans un
   simple span : aucun fond, aucun padding — c'est l'artwork qui fait bouton. */
.site-header__cta {
    display: inline-flex;
    align-items: center;
    padding: 0;
    background: transparent;
}

/* Comparaison A/B de l'essai Inter : `?font=system` → pile système. */
.font-system {
    --font-ui: var(--font-system);
}

/* ============================================================================
   Blocs rendus HORS accueil (pages statiques)

   Toute la géométrie des blocs est scopée `.landing` : hors accueil,
   `.block-inner` est un div nu et `.feature-grid` / `.testimonials` sont des
   grilles SANS colonnes ni gouttière — les cartes s'empilaient collées, leurs
   filets doublés. Aucune page statique n'existe aujourd'hui, mais l'admin peut
   en créer une avec ces blocs : cette base évite qu'elle sorte cassée.
   ========================================================================== */

.site-page .block {
    margin-block: var(--space-7);
}

.site-page .feature-grid,
.site-page .testimonials {
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: var(--space-5);
}

/* ============================================================================
   Blog
   ========================================================================== */

.post-summary {
    padding-block: var(--space-6);
    border-top: 1px solid var(--border);
}

/* Les titres aussi ont besoin d'une mesure : sans borne, un titre d'article
   courait sur toute la largeur du conteneur (~1052 px à 1280). */
.post-summary h2 {
    margin: 0 0 var(--space-2);
    font-size: var(--text-xl);
    max-width: 28ch;
}

.post-summary a {
    text-decoration: none;
}

.post-summary h2 a:hover {
    text-decoration: underline;
}

.post-summary time,
.post time {
    color: var(--muted);
    font-size: var(--text-sm);
}

.post__header {
    margin-bottom: var(--space-6);
}

.post__footer {
    margin-top: var(--space-8);
    padding-top: var(--space-5);
    border-top: 1px solid var(--border);
}

.pagination {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    margin-top: var(--space-7);
}

.pagination__item {
    text-decoration: none;
    font-weight: 600;
    font-size: var(--text-sm);
}

.pagination__item.is-disabled {
    color: var(--ink-tertiary);
}

.pagination__status {
    color: var(--muted);
    font-size: var(--text-sm);
    margin-inline-start: auto;
}

/* ============================================================================
   Bandeau de prévisualisation
   ========================================================================== */

.preview-banner {
    position: relative;
    z-index: 6;
    background: var(--upn-gold);
    color: var(--deep);
    padding: var(--space-2) var(--gutter);
    font-weight: 700;
    font-size: var(--text-sm);
}

/* ============================================================================
   Pied de page
   ========================================================================== */

.site-footer {
    background: var(--upn-nuit);
    color: var(--upn-ice);
    font-size: var(--text-sm);
    padding-block: var(--space-7);
}

.site-footer__inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-5);
    align-items: center;
    justify-content: space-between;
}

.site-footer__brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--surface);
    font-family: var(--font-display);
    font-weight: 700;
    font-size: var(--text-lg);
    text-decoration: none;
}

.site-footer__mark {
    width: 40px;
    height: 40px;
    background: var(--surface);
    border-radius: var(--space-2);
    padding: 3px;
    flex: none;
}

.site-footer__nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-5);
}

.site-footer a {
    color: var(--upn-ice);
}

.site-footer__nav a {
    text-decoration: none;
    font-weight: 600;
}

.site-footer__nav a:hover {
    text-decoration: underline;
}

.site-footer a:focus-visible {
    outline-color: var(--surface);
}

.site-footer__legal {
    margin: var(--space-6) 0 0;
    font-size: var(--text-xs);
    color: var(--upn-ice);
    line-height: var(--lh-small);
}

/* ============================================================================
   Petits écrans
   ========================================================================== */

@media (max-width: 800px) {
    .year-grid {
        max-width: 420px;
        justify-self: start;
    }
}

/* Pastilles compactes DÈS 920 px, pas seulement en mobile. En disposition à
   deux colonnes, la colonne de texte ne fait que 6/12 : à 820 px elle tombe à
   341 px alors que les deux pastilles pleines en demandent 376 — elles
   passaient sur deux lignes à la charnière tablette, alors qu'elles tenaient
   côte à côte en mobile ET en bureau. Le calcul donne le seuil : il faut
   ~890 px de fenêtre pour que la version pleine tienne. Mesuré, pas estimé. */
@media (max-width: 920px) {
    .landing .block--hero .hero-badges li {
        padding: var(--space-1) 10px;
    }
}

@media (max-width: 640px) {
    :root {
        --space-8: 56px;
        --space-7: 36px;
        --gap-title: 48px;
        --header-h: 69px;   /* barre compacte mesurée à 375 : logo 40 px + bouton menu 44 px, le badge a quitté la barre (2026-09-15) */
    }

    .site-header__inner {
        gap: var(--space-3);
    }

    /* TOUR 3 — l'en-tête tenait sur DEUX rangées : il manquait une vingtaine de
       pixels. Marque et actions resserrées, il retombe sur une seule ligne
       (≈ 292 px occupés pour 342 disponibles à 390 px de large). */
    .site-brand__mark {
        width: 40px;
        height: 40px;
    }

    .site-brand {
        font-size: var(--text-base);
        gap: var(--space-2);
    }

    .site-header__actions {
        gap: var(--space-2);
    }


    /* « Blog » sort de la barre (il reste dans le pied de page) et le CTA
       se compacte : l'en-tete retombe sur UNE ligne. */
    .site-nav {
        display: none;
    }

    .site-header__inner {
        padding-block: var(--space-3);
    }

    /* calai mobile : navigation et boutons secondaires en 14 / 500. */
    .site-header__cta,
    .site-nav a,
    .site-lang__toggle {
        font-size: var(--text-xs);
        font-weight: 500;
    }

    .site-header__cta {
        padding: var(--space-2) var(--space-4);
    }

    .site-lang__toggle {
        padding: var(--space-2) var(--space-3);
    }

    .site-nav {
        gap: var(--space-4);
    }

    .landing .block--hero {
        padding-block: var(--space-6) var(--space-7);
    }
}

/* Sous 430 px le MOT était masqué (la barre demandait 354 px pour 342). Depuis
   le menu mobile (2026-09-15) le badge App Store et le sélecteur de langue ont
   quitté la barre : logo + « Upchin » + bouton tiennent (≈ 190 px), le nom
   reste affiché à toutes les largeurs. */


/* ============================================================================
   Badge App Store INERTE (partiel _cta, config site.app_store_live = false)
   Tant que l'app n'est pas publiée, l'artwork reste visible mais n'est pas un
   lien : aucun curseur main, aucun effet de survol. PO 2026-09-15.
   ========================================================================== */

.store-badge--soon {
    cursor: default;
}

.store-badge--soon img {
    opacity: 1;
}

/* ============================================================================
   Menu mobile (partiel _menu) — ≤ 640 px seulement
   Sous ce seuil « Blog » sortait de la barre et le sélecteur de langue se
   disputait la place avec le badge : les deux passent dans ce panneau, le
   badge reste dans la barre. <details> natif, pas de JS obligatoire.
   ========================================================================== */

.site-menu {
    display: none;
    position: static;
}

.site-menu__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    cursor: pointer;
    list-style: none;
    color: var(--ink);
    /* Sans liseré (PO 2026-09-15) : les trois barres suffisent. */
    border: 0;
    border-radius: var(--radius-pill);
}

.site-menu__toggle::-webkit-details-marker {
    display: none;
}

.site-menu[open] .site-menu__toggle {
    background: var(--bg);
}

.is-landing .site-menu__toggle {
    color: var(--on-deep);
}

.is-landing .site-menu[open] .site-menu__toggle {
    background: rgba(255, 255, 255, 0.14);
}

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

/* Trois barres dessinées au pseudo-élément ; ouvertes, la barre du milieu
   s'efface et les deux autres se croisent en X. */
.site-menu__bars,
.site-menu__bars::before,
.site-menu__bars::after {
    display: block;
    width: 20px;
    height: 2px;
    background: currentColor;
    border-radius: 2px;
    transition: transform 0.2s ease, background-color 0.15s ease;
}

.site-menu__bars {
    position: relative;
}

.site-menu__bars::before,
.site-menu__bars::after {
    content: "";
    position: absolute;
    left: 0;
}

.site-menu__bars::before { top: -6px; }
.site-menu__bars::after  { top: 6px; }

.site-menu[open] .site-menu__bars {
    background: transparent;
}

.site-menu[open] .site-menu__bars::before {
    transform: translateY(6px) rotate(45deg);
}

.site-menu[open] .site-menu__bars::after {
    transform: translateY(-6px) rotate(-45deg);
}

/* Le panneau se pose SOUS la barre, sur toute la largeur : l'en-tête est
   fixed, il sert de bloc conteneur (en prévisualisation il repasse en
   relative pour la même raison). */
.site-menu__panel {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    max-height: calc(100vh - var(--header-h));
    overflow-y: auto;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 16px 32px rgba(11, 26, 77, 0.18);
    padding: var(--space-3) var(--space-5) var(--space-5);
}

/* Opaque : à 96 % le titre du hero transparaissait derrière les liens. */
.is-landing .site-menu__panel {
    background: rgb(6, 16, 46);
    border-bottom-color: rgba(255, 255, 255, 0.12);
}

.is-preview .site-header {
    position: relative;
}

.site-menu__nav {
    display: flex;
    flex-direction: column;
}

.site-menu__nav a {
    color: var(--ink);
    text-decoration: none;
    font-weight: 500;
    font-size: var(--text-base);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--border-soft);
}

.is-landing .site-menu__nav a {
    color: var(--on-deep);
    border-bottom-color: rgba(255, 255, 255, 0.12);
}

.site-menu__title {
    margin: var(--space-4) 0 var(--space-2);
    font-size: var(--text-xs);
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
}

.is-landing .site-menu__title {
    color: rgba(242, 245, 253, 0.7);
}

.site-menu__langs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.site-menu__lang {
    color: var(--ink);
    text-decoration: none;
    font-size: var(--text-sm);
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
}

.site-menu__lang.is-active {
    font-weight: 700;
    color: var(--accent);
    border-color: var(--accent);
}

.is-landing .site-menu__lang {
    color: var(--on-deep);
    border-color: rgba(255, 255, 255, 0.35);
}

.is-landing .site-menu__lang.is-active {
    color: var(--upn-azure-bright);
    border-color: var(--upn-azure-bright);
}

/* Le badge App Store ferme le menu (PO : « en bas du menu, sous langues ») ;
   il quitte la barre sous 640 px, où le nom « Upchin » reprend sa place. */
.site-menu__cta {
    margin: var(--space-5) 0 0;
    line-height: 0;
}

@media (max-width: 640px) {
    .site-menu {
        display: block;
    }

    /* Le sélecteur de langue de la barre passe dans le menu ; ce qui reste
       (badge + bouton) se range à droite. */
    .site-header__actions .site-lang,
    .site-header__actions .site-header__cta {
        display: none;
    }

    .site-header__actions {
        margin-inline-start: auto;
    }
}

@media (prefers-reduced-motion: reduce) {
    .site-menu__bars,
    .site-menu__bars::before,
    .site-menu__bars::after {
        transition: none;
    }
}
