/* ==========================================================================
   Nota — design tokens and styles.
   Brand system: Nota blue-teal, midnight ink, and the Québec signal.
   Deviation from Tablix on purpose: squared corners (Tablix uses rounded-xl).
   All colors are tokens — no literal color values below the :root blocks.
   ========================================================================== */

/* ==========================================================================
   Les polices, servies par Nota — jamais par un tiers.
   Inter (texte) et Sora (titres) arrivaient de fonts.googleapis.com : l'adresse
   IP et l'agent de CHAQUE visiteur atteignaient Google avant toute bannière, sur
   un site qui vend des actes notariés (propriétaire, 2026-09-12). Les octets
   vivent désormais dans /fonts, licence OFL jointe (fonts/OFL.txt).
   Ce sont les versions VARIABLES : un seul fichier par tranche couvre toutes
   les graisses. `swap` garde le texte lisible pendant le chargement.
   ========================================================================== */
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('/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; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('/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: 'Sora'; font-style: normal; font-weight: 100 800; font-display: swap;
  src: url('/fonts/sora-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; }
@font-face { font-family: 'Sora'; font-style: normal; font-weight: 100 800; font-display: swap;
  src: url('/fonts/sora-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; }


:root {
  /* Nota blue ramp: institutional cobalt for trust and rigor, with a
     restrained light blue for digital interactions. */
  --nota-blue-50: #ebf1f5;
  --nota-blue-100: #e5eef3;
  --nota-blue-200: #c8dbe3;
  --nota-blue-300: #a7c1cd;
  --nota-blue-400: #78a9bf;
  --nota-blue-500: #407598;
  --nota-blue-600: #386888;
  --nota-blue-700: #386888;
  --nota-blue-800: #274a62;
  --nota-blue-900: #264961;
  --nota-blue-950: #101b26;
  /* Warm Nota accent: use the dark value for text on light surfaces and the
     brighter value on midnight surfaces so the orange remains readable. */
  --nota-orange-600: #b45309;
  --nota-orange-400: #f79009;
  --accent-warm: var(--nota-orange-600);
  /* Vibrant accent colors — purple, blue, orange that catch the eye across all pages. */
  --accent-purple: #a855f7;
  --accent-blue: #6366f1;
  --accent-sky: #0ea5e9;
  /* Accent INK: an accent written as TEXT (a kicker, a badge label) owes AA
     4.5:1 on --surface; #a855f7 read 3.42:1 at 12 px (XC-CC-14). No new hue:
     paper pulls the accent toward the ink, the grammar --action-ink already
     uses (≥ 4.5:1 on --surface, --bg and --surface-inset); the dark themes
     write the accent itself (≥ 5.4:1 on the night). Fills, rules and borders
     keep the accent. */
  --accent-purple-ink: color-mix(in srgb, var(--accent-purple) 75%, var(--ink));
  --accent-blue-ink: color-mix(in srgb, var(--accent-blue) 80%, var(--ink));
  /* Channel-only copies of the three steps that alpha tokens below need
     (shadows, focus glow, background wash). rgb() cannot take a hex var, so
     these exist so no ramp value is ever retyped as a literal downstream. */
  --nota-blue-400-rgb: 120 169 191;
  --nota-blue-500-rgb: 64 117 152;
  --nota-blue-700-rgb: 56 104 136;

  /* Semantic tokens (light) */
  --brand: var(--nota-blue-700);
  /* Couleurs de MARQUE des fournisseurs tiers (portes à venir). Ce sont des
     constantes appartenant à Google, Meta et LinkedIn : elles ne suivent pas
     le thème et ne se redéfinissent nulle part — mais elles restent des
     jetons nommés plutôt que des littéraux dispersés dans les règles. */
  --marque-google-chip: var(--paper); /* le « G » doit rester sur fond blanc */
  --marque-youtube: #ff0000;
  --marque-facebook: #1877f2;
  --marque-linkedin: #0a66c2;
  --brand-bright: var(--nota-blue-500);
  --brand-ink: var(--paper);
  --brand-hover: var(--nota-blue-800);
  --on-accent: var(--paper); /* text/knob on saturated accents (tier pills, toggle) — white in every theme */

  /* The LIGHT canvas, named once. Two blocks need these exact values: the
     light theme itself, and the print block far below, which has to restate a
     light canvas because both dark blocks outrank :root on paper. Naming them
     here means the print block aliases instead of retyping — a light value
     exists in exactly one place (ADR 0048).
     The grounds are NEUTRAL GREYS (owner, 2026-10-03, on the light profile
     page: « I hate lightblue as background »). They were cyan (#eef5f7,
     #e6f0f4, #dcecf2, chroma 9–22); each grey is matched to the luminance it
     replaces, rounded lighter, so no ink loses contrast. Blue stays on what
     signals — accents, links, buttons, focus, the night header.
     fonds-neutres.test.mjs holds every light fill to chroma ≤ 4 and AA. */
  --canvas-bg: #f3f4f5;
  --canvas-surface: #fbfdfd; /* already neutral (chroma 2): kept, so its copies (emails, films, the light lockup) stay true */
  --canvas-surface-inset: #eeeff0;
  --canvas-surface-hover: #e9eaeb;
  /* What the light washes pour (--brand-tint, --brand-tint-solid, -strong):
     a neutral at the luminance of --brand-bright, so a wash darkens its
     surface exactly as much as the blue one did, without the blue. The dark
     themes pour --brand-bright itself (their own --brand-tint-pour). */
  --canvas-wash: #727374;
  --canvas-ink: #173b52;
  --canvas-ink-muted: #526b78; /* was #607986: 4.49:1 on the surface, 3.79 on the canvas — under AA for the 12–15px muted text it colours; #526b78 clears 4.6 everywhere (2026-09-11) */
  --canvas-subtitle-ink: #000000;
  --canvas-border: #d4d5d6;
  --canvas-border-strong: #bebfc0;
  --paper: #ffffff; /* true white — printed paper, and the ink on a brand fill */

  --bg: var(--canvas-bg);
  --surface: var(--canvas-surface);
  --surface-inset: var(--canvas-surface-inset);
  --surface-hover: var(--canvas-surface-hover);
  --ink: var(--canvas-ink);
  --ink-muted: var(--canvas-ink-muted);
  /* A field's placeholder (XC-CC-21): quieter than a typed value, still AA on
     the field. Paper has no room under --ink-muted (5.5:1 on --surface), so it
     is the muted ink itself; the night thins it (≥ 5.2:1 on --surface-inset). */
  --placeholder-ink: var(--ink-muted);
  --subtitle-ink: var(--canvas-subtitle-ink);
  --border: var(--canvas-border);
  --border-strong: var(--canvas-border-strong);
  --ring: var(--nota-blue-600);

  --footer-heart: #e53e3e; /* Decorative Québec credit, shared across themes. */
  --danger: #b42318;
  --warn: var(--accent-warm);
  --ok: var(--nota-blue-600);
  --danger-solid: #b42318; /* solid deep red for badge fills — dark --danger is a light salmon that fails white-text contrast */
  /* The tint pair (owner, 2026-08-27: « components carry no opacity — the
     background does »): --brand-tint keeps its alpha and is RESERVED for the
     background layers (the body's top fade, --wash-glow, the intro film),
     where translucency lets the drifting marks live. --brand-tint-solid is
     the SAME wash mixed down to the surface — fully opaque — and is what
     components (chips, badges, step discs, hover fills) paint with, so
     nothing ever bleeds through a component. Auto-follows each theme:
     the mix resolves against that theme's --brand-tint-pour / --surface.
     The POUR is a neutral grey on light (--canvas-wash, owner 2026-10-03:
     no light-blue ground) and --brand-bright on the dark themes, which
     restate it — so the washes are grey on paper and brand on the night.
     --brand-pour is the same switch for the rules that wash with the deeper
     --brand (the « mine » chat bubble, the compliance status): grey on
     light, --brand on the dark themes. */
  --brand-tint-pour: var(--canvas-wash);
  --brand-pour: var(--canvas-wash);
  --brand-tint: color-mix(in srgb, var(--brand-tint-pour) 5%, transparent); /* faint wash — background layers only */
  --brand-tint-mix: 6%;
  --brand-tint-solid: color-mix(in srgb, var(--brand-tint-pour) var(--brand-tint-mix), var(--surface));
  /* The HIGHLIGHT tint: one step richer than the component wash, for the one
     card a pane singles out (the notaire reward). Own mix, because the dark
     themes need a much deeper pour — at the component mix the highlight
     reads as washed grey on near-black, not as brand. */
  --brand-tint-mix-strong: 12%;
  --brand-tint-solid-strong: color-mix(in srgb, var(--brand-tint-pour) var(--brand-tint-mix-strong), var(--surface));
  /* The ink the highlight card writes with. On light the deep --brand clears
     AA over the strong tint; on dark it does not (the tint is a POUR of the
     accent, so ink and ground converge), so the dark themes step up to the
     accent itself. One token, so the rules that write on the strong tint
     never have to know which theme they are in. */
  --brand-on-tint-strong: var(--brand);
  /* L'encre d'un ÉTAT VIVANT — un onglet retenu, une ligne de marché survolée,
     la carte d'acte choisie. Sur papier, c'est la marque elle-même. Sur fond de
     nuit, --brand (#407598) est plus SOMBRE que l'encre : le libellé tombait à
     2,5–3,3:1 — il s'ÉTEIGNAIT au moment même où le doigt le désigne
     (propriétaire, 2026-09-12 : « le hover sur le financement… quel acte ? »).
     Les thèmes sombres montent donc d'un barreau, exactement comme
     --brand-on-tint-strong juste au-dessus. Aucune règle d'état ne repasse par
     --brand nu dans un `color:` — contraste-etats.test.mjs le refuse. */
  --brand-on-surface: var(--brand);
  /* The ink of a CONFIRMED state (« Retenue par un notaire », « ✓ Transmis »):
     the same rule as --brand-on-surface — --ok on paper, one rung lighter on
     the night, where --ok (#407598) falls to 3.3:1 (CA-A1). */
  --ok-on-surface: var(--ok);
  /* L'encre du MOT « OTA » du lockup. Elle vivait dans une règle tardive
     (.brand-word { color: var(--brand-hover) }) qui battait sans le dire la
     règle de base 6 500 lignes plus haut, et le film d'accueil — canevas
     CLAIR même quand la page est en nuit — héritait alors d'un bleu de nuit
     posé sur du blanc. Un jeton, une seule règle.
     LE MOT EST DE L'ENCRE (propriétaire, 2026-09-12 : « the brand nota make
     the font white ») : blanc sur la nuit, noir sur le jour — le registre
     d'encre des titres vaut aussi pour la marque. Le bleu ne reste que sur ce
     qui SIGNALE : le carré du sceau et le point final du mot. */
  --brand-word-ink: var(--ink);
  /* The earlier 95 % skin (owner, 2026-08-27: « blanc c'est très beau… mais on
     pourrait mettre un tout petit peu d'opacité, genre quatre-vingt-quinze
     pour cent » — superseding the earlier 90 %) — the shared translucent
     surface for the LARGE landing panels (calendar, reward cards, gate
     cards), always paired with a backdrop blur so what bleeds through
     reads as light, not marks. Small controls (buttons, chips, inputs) and
     anything stacked (dialogs, dropdowns, sticky header) stay fully opaque.
     An alias like --brand-tint-solid: auto-follows each theme's --surface. */
  /* 2026-09-12: lighter panels, with text opacity preserved at 100%. */
  --surface-veil-mix: 84%;
  --surface-veil: color-mix(in srgb, var(--surface) var(--surface-veil-mix), transparent);
  /* The hover wash at the same register — for rows that light up OVER a
     veiled panel (the landing's market rows): an opaque hover card over a
     translucent panel read as a pasted-on sticker (owner: « le hover sur le
     financement est bizarre »). Auto-follows each theme's --surface-hover. */
  --surface-soft-veil: color-mix(in srgb, var(--surface) 58%, transparent);
  --panel-border: color-mix(in srgb, var(--border) 70%, transparent);
  /* The quiet crown of the card register (owner, 2026-10-06: the three AI
     choices are siblings of the account door, not rivals — the door alone wears
     --brand and lifts). A crown is a trait qui signale, so it owes the house
     3:1 (fonds-neutres.test.mjs, AA_FILET): --border-strong gave 1.79:1 on
     paper and 2.65:1 on the night, which is why the band read flat. Grey on
     paper (the neutral the light washes already pour), one rung of the night's
     own blue on the dark themes — and each stays under --brand on both grounds,
     so the primary still wins. */
  --accent-line-neutral: var(--canvas-wash);
  /* THE MOTION REGISTER. Every transition and animation that is interface
     FEEDBACK reads one of these three rungs and --ease-ui; no UI rule sets its
     own duration or its own curve. Before 2026-09-14 the tokens existed and
     were used seven times against 139 hand-set durations and 34 ad hoc
     bezier curves, so nothing in the product moved on a shared rhythm.
       --motion-fast   hover, colour, focus, small state flips
       --motion-ui     panels, dialogs, pane entry
       --motion-enter  the larger arrivals
     The exception, deliberately left literal: scene-setting motion that is not
     feedback — the film's slow gradient drifts (igLightDrift 26s, igOrbit),
     attention flashes, the button spinner. Those are measured in seconds and
     belong to no UI rung; tokenising them would invite someone to later
     "harmonise" a 26s ambient drift with a 150ms hover. */
  --motion-fast: 150ms;
  --motion-ui: 220ms;
  --motion-enter: 320ms;
  /* Arrival overview (ADR 0056, mechanism replaced by ADR 0075, its mouse
     brought back by ADR 0077): deliberate, guided pacing — each scene breathes
     so the eye registers what the frame holds before it moves on. The frame
     draws itself over the control at its own size and the arrow rests on it:
     larger than the system cursor so it reads as the guide, and it does not pulse. */
  --arrival-start: 900ms;
  --arrival-scene: 5000ms;
  /* What a WHOLE tour may hold on screen, shared across its stops: seven stops
     read faster than two rather than taking seven times as long to watch. */
  --arrival-read: 13000ms;
  --arrival-pulse: 1800ms;
  /* The overview's own rung, one step above --motion-enter: the frame and its
     sentence are drawn to be followed, not to be noticed out of the corner of
     the eye (propriétaire, 2026-09-28 : « un peu plus lente »). */
  --arrival-enter: 480ms;
  /* The viewfinder: two corner brackets (the same chasing-corner grammar as
     the busy spinner — registre-carre) drawn this far off the control. */
  --arrival-frame-offset: 9px;
  --arrival-frame-weight: 3px;
  --arrival-frame-corner: 42%;
  /* The mouse (ADR 0077, back beside the frame): a real arrow, sized above
     the system cursor so a visitor recognises it as the guide, not their own
     pointer. --arrival-travel is the ceiling of one glide; the engine bounds
     it to the scene it lands in. */
  --arrival-cursor-size: 40px;
  --arrival-cursor-ratio: .714;
  --arrival-travel: 1400ms;
  /* The arrow's rim, in the units of its own 14 × 20 drawing: the surface
     colour stroked under the fill, so it detaches from any background. */
  --arrival-cursor-stroke: 1.5;
  /* The overview's layers. The showcase sits over the sticky site header and
     under the help dial; inside it, the sentence under the frame, the arrow
     over both — it points at what the frame holds, never behind it. */
  --arrival-layer: 45;
  --arrival-layer-note: 1;
  --arrival-layer-frame: 2;
  --arrival-layer-cursor: 3;
  /* The brand splash (brand-splash.test.mjs): the web counterpart of the video
     splash. --splash-hold is when the overlay starts to leave (it then fades
     for --motion-enter: ~3.5 s in all); the stage holds for ~2 s after the
     last word lands so the signature line can be read. --splash-still is how long the
     motionless lockup stays under prefers-reduced-motion. The stage is the
     video's (.video-stage: a radial glow at 30% 40% over the night) in the
     dark theme. In light it is « le blanc »: the brand guide's light lockup
     specimen (brand.html — the word in --nota-blue-950 on --canvas-surface)
     under a brand glow. There white light cannot read: screened, it vanishes
     on the paper and only bleaches the tile. So the light turns brand blue and
     is painted as is BEHIND the lockup (--splash-blend, --splash-layer), and
     the full-screen veil stays off (--splash-veil). --splash-ink is the word,
     set on the overlay itself: index.html's foundation block, after this
     sheet, paints --brand-word-ink with the header's night ink at :root, which
     left « OTA » white on white (owner, 2026-09-24). --splash-dot is the
     wordmark's period (its one literal fill is this blue): the middle dots of
     the signature line under the lockup are that period, in both themes. */
  --splash-hold: 3500ms;
  --splash-dot: var(--nota-blue-500);
  --splash-still: 700ms;
  --splash-bg: var(--canvas-surface);
  --splash-glow: rgb(var(--nota-blue-500-rgb) / .12);
  --splash-light: var(--nota-blue-300);
  --splash-signal: var(--nota-blue-400);
  --splash-ink: var(--nota-blue-950);
  --splash-blend: normal;
  --splash-layer: -1;
  --splash-veil: 0;
  --ease-ui: cubic-bezier(.22, 1, .36, 1);
  /* Le ressort. UNE seule courbe de dépassement, et elle est nommée : le point
     de contrôle 1.56 fait passer la transformation AU-DELÀ de sa cible avant de
     revenir. Réservée à ce qui se pose (la tuile, la graine et le point final
     de l'arrivée ; le « ? » qui tourne en « ✕ ») — jamais à une couleur,
     jamais à une opacité, qui n'ont pas de « au-delà ».
     Tout le reste lit --ease-ui (guide §6.3 principe 2). */
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  /* Menu entrance stagger — each drawer row arrives 40ms after the one above,
     so the panel fills top-to-bottom instead of appearing as a block. */
  --menu-stagger: 40ms;
  /* Drawer-item arrival: a quiet slide + fade, timed to --motion-ui. */
  --menu-item-dur: var(--motion-ui);
  /* Hamburger ↔ ✕ morph: three lines translate to centre then rotate. */
  --burger-dur: var(--motion-ui);
  --surface-hover-veil: color-mix(in srgb, var(--surface-hover) var(--surface-veil-mix), transparent);

  /* Tier accent ramp — cool (calm) to warm (urgent).
     All five are dark enough for white .pill text to meet WCAG AA (>=4.5:1). */
  --tier-standard: #64748b;
  --tier-rapide: #356c87;
  --tier-prioritaire: #906800;
  --tier-urgence: #c2410c;
  --tier-extreme: #b42318;
  /* Outline text/border tint per tier — light = base, dark = brighter for AA
     (the dark values live in BOTH dark blocks below; keep all three in sync).
     Rapide is the CALM baseline and covers most of a month's cells, so its ink
     is NEUTRAL (owner, 2026-08-28, twice: « trop vert ») — the cell's tier bar
     and the legend dot keep the blue; the saturated ramp starts at urgency. */
  --tier-rapide-ink: var(--ink-muted); --tier-prioritaire-ink: var(--tier-prioritaire);
  --tier-urgence-ink: var(--tier-urgence); --tier-extreme-ink: var(--tier-extreme);
  /* The calendar badge's own ink (2026-10-12): at 11 px on a selected or hovered
     cell (--action-tint) the prioritaire and urgence inks measured 4.26:1 and
     4.38:1; these hold 4.77:1 and 4.94:1 there, 5.6:1 and 5.8:1 on paper. Only
     the badge text moves: the bar, the pill, the legend dot and --accent-gold
     keep the base hue. On the night the badge reads the ink (both dark blocks). */
  --tier-prioritaire-badge-ink: #866100; --tier-urgence-badge-ink: #b53b0b;

  /* Per-service categorical hues — deliberately distinct from the blue brand
     and the blue→red tier ramp; used as calendar data-viz fills (the
     per-service mix bar + its legend) and, poured to a pale ground, by two
     appearance presets (--preset-accent-*). TWO acts of the financing family
     (ADR 0010): each act owns its token here, and every theme block below
     redefines EACH of them — a service colour must never fall back to a light
     value in the dark themes. */
  --svc-refinancement: #0ea5e9; /* vibrant sky blue — strong on light surfaces */
  --svc-financement: #6366f1; /* vibrant indigo — clearly apart from the teal sibling */
  --svc-pret-particuliers: #f43f5e; /* vibrant rose — apart from blue, teal, amber and purple siblings */
  --svc-testament: #f59e0b; /* vibrant amber — warm and eye-catching */
  --svc-procuration: #a855f7; /* vibrant purple — distinct and bold */
  --svc-mandat-protection: #14b8a6; /* vibrant teal — apart from blue, indigo, amber and purple siblings */
  /* Service MARKS (HR-25): the dot, stripe or rule that names an act beside
     its label owes 3:1 on --surface and on --bg (WCAG 1.4.11). Three paper
     hues fall short (sky 2.71, amber 2.10, teal 2.44): they pull toward the
     ink, no new hue; the three that pass alias their act. Marks only — amounts
     and names stay ink (brand §2.8 rule 5). Every theme block redefines each
     mark; the dark themes alias the act colour (≥ 5.4:1 on the night). */
  --svc-refinancement-mark: color-mix(in srgb, var(--svc-refinancement) 85%, var(--ink));
  --svc-financement-mark: var(--svc-financement);
  --svc-pret-particuliers-mark: var(--svc-pret-particuliers);
  --svc-testament-mark: color-mix(in srgb, var(--svc-testament) 70%, var(--ink));
  --svc-procuration-mark: var(--svc-procuration);
  --svc-mandat-protection-mark: color-mix(in srgb, var(--svc-mandat-protection) 75%, var(--ink));

  /* Offer status: waiting for a notary vs. retained by one. var() aliases, so
     they track the theme automatically — do not duplicate into the dark blocks. */
  --status-ouverte: var(--brand-bright);
  --status-retenue: var(--ink-muted);

  /* Geometry — ONE square scale for every shape (owner's ask, 2026-08-27:
     « tout garder le même style carré »). No pills, no circles: controls and
     chips take --radius, badges and knobs --radius-sm, thin bars --radius-xs.
     Pure signal DOTS (≤8px, border-radius:50%) are the only round survivors —
     a dot is punctuation, not a component. */
  --radius: 8px;
  --radius-sm: 6px;
  --radius-xs: 3px;
  --radius-lg: 12px;

  color-scheme: light;
  --shadow-sm: 0 1px 2px rgb(16 27 38 / .04), 0 4px 12px -8px rgb(16 27 38 / .12);
  --shadow: 0 2px 6px -3px rgb(16 27 38 / .07), 0 16px 36px -20px rgb(16 27 38 / .2);
  --shadow-lg: 0 24px 50px -16px rgb(var(--nota-blue-700-rgb) / 0.24);
  --shadow-brand: 0 6px 18px -8px rgb(var(--nota-blue-700-rgb) / .24); /* brand-primary hover lift */
  --focus-ring: rgb(var(--nota-blue-500-rgb) / 0.3);      /* brand focus glow (3px) */
  --backdrop: rgb(16 27 38 / 0.12);                    /* light dialog scrim keeps the page readable */
  --backdrop-soft: rgb(16 27 38 / 0.08);               /* the tour's light scrim — it guides, it does not seal the page */
  /* The DIALOG backdrop (XC-CC-01). dialog::backdrop mixed --backdrop at
     160 %, a parse error that dropped the whole declaration: every modal
     blurred the page and never dimmed it. One valid mix per theme, every
     proportion within 0–100 %: the scrim deepened toward the brand night,
     ≈ .32 alpha on paper and ≈ .55 on the night (brand §4.4 rule 15). */
  --dialog-backdrop: color-mix(in srgb, var(--backdrop) 77%, var(--nota-blue-950));
  --dialog-backdrop-filter: blur(10px) saturate(.85);

  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, monospace;
  /* The wordmark uses a custom monoline SVG, so every letter stays aligned at
     every size. Sora is the display face of EVERY heading — owner, 2026-09-10:
     the Signature beta page is the reference and the whole app follows it. */
  --font-display: 'Sora', var(--font-sans);
  --weight-display: 800;
  /* --- Type scale: quieter headings, comfortable running text -----------
     One register for the whole app. No heading rule sets its own size, face
     or weight — it reads a step of this scale (typographie.test.mjs enforces
     it), so the three doors, the dialogs and the beta print one voice. */
  --type-h1: clamp(26px, 2.25vw, 36px); --type-h1-lh: 1.12; --type-h1-ls: -.04em;
  --type-h1-compact: clamp(26px, 6vw, 32px); /* the stacked (narrow) hero */
  --type-h2: clamp(19px, 1.5vw, 24px); --type-h2-lh: 1.2; --type-h2-ls: -.035em;
  --type-h3: 17px; --type-h3-lh: 1.3; --type-h3-ls: -.02em;
  --type-h4: 15px; --type-h4-lh: 1.3; --type-h4-ls: -.01em; /* dense card kickers (.nc-h, .nc-live-h, .nc-conformite-h) — ADR 0050, addendum 2026-09-11 */
  --type-lead: 16px; --type-lead-lh: 1.6;
  --type-eyebrow: 12px; --type-eyebrow-ls: .1em;
  /* Body register (owner, 2026-09-11: « every font size the same across ALL
     applications »): the running-text size and leading, named so the brand
     guide, the pitch deck and the private planning documents read the SAME
     step instead of 15 / 16.5 / browser-default. Mirrored verbatim in
     tokens.css, signature.css, brand.html, pitch-deck.html and the private
     plan render under docs/ (typographie.test.mjs compares every copy). */
  --type-body: 16px; --type-body-lh: 1.5;
  /* The dense calendar rung (CAR-CC-22): a phone cell's day number, « dès »
     and amount read one named size instead of 10.5–12.5 px literals. */
  --type-cal-dense: 13px;

  --layout-width: 2200px;
  --logo-header-tile: 32px;
  --logo-footer-tile: 24px;
  --layout-gutter: clamp(16px, 4vw, 40px);
  --maxw: var(--layout-width); /* maximized width — unified rail from brand-foundations */
  --gap: 20px;
  /* The four-column rows of the notary landing (requests band, bottom band,
     AI choices) share one wider gutter (propriétaire, 2026-09-26 : « more
     margin between the four columns »). All three grids use it, so the three
     choices still land exactly under request tiles two to four. */
  --nc-col-gap: clamp(var(--space-12), 2.4vw, var(--space-16) + var(--space-8));
  /* Spacing scale. These are the values already most used in this file, named
     so neighbouring components stop drifting 1-3px apart. */
  --space-1: 2px;  --space-2: 4px;   --space-3: 6px;   --space-4: 8px;
  --space-5: 10px; --space-6: 12px;  --space-7: 14px;  --space-8: 16px;
  --space-9: 18px; --space-10: 20px; --space-12: 24px; --space-14: 28px;
  --space-16: 32px;
  /* Control height: minimum touch target size for interactive elements.
     WCAG 2.2 AA requires 24x24px minimum; 44px is the recommended size for
     touch targets on mobile devices (Apple HIG, Material Design). */
  --control-height: 44px;
  /* The canonical icon (brand §4.4 rule 22: one 20 px SVG in inherited ink)
     and the inset that starts a child line on its parent title's TEXT edge,
     after the icon and its gap (§4.4 rule 14, CAR-CC-17). */
  --icon-size: 20px;
  --icon-inset: calc(var(--icon-size) + var(--space-3));
  /* One visual height for every control of the header row — tabs, icon
     buttons, auth actions, the Hub trigger (XC-CC-23) — the canonical target,
     so the visible box is the hit area. The 28 px language and theme strip
     stays the owner's exception. */
  --header-control: var(--control-height);
  /* Between two bands of one page (a hero, a row of requests, a form band):
     above the largest in-band gap, so a band never reads as part of the one
     above it. 32px on a phone, 48px on a wide screen (brand guide §4.2). */
  --section-gap: clamp(var(--space-16), 3.2vw, calc(var(--space-16) + var(--space-8)));
  /* The intro's responsive breathing room, shared with anything that must
     top-align with the intro's first line (e.g. the notary sign-in card). */
  --intro-pad-top: clamp(16px, 1.8vw, 24px);
  /* Ambient gradients (owner, 2026-08-27, superseding the morning's
     « gradient on every component »: « the gradient and the opacity are for
     the REST, not the component themselves »). Atmosphere lives ONLY on the
     background layers — the body's top fade and --wash-glow behind sections.
     Components are flat, fully opaque surfaces
     (--surface / --surface-inset / --brand-tint-solid): their contrast
     never shifts with what sits beneath, and nothing bleeds through. */
  /* Each glow must reach full transparency BEFORE every edge of the box it
     paints (nearest-edge distance / radius >= the fade stop on all four
     sides), otherwise the box boundary slices the light and draws the very
     edge this token exists to avoid. */
  --wash-glow:
    radial-gradient(38% 52% at 72% 38%, var(--brand-tint) 26%, transparent 72%),
    radial-gradient(30% 52% at 22% 62%, var(--brand-tint) 26%, transparent 72%);
  /* The three decisions everything else derives from. */
  --pad-card: var(--space-6) var(--space-7);
  --pad-card-lg: var(--space-8) var(--space-9);
  --pad-dialog: var(--space-12);
  /* The booking sheet's reading width (CB-23): today's #day-dialog clamp, named. */
  --dialog-w-book: clamp(760px, 78vw, 1180px);

  --header-h: 64px;   /* The dark brand band has room for the full lockup. */
  --sticky-top: 84px; /* header height + breathing gap — used for scroll-padding / scroll-margin */
  --header-bg: var(--nota-blue-950);
  --header-ink: #f4f8fa;
  --header-muted: var(--nota-blue-200);
  --header-border: var(--nota-blue-800);
  --header-badge-bg: var(--nota-blue-100);
  --header-badge-ink: var(--nota-blue-950);

  /* Fixed third-party brand-mark colours (Google / Outlook calendar logos).
     These are brand IDENTITY constants, NOT theme tokens: they must render
     identically in every theme and must never be restyled. Defined here only so
     no literal colour lives in markup — reference via var() in the SVG fills. */
  /* --- Surfaces that stay dark in EVERY theme ------------------------------
     The signing room (ADR 0047) and the video frames are a dark room by
     design: they do not flip with the theme, so they cannot read --ink / --bg
     / --danger-solid, which do. The roles they need are named ONCE here, with
     the dark theme's own values, rather than retyped as literals in the rules
     below (ADR 0048: a one-off hex in a component rule is a defect). */
  --midnight-ink: #f4f8fa;                    /* = the dark theme's --ink */
  --midnight-matte: #000000;                  /* letterbox behind a video frame */
  --midnight-veil-soft: rgb(255 255 255 / 0.04);   /* block fill on the rail */
  --midnight-veil: rgb(255 255 255 / 0.06);        /* quiet button fill */
  --midnight-veil-strong: rgb(255 255 255 / 0.12); /* its hover */
  --sheen: rgb(255 255 255 / 0.34);           /* inset top highlight on a raised panel */
  --sheen-faint: rgb(255 255 255 / 0.03);     /* hairline inset on a dialog top edge */
  --scrim-deep: rgb(0 0 0 / 0.20);            /* black the dialog backdrop is mixed toward */
  --danger-solid-rgb: 180 35 24;              /* channels of --danger-solid, for alpha washes */
  --danger-wash: rgb(var(--danger-solid-rgb) / 0.18); /* alert ground on a midnight surface */
  --danger-pale: #ffe4e1;                     /* the ink that reads on --danger-wash */
  --on-warn: var(--paper);                    /* ink on an --accent-warm fill — orange-600 in light: white reads 5.02:1 */

  --logo-google-blue: #4285f4;
  --logo-google-green: #34a853;
  --logo-google-yellow: #fbbc05;
  --logo-google-red: #ea4335;
  --logo-outlook-blue: #0f6cbd;
  --logo-microsoft-red: #f25022;
  --logo-microsoft-green: #7fba00;
  --logo-microsoft-blue: #00a4ef;
  --logo-microsoft-yellow: #ffb900;
  --logo-on-brand: var(--paper);
}

/* Dark theme — system preference.
   A STANDALONE @media block: prefers-color-scheme now actually applies. The old
   form comma-joined a selector list to an @media rule (`:root[...], @media {...}`),
   which is invalid CSS and was silently discarded — so system-dark users got the
   light UI. Excludes an explicit light opt-out via :not([data-theme='light']). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --brand: var(--nota-blue-500);
    --brand-bright: var(--nota-blue-400);
    --brand-ink: #061c2a;
    --brand-hover: var(--nota-blue-400);
    --accent-warm: var(--nota-orange-400);
    --on-warn: #1b1300; /* orange-400 in dark: dark ink reads 7.85:1 */

    /* A blue foundation, softened (owner, 2026-09-14: « keep a blue
       foundation, but softer and less intense »). This supersedes the
       neutral-graphite note that stood here: the canvas now carries a blue
       cast, but at the graphite's luminance, so blue reads as the ground
       without flooding it at full intensity. */
    --bg: #14181d;
    --surface: #1b2026;
    --surface-inset: #222830;
    --surface-hover: #29313a;
    --ink: #f4f8fa;
    --ink-muted: #afc2cf;
    --placeholder-ink: color-mix(in srgb, var(--ink-muted) 75%, transparent);
    --subtitle-ink: #f4f8fa;
    --border: #37424e;
    --border-strong: #536273;
    --accent-line-neutral: color-mix(in srgb, var(--border-strong) 85%, var(--nota-blue-400)); /* 3.09:1 on the card, under the brand's 3.33 */
    --ring: var(--nota-blue-400);

    --danger: #f97066;
    --warn: var(--accent-warm);
    --ok: var(--nota-blue-500);
    --danger-solid: #b42318;
    --brand-tint: rgb(255 255 255 / 0.025);
    --brand-tint-pour: var(--brand-bright); /* the night keeps its blue foundation: its washes pour the brand */
    --brand-pour: var(--brand);
    --brand-tint-mix: 8%;
    --brand-tint-mix-strong: 20%; /* deep pour: on near-black a component-level mix greys out — but 26 % pushed --brand ink on the highlight card below AA, so the card now carries --brand-on-tint-strong instead */
    --brand-on-tint-strong: var(--brand-bright);
    --brand-on-surface: var(--brand-bright); /* l’encre des états vivants : un barreau plus clair, sinon le libellé s’éteint au moment où on le désigne */
    --ok-on-surface: var(--nota-blue-400); /* the lighter ok rung, as --brand-bright: 6.4:1 on the night */
    --brand-word-ink: var(--ink); /* le mot du lockup est blanc sur la nuit : de l'encre, jamais du bleu */
    --splash-bg: var(--nota-blue-950); --splash-glow: rgb(var(--nota-blue-700-rgb) / .31); --splash-light: var(--nota-blue-100); /* the video stage: --video-bg / --video-glow / --video-ink */
    --splash-ink: var(--ink); --splash-blend: screen; --splash-layer: auto; --splash-veil: 1; --splash-ambient-peak: 0.08; /* light over the night: screened in front of the mark, veil on */
    --backdrop: rgb(3 7 9 / 0.52);
    --backdrop-soft: rgb(3 7 9 / 0.38);
    --dialog-backdrop: color-mix(in srgb, var(--backdrop) 94%, var(--nota-blue-950));

    --tier-standard: #64748b;
    --tier-rapide: #356c87;
    --tier-prioritaire: #906800;
    --tier-urgence: #c2410c;
    --tier-extreme: #b42318;
    --tier-rapide-ink: var(--ink-muted); /* NEUTRAL — the green cast was too much (owner, 2026-08-28) */ --tier-prioritaire-ink: #e0b44a; --tier-urgence-ink: #f0894c; --tier-extreme-ink: #f97066; --tier-prioritaire-badge-ink: var(--tier-prioritaire-ink); --tier-urgence-badge-ink: var(--tier-urgence-ink);
    --svc-refinancement: #78a9bf;
    --svc-financement: #818cf8;
    --svc-pret-particuliers: #fda4af;
    --svc-testament: var(--nota-orange-400);
    --svc-procuration: #c4a7ff;
    --svc-mandat-protection: #5eead4;
    --accent-purple: #c4a7ff;
    --accent-blue: #818cf8;
    --accent-sky: #78a9bf;
    --accent-purple-ink: var(--accent-purple);
    --accent-blue-ink: var(--accent-blue);
    --svc-refinancement-mark: var(--svc-refinancement);
    --svc-financement-mark: var(--svc-financement);
    --svc-pret-particuliers-mark: var(--svc-pret-particuliers);
    --svc-testament-mark: var(--svc-testament);
    --svc-procuration-mark: var(--svc-procuration);
    --svc-mandat-protection-mark: var(--svc-mandat-protection);

    /* Dark shadows are cast in black, not light ink, so they read on #0a0a0a. */
    --shadow-sm: 0 1px 2px rgb(0 0 0 / .2), 0 4px 12px -8px rgb(0 0 0 / .3);
    --shadow: 0 2px 6px -3px rgb(0 0 0 / .2), 0 16px 36px -20px rgb(0 0 0 / .5);
    --shadow-lg: 0 20px 40px -12px rgb(0 0 0 / 0.6);
    --shadow-brand: 0 6px 18px -8px rgb(0 0 0 / .4);
    --focus-ring: rgb(var(--nota-blue-400-rgb) / 0.45);
    color-scheme: dark;
    /* Dark surfaces are already dark — the light-theme blue-50 would look wrong. */
    :is(dialog, #day-dialog) { background: var(--surface); }
    .day-head { background: linear-gradient(to bottom, var(--surface-hover), var(--surface)); }
  }
}

/* Explicit dark toggle — wins over system preference in both directions.
   Defines the SAME complete token set as the @media block above; keep the two in
   lockstep so no token (e.g. --warn, --shadow-lg) drifts back to its light value. */
:root[data-theme='dark'] {
  --brand: var(--nota-blue-500);
  --brand-bright: var(--nota-blue-400);
  --brand-ink: #061c2a;
  --brand-hover: var(--nota-blue-400);
  --accent-warm: var(--nota-orange-400);
  --on-warn: #1b1300; /* orange-400 in dark: dark ink reads 7.85:1 */

  /* A blue foundation, softened (owner, 2026-09-14: « keep a blue
     foundation, but softer and less intense »). This supersedes the
     neutral-graphite note that stood here: the canvas now carries a blue
     cast, but at the graphite's luminance, so blue reads as the ground
     without flooding it at full intensity. */
  --bg: #14181d;
  --surface: #1b2026;
  --surface-inset: #222830;
  --surface-hover: #29313a;
  --ink: #f4f8fa;
  --ink-muted: #afc2cf;
  --placeholder-ink: color-mix(in srgb, var(--ink-muted) 75%, transparent);
  --subtitle-ink: #f4f8fa;
  --border: #37424e;
  --border-strong: #536273;
  --accent-line-neutral: color-mix(in srgb, var(--border-strong) 85%, var(--nota-blue-400)); /* 3.09:1 on the card, under the brand's 3.33 */
  --ring: var(--nota-blue-400);

  --danger: #f97066;
  --warn: var(--accent-warm);
  --ok: var(--nota-blue-500);
  --danger-solid: #b42318;
  --brand-tint: rgb(255 255 255 / 0.025);
  --brand-tint-pour: var(--brand-bright); /* the night keeps its blue foundation: its washes pour the brand */
  --brand-pour: var(--brand);
  --brand-tint-mix: 8%;
  --brand-tint-mix-strong: 20%; /* deep pour: on near-black a component-level mix greys out — but 26 % pushed --brand ink on the highlight card below AA, so the card now carries --brand-on-tint-strong instead */
  --brand-on-tint-strong: var(--brand-bright);
  --brand-on-surface: var(--brand-bright); /* l’encre des états vivants : un barreau plus clair, sinon le libellé s’éteint au moment où on le désigne */
  --ok-on-surface: var(--nota-blue-400); /* the lighter ok rung, as --brand-bright: 6.4:1 on the night */
  --brand-word-ink: var(--ink); /* le mot du lockup est blanc sur la nuit : de l'encre, jamais du bleu */
  --splash-bg: var(--nota-blue-950); --splash-glow: rgb(var(--nota-blue-700-rgb) / .31); --splash-light: var(--nota-blue-100); /* the video stage: --video-bg / --video-glow / --video-ink */
  --splash-ink: var(--ink); --splash-blend: screen; --splash-layer: auto; --splash-veil: 1; --splash-ambient-peak: 0.08; /* light over the night: screened in front of the mark, veil on */
  --backdrop: rgb(3 7 9 / 0.52);
  --backdrop-soft: rgb(3 7 9 / 0.38);
  --dialog-backdrop: color-mix(in srgb, var(--backdrop) 94%, var(--nota-blue-950));

  --tier-standard: #64748b;
  --tier-rapide: #356c87;
  --tier-prioritaire: #906800;
  --tier-urgence: #c2410c;
  --tier-extreme: #b42318;
  --tier-rapide-ink: var(--ink-muted); /* NEUTRAL — the green cast was too much (owner, 2026-08-28) */ --tier-prioritaire-ink: #e0b44a; --tier-urgence-ink: #f0894c; --tier-extreme-ink: #f97066; --tier-prioritaire-badge-ink: var(--tier-prioritaire-ink); --tier-urgence-badge-ink: var(--tier-urgence-ink);
  --svc-refinancement: #78a9bf;
  --svc-financement: #818cf8;
  --svc-pret-particuliers: #fda4af;
  --svc-testament: var(--nota-orange-400);
  --svc-procuration: #c4a7ff;
  --svc-mandat-protection: #5eead4;
  --accent-purple: #c4a7ff;
  --accent-blue: #818cf8;
  --accent-sky: #78a9bf;
  --accent-purple-ink: var(--accent-purple);
  --accent-blue-ink: var(--accent-blue);
  --svc-refinancement-mark: var(--svc-refinancement);
  --svc-financement-mark: var(--svc-financement);
  --svc-pret-particuliers-mark: var(--svc-pret-particuliers);
  --svc-testament-mark: var(--svc-testament);
  --svc-procuration-mark: var(--svc-procuration);
  --svc-mandat-protection-mark: var(--svc-mandat-protection);

  /* Dark shadows are cast in black, not light ink, so they read on #0a0a0a. */
  --shadow-sm: 0 1px 2px rgb(0 0 0 / .2), 0 4px 12px -8px rgb(0 0 0 / .3);
  --shadow: 0 2px 6px -3px rgb(0 0 0 / .2), 0 16px 36px -20px rgb(0 0 0 / .5);
  --shadow-lg: 0 20px 40px -12px rgb(0 0 0 / 0.6);
  --shadow-brand: 0 6px 18px -8px rgb(0 0 0 / .4);
  --focus-ring: rgb(var(--nota-blue-400-rgb) / 0.45);
  color-scheme: dark;
  /* Dark surfaces are already dark — the light-theme blue-50 would look wrong. */
  :is(dialog, #day-dialog) { background: var(--surface); }
  .day-head { background: linear-gradient(to bottom, var(--surface-hover), var(--surface)); }
}

/* Interaction colors describe the action, while service colors keep their
   data meaning. Derive ink and opaque tints in each scope so both themes keep
   readable text; labels, borders and focus outlines also express the state. */
:root {
  --action-accent: var(--accent-blue);
  --action-ink-mix: 60%;
  --action-surface: var(--surface);
  --action-text: var(--ink);
  --accent-gold: var(--tier-prioritaire-ink);
  --help-control-size: 56px;
  --help-edge-gap: 24px;
  --help-press: .96;
}
:root, .tab-pane, dialog, .site-header, .nav-tab, .mnav-link, .header-tools,
.experience-path, .speed-dial, .speed-dial-option, .pulse-row, .chip-svc, .notif-panel {
  --action-ink: color-mix(in srgb, var(--action-accent) var(--action-ink-mix), var(--action-text));
  --action-tint: color-mix(in srgb, var(--action-accent) 12%, var(--action-surface));
  --action-tint-strong: color-mix(in srgb, var(--action-accent) 20%, var(--action-surface));
  --ring: var(--action-ink);
  --focus-ring: color-mix(in srgb, var(--action-accent) 32%, transparent);
}
#pane-notaires, [data-tab='notaires'], .experience-path--notary { --action-accent: var(--accent-purple); }
#pane-partenaires, [data-tab='partenaires'], .speed-dial, #sd-guide {
  --action-accent: var(--accent-gold);
  --action-ink-mix: 40%;
}
#pane-beta, [data-tab='beta'], #sd-chat { --action-accent: var(--accent-blue); }
[data-svc='refinancement'] { --action-accent: var(--svc-refinancement); }
[data-svc='financement'] { --action-accent: var(--svc-financement); }
[data-svc='pret-particuliers'] { --action-accent: var(--svc-pret-particuliers); }
[data-svc='testament'] { --action-accent: var(--svc-testament); --action-ink-mix: 40%; }
[data-svc='procuration'] { --action-accent: var(--svc-procuration); }
/* Teal is a light accent like amber: the same 40 % mix keeps the ink legible. */
[data-svc='mandat-protection'] { --action-accent: var(--svc-mandat-protection); --action-ink-mix: 40%; }
.site-header {
  --action-surface: var(--header-bg);
  --action-text: var(--header-ink);
  --control-surface: color-mix(in srgb, var(--header-ink) 12%, var(--header-bg));
}
/* The account menu hangs from the night bar but is painted on page paper, so
   it derives its action palette from that paper. Inheriting the bar's palette
   tinted a hovered row navy under page ink (~1.3:1) and drew lavender icons
   (~1.9:1). One hue, the brand its avatar and role tag already wear, carries
   hover, icons and focus (account-menu-palette.test.mjs). */
.site-header .notif-panel {
  --action-accent: var(--brand-on-surface);
  --action-ink-mix: 60%;
  --action-surface: var(--surface);
  --action-text: var(--ink);
  /* Hover is a neutral state layer, not a brand wash (owner, 2026-10-03:
     « I hate lightblue as background »): the brand stays on the icons, the
     focus ring and a hovered row's inset bar. */
  --action-tint: var(--surface-hover);
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }
/* Selected text answers to the brand, not to the browser's default blue —
     which read as a foreign UI on a dark Nota page and lost contrast on
   the light one. Both halves are stated (never colour alone on a veil). */
::selection { background: color-mix(in srgb, var(--brand) 30%, transparent); color: var(--ink); }

body {
  margin: 0;
  /* Sticky-footer column: without it the footer sits wherever the content ends
     and the rest of the viewport is bare canvas — 412px on Notaires, 784px on
     Profil. dvh (with a vh fallback) so mobile browser chrome does not add a
     phantom scrollbar. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
  font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
  /* The page's ambient light: a full-width fade bleeding down from the top
     edge, over the flat token — every pane opens under it. 180deg only: a
     centred radial pooled in the middle of a wide screen and left the
     margins flat black (owner: « clash with black margin »). */
  background: linear-gradient(180deg, var(--brand-tint), transparent 460px) no-repeat, var(--user-bg, var(--bg));
  color: var(--ink);
  font-size: var(--type-body);
  line-height: var(--type-body-lh);
  -webkit-font-smoothing: antialiased;
  transition: background-color var(--motion-ui) var(--ease-ui), color var(--motion-ui) var(--ease-ui);
}

/* Takes the slack in the body column, so the footer is pushed to the bottom on
   short pages and simply follows the content on long ones. The bottom padding
   keeps every pane's last card off the footer rule. */
#main { flex: 1 0 auto; padding-bottom: var(--space-14); }

/* Every heading is display type on the shared scale; a pane only colours it. */
h1, h2, h3 { font-family: var(--font-display); font-weight: var(--weight-display); margin: 0; }
h1 { font-size: var(--type-h1); line-height: var(--type-h1-lh); letter-spacing: var(--type-h1-ls); text-wrap: balance; }
h2 { font-size: var(--type-h2); line-height: var(--type-h2-lh); letter-spacing: var(--type-h2-ls); }
h3 { font-size: var(--type-h3); line-height: var(--type-h3-lh); letter-spacing: var(--type-h3-ls); }

a { color: var(--action-ink); text-decoration: none; }
a:hover { text-decoration: underline; }

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

/* --- Skip link ----------------------------------------------------------- */
.skip {
  position: absolute; left: -999px; top: 0;
  background: var(--brand); color: var(--brand-ink);
  padding: 10px 16px; z-index: 100; border-radius: 0 0 var(--radius) 0;
}
.skip:focus { left: 0; }
/* The hidden SVG symbol table (the logomark): zero-size, out of flow, never read. */
.svg-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* --- Header -------------------------------------------------------------- */
.site-header {
  /* The menu is its OWN layer above the page (owner, 2026-08-27: « a bigger
     diff in between menu and body ») — and a SOLID one (same day, later:
     « components carry no opacity — the background does »): opaque surface
     plus a real shadow carry the separation. The former glass tone + blur
     let the page smear through the bar; WCAG-wise a translucent chrome's
     contrast shifts with whatever scrolls beneath — solid never does.
     Distinct without ever drawing a hairline. */
  background: var(--header-bg);
  box-shadow: var(--shadow);
  position: sticky; top: 0; z-index: 40;
}
.site-header .wrap {
  display: flex; align-items: center; gap: 12px;
  height: var(--header-h);
  position: relative;
}
.site-header :is(.icon-btn, .header-auth .btn, .header-tools) {
  color: var(--header-muted);
}
.site-header .nav-tab-badge { color: var(--header-muted); }
.site-header .icon-btn { border-color: var(--header-border); }
.site-header .icon-btn:is(:hover, :focus-visible) { background: var(--control-surface); color: var(--header-ink); border-color: var(--header-muted); }
.site-header .header-tools {
  --action-accent: var(--header-ink);
  /* The existing controls inherit one header palette. Their tracks remain
     unfilled; selection and hover share the same solid surface. */
  --surface-inset: transparent;
  --surface: var(--control-surface);
  --surface-hover: var(--control-surface);
  --ink: var(--header-ink);
  --ink-muted: var(--header-muted);
  --brand: var(--header-ink);
  --brand-on-surface: var(--header-ink);
  --border: var(--header-border);
}
/* The selected whole artwork keeps its native aspect ratio. Canonical height
   and badge spacing are tokens shared by generated chrome. Compatibility crop
   dimensions remain available for standalone marks and historical studies. */
.brand, .footer-brand, .brand-splash-lockup{
  --lockup-tile: 44px;
  --lockup-logo-height: calc(var(--lockup-tile) * 1.28);
  --lockup-logo-ratio: var(--logo-ratio);
  --lockup-word: calc(var(--lockup-tile) * .48);
  --lockup-gap: calc(var(--lockup-tile) * .12);
  --lockup-badge-gap: calc(var(--lockup-tile) * .12);
  --lockup-badge-size: max(8px, calc(var(--lockup-tile) * .135)); /* Québec is a location signal: smaller than the wordmark, with room to breathe beside it. */
}
.brand {
  display: flex; align-items: center; gap: var(--lockup-badge-gap);
  font-weight: 800; font-size: 18px; letter-spacing: -0.03em; color: var(--header-ink);
  border: 0; background: transparent; box-shadow: none; padding: 0;
}
.brand:hover { text-decoration: none; }
.brand-lockup {
  display: inline-flex; align-items: center; gap: var(--lockup-gap); flex: none;
  color: var(--brand); line-height: 1;
  background: transparent; border: 0; border-radius: 0; box-shadow: none; padding: 0;
}
.brand-logo-svg { display: block; width: calc(var(--lockup-logo-height) * var(--lockup-logo-ratio)); height: var(--lockup-logo-height); flex: none; }
/* La valeur de repli n'est pas de la décoration : --lockup-tile n'est posé
   que par .brand-lockup, et une marque placée AILLEURS (l'en-tête du tiroir)
   voyait sa largeur devenir invalide, donc `auto` — 162 px de logo sur un
   téléphone de 375 (capture du propriétaire, 2026-09-12). */
.brand-mark { width: var(--lockup-tile, 28px); height: var(--lockup-tile, 28px); display: grid; place-items: center; }
.brand-mark img { display: block; width: 100%; height: 100%; }
/* Québec is a location signal, not a footnote: keep it readable in both themes. */
.brand-sub {
  display: inline-flex; align-items: center; flex: none; white-space: nowrap;
  color: var(--header-badge-ink); font-weight: 600; font-size: var(--lockup-badge-size); text-transform: uppercase;
  letter-spacing: .1em; padding: .3em .42em; background: var(--header-badge-bg); border-radius: var(--radius-xs);
}

/* ==========================================================================
   The brand splash (brand-splash.test.mjs, e2e/brand-splash.spec.js).
   The web counterpart of the video splash (output/video-scripts-2026-09-22/
   brand-splash/make_splash.py), same score at the short formats' speed:
   the signal seed sparks, the tile opens on the spring, the N is painted in
   three wipes, O-T-A rise, the period strikes, a second soft flash, then the
   QUÉBEC badge slides in. The <head> gate sets html[data-splash] to « play »
   or « still » (reduced motion); without it the overlay never displays. The
   drawing is the product's own: the inline script clones the two symbols
   into the named parts animated here (.splash-tile, .splash-n-left…).
   ========================================================================== */
.brand-splash { display: none; }
html[data-splash] .brand-splash {
  position: fixed; inset: 0; z-index: 12000; display: grid; place-items: center; overflow: hidden;
  /* Two rows, the lockup then the signature line, centred as ONE group. */
  align-content: center; row-gap: var(--space-16);
  background-color: var(--brand-canvas-bg);
  background-image: var(--brand-canvas-gradient);
  /* This scene keeps the selected master's navy ground even when the working
     page is light. Only its local ink and light effects use the night score. */
  --splash-ink: var(--brand-canvas-ink);
  --splash-light: var(--nota-blue-100);
  --splash-blend: screen;
  --splash-layer: auto;
  --splash-veil: 1;
  --splash-ambient-peak: 0.08;
  --brand-word-ink: var(--splash-ink);
  /* The score, in one place: when each gesture starts. */
  --splash-at-spark: 0ms; --splash-at-seed: 0ms; --splash-at-flash: 30ms; --splash-at-tile: 80ms;
  --splash-at-n-left: 240ms; --splash-at-n-diag: 300ms; --splash-at-n-right: 350ms;
  --splash-at-o: 490ms; --splash-at-t: 570ms; --splash-at-a: 640ms;
  --splash-at-period: 770ms; --splash-at-flash2: 850ms; --splash-at-badge: 1000ms;
  /* Each keyword finishes its arrival before the next one starts. */
  --splash-at-act: 1200ms;
  --splash-at-date: calc(var(--splash-at-act) + var(--motion-enter));
  --splash-at-price: calc(var(--splash-at-date) + var(--motion-enter));
}
/* Wide screens: the lockup commands the stage. */
.brand-splash-lockup {
  --splash-logo-scale: .9;
  --lockup-tile: calc(clamp(100px, 15vw, 240px) * var(--splash-logo-scale));
  --lockup-badge-gap: calc(var(--lockup-tile) * var(--logo-badge-gap-ratio));
  position: relative; display: flex; align-items: center; gap: var(--lockup-badge-gap);
}
.brand-splash .brand-sub {
  margin-inline-start: calc(var(--lockup-logo-height) * var(--logo-end-clearance-ratio) * -1);
  translate: 0 calc(var(--lockup-logo-height) * var(--logo-word-center-offset-ratio));
}
/* The selected artwork uses registered crops of the same canonical symbol.
   Keep the production score while preserving its pixels and light-theme tone. */
.brand-splash .brand-lockup { position: relative; }
.splash-logo-reveal { position: absolute; inset: 0; display: none; }
.splash-logo-reveal > svg { overflow: hidden; }
.splash-logo-reveal g { transform-box: view-box; transform-origin: center; }
html[data-splash='play'] .splash-logo-still { visibility: hidden; }
html[data-splash='play'] .splash-logo-reveal { display: block; }
html[data-splash='play'] .splash-n { animation: splash-pop var(--motion-enter) var(--ease-spring) var(--splash-at-tile) both; }
/* The signature line (owner, 2026-09-24; horizontal 2026-09-25): three words
   on one row — « Votre acte · Votre date · Votre prix » — separated by middle
   dots, each arriving on its own beat (staggered blur-to-sharp). */
.splash-line {
  --splash-keyword-bg: var(--nota-blue-800);
  --splash-keyword-border: var(--nota-blue-600);
  margin: 0;
  box-sizing: border-box;
  max-inline-size: calc(100vw - var(--space-8));
  white-space: nowrap;
  display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 0.45em;
  font-family: var(--font-display); font-weight: var(--weight-display);
  font-size: var(--type-h1); letter-spacing: var(--type-h1-ls); line-height: var(--type-h1-lh);
  color: var(--splash-ink);
}
.splash-line > span { display: inline-block; }
.splash-line > :is(.splash-act, .splash-date, .splash-price) {
  box-sizing: border-box;
  padding: var(--space-3) var(--space-5);
  border: var(--space-1) solid var(--splash-keyword-border);
  border-radius: var(--radius);
  background: var(--splash-keyword-bg);
}
.splash-sep { display: inline; color: var(--splash-signal); }
/* Owner's rule: on a portrait phone there is no room for QUÉBEC — the badge
   goes and « Nota » alone is centred, on a larger tile. Wider viewports keep it.
   The signature line steps down to --type-h2 there: French, the wider language,
   then holds on one line down to 320 px, about as wide as « Nota » itself. */
@media (max-width: 600px), (orientation: portrait) and (max-aspect-ratio: 5/8) {
  .brand-splash-lockup { --lockup-tile: calc(clamp(80px, 24vw, 140px) * var(--splash-logo-scale)); }
  .brand-splash .brand-sub { display: none; }
  .splash-line { font-size: var(--type-h2); letter-spacing: var(--type-h2-ls); }
}
/* The English legal label is longer than the French one. Keep all three
   beats intact with the dense-heading step on narrow phones. */
@media (max-width: 420px) {
  .splash-line { font-size: var(--type-h4); gap: var(--space-1); }
  .splash-line > :is(.splash-act, .splash-date, .splash-price) { padding-inline: var(--space-2); }
}

/* Light: blooms, anamorphic flares and a full-screen veil. On the night they
   are screened in front of the mark; on « le blanc » they are painted behind
   it and the veil is off (--splash-blend, --splash-layer, --splash-veil). */
.splash-bloom, .splash-flare, .splash-veil { position: absolute; pointer-events: none; opacity: 0; mix-blend-mode: var(--splash-blend); z-index: var(--splash-layer); }
.splash-bloom { translate: -50% -50%; background: radial-gradient(closest-side, var(--splash-light), color-mix(in srgb, var(--splash-light) 30%, transparent) 45%, transparent); }
/* The seed is the tile's signal square: centred at 75% / 25% of the tile. */
.splash-bloom--spark { left: calc(var(--lockup-tile) * .75); top: calc(50% - var(--lockup-tile) * .25); width: calc(var(--lockup-tile) * .8); height: calc(var(--lockup-tile) * .8); --peak: .9; }
.splash-bloom--flash { left: calc(var(--lockup-tile) * .75); top: calc(50% - var(--lockup-tile) * .25); width: calc(var(--lockup-tile) * 2); height: calc(var(--lockup-tile) * 1.9); --peak: .75; }
/* The second flash is centred on the whole mark (tile + gap + word). */
.splash-bloom--flash2, .splash-flare--mark { left: calc(var(--lockup-tile) * (1.12 + .48 * 91.8 / 28) / 2); top: 50%; }
.splash-bloom--flash2 { width: calc(var(--lockup-tile) * 3.1); height: calc(var(--lockup-tile) * 2.6); --peak: .5; }
.splash-flare { translate: -50% -50%; height: calc(var(--lockup-tile) * .11); width: calc(var(--lockup-tile) * 3.8);
  background: radial-gradient(closest-side, var(--splash-light), transparent), radial-gradient(closest-side, var(--splash-signal), transparent); }
.splash-flare--seed { left: calc(var(--lockup-tile) * .75); top: calc(50% - var(--lockup-tile) * .25); --peak: .9; }
.splash-flare--mark { --peak: .6; }
.splash-veil { inset: 0; background: var(--splash-light); --peak: calc(.16 * var(--splash-veil)); }
.splash-veil::after { content: ''; position: absolute; inset: 0; background: inherit; opacity: 0; --peak: calc(.12 * var(--splash-veil)); }

/* « play »: the score. Positions ride --ease-ui, the three things that SETTLE
   (seed, tile, period) ride the brand's one spring; light is a pulse. */
html[data-splash='play'] .brand-splash { animation: splash-out var(--motion-enter) var(--ease-ui) var(--splash-hold) forwards; }
html[data-splash='play'] .splash-seed { animation: splash-pop var(--motion-enter) var(--ease-spring) var(--splash-at-seed) both; }
html[data-splash='play'] .splash-tile { animation: splash-pop var(--motion-enter) var(--ease-spring) var(--splash-at-tile) both; }
html[data-splash='play'] .splash-n-left { animation: splash-wipe-down var(--motion-fast) var(--ease-ui) var(--splash-at-n-left) both; }
html[data-splash='play'] .splash-n-diag { animation: splash-wipe-right var(--motion-fast) var(--ease-ui) var(--splash-at-n-diag) both; }
html[data-splash='play'] .splash-n-right { animation: splash-wipe-down var(--motion-fast) var(--ease-ui) var(--splash-at-n-right) both; }
html[data-splash='play'] .splash-o { animation: splash-rise var(--motion-fast) var(--ease-ui) var(--splash-at-o) both; }
html[data-splash='play'] .splash-t { animation: splash-rise var(--motion-fast) var(--ease-ui) var(--splash-at-t) both; }
html[data-splash='play'] .splash-a { animation: splash-rise var(--motion-fast) var(--ease-ui) var(--splash-at-a) both; }
html[data-splash='play'] .splash-period { animation: splash-pop var(--motion-fast) var(--ease-spring) var(--splash-at-period) both; }
html[data-splash='play'] .brand-splash .brand-sub { animation: splash-slide var(--motion-ui) var(--ease-ui) var(--splash-at-badge) both; }
/* Each word and its own blue chip rise on one complete beat:
   « Votre acte » → « Votre date » → « Votre prix ». */
html[data-splash='play'] .splash-act { animation: splash-word-rise var(--motion-enter) var(--ease-ui) var(--splash-at-act) both; }
html[data-splash='play'] .splash-date { animation: splash-word-rise var(--motion-enter) var(--ease-ui) var(--splash-at-date) both; }
html[data-splash='play'] .splash-price { animation: splash-word-rise var(--motion-enter) var(--ease-ui) var(--splash-at-price) both; }
html[data-splash='play'] .splash-act + .splash-sep { animation: splash-pop var(--motion-fast) var(--ease-spring) var(--splash-at-act) both; }
html[data-splash='play'] .splash-date + .splash-sep { animation: splash-pop var(--motion-fast) var(--ease-spring) var(--splash-at-date) both; }
html[data-splash='play'] .splash-bloom--spark { animation: splash-pulse var(--motion-fast) var(--ease-ui) var(--splash-at-spark) both; }
html[data-splash='play'] .splash-bloom--flash { animation: splash-pulse var(--motion-enter) var(--ease-ui) var(--splash-at-flash) both; }
html[data-splash='play'] .splash-bloom--flash2 { animation: splash-pulse var(--motion-enter) var(--ease-ui) var(--splash-at-flash2) both; }
html[data-splash='play'] .splash-flare--seed { animation: splash-flare var(--motion-enter) var(--ease-ui) var(--splash-at-flash) both; }
html[data-splash='play'] .splash-flare--mark { animation: splash-flare var(--motion-enter) var(--ease-ui) var(--splash-at-flash2) both; }
html[data-splash='play'] .splash-veil { animation: splash-veil var(--motion-enter) var(--ease-ui) var(--splash-at-flash) both; }
html[data-splash='play'] .splash-veil::after { animation: splash-veil var(--motion-enter) var(--ease-ui) var(--splash-at-flash2) both; }
/* « still » (prefers-reduced-motion): the lockup, complete and motionless,
   for one short beat — then it is simply gone. The zero-length step is the
   CSS safety net behind the script's own timer. */
html[data-splash='still'] .brand-splash { animation: splash-gone 0s var(--splash-still) forwards; }
/* A registered scene (index.html NotaSplash.run, data-scene) owns the host once
   mounted: its panels are the whole ground so they can part over the page, and
   its own score is the exit. The stage paint and the play / still steps above
   (splash-out, splash-gone) belong to the unmounted markup; on a scene they
   faded the tuile before its coupe exit, and hid its reduced-motion still. */
html[data-splash='play'] .brand-splash[data-scene],
html[data-splash='still'] .brand-splash[data-scene] { background: none; animation-name: none; }

@keyframes splash-pop { from { transform: scale(0); } to { transform: none; } }
@keyframes splash-wipe-down { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0); } }
@keyframes splash-wipe-right { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0); } }
@keyframes splash-rise { from { opacity: 0; transform: translateY(60%) scale(.92); } to { opacity: 1; transform: none; } }
/* The punchline entrance: each word rises from below, blurred out, then
   sharpens into focus — a cinematic reveal that draws the eye left to right
   as the three beats land. */
@keyframes splash-word-rise { from { opacity: 0; transform: translateY(40%) scale(.94); filter: blur(8px); } to { opacity: 1; transform: none; filter: blur(0); } }
@keyframes splash-slide { from { opacity: 0; transform: translateX(calc(var(--lockup-tile) * .35)); } to { opacity: 1; transform: none; } }
@keyframes splash-pulse { 0% { opacity: 0; transform: scale(.7); } 26% { opacity: var(--peak, 1); } 100% { opacity: 0; transform: scale(1.1); } }
@keyframes splash-flare { 0% { opacity: 0; transform: scaleX(.2); } 26% { opacity: var(--peak, 1); } 100% { opacity: 0; transform: scaleX(1); } }
/* The veil covers the whole stage, so it only breathes: scaled from 70 % like
   a bloom, it showed its four edges — a hard rectangle in the night at 40 ms. */
@keyframes splash-veil { 0%, 100% { opacity: 0; } 26% { opacity: var(--peak, 1); } }
@keyframes splash-out { to { opacity: 0; visibility: hidden; } }
@keyframes splash-gone { to { visibility: hidden; } }

/* The ambient layer: two large, soft radial gradients drift slowly behind the
   lockup, plus a full-screen breathing glow that ties the stage together.
   Each gradient orbits a different centre at a different period (24 s / 30 s /
   18 s), so the overlap never repeats visibly. The peak opacity stays low
   enough to read as atmosphere, not content — an aurora behind the word. */
.splash-ambient {
  position: absolute; pointer-events: none;
  opacity: 0;
  mix-blend-mode: var(--splash-blend);
  z-index: var(--splash-layer);
}
.splash-ambient--a {
  width: clamp(400px, 65vw, 900px); height: clamp(400px, 65vw, 900px);
  left: 18%; top: 35%;
  translate: -50% -50%;
  background: radial-gradient(closest-side, var(--splash-light), transparent 70%);
}
.splash-ambient--b {
  width: clamp(320px, 50vw, 680px); height: clamp(320px, 50vw, 680px);
  left: 76%; top: 60%;
  translate: -50% -50%;
  background: radial-gradient(closest-side, var(--splash-signal), transparent 70%);
}
.splash-ambient--c {
  inset: 0;
  background: radial-gradient(ellipse at 50% 48%, var(--splash-glow), transparent 72%);
}
html[data-splash='play'] .splash-ambient--a {
  animation: splash-ambient-1 24s var(--ease-ui) infinite;
}
html[data-splash='play'] .splash-ambient--b {
  animation: splash-ambient-2 30s var(--ease-ui) infinite;
}
html[data-splash='play'] .splash-ambient--c {
  animation: splash-ambient-3 18s var(--ease-ui) infinite;
}
@keyframes splash-ambient-1 {
  0%, 100% { opacity: 0; transform: translate(0, 0) scale(1); }
  25% { opacity: var(--splash-ambient-peak, 0.12); }
  50% { opacity: 0.06; transform: translate(25px, -18px) scale(1.06); }
  75% { opacity: var(--splash-ambient-peak, 0.12); }
}
@keyframes splash-ambient-2 {
  0%, 100% { opacity: 0; transform: translate(0, 0) scale(1); }
  30% { opacity: var(--splash-ambient-peak, 0.12); }
  60% { opacity: 0.04; transform: translate(-22px, 14px) scale(0.95); }
  80% { opacity: var(--splash-ambient-peak, 0.12); }
}
@keyframes splash-ambient-3 {
  0%, 100% { opacity: 0; }
  50% { opacity: var(--splash-ambient-peak, 0.08); }
}

/* s14 tokens — scoped to the stage element (not :root), because --lockup-logo-width
   has a different value here (splash-sized) than the header's --logo-header-tile. */
.s14 {
  /* Lockup sizing (splash scale, from the prototype's tokens.css). */
  --splash-logo-scale: .9;
  --lockup-tile: calc(clamp(100px, 15vw, 240px) * var(--splash-logo-scale));
  --lockup-logo-height: calc(var(--lockup-tile) * var(--logo-height-ratio));
  --lockup-logo-width: calc(var(--lockup-logo-height) * var(--logo-ratio));
  --lockup-badge-gap: calc(var(--lockup-tile) * var(--logo-badge-gap-ratio));
  /* The stage is always dark (navy canvas), regardless of the page theme. */
  --splash-ink: var(--brand-canvas-ink);
  --splash-light: var(--nota-blue-100);
  --splash-keyword-bg: var(--nota-blue-800);
  --splash-keyword-border: var(--nota-blue-600);
  --brand-word-ink: var(--splash-ink);

  /* The cut, in master pixels (identity.json registry). */
  --s14-x1: 1568; --s14-y1: 340; --s14-x2: 1478; --s14-y2: 590;
  --s14-master-w: 2189; --s14-master-h: 728;
  --s14-k: calc((var(--s14-x1) - var(--s14-x2)) / (var(--s14-y2) - var(--s14-y1)));
  --s14-angle: atan(var(--s14-k));
  --s14-joint-x: calc((var(--s14-x1) + var(--s14-x2)) / 2);
  --s14-joint-y: calc((var(--s14-y1) + var(--s14-y2)) / 2);
  --s14-cut-at-top: calc((var(--s14-x1) + var(--s14-k) * var(--s14-y1)) / var(--s14-master-w) * 100%);
  --s14-cut-at-bot: calc((var(--s14-x1) - var(--s14-k) * (var(--s14-master-h) - var(--s14-y1))) / var(--s14-master-w) * 100%);
  --s14-ink-l: 178; --s14-ink-r: 2012; --s14-ink-t: 91;
  --s14-sig-x0: 574; --s14-sig-x1: 714; --s14-sig-y0: 179; --s14-sig-y1: 308; --s14-sig-r: 11;
  --s14-dot-x0: 1909; --s14-dot-y0: 490; --s14-dot-w: 111; --s14-dot-h: 102;
  --s14-optic: calc(.5 - (var(--s14-ink-l) + var(--s14-ink-r)) / 2 / var(--s14-master-w));
  --s14-train-l: 70%;
  --s14-train-r: -22.5%;
  --s14-blade-w: 3px;
  --s14-blade: var(--brand);

  /* Curves: named linear() samples plus the kit's --ease-ui. */
  --s14-ease-slide: linear(0, .09 8%, .26 18%, .48 30%, .7 44%, .87 58%, .97 72%, 1.008 86%, 1);
  --s14-ease-seal: linear(0, .03 20%, .12 40%, .3 60%, .58 80%, .9 95%, 1);
  --s14-ease-gleam: linear(0, .2 8%, .37 16%, .52 25%, .64 34%, .745 44%, .83 54%, .9 65%, .95 77%, .985 89%, 1);
  --s14-ease-grow: linear(0, .1 6%, .22 13%, .36 22%, .5 32%, .63 43%, .75 55%, .85 68%, .93 81%, .98 92%, 1);
  --s14-ease-cut: linear(0, 0.003, 0.011, 0.027, 0.054, 0.096, 0.161, 0.264, 0.415, 0.579, 0.705, 0.792, 0.854, 0.899, 0.932, 0.956, 0.974, 0.986, 0.994, 0.999, 1);
  --s14-ease-exit: linear(0, .03 20%, .12 40%, .3 60%, .62 80%, 1);

  /* The grid (« trame »): square cells, major every 5, crosses on majors. */
  --s14-cell: 24px;
  --s14-major-n: 5;
  --s14-major: calc(var(--s14-cell) * var(--s14-major-n));
  --s14-arm: 4px;
  --s14-feather: 96px;
  --s14-minor-ink: rgb(var(--nota-blue-400-rgb) / .05);
  --s14-major-ink: rgb(var(--nota-blue-400-rgb) / .085);
  --s14-tick-ink: rgb(var(--nota-blue-400-rgb) / .17);
  --s14-grain-a: .055;
  --s14-on: var(--paper);
  --s14-off: transparent;
  --s14-edge: color-mix(in srgb, var(--s14-on) 45%, transparent);
  --s14-edge-w: var(--space-16);
  --s14-focus-kx: 1.85;
  --s14-focus-ky: 2.5;
  --s14-soft-blur: 1.5px;
  --s14-minor-soft: rgb(var(--nota-blue-400-rgb) / .034);
  --s14-major-soft: rgb(var(--nota-blue-400-rgb) / .052);
  --s14-tick-soft: rgb(var(--nota-blue-400-rgb) / .07);
  --s14-m04: color-mix(in srgb, var(--s14-on) 4.3%, transparent);
  --s14-m16: color-mix(in srgb, var(--s14-on) 15.6%, transparent);
  --s14-m32: color-mix(in srgb, var(--s14-on) 31.6%, transparent);
  --s14-m50: color-mix(in srgb, var(--s14-on) 50%, transparent);
  --s14-m68: color-mix(in srgb, var(--s14-on) 68.4%, transparent);
  --s14-m84: color-mix(in srgb, var(--s14-on) 84.4%, transparent);
  --s14-m96: color-mix(in srgb, var(--s14-on) 95.7%, transparent);
  --s14-gm0: color-mix(in srgb, var(--s14-on) 55%, transparent);
  --s14-gm04: color-mix(in srgb, var(--s14-on) 56.9%, transparent);
  --s14-gm16: color-mix(in srgb, var(--s14-on) 62%, transparent);
  --s14-gm32: color-mix(in srgb, var(--s14-on) 69.2%, transparent);
  --s14-gm50: color-mix(in srgb, var(--s14-on) 77.5%, transparent);
  --s14-gm68: color-mix(in srgb, var(--s14-on) 85.8%, transparent);
  --s14-gm84: color-mix(in srgb, var(--s14-on) 93%, transparent);
  --s14-gm96: color-mix(in srgb, var(--s14-on) 98%, transparent);
  /* The stage's edge shade: a navy deeper than --brand-canvas-bg, so the vignette
     darkens even the bare canvas. Theme-independent, like the stage itself. */
  --s14-vignette: rgb(10 20 35 / .5);

  /* The gleam: specular band at the cut angle, masked by the master's alpha. */
  --s14-gw: calc(var(--lockup-logo-width) * .09);
  --s14-gleam-wing: color-mix(in srgb, var(--splash-light) 9%, transparent);
  --s14-gleam-hi: color-mix(in srgb, var(--splash-light) 22%, transparent);
  --s14-gleam-core: color-mix(in srgb, var(--paper) 46%, transparent);
  --s14-blink: color-mix(in srgb, var(--paper) 40%, transparent);
  --s14-band-0: calc(var(--lockup-logo-width) * var(--s14-joint-x) / var(--s14-master-w) - var(--s14-gw) / 2);
  --s14-band-1: calc(var(--lockup-logo-width) * var(--s14-ink-l) / var(--s14-master-w) - var(--s14-k) * (var(--s14-joint-y) - var(--s14-ink-t)) / var(--s14-master-h) * var(--lockup-logo-height) - var(--s14-gw) - var(--space-2));

  /* Score timing, chained from motion tokens (no literal delay over 1 s). */
  --s14-t-cut: 80ms;
  --s14-d-cut: calc(var(--motion-enter) * .875);
  --s14-t-grid: 120ms;
  --s14-d-grid: calc(var(--motion-enter) * 3.6875);
  --s14-t-left: 160ms;
  --s14-d-left: calc(var(--motion-enter) * 2.6875);
  --s14-t-land: calc(var(--s14-t-left) + var(--s14-d-left));
  --s14-d-seal: calc(var(--motion-enter) * 1.3125);
  --s14-t-seal: calc(var(--s14-t-land) - var(--s14-d-seal));
  --s14-t-release: calc(var(--s14-t-land) + 40ms);
  --s14-t-gleam: var(--s14-t-land);
  --s14-d-gleam: calc(var(--motion-enter) * 1.1875);
  --s14-d-blink: calc(var(--motion-fast) * 1.2);
  --s14-blink-at: .312;
  --s14-blink-peak: .28;
  --s14-t-blink: calc(var(--s14-t-gleam) + var(--s14-d-gleam) * var(--s14-blink-at) - var(--s14-d-blink) * var(--s14-blink-peak));
  --s14-t-kw1: calc(var(--s14-t-land) + 280ms);
  --s14-kw-step: 180ms;
  --s14-d-kw: calc(var(--motion-enter) * 1.0625);
  --s14-t-kw2: calc(var(--s14-t-kw1) + var(--s14-kw-step));
  --s14-t-kw3: calc(var(--s14-t-kw2) + var(--s14-kw-step));
  --s14-t-badge: calc(var(--s14-t-kw1) + 120ms);
  --s14-t-rest: calc(var(--s14-t-kw3) + var(--s14-d-kw));
  --s14-hold: 960ms;
  --s14-t-out: calc(var(--s14-t-rest) + var(--s14-hold));
  --s14-d-reopen: var(--motion-ui);
  --s14-t-home: calc(var(--s14-t-out) + 40ms);
  --s14-d-home: calc(var(--motion-enter) * 2);
  --s14-part-lag: .5;
  --s14-t-part: calc(var(--s14-t-home) + var(--s14-d-home) * var(--s14-part-lag));
  --s14-d-part: calc(var(--motion-enter) * 1.75);

  /* Fallback geometry until measure() snaps real values. */
  --s14-cut-top: calc(50vw + 18vh); --s14-cut-bot: calc(50vw - 18vh); --s14-seam: .45;
  --s14-nx: 0px; --s14-ny: 0px;
  --s14-gx: 50vw; --s14-gy: 50vh;
  --s14-fcx: 50vw; --s14-fcy: 50vh; --s14-fhw: 22vw; --s14-fhh: 16vh;
  --s14-bs: 1px; --s14-ma0: -9999px 0px; --s14-mb0: -9999px 0px; --s14-ma1: -9999px 0px; --s14-mb1: -9999px 0px;
  --s14-fx: 0px; --s14-fy: 0px; --s14-fs: 1;
  --s14-bx: 0px; --s14-by: 0px; --s14-bsc: 1;
}

/* Engines without CSS trigonometry (pre-2023) get the same angle, precomputed. */
@supports not (rotate: atan(1)) { .s14 { --s14-angle: 19.799deg; } }

/* Default hidden; the gate script sets [data-splash] to show. */
.s14 { display: none; }
.s14[data-splash] {
  position: fixed; inset: 0; z-index: 12000; display: grid; place-items: center; overflow: hidden;
  background: none;
}

/* Stage panels: the canvas is painted by the two panels, so they can part.
   Texture layers: dot grid, SVG fractalNoise, brand gradient — each panel carries the same
   stack so the two halves match at the seam. */
.s14-panel { position: absolute; inset: 0; pointer-events: none; will-change: transform;
  background-color: var(--brand-canvas-bg);
  background-image:
    radial-gradient(circle, rgb(var(--nota-blue-400-rgb) / 0.06) 1px, transparent 1px),
    url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.08'/%3E%3C/svg%3E"),
    var(--brand-canvas-gradient);
  background-size: 24px 24px, 256px 256px, 100% 100%;
  background-position: 0 0, 0 0, center;
  background-repeat: repeat, repeat, no-repeat; }
.s14-panel::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: 0.8;
  background-image:
    repeating-linear-gradient(45deg, transparent, transparent 11px, rgb(var(--nota-blue-400-rgb) / 0.03) 11px, rgb(var(--nota-blue-400-rgb) / 0.03) 12px),
    repeating-linear-gradient(-45deg, transparent, transparent 11px, rgb(var(--nota-blue-400-rgb) / 0.03) 11px, rgb(var(--nota-blue-400-rgb) / 0.03) 12px); }
.s14-panel::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 40%, var(--s14-vignette) 100%); }
.s14-panel--l { clip-path: polygon(calc(min(0px, var(--s14-cut-bot)) - 4px) 0, calc(var(--s14-cut-top) + 2px) 0,
  calc(var(--s14-cut-bot) + 2px) 100%, calc(min(0px, var(--s14-cut-bot)) - 4px) 100%); }
.s14-panel--r { clip-path: polygon(var(--s14-cut-top) 0, calc(max(100%, var(--s14-cut-top)) + 4px) 0,
  calc(max(100%, var(--s14-cut-top)) + 4px) 100%, var(--s14-cut-bot) 100%); }

/* Grid birth: two half-planes parallel to the cut, feathered edges, moving apart. */
.s14-grid { position: absolute; inset: 0; pointer-events: none; will-change: transform;
  mask-image:
    linear-gradient(calc(90deg + var(--s14-angle)), var(--s14-off) 50%, var(--s14-on) calc(50% + var(--s14-feather))),
    linear-gradient(calc(90deg + var(--s14-angle)), var(--s14-on) calc(50% - var(--s14-feather)), var(--s14-off) 50%);
  mask-size: var(--s14-bs) var(--s14-bs);
  mask-repeat: no-repeat;
  mask-position: var(--s14-ma0), var(--s14-mb0);
  mask-composite: intersect; }
.s14-mesh { position: absolute; inset: 0; }
.s14-mesh.is-sharp {
  mask-image:
    radial-gradient(calc(var(--s14-fhw) * var(--s14-focus-kx)) calc(var(--s14-fhh) * var(--s14-focus-ky)) at var(--s14-fcx) var(--s14-fcy),
      var(--s14-off) 0 40%, var(--s14-m04) 47.5%, var(--s14-m16) 55%, var(--s14-m32) 62.5%, var(--s14-m50) 70%,
      var(--s14-m68) 77.5%, var(--s14-m84) 85%, var(--s14-m96) 92.5%, var(--s14-on) 100%),
    linear-gradient(to right, var(--s14-edge), var(--s14-on) var(--s14-edge-w), var(--s14-on) calc(100% - var(--s14-edge-w)), var(--s14-edge)),
    linear-gradient(to bottom, var(--s14-edge), var(--s14-on) var(--s14-edge-w), var(--s14-on) calc(100% - var(--s14-edge-w)), var(--s14-edge));
  mask-composite: intersect; }
.s14-mesh.is-soft { --s14-minor-ink: var(--s14-minor-soft); --s14-major-ink: var(--s14-major-soft); --s14-tick-ink: var(--s14-tick-soft);
  filter: blur(var(--s14-soft-blur)); will-change: transform;
  mask-image: radial-gradient(calc(var(--s14-fhw) * var(--s14-focus-kx)) calc(var(--s14-fhh) * var(--s14-focus-ky)) at var(--s14-fcx) var(--s14-fcy),
    var(--s14-on) 0 40%, var(--s14-m96) 47.5%, var(--s14-m84) 55%, var(--s14-m68) 62.5%, var(--s14-m50) 70%,
    var(--s14-m32) 77.5%, var(--s14-m16) 85%, var(--s14-m04) 92.5%, var(--s14-off) 100%); }
.s14-lines, .s14-ticks, .s14-ticks::before, .s14-ticks::after { position: absolute; inset: 0; }
.s14-lines {
  transform: rotate(var(--s14-angle)) scale(1.5);
  transform-origin: calc(var(--s14-joint-x) / var(--s14-master-w) * 100%)
                    calc(var(--s14-joint-y) / var(--s14-master-h) * 100%);
  background-image:
    linear-gradient(to bottom, var(--s14-major-ink) 0 1px, var(--s14-off) 1px),
    linear-gradient(to right, var(--s14-major-ink) 0 1px, var(--s14-off) 1px),
    linear-gradient(to bottom, var(--s14-minor-ink) 0 1px, var(--s14-off) 1px),
    linear-gradient(to right, var(--s14-minor-ink) 0 1px, var(--s14-off) 1px);
  background-size: 100% var(--s14-major), var(--s14-major) 100%, 100% var(--s14-cell), var(--s14-cell) 100%;
  background-position: var(--s14-gx) var(--s14-gy); }
.s14-ticks {
  transform: rotate(var(--s14-angle)) scale(1.5);
  transform-origin: calc(var(--s14-joint-x) / var(--s14-master-w) * 100%)
                    calc(var(--s14-joint-y) / var(--s14-master-h) * 100%); }
.s14-ticks::before { content: '';
  background-image: linear-gradient(to right, var(--s14-tick-ink) 0 1px, var(--s14-off) 1px);
  background-size: var(--s14-major) var(--s14-major); background-position: var(--s14-gx) 0;
  mask-image: linear-gradient(to bottom, var(--s14-on) 0 calc(var(--s14-arm) * 2 + 1px), var(--s14-off) 0);
  mask-size: 100% var(--s14-major); mask-position: 0 calc(var(--s14-gy) - var(--s14-arm)); }
.s14-ticks::after { content: '';
  background-image: linear-gradient(to bottom, var(--s14-tick-ink) 0 1px, var(--s14-off) 1px);
  background-size: var(--s14-major) var(--s14-major); background-position: 0 var(--s14-gy);
  mask-image: linear-gradient(to right, var(--s14-on) 0 calc(var(--s14-arm) * 2 + 1px), var(--s14-off) 0);
  mask-size: var(--s14-major) 100%; mask-position: calc(var(--s14-gx) - var(--s14-arm)) 0; }

/* Film grain: SVG feTurbulence, static, one render per panel. */
.s14-grain { position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  opacity: var(--s14-grain-a); pointer-events: none; will-change: transform;
  mask-image: radial-gradient(calc(var(--s14-fhw) * var(--s14-focus-kx)) calc(var(--s14-fhh) * var(--s14-focus-ky)) at var(--s14-fcx) var(--s14-fcy),
    var(--s14-gm0) 0 40%, var(--s14-gm04) 47.5%, var(--s14-gm16) 55%, var(--s14-gm32) 62.5%, var(--s14-gm50) 70%,
    var(--s14-gm68) 77.5%, var(--s14-gm84) 85%, var(--s14-gm96) 92.5%, var(--s14-on) 100%); }

/* The blade: a line along the cut, clip-path polygon per state. */
.s14-blade { position: absolute; inset: 0; pointer-events: none; background: var(--s14-blade);
  --bw: var(--s14-blade-w); --bdx: 0px;
  --bx0: calc(var(--s14-cut-top) + var(--bdx));
  --bx1: calc(var(--s14-cut-bot) + var(--bdx));
  --bxm: calc(var(--s14-cut-top) + (var(--s14-cut-bot) - var(--s14-cut-top)) * var(--s14-seam) + var(--bdx));
  --bym: calc(var(--s14-seam) * 100%);
  --seg-mm: polygon(var(--bxm) var(--bym), calc(var(--bxm) + var(--bw)) var(--bym), calc(var(--bxm) + var(--bw)) var(--bym), var(--bxm) var(--bym));
  --seg-0m: polygon(var(--bx0) 0, calc(var(--bx0) + var(--bw)) 0, calc(var(--bxm) + var(--bw)) var(--bym), var(--bxm) var(--bym));
  --seg-m1: polygon(var(--bxm) var(--bym), calc(var(--bxm) + var(--bw)) var(--bym), calc(var(--bx1) + var(--bw)) 100%, var(--bx1) 100%);
  --seg-01: polygon(var(--bx0) 0, calc(var(--bx0) + var(--bw)) 0, calc(var(--bx1) + var(--bw)) 100%, var(--bx1) 100%);
  clip-path: var(--seg-mm); }
.s14-blade--lip { --bdx: calc(-1 * var(--s14-blade-w)); }

/* Column, row, logo, sides. */
.s14-col { position: relative; z-index: 2; display: grid; justify-items: center; gap: clamp(18px, 3vh, 34px); }
.s14-row { position: relative; left: calc(var(--lockup-logo-width) * var(--s14-optic) + var(--s14-nx)); top: var(--s14-ny); }
.s14-logo { position: relative; width: var(--lockup-logo-width); aspect-ratio: 2189 / 728; transform-origin: 0 0; }
.s14-side { position: absolute; inset: 0; }

/* Lockup parts: positioned windows of ONE master (identity.json). */
.s14 .lk { position: relative; width: var(--lockup-logo-width); aspect-ratio: 2189 / 728; }
.s14 .lk > img { position: absolute; top: 0; height: 100%; width: auto; max-width: none; user-select: none; }
.s14 .lk-n      { left: 0;        width: 38.3737% !important; transform-origin: 59.6% 52.2%; }
.s14 .lk-o      { left: 38.3737%; width: 18.3189% !important; transform-origin: 50% 57%; }
.s14 .lk-t      { left: 56.6926%; width: 14.9383% !important; transform-origin: 50% 57%; }
.s14 .lk-a      { left: 67.5194%; width: 19.5523% !important; transform-origin: 56% 57%; }
.s14 .lk-period { left: 87.0717%; width: 12.9283% !important; transform-origin: 20.5% 74%; }

/* The gleam: masked by the master's alpha; the band is skewed to the cut. */
.s14-gleam { position: absolute; inset: 0; pointer-events: none; mix-blend-mode: screen;
  mask-image: url(assets/logo-full.webp); mask-size: 100% 100%; mask-repeat: no-repeat; }
.s14-band { position: absolute; top: 0; left: 0; height: 100%; width: var(--s14-gw);
  background-image: linear-gradient(to right, var(--s14-off), var(--s14-gleam-wing) 30%, var(--s14-gleam-hi) 45%,
    var(--s14-gleam-core) 48.5%, var(--s14-gleam-core) 51.5%, var(--s14-gleam-hi) 55%, var(--s14-gleam-wing) 70%, var(--s14-off));
  transform: skewX(calc(-1 * var(--s14-angle)));
  transform-origin: 50% calc(var(--s14-joint-y) / var(--s14-master-h) * 100%);
  translate: var(--s14-band-0) 0; opacity: 0; }

/* The blink: signal and period, masked overlays. */
.s14-blink { position: absolute; pointer-events: none; opacity: 0; background: var(--s14-blink); mix-blend-mode: screen; }
.s14-blink--sig {
  left: calc(var(--s14-sig-x0) / var(--s14-master-w) * 100%);
  top: calc(var(--s14-sig-y0) / var(--s14-master-h) * 100%);
  width: calc((var(--s14-sig-x1) - var(--s14-sig-x0)) / var(--s14-master-w) * 100%);
  height: calc((var(--s14-sig-y1) - var(--s14-sig-y0)) / var(--s14-master-h) * 100%);
  border-radius: calc(var(--s14-sig-r) / (var(--s14-sig-x1) - var(--s14-sig-x0)) * 100%) / calc(var(--s14-sig-r) / (var(--s14-sig-y1) - var(--s14-sig-y0)) * 100%); }
.s14-blink--dot {
  left: calc(var(--s14-dot-x0) / var(--s14-master-w) * 100%);
  top: calc(var(--s14-dot-y0) / var(--s14-master-h) * 100%);
  width: calc(var(--s14-dot-w) / var(--s14-master-w) * 100%);
  height: calc(var(--s14-dot-h) / var(--s14-master-h) * 100%);
  mask-image: url(assets/terminal-full.webp); mask-size: 100% 100%; mask-repeat: no-repeat; }

/* QUÉBEC badge: outside the row's flow, flies to #page-badge (FLIP). */
.s14-bfly { position: absolute; left: 100%; top: calc(50% + var(--lockup-logo-height) * var(--logo-word-center-offset-ratio));
  margin-top: -.8em; margin-inline-start: calc(var(--lockup-tile) * -.18); transform-origin: 0 0; white-space: nowrap;
  font: 600 max(8px, calc(var(--lockup-tile) * .135))/1 var(--font-sans);
  --chip-s: calc(var(--s14-k) * 1.6em);
  clip-path: polygon(calc(-1 * var(--chip-s)) 0, calc(100% + var(--chip-s) + 2px) 0, calc(100% + 2px) 100%, calc(-1 * var(--chip-s)) 100%); }
.s14-badge { display: block; letter-spacing: .1em; text-transform: uppercase; padding: .3em .42em;
  border-radius: var(--radius-xs); background: var(--header-badge-bg); color: var(--header-badge-ink); }
@media not all and (min-width: 901px) and (min-aspect-ratio: 5/8) { .s14-bfly { display: none; } }

/* Keyword chips: each in a slot whose right edge is a static slant at the cut angle. */
.s14 .kw { -webkit-font-smoothing: antialiased; font-kerning: normal;
  display: flex; align-items: center; justify-content: center; flex-wrap: nowrap; gap: var(--space-6);
  font: var(--weight-display) var(--type-h1)/1 var(--font-display); letter-spacing: var(--type-h1-ls);
  color: var(--splash-ink); white-space: nowrap; }
.s14 .kw-chip { padding: var(--space-3) var(--space-5); border: var(--space-1) solid var(--splash-keyword-border);
  border-radius: var(--radius); background: var(--splash-keyword-bg); }
.s14 .kw-sep { color: var(--splash-signal); }
.s14-slot { display: block; --chip-s: calc(var(--s14-k) * (1em + var(--space-8)));
  clip-path: polygon(calc(-1 * var(--chip-s)) 0, calc(100% + var(--chip-s) + 2px) 0, calc(100% + 2px) 100%, calc(-1 * var(--chip-s)) 100%); }
.s14-slot--sep { --chip-s: calc(var(--s14-k) * 1em); }
.s14 .kw-chip, .s14 .kw-sep { display: block; }

/* Visibility: nothing shows before the score starts; header logo hidden during play. */
.s14-col { visibility: hidden; }
.s14-playing .s14-col { visibility: visible; }

/* ---- Score ---- */
.s14-playing .s14-cut--up { animation:
  s14-open-up var(--s14-d-cut) var(--ease-ui) var(--s14-t-cut) both,
  s14-seal-up var(--s14-d-seal) var(--s14-ease-seal) var(--s14-t-seal) forwards; }
.s14-playing .s14-cut--dn { animation:
  s14-open-dn var(--s14-d-cut) var(--ease-ui) var(--s14-t-cut) both,
  s14-seal-dn var(--s14-d-seal) var(--s14-ease-seal) var(--s14-t-seal) forwards; }
.s14-playing .s14-grid { animation: s14-birth var(--s14-d-grid) var(--s14-ease-grow) var(--s14-t-grid) both; }
.s14-playing .s14-side--l { animation: s14-slit-l 1ms var(--ease-ui) var(--s14-t-release) both; }
.s14-playing .s14-side--r { animation: s14-slit-r 1ms var(--ease-ui) var(--s14-t-release) both; }
.s14-playing .s14-train--l { animation: s14-train-l var(--s14-d-left) var(--s14-ease-slide) var(--s14-t-left) both; }
.s14-playing .s14-train--r { animation: s14-train-r var(--s14-d-left) var(--s14-ease-slide) var(--s14-t-left) both; }
.s14-playing .s14-band { animation: s14-sweep var(--s14-d-gleam) var(--s14-ease-gleam) var(--s14-t-gleam),
  s14-lit var(--s14-d-gleam) var(--ease-ui) var(--s14-t-gleam); }
.s14-playing .s14-blink { animation: s14-blink var(--s14-d-blink) var(--ease-ui) var(--s14-t-blink) both; }
.s14-playing .kw-1 { animation: s14-emerge var(--s14-d-kw) var(--ease-ui) var(--s14-t-kw1) both,
  s14-retract var(--motion-fast) var(--s14-ease-exit) var(--s14-t-out) forwards; }
.s14-playing .sep-1 { animation: s14-emerge var(--motion-fast) var(--ease-ui) var(--s14-t-kw2) both,
  s14-retract var(--motion-fast) var(--s14-ease-exit) var(--s14-t-out) forwards; }
.s14-playing .kw-2 { animation: s14-emerge var(--s14-d-kw) var(--ease-ui) var(--s14-t-kw2) both,
  s14-retract var(--motion-fast) var(--s14-ease-exit) var(--s14-t-out) forwards; }
.s14-playing .sep-2 { animation: s14-emerge var(--motion-fast) var(--ease-ui) var(--s14-t-kw3) both,
  s14-retract var(--motion-fast) var(--s14-ease-exit) var(--s14-t-out) forwards; }
.s14-playing .kw-3 { animation: s14-emerge var(--s14-d-kw) var(--ease-ui) var(--s14-t-kw3) both,
  s14-retract var(--motion-fast) var(--s14-ease-exit) var(--s14-t-out) forwards; }
.s14-playing .s14-badge { animation: s14-emerge var(--motion-enter) var(--ease-ui) var(--s14-t-badge) both; }

/* Exit: cut reopens, lockup lifts, stage parts. */
.s14-playing .s14-blade--out { animation: s14-open-all var(--s14-d-reopen) var(--ease-ui) var(--s14-t-out) both; }
.s14-playing .s14-blade--lip { animation: s14-open-all var(--motion-fast) var(--ease-ui) var(--s14-t-part) both; }
.s14-playing .s14-panel--l { animation: s14-part-l var(--s14-d-part) var(--s14-ease-cut) var(--s14-t-part) both; }
.s14-playing .s14-panel--r { animation: s14-part-r var(--s14-d-part) var(--s14-ease-cut) var(--s14-t-part) both; }
.s14-playing .s14-logo { animation: s14-home var(--s14-d-home) var(--s14-ease-cut) var(--s14-t-home) both; }
.s14-playing .s14-bfly { animation: s14-home-b var(--s14-d-home) var(--s14-ease-cut) var(--s14-t-home) both; }

@keyframes s14-open-up { from { clip-path: var(--seg-mm); } to { clip-path: var(--seg-0m); } }
@keyframes s14-open-dn { from { clip-path: var(--seg-mm); } to { clip-path: var(--seg-m1); } }
@keyframes s14-seal-up { from { clip-path: var(--seg-0m); } to { clip-path: var(--seg-mm); } }
@keyframes s14-seal-dn { from { clip-path: var(--seg-m1); } to { clip-path: var(--seg-mm); } }
@keyframes s14-open-all { from { clip-path: var(--seg-mm); } to { clip-path: var(--seg-01); } }
@keyframes s14-birth { from { mask-position: var(--s14-ma0), var(--s14-mb0); } to { mask-position: var(--s14-ma1), var(--s14-mb1); } }
@keyframes s14-slit-l {
  from { clip-path: polygon(-4% 0, var(--s14-cut-at-top) 0, var(--s14-cut-at-bot) 100%, -4% 100%); }
  to { clip-path: polygon(-4% 0, 104% 0, 104% 100%, -4% 100%); } }
@keyframes s14-slit-r {
  from { clip-path: polygon(var(--s14-cut-at-top) 0, 104% 0, 104% 100%, var(--s14-cut-at-bot) 100%); }
  to { clip-path: polygon(-4% 0, 104% 0, 104% 100%, -4% 100%); } }
@keyframes s14-train-l { from { translate: var(--s14-train-l) 0; } to { translate: 0 0; } }
@keyframes s14-train-r { from { translate: var(--s14-train-r) 0; } to { translate: 0 0; } }
@keyframes s14-sweep { from { translate: var(--s14-band-0) 0; } to { translate: var(--s14-band-1) 0; } }
@keyframes s14-lit { from, to { opacity: 1; } }
@keyframes s14-blink { 0% { opacity: 0; } 28% { opacity: 1; } 100% { opacity: 0; } }
@keyframes s14-emerge { from { translate: calc(100% + var(--chip-s) + 2px) 0; } to { translate: 0 0; } }
@keyframes s14-retract { from { translate: 0 0; } to { translate: calc(100% + var(--chip-s) + 2px) 0; } }
@keyframes s14-part-l { from { translate: 0 0; } to { translate: calc(-1 * var(--s14-cut-top) - var(--s14-blade-w) - 12px) 0; } }
@keyframes s14-part-r { from { translate: 0 0; } to { translate: calc(100% - var(--s14-cut-bot) + var(--s14-blade-w) + 12px) 0; } }
@keyframes s14-home { from { translate: 0 0; scale: 1; } to { translate: var(--s14-fx) var(--s14-fy); scale: var(--s14-fs); } }
@keyframes s14-home-b { from { translate: 0 0; scale: 1; } to { translate: var(--s14-bx) var(--s14-by); scale: var(--s14-bsc); } }

/* Responsive: phones get larger cells, fewer majors, lighter lines. */
@media (max-width: 600px), (orientation: portrait) and (max-aspect-ratio: 5/8) {
  .s14 { --s14-cell: 32px; --s14-major-n: 4; --s14-feather: 72px;
    --s14-minor-ink: rgb(var(--nota-blue-400-rgb) / .04); --s14-major-ink: rgb(var(--nota-blue-400-rgb) / .07);
    --s14-tick-ink: rgb(var(--nota-blue-400-rgb) / .13); --s14-grain-a: .035;
    --lockup-tile: calc(clamp(80px, 24vw, 140px) * var(--splash-logo-scale)); }
  .s14 .kw { font-size: var(--type-h2); gap: var(--space-4); }
}
@media (max-width: 900px), (orientation: portrait) {
  .s14 { --s14-d-home: calc(var(--motion-enter) * 1.875); --s14-part-lag: .7; --s14-d-part: calc(var(--motion-enter) * 1.5); }
}
@media (max-width: 420px) {
  .s14-slot { --chip-s: calc(var(--s14-k) * (1em + var(--space-6))); }
  .s14 .kw { font-size: var(--type-h4); gap: var(--space-2); }
  .s14 .kw-chip { padding: var(--space-2) var(--space-2); }
}
@media (min-width: 361px) and (max-width: 420px) { .s14 .kw { font-size: var(--type-h3); gap: var(--space-3); } }
@media (max-width: 360px) {
  .s14 .kw { gap: var(--space-2); }
  .s14 .kw-chip { padding: var(--space-2) var(--space-2); }
}

.result-count { font-size: 12px; font-weight: 600; color: var(--ink-muted); white-space: nowrap; }
/* Next-availability cue in the calendar toolbar — a live blue dot + soonest
   open date, so the carnet reads as active at a glance. */
.cal-avail { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 600; color: var(--ink); white-space: nowrap; padding: 4px 11px; border: 1px solid var(--border); border-radius: var(--radius); }
.cal-avail::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--brand); flex: none; }
/* The captured partner code is visible only on this visitor's device. */
.referral-status { width: 100%; box-sizing: border-box; padding-top: 12px; padding-bottom: 0; flex: 0 0 auto; }
.referral-status[hidden] { display: none; }
.referral-status-card { display: flex; align-items: center; gap: 12px; padding: 12px 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.referral-status-check { display: grid; place-items: center; width: 32px; height: 32px; flex: none; border-radius: var(--radius); color: var(--brand); background: var(--surface-inset); }
.referral-status-copy { display: grid; gap: 3px; flex: 1; min-width: 0; font-size: 13px; line-height: 1.45; }
.referral-status-copy > span { color: var(--ink-muted); }
.referral-status-card .btn { min-height: 44px; flex: none; }
@media (max-width: 540px) {
  .referral-status-card { flex-wrap: wrap; gap: 8px; }
  .referral-status-copy { flex-basis: calc(100% - 44px); }
  .referral-status-card .btn { margin-left: 40px; }
}
.book-referral { margin: 12px 0; }

.header-spacer { flex: 1; }

/* flex:none — the tab strip never shrinks below its labels: a squeezed header
   must slim its paddings (compact band below), never wrap a label to two lines.
   The four public doors stay flat, with no submenu layer. */
.nav-tabs { display: flex; gap: 2px; flex: none; position: relative; }
.nav-tab {
  appearance: none; border: 1px solid transparent; background: transparent;
  color: var(--header-muted); font: inherit; font-weight: 600; font-size: 13.5px;
  padding: 6px 11px; border-radius: var(--radius); cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
  white-space: nowrap;
  position: relative;
  transition: background-color var(--motion-fast) var(--ease-ui), color var(--motion-fast) var(--ease-ui), border-color var(--motion-fast) var(--ease-ui), transform var(--motion-fast) var(--ease-ui);
}
/* A door kept from this visitor (renderRoleNavigation: the website's four or the
   signed-in client's two) — the author display above beats the UA [hidden].
   The carnet hero's audience parts (.hero-cta, .eyebrow) set their own too. */
.nav-tab[hidden], .mnav-link[hidden], [data-audience][hidden] { display: none; }
.nav-tab-icon { flex: none; color: currentColor; transition: transform var(--motion-fast) var(--ease-ui); }
.nav-tab-badge {
  flex: none; margin-left: 1px; padding: 2px 5px;
  border: 1px solid currentColor; border-radius: var(--radius-xs);
  color: var(--accent-purple); font-size: 9px; font-weight: 800;
  letter-spacing: .06em; line-height: 1; text-transform: uppercase;
}
.nav-tab:is(:hover, :focus-visible) { background: var(--action-tint); color: var(--action-ink); border-color: var(--action-ink); }
.nav-tab:hover .nav-tab-icon { transform: translateY(-1px); }
.nav-tab[aria-selected='true'] {
  background: var(--action-tint-strong); color: var(--action-ink);
  border-color: var(--action-ink);
}
/* The active door carries a 2px accent line anchored to the bottom edge —
   it slides under the selected tab as the single moving identifier. */
.nav-tab[aria-selected='true']::after {
  content: ''; position: absolute; bottom: -1px; left: 50%; width: 60%;
  height: 2px; background: var(--header-badge-bg); border-radius: var(--radius-xs);
  transform: translateX(-50%);
  animation: navIndicator var(--motion-ui) var(--ease-ui) both;
}
/* Keep navigation in normal layout so language and account controls
   always reserve their real width instead of covering an absolute tab row. */
/* Header compact band: between the drawer threshold (900) and a roomy desktop
   (1280) the full control set fits by slimming chrome: tighter gaps and paddings,
   and the QUÉBEC pill steps aside. Labels never wrap (nowrap above); this band
   is what guarantees they also never overflow. */
@media (min-width: 900px) and (max-width: 1279.98px) {
  .site-header .wrap { gap: 8px; padding-inline: 16px; }
  .brand-sub { display: none; }
  /* The tab GLYPHS step aside with the QUÉBEC pill. The band was measured in
     English, where the same controls need exactly 900px; French needs 1002 —
     « Espace notaire », « Partenaires », « Se connecter », « S'inscrire » —
     so from 900 to 1001 the signup button was painted past the right edge and
     clipped by `overflow-x: clip`, with no sideways scroll to reach it. Four
     icons and their gaps are 92px; the LABELS are what a door is, and they all
     stay. (e2e/french-fits.spec.js measures the fit in both languages.) */
  body:not([data-nota-surface="acquisition"]) .nav-tab-icon { display: none; }
  .nav-tab { padding: 6px 8px; }
  .header-auth .btn { padding-left: 9px; padding-right: 9px; }
  /* The tool strip tightens with the band: narrower gaps and FR/EN cells
     (the hairlines re-centre themselves from --tools-gap). */
  .header-tools { --tools-gap: 8px; }
  .header-tools .mini-seg-btn { padding: 0 6px; }
}

.icon-btn {
  appearance: none; font: inherit; border: 1px solid var(--border-strong); background: transparent;
  color: var(--ink); width: 34px; height: 34px; border-radius: var(--radius);
  cursor: pointer; display: grid; place-items: center; font-size: 16px;
  transition: background-color var(--motion-fast), color var(--motion-fast), border-color var(--motion-fast);
}
.icon-btn:hover { background: transparent; border-color: var(--action-ink); }
.icon-btn[aria-pressed='true'] { background: transparent; color: var(--action-ink); border-color: var(--action-ink); }
.icon-btn svg { display: block; }
.icon-btn:active { transform: scale(0.94); }
/* Language and theme share one unfilled rail. The local header palette
   gives selected controls a consistent surface without nested background boxes. */
.header-tools {
  --tools-gap: 12px;
  display: inline-flex; align-items: center; gap: var(--tools-gap); flex: none;
  padding: 2px 4px; border-radius: var(--radius); background: var(--surface-inset);
}
.header-tools > * { position: relative; }
.header-tools > * + *::after {
  content: ''; position: absolute; top: 6px; bottom: 6px;
  left: calc(var(--tools-gap) / -2 - 0.5px); width: 1px; background: var(--border);
}
/* Small FR | EN segment: BOTH options stay visible and the current one sits
   raised on a filled pill — the state reads at a glance, where the old single
   button showed only the OTHER language. Active marking and clicks are wired
   by NotaI18N.wireToggles(). Used in the header strip and the drawer's
   preference rows; 28px tall everywhere, matching the "?" and the switch. */
.mini-seg {
  display: inline-flex; align-items: center; gap: 2px; flex: none;
  height: 28px; padding: 2px; border-radius: var(--radius); background: var(--surface-inset);
}
.mini-seg-btn {
  appearance: none; display: inline-flex; align-items: center; justify-content: center;
  height: 24px; padding: 0 9px; border: 1px solid transparent; background: transparent;
  color: var(--ink-muted); font: inherit; font-size: 11.5px; font-weight: 700;
  letter-spacing: 0.04em; line-height: 1; border-radius: var(--radius-sm);
  cursor: pointer; transition: background var(--motion-fast), color var(--motion-fast);
}
.mini-seg-btn:hover { color: var(--ink); }
.mini-seg-btn[aria-pressed='true'] {
  background: var(--surface); color: var(--action-ink);
  border-color: var(--border); box-shadow: var(--shadow-sm);
}

/* Theme switch: a small sun/moon track whose knob covers the CURRENT side
   (checked = dark). The knob and icon tint follow html[data-theme] so the
   control is right from the first paint — before app.js boots; app.js then
   mirrors the state onto aria-checked (header + drawer twins) for AT.
   Geometry: 28 = 2px pad + 24px cell + 2px pad; two 24px icon cells; the
   24px knob slides one cell over. */
.tswitch {
  appearance: none; font: inherit; position: relative; display: inline-flex; align-items: center;
  justify-content: space-between; flex: none; width: 52px; height: 28px; padding: 2px;
  border: 0; border-radius: var(--radius);
  background: var(--surface-inset); color: var(--ink-muted); cursor: pointer;
}
.tswitch svg { position: relative; z-index: 1; display: block; flex: none; margin: 0 5.5px; }
.tswitch::before {
  content: ''; position: absolute; top: 2px; left: 2px; z-index: 0;
  width: 24px; height: 24px; border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--border);
  box-shadow: var(--shadow-sm); transition: transform var(--motion-fast) var(--ease-ui);
}
:root[data-theme='dark'] .tswitch::before { transform: translateX(24px); }
:root[data-theme='dark'] .tswitch .tswitch-moon,
:root:not([data-theme='dark']) .tswitch .tswitch-sun { color: var(--brand); }

/* --- Notifications (bell + dropdown, paired with the email the API sends) -- */
.notif-wrap { position: relative; }
#notif-bell { position: relative; }
#notif-bell.has-unread { color: var(--brand); }
.notif-badge {
  position: absolute; top: -5px; right: -5px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: var(--radius-sm); background: var(--danger-solid); color: var(--on-accent);
  font-size: 10px; font-weight: 800; line-height: 17px; text-align: center;
  border: 2px solid var(--surface);
}
.notif-panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  width: min(340px, calc(100vw - 32px));
  background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  overflow: hidden; animation: paneIn var(--motion-fast) var(--ease-ui) both;
}
.notif-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 12px 14px; border-bottom: 1px solid var(--border);
}
.notif-head-title { font-weight: 700; font-size: 13px; }
.notif-list { max-height: min(420px, 70vh); overflow-y: auto; overscroll-behavior: contain; }
.notif-empty { padding: var(--space-7); text-align: center; color: var(--ink-muted); font-size: var(--type-h4); line-height: var(--type-body-lh); }
.notif-item {
  --notif-summary-lines: 2;
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
  gap: var(--space-2) var(--space-3); padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border); cursor: default;
  transition: background-color var(--motion-fast);
}
.notif-item:last-child { border-bottom: 0; }
.notif-item.has-destination { cursor: pointer; }
.notif-item.has-destination:hover, .notif-item:focus-within { background: var(--action-tint); }
.notif-item.is-unread .notif-title::before {
  content: ''; display: inline-block; width: var(--space-3); height: var(--space-3); margin-right: var(--space-3);
  border-radius: 50%; background: var(--brand-on-surface); vertical-align: middle;
}
.notif-title {
  min-width: 0; font: inherit; font-size: var(--type-h4); line-height: var(--type-body-lh);
  font-weight: 600; color: var(--ink); overflow-wrap: anywhere; text-align: left;
}
.notif-open { appearance: none; border: 0; padding: 0; background: transparent; cursor: pointer; min-height: var(--control-height); }
.notif-item.has-details:not(.is-expanded) .notif-title { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: var(--notif-summary-lines); overflow: hidden; }
.notif-actions { display: flex; align-items: center; gap: var(--space-1); }
.notif-details, .notif-x {
  display: inline-flex; align-items: center; justify-content: center; flex: none; appearance: none;
  min-height: var(--control-height); padding: 0 var(--space-2); border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-muted); font: inherit; cursor: pointer;
}
.notif-details { font-size: var(--type-eyebrow); line-height: var(--type-body-lh); }
.notif-x { width: var(--control-height); font-size: var(--type-body); line-height: 1; }
.notif-details:hover, .notif-x:hover { background: var(--action-tint); color: var(--ink); }
.notif-open:focus-visible, .notif-details:focus-visible, .notif-x:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.notif-body { grid-column: 1 / -1; min-width: 0; font-size: var(--type-body); line-height: var(--type-body-lh); color: var(--ink-muted); overflow-wrap: anywhere; }
.notif-body[hidden] { display: none; }

/* Account menu (avatar) — merges profile + notifications + menu into one panel. */
.acct-btn { border-radius: var(--radius); border: 1px solid var(--border); }
.acct-btn:hover { border-color: var(--action-ink); }
.acct-btn[aria-expanded='true'] { border-color: var(--action-ink); color: var(--action-ink); }
.acct-panel {
  padding: 8px;
  animation: acctPanelIn var(--motion-ui) var(--ease-ui) both;
}
@keyframes acctPanelIn { from { opacity: 0; transform: translateY(4px) scale(.98); } to { opacity: 1; transform: none; } }
.acct-panel-head { display: flex; align-items: flex-start; gap: 2px; padding: 2px 2px 6px; }
.acct-panel-head .acct-head { flex: 1; min-width: 0; }
.acct-close { flex: none; }
.acct-item {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  appearance: none; background: transparent; border: 0; border-radius: var(--radius);
  padding: 9px 10px; font: inherit; color: var(--ink); cursor: pointer;
  animation: acctItemIn var(--motion-fast) var(--ease-ui) both;
}
@keyframes acctItemIn { from { opacity: 0; transform: translateX(-4px); } to { opacity: 1; transform: none; } }
.acct-action:nth-child(1) { animation-delay: 40ms; }
.acct-action:nth-child(2) { animation-delay: 80ms; }
.acct-action:nth-child(3) { animation-delay: 120ms; }
.acct-action:nth-child(4) { animation-delay: 160ms; }
.acct-item:hover { background: var(--action-tint); }
.acct-item:focus-visible { background: var(--action-tint); outline: 2px solid var(--ring); outline-offset: -2px; }
.acct-item svg { flex: none; color: var(--ink-muted); }
.acct-avatar { display: grid; place-items: center; width: 36px; height: 36px; border-radius: var(--radius); border: 1px solid var(--border); color: var(--brand-on-surface); flex: none; background: var(--surface-inset); }
.acct-labels { display: flex; flex-direction: column; min-width: 0; }
.acct-item-title { font-weight: 600; font-size: 14px; }
.acct-item-sub { font-size: 12px; color: var(--ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Role tag under the identity head (Client / Espace notaire). Outline pill, brand
   ink — never a filled swatch — so it reads as a label in both themes; it sits
   on the hover tint whenever the head is hovered, hence --brand-on-surface. */
.acct-role { align-self: flex-start; margin-top: 4px; font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--brand-on-surface); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 1px 8px; }
/* Role-aware action rows reuse .acct-item; the group is just a stack. */
.acct-actions { display: flex; flex-direction: column; padding: 0 2px; }
/* Section label inside the account panel — small uppercase header that groups
   the rows below it (Compte, Notifications, Personnalisation). */
.acct-section-label {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--ink-muted); padding: 6px 10px 4px;
}
/* Unread notary messages (ADR 0033) ride the « Mon profil » door: a filled
   square count, brand on brand-ink. */
.acct-badge {
  display: inline-flex; align-items: center; justify-content: center; margin-left: auto;
  min-width: 20px; height: 20px; padding: 0 6px; font-size: 11px; font-weight: 800;
  border-radius: var(--radius-sm); background: var(--brand); color: var(--brand-ink);
  font-variant-numeric: tabular-nums;
}
.acct-sep { height: 1px; background: var(--border); margin: 8px 4px; }
.acct-panel .notif-head { padding: 4px 10px 2px; border-bottom: 0; }
.acct-panel .notif-head-title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; }
.acct-panel .notif-list { max-height: min(320px, 50vh); }
/* Legal links cluster at the foot of the account menu — compact wrapping links,
   not full-width buttons, so it reads as a footer. */
.acct-legal { display: flex; flex-wrap: wrap; gap: 2px; padding: 2px 2px 4px; }
.acct-legal-link { appearance: none; border: 0; background: transparent; color: var(--ink-muted); font: inherit; font-size: 12px; font-weight: 600; padding: 6px 8px; border-radius: var(--radius-sm); cursor: pointer; }
.acct-legal-link:hover { background: var(--action-tint); color: var(--ink); }
.acct-legal-link:focus-visible { background: var(--action-tint); outline: 2px solid var(--ring); outline-offset: -2px; }
/* Logged-out auth buttons (login + signup); the avatar takes over once signed in
   — the whole account wrap (bell + panel) is [hidden] while anonymous. */
.header-auth { display: inline-flex; align-items: center; gap: 6px; flex: none; }
.header-auth[hidden] { display: none; }
.header-auth .btn { white-space: nowrap; }
/* Safety cap so a long notifications list can never push the menu past the
   viewport. The cap subtracts the sticky header the panel hangs from (plus its
   8px gap and an 8px floor margin) — a bare 88vh ignored that offset and let
   the panel's bottom edge run past short landscape viewports. dvh line for
   mobile URL-bar honesty; the vh line above it is the older-browser fallback. */
.acct-panel { max-height: min(calc(100vh - var(--header-h) - 16px), 660px); overflow-y: auto; overscroll-behavior: contain; }
.acct-panel { max-height: min(calc(100dvh - var(--header-h) - 16px), 660px); }

/* Device-local appearance (#profil-colors). Owner, 2026-10-03: « only suggest
   a few presets » — a free RGB picker could lay any ground under the page inks.
   Each surface offers its theme default and a few presets. A preset is an
   accent the palette already carries, poured into the theme's own paper, so ONE
   definition follows light and dark. profile-color-presets.test.mjs holds every
   preset × theme to AA under every ink it carries and a visible step (OKLab)
   from its siblings — and that is what sets the doses: in light, --ink-muted on
   the canvas and the calendar's gold « prioritaire » label on its paper sit
   just above AA, so a pour may shift the hue but hardly dim the ground.
   Luminous hues (amber, teal) move furthest for the least dimming; the
   calendar's budget holds two of them, not three (the test's second witness
   tries Lilas at every dose). app.js stores the preset id and sets
   html[data-bg-preset] / html[data-calendar-preset]; no colour lives in JS. */
:root {
  --preset-accent-lilas: var(--accent-purple);
  --preset-accent-miel: var(--svc-testament);
  --preset-accent-menthe: var(--svc-mandat-protection);
  --preset-bg-defaut: var(--bg);
  --preset-bg-lilas: color-mix(in srgb, var(--preset-accent-lilas) 10%, var(--surface));
  --preset-bg-miel: color-mix(in srgb, var(--preset-accent-miel) 10%, var(--surface));
  --preset-bg-menthe: color-mix(in srgb, var(--preset-accent-menthe) 13%, var(--surface));
  --preset-calendar-defaut: var(--surface);
  --preset-calendar-miel: color-mix(in srgb, var(--preset-accent-miel) 9%, var(--surface));
  --preset-calendar-menthe: color-mix(in srgb, var(--preset-accent-menthe) 7%, var(--surface));
}
:root[data-bg-preset='lilas'] { --user-bg: var(--preset-bg-lilas); }
:root[data-bg-preset='miel'] { --user-bg: var(--preset-bg-miel); }
:root[data-bg-preset='menthe'] { --user-bg: var(--preset-bg-menthe); }
:root[data-calendar-preset='miel'] { --user-calendar-solid: var(--preset-calendar-miel); }
:root[data-calendar-preset='menthe'] { --user-calendar-solid: var(--preset-calendar-menthe); }
/* The calendar keeps its skin: a preset tints the paper, the veil and the blur stay. */
:root[data-calendar-preset] { --user-calendar: color-mix(in srgb, var(--user-calendar-solid) var(--surface-veil-mix), transparent); }
/* One radio group per surface. The native radio covers its whole chip, so the
   44 px target, the arrow keys and the accessible name stay the browser's. */
.profil-color-group { min-width: 0; margin: 0; padding: 0; border: 0; }
.profil-color-legend { padding: 0; margin-bottom: var(--space-4); font-size: var(--type-body); font-weight: 600; color: var(--ink); }
.profil-color-options { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
.profil-color-option { position: relative; display: flex; min-width: 0; }
.profil-color-input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.profil-color-chip {
  display: flex; flex: 1; align-items: center; gap: var(--space-4); min-width: 0; min-height: var(--control-height);
  padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius);
  font-size: var(--type-body); line-height: var(--type-body-lh); color: var(--ink); overflow-wrap: anywhere;
  transition: border-color var(--motion-fast), box-shadow var(--motion-fast);
}
.profil-color-swatch {
  display: grid; place-items: center; flex: none; width: var(--space-12); height: var(--space-12);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--swatch); color: var(--ink);
}
.profil-color-input:hover + .profil-color-chip { border-color: var(--border-strong); }
.profil-color-input:focus-visible + .profil-color-chip { outline: 2px solid var(--ring); outline-offset: var(--space-1); }
/* The chosen preset reads by more than its colour: a doubled frame and a tick. */
.profil-color-input:checked + .profil-color-chip { border-color: var(--action-ink); box-shadow: inset 0 0 0 1px var(--action-ink); }
.profil-color-input:checked + .profil-color-chip .profil-color-swatch::after { content: '✓'; }
[data-swatch='bg-defaut'] { --swatch: var(--preset-bg-defaut); }
[data-swatch='bg-lilas'] { --swatch: var(--preset-bg-lilas); }
[data-swatch='bg-miel'] { --swatch: var(--preset-bg-miel); }
[data-swatch='bg-menthe'] { --swatch: var(--preset-bg-menthe); }
[data-swatch='calendar-defaut'] { --swatch: var(--preset-calendar-defaut); }
[data-swatch='calendar-miel'] { --swatch: var(--preset-calendar-miel); }
[data-swatch='calendar-menthe'] { --swatch: var(--preset-calendar-menthe); }
/* --- Mobile navigation drawer (hamburger) -------------------------------- */
/* Phones and portrait tablets: the burger and its right-side drawer replace the
   inline tabs, login button and theme toggle below 900px. The wider touch
   band keeps the sticky header inside the viewport at tablet widths. */
.nav-burger {
  display: none; border: 0;
  position: relative;
}
.nav-burger svg { display: block; }
/* Three-line morph: each bar translates to the vertical centre then rotates
   to form the ✕ diagonals.  transform-origin at the viewBox midpoint so both
   diagonals cross at (12, 12). */
.nav-burger .burger-line {
  transition: transform var(--burger-dur) var(--ease-ui), opacity var(--motion-fast) var(--ease-ui);
  transform-origin: 12px 12px;
}
.nav-burger.is-open .burger-top { transform: rotate(45deg) translateY(5px); }
.nav-burger.is-open .burger-mid { opacity: 0; }
.nav-burger.is-open .burger-bot { transform: rotate(-45deg) translateY(-5px); }
.nav-burger:hover .burger-line { stroke: var(--header-ink); }
.mnav-scrim {
  position: fixed; inset: 0; z-index: 65; background: var(--backdrop);
  -webkit-backdrop-filter: blur(12px) saturate(.8); backdrop-filter: blur(12px) saturate(.8);
  opacity: 0; pointer-events: none; transition: opacity var(--motion-fast) var(--ease-ui);
}
.mnav-scrim.is-open { opacity: 1; pointer-events: auto; }
.mnav {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 70;
  width: min(320px, 84vw);
  display: flex; flex-direction: column;
  padding: var(--space-5) var(--space-7) calc(var(--space-8) + env(safe-area-inset-bottom, 0px));
  background: var(--surface); border-left: 1px solid var(--border); box-shadow: var(--shadow-lg);
  overflow-y: auto; overscroll-behavior: contain;
  /* visibility swaps AFTER the slide-out so the exit still animates; on open
     the delay is removed so the panel is interactive immediately. */
  transform: translateX(105%); visibility: hidden;
  transition: transform var(--motion-ui) var(--ease-ui), visibility 0s var(--motion-ui);
}
.mnav.is-open { transform: none; visibility: visible; transition: transform var(--motion-ui) var(--ease-ui); }
/* No page scroll behind the open drawer. */
html.nav-open, html.nav-open body { overflow: hidden; }
/* Sticky for the same reason as the dialogs' ✕: the drawer scrolls, its close
   button must not. Background so links slide under it, not through it. */
.mnav-head { --lockup-tile: 26px; position: sticky; top: 0; z-index: 2; background: var(--surface); display: flex; align-items: center; gap: 10px; padding: var(--space-2) var(--space-2) var(--space-4); }
.mnav-head .icon-btn { width: var(--control-height); height: var(--control-height); min-width: var(--control-height); min-height: var(--control-height); flex-shrink: 0; }
.mnav-title { font-weight: 800; font-size: 15px; letter-spacing: -0.01em; }
.mnav-group { display: flex; flex-direction: column; }
.mnav-link {
  min-height: 44px;
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  appearance: none; background: transparent; border: 0; border-radius: var(--radius);
  padding: var(--space-5) var(--space-6); font: inherit; font-size: 14.5px; font-weight: 600;
  color: var(--ink); cursor: pointer; transition: background var(--motion-fast), color var(--motion-fast);
}
.mnav-link:is(:hover, :focus-visible) { background: var(--action-tint); text-decoration: none; color: var(--ink); }
.mnav-link.is-on { background: var(--action-tint); color: var(--action-ink); }
.mnav-link svg { flex: none; color: var(--ink-muted); }
.mnav-link.is-on svg { color: var(--action-ink); }
.mnav-link .nav-tab-badge { margin-left: auto; }
/* Focus ring drawn INSIDE the row: the drawer's rows are full-bleed, so the
   default outer ring painted a giant box around the first row on open. */
.mnav-link:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
/* The legal fold — the drawer's one expandable row: icon + label left,
   chevron right. Only the CHEVRON rotates on expand — the row icon sits still.
   The section links themselves are flat (ADR 0010 §2), no accordions. */
.mnav-expandrow { justify-content: space-between; }
.mnav-row-l { display: flex; align-items: center; gap: 10px; }
.mnav-row-l svg { flex: none; color: var(--ink-muted); }
.mnav-chevron { transition: transform var(--motion-fast); }
.mnav-expandrow[aria-expanded='true'] .mnav-chevron { transform: rotate(180deg); }
/* Sub-rows hang off a thin rail so they read as children of their row. */
.mnav-sub {
  display: flex; flex-direction: column; padding: 0 0 var(--space-3);
  margin-left: var(--space-6); border-left: 1px solid var(--border);
}
.mnav-sub[hidden] { display: none; }
.mnav-subitem {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
  appearance: none; background: transparent; border: 0; border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--space-4) var(--space-6) var(--space-4) var(--space-5);
  font: inherit; color: var(--ink); cursor: pointer;
}
.mnav-subitem:hover { background: var(--action-tint); text-decoration: none; color: var(--ink); }
.mnav-subitem:focus-visible { background: var(--action-tint); outline: 2px solid var(--ring); outline-offset: -2px; }
.mnav-sep { height: 1px; background: var(--border); margin: var(--space-5) var(--space-2); flex: none; }
.mnav-auth { gap: var(--space-4); padding: 0 var(--space-2); }
.mnav-auth .btn { width: 100%; }
/* When the auth pair hides (signed in), its trailing separator must go with it
   or two hairlines stack. */
#mnav-auth[hidden] + .mnav-auth-sep { display: none; }
.mnav-legal .mnav-subitem { font-size: 13.5px; font-weight: 500; color: var(--ink-muted); padding-block: var(--space-4); }
.mnav-legal .mnav-subitem:hover { color: var(--ink); }
/* Preference rows (language, theme): label left, small toggle right. NOT
   .mnav-link — the drawer's "any choice closes" delegate must ignore them,
   because flipping a preference is not navigation. The label column reuses
   .mnav-row-l so its icon+text align with the rows above. */
.mnav-pref {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: var(--space-4) var(--space-6);
  font-size: 14.5px; font-weight: 600; color: var(--ink);
}
.mnav-pref .mnav-row-l svg { color: var(--ink-muted); }

/* Staggered entrance — each direct child of the drawer arrives --menu-stagger
   after the previous, so the panel fills top-to-bottom instead of appearing
   as a block.  --i is set inline on each element in the HTML. */
.mnav.is-open > *:not(.mnav-head) {
  animation: menuItemIn var(--menu-item-dur) var(--ease-ui) both;
  animation-delay: calc(var(--i, 0) * var(--menu-stagger));
}
/* Section labels: small uppercase headers that group the drawer rows. */
.mnav-section-label {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .1em; color: var(--ink-muted);
  padding: var(--space-4) var(--space-6) var(--space-2);
  opacity: 0.7;
}

/* --- Sign-in / sign-up modal -------------------------------------------- */
/* Assez large pour un courriel long et les portes sociales sur une ligne. */
.auth-modal { --auth-modal-w: 520px; border: 1px solid color-mix(in srgb, var(--border-strong) 70%, var(--border)); border-radius: var(--radius-lg); color: var(--ink); box-shadow: var(--shadow-lg), inset 0 1px 0 var(--sheen-faint); padding: 0; width: min(var(--auth-modal-w), 92vw); max-width: none; max-height: 92vh; overflow: auto; }
/* Close ✕: the shared .dlg-x construction — see the base dialog rules. */
.auth-body { padding: 30px 26px 24px; display: flex; flex-direction: column; }
.auth-mark { display: grid; place-items: center; margin-bottom: 12px; }
.auth-mark .brand-badge {
  width: 46px; height: 46px; border-radius: var(--radius-lg); display: grid; place-items: center;
  background: var(--surface-inset); border: 1px solid var(--border);
  animation: authMarkIn var(--motion-enter) var(--ease-ui) both;
}
@keyframes authMarkIn { from { opacity: 0; transform: scale(.85); } to { opacity: 1; transform: none; } }
.auth-title { text-align: center; margin: 0; } /* a dialog title sits on the h2 rung (ADR 0050) */
.auth-sub { font-size: 13.5px; color: var(--ink-muted); text-align: center; margin: 6px 0 18px; line-height: 1.5; }
/* The base .seg lets the unselected option sit as bare text in the track, which
   reads as a label, not a button. Here both halves must read as the same kind of
   thing: equal widths, a hover fill that invites the click, and the selected one
   raised on a filled pill so the pair reads as one two-position control.
   Scoped to .auth-role only — the calendar's segments keep the base look. */
.auth-role { display: flex; width: 100%; margin-bottom: 16px; }
.auth-role .seg-btn { flex: 1; text-align: center; padding: 8px 14px; font-weight: 600; }
.auth-role .seg-btn:not(.is-on):hover { background: var(--action-tint); color: var(--ink); }
/* Not var(--surface): in dark it sits below the inset track and would read
   sunken. --surface-hover stays lighter than the track in both themes; the
   strong hairline and the bigger shadow are what separate it from a hover. */
.auth-role .seg-btn.is-on { background: var(--action-tint); border-color: var(--border-strong); box-shadow: var(--shadow); }
.auth-email { display: flex; flex-direction: column; gap: 8px; }
.auth-email .lbl { font-size: 12px; margin-bottom: 0; }
.auth-email input { width: 100%; }
.auth-continue { width: 100%; margin-top: 4px; }
.auth-fine { font-size: 12px; color: var(--ink-muted); line-height: 1.5; margin: 14px 0 0; text-align: center; }

/* --- Bascule inscription ↔ connexion ------------------------------------- */
/* Une phrase, pas un bouton : le geste principal reste le CTA au-dessus. */
.auth-switch { font-size: 13px; color: var(--ink-muted); text-align: center; margin: 14px 0 0; }
.auth-switch-btn {
  appearance: none; border: 0; background: none; padding: 0; margin-left: 4px;
  font: inherit; font-weight: 650; color: var(--action-ink); cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.auth-switch-btn:hover { color: var(--action-ink); }

/* --- Séparateur ---------------------------------------------------------- */
/* Le filet passe DERRIÈRE le mot, qui porte le fond de la carte : pas de
   largeur codée en dur, donc il tient dans les deux langues. */
.auth-or {
  display: flex; align-items: center; gap: 12px;
  margin: 18px 0 14px; color: var(--ink-muted); font-size: 12px;
}
.auth-or::before, .auth-or::after { content: ''; flex: 1; height: 1px; background: var(--border); }

/* --- Fournisseurs d’authentification ------------------------------------- */
.auth-social { display: flex; flex-direction: column; gap: 8px; }
.auth-social-h {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-muted); text-align: center; margin: 0 0 2px;
}
.auth-soc-list { display: flex; flex-direction: column; gap: 8px; }
/* Elles se lisent comme des boutons — c'est ce qu'elles seront — mais en
   retrait : elles n'entrent pas en concurrence avec le seul chemin qui marche.
   Pas de `filter: grayscale` : la marque doit rester reconnaissable, c'est tout
   l'intérêt de les montrer d'avance. */
.auth-soc-btn {
  appearance: none; font: inherit; cursor: not-allowed;
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; border-radius: var(--radius);
  border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-muted); text-align: left;
  transition: border-color var(--motion-fast), background var(--motion-fast);
}
.auth-soc-btn:hover { border-color: var(--border-strong); background: var(--action-tint); }
.auth-soc-btn:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
/* Le « G » de Google doit apparaître sur fond blanc, quel que soit le thème
   (exigence de marque) : la pastille le porte. Les deux autres logos sont
   monochromes et tiennent sur n'importe quel fond. */
.auth-soc-ic {
  display: grid; place-items: center; flex: none;
  width: 26px; height: 26px; border-radius: var(--radius-sm);
}
.auth-soc-btn[data-provider='google'] .auth-soc-ic {
  background: var(--marque-google-chip); border: 1px solid var(--border);
}
.auth-soc-t { flex: 1; font-size: 13.5px; font-weight: 600; color: var(--ink); }
.auth-social-note { text-align: center; margin: 4px 0 0; }
@media (max-width: 420px) {
  .auth-soc-btn { padding: 8px 10px; gap: 8px; }
  .auth-soc-t { font-size: 13px; }
}
/* --- Comptes locaux (localhost seulement) -------------------------------- */
/* Un outil de développeur, pas une porte du produit : filet pointillé et
   étiquette « localhost » pour qu'on ne le confonde jamais avec la vraie page. */
.auth-local {
  margin: 18px 0 0; padding: 12px; display: flex; flex-direction: column; gap: 8px;
  border: 1px dashed var(--border-strong); border-radius: var(--radius);
  background: var(--surface-inset);
}
.auth-local-h {
  display: flex; align-items: center; gap: 8px; margin: 0;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-muted);
}
.auth-local-tag {
  font-family: var(--font-mono); text-transform: none; letter-spacing: 0;
  padding: 1px 6px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); color: var(--ink);
}
.auth-local-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 6px; }
.auth-local-btn {
  appearance: none; font: inherit; cursor: pointer; text-align: left;
  display: flex; align-items: center; gap: 8px; min-width: 0;
  padding: 7px 10px; border-radius: var(--radius);
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  transition: border-color var(--motion-fast), background var(--motion-fast);
}
.auth-local-btn:hover { border-color: var(--border-strong); background: var(--action-tint); }
.auth-local-btn:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.auth-local-kind { flex: none; font-size: 11px; font-weight: 700; color: var(--action-ink); }
.auth-local-who { display: flex; flex-direction: column; min-width: 0; }
.auth-local-name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.auth-local-mail { font-size: 11.5px; color: var(--ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* La réponse au clic : vide, elle ne prend aucune place ; remplie, elle se lit
   comme une réponse et non comme une erreur. */
.auth-soc-live:empty { display: none; }
.auth-soc-live {
  margin: 8px 0 0; padding: 8px 10px; font-size: 12.5px; line-height: 1.45;
  color: var(--ink); text-align: center;
  background: var(--brand-tint-solid); border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* --- Buttons ------------------------------------------------------------- */
.btn {
  appearance: none; font: inherit; font-weight: 500; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  padding: 4px 11px; border-radius: var(--radius);
  border: 1px solid var(--border); background: transparent; color: var(--ink);
  transition: background var(--motion-fast), border-color var(--motion-fast), transform var(--motion-fast);
}
.btn:not(:disabled):hover { background: var(--action-tint); }
.btn:not(:disabled):active { transform: translateY(1px); }
/* L'action principale se lit à la COULEUR DE MARQUE et à la GRAISSE, jamais à
   un aplat (propriétaire, 2026-09-12 : « je vois plusieurs boutons qui ont des
   backgrounds remplis… aucun background rempli »). La feuille le disait déjà
   pour la feuille de réservation le 2026-09-07 — « couleur de marque et graisse,
   jamais un aplat » ; la règle vaut désormais pour TOUS les boutons. L'encre
   passe par --action-ink, dérivé de l’accent du parcours et du thème. */
.btn-primary { background: transparent; color: var(--action-ink); border-color: var(--action-ink); font-weight: 700; transition: background var(--motion-fast), border-color var(--motion-fast), transform var(--motion-fast) var(--ease-ui), box-shadow var(--motion-fast) var(--ease-ui); }
/* Au survol l'action se lève et son contour se renforce — la teinte reste le
   voile opaque de --action-tint, pas une couleur pleine. */
.btn-primary:not(:disabled):is(:hover, :focus-visible) { background: var(--action-tint); border-color: var(--action-ink); color: var(--action-ink); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.btn-primary:not(:disabled):active { transform: translateY(0); box-shadow: none; }
.btn-ghost { border-color: transparent; background: transparent; color: var(--ink-muted); }
.btn-ghost:hover { background: var(--action-tint); color: var(--ink); }
.btn-sm { padding: 3px 9px; font-size: 12.5px; }
.btn-lg { padding: 10px 20px; font-size: 14px; font-weight: 600; }

/* --- Mini icon buttons ---------------------------------------------------- */
/* The small round actions that sit ON a component (offer row, pulse row,
   notary card, footer). Icon-only by design, so every one carries a title +
   aria-label; 30px box, 44px on coarse pointers via the touch-target rule. */
.mini-btn {
  display: inline-grid; place-items: center; flex: none;
  width: 30px; height: 30px; padding: 0; font: inherit; border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: none; color: var(--ink-muted); cursor: pointer;
  text-decoration: none; transition: background-color var(--motion-fast) var(--ease-ui), color var(--motion-fast) var(--ease-ui), border-color var(--motion-fast) var(--ease-ui), transform var(--motion-fast) var(--ease-ui);
}
.mini-btn:hover { background: transparent; border-color: var(--action-ink); color: var(--action-ink); text-decoration: none; }
/* Pressing compresses — the standard tactile cue for an icon-only control. */
.mini-btn:active { background: transparent; border-color: var(--action-ink); transform: scale(.94); }
.mini-btn svg { display: block; }
/* Actions stay quiet until the row they belong to is hovered or focused —
   the data reads first, the tools appear when you go for them. */
/* Disabled reads through TOKENS, never element opacity (a half-transparent
   button lets the page bleed through it): muted ink, quiet edge, flat — et
   sans aplat, sinon l'inactif serait le SEUL bouton rempli de la page. */
.btn:disabled { cursor: not-allowed; background: transparent; border-color: var(--border); color: var(--ink-muted); box-shadow: none; }
.btn svg { flex: none; }

/* A slow transition (third-party auth redirect, checkout hand-off, any submit
   awaiting a response) gets a spinner instead of a button that just sits
   there re-labelled. Keyed off the `aria-busy` attribute already set
   throughout app.js — no new markup needed anywhere it is used. Squared, not
   a ring: the brand carries no circles (registre-carre) — a chasing corner
   reads as motion just as well. `content` is forced: some buttons already
   carry their own ::after glyph (an arrow, a chevron) at higher specificity,
   and the busy state must replace that glyph, never spin it. */
button[aria-busy="true"] { cursor: wait; }
button[aria-busy="true"]::after {
  content: '' !important; flex: none; width: .9em; height: .9em;
  border: 2px solid currentColor; border-right-color: transparent; border-bottom-color: transparent;
  border-radius: var(--radius-xs);
  animation: btn-spin .6s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }

/* Hero call-to-action — the buyer's first, obvious action */
/* Actions follow the pitch at a stable distance, independently of the aside. */
/* A touch of breathing room between the two CTAs; the margin above follows
   the spacing scale so the hero hands off at a measured distance. */
.hero-cta { display: flex; align-items: center; gap: var(--space-7); flex-wrap: wrap; margin-top: var(--space-10); padding-top: 0; }
/* Ce qui ne coûte rien, dit sous la décision et non dans les conditions
   (propriétaire, 2026-09-12). Une ligne d'aide, jamais une carte : c'est un
   fait rassurant, pas une offre. */
.hero-free { margin: var(--space-5) 0 0; max-width: 52ch; }
.hero-free strong { color: var(--ink); }
/* A slim, equal pair: ONE set height rather than padding-derived boxes, so the
   filled and the outlined button can never drift apart by a border or a wrap.
   Wide screens only — under 681px the pair keeps its full touch height. */
/* CTA buttons keep their natural padding at every width — the 44px touch
   target from .experience-paths .btn is the floor, not a ceiling. */
@media (min-width: 681px) {
  .hero-cta .btn { height: auto; }
}

/* Guided step badges (1 · 2 · 3) in the offer panel. Plancher de lisibilité
   2026-09-25 : la pastille monte à --type-h4 (elle ne se peint plus dans la
   feuille — l’écran la masque — mais rien ne se réécrit sous 14 px). */
.step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: var(--radius-sm); flex: none;
  background: var(--brand); color: var(--brand-ink); font-size: var(--type-h4); font-weight: 800;
}

:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

/* --- Hero / intro -------------------------------------------------------- */
/* A touch more floor than ceiling: the hero hands off to a PANEL below, and
   the seam between an open surface and a card needs the larger share of air. */
/* No wash of its own: the intro sits under the body's edge-to-edge fade — a
   boxed glow here ends at the content column and seams against the gutter. */
.intro { padding-block: var(--intro-pad-top); }
/* Split hero: copy on the left, live content on the right — the market pulse on
   Carnet, the "how it works" steps on Notaires. Both asides are CHROMELESS (no
   card, no border, no fill): the hero is one continuous surface, and the two
   columns are tied together by a shared top edge instead of a box. */
.intro--hero { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 0.8fr); gap: clamp(16px, 2vw, 28px); align-items: stretch; }
.intro-main { min-width: 0; display: flex; flex-direction: column; justify-content: center; }
/* The pulse kicker sits on the SAME baseline as the .eyebrow opposite it:
   same 12px, same 700, same inherited 1.5 line-height (kept explicit — the
   two columns start on one line only as long as these agree). */
.hero-steps-title {
  font-size: 12px; font-weight: 700; line-height: 1.5; text-transform: uppercase;
  letter-spacing: .06em; color: var(--accent-purple); margin-bottom: 12px;
}
/* Hero pulse — live per-service medians beside the carnet hero. No rules, no
   box: the rows are separated by rhythm and by their own volume bar, and each
   one is a button that filters the carnet below. */
.pulse { container-type: inline-size; min-width: 0; display: flex; flex-direction: column; }
.pulse-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 8px 16px;
}
.pulse .hero-steps-title { margin-bottom: 0; }
.pulse-head .hero-steps-title,
.pulse-month { min-width: 0; }
/* The month never breaks (« Septembre – octobre 2026 » is one fact); the
   kicker opposite it may, and must — both were nowrap, so in a column too
   narrow for the pair they simply painted over each other. */
.pulse-month { white-space: nowrap; }
/* fr-CA dates are lower-case (`jeudi 20 août`). `text-transform: capitalize`
   title-cases every word and produced `Jeudi 20 Août`; ::first-letter gives a
   heading its opening capital without touching the month. */
.pulse-month { font-size: 12px; font-weight: 600; color: var(--ink-muted); }
.pulse-month::first-letter { text-transform: uppercase; }
/* Rows hug their content at every viewport — no stretching to match the copy
   column. Distributing them over the hero's height (space-between + flex:1)
   opened huge gaps whenever the copy column was tall or the hero stacked. */
.pulse-rows { display: grid; gap: var(--space-2); margin-top: var(--space-6); align-content: start; }
.pulse-rows .collection-empty { margin: 0; padding: 14px 12px; border: 0; color: var(--ink-muted); text-align: center; background: none; font-size: 12px; font-weight: 700; }
/* Open rows group the filter and booking action without a card surface.
   The calendar action has its own 44px target and a quiet internal divider. */
.pulse-item {
  min-width: 0; display: flex; align-items: center; gap: 8px;
  padding: 4px 5px 4px 11px;
}
.pulse-item .pulse-row { flex: 1; min-width: 0; padding: 8px 0; margin: 0; }
.pulse-item .mini-btn {
  position: relative; width: 44px; height: 44px; margin: 0;
  border-color: transparent; color: var(--ink-muted);
}
.pulse-item .mini-btn::before {
  content: ''; position: absolute; left: -4px; top: 12px; bottom: 12px;
  width: 1px; background: var(--border);
}
.pulse-item .mini-btn:hover, .pulse-item .mini-btn:focus-visible {
  color: var(--action-ink); border-color: var(--border-strong);
}
.pulse-item .mini-btn svg { width: 18px; height: 18px; }
.pulse-row {
  display: grid; grid-template-columns: auto minmax(24px, 1fr) auto; align-items: baseline;
  gap: 2px 12px; text-align: left;
  /* Bled out by its own padding so the text stays flush with the hero copy.
     The row
     STRETCHES (no width:100%, which would shrink the content box by the
     padding and pull the amounts 20px off the hero's right edge). */
  padding: 6px 10px; margin: 0 -10px;
  background: none; border: 0; border-radius: var(--radius);
  font: inherit; color: inherit; cursor: pointer;
}
/* Underline the service for hover and selection; keyboard focus keeps the
   shared focus ring. The rows remain unboxed in every state. */
.pulse-row:hover .pulse-svc, .pulse-row.is-on .pulse-svc {
  text-decoration: underline; text-underline-offset: 3px;
}
.pulse-row.is-on .pulse-svc { color: var(--action-ink); text-decoration-color: var(--action-ink); }
.pulse-svc { grid-column: 1 / 3; grid-row: 1; display: flex; align-items: center; gap: 7px; min-width: 0; font-size: clamp(12px, 1.1vw, 14px); font-weight: 700; line-height: 1.25; }
.pulse-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
/* One glyph per act, so a service is recognised before its name is read. */
.svc-ic { flex: none; display: block; }
.chip-svc { display: inline-flex; align-items: center; gap: 6px; }
.chip-svc .svc-ic { color: var(--svc-fallback, currentColor); }
.chip-svc[data-svc='refinancement'] .svc-ic { color: var(--svc-refinancement); }
.chip-svc[data-svc='financement'] .svc-ic { color: var(--svc-financement); }
.chip-svc[data-svc='pret-particuliers'] .svc-ic { color: var(--svc-pret-particuliers); }
.chip-svc[data-svc='testament'] .svc-ic { color: var(--svc-testament); }
.chip-svc[data-svc='procuration'] .svc-ic { color: var(--svc-procuration); }
.chip-svc[data-svc='mandat-protection'] .svc-ic { color: var(--svc-mandat-protection); }
.chip-svc.is-on .svc-ic { color: currentColor; }
.legend-item .svc-ic, .legend-status-item .svc-ic { margin-right: 1px; }
/* Two figures per act, because they answer two questions: the floor the server
   will accept at all, and what other clients are actually offering this month. */
.pulse-figs { grid-column: 3; grid-row: 1 / 3; display: flex; align-items: baseline; gap: clamp(10px, 1.4vw, 18px); min-width: max-content; white-space: nowrap; }
.pulse-fig { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
.pulse-fig-k { font-size: 10.5px; font-weight: 600; color: var(--ink-muted); letter-spacing: .01em; white-space: nowrap; }
.pulse-fig-v { font-size: clamp(13px, 1.2vw, 15px); font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
.pulse-fig-v.is-empty { color: var(--ink-muted); font-weight: 600; }
.pulse-meta { grid-column: 1; grid-row: 2; font-size: 11.5px; color: var(--ink-muted); padding-left: 22px; white-space: nowrap; }
/* Share of the month's demand, in the service's own hue. The gauge runs the
   full middle column — from the counts to the figures — so on a wide hero the
   fill level stays readable instead of ending in a stub. */
.pulse-bar {
  grid-column: 2; grid-row: 2; align-self: center; justify-self: stretch;
  height: 4px; border-radius: var(--radius-xs);
  /* The track is --border, not --surface-inset: on dark the inset is a near
     twin of the page and the gauge read as a lone underline. The hairline
     token is tuned to stay visible against every surface in both themes, so
     the empty share of the month is a shape, not a guess. */
  background: var(--border); overflow: hidden; margin-left: 12px;
}
/* The fill eases to its level when the month's data lands — the motion is the
   "live" cue. Decelerating curve, so it settles rather than snaps. */
.pulse-bar > span { display: block; height: 100%; border-radius: inherit; min-width: 4px; transition: width var(--motion-enter) var(--ease-ui); }
.pulse-row.is-on .pulse-bar > span { background: var(--action-ink); }
/* Phones: no per-width tuning of the stacked rows here any more — below the
   hero threshold (768) the strip changes SHAPE entirely (the two side-by-side
   act cards; see the hero threshold block near the end of this file). */

/* One column below 900px: the aside stacks under the copy, still chromeless —
   separated by space alone, and with roomier rows for touch. */
@media (max-width: 900px) {
  .pulse-row { padding-block: 10px; } /* comfortable touch target when stacked */
}
.intro h1 { margin-top: 4px; }
/* The lede mirrors the beta's: same size, leading and measure. */
.intro p { color: var(--ink-muted); max-width: 47ch; margin: 16px 0 0; font-size: var(--type-lead); line-height: var(--type-lead-lh); }
/* A page intro uses its rail (owner, 2026-10-12: « we must fill all the empty
   space across each and every resolution »). From a tablet up the kicker and
   the title hold the first half and the lede the second, on the title's own
   line, at a reading measure. The last row is the flexible one, so a long
   lede never pushes the stamp or the action away from the title. A hero
   (`.intro--hero`) and an intro already paired with its cards
   (`.legal-overview`, from 1100 px) keep their own composition. */
@media (min-width: 700px) {
  .intro:not(.intro--hero) { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(4, auto) 1fr; column-gap: var(--space-16); justify-items: start; }
  .intro:not(.intro--hero) > * { grid-column: 1; }
  .intro:not(.intro--hero) > h1 + p { grid-column: 2; grid-row: 2 / -1; max-width: 68ch; margin-top: var(--space-2); }
}
/* Block, not inline-block: as an inline box it sat on the line-box strut, so on
   wide viewports (where it fits on ONE line) it dropped ~4px below the aside's
   kicker opposite it and the two hero columns started off-line. */
.eyebrow {
  display: block; font-size: var(--type-eyebrow); font-weight: var(--weight-display); line-height: 1.5; text-transform: uppercase;
  letter-spacing: var(--type-eyebrow-ls); color: var(--ink); margin-bottom: 16px;
}


/* --- Layout -------------------------------------------------------------- */
/* Only set vertical padding here — the horizontal padding comes from .wrap.
   A `padding: … 0 …` shorthand would reset .wrap's side padding, pushing the
   panels 28px past the hero content on each side (misalignment). */
/* minmax(0, 1fr), not 1fr: a bare 1fr's auto minimum is the panel's
   min-content, and the filter bar's nowrap scroll rows would force the track
   (and the page) wider than the phone viewport instead of scrolling. */
.layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: clamp(14px, 2vw, 20px); padding-top: clamp(4px, 0.8vw, 8px); padding-bottom: clamp(24px, 4vw, 40px); align-items: start; }
/* Full-screen calendar (Fullscreen API on #carnet-panel): fill the viewport
   with a padded surface and taller day cells that use the extra height. */
#carnet-panel:fullscreen, #carnet-panel:-webkit-full-screen {
  background: var(--user-calendar-solid, var(--surface)); padding: 10px 18px 20px; overflow: auto; border-radius: 0;
}
#carnet-panel:fullscreen .cal-cell, #carnet-panel:-webkit-full-screen .cal-cell { min-height: clamp(88px, 11vh, 128px); }

.panel {
  background: var(--surface);
  /* Ringless (owner: « ensure there is no clash with the calendar ») — a 1px
     border against the tinted band read as a box again; the gradient surface
     and the shadow carry every panel now. */
  border: 0;
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
/* The calendar wears the shared 90 % skin (owner, 2026-08-27 evening: « une
   petite touche d'opacité pour le calendrier, environ 90 % » — superseding the
   morning's fully-opaque call, which had itself retired an 80 % backing). The
   backdrop blur keeps every cell legible: the drifting dice read as soft
   light through the veil, never as marks under the grid. */
#carnet-panel { background: var(--user-calendar, var(--surface-veil)); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); }
.panel-pad { padding: var(--space-9); }

/* --- Filters ------------------------------------------------------------- */
.filters { display: flex; flex-wrap: wrap; gap: 8px; padding: 12px 18px; }
.field { display: flex; flex-direction: column; gap: 4px; }
.field > label { font-size: 11px; font-weight: 600; color: var(--ink-muted); text-transform: uppercase; letter-spacing: .04em; }
select, input[type='text'], input[type='email'], input[type='tel'], input[type='url'], input[type='number'], input[type='date'], textarea {
  font: inherit; padding: 8px 10px; border: 1px solid var(--border-strong);
  border-radius: var(--radius); background: var(--surface); color: var(--ink); min-width: 120px;
}
select:focus, input:focus, textarea:focus { border-color: var(--ring); outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }
textarea { display: block; width: 100%; resize: vertical; }
/* Native checks everywhere take the brand accent — the scoped rules below
   (.check-line, .crit-flag, …) are kept as documentation of intent. */
input[type='checkbox'], input[type='radio'] { accent-color: var(--brand); }

/* --- Nota select ---------------------------------------------------------- */
/* Native-backed select-only combobox. The popup enters the native top layer
   where supported; measured coordinates keep it in the viewport/modal. */
.nselect { position: relative; min-inline-size: 0; }
.nselect-native {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
.nselect-btn {
  font: inherit; display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  width: 100%; min-width: 0; min-height: var(--control-height); box-sizing: border-box;
  padding: var(--space-4) var(--space-5); text-align: start; cursor: pointer;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--ink);
  transition: border-color var(--motion-fast), box-shadow var(--motion-fast);
}
.nselect-btn:hover:not(:disabled) { border-color: var(--action-ink); background: var(--action-tint); }
.nselect-btn:focus-visible { border-color: var(--ring); outline: 2px solid var(--ring); outline-offset: var(--space-1); }
.nselect-btn:disabled { cursor: not-allowed; color: var(--ink-muted); background: var(--surface-hover); }
.nselect-btn[aria-invalid='true'] { border-color: var(--danger); }
.nselect-btn::after {
  content: ''; flex: none; width: var(--space-4); height: var(--space-4);
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-25%) rotate(45deg); transition: transform var(--motion-fast);
}
.nselect.is-open .nselect-btn::after { transform: translateY(25%) rotate(-135deg); }
.nselect-value { min-inline-size: 0; overflow-wrap: anywhere; }
.nselect-value.is-placeholder { color: var(--ink-muted); }
.nselect-list {
  position: fixed; inset: auto; top: var(--nselect-top, 0px); left: var(--nselect-left, 0px); z-index: 60;
  width: var(--nselect-width, 100%); box-sizing: border-box; margin: 0; padding: var(--space-2); list-style: none;
  max-height: min(calc(var(--control-height) * 6 + var(--space-4)), var(--nselect-max-height, 40vh));
  overflow: auto; overscroll-behavior: contain; scroll-padding-block: var(--space-2);
  background: var(--surface); color: var(--ink); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
}
.nselect-list[data-placement='above'] { transform: translateY(-100%); }
.nselect-list[hidden] { display: none; }
.nselect-opt {
  position: relative; display: flex; align-items: center; gap: var(--space-4); box-sizing: border-box;
  min-height: var(--control-height); padding: var(--space-4) var(--space-5); padding-inline-end: var(--space-16);
  border-radius: var(--radius-sm); cursor: pointer; font: var(--type-body)/var(--type-body-lh) var(--font-sans);
  color: var(--ink); overflow-wrap: anywhere;
}
.nselect-opt[hidden] { display: none; }
.nselect-opt:hover:not([aria-disabled='true']), .nselect-opt.is-active { background: var(--action-tint); }
.nselect-opt.is-active { outline: 2px solid var(--ring); outline-offset: calc(var(--space-1) * -1); }
.nselect-opt[aria-selected='true'] { color: var(--action-ink); font-weight: 600; }
.nselect-opt[aria-selected='true']::after {
  content: ''; position: absolute; inset-inline-end: var(--space-5); width: var(--space-3); height: var(--space-5);
  border-inline-end: 2px solid currentColor; border-block-end: 2px solid currentColor; transform: rotate(45deg);
}
.nselect-opt[aria-disabled='true'], .nselect-empty { color: var(--ink-muted); cursor: not-allowed; }
.nselect-empty { margin: 0; padding: var(--space-4) var(--space-5); font: inherit; }
.nselect-validation { margin: var(--space-2) 0 0; color: var(--danger); font: var(--type-control)/var(--type-body-lh) var(--font-sans); }
@media (prefers-reduced-motion: reduce) { .nselect-btn, .nselect-btn::after { transition: none; } }

/* --- Filters: the « Filtres » button opens #filters-dialog (guide §8.2) --- */
/* In its modal the filter set is the dialog's body, not a card inside a card:
   one labelled group per line, each control at its natural width. */
.filterbar { display: flex; flex-direction: column; gap: var(--space-7); margin-bottom: var(--space-8); }
.filterbar > .field { align-items: flex-start; gap: var(--space-3); }
.filterbar .lbl { font-size: var(--type-eyebrow); font-weight: 700; color: var(--ink-muted); }
.chip-group { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  appearance: none; font: inherit; font-weight: 500; font-size: 12.5px; cursor: pointer;
  padding: 5px 12px; border-radius: var(--radius); border: 1px solid var(--border);
  background: transparent; color: var(--ink-muted); white-space: nowrap;
  transition: background var(--motion-fast), color var(--motion-fast), border-color var(--motion-fast), box-shadow var(--motion-fast), transform var(--motion-fast);
}
.chip:hover { border-color: var(--action-ink); color: var(--ink); transform: translateY(-1px); }
.chip:active { transform: translateY(0); }
.chip.is-on { background: transparent; border-color: var(--action-ink); color: var(--action-ink); font-weight: 600; box-shadow: none; }
.chip.sm { font-size: 11.5px; padding: 4px 11px; }

.seg { display: inline-flex; background: var(--surface-inset); border: 1px solid var(--border); border-radius: var(--radius); padding: 3px; gap: 2px; }
.seg-btn {
  appearance: none; font: inherit; font-weight: 500; font-size: 12.5px; cursor: pointer;
  padding: 5px 12px; border-radius: var(--radius-sm); border: 1px solid transparent; background: transparent; color: var(--ink-muted);
  transition: background var(--motion-fast), color var(--motion-fast), box-shadow var(--motion-fast), border-color var(--motion-fast);
}
.seg-btn:hover { color: var(--ink); }
/* Selected chip reads RAISED off the track: a fill lighter than the inset track
   plus a strong hairline, so it never looks sunken in dark. */
.seg-btn.is-on { background: var(--action-tint); color: var(--action-ink); border-color: var(--border-strong); box-shadow: var(--shadow-sm); }

/* --- Calendar ------------------------------------------------------------ */
.cal-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; row-gap: 8px; padding: 10px 14px; }
/* 44px targets 4px apart still mis-tap: 8px is the floor between neighbours. */
.cal-nav { display: inline-flex; gap: var(--space-4); }
.cal-title { font-weight: 700; font-size: 15px; white-space: nowrap; }
.cal-title::first-letter { text-transform: uppercase; }
/* Active-filter count riding on the Filtres toggle. */
.filter-count {
  display: inline-grid; place-items: center; min-width: 17px; height: 17px; margin-left: 6px;
  padding: 0 4px; border-radius: var(--radius-sm); background: var(--brand); color: var(--brand-ink);
  font-size: 11px; font-weight: 800; line-height: 1;
}
#filters-toggle.has-active { border-color: var(--brand); color: var(--brand); }

/* The month grid is a QUERY CONTAINER: cells always keep 7 columns, so their
   content must respond to the GRID's width — not the viewport — to stay legible
   in a narrow column, in fullscreen, or on a phone. See the @container rules below. */
.cal-grid { container: cal / inline-size; display: flex; flex-direction: column; gap: 5px; padding: 6px 14px 12px; }
.cal-empty { margin: 0 14px 12px; padding: 10px 14px; text-align: center; color: var(--ink-muted); border: 1px dashed var(--border); border-radius: var(--radius); }
.cal-empty-text { margin-right: var(--space-6); }
.cal-row { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.cal-dow { text-align: center; font-size: 11px; font-weight: 700; color: var(--ink-muted); padding: 4px 0; text-transform: uppercase; letter-spacing: .04em; }
.cal-cell {
  background: transparent; min-height: clamp(68px, 5.4vw, 98px); padding: 6px 8px 12px; position: relative;
  min-width: 0;
  display: flex; flex-direction: column; gap: 3px; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm); scroll-margin-top: var(--sticky-top);
  text-align: left; font: inherit; color: var(--ink);
  transition: transform var(--motion-fast) var(--ease-ui), box-shadow var(--motion-fast) var(--ease-ui), border-color var(--motion-fast);
}
/* z-index rides the lift: the transform makes the hovered cell a stacking
   context, which traps the .svc-tip bubble's own z-index — without this the
   DOM-later neighbour paints over the bubble mid-hover. */
.cal-cell:hover { background: var(--action-tint); border-color: var(--border-strong); transform: translateY(-1px); box-shadow: var(--shadow-sm); z-index: 5; }
/* Adjacent-month days: present and readable, but clearly not this month.
   The date sits at the same weight a past day does.
   A future one navigates to its own month, so it takes a pointer. */
.cal-cell.is-out { background: transparent; cursor: default; }
.cal-cell.is-out .cal-daynum { color: var(--border-strong); }
.cal-cell.is-out.is-nav { cursor: pointer; }
.cal-cell.is-out.is-nav:hover .cal-daynum { color: var(--ink-muted); }
/* Past dates: inert, but kept in the grid so no row is lost. */
.cal-cell.is-past { background: transparent; cursor: default; pointer-events: none; box-shadow: none; }
.cal-cell.is-past .cal-daynum { color: var(--border-strong); }
.cal-cell.is-selected { border-color: transparent; box-shadow: inset 0 -3px 0 var(--action-ink); background: var(--action-tint); }
.cal-cell:focus-visible { outline: none; border-color: var(--action-ink); box-shadow: 0 0 0 3px var(--focus-ring); }
/* Demand days carry NO box of their own: the price + chance% (and the thin left
   tier strip) are the signal. No fill, no border — empty days stay blank, so the
   month reads as figures on a grid, not a wall of blue rectangles. */
.cal-cell.has-bids { background: transparent; }
.cal-cell.has-bids:hover { background: var(--action-tint); }
/* Urgency, said in words and only on the dates where it changes the decision.
   A calm date shows nothing: its tier is a pure function of the date the cell
   already prints, so a marker there is noise. Elevated dates get a named label
   plus a matching left edge — colour reinforces the word, it never carries the
   meaning alone. */
/* IN FLOW at every width, between the date and the prices (it is what the
   price MEANS, so it reads before it). It was absolutely positioned top-right
   and printed over the day number on every mid-width grid (tablet, narrow
   desktop) — a badge in flow can never overlap anything, at any resolution. */
.cal-urgency {
  position: static; align-self: flex-start; order: 1;
  font-size: 11px; font-weight: 800; letter-spacing: .01em; white-space: nowrap;
  color: var(--ink-muted);
}
.cal-svc-bids, .cal-avg { order: 2; }
.cal-more { order: 3; }
.cal-urgency[data-tier='rapide'] { color: var(--tier-rapide-ink); }
.cal-urgency[data-tier='prioritaire'] { color: var(--tier-prioritaire-badge-ink); }
.cal-urgency[data-tier='urgence'] { color: var(--tier-urgence-badge-ink); }
/* The steepest step is the one a client must not miss: it takes the darkest
   ink of the four, but no fill — no number in the grid carries a background
   (owner, 2026-09-10), the word and the colour say it. */
.cal-urgency[data-tier='extreme'] { color: var(--tier-extreme-ink); }
.legend-mult { font-weight: 800; }
.cal-cell[data-tier]::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  pointer-events: none;
  background: var(--tier-standard);
}
.cal-cell[data-tier='rapide']::before { background: var(--tier-rapide); }
.cal-cell[data-tier='prioritaire']::before { background: var(--tier-prioritaire); }
.cal-cell[data-tier='urgence']::before { background: var(--tier-urgence); }
.cal-cell[data-tier='extreme']::before { background: var(--tier-extreme); width: 3px; }
/* align-self keeps the box on the text: a stretched flex item would span the
   whole cell and sit under the (absolute) chevron for no reason. min-height
   clears the chevron's 26px hit circle (top:4), so the in-flow price badge on
   the next line can never slide under it, however narrow the cell gets. */
.cal-daynum { font-size: 12px; font-weight: 600; color: var(--ink-muted); align-self: flex-start; min-height: 24px; }
/* The rolling window carries a month seam inside the grid: the 1st names its
   month beside the number ("1 sept"), the way every booking calendar labels it. */
.cal-daynum[data-month]::after {
  content: " " attr(data-month);
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-muted);
}
/* Today is said in ink alone — brand colour and the heavy weight, no fill.
   Owner, 2026-09-10: « remove the colour fill from the 10 and any number » —
   a filled square read as a button, and a date is a fact, not a control. */
.cal-cell.is-today .cal-daynum {
  align-self: flex-start; display: inline-flex; align-items: center;
  min-height: 20px; color: var(--brand-on-surface); font-weight: 800;
  /* The phone grid prints the weekday as ::before with a trailing space —
     flex layout drops that space from the flow, fusing « JEU27 ». The pill
     carries its own gap (inert on desktop, where no ::before exists). */
  gap: 4px;
}
/* Cell content: the day's AVERAGE offer (headline) + a plain availability chip. */
.cal-avg { font-size: 16px; font-weight: 800; letter-spacing: -0.02em; margin-top: auto; white-space: nowrap; color: var(--ink); }
/* Available days stay brand-tinted (inviting); fully-taken days read muted. */
.cal-cell.is-taken { background: var(--surface-inset); }
.cal-cell.is-taken:hover { background: var(--action-tint); }
/* …and it says so in a word under the amount, so the state is never carried by
   the tint alone (WCAG 1.4.1). Eyebrow step at every width: it fits the
   narrowest cell, and ellipsizes rather than bleed into the next day. */
.cal-cleared-lbl { order: 2; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--type-eyebrow); font-weight: 700; letter-spacing: .01em; color: var(--ink-muted); }
/* --- Per-act prices, and the chevron that opens the detail ---------------- */
/* A dot plus a price. The dot's colour is decoded by the Service key in the
   legend, so the act's name is never repeated on a cell or a card. */
.svc-bid { display: inline-flex; align-items: baseline; gap: 5px; min-width: 0; max-width: 100%; }
.svc-bid-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; align-self: center; }
.svc-bid-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
/* In a cell the glyph carries the act, so it is tinted rather than outlined. */
.svc-bid .svc-ic { align-self: center; }
.svc-bid-amount { font-weight: 800; letter-spacing: -0.02em; white-space: nowrap; }

/* A calendar cell prints the dot and the price only. Hovering the row raises a
   bubble that names the act — icon first — and, below, what the cell has no
   room to say: how many offers are still open and the act's starting price,
   so the printed figure reads as high or low at a glance. The same words sit
   in the DOM for screen readers. The bubble inherits the row's service colour
   as currentColor, which tints its wash, edge and icon without a per-service
   rule. Nothing on the path to the grid clips, so it escapes its cell cleanly. */
.cal-svc-bids .svc-bid { position: relative; }
.svc-tip {
  position: absolute; bottom: calc(100% + 8px); left: -6px;
  z-index: 6; padding: 10px 14px; border-radius: var(--radius);
  background: color-mix(in srgb, currentColor 9%, var(--surface));
  border: 1px solid color-mix(in srgb, currentColor 45%, var(--border-strong));
  border-left: 3px solid currentColor; box-shadow: var(--shadow);
  width: max-content; max-width: 260px;
  display: none; flex-direction: column; gap: 4px; pointer-events: none;
}
.svc-tip-head { display: inline-flex; align-items: center; gap: 7px; }
.svc-tip-head .svc-ic { flex: none; }
.svc-tip-name { color: var(--ink); font-size: 13.5px; font-weight: 700; }
.svc-tip-detail { color: var(--ink-muted); font-size: 12.5px; font-weight: 600; line-height: 1.4; white-space: normal; }
/* The bubble hangs off its row's left edge; in the last two columns that runs
   past the grid's right edge, so it right-aligns there instead. */
.cal-row .cal-cell:nth-child(n+6) .svc-tip { left: auto; right: -6px; }
@media (hover: hover) and (pointer: fine) {
  .cal-svc-bids .svc-bid:hover .svc-tip { display: flex; }
}
@media (prefers-reduced-motion: reduce) {
  .svc-tip { transition: none; }
  /* Motion-flavoured polish (lifts, nudges, eased fills) goes still; colour
     and shadow state changes remain. */
  .btn-primary, .mini-btn, .pulse-item, .pulse-bar > span, .cal-cell { transition: none; }
  .btn-primary:not(:disabled):hover, .cal-cell:hover { transform: none; }
  .mini-btn:active { transform: none; }
}
.cal-svc-bids { display: flex; flex-direction: column; gap: 2px; margin-top: auto; min-width: 0; font-size: 12.5px; }
.cal-svc-bids .svc-bid { min-width: 0; }

/* Collapsed is the resting state everywhere. The chevron is the only way in, so
   it works the same with a mouse, a finger or a keyboard. */
.cell-chevron {
  position: absolute; top: 4px; right: 4px; display: grid; place-items: center;
  width: 26px; height: 26px; padding: 0; appearance: none; font: inherit; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-muted); cursor: pointer;
  transition: transform var(--motion-fast) var(--ease-ui), background var(--motion-fast) var(--ease-ui), color var(--motion-fast) var(--ease-ui);
}
.cell-chevron:hover { background: var(--action-tint); color: var(--ink); }
/* On a touch screen the 26px glyph keeps its size; its hit area grows to the
   44px the rest of the app promises on coarse pointers (the ✕ of a dialog, the
   legal nav) — the same pseudo-element trick, so nothing in the cell moves. */
@media (pointer: coarse) {
  .cell-chevron { position: relative; }
  .cell-chevron { position: absolute; }
  .cell-chevron::before { content: ''; position: absolute; inset: -9px; }
}
/* --- Touch targets: the 44px promise, kept everywhere a finger lands ---------
   The 2026-09-11 twelve-viewport audit read every interactive control on a
   coarse pointer. The ones below sat between 18 and 38px: legible, but a
   miss for a thumb. Each keeps its resting size on a mouse; on touch it grows
   its hit area — by min-height where the control is a block, by a ::before
   extension where its box must not move (a switch, a fold glyph). Inline
   links set in running text are exempt (WCAG 2.5.8), so nothing here touches
   them. */
@media (pointer: coarse) {
  .book-rail-b, #day-date, .day-bids-toggle, .offer-hint-link, .sup-chip, #chat-topic-search,
  #nc-cnq, #auth-switch-btn, .password-toggle { min-height: 44px; }
  .crit-sec-fold, #chat-close, .mini-seg-btn, .tswitch, .my-offer-day { position: relative; }
  .crit-sec-fold::before, #chat-close::before { content: ''; position: absolute; inset: -10px; }
  .my-offer-day::before { content: ''; position: absolute; inset: -12px 0; }
  .mini-seg-btn { min-width: 44px; }
  .mini-seg-btn::before { content: ''; position: absolute; inset: -10px 0; }
  /* ::after, PAS ::before : ::before est le galet de l'interrupteur (24 px,
     fond, cadre, ombre). Le redéclarer ici écrasait sa géométrie et le
     poussait hors de la piste sur tout pointeur grossier — c'est-à-dire sur
     tous les téléphones (capture du propriétaire, 2026-09-12). */
  .tswitch::after { content: ''; position: absolute; inset: -8px -4px; }
}
.cell-chevron[aria-expanded='true'] { transform: rotate(180deg); }
/* Touch: 26px alone is a mis-tap magnet. A transparent halo grows the hit
   circle to 40×40 without moving a pixel of layout — the glyph (and its
   expanded-state rotation, which the pseudo simply follows) stays put. */
@media (pointer: coarse) {
  .cell-chevron::after { content: ''; position: absolute; inset: -7px; border-radius: 50%; }
}

/* What the chevron reveals. */
.cal-more { display: none; }
.cal-cell.is-expanded .cal-more { display: flex; flex-wrap: wrap; gap: 2px 8px; margin-top: 4px; font-size: 10.5px; line-height: 1.35; color: var(--ink-muted); }
.cal-more-beat { font-weight: 700; color: var(--brand-on-surface); }
/* An expanded cell must be able to outgrow its row without shoving neighbours. */
.cal-cell.is-expanded { z-index: 3; background: var(--surface); border-color: var(--border-strong); box-shadow: var(--shadow-lg); }




/* Responsive cells, driven by the GRID width (7 columns are fixed, so cell width
   ≈ grid width / 7). Detail is shed progressively so the essentials — day number,
   count, top price, urgency strip, service-mix + status bars — always fit cleanly
   at every resolution, and nothing ever clips mid-word. */
@container cal (max-width: 740px) {
  .cal-avg { font-size: 13px; }
  .cal-svc-bids .svc-bid-amount { font-size: 11.5px; }
  /* "En attente / Approuvé / Expiré" would clip, so collapse the own-offer badge to a status dot. */
}
@container cal (max-width: 560px) {
  .cal-cell { padding: 6px 6px 12px; min-height: clamp(66px, 15cqw, 96px); }
  .cal-daynum { font-size: 11px; }
  /* Cells get too narrow for "3 285 $": swap to the compact form (data-compact),
     which always fits. The full amount stays in the DOM + day dialog for a11y. */
  .cal-svc-bids .svc-bid-amount { font-size: 0; }
  .cal-svc-bids .svc-bid-amount::after { content: attr(data-compact); font-size: 11px; font-weight: 800; }
  .cal-avg { font-size: 0; }
  .cal-avg::after { content: attr(data-compact); font-size: 12.5px; font-weight: 800; letter-spacing: -0.02em; }
}
/* The in-flow urgency badge is ~66px at its widest ("dès 4 000 $", the 2× same-day
   ceiling on the 2 000 $ floor) and needs an
   ~84px cell — a 620px seven-column grid — or its nowrap text runs into the
   next day (measured 1-20px of bleed from 710px down to 500px viewports).
   Under that, swap it to the compact figure (data-compact, the same pattern as
   the amounts above). The full label stays in the DOM and in the badge's
   title, so screen readers and hover keep the exact price. */
@container cal (max-width: 620px) {
  .cal-urgency { font-size: 0; }
  .cal-urgency::after { content: attr(data-compact); font-size: 11px; font-weight: 800; letter-spacing: .01em; }
}
/* Phones. Seven columns are not negotiable, so on a 320px screen a cell is 34px
   wide with a 26px content box, and a glyph + gap + compact amount laid out in a
   ROW needs 45px. The row was clamped to the box while its text carried on
   painting over the next day, because the tooltip above deliberately leaves
   nothing on the path to the grid clipping. That tooltip is display:none without
   a fine pointer, so here the cell can safely contain its own content again.
   Measured at 320/375/414: every act still prints its price, and nothing leaves
   its cell. The cost is height (87px -> 127px at 320), which a phone scrolls. */
/* PHONES: the month grid drops from 7 columns to 3.
   Seven columns on a 320px screen give a 34px cell, which cannot carry a glyph
   and a price side by side however the content is tuned — the previous attempt
   stacked them and still had to compact "1 900 $" down to "1,9k". Three columns
   give an 82px cell, wider than the 7-column cell is at ANY phone size, so the
   cell renders the way it does on a desktop and the prices come back in full.

   What that costs: a cell is no longer under a weekday column, so the weekday
   header row goes and each cell prints its own ("MER 19", app.js sets data-dow).
   Empty padding cells and past days are dropped rather than blanked, so the grid
   opens on today and packs 13 real dates into 5 rows instead of 21 slots into 3.

   This is keyed on a container on the grid's PARENT: an element cannot respond
   to its own container query, and .cal-grid is the `cal` container itself. */
#view-calendrier { container: calwrap / inline-size; }
@container calwrap (max-width: 468px) {
  .cal-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
  .cal-row { display: contents; }
  .cal-dow-row { display: none; }
  /* Out-of-month blanks exist ONLY to hold a weekday column, and there are no
     weekday columns here. The dead history that anchors the rolling window's
     first week holds no place either: the phone grid opens on today. */
  .cal-cell.is-out, .cal-cell.is-past { display: none; }
}
/* Cell content in reflowed mode. The `cal` container is still narrow here, so
   the ≤560 compact-money swap is still matching — but the CELL is now 82px, and
   grid width no longer implies cell width once the column count changes. */
@container cal (max-width: 440px) {
  .cal-cell { padding: 7px 9px 10px; gap: 3px; overflow: hidden; }
  /* One stable header row: the label owns the cell's width and ellipsizes
     ("MAR 1 SE…") instead of wrapping under the chevron. The chevron corner is
     reserved only on offer days (the only cells that carry one), and never for
     today's date-circle — a padded pill would smear its fill sideways; the
     pill is short ("MER 27") and clears the glyph on its own. */
  .cal-daynum { align-self: stretch; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .cal-cell.has-bids:not(.is-today) .cal-daynum { padding-right: 30px; }
  /* The month seam used to ride the date line and ellipsized into
     « MAR 1 SE… » wherever the chevron reserve ate the right edge. A seam
     cell may wrap instead: « MAR 1 » stays whole and the month reads as its
     own quiet line beneath it — no leading space, it owns the line. */
  .cal-daynum[data-month] { white-space: normal; }
  .cal-daynum[data-month]::after { content: attr(data-month); display: block; line-height: 1.3; }
  /* Offer lines breathe the same whether the day holds one or three. */
  .cal-svc-bids { gap: 3px; }
  .cal-daynum::before {
    content: attr(data-dow) " ";
    font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  }
  /* An 82px cell fits "1 900 $" (57px with its glyph). Undo the compaction. */
  .cal-svc-bids .svc-bid-amount { font-size: 11.5px; }
  .cal-svc-bids .svc-bid-amount::after { content: none; }
  /* Three-column cells are ≥93px down to a ~350px screen: the full urgency
     label fits again. */
  .cal-urgency { font-size: 11px; }
  .cal-urgency::after { content: none; }
}
/* The very smallest screens (≤~350px): three-column cells drop under 83px and
   the widest label ("dès 4 000 $", ~66px) no longer fits — back to compact. */
@container cal (max-width: 265px) {
  .cal-urgency { font-size: 0; }
  .cal-urgency::after { content: attr(data-compact); font-size: 11px; font-weight: 800; letter-spacing: .01em; }
}

/* --- Agenda / bid list --------------------------------------------------- */
/* Agenda: a dense responsive grid of day-cards so it fills the full width below
   the calendar (no sparse full-width rows / blank space). */
/* Row-aligned grid: day-groups line up in tidy rows/columns. Each day shows only
   its best offer (the rest behind "+N autres offres"), so heights stay close and
   rows align without large gaps. */

/* Same bargain as the calendar cell: the resting card stays simple — the day,
   one amount, the odds — and hover is where a client digs into what it takes to
   be retained. Fine pointers only; on touch the tap opens the day dialog, which
   carries the same detail. */

/* Without hover (touch), the coaching line is simply always there. */

/* A day with no bet is still a full rectangle: the placeholder fills the card and doubles as a "reserve this date" affordance. */
/* The card header carries the date and, like the calendar cell, the odds of
   getting a notary that day — the list was the one view showing a price with no
   sense of how gettable the date is. */
.bid-row { display: flex; align-items: center; gap: 10px; padding: 9px var(--space-12) 9px 22px; border-top: 1px solid var(--border); border-left: 2px solid transparent; }
.bid-row.is-open { border-left-color: var(--status-ouverte); }
.bid-row.is-retenue { border-left-color: var(--status-retenue); }
.status-chip {
  font-size: var(--type-h4); font-weight: 700; padding: 3px 9px; border-radius: var(--radius-sm);
  background: var(--surface); color: var(--status-retenue); border: 1px solid var(--border-strong);
  max-width: 44%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: none;
}
/* A list card is only 240-390px wide at EVERY breakpoint (the grid packs 1-4
   columns), and a single flex line cannot hold amount + name + service + tier +
   actions: the client name and the service were ellipsised into "C..." /
   "Refi..." on phones AND on a 1920 desktop alike. Give the numbers one line and
   the identity its own full-width line beneath, via `order` (the markup order is
   amount, meta, tier, actions). The day dialog keeps the single-line row — it
   has 600px to work with. */
.bid-amount { font-weight: 800; font-size: 15px; min-width: 78px; }
.bid-meta { flex: 1; min-width: 0; }
.bid-who { font-size: var(--type-h4); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bid-sub { font-size: var(--type-h4); color: var(--ink-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pill {
  font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: var(--radius-sm);
  color: var(--on-accent); white-space: nowrap;
}
.pill[data-tier='standard'] { background: var(--tier-standard); }
.pill[data-tier='rapide'] { background: var(--tier-rapide); }
.pill[data-tier='prioritaire'] { background: var(--tier-prioritaire); }
.pill[data-tier='urgence'] { background: var(--tier-urgence); }
.pill[data-tier='extreme'] { background: var(--tier-extreme); }
.pill-retenue { background: var(--surface-inset); color: var(--ink-muted); border: 1px solid var(--border-strong); }
/* ADR 0076 — la marque d'une offre de démonstration est la voisine de celle de
   l'anonymat : même registre, même discrétion. Un exemple se déclare, il ne
   s'annonce pas — ni fond d'alerte, ni --warn, ni corps plus petit. */
.tag-anon, .tag-demo { font-size: var(--type-h4); color: var(--ink-muted); border: 1px dashed var(--border-strong); border-radius: var(--radius-sm); padding: 1px 6px; margin-left: 6px; }

.legend { display: flex; flex-wrap: wrap; gap: 8px 10px; padding: 2px 14px 10px; font-size: 12px; color: var(--ink-muted); }
.legend-item, .legend-status-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink); }
.legend-label--sep { margin-left: 4px; padding-left: 12px; border-left: 1px solid var(--border); }
.legend-dot { width: 10px; height: 10px; border-radius: var(--radius-sm); }
/* Full-width explainer under the colour keys: names the two figures each cell
   prints. Own line so it never competes with the dotted keys. */
.legend-note { flex: 1 0 100%; margin-top: 2px; font-size: 11.5px; line-height: 1.5; color: var(--ink-muted); }
.legend-note strong { color: var(--ink); font-weight: 700; }

/* --- Offer form (now inside the day-booking dialog) --------------------- */
.form-row { margin-bottom: 14px; }
.form-row label.lbl { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 600; margin-bottom: 8px; }
/* A line of help is read, so it keeps a reading measure on any rail. */
.help { font-size: 12px; color: var(--ink-muted); margin-top: 4px; max-width: 80ch; }

/* --- A held line --------------------------------------------------------
   The other half of ADR 0039. Some messages CANNOT go behind an « i »: a
   validation refusal, a character counter, a « ✓ enregistré », a live status
   line. They have to be read, out loud, in place. But `hidden` means
   `display: none`, so each one still took its line away with it — and the
   button the user was aiming at moved under their cursor at the exact moment
   the refusal explained why they had missed it.

   Here `hidden` stops meaning « take the line away » and starts meaning « say
   nothing on it ». An author `display` beats the UA's `[hidden]{display:none}`,
   so the box stays; `visibility` takes the content out of sight, out of the
   focus order and out of the accessibility tree — everything `hidden`
   promised, minus the jump. `.hold-line` adds the reserved height for the
   nodes that are EMPTIED when they fall silent (an error list, a counter);
   a node that keeps its text needs `.hold` alone. */
/* Voici la liste, en un seul endroit : `.hold` pour tout nouvel arrivant, et
   les lignes déjà nommées qui la rejoignent sans changer de nom. Le `display`
   est !important parce que le tapis général `.hidden, [hidden]` l'est aussi ;
   `block` suffit partout — un enfant de flex est de toute façon « blockifié »
   par le navigateur. `.hold-line` réserve la hauteur des nœuds qu'on VIDE
   quand ils se taisent (un compteur, une liste d'erreurs) ; un nœud qui garde
   son texte n'en a pas besoin. */
.hold[hidden],
.offer-hint[hidden], .day-beat[hidden],
.chat-error[hidden], .chat-count[hidden], .nc-chat-count[hidden], .nc-chat-err[hidden],
.nc-form-errors[hidden], .nc-field-err[hidden],
.sup-reply-courriel[hidden], .ct-count[hidden],
.cal-avail[hidden],
#chat-reply-error[hidden], #chat-reply-sent[hidden] { display: block !important; visibility: hidden; }
/* CE QUI N'EST PAS DANS CETTE LISTE, ET POURQUOI (ADR 0046). La ligne réservée
   est faite pour un message de VALIDATION : il apparaît et disparaît sous le
   doigt du visiteur, et sans elle le formulaire sauterait à chaque frappe. Une
   NOTE DE SITUATION, elle, change une fois par conversation — et si elle garde
   sa ligne, elle la garde tout le temps. « Conversation terminée » et l'avis
   d'escalade réservaient à eux deux 108 px de vide permanent au milieu d'un
   panneau de 380 px : c'était, littéralement, le trou qui faisait paraître la
   messagerie cassée. Ils se replient donc, comme les questions d'amorce.
   La ligne du marché (« ce que d'autres offrent ce jour-là ») se replie pour
   la même raison, et pour une de plus : elle n'est jamais VIDÉE, elle garde
   son étiquette. À 320 px, cette étiquette invisible tenait deux lignes et le
   bouton « Offrir autant » retenu avec elle en occupait une troisième : 79 px
   de vide au milieu du panneau, mesurés sous le doigt du visiteur. L'absence
   de référence se dit par la note qui suit — « fixez votre prix » — pas par
   une ligne fantôme. Le compteur de la messagerie en est sorti le 2026-10-03
   pour la même raison : il ne paraît que près du plafond, et sa ligne tenue
   creusait un vide permanent sous chaque composeur. */
/* Le dernier conteneur flex de la liste garde son flex : en `block` son
   contenu se recomposerait, et une ligne tenue changerait de hauteur — soit
   exactement ce qu'on est venu empêcher. */
.cal-avail[hidden] { display: flex !important; }
.hold-line { min-height: 1.5em; }

/* --- Info tip (« ⓘ ») ----------------------------------------------------
   The house pattern for a SITUATIONAL note — « this date is short notice »,
   « few notaries travel that far », « check these older answers ». Such a
   sentence used to print itself as an extra line under the question, so an
   answer that turned it on GREW its card and pushed everything below it down
   the page; answering the next question shoved it back. The sentence now
   rides a small « i » pinned to the question's own line and opens in a panel
   that floats OVER the page: the form's height never depends on what the
   notes have to say.

   The box is reserved whether or not the tip has something to say
   (visibility, never display) — so the label line does not jitter either, and
   a hidden tip is neither focusable nor read aloud.

   Three ways in, because a tooltip that only answers the mouse answers nobody
   on a phone or a keyboard: hover, keyboard focus, and a click that PINS the
   panel open until Escape, an outside click, or another tip takes over. The
   pinned panel takes the pointer, so its sentence can be selected and copied. */
.itip { position: relative; display: inline-flex; flex: none; vertical-align: -3px; margin-left: 6px; --itip-arrow-x: 50%; --itip-hover-grace: var(--motion-fast); }
.itip[data-on='false'] { visibility: hidden; }
.itip-btn {
  appearance: none; border: 0; background: none; padding: 0; margin: 0;
  position: relative;
  width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-xs); color: var(--ink-muted); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* WCAG 2.2 « Target Size (Minimum) » asks for 24 × 24 CSS px. The glyph stays
   at 18 so it never lifts the label's line — the target is grown OUTWARD by an
   invisible pad, which costs the layout nothing because it is out of flow. */
.itip-btn::after { content: ''; position: absolute; inset: -3px; }
.itip-btn:hover, .itip[data-open='true'] .itip-btn { color: var(--ink); background: var(--action-tint); }
.itip-btn:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 1px; }
/* The panel: absolutely placed, so it costs the layout nothing. Its side and
   its alignment are chosen by script the moment it opens (placeItip), so it
   never opens off the edge of a dialog or a phone. */
/* L'aide d'une question vit maintenant DANS ce panneau : elle en prend la voix
   (le gris de .help se perdrait sur la surface du panneau) et n'y garde pas la
   marge qu'elle avait en pleine page. */
.itip-pop > .help { margin: 0; font-size: inherit; color: inherit; }
.itip-pop {
  position: absolute; z-index: 60; top: calc(100% + 9px); left: 50%; transform: translateX(-50%);
  width: max-content; max-width: min(280px, calc(100vw - 16px));
  padding: var(--space-4); text-align: left;
  font-size: 13px; font-weight: 500; line-height: 1.5; color: var(--ink);
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  opacity: 0; visibility: hidden; pointer-events: none;
}
.itip[data-side='top'] > .itip-pop { top: auto; bottom: calc(100% + 9px); }
/* La flèche : un carré de la surface du panneau, tourné de 45°, ancré sur le
   bord qui fait face au déclencheur. Elle vit sur `.itip::after`, jamais sur
   `.itip-pop::before` : le panneau scrolle (`overflow-y: auto`, section
   « Shared help affordance ») et rogne tout pseudo ancré sur son cadre — la
   flèche existait sans se peindre (règle 14, revue du 2026-09-25). data-side
   décide du bord (le script place déjà la bulle) ; --itip-arrow-x, posé par
   itipPlace, suit le panneau quand il glisse hors de l’aplomb du « i ».
   Aucune bordure — l’ombre du panneau suffit à le détacher. */
.itip::after {
  content: ''; position: absolute; z-index: 61;
  width: 10px; height: 10px; background: var(--surface);
  top: calc(100% + 4px); left: var(--itip-arrow-x);
  transform: translateX(-50%) rotate(45deg);
  opacity: 0; visibility: hidden; display: none;
}
.itip[data-side='top']::after { top: auto; bottom: calc(100% + 4px); }
/* Hover is offered only where a pointer can actually hover: on a touch screen
   a tap leaves a « stuck » hover behind, and the panel would never close. There
   the pinning tap is the way in, and it has a way out. */
/* FERMÉE, la bulle sort de la mise en page (2026-09-06). Elle est hors flux,
   mais une boîte absolue invisible compte quand même dans le scrollWidth de son
   conteneur : à 375 px, quatre bulles de 280 px ancrées sur leur « i » faisaient
   dépasser la feuille de réservation de 30 px, et #day-dialog (overflow-y:auto,
   donc overflow-x calculé à auto) se laissait tirer de côté — rien à voir, tout
   à faire glisser. C'est la SEULE exception au registre « visibility, jamais
   display » du « i » : celui-ci ne tient aucune ligne, donc il n'en bouge
   aucune. Ouverte, elle reprend sa boîte AVANT qu'itipPlace la mesure (le clic
   pose data-open, le survol et le focus posent l'état CSS, puis l'événement). */
.itip-pop { display: none; }
@media (hover: hover) {
  .itip:hover > .itip-pop, .itip[data-hovered='true'] > .itip-pop { opacity: 1; visibility: visible; display: block; }
  .itip:hover::after, .itip[data-hovered='true']::after { opacity: 1; visibility: visible; display: block; }
}
.itip-btn:focus-visible ~ .itip-pop,
.itip[data-open='true'] > .itip-pop { opacity: 1; visibility: visible; display: block; }
/* La flèche ouvre et ferme par les mêmes portes que son panneau : survol (à la
   pointe réelle), focus visible du « i », clic qui épingle. Spécificité tenue
   sous la règle `data-dismissed` plus bas, comme celle du panneau. */
.itip:has(:where(.itip-btn:focus-visible))::after,
.itip[data-open='true']::after { opacity: 1; visibility: visible; display: block; }
.itip[data-open='true'] > .itip-pop { pointer-events: auto; }
@media (prefers-reduced-motion: no-preference) {
  .itip-pop, .itip::after { transition: opacity var(--motion-fast) var(--ease-ui), visibility var(--motion-fast); }
  /* A note that has just turned on wakes its « i » once, so a signal that no
     longer takes a line of its own is still noticed. */
  @keyframes itip-wake {
    from { box-shadow: 0 0 0 4px color-mix(in srgb, currentColor 35%, transparent); }
    to { box-shadow: 0 0 0 4px transparent; }
  }
  .itip[data-fresh='true'] .itip-btn { animation: itip-wake 1.2s ease-out; }
}

/* Inside step 3, right above the amount: WHY it is pre-filled (delay tier +
   going range). Compact and muted — context, never a competing price signal. */
.tier-preview { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tier-preview .pill { font-size: var(--type-h4); }
.tier-preview .tp-text { font-size: var(--type-h4); color: var(--ink-muted); }

.slider-row { display: flex; align-items: center; gap: 12px; }
/* La marge par défaut du navigateur (2 px) décalait la piste de son rail :
   le curseur commençait 2 px à droite du montant et des bornes au-dessus. */
input[type='range'] { flex: 1; margin: 0; accent-color: var(--brand); }
/* The slider's typed twin: a client who knows their number types it. */
.amount-input-wrap { display: inline-flex; align-items: center; gap: 4px; flex: none; }
.amount-input { width: 7ch; padding: 6px 8px; font: inherit; font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); }
.amount-input:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 1px; }
.amount-input-unit { font-size: var(--type-h4); font-weight: 700; color: var(--ink-muted); }
/* Where the slider starts and stops, in dollars, under its two ends. */
.slider-bounds { display: flex; justify-content: space-between; margin-top: 2px; font-size: var(--type-h4); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
/* The reason the publish button is dead, said before it is pressed. Each
   named question is a door back to its own line — underlined, same voice. */
.offer-hint { margin: 0 0 8px; min-height: 1.5em; font-size: var(--type-h4); font-weight: 600; color: var(--warn); }
.offer-hint-link {
  appearance: none; border: 0; background: none; padding: 0; margin: 0;
  font: inherit; color: inherit; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.offer-hint-link:hover, .offer-hint-link:focus-visible { color: var(--ink); }
/* Law 25 small print directly under the CTA: always visible, quiet. The
   negative margin tucks it against the button despite the form's flex gap. */
.consent-line { font-size: var(--type-h4); line-height: 1.5; max-width: 80ch; }
.day-book > .consent-line { margin-top: -12px; }
.amount-big { font-size: 26px; font-weight: 800; letter-spacing: -0.02em; }

.gauge { height: 8px; border-radius: var(--radius-xs); background: var(--surface-inset); overflow: hidden; margin: 8px 0 4px; }
.gauge-fill { width: 0; height: 100%; background: linear-gradient(90deg, var(--tier-rapide), var(--tier-prioritaire), var(--tier-urgence)); }
.gauge-label { font-size: var(--type-h4); font-weight: 600; }

/* LE DEVIS (ADR 0031) — les deux lignes que le client paie, dans l'étape du
   montant. Il doit se lire comme un reçu, pas comme une alerte : aucune
   couleur d'accent, juste un filet et une ligne de total plus lourde. Le
   supplément de Nota n'est pas une mauvaise nouvelle qu'on cache, c'est un
   prix qu'on annonce — l'art. 68 du Code de déontologie interdit la publicité
   incomplète, et une ligne discrète mais toujours visible est la réponse. */
.devis { margin-top: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-inset); }
.devis-l { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: var(--type-h4); line-height: 1.7; }
.devis-k { color: var(--ink-muted); }
.devis-v { font-variant-numeric: tabular-nums; font-weight: 600; }
.devis-total { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--border); font-size: var(--type-body); }
.devis-total .devis-k, .devis-total .devis-v { color: var(--ink); font-weight: 700; }
/* La revendication du modèle n'avait AUCUNE règle : elle héritait des 16 px du
   formulaire et parlait donc plus fort que le total juste au-dessus. Elle
   compte (art. 32.1 2° L.N.), mais elle se lit après le chiffre, pas avant. */
.devis-claim { margin: var(--space-5) 0 0; font-size: var(--type-h4); font-weight: 600; line-height: 1.5; color: var(--ink); }
.devis-note { margin: 6px 0 0; font-size: var(--type-h4); line-height: 1.5; color: var(--ink-muted); }

/* Discreet inline checkbox (the bid dialog's account opt-in): plain row, no
   switch chrome — the option must read as one more optional field, not a step. */
.check-line { display: flex; align-items: center; gap: 8px; font-size: 13.5px; cursor: pointer; }
.check-line input { width: 16px; height: 16px; margin: 0; flex: none; accent-color: var(--brand); }
.check-line input:disabled { cursor: not-allowed; }
.check-line:has(input:disabled) { color: var(--ink-muted); cursor: not-allowed; }

.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 0; }
.switch { position: relative; width: 42px; height: 24px; flex: none; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .track { position: absolute; inset: 0; background: var(--border-strong); border-radius: var(--radius-sm); transition: background var(--motion-fast); cursor: pointer; }
.switch .track::before { content: ''; position: absolute; width: 18px; height: 18px; left: 3px; top: 3px; background: var(--on-accent); border-radius: var(--radius-xs); transition: transform var(--motion-fast); }
.switch input:checked + .track { background: var(--brand); }
.switch input:checked + .track::before { transform: translateX(18px); }
.switch input:focus-visible + .track { box-shadow: 0 0 0 3px var(--focus-ring); }

/* Délibérément PAS une ligne tenue (ADR 0039 §5) : une liste de refus répond à
   un envoi volontaire, pas à une frappe — le saut arrive une fois, à un moment
   où l'œil est déjà sur le message. La réserver mettrait ~27 px de vide
   permanent au-dessus de six boutons d'envoi, ce qui coûte plus cher que le
   saut qu'elle éviterait. Les listes qui répondent, elles, à CHAQUE frappe
   (.nc-form-errors, .nc-field-err) tiennent bien leur ligne. */
.errors { list-style: none; margin: 8px 0 0; padding: 0; }
.errors li { color: var(--danger); font-size: 13px; padding: 2px 0; }
/* Screen 4 validation errors: a soft warm background draws the eye without shouting.
   The wash follows the theme — the theme's --warn poured into its --surface —
   so on the night it is a warm dark box, not the pale beige it used to be
   (orange into --nota-blue-50 in every theme: the red read 2.30:1 on it). */
#offer-errors {
  padding: 10px 14px;
  background: color-mix(in srgb, var(--warn) 8%, var(--surface));
  border-left: 3px solid var(--accent-warm);
  border-radius: var(--radius-sm);
  font-size: var(--type-h4);
  line-height: 1.5;
  margin: 16px 0 8px;
}
/* Light: the warm wash keeps its brighter orange, on a neutral ground, never
   on blue (owner, 2026-10-03). The shared rule above paints the dark themes. */
:root[data-theme='light'] #offer-errors { background: color-mix(in srgb, var(--nota-orange-400) 8%, var(--canvas-bg)); }
/* « Nous joindre »: the message counter, shown only near the cap (app.js contactCount). */
.ct-count { margin: 4px 0 0; min-height: 1.5em; text-align: right; font-variant-numeric: tabular-nums; }

/* --- Partenaires (ADR 0011) ---------------------------------------------- */
/* The entry form uses the shared landing grammar. Once a code is claimed,
   sharing and private results become a full-width workspace, rather than
   forcing the introduction to sit beside a long, narrow column.
   REFONTE DU 2026-09-20 (propriétaire : « tout l'espace est utilisé, aucun
   composant trop gros, des animations à nous, toutes les transitions douces ») :
   - sous l'introduction, les quatre étapes tiennent UNE rangée dès 1280 px
     (deux rangées entre 900 et 1280, une colonne au téléphone) ;
   - le mouvement de la porte vit en fin de bloc, sous « no-preference ». */
.partner-landing { --partner-landing-columns: minmax(0, 2fr) minmax(0, 1fr); --partner-landing-gap: clamp(16px, 2vw, 28px); }
@media (min-width: 900.01px) {
  /* The form and the guarantee begin on the same column line. One shared
     definition keeps the two independent rows aligned as the viewport grows. */
  .partner-landing:not(:has(.pr-claim[data-state='claimed'])) { grid-template-columns: var(--partner-landing-columns); column-gap: var(--partner-landing-gap); }
  .partner-landing:not(:has(.pr-claim[data-state='claimed'])) { grid-template-rows: min-content 1fr; }
  .partner-landing:not(:has(.pr-claim[data-state='claimed'])) > .pr-claim { grid-column: 2; grid-row: 1 / span 2; }
  .partner-landing:not(:has(.pr-claim[data-state='claimed'])) > .pr-steps { grid-column: 1; grid-row: 2; }
  /* Devant le formulaire, les étapes s'étirent jusqu'à son bas : les deux
     colonnes du héros finissent sur la même ligne, sans bande vide à gauche.
     Pas devant le reçu « en attente » (le panneau s'allonge de 200 px : des
     tuiles de 450 px seraient le composant trop gros qu'on vient de retirer). */
  .partner-landing:has(.pr-claim[data-state='form']) > .pr-steps { align-self: start; }
}
/* L'introduction occupe sa colonne : la phrase tient deux lignes au lieu de
   trois, la ligne d'aide et son « Pourquoi ? » une seule. */
.partner-landing > .landing-copy > .landing-lede { max-width: 62ch; }
.partner-landing > .landing-copy > .hero-free { max-width: none; }
.partner-landing:has(.pr-claim[data-state='claimed']) { grid-template-columns: minmax(0, 1fr); }
.partner-landing:has(.pr-claim[data-state='claimed']) > .landing-copy {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-8) var(--space-16); align-items: start;
}
.partner-landing:has(.pr-claim[data-state='claimed']) > .landing-copy > :is(.eyebrow, h1, .hero-free) { grid-column: 1 / -1; }
.partner-landing:has(.pr-claim[data-state='claimed']) > .landing-copy > h1 { max-width: none; margin: 0; }
.partner-landing:has(.pr-claim[data-state='claimed']) > .landing-copy > :is(.landing-lede, .hero-cta, .hero-free) { margin: 0; }
.partner-workspace:not([hidden]) { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: var(--space-12) var(--space-16); align-items: start; }
.partner-sharing, .partner-workspace .partner-results { min-width: 0; }
.partner-workspace .partner-results { margin: 0; padding: 0 0 0 var(--space-16); border-top: 0; border-left: 1px solid var(--border); }
.pr-claim .partner-workspace > .partner-rappel-line { grid-column: 1 / -1; margin: 0; }
.pr-claim .partner-workspace > .partner-payout { grid-column: 1 / -1; margin: 0; }
@media (max-width: 899.98px) {
  .partner-landing:has(.pr-claim[data-state='claimed']) > .landing-copy { grid-template-columns: minmax(0, 1fr); }
  .partner-workspace:not([hidden]) { grid-template-columns: minmax(0, 1fr); gap: var(--space-12); }
  .partner-workspace .partner-results { padding: var(--space-12) 0 0; border-left: 0; border-top: 1px solid var(--border); }
}
/* Le panneau de droite : un .panel dans le registre des grands panneaux (le
   voile ET son flou — la paire de #carnet-panel). */
.pr-claim {
  min-width: 0; padding: var(--space-9); scroll-margin-top: var(--sticky-top);
  background: var(--surface-veil);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
/* `.intro p` parle au texte du héros (16 px, 47ch, 16 px d'air). Le panneau est
   un formulaire posé DANS le héros : ses paragraphes reprennent la petite voix
   de .help et la largeur du panneau — sans quoi chaque aide se lisait comme
   une seconde phrase d'accroche. */
.pr-claim p { max-width: none; margin: var(--space-2) 0 0; font-size: 12px; line-height: 1.5; }
/* Survol et focus lisses sur tout ce qui se manipule dans le panneau : le champ
   prend l'encre de l'action au survol, son anneau de focus arrive en fondu ; la
   puce de métier prend la teinte de survol du registre (jamais un aplat). */
/* The claim form keeps its register in its modal (« Réclamer un autre code »,
   #partner-claim-dialog): the rules that shape the form name both homes. */
:is(.pr-claim, .pr-claim-dialog) input { width: 100%; transition: border-color var(--motion-fast) var(--ease-ui), box-shadow var(--motion-fast) var(--ease-ui); }
:is(.pr-claim, .pr-claim-dialog) input:hover:not(:focus) { border-color: var(--action-ink); }
:is(.pr-claim, .pr-claim-dialog) .chip:is(:hover, :focus-visible) { background: var(--action-tint); }
/* Dès que le panneau les contient, les métiers tiennent UNE rangée (autant de
   colonnes que le domaine fournit de métiers) : entre 1100 et 1500 px la
   troisième puce passait seule à la ligne et allongeait tout le panneau. Chaque
   colonne prend la largeur de son libellé tant qu'il y a la place ; sinon le
   libellé le plus long se replie DANS sa puce, jamais sous son plus long mot.
   Plus étroit, et au téléphone, elles s'enchaînent comme partout. */
@media (min-width: 1100px) {
  .pr-claim[data-state='form'] .chip-group, .pr-claim[data-state='pending'] .chip-group { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(min-content, auto); }
  .pr-claim .chip { white-space: normal; line-height: 1.25; }
}
/* Sous le doigt, 44 px : les champs, les boutons et les puces du panneau
   grandissent par min-height ; le « Pourquoi ? », posé dans une phrase, par un
   halo qui ne bouge pas la ligne. */
@media (max-width: 600px), (pointer: coarse) {
  :is(.pr-claim, .pr-claim-dialog) input, :is(.pr-claim, .pr-claim-dialog) .btn, :is(.pr-claim, .pr-claim-dialog) .chip { min-height: 44px; }
  .pr-why { position: relative; }
  .pr-why::before { content: ''; position: absolute; inset: calc(var(--space-5) * -1) 0; }
}
:is(.pr-claim, .pr-claim-dialog) .form-row { margin-bottom: var(--space-7); }
:is(.pr-claim, .pr-claim-dialog) span.lbl { display: block; font-size: 14px; font-weight: 700; margin-bottom: var(--space-4); }
/* « Pourquoi ? » suit la phrase qu'il explique, sur sa ligne. */
.pr-why { margin-left: var(--space-2); vertical-align: baseline; }
/* Steps sit below the introduction beside the entry form, then follow the
   full-width claimed workspace. On phones, DOM order keeps the form first.
   Dès 1280 px : UNE rangée de quatre tuiles — le chiffre et son filet en haut,
   le libellé et son détail ancrés en bas : quand la rangée s'étire jusqu'au bas
   du formulaire, l'air se loge entre les deux, jamais sous le texte. */
.pr-steps {
  list-style: none; margin: 0; padding: 0; display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-5);
}
/* L'éclat des montants (amtGlow) : la rangée d’étapes porte sa durée locale. */
.pr-steps { --pr-glow-beat: 1.2s; }
/* Une étape est une carte d'accent : son trait de 3 px est celui du registre
   partagé (.accent-line), qui le TRACE à l'arrivée — de 1 à 4, c'est le fil de
   progression de la porte. Une étape n'est pas un contrôle : ni survol, ni
   curseur, ni marqueur généré sur la carte. */
.pr-card {
  --accent-line: var(--brand);
  display: flex; flex-direction: column; gap: var(--space-2);
  min-width: 0; padding: var(--space-8) var(--space-8) var(--space-7);
  border: 0; border-radius: var(--radius-lg);
  background: var(--surface-veil);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  box-shadow: var(--shadow-sm);
}
/* Le quatrième pas (référer un notaire) : même grille, trait chaud — une
   récompense unique, pas un revenu récurrent. */
.pr-card--once { --accent-line: var(--accent-warm); }
/* L'ordre d'entrée : trois crans de --menu-stagger entre deux étapes, posés ici
   (la porte ne porte aucun attribut style). */
.pr-card:nth-child(2) { --reveal-i: 3; }
.pr-card:nth-child(3) { --reveal-i: 6; }
.pr-card:nth-child(4) { --reveal-i: 9; }
/* Le rang est un chiffre d'affichage, à l'encre — jamais une pastille. Dans la
   rangée de quatre, un filet de 2 px part du chiffre et file vers l'étape
   suivante ; la dernière n'en a pas : le parcours s'arrête là. */
.pr-step-num { display: flex; align-items: center; gap: var(--space-6); transform-origin: left bottom; font-family: var(--font-display); font-size: var(--type-h1); line-height: var(--type-h1-lh); letter-spacing: var(--type-h1-ls); font-weight: var(--weight-display); font-variant-numeric: tabular-nums; color: var(--ink); }
.pr-step-num::after { content: ''; flex: 1; height: 2px; background: var(--border-strong); transform-origin: left center; }
.pr-card:last-child > .pr-step-num::after { content: none; }
/* Le glyphe de l'étape (2026-09-20) : un dessin au trait qui occupe le milieu de
   la tuile quand la rangée s'étire jusqu'au bas du formulaire — l'air a une
   raison d'être. Il se DESSINE à l'arrivée (.stroke-draw, registre partagé), au
   rang de sa carte. Décor : aria-hidden, et il se retire dès que la carte
   redevient une ligne (sous 1280 px). */
.pr-step-glyph { display: flex; align-items: center; color: var(--brand-on-surface); }
.pr-step-glyph > svg { width: var(--space-14); height: var(--space-14); }
.pr-card--once > .pr-step-glyph { color: var(--accent-warm); }
.pr-step-label { margin-top: 0; padding-top: var(--space-2); font-family: var(--font-display); font-size: var(--type-h4); line-height: var(--type-h4-lh); letter-spacing: var(--type-h4-ls); font-weight: var(--weight-display); color: var(--ink); }
.pr-step-detail { font-size: 13px; line-height: 1.5; color: var(--ink-muted); }
/* Le montant est la seule chose en bleu : un chiffre, pas un titre. */
.pr-step-detail strong { color: var(--brand-on-surface); font-variant-numeric: tabular-nums; }

/* Un gain est une ligne de lecture, sans action de survol. */

.pr-earnings { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); min-width: 0; }
.pr-earning-card {
  --accent-line: var(--brand);
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center;
  column-gap: var(--space-8); min-width: 0; padding: var(--space-7) var(--space-8) var(--space-6);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.pr-earning-card--once { --accent-line: var(--accent-warm); }
.pr-earning-card:nth-child(2) { --reveal-i: 3; }
/* Le chemin est un libellé : à l'encre. Le montant, lui, est un chiffre. */
.pr-earning-path { grid-column: 2; grid-row: 1; font-size: 13px; line-height: 1.5; font-weight: 700; color: var(--ink); }
/* min-width : empilés au téléphone, les deux montants gardent la même colonne. */
.pr-earning-amount { grid-column: 1; grid-row: 1 / span 2; min-width: 4ch; font-family: var(--font-display); font-size: var(--type-h2); line-height: var(--type-h2-lh); font-weight: var(--weight-display); color: var(--brand-on-surface); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pr-earning-note { grid-column: 2; grid-row: 2; font-size: 13px; line-height: 1.5; color: var(--ink-muted); }

/* Sous 1280 px la colonne du héros ne tient plus quatre cartes lisibles : deux
   rangées de deux, la carte redevient une ligne (le chiffre à gauche) et le
   filet, qui ne relierait plus rien, se retire. */
@media (max-width: 1279.98px) {
  .pr-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 1fr; }
  .pr-card { display: grid; grid-template-columns: auto minmax(0, 1fr); align-content: start; align-items: baseline; gap: var(--space-1) var(--space-6); padding: var(--space-7) var(--space-8) var(--space-6); }
  .pr-step-num { grid-row: span 2; font-size: var(--type-h2); line-height: var(--type-h2-lh); letter-spacing: var(--type-h2-ls); }
  .pr-step-label { margin-top: 0; padding-top: 0; }
  .pr-step-num::after { content: none; }
  .pr-step-glyph { display: none; }
}
@media (max-width: 600px) {
  .pr-steps, .pr-earnings { grid-template-columns: minmax(0, 1fr); }
}

/* Orange is the interaction cue: blue remains the selected/brand state,
   while a pointer preview is unmistakably actionable. */
/* Owner, 2026-10-03: « use better color than light blue ». The chosen act
   speaks the brand — the logo tile's blue (--brand-on-surface) — instead of
   its own data colour (refinancement's sky blue read as baby blue); the act
   keeps its colour on its glyph. The orange preview is for the cards not yet
   chosen: on the chosen one it fought the selection frame. */
#o-service-chips .chip { --action-accent: var(--brand-on-surface); }
#o-service-chips .chip:not(.is-on):hover,
#o-service-chips .chip:not(.is-on):focus-visible {
  border-color: var(--nota-orange-400);
  color: var(--nota-orange-400);
  background: color-mix(in srgb, var(--nota-orange-400) 12%, var(--surface));
}
#o-service-chips .chip:not(.is-on):hover .chip-svc-badge,
#o-service-chips .chip:not(.is-on):focus-visible .chip-svc-badge {
  border-color: var(--nota-orange-400);
}
#o-service-chips .chip:not(.is-on):hover .svc-ic,
#o-service-chips .chip:not(.is-on):focus-visible .svc-ic {
  color: var(--nota-orange-400);
}
/* The claim form's payoff: the shareable link, ready to copy. */
/* Le bouton ne TRAVERSE plus la colonne (2026-09-07, même retour que la
   feuille de réservation) : une action large de 480 px pour deux mots se lit
   comme une bande, pas comme une cible. Il se dimensionne sur son libellé et
   s'aligne sur le bord gauche des champs qu'il conclut. */
#partner-form .btn-lg { margin-top: var(--space-6); width: auto; min-width: 200px; }
.partner-success { margin-top: var(--space-5); padding: var(--space-5) var(--space-6); border: 1px solid var(--accent-blue); border-radius: var(--radius); background: var(--surface-inset); }
.partner-success[hidden] { display: none; }
/* Le chemin d'un menu, écrit sous le lien qu'il faut y coller. Deux lignes,
   un nom d'application en tête, des chevrons entre les étapes : rien à
   cliquer ici, c'est une instruction, pas un contrôle. */
.sub-howto { list-style: none; margin: var(--space-4) 0 0; padding: 0; display: grid; gap: var(--space-2); }
.sub-howto li { font-size: var(--type-body); line-height: var(--type-body-lh); color: var(--ink-muted); }
.sub-howto-app { display: inline-block; margin-right: var(--space-3); font-weight: 800; color: var(--ink); }
.sub-howto-sep { margin: 0 4px; color: var(--border-strong); }

/* The .ics download in the two subscription modals and its footnote: the
   button becomes block-level so the gaps are the margins themselves, not the
   invisible line box an inline <a> would bring — the footnote was sitting on
   the button's bottom border (propriétaire, 2026-09-26). */
#sub-subscription-dialog .sub-btn,
#notary-subscription-dialog .sub-btn { display: flex; width: fit-content; margin-top: var(--space-8); }
/* Any button and the .help footnote directly under it is one unit — the
   control and its explanation. The form-row 4px below does not read under a
   button, where the eye needs the note to belong to it, not touch it. */
.btn + .help { margin-top: var(--space-5); }

/* The ready-to-send message under the claimed link: a quiet inset block the
   partner reads once, then its own copy button. */
.partner-msg { margin-top: var(--space-6); padding-top: var(--space-5); border-top: 1px solid var(--border); }
.partner-results { margin-top: var(--space-8); padding-top: var(--space-6); border-top: 1px solid var(--border); }
/* LES QUATRE GESTES DU PARTENAIRE (plan 2026-09-22 §4.3) — une rangée, un
   chiffre et un mot par étape. Le trait du haut dit l'état : à venir (filet),
   actuel (la marque, en encre lisible sur la nuit), fait (l'encre). Rien n'est
   cliquable ; les libellés restent à l'encre (registre d'encre). */
.partner-journey { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-3); margin: 0 0 var(--space-6); padding: 0; list-style: none; }
.partner-journey[hidden] { display: none; }
.partner-journey > li { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; padding-top: var(--space-3); border-top: 2px solid var(--border); color: var(--ink-muted); font-size: var(--type-eyebrow); }
.partner-journey-num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.partner-journey-label { overflow-wrap: anywhere; }
.partner-journey > li[data-etat='actuel'] { border-top-color: var(--brand-on-surface); color: var(--ink); font-weight: 700; }
.partner-journey > li[data-etat='fait'] { border-top-color: var(--ink); color: var(--ink); }
/* Étape 4 — « Être payé » : un titre, une ligne, un bouton en contour. */
.partner-payout { margin-top: var(--space-8); padding-top: var(--space-6); border-top: 1px solid var(--border); }
.partner-payout[hidden] { display: none; }
.partner-payout > .h-sub { margin: 0 0 var(--space-4); }
.partner-payout #partner-payout { margin-top: var(--space-3); }
.partner-results > .h-sub { margin: 0 0 var(--space-4); }
.partner-results-flow { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin: 0 0 var(--space-4); padding: 0; list-style: none; color: var(--ink-muted); font-size: var(--type-eyebrow); }
.partner-results-flow li { display: flex; align-items: center; gap: var(--space-4); }
.partner-results-flow li:not(:last-child)::after { content: '→'; color: var(--accent-blue); }
.partner-results form { display: grid; gap: var(--space-3); }
.partner-results form[hidden] { display: none; }
.partner-results form .btn { justify-self: start; }
.partner-results-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); margin: var(--space-5) 0; }
.partner-results-metrics[hidden] { display: none; }
.partner-results-metrics > div { min-width: 0; }
.partner-results-metrics dt { color: var(--ink-muted); font-size: var(--type-eyebrow); }
.partner-results-metrics dd { margin: var(--space-2) 0 0; font-family: var(--font-mono); font-weight: 700; color: var(--ink); overflow-wrap: anywhere; }
.partner-msg-text { margin: var(--space-3) 0 var(--space-4); padding: var(--space-4) var(--space-5); font-size: 13px; line-height: 1.5; color: var(--ink); background: var(--surface-inset); border-radius: var(--radius-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
/* A copy button that just worked: the label reads « Copié ✓ » in the ok ink for a beat (app.js flashCopied). */
.btn.is-copied { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.partner-link-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
/* user-select: all — un clic prend tout le lien, même là où le presse-papiers
   n'existe pas (le bouton « Copier » le dit alors, sans prétendre avoir copié). */
.partner-link-row code { min-width: 0; font-family: var(--font-mono); font-size: 13px; padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); overflow-wrap: anywhere; user-select: all; transition: border-color var(--motion-fast) var(--ease-ui); }
.partner-link-row code:hover { border-color: var(--action-ink); }
.pr-claim .partner-active-code { margin: var(--space-5) 0 var(--space-7); font-family: var(--font-mono); font-size: clamp(20px, 2.2vw, 26px); font-weight: 700; letter-spacing: .03em; color: var(--accent-blue); overflow-wrap: anywhere; }
/* « Ce qui manque encore » : une ligne tenue sous les champs, qui passe à
   l'encre vivante quand tout est prêt. */
.partner-next-step { padding-left: var(--space-5); border-left: 2px solid var(--border-strong); line-height: 1.6; transition: border-color var(--motion-ui) var(--ease-ui), color var(--motion-ui) var(--ease-ui); }
.partner-next-step[data-ready='true'] { border-color: var(--accent-warm); color: var(--brand-on-surface); }
:is(.pr-claim, .pr-claim-dialog) .partner-rappel-line { margin: var(--space-8) 0 0; line-height: 1.6; }
:is(.pr-claim, .pr-claim-dialog) .partner-pending-to { font-size: 14px; line-height: 1.6; overflow-wrap: anywhere; }
/* Le message prêt à envoyer garde sa taille de lecture dans le panneau. */
.pr-claim .partner-msg-text { margin: var(--space-3) 0 var(--space-4); font-size: 13px; }
#partner-pending-email { display: block; color: var(--ink); }
#partner-pending { border-color: var(--brand); }
#partner-email-edit { min-height: 44px; margin-top: var(--space-4); }
/* L'aperçu du lien est une mention vivante : l'encre des états, lisible aussi
   sur la nuit (le bleu de marque nu y tombe sous AA). */
#partner-code-preview[data-state='ok'] { color: var(--brand-on-surface); }

/* LE MOUVEMENT DE LA PORTE (2026-09-20) — quatre gestes à elle, sobres, sur les
   barreaux du registre et la seule courbe --ease-ui :
     pr-num-ink      le chiffre de chaque étape s'encre, de 1 à 4, à la suite du
                     trait d'accent que .accent-line trace au-dessus de lui ;
     pr-thread-draw  le filet de 2 px file du chiffre vers l'étape suivante ;
     pr-state-in     le bloc d'état qui apparaît dans le panneau (formulaire,
                     reçu « en attente », code réclamé, notaire) entre en
                     fondu-montée sur le barreau des panneaux : `hidden` tombe,
                     l'élément est rendu, l'animation part — aucun script ;
     pr-link-land    l'aperçu du lien se pose quand le code devient valide (il
                     ne rejoue pas à chaque frappe : l'état reste « ok »).
   Les montants s'allument UNE fois (amtGlow) : un éclat revient à son repos,
   sa durée est donc un jeton local (--pr-glow-beat), hors des trois barreaux.
   Tout vit sous « no-preference » : en mouvement réduit rien n'attend à l'état
   « from ». Les chiffres ne s'animent que sous .is-in (posé par wireReveal) :
   sans script ni IntersectionObserver ils sont simplement là. L'état FINAL de
   chaque keyframe est l'état lisible. */
@media (prefers-reduced-motion: no-preference) {
  .pr-steps.is-in .pr-step-num { animation: pr-num-ink var(--motion-enter) var(--ease-ui) both; animation-delay: calc(var(--motion-ui) + var(--reveal-i, 0) * var(--menu-stagger)); }
  .pr-steps.is-in .pr-step-num::after { animation: pr-thread-draw var(--motion-enter) var(--ease-ui) both; animation-delay: calc(var(--motion-enter) + var(--reveal-i, 0) * var(--menu-stagger)); }
  .pr-steps.is-in .pr-step-detail strong { animation: amtGlow var(--pr-glow-beat) var(--ease-ui) both; animation-delay: calc(var(--motion-enter) * 2 + var(--reveal-i, 0) * var(--menu-stagger)); }
  #partner-form:not([hidden]), #partner-pending:not([hidden]), #partner-success:not([hidden]), #partner-notary-block:not([hidden]) { animation: pr-state-in var(--motion-ui) var(--ease-ui) both; }
  #partner-code-preview[data-state='ok'] { animation: pr-link-land var(--motion-ui) var(--ease-ui) both; }
}
@keyframes pr-num-ink { from { opacity: 0; transform: translateY(var(--space-3)) scale(.9); } to { opacity: 1; transform: none; } }
@keyframes pr-thread-draw { from { transform: scaleX(0); } to { transform: none; } }
@keyframes pr-state-in { from { opacity: 0; transform: translateY(var(--space-4)); } to { opacity: 1; transform: none; } }
@keyframes pr-link-land { from { opacity: 0; transform: translateY(calc(var(--space-3) * -1)); } to { opacity: 1; transform: none; } }

/* --- Dossier ------------------------------------------------------------- */
.dossier-bar { height: 8px; background: var(--surface-inset); border-radius: var(--radius-xs); overflow: hidden; margin: 8px 0; }
.dossier-bar > span { display: block; width: 0; height: 100%; background: var(--brand); }
/* The gate line (what's left before the demand is sellable) renders inside
   the consent card — right where the gate completes (ADR 0010 §3). */
.dossier-missing { min-height: 1.5em; font-size: 13px; color: var(--ink-muted); margin-top: 8px; }
.dossier-missing[data-ready='true'] { color: var(--ok); font-weight: 700; }
/* Heading over the preparation checklist — carries the 0/N count and bar of
   the list it measures (progress, never a gate, ADR 0010 §3). */
.dossier-prep-h { display: flex; flex-direction: column; gap: 2px; padding: 6px 2px 0; }
.dossier-prep-h strong { font-size: 14px; letter-spacing: -0.01em; }
.dossier-prep-count { font-size: 13px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
/* Upload and "already sent" side by side — two channels, one row. */
.doc-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
/* The drag hint rides the actions row (inline, zeroed stacking margin) — its
   own line cost every empty card ~20px for eight muted words. The picker's own
   stacking margin is zeroed too: the row already carries the spacing. */
.doc-actions .file-note, .doc-actions .file-field { margin-top: 0; }
/* "Transmis autrement" (ADR 0010 §4): a distinct provided-state — the ok hue
   says done, the undo keeps it reversible. */
.doc-transmis { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.doc-transmis-lbl { font-size: 13px; font-weight: 700; color: var(--ok); }
.consent-toggle { display: inline-flex; align-items: center; gap: 8px; margin-top: 8px; font-size: 13px; font-weight: 600; cursor: pointer; }
.consent-toggle input { width: 18px; height: 18px; accent-color: var(--brand); cursor: pointer; }
/* Dossier: price card, consent, then the document checklist. The checklist
   packs into a card GRID — several small pieces per row instead of one
   full-width line each, so 7 items don't scroll for a screen and a half. */
.dossier-list { display: flex; flex-direction: column; gap: 8px; padding: 4px 0; }
.dossier-item { display: flex; gap: 10px; align-items: flex-start; padding: var(--space-5) var(--space-6); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.dossier-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px; }
/* Each checklist card: its name and « i » on top, the action (upload /
   already-sent, or the answer field) pinned to the bottom so neighbouring
   cards end on the same line whatever the length of their name. */
.dossier-row .dossier-body { display: flex; flex-direction: column; align-self: stretch; }
.dossier-row .doc-actions, .dossier-row .doc-transmis,
.dossier-row .dossier-body > input[type='text'] { margin-top: auto; }
.dossier-row .dossier-body > .tip-line { margin-bottom: var(--space-4); }
.dossier-pricing { padding: var(--space-6) var(--space-7); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
/* Capped at 3 tracks: auto-fit on a wide screen spread 6 questions into one
   ragged row of uneven heights — 3×2 keeps the rows balanced and the help
   text wrapping instead of a dead right half. */
.dossier-pricing .o-criteria { margin-top: var(--space-6); display: flex; flex-direction: column; gap: var(--space-10); }
.dossier-pricing .crit-sec-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-5) var(--space-8); }
@media (max-width: 1100px) { .dossier-pricing .crit-sec-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* The « i » rides its question's line at every width (owner, 2026-10-03:
   « the i must not be on two lines »): the label wraps inside its own box and
   the marks never drop under it, checkbox questions included (they stacked
   label and mark in a column). The booking dialog pins its marks to a rail
   for the same reason (#day-dialog .o-criteria). info-tip-line.test.mjs */
.dossier-pricing :is(.crit-head, .crit-text) { flex-direction: row; flex-wrap: nowrap; align-items: center; }
.dossier-pricing :is(.crit-head, .crit-text) > .crit-label { min-width: 0; overflow-wrap: anywhere; }
/* The same contract for any label and its « i » (labelTip in app.js — the
   profile's cards, fields, dossier headings and pieces; owner, 2026-10-03:
   « privilege résumé and i »): one row that never wraps, a label that wraps
   inside its own box, a mark that keeps its size. profile-text-budget.test.mjs */
.tip-line { display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center; min-width: 0; }
.tip-line > :first-child { min-width: 0; margin: 0; overflow-wrap: anywhere; }
.tip-line > .itip { flex: none; }
@media (max-width: 680px) { .dossier-pricing .crit-sec-grid { grid-template-columns: minmax(0, 1fr); } }
/* Les questions du notaire en SECTIONS (2026-09-05) : trois temps déclarés par
   le domaine — le prêt, l'immeuble, la signature. La pile des sections est
   verticale ; c'est DANS une section que les questions s'alignent sur la grille
   à deux colonnes (audit 2.15 : l'ordre de lecture est l'ordre du DOM, jamais
   du multicol qui coulait la question 3 sous la question 1). Les lignes
   s'alignent en haut, et le grand contrôle du déplacement traverse la grille. */
#o-criteria { display: flex; flex-direction: column; gap: var(--space-6); }
.crit-sec-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6) var(--space-9); align-items: start; }
@media (max-width: 560px) { .crit-sec-grid { grid-template-columns: minmax(0, 1fr); } }
#o-criteria .crit-row { margin: 0; }
.crit-row--wide { grid-column: 1 / -1; }
/* Une question seule sur sa ligne PREND la ligne (retour du propriétaire,
   2026-09-05 : « avoid blank space ») : trois questions dans une grille à deux
   colonnes laissaient une demi-carte vide à droite de la troisième, et une
   section qui n'en pose qu'une (« L'immeuble ») laissait la moitié du cadre
   nu. Le dernier enfant d'un rang impair traverse la grille. */
.crit-sec-grid > .crit-row:last-child:nth-child(odd) { grid-column: 1 / -1; }
/* Et un rang qui PORTE TROIS CHOIX prend la ligne quoi qu'il arrive : dans une
   demi-largeur, « Approbation bancaire » s'empilait en trois lignes à côté d'un
   champ d'une seule — deux cellules d'une même paire, l'une trois fois plus
   haute que l'autre (retour du propriétaire, 2026-09-06). Sur la ligne entière
   les trois choix se rangent côte à côte, et la paire au-dessus est faite de
   deux rangs de même taille. Le tri du DOM (renderCriteriaSections) fait
   descendre ces rangs sous les compacts, donc aucune demi-cellule ne reste
   vide au-dessus d'eux. */
.crit-sec-grid > .crit-row[data-span='row'] { grid-column: 1 / -1; }
/* Et le rang compact resté SEUL DEVANT une bande prend la ligne à son tour
   (retour du propriétaire, 2026-09-07 : « enlever tous les espaces blancs ») :
   « Prêteur hypothécaire » gardait un demi-cadre nu à sa droite parce que la
   question suivante traversait déjà la grille — la règle du dernier rang impair
   ne voyait que la FIN de la section. Le filet CSS tient même là où rien n'est
   mesurable (dialogue fermé, impression) ; la passe mesurée, elle, évite le cas
   en faisant d'abord tenir les réponses sur une ligne. */
.crit-sec-grid > .crit-row:nth-child(odd):has(+ .crit-row[data-span='row']),
.crit-sec-grid > .crit-row:nth-child(odd):has(+ .crit-row--wide) { grid-column: 1 / -1; }
/* Et la dernière rangée de rangs compacts se remplit, quel que soit le nombre
   de colonnes : packCriteriaGrids compte les pistes réelles et dit ici combien
   il en reste à couvrir (le carnet du dossier en dessine trois, la feuille
   deux). Deux valeurs suffisent : aucune grille du produit ne va plus loin. */
.crit-sec-grid > .crit-row[data-fill='2'] { grid-column: span 2; }
.crit-sec-grid > .crit-row[data-fill='3'] { grid-column: span 3; }
/* Et elle OCCUPE la ligne au lieu de s'y perdre : les réponses en boutons
   s'étalent sur toute la largeur (flex: 1 1 auto), et une liste s'allonge
   jusqu'à une largeur de lecture — un contrôle de 320 px suivi d'un demi-cadre
   nu était le vide le plus visible de la grille. */
@media (min-width: 561px) {
  .crit-sec-grid > .crit-row:last-child:nth-child(odd) > .nselect,
  .crit-sec-grid > .crit-row:nth-child(odd):has(+ .crit-row[data-span='row']) > .nselect,
  .crit-sec-grid > .crit-row:nth-child(odd):has(+ .crit-row--wide) > .nselect,
  .crit-sec-grid > .crit-row[data-fill] > .nselect { max-width: 420px; }
}
/* Les écrans qui portent des SECTIONS ne portent plus leur propre cadre : deux
   boîtes emboîtées mangeaient 30 px de largeur de chaque côté et donnaient au
   dialogue trois niveaux de bordure. Le titre de l'écran coiffe les cartes. */
#o-criteria-step, .book-step-offer { padding: 0; border: 0; background: none; }
/* Une section est une CARTE, pas un simple filet (retour du propriétaire,
   2026-09-05 : « mieux découpler les sections, ajouter des petites marges »).
   Un trait à gauche ne suffisait pas : le titre de la section suivante venait
   coller à la dernière réponse de la précédente, et les trois temps se lisaient
   comme une seule colonne. Cadre + coin carré + air autour, et 20 px entre les
   cartes. Le FOND reste celui du dialogue : les contrôles à l'intérieur
   (segments, listes, volets) portent déjà --surface-inset, et remplir la carte
   de la même teinte les effacerait. */
.crit-sec, .offer-sec {
  display: flex; flex-direction: column; gap: var(--space-6);
  padding: var(--space-7) var(--space-8) var(--space-8);
  border: 1px solid var(--border); border-radius: var(--radius);
}
/* L'écran 3 emprunte le même registre (voir index.html) : trois cartes — le
   montant, le marché, le devis — au lieu de dix blocs à la file. Ses contenus
   se serrent un cran de plus que des questions : ce sont des lignes de lecture,
   pas des contrôles à viser. */
.offer-sec { gap: var(--space-5); }
.book-step-offer { gap: var(--space-8); }
/* Les boîtes intérieures se sont FONDUES dans leur carte : le marché et le
   devis portaient chacun leur propre cadre + fond, ce qui faisait deux cadres
   l'un dans l'autre dès qu'ils sont entrés en section. La carte est le cadre. */
.offer-sec > .day-market, .offer-sec > .devis {
  margin: 0; padding: 0; border: 0; background: none; border-radius: 0;
}
.offer-sec > .day-market .day-bids { margin: var(--space-4) 0 0; }
/* L'en-tête est une bande : le titre, son compte, sa raison d'être — puis un
   filet qui la sépare des questions. */
/* L'en-tête est une BANDE, pas un titre suivi d'un filet (2026-09-06) : elle
   court d'un bord à l'autre de la carte, sur sa propre teinte, et coiffe le
   sujet comme l'en-tête d'un tableau coiffe sa colonne. Un titre posé DANS le
   cadre, à la même distance des questions que les questions entre elles, ne
   disait pas où un sujet commençait. Les marges négatives valent le padding de
   .crit-sec — la bande est le haut de la carte, pas un bloc posé dedans. */
.crit-sec-h {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-5);
  margin: calc(-1 * var(--space-7)) calc(-1 * var(--space-8)) 0;
  padding: var(--space-5) var(--space-8);
  background: var(--surface-inset);
  border-bottom: 1px solid var(--border);
  border-radius: var(--radius) var(--radius) 0 0;
}
/* Repliée, la bande EST la carte : plus de trait sous elle, rien en dessous. */
.crit-sec[data-fold='true'] > .crit-sec-h { border-bottom-color: transparent; border-radius: var(--radius); }
.crit-sec[data-fold='true'] > .crit-sec-grid,
.crit-sec[data-fold='true'] > .crit-more { display: none; }
.crit-sec[data-fold='true'] { padding-bottom: 0; gap: 0; }
/* Le chevron du pli : muet, à droite du compte, tourné vers le bas quand la
   section est ouverte. Il porte le nom de sa section en aria-label — le titre
   est à deux centimètres, il n'a pas de mot à ajouter. */
.crit-sec-fold {
  appearance: none; border: 0; background: none; padding: 0; margin-left: var(--space-3);
  width: 24px; height: 24px; display: inline-grid; place-items: center; flex: none;
  color: var(--ink-muted); cursor: pointer; border-radius: var(--radius-xs);
  transition: color var(--motion-fast), background-color var(--motion-fast);
}
.crit-sec-fold:hover { color: var(--ink); background: var(--action-tint); }
.crit-sec-fold .crit-sec-chev { transition: transform var(--motion-fast) var(--ease-ui); }
.crit-sec-fold[aria-expanded='false'] .crit-sec-chev { transform: rotate(-90deg); }
/* Le compte cesse de pousser le chevron au bout de la ligne : c'est LUI qui
   ferme la bande. */
.crit-sec-h:has(.crit-sec-fold) .crit-sec-n { margin-left: auto; }
/* Le titre de section portait 14px/700 quand la question en dessous porte
   13,5px/600 : un cheveu d'écart, donc aucune hiérarchie — l'œil ne trouvait
   pas où un sujet commençait (retour du propriétaire, 2026-09-06 : « comprendre
   d'un coup d'œil que ceci se rapporte à tel sujet »). Un vrai cran au-dessus. */
.crit-sec-t { font-size: 16px; font-weight: 800; color: var(--ink); letter-spacing: -0.01em; }
/* La pastille du sujet : même construction que celle des actes (glyphe au trait
   dans un carré à filet), un cran plus petite — c'est un en-tête, pas une
   cible. Elle passe à la marque quand la section est complète, si bien que
   l'avancement se lit sur la colonne des pastilles sans lire un seul compte. */
.crit-sec-ic {
  width: 28px; height: 28px; flex: none; display: inline-grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-inset); color: var(--ink-muted);
  transition: color var(--motion-fast), border-color var(--motion-fast), background var(--motion-fast);
}
.crit-sec:has(.crit-sec-n[data-state='done']) .crit-sec-ic {
  color: var(--brand); border-color: color-mix(in srgb, var(--brand) 45%, var(--border));
  background: transparent;
}
/* La raison d'être de la section prend toute la ligne suivante : elle explique,
   elle ne se dispute pas la première ligne avec le compte. Elle s'aligne sous
   le TITRE, pas sous la pastille : le bloc d'en-tête se lit alors comme un
   seul objet au lieu de deux lignes qui commencent à deux endroits. */
/* Elle RIDE la ligne du titre (2026-09-07) : une ligne entière par section
   pour se présenter, c'était trois lignes dépensées avant la première question
   — et l'écran s'ouvrait sur des bandes plutôt que sur des réponses à donner.
   Elle ne bascule sous le titre que si la largeur lui manque (flex-basis), et
   le compte reste collé au bord droit par sa marge automatique. */
.crit-sec-aide { flex: 1 1 240px; min-width: 0; margin: 0; font-size: var(--type-h4); color: var(--ink-muted); }
/* Sur un téléphone elle ne tient jamais à côté du titre : elle repasse SOUS la
   bande — mais après le compte et le chevron, sinon c'est le chevron qui tombe
   sur une troisième ligne et la bande coûte plus cher qu'avant. */
@media (max-width: 560px) {
  .crit-sec-aide { order: 1; flex: 1 0 100%; margin-top: var(--space-1); }
}
/* Le compte de la section — le même vocabulaire et les mêmes couleurs que le
   compte de l'étape : ambre tant qu'on attend, marque une fois complet. */
.crit-sec-n {
  margin-left: auto; min-width: 18px; padding: 1px 5px; border-radius: var(--radius-xs);
  background: transparent; border: 1px solid var(--warn);
  font-size: var(--type-h4); font-weight: 700; color: var(--warn); text-align: center;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.crit-sec-n[data-state='done'] { color: var(--brand); background: transparent; border-color: var(--brand); }
/* Les précisions d'une section, repliées à côté du cas qu'elles précisent. */
.crit-sec > .crit-more { margin: var(--space-1) 0 0; }
.crit-sec > .crit-more .crit-sec-grid { padding: var(--space-6) var(--space-7) var(--space-4); }
/* Le volet dit combien de questions il cache. */
.crit-more-n {
  margin-left: 7px; min-width: 16px; padding: 1px 5px; border-radius: var(--radius-xs);
  background: var(--surface-inset); color: var(--ink-muted);
  font-size: var(--type-h4); font-weight: 700; font-variant-numeric: tabular-nums;
}
/* Ce qu'une réponse change à la liste des documents, sous la réponse. Ligne
   RÉSERVÉE dès la première peinture et masquée par `visibility` (registre ADR
   0039) : répondre, se raviser, répondre à nouveau ne déplace jamais la
   question suivante. */
.crit-effet { font-size: var(--type-h4); color: var(--ink-muted); }
.crit-effet[data-on='false'] { visibility: hidden; }
.crit-effet-k { font-weight: 600; color: var(--brand); }
/* The card's payoff — the determined price — separated from the questions and
   said a size louder, not buried in the bottom-left corner. */
.dossier-price { margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px dashed var(--border); font-weight: 700; color: var(--ink); font-size: 15px; font-variant-numeric: tabular-nums; }
/* Profile: a top row of at-a-glance / set-once cards (offers · coordinates ·
   notifications) that auto-fit across the width, then a full-width documents
   work area below — no tall-thin void, honest use of the horizontal space. */
.profil-stack { display: flex; flex-direction: column; gap: 10px; }
/* Every section is a full-width band whose content lays out in a grid that
   fills the width (auto-fit) — no empty right half, no ragged inter-card void. */
/* Four equal tracks: the profile's four fields make ONE row on a wide screen —
   the whole Coordonnées band is head + a single line of inputs. Short values
   (code postal) simply breathe in theirs. */
.profil-fields { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px 16px; align-items: end; }
@media (max-width: 1000px) { .profil-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .profil-fields { grid-template-columns: 1fr; } }
.profil-fields .form-row { margin: 0; }
/* Fill the cell: capped at 460px the inputs left ~100px of dead track beside
   each one, which read as a broken grid rather than as breathing room. */
.profil-fields .form-row input { width: 100%; }
.profil-switches { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 22px; max-width: 1060px; }
@media (max-width: 900px) { .profil-switches { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px) { .profil-switches { grid-template-columns: 1fr; } }
.profil-card { padding: 12px 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); display: flex; flex-direction: column; gap: 6px; }
.profil-card-title { font-size: 15px; font-weight: 700; color: var(--ink); }
/* Iconed section header so the bands are easy to tell apart at a glance. The
   caption (.profil-card-sub) rides the same row, wrapping under the title only
   when the width runs out — a full line per card was pure chrome. */
.profil-card-head { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; }
.profil-card-sub { flex: 1 1 320px; min-width: 0; margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--ink-muted); }
.profil-card-ic { display: grid; place-items: center; width: 26px; height: 26px; flex: none; border-radius: var(--radius-sm); background: var(--surface-inset); color: var(--brand); }
.profil-card-ic svg { display: block; }
/* The card is a flex column with its own gap — zero the children's stacking
   margins so field/switch spacing isn't doubled (the source of the void). */
.profil-card .form-row { margin-bottom: 2px; }
.profil-card .form-row label.lbl { margin-bottom: 6px; }
.profil-card .switch-row { padding: 4px 0; justify-content: flex-start; gap: 12px; }
.profil-card .help { margin-top: 0; }
/* Mes offres — the client's posted offers with live status. */
.my-offer:hover, .my-offer:focus-visible { border-color: var(--action-ink); outline: none; }
/* Documents inside the profile: upload / remove / mark-validated. */
.profil-doc-chips { margin: 2px 0 6px; }
.dossier-sync { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-5); padding-top: var(--space-5); }
.dossier-sync .help { flex: 1 1 auto; margin: 0; }
.dossier-sync [data-state='error'] { color: var(--danger); }
.profil-docs #dossier-surface { padding: 0; margin: 0; border: 0; border-radius: 0; background: transparent; min-width: 0; }
.profil-docs .dossier-grid { min-width: 0; grid-template-columns: repeat(auto-fill, minmax(min(calc(var(--control-height) * 6), 100%), 1fr)); gap: var(--space-5); }
.profil-docs .dossier-body { min-width: 0; overflow-wrap: anywhere; }
.profil-docs .doc-file-name { min-width: 0; flex: 1; overflow: visible; text-overflow: clip; white-space: normal; overflow-wrap: anywhere; }
#profil-dossier-title { scroll-margin-top: calc(var(--sticky-top) + var(--space-8)); }
#profil-dossier-title:focus-visible { outline: var(--space-1) solid var(--brand); outline-offset: var(--space-2); }
/* First-time empty state (Mes offres with no offers). */
/* Mes offres — four facts per offer, on one axis. Full-width bands spent about
   1500px of horizontal space per row saying four short things, and made seven
   offers an 800px wall. */
.my-offers { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.my-offers th {
  text-align: left; padding: 0 10px 6px;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
  color: var(--ink-muted); border-bottom: 1px solid var(--border);
}
.my-offers td { padding: 9px 10px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.my-offers tbody tr:last-child td { border-bottom: 0; }
.my-offers .c-acte { font-weight: 600; min-width: 150px; }
.my-offers .c-date { width: 24%; min-width: 120px; }
.my-offers .c-montant { width: 16%; min-width: 92px; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.my-offers .c-statut { width: 14%; min-width: 96px; }
/* The date is a door to that day on the carnet: a real button, styled as a link. */
.my-offer-day { display: block; padding: 0; border: 0; background: none; font: inherit; font-weight: 600; color: var(--brand); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; text-align: left; }
.my-offer-day:hover { color: var(--ink); }
.my-offer-day:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 2px; border-radius: var(--radius-xs); }
.my-offer-rel { display: block; margin-top: 1px; font-size: 12px; color: var(--ink-muted); }
/* Under each row: the next step, the agenda links, and what a notary sent back. */
.my-offer-detail td { padding: 4px 10px 12px; }
.my-offer-next { display: flex; flex-wrap: wrap; gap: 4px 8px; font-size: 12.5px; line-height: 1.45; }
.my-offer-next-k { font-weight: 700; color: var(--ink-muted); text-transform: uppercase; font-size: 10.5px; letter-spacing: .03em; align-self: center; }
.my-offer-next-v { color: var(--ink); }
.my-offer-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
/* Le reçu du client : deux lignes et un total, dans le registre carré de la
   carte. Il paraît une fois l'acte réglé, au-dessus de l'évaluation. */
.my-offer-receipt { margin-top: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-inset); }
.my-offer-receipt-h { font-weight: 700; color: var(--ink-muted); text-transform: uppercase; font-size: 10.5px; letter-spacing: .03em; margin-bottom: 6px; }
.my-offer-receipt-lines { display: grid; grid-template-columns: 1fr auto; gap: 3px 12px; margin: 0; font-size: 12.5px; }
.my-offer-receipt-lines dt { color: var(--ink-muted); margin: 0; }
.my-offer-receipt-lines dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; color: var(--ink); }
.my-offer-receipt-lines .my-offer-receipt-total { font-weight: 700; color: var(--ink); border-top: 1px solid var(--border); padding-top: 4px; margin-top: 2px; }
/* ADR 0035 — la carte a été refusée au moment de réserver la somme. Un bloc
   qui se voit (filet rouge, fond teinté) sans crier : rien n'est perdu, mais
   il y a un geste à poser avant la signature. */
.my-offer-carte {
  margin-top: var(--space-4); padding: var(--space-4) var(--space-5);
  border: 1px solid var(--danger); border-left-width: 3px; border-radius: var(--radius-sm);
  background: var(--surface-inset);
}
.my-offer-carte-h { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--danger); }
.my-offer-carte-t { margin: var(--space-2) 0 0; font-size: 13px; line-height: 1.5; color: var(--ink); }
/* Mise en relation (ADR 0010 §4): whom to contact once the offer is retained. */
.my-offer-contact { margin-top: 8px; font-size: 13px; line-height: 1.5; }
.my-offer-contact-mail { color: var(--brand); font-weight: 600; }
/* « Votre notaire » (ADR 0033): heading, name line, then one row per way to
   reach them — tel:, mailto:, the étude on a map. Facts only, square register. */
.my-offer-contact-h { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-muted); }
.my-offer-contact-name { margin-top: var(--space-2); font-size: 14px; }
.my-offer-contact-etude { color: var(--ink-muted); }
.my-offer-contact-rows { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-8); margin-top: var(--space-2); }
.my-offer-contact-row {
  display: inline-flex; align-items: center; gap: 6px; min-height: 28px;
  font-size: 13px; font-weight: 600; color: var(--brand); text-decoration: none; overflow-wrap: anywhere;
}
.my-offer-contact-row:hover { text-decoration: underline; }
.my-offer-contact-tel { font-variant-numeric: tabular-nums; }
.my-offer-contact-addr { font-weight: 500; }
/* Unread notary messages beside the status: a door to the thread, a filled
   square badge while there is something to read, nothing when there is not. */
.my-offer-unread {
  display: inline-flex; align-items: center; margin-left: var(--space-3); padding: 2px 8px;
  border: 0; font: inherit; font-size: 11px; font-weight: 800; white-space: nowrap; cursor: pointer;
  border-radius: var(--radius-sm); background: var(--brand); color: var(--brand-ink);
}
.my-offer-unread:empty { display: none; }
.my-offer-unread:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 2px; }
/* A notary's proposition / document request: the block that asks for a decision. */
.my-offer-prop, .my-offer-demande {
  margin-top: 10px; padding: 10px 12px; border-radius: var(--radius);
  border: 1px solid var(--brand); border-left-width: 4px; background: var(--surface-inset);
}
.my-offer-prop[data-status='acceptee'] { border-color: var(--ok); }
/* Settled blocks recede through muted ink, never element opacity. */
.my-offer-prop[data-status='refusee'], .my-offer-demande[data-fournie='true'] { border-color: var(--border); color: var(--ink-muted); }
.my-offer-prop-text { font-size: 13.5px; line-height: 1.45; }
.my-offer-prop-msg { margin: 6px 0 0; font-size: 12.5px; color: var(--ink-muted); font-style: italic; }
.my-offer-prop-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.my-offer-prop-state { margin-top: 6px; font-size: 12.5px; font-weight: 700; color: var(--ink-muted); }
.my-offer-prop-state.is-done { color: var(--ok); }
.my-offer-status {
  display: inline-block; padding: 2px 9px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); color: var(--ink-muted);
  font-size: 11px; font-weight: 700; white-space: nowrap;
}
.my-offer-status[data-status='approved'] { color: var(--ok); border-color: var(--ok); }
.my-offer-status[data-status='pending'] { color: var(--brand); border-color: var(--brand); }
.my-offer-status[data-status='expired'] { color: var(--ink-muted); border-style: dashed; }
/* Offers whose date has passed are present, never diluting the live list:
   they read in their modal (#my-offers-past-dialog), in the muted ink. */
.my-offers-past tbody tr { color: var(--ink-muted); }
/* A card's button that opens a modal keeps its own width in the card's column. */
.profil-open { align-self: flex-start; min-height: 44px; }

/* Below this a table cannot hold four columns without wrapping the act name to
   three lines, so each row becomes a card. The status pill gets its OWN row and
   may wrap: pinned beside the act name it forced the card past a 320px screen
   ("Ouverte — en attente d’un notaire" is 200px that must never be cut). */
@media (max-width: 860px) {
  .my-offers, .my-offers tbody, .my-offers tr, .my-offers td { display: block; width: auto; }
  .my-offers thead { display: none; }
  .my-offers tbody { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 8px; }
  .my-offers tbody tr {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: 'acte acte' 'date montant' 'statut statut';
    gap: 4px 10px; align-items: center;
    padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius);
  }
  .my-offers td { padding: 0; border: 0; min-width: 0; }
  /* The detail band joins its row's card on a phone (one card per offer). */
  .my-offers tbody tr.my-offer { border-bottom: 0; border-radius: var(--radius) var(--radius) 0 0; }
  .my-offers tbody tr.my-offer-detail { display: block; margin-top: -8px; padding: 0 12px 10px; border: 1px solid var(--border); border-top: 0; border-radius: 0 0 var(--radius) var(--radius); }
  .my-offers .c-acte { grid-area: acte; }
  .my-offers .c-statut { grid-area: statut; justify-self: start; }
  .my-offers .c-date { grid-area: date; }
  .my-offers .c-montant { grid-area: montant; }
  .my-offer-status { white-space: normal; }
}
@media (max-width: 560px) { .my-offers tbody { grid-template-columns: minmax(0, 1fr); } }

/* Empty states are one compact row — the pitch left, the CTA right. The tall
   centered block spent ~250px of band height on one sentence and a button. */
.profil-empty { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 18px; padding: 2px 0; }
.profil-empty-text { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-muted); max-width: 64ch; }
.profil-empty .btn { flex: none; }
.profil-empty-copy { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
/* Parrainage — the claimed code, big enough to read out loud; the link row
   reuses .partner-link-row from the Partenaires pane. */
.parr-code-row { display: flex; align-items: baseline; gap: 10px; }
.parr-code-lbl { font-size: 12px; font-weight: 600; color: var(--ink-muted); }
.parr-code { font-family: var(--font-mono); font-size: 18px; letter-spacing: 0.06em; color: var(--ink); }
.doc-valid { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--ink-muted); white-space: nowrap; cursor: pointer; }
.doc-valid input { accent-color: var(--brand); }
.doc-file { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 12.5px; color: var(--ink-muted); }
.doc-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The ✓ only shows once the item is actually provided — a blue check on
   every empty row made 0/8 read as done. */
.dossier-check { flex: none; margin-top: 2px; width: 18px; height: 18px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); display: grid; place-items: center; color: transparent; font-weight: 800; font-size: 12px; }
.dossier-check[data-on='true'] { background: transparent; color: var(--brand); border-color: var(--brand); }
.dossier-body { flex: 1; min-width: 0; }
.dossier-name { font-weight: 600; font-size: 13.5px; }
/* A piece's explanation waits in its name's « i » (labelTip), provided or
   not: nothing prints under the name any more, so nothing has to be hidden
   once the piece is in. The check faces its name, centred on the « i »'s line. */
.dossier-item:has(> .dossier-body > .tip-line:first-child) > .dossier-check { margin-top: var(--space-3); }
@media (pointer: coarse) { .dossier-item:has(> .dossier-body > .tip-line:first-child) > .dossier-check { margin-top: var(--space-7); } }
.dossier-item input[type='text'] { width: 100%; margin-top: 8px; }
.dossier-item[data-done='true'] .dossier-name { color: var(--ink-muted); }
/* Themed file picker — hides the raw OS "Choose File" chrome. */
.file-field { display: inline-flex; align-items: center; gap: 8px; margin-top: 8px; cursor: pointer; }
input.file-native { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; padding: 0; }
/* Square like every other action button (owner, 2026-08-27: « make it more
   square ») — the shared radius token, never a pill; pills stay for chips. */
.file-cta { display: inline-flex; align-items: center; padding: 5px 12px; border-radius: var(--radius); border: 1.5px solid var(--border-strong); background: var(--surface); color: var(--ink-muted); font-weight: 600; font-size: 12.5px; transition: border-color var(--motion-fast), color var(--motion-fast); }
.file-field:hover .file-cta { border-color: var(--action-ink); color: var(--ink); }
.file-native:focus-visible + .file-cta { outline: 2px solid var(--ring); outline-offset: 2px; }
.file-note { font-size: 12px; color: var(--ink-muted); margin-top: 6px; }
/* A refusal (format the notary could not open, oversize) is explained IN
   PLACE, in the danger hue — nothing is saved, nothing else moves. */
.file-error { font-size: 12.5px; font-weight: 600; color: var(--danger); margin-top: 6px; }
/* The whole checklist row is a drop target while a file hovers it. */
.dossier-item[data-drop='true'] { border-color: var(--brand); background: var(--surface-inset); }
/* The drag affordance is desktop prose — touch has no drag to speak of. */
@media (max-width: 720px), (pointer: coarse) { .file-hint { display: none; } }

/* --- Model / footer sections -------------------------------------------- */
.section { padding: clamp(24px, 4vw, 40px) 0; border-top: 2px solid var(--brand); }
.section h2 { margin-bottom: 8px; }
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 20px; }
@media (max-width: 780px) { .cards { grid-template-columns: 1fr; } }
.card { border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--space-9); background: var(--surface); box-shadow: var(--shadow-sm); }
.card h3 { margin-bottom: 6px; }
.card p { color: var(--ink-muted); font-size: 14px; margin: 0; max-width: 68ch; }
.price { font-size: 22px; font-weight: 800; color: var(--brand); }

.note {
  border-left: 3px solid var(--warn); background: var(--surface-inset);
  padding: 12px 16px; border-radius: var(--radius); font-size: 13px; color: var(--ink-muted);
}
.note strong { color: var(--ink); }

/* --- Progressive disclosure (native <details>, token-styled) --------------
   Keeps required copy (Loi 25, business model) available but collapsed by
   default, so the booking flow isn't buried under text. */
.disclosure {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-inset); margin: 10px 0 14px;
}
.disclosure > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px; font-size: 12px; font-weight: 600; color: var(--ink-muted);
}
.disclosure > summary::-webkit-details-marker, .sup-topics > summary::-webkit-details-marker { display: none; }
.disclosure > summary::before, .sup-topics > summary::before {
  content: ''; width: 6px; height: 6px; flex: none;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg); transition: transform var(--motion-fast);
}
.disclosure[open] > summary::before, .sup-topics[open] > summary::before { transform: rotate(45deg); }
.disclosure > summary:hover { color: var(--ink); }
.disclosure > summary:focus-visible {
  outline: none; border-radius: var(--radius-sm);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
.disclosure-body { padding: 0 14px 14px; }
.disclosure-body p { font-size: 12px; color: var(--ink-muted); margin-top: 0; }
.disclosure-body p + p { margin-top: 8px; }

/* nota-footer:start */
/* Four navigation groups share one rail; the compact identity sits above.
   Its size is --logo-footer-tile, given to .nota-footer by the generated
   chrome (brand-foundations.css): no footer recopies it.
   The band and its rail are two widths, like the header's (owner, 2026-10-11:
   « ensure that the footer is taking the full width »). The BAND is a block
   with no cap of its own, so the navy canvas runs edge to edge on every
   display; the RAIL (`.wrap`) keeps the page's --layout-width and gutter, so
   the logo and the four groups start and end where the header and the body
   content do. This settles the two requests of 2026-10-07 at once: the band
   « full-bleed », its contents « the same size [as] the body ». A cap on the
   band itself boxed the canvas between two bare margins on a wide display. */
.site-footer { --action-tint: var(--brand-canvas-glow); --ring: var(--brand-canvas-ink); border-top: 1px solid var(--border, var(--line)); padding: var(--space-12) 0 var(--space-12); color: var(--ink-muted, var(--muted)); font: var(--type-control)/var(--type-body-lh) var(--font-sans); }
.site-footer .wrap { box-sizing: border-box; width: 100%; max-width: var(--layout-width); margin-inline: auto; padding-inline: var(--layout-gutter); }
.site-footer a { color: inherit; text-decoration: none; }
.site-footer :is(a, button):is(:hover, :focus-visible) { color: var(--action-ink, var(--ink)); text-decoration: underline; text-underline-offset: .2em; }
.site-footer :is(a, button):focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
.footer-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-12); align-items: start; }
/* The identity row wraps as a whole: the signature keeps one line beside the
   logo while it fits, and drops under it on a narrow screen. */
.footer-id { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-12); margin-bottom: var(--space-8); min-width: 0; }
.site-footer .footer-brand { --lockup-tile: var(--logo-tile); display: inline-flex; align-items: center; gap: var(--lockup-badge-gap); flex: none; min-height: var(--control-height); color: var(--ink); }
.site-footer .brand-sub { display: none; }
.footer-tag { margin: 0; font: var(--type-body)/var(--type-body-lh) var(--font-sans); }
/* footer-signature:start */
/* Three phrases, one readable sentence and one compact brand point. The point
   follows a measured closed path around the middle phrase; no glyph moves.
   Geometry properties are written only at entry/resize/replay, never per frame.
   Static fallback is complete without script, path support or allowed motion. */
.footer-tag { flex: 0 1 auto; inline-size: max-content; min-inline-size: 0; max-inline-size: 100%; }
.footer-signature-accessible { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.footer-signature-visual { --signature-leg: var(--motion-ui); --signature-lift: var(--motion-fast); --signature-tour: calc(var(--motion-enter) * 5); --signature-span: var(--space-16); --signature-dot: var(--space-4); --signature-loop: var(--space-4); --signature-dot-ink: var(--nota-blue-400); --signature-line: calc(var(--space-1) / 2); --signature-path: none; --signature-first-fraction: 0; --signature-second-at: 0; --signature-second-fraction: 0; position: relative; display: grid; grid-template-columns: max-content minmax(var(--space-4), var(--signature-span)) max-content minmax(var(--space-4), var(--signature-span)) max-content; align-items: center; column-gap: var(--space-3); inline-size: fit-content; max-inline-size: 100%; min-inline-size: 0; }
.footer-signature-word { min-inline-size: 0; color: var(--ink); overflow-wrap: anywhere; }
.footer-signature-link { position: relative; inline-size: 100%; max-inline-size: var(--signature-span); block-size: var(--signature-line); }
.footer-signature-link::before { content: ''; position: absolute; inset: 0; background: var(--ink); transform-origin: 0 50%; }
/* The old first-line point supplies the finished/no-script rest position. */
.footer-signature-link::after { content: ''; position: absolute; inset-block: calc((var(--signature-line) - var(--signature-dot)) / 2); inset-inline: 0 var(--signature-dot); background: radial-gradient(circle closest-side, var(--signature-dot-ink) calc(100% - 1px), transparent) 0 50% / var(--signature-dot) var(--signature-dot) no-repeat; opacity: 0; }
.footer-signature-word:first-child + .footer-signature-link::after { opacity: 1; }
/* One absolute decorative carrier. Its closed curve has continuous tangents
   and constant path speed; the words and layout are untouched throughout. */
.footer-signature-visual::after { content: ''; position: absolute; inset: 0 auto auto 0; inline-size: var(--signature-dot); block-size: var(--signature-dot); background: radial-gradient(circle closest-side, var(--signature-dot-ink) calc(100% - 1px), transparent); opacity: 0; pointer-events: none; offset-anchor: 50% 50%; offset-position: 0 0; offset-rotate: 0deg; offset-distance: 0%; }
.footer-tag.is-armed .footer-signature-word { color: var(--ink-muted); }
.footer-tag.is-armed .footer-signature-link::before { transform: scaleX(0); }
.footer-tag.is-armed.is-in .footer-signature-word { animation: footer-signature-lift var(--signature-lift) var(--ease-ui) both; }
.footer-tag.is-armed.is-in .footer-signature-link::before { animation: footer-signature-draw var(--signature-leg) var(--ease-ui) both; }
.footer-tag.is-armed.is-in .footer-signature-visual > :nth-child(2)::before { animation-delay: var(--signature-lift); }
.footer-tag.is-armed.is-in .footer-signature-visual > :nth-child(3) { animation-delay: calc(var(--signature-lift) + var(--signature-tour) * var(--signature-first-fraction)); }
.footer-tag.is-armed.is-in .footer-signature-visual > :nth-child(4)::before { animation-delay: calc(var(--signature-lift) + var(--signature-tour) * var(--signature-second-at)); }
.footer-tag.is-armed.is-in .footer-signature-visual > :nth-child(5) { animation-delay: calc(var(--signature-lift) + var(--signature-tour) * (var(--signature-second-at) + var(--signature-second-fraction))); }
.footer-tag.is-touring .footer-signature-visual::after { opacity: 1; offset-path: var(--signature-path); animation: footer-signature-tour var(--signature-tour) linear both; }
.footer-tag.is-touring.is-arriving .footer-signature-visual::after { animation-delay: var(--signature-lift); }
.footer-tag.is-touring .footer-signature-word:first-child + .footer-signature-link::after { opacity: 0; }
@keyframes footer-signature-lift { from { color: var(--ink-muted); } to { color: var(--ink); } }
@keyframes footer-signature-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes footer-signature-tour { from { offset-distance: 0%; } to { offset-distance: 100%; } }
@media (prefers-reduced-motion: reduce), print {
 .footer-tag.is-armed .footer-signature-word, .footer-tag.is-armed.is-in .footer-signature-word { animation: none; color: var(--ink); }
 .footer-tag.is-armed .footer-signature-link::before, .footer-tag.is-armed.is-in .footer-signature-link::before { animation: none; transform: none; }
 .footer-tag.is-touring .footer-signature-visual::after { animation: none; opacity: 0; offset-path: none; }
 .footer-tag.is-touring .footer-signature-word:first-child + .footer-signature-link::after { opacity: 1; }
}
/* footer-signature:end */

.footer-col { display: flex; flex-direction: column; align-items: flex-start; gap: 0; min-width: 0; }
.footer-col-h { font-size: var(--type-eyebrow); font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ink); margin-bottom: var(--space-2); }
.footer-col > a, .footer-col .footer-link { display: inline-flex; align-items: center; box-sizing: border-box; min-height: var(--control-height); min-inline-size: var(--control-height); max-width: 100%; padding: var(--space-2) 0; overflow-wrap: anywhere; }
/* The app's guide and privacy actions keep the same targets as links. */
.footer-col .footer-link { appearance: none; background: none; border: 0; font: inherit; color: inherit; cursor: pointer; text-align: left; }
.footer-base { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--space-2) var(--space-6); margin-top: var(--space-8); padding-top: var(--space-4); border-top: 1px solid var(--border, var(--line)); font-size: var(--type-control); }
.footer-credit { display: inline-flex; flex-wrap: wrap; align-items: center; min-width: 0; gap: var(--space-2) var(--space-6); white-space: normal; }
.footer-origin { display: inline-flex; align-items: center; gap: .3em; white-space: nowrap; }
.footer-author { display: inline-flex; align-items: center; min-height: var(--control-height); max-width: 100%; padding-inline: var(--space-2); border-radius: var(--radius-xs); color: inherit; text-decoration: underline; text-decoration-thickness: calc(var(--space-1) / 2); text-underline-offset: var(--space-2); overflow-wrap: anywhere; }
.footer-author:hover { color: inherit; text-decoration-thickness: var(--space-1); }
.footer-author:focus-visible { outline: var(--space-1) solid currentColor; outline-offset: var(--space-2); }
.footer-heart { display: inline-block; flex: none; width: 1.1em; height: 1.1em; color: var(--footer-heart); fill: currentColor; stroke: none; }
@media (max-width: 760px) {
  .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-10) var(--space-8); }
}
/* Réseaux sociaux : dessins en contour, sans plaque remplie.
   Propriétaire, 2026-10-02 : retirer le fond gris rempli des icônes.
   Les glyphes suivent l’encre du thème; le focus conserve son contour.
   Propriétaire, 2026-10-03 : « trop gros, et inversés » — Facebook, LinkedIn
   et YouTube sont leur glyphe seul, fond transparent
   (apps/web/scripts/social-glyphs.mjs), haut de --footer-social-glyph dans
   une case de --footer-social-slot : les libellés partagent une même ligne.
   Le glyphe est peint à --footer-social-ink, l’encre du pied : .nota-footer
   est la toile de marque dans les deux thèmes, donc le blanc demandé (« le F
   de Facebook en blanc »). L’image n’est qu’un masque : la couleur reste un
   jeton, jamais une peinture figée dans l’actif.
   Propriétaire, 2026-10-06 : « maker the icons smaller » — la case descend à
   --space-8 et le glyphe à --space-6, une étape plus bas sur la même échelle. */
.footer-social {
  --footer-social-stroke: 1.8;
  --footer-social-slot: var(--space-8);
  --footer-social-glyph: var(--space-6);
  --footer-social-ink: var(--ink);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
  margin-top: 0;
}
.footer-social-link {
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  /* Same hit-target as every other footer action (--control-height, 44 px):
     the glyph stays small, the row grows around it. No inline-start padding:
     the icon sits on the column's left edge, under the « Contact » heading. */
  min-height: var(--control-height);
  min-inline-size: var(--control-height);
  gap: var(--space-5);
  padding-block: var(--space-3);
  padding-inline: 0 var(--space-4);
  border-radius: var(--radius-sm);
  color: var(--ink-muted, var(--muted));
  background: transparent;
  transition: color var(--motion-fast) var(--ease-ui), background-color var(--motion-fast) var(--ease-ui), transform var(--motion-fast) var(--ease-ui);
  text-decoration: none;
}
.footer-social-link:hover,
.footer-social-link:focus-visible {
  background: transparent;
  color: var(--action-ink, var(--ink));
  transform: translateX(var(--space-1));
}
.footer-social-link:focus-visible {
  outline: 2px solid var(--action-ink, var(--ink));
  outline-offset: 2px;
}
.footer-social-link svg {
  display: block;
  width: var(--footer-social-slot);
  height: var(--footer-social-slot);
  fill: none;
  stroke: currentColor;
  stroke-width: var(--footer-social-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--footer-social-ink);
  /* The glyph's ink moves with its label: same timing, same reduced-motion opt-out. */
  transition: inherit;
  flex: none;
}
.footer-social-link:is(:hover, :focus-visible) svg {
  color: inherit;
}
.footer-social-link svg.footer-social-logo {
  width: var(--footer-social-slot);
  height: var(--footer-social-glyph);
}
.footer-social-label {
  min-inline-size: 0;
  overflow-wrap: anywhere;
  font-size: var(--type-control);
  line-height: var(--type-body-lh);
}
/* App contact actions: text links with a small prefix icon. Each row
   reads as "icon + label" so users know what they are clicking. Like the
   social rows below, no inline-start padding: the icon sits on the column's
   left edge, under the « Contact » heading. */
.footer-contact { display: flex; flex-direction: column; align-items: stretch; gap: var(--space-1); }
.footer-contact-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  padding-block: var(--space-3);
  padding-inline: 0 var(--space-4);
  border-radius: var(--radius-sm);
  color: var(--ink-muted, var(--muted));
  font-size: var(--type-control);
  line-height: var(--type-body-lh);
  text-decoration: none;
  transition: color var(--motion-fast) var(--ease-ui), background-color var(--motion-fast) var(--ease-ui), transform var(--motion-fast) var(--ease-ui);
  min-height: var(--control-height);
  box-sizing: border-box;
}
.footer-contact-link:hover,
.footer-contact-link:focus-visible {
  color: var(--action-ink, var(--ink));
  background: color-mix(in srgb, var(--action-ink, var(--ink)) 8%, transparent);
  transform: translateX(var(--space-1));
}
.footer-contact-link:focus-visible {
  outline: 2px solid var(--action-ink, var(--ink));
  outline-offset: 2px;
}
.footer-contact-link svg {
  display: block;
  width: var(--space-8);
  height: var(--space-8);
  flex: none;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.footer-contact-link--email {
  font-weight: 500;
  color: var(--ink, inherit);
}
/* nota-footer:end */

.notary-empty-state {
  margin: 12px 0 0;
  padding: 14px 16px;
  border: 1px solid color-mix(in srgb, var(--brand) 22%, var(--border));
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--brand-tint-solid) 58%, var(--surface));
  color: var(--ink-muted);
}

/* Final phone guardrail: priced questionnaire choices become one readable,
   full-width column. */
@media (max-width: 480px) {
  .o-criteria .crit-row:has(.seg-btn .crit-add) .seg {
    flex-direction: column;
    align-items: stretch;
  }
  .o-criteria .crit-row:has(.seg-btn .crit-add) .seg-btn {
    width: 100%;
    min-width: 0;
  }
  .o-criteria .crit-row:has(.seg-btn .crit-add) .crit-add {
    margin-left: auto;
  }
}

/* --- Toast --------------------------------------------------------------- */

/* The SDLC story: a compact, self-contained visual that communicates the
   discipline without pretending that models or compliance are magic. */

/* --- Dialog -------------------------------------------------------------- */
dialog {
  border: 1px solid color-mix(in srgb, var(--border-strong) 70%, var(--border));
  border-top: 3px solid var(--brand);
  border-radius: var(--radius-lg); padding: 0;
  background: var(--surface);
  color: var(--ink); max-width: 440px; width: 92vw; box-shadow: var(--shadow-lg), inset 0 1px 0 var(--sheen-faint);
}
/* Light-theme dialogs are white paper — never a blue tint (owner, 2026-09-22
   « white dialogs », 2026-10-03 « I hate lightblue as background »). The
   dark-theme nested :is(dialog, #day-dialog) { background: var(--surface) }
   override wins over this because it sits inside a more specific
   :root:not([data-theme=...]) context. */
:root:not([data-theme='dark']) dialog { background: var(--paper); }
/* The beta details dialog carries a lot of prose — give it room to breathe.
   680 → 880 (propriétaire, 2026-09-25 : « make the modal larger »). La largeur
   ne vaut rien sans la MESURE : le guide (§4.4, item 1 et 13) borne une colonne
   qu'on lit, d'où les 68 caractères du paragraphe. Les actions étaient hautes
   d'un padding de 4 px : trop minces à côté du détail qu'elles ouvrent ; elles
   prennent la hauteur de contrôle du système, et le lien « démonstration
   technique » l'encre d'accent d'une action principale — sans aplat (§8.2).
   Ces deux règles sont adresSÉES par l'identifiant de la modale, non par la
   famille `.beta-` de la porte : elles réglent UNE fenêtre lue, pas la
   géométrie partagée du parcours (`porte-signature-phase3.test.mjs` tient le
   compte des règles de la porte à 24). La `.nota-guarantee` est exclue de la
   mesure : c'est un encadré qui porte sur toute la ligne, et la porte tient
   déjà cette largeur (`porte-signature-phase3.spec.js`, « la garantie occupe
   toute la ligne du détail »). */
#beta-details-dialog { max-width: min(880px, 94vw); width: 94vw; }
#beta-details-dialog .dialog-pad > p:not(.nota-guarantee) { max-width: 68ch; }
#beta-details-dialog .btn { min-height: var(--control-height); padding: var(--space-5) var(--space-9); }
/* ONE scrim for every dialog — the token plus the same soft blur. Scoped
   overrides drifted (two dialogs carried a hardcoded black): the shared shell
   owns the backdrop now. */
dialog::backdrop { background: color-mix(in srgb, var(--backdrop) 160%, var(--scrim-deep)); -webkit-backdrop-filter: blur(10px) saturate(.85); backdrop-filter: blur(10px) saturate(.85); }
/* Every popup is built the same way: its first child is
   <form method="dialog" class="dlg-x-form"> holding one ✕ button. The form is
   sticky against the dialog's own scroll box with height:0, so the ✕ stays
   pinned to the top-right corner while the dialog scrolls and costs no layout.
   The filled circle keeps the glyph legible over whatever slides beneath it. */
dialog {
  --dialog-close-size: var(--control-height);
  --dialog-close-inset: var(--space-5);
  --dialog-control-gap: var(--space-8);
  --dialog-close-clearance: calc(var(--dialog-close-size) + var(--dialog-close-inset) + var(--dialog-control-gap));
}
.dlg-x-form { position: sticky; top: 0; height: 0; margin: 0; z-index: 6; display: flex; justify-content: flex-end; }
.dlg-x {
  appearance: none; border: 1px solid color-mix(in srgb, var(--border-strong) 70%, var(--border)); background: var(--surface-inset);
  color: var(--ink-muted); cursor: pointer; width: 34px; height: 34px; margin: 10px 10px 0 0;
  border-radius: var(--radius); display: grid; place-items: center; flex: none;
  transition: background var(--motion-fast), border-color var(--motion-fast), color var(--motion-fast), transform var(--motion-fast);
  box-shadow: var(--shadow-sm);
}
.dlg-x:hover { background: var(--surface-hover); border-color: var(--action-accent); color: var(--ink); transform: scale(1.05); box-shadow: var(--shadow); }
.dlg-x:active { transform: scale(.95); }
/* 34px is under the touch minimum — give fingers 44px without changing what a
   mouse sees. */
@media (pointer: coarse) { .dlg-x { width: 44px; height: 44px; } }
/* A modal owns the scroll. The lock must sit on <html>: the root element is
   the page's scroller, so hiding overflow on <body> alone still lets the page
   drift (and lose its place) under the wheel/touch behind the open dialog. */
html:has(dialog[open]), body:has(dialog[open]) { overflow: hidden; }
/* Staggered content entrance — dialog body children slide up in sequence so
   the modal feels like it's assembling rather than appearing as a block. */
dialog[open] .dialog-pad > *:not(.dlg-x-form),
dialog[open] .auth-body > *:not(.dlg-x-form) {
  animation: dialogContentIn var(--motion-ui) var(--ease-ui) both;
}
dialog[open] .dialog-pad > *:nth-child(1),
dialog[open] .auth-body > *:nth-child(1) { animation-delay: 60ms; }
dialog[open] .dialog-pad > *:nth-child(2),
dialog[open] .auth-body > *:nth-child(2) { animation-delay: 100ms; }
dialog[open] .dialog-pad > *:nth-child(3),
dialog[open] .auth-body > *:nth-child(3) { animation-delay: 140ms; }
dialog[open] .dialog-pad > *:nth-child(4),
dialog[open] .auth-body > *:nth-child(4) { animation-delay: 180ms; }
dialog[open] .dialog-pad > *:nth-child(5),
dialog[open] .auth-body > *:nth-child(5) { animation-delay: 220ms; }
dialog[open] .dialog-pad > *:nth-child(6),
dialog[open] .auth-body > *:nth-child(6) { animation-delay: 260ms; }
@keyframes dialogContentIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
/* The account-link confirmation is created after the OAuth callback. */
#oauth-link-dialog { padding: var(--pad-dialog); width: min(440px, calc(100vw - 32px)); }
#oauth-link-dialog h2 { margin: 0 0 12px; }
#oauth-link-dialog form { display: grid; gap: 10px; margin: 18px 0; }
#oauth-link-dialog input { box-sizing: border-box; width: 100%; min-height: 44px; }
#oauth-link-dialog .help { line-height: 1.5; }
/* One dialog padding for every popup — this and #oauth-link-dialog used to
   read 22px and 24px, two hand-set values for the same slot. */
.dialog-pad { padding: var(--pad-dialog); }
.legal-contract-gate { margin-block: var(--space-4); }
.legal-contract-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block: var(--space-3); }
.legal-contract-body { overflow-wrap: anywhere; }
.legal-contract-reader { max-width: min(var(--layout-width), calc(var(--control-height) * 18)); width: calc(100% - var(--space-8)); }
.legal-contract-reader .dialog-pad { max-inline-size: 100%; }
.dialog-pad h3 { margin-bottom: 8px; }
.dialog-pad p { color: var(--ink-muted); font-size: var(--type-lead); line-height: var(--type-lead-lh); margin: 0 0 16px; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* Aucun bouton rempli (propriétaire) : l'action principale d'un dialogue est
   le même contour que partout — .btn-primary au repos, --action-tint au survol.
   Un aplat en dégradé revenu le 2026-09-20 (4a670fe) est retiré (audit 2026-09-22). */
.dialog-actions .btn-primary { font-weight: 600; }
/* The public rating badge — brand star, tabular figures, quiet count. */
.rating-badge { color: var(--brand); font-weight: 700; font-size: 12.5px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.nc-own-rating { margin-bottom: var(--space-4); }

/* The CNQ membership pill and the official fiche link (ADR 0016). */
.cnq-badge {
  display: inline-block; padding: 0 6px; border: 1px solid var(--brand); border-radius: var(--radius-sm);
  color: var(--brand); background: var(--brand-tint-solid); font-weight: 700; font-size: 11px;
  line-height: 18px; white-space: nowrap; vertical-align: middle;
}
.cnq-link { font-size: 12.5px; white-space: nowrap; }
/* ADR 0030 — la pastille de cote et sa note d'explication ont été retirées des
   vues client : l'art. 70 du Code de déontologie interdit au notaire de
   permettre qu'un témoignage d'appui le concernant soit utilisé, et une note
   affichée transforme un annuaire en recommandation. Ce qui reste à côté du
   badge CNQ est un FAIT : le nombre d'actes portés sur Nota. */
.my-offer-acts { white-space: nowrap; }
#nc-profil-form .btn { margin-top: var(--space-3); }
/* Les champs texte des modales de la console et de l'Espace équipe prennent la
   largeur de leur rangée, comme leurs listes déroulantes : à 204 px dans des
   colonnes de 327 px, l'adresse de l'étude et la fiche CNQ se lisaient tronquées
   (passe navigateur du 2026-10-03). */
:is(.nc-dialog, #team-gate) .form-row > :is(input[type='text'], input[type='email'], input[type='tel'], input[type='url']) { box-sizing: border-box; width: 100%; }

/* --- Notary evaluation (after the signed act, ADR 0015) ------------------- */
.my-offer-eval { margin-top: var(--space-6); padding: var(--pad-card); background: var(--surface-inset); border-radius: var(--radius); display: flex; flex-direction: column; gap: var(--space-4); }
.my-offer-eval-t { font-size: 13.5px; }
.eval-stars { display: flex; gap: 2px; }
.eval-star { appearance: none; border: 0; background: transparent; cursor: pointer; font-size: 24px; line-height: 1; padding: 2px 4px; color: var(--brand); }
.eval-star:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; border-radius: var(--radius-sm); }
.eval-comment { resize: vertical; }
.eval-submit { align-self: flex-start; }
.my-offer-eval-done { font-size: 13.5px; }
.my-offer-eval-done strong { color: var(--brand); }

/* Destructive confirm (cancel an offer): quiet red outline, filled on hover —
   visible enough to read as consequence, never louder than the safe way out. */
.btn-danger { color: var(--danger); border-color: var(--danger); }
.btn-danger:hover { background: transparent; border-color: var(--danger); color: var(--danger); box-shadow: inset 0 0 0 1px var(--danger); }
.btn-offer-cancel { color: var(--danger); }
/* Per-offer help door: a visible secondary action with a compact help mark.
   It stays at the end of the row on wide cards and becomes full-width on a
   narrow card, so it never reads like an unrelated browser-default button. */
.my-offer-help {
  margin-left: auto; align-self: center; display: inline-flex; align-items: center;
  gap: 7px; min-height: 34px; padding-inline: 12px; border-color: var(--brand);
  color: var(--brand-on-surface); font-weight: 700; white-space: nowrap;
}
.my-offer-help:hover { background: var(--action-tint); border-color: var(--action-ink); color: var(--action-ink); }
.my-offer-help-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 19px; height: 19px; border: 1.5px solid currentColor; border-radius: var(--radius-sm);
  font-size: 13px; line-height: 1; font-weight: 800;
}
.my-offer-help:focus-visible { outline-offset: 3px; }
@media (max-width: 560px) {
  .my-offer-help { margin-left: 0; width: 100%; justify-content: center; }
}
/* The « ou écrivez-nous » quick line under the contact form's submit. */
#ct-quick { margin-top: 12px; text-align: center; }
#ct-quick:empty { display: none; }
/* --- « Nous joindre » (2026-09-04 redesign) ----------------------------------
   Scoped to the contact dialog — the shared dialog shell (.dlg-x-form,
   .dialog-actions, the 440px column) is untouched. The pass tightens every
   small space to the token scale, seats nom + courriel on one row where the
   column is wide enough, lets the message box grow with the text, and gives
   the sent state a real confirmation with its two actions. Under 480px the
   dialog becomes a full-width sheet with its own scroll, so a 320px phone
   never hides the Envoyer button below an unreachable fold. */
#contact-dialog .ct-pad { padding: var(--space-9) var(--space-9) var(--space-8); }
/* The sticky ✕ sits in the top-right corner: the title line and the lede's
   first line keep clear of it instead of running underneath. */
#contact-dialog h3 { margin-bottom: var(--space-3); padding-right: 52px; }
#contact-dialog .ct-lede { padding-right: 28px; }
#contact-dialog .ct-lede { font-size: 14px; line-height: 1.45; margin: 0 0 var(--space-6); max-width: 52ch; }
#contact-dialog .ct-trust {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2);
  margin: calc(-1 * var(--space-2)) 0 var(--space-7); padding: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-inset);
}
#contact-dialog .ct-trust-item {
  display: flex; align-items: flex-start; gap: var(--space-2); min-width: 0;
  color: var(--ink); font-size: 11px; line-height: 1.25;
}
#contact-dialog .ct-trust-item strong,
#contact-dialog .ct-trust-item small { display: block; }
#contact-dialog .ct-trust-item strong { font-size: 11px; font-weight: 800; }
#contact-dialog .ct-trust-item small { margin-top: 2px; color: var(--ink-muted); font-size: 10.5px; line-height: 1.25; }
#contact-dialog .ct-trust-mark {
  display: inline-grid; place-items: center; flex: none; width: 20px; height: 20px;
  border-radius: var(--radius-sm); background: var(--brand-tint); color: var(--brand); font-weight: 800;
}
#contact-dialog .form-row { margin-bottom: var(--space-5); }
#contact-dialog .lbl { margin-bottom: var(--space-2); }
#contact-dialog .ct-field-help { margin: var(--space-2) 0 0; font-size: 11.5px; line-height: 1.35; }
/* Le libellé et son compteur partagent UNE ligne — le compteur garde sa
   réserve (ADR 0039) sur une hauteur qui existait déjà. */
#contact-dialog .lbl-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
#contact-dialog .lbl-row .lbl { margin-bottom: var(--space-2); }
#contact-dialog .lbl-row .ct-count { margin: 0; }
#contact-dialog .lbl-hint { font-weight: 500; color: var(--ink-muted); }
#contact-dialog .ct-grid { display: grid; grid-template-columns: 1fr; gap: 0 var(--space-6); }
#contact-dialog textarea { min-height: 58px; max-height: 40vh; resize: vertical; }
#contact-dialog .ct-context { margin: calc(-1 * var(--space-3)) 0 var(--space-5); }

#contact-dialog #ct-quick { margin-top: var(--space-5); }
/* « Envoyer » suit le même registre que les autres formulaires (2026-09-07) :
   largeur de son libellé, jamais toute la feuille — et rangé à droite, là où
   .dialog-actions pose déjà les actions de tous les autres dialogues. */
#contact-dialog #ct-submit { display: flex; width: fit-content; min-width: 200px; margin-left: auto; }
/* The sent state: a check chip on the title line, one sentence, then the two
   actions — the messagerie first when the thread token came back. */
.ct-ok-head { display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-3); }
.ct-ok-ic { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; flex: none; border-radius: var(--radius); background: var(--brand-tint-solid-strong); color: var(--brand-on-tint-strong); }
.ct-ok-ic svg { width: 18px; height: 18px; }
#contact-dialog .ct-ok-title { margin: 0; font-size: 16px; color: var(--ink); }
#contact-dialog .ct-ok-body { margin: 0 0 var(--space-7); max-width: 46ch; }
.ct-ok-actions { flex-wrap: wrap; }
.ct-ok-actions .btn { flex: 1 1 auto; }
/* The dialog column is comfortable from 480px up; below, every side space
   shrinks one step and nom + courriel stack (a 300px column cannot hold two). */
@container (min-width: 380px) { #contact-dialog .ct-grid { grid-template-columns: 1fr 1fr; } }
#contact-dialog .dialog-pad { container-type: inline-size; }
@media (max-width: 479.98px) {
  #contact-dialog { width: calc(100vw - 16px); max-width: none; max-height: calc(100% - 16px); margin: 8px auto; }
  #contact-dialog .ct-pad { padding: var(--space-7) var(--space-6) var(--space-6); }
  #contact-dialog .ct-lede { margin-bottom: var(--space-5); padding-right: 52px; }
  #contact-dialog .ct-trust { grid-template-columns: 1fr; margin-bottom: var(--space-6); }
  #contact-dialog .form-row { margin-bottom: var(--space-5); }
  #contact-dialog textarea { min-height: 52px; }
}

/* --- Day detail dialog (enlarged day over a dimmed backdrop) -------------- */
/* La feuille suivait la fenêtre jusqu'à 620 px et s'arrêtait là, quelle que
   soit la taille de l'écran (2026-09-06) : sur un 27 pouces, les questions
   s'appariaient dans deux colonnes trop étroites, si bien qu'une réponse à
   trois boutons s'empilait en trois lignes à côté d'un simple champ. Elle
   grandit maintenant AVEC l'écran : 760 px reste le plancher de lecture et
   1 180 px le plafond, assez large pour que les choix et les champs forment
   des rangées nettes sans transformer la feuille en pleine largeur. */
#day-dialog { box-sizing: border-box; max-width: min(94vw, clamp(760px, 78vw, 1180px)); width: 94vw; max-height: min(90vh, 90dvh); overflow-y: auto; overscroll-behavior: contain; background: var(--paper); border-top: 3px solid var(--brand); }
/* Sticky like the ✕ above it: the date being booked stays readable while the
   long booking form scrolls. padding-right clears the pinned ✕. */
.day-head { position: sticky; top: 0; z-index: 5; background: linear-gradient(to bottom, var(--surface-hover), var(--paper)); display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 8px 20px; padding: var(--space-8) 72px var(--space-5) var(--space-10); border-bottom: 1px solid color-mix(in srgb, var(--border-strong) 50%, var(--border)); }
.day-heading { min-width: 0; }
.day-eyebrow { margin-bottom: 4px; font-size: var(--type-h4); line-height: 1.4; color: var(--subtitle-ink); }
.day-title { margin: 0; }
.day-title::first-letter { text-transform: uppercase; }
.day-sub { font-size: var(--type-h4); color: var(--ink-muted); margin-top: 2px; }
/* The header's native date picker: the date moves without closing the dialog.
   Pushed to the right edge, inside the padding that clears the pinned ✕. */
.day-date-edit { min-width: 0; max-width: 100%; }
.day-date-edit > summary { display: flex; align-items: center; min-height: 44px; cursor: pointer; font-size: var(--type-h4); font-weight: 600; color: var(--brand-on-surface); text-decoration: underline; text-underline-offset: 3px; list-style: none; }
.day-date-edit > summary::-webkit-details-marker { display: none; }
.day-date-input { box-sizing: border-box; width: 100%; max-width: 220px; min-width: 0; min-height: 44px; font: inherit; font-size: 16px; padding: 8px 10px; border: 1px solid color-mix(in srgb, var(--border-strong) 70%, var(--border)); border-radius: var(--radius-sm); background: var(--surface-inset); color: var(--ink); }
.day-date-input:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 1px; border-color: var(--action-ink); }
/* A tapped date opens a calm decision, with the market behind one disclosure.
   The same content stays readable in a phone sheet or on a touch desktop. */
#day-dialog[data-interaction='preview'] { width: min(94vw, 560px); max-width: 560px; }
#day-dialog[data-interaction='preview'] .day-head { grid-template-columns: minmax(0, 1fr); gap: 0; padding: 20px 68px 8px 20px; }
.day-preview { padding: 16px 20px max(20px, env(safe-area-inset-bottom)); }
.day-preview-intro { margin: 0 0 16px; color: var(--ink-muted); font-size: 14px; line-height: 1.5; }
#day-preview > button { width: 100%; min-height: 48px; justify-content: center; background: transparent; color: var(--action-ink); border-color: var(--action-ink); }
#day-preview > button:hover { background: var(--action-tint); color: var(--action-ink); border-color: var(--action-ink); }
.day-preview-offers { margin-top: 20px; border-top: 1px solid var(--border); }
.day-preview-offers > summary { display: flex; align-items: center; gap: 8px; min-height: 52px; cursor: pointer; color: var(--ink-muted); font-size: var(--type-h4); font-weight: 600; list-style: none; }
.day-preview-offers > summary::-webkit-details-marker { display: none; }
.day-preview-offers > summary::after { content: '+'; margin-left: auto; font-size: 20px; font-weight: 400; }
.day-preview-offers[open] > summary::after { content: '−'; }
.day-preview-count { min-width: 22px; padding: 2px 6px; text-align: center; background: var(--surface-inset); border-radius: var(--radius-xs); font-variant-numeric: tabular-nums; }
.day-preview-empty { margin: 16px 0 0; font-size: var(--type-h4); line-height: 1.5; color: var(--ink-muted); }
#day-preview .day-bids { max-height: none; overflow: visible; margin: 0; border-top: 0; }
#day-preview .bid-row { padding: 12px 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; border-left: 0; background: transparent; }
#day-preview .bid-meta { grid-column: 1; grid-row: 1 / 3; display: flex; flex-direction: column-reverse; min-width: 0; }
#day-preview .bid-sub { font-size: var(--type-h4); line-height: 1.4; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
#day-preview .bid-who { font-size: var(--type-h4); font-weight: 400; color: var(--ink-muted); }
#day-preview .bid-amount { grid-column: 2; grid-row: 1; font-size: 15px; white-space: nowrap; }
#day-preview .bid-row > .pill { display: none; }
#day-preview .status-chip { grid-column: 2; grid-row: 2; max-width: 14ch; font-size: var(--type-h4); }
#day-preview .day-bids-count { display: none; }
@media (max-width: 680px) {
  #day-dialog .day-head { grid-template-columns: minmax(0, 1fr); gap: 0; padding: 16px 64px 8px 16px; }
  #day-dialog[data-interaction='preview'] { width: 100%; max-width: 100%; }
  .day-preview { padding-inline: 16px; }
}
@media (max-height: 480px) and (min-width: 681px) {
  #day-dialog[data-interaction='preview'] { max-height: 96dvh; }
  #day-dialog[data-interaction='preview'] .day-head { grid-template-columns: minmax(0, 1fr) auto; padding-block: 12px; }
  .day-preview { padding-top: 12px; }
  .day-preview-intro { margin-bottom: 12px; }
  .day-preview-offers { margin-top: 12px; }
}
/* What happens after a real publication (success screen) and, back from
   Checkout, the same line standing on « Mes offres ». */
.offer-success-next { margin-top: 6px; }
.checkout-notice { padding: 12px 16px; border: 1px solid var(--border); border-left: 3px solid var(--brand); border-radius: var(--radius-sm); font-size: 13px; line-height: 1.5; color: var(--ink); }

/* --- Day booking surface (acte → offre à battre → book, all in one dialog) - */
/* Act-scoped market context: the offer to beat FOR THE SELECTED ACT — one
   quiet strip inside step 1 (it changes with the chips), so step 3 keeps the
   only big number in the dialog. */
.day-market { background: var(--surface-inset); border-radius: var(--radius-sm); padding: 8px 12px; overflow: hidden; }
/* Centre-aligned, and the LABEL is the flexible column (it wraps within
   itself): the reference figure and the one-tap pill hold the right rail of
   the same row at every dialog width — the pill never strands on an orphan
   row with dead space beside it (owner, 2026-08-27: « align »). */
.day-market-line { display: flex; align-items: center; gap: 4px 10px; flex-wrap: wrap; min-height: 1.5em; }
/* No open offer for the act → the line is hidden outright (every date is
   bookable, so there is no bar to print); the hint below owns the message. */
/* 2026-09-05 : la capitale d'affichage criait (« CE QUE D'AUTRES OFFRENT CE
   JOUR-LÀ · REFINANCEMENT » sur deux lignes) alors que la carte porte déjà son
   titre. Le libellé redevient une légende : casse normale, poids moyen. */
.day-value-k { font-size: var(--type-h4); font-weight: 600; color: var(--ink-muted); flex: 1 1 12ch; min-width: 0; }
.day-value-v { font-size: 15px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.day-value-note { min-height: 1.5em; font-size: var(--type-h4); color: var(--ink-muted); margin: 2px 0 0; }
/* The live line flips blue the moment the slider clears the bar. */
.day-value-note.is-ahead { color: var(--ok); font-weight: 600; }
/* One tap over the bar — small, inline, at the end of the strip's line. */
.day-beat {
  appearance: none; font: inherit; font-size: var(--type-h4); font-weight: 700; cursor: pointer;
  margin-left: auto; padding: 3px 10px; border-radius: var(--radius-sm);
  border: 1px solid var(--brand); background: transparent; color: var(--brand-on-surface);
  transition: background var(--motion-fast), color var(--motion-fast), border-color var(--motion-fast);
}
.day-beat:hover { background: var(--action-tint); border-color: var(--action-ink); color: var(--action-ink); }
/* Stepping back does not take its place with it: the label is written even
   while the button is off show, so the strip's line keeps its height and its
   wrap (ADR 0039 — une ligne tenue). */
/* The headline lands with a small settle; the amount echoes it after a one-tap
   jump. Both sit still under prefers-reduced-motion. */
@keyframes day-best-in { from { opacity: 0; transform: translateY(3px); } }
.day-value-v.is-refreshed { animation: day-best-in var(--motion-ui) var(--ease-ui); }
.amount-big.flash { animation: day-best-in var(--motion-enter) var(--ease-ui); }
@media (prefers-reduced-motion: reduce) { .day-value-v.is-refreshed, .amount-big.flash { animation: none; } }
/* Says what the calendar's % means, for the date being booked. Sits under the
   header — it depends on the DATE, not on the act chosen below. Top padding
   keeps it off the sticky header's border. */
.day-chance { margin: 0; font-size: var(--type-h4); line-height: 1.5; color: var(--ink-muted); }
.day-bids { max-height: 30vh; overflow: auto; overscroll-behavior: contain; border-top: 1px solid var(--border); }
/* Inside the market strip the list bleeds to the edges, so its border-top
   spans the strip and the rows keep their own gutters. */
.day-market .day-bids { margin: 8px -12px -8px; }
.day-bids:empty { display: none; }
.day-bids .bid-row:first-child { border-top: 0; }
.day-bids-rest[hidden] { display: none; }
/* The door to the rest of the day is INLINE on the totals line (owner,
   2026-08-27: « compacter ») — a quiet link-weight button holding the right
   rail of the count's own row, never a stacked full-width bar. */
.day-bids-toggle {
  appearance: none; border: 0; background: none; font: inherit; cursor: pointer;
  font-size: var(--type-h4); font-weight: 700; color: var(--brand);
  padding: 0; margin-left: auto; white-space: nowrap;
}
.day-bids-toggle:hover { text-decoration: underline; }
/* The day's totals under the single headline offer: how many for the act being
   booked, and how many that date carries in all — with the unfold door at the
   end of the same line. */
.day-bids-count { display: flex; align-items: center; gap: 4px 12px; flex-wrap: wrap; padding: var(--space-4) var(--space-12); font-size: var(--type-h4); color: var(--ink-muted); line-height: 1.5; }
/* Scale guard: a day-group caps its rows and links to the full day view. */
.day-book {
  padding: var(--space-7) var(--space-10) var(--space-10);
  border-top: 1px solid var(--border);
  background: var(--surface-inset);
  display: flex; flex-direction: column; gap: var(--space-6);
}
/* Each step is its own bordered card so the three-beat flow reads at a glance.
   Regular card padding — the dialog is a working surface, not a brochure
   (owner, 2026-08-27: « compacter toutes les sections »). */
.book-step { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--pad-card); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
/* Step 3 — the offer — is the decision, so it is the dominant card. */
.book-step-offer { border-color: var(--border-strong); background: var(--surface-inset); }

/* --- La feuille de réservation en QUATRE ÉCRANS (ADR 0040) ---------------
   Retour du propriétaire (2026-09-05) : « l'expérience est vraiment confuse
   — le faire en plusieurs screens ». Les cinq blocs empilés donnaient une
   colonne à faire défiler où l'acte, les questions du notaire, le prix, le
   secteur et l'identité se disputaient le même premier regard.

   Un écran à la fois. La structure du formulaire NE CHANGE PAS : chaque bloc
   porte le numéro de l'écran auquel il appartient (`data-screen`), le
   formulaire porte celui où l'on est (`data-at`), et c'est le CSS seul qui
   n'en montre qu'un — un `display: none` retire déjà du parcours au clavier
   et de l'arbre d'accessibilité. */
.day-book > [data-screen] { display: none !important; }
.day-book[data-at='1'] > [data-screen='1'],
.day-book[data-at='2'] > [data-screen='2'],
.day-book[data-at='3'] > [data-screen='3'],
.day-book[data-at='4'] > [data-screen='4'] { display: block !important; }
@keyframes book-screen-enter {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
.day-book[data-at] > [data-screen] {
  will-change: opacity, transform;
}
.day-book[data-at='1'] > [data-screen='1'],
.day-book[data-at='2'] > [data-screen='2'],
.day-book[data-at='3'] > [data-screen='3'],
.day-book[data-at='4'] > [data-screen='4'] {
  animation: book-screen-enter var(--motion-ui) var(--ease-ui) both;
}
/* BUG 2 — l'offre publiée : le rail et la barre d'action restaient à l'écran
   à côté de la carte de confirmation. « done » ne montre aucun écran, ni rail,
   ni barre. */
.day-book[data-at='done'] > .book-rail,
.day-book[data-at='done'] > .book-nav { display: none; }

/* Le rail : où l'on est, ce qui reste, et la porte de retour vers une étape
   déjà franchie. Une étape qu'on n'a pas encore atteinte n'est pas cliquable
   — on ne saute pas par-dessus une question du notaire. */
.book-rail { display: flex; align-items: stretch; gap: var(--space-2); list-style: none; margin: 0 0 var(--space-6); padding: 0; }
/* Le rail numérote les étapes ; la carte n'a pas à le refaire une ligne plus
   bas. Le libellé reste — il nomme l'écran — la pastille part. */
.day-book[data-at] .book-step > .book-step-lbl > .step { display: none; }
.book-rail li { flex: 1 1 auto; min-width: 0; }
.book-rail-b {
  appearance: none; width: 100%; height: 100%; display: flex; align-items: center; gap: 7px;
  padding: 7px 9px; font: inherit; font-size: var(--type-h4); font-weight: 650; text-align: left;
  color: var(--ink-muted); background: transparent; cursor: pointer;
  border: 0; border-top: 2px solid var(--border); border-radius: 0;
}
.book-rail-b::after {
  content: '';
  display: block;
  width: 0;
  height: 2px;
  margin-left: auto;
  background: var(--brand);
  transition: width var(--motion-enter) var(--ease-ui);
}
.book-rail li[data-state='done'] .book-rail-b::after,
.book-rail li[data-state='at'] .book-rail-b::after { width: 18px; }
.book-rail-b[disabled] { cursor: default; opacity: 0.55; }
.book-rail-n {
  flex: none; width: 19px; height: 19px; display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--type-h4); font-weight: 800; border-radius: var(--radius-xs);
  background: var(--surface-inset); color: var(--ink-muted);
}
.book-rail-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.book-rail li[data-state='done'] .book-rail-b { color: var(--ink); border-top-color: var(--brand); }
.book-rail li[data-state='done'] .book-rail-n { background: transparent; color: var(--brand); border: 1.5px solid var(--brand); }
.book-rail li[data-state='at'] .book-rail-b { color: var(--ink); border-top-color: var(--brand); }
.book-rail li[data-state='at'] .book-rail-n { background: transparent; color: var(--brand); border: 1.5px solid var(--brand); }
/* Traversée sans y répondre : on peut y revenir, et le rail le dit. */
.book-rail li[data-state='open'] .book-rail-b { color: var(--ink); border-top-color: var(--warn); }
.book-rail li[data-state='open'] .book-rail-n { background: transparent; color: var(--warn); border: 1.5px solid var(--warn); }
.book-rail-b:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 2px; }
/* Une étape sautée (l'acte n'a pas de questions) se dit à voix basse. */
.book-rail li[data-state='skip'] { display: none; }

/* Une seule barre d'action, toujours au même endroit, en bas : « Retour » à
   gauche, et à droite l'action qui fait avancer. Au dernier écran c'est
   « Publier mon offre » qui prend cette place — jamais deux boutons primaires
   à la fois. */
.book-nav { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-2); }
.book-nav .book-back { flex: none; }
/* L'action va AU BOUT DE LA BARRE, elle ne la remplit pas (retour du
   propriétaire, 2026-09-07 : « button are too big, do not fill them ») :
   étirée sur toute la largeur restante, « Continuer » faisait un bandeau de
   600 px pour un mot, et le regard n'avait plus de point à viser. Elle garde
   une largeur de vraie cible et se range à droite, « Retour » à gauche. */
.book-nav .book-fwd, .book-nav .book-submit { flex: 0 0 auto; width: auto; min-width: 200px; margin: 0 0 0 auto; }
.book-nav > [hidden] { display: none !important; }
/* La barre porte la SEULE décision de l'écran, et elle ne se lisait pas comme
   telle (propriétaire, 2026-09-06) : 37 px de haut sur 578 px de large, un
   trait vert sous le formulaire. Elle prend la hauteur d'une vraie cible —
   44 px à la souris AUSSI, pas seulement au doigt — et une ligne de texte plus
   grande. « Retour » monte à la même hauteur pour que la barre tienne d'un
   bord à l'autre, mais garde son encre sourde : deux poids, une seule action. */
.book-nav .book-fwd, .book-nav .book-submit { min-height: 44px; font-size: 15px; }
.book-nav .book-back { min-height: 44px; }
/* Et elle rentre dans le rang : l'exception de l'aplat vert est LEVÉE
   (2026-09-07). Elle avait été posée le 2026-09-06 parce qu'un contour de
   44 px sur 560 se lisait comme un trait de séparation — c'était la LARGEUR le
   problème, pas le contour. Rendue à sa taille, l'action se lit comme partout
   ailleurs dans l'application : couleur de marque et graisse, jamais un aplat.
   Redit ici parce que l'id #offer-submit l'emporte sur la classe .btn-primary ;
   désactivé, il retombe sur les jetons d'inactif pour la même raison. */
.book-nav .book-fwd, .book-nav .book-submit {
  background: transparent; border-color: var(--action-ink); color: var(--action-ink); font-weight: 700;
}
.book-nav .book-fwd:not(:disabled):hover, .book-nav .book-submit:not(:disabled):hover {
  background: var(--action-tint); border-color: var(--action-ink);
}
.book-nav .book-fwd:disabled, .book-nav .book-submit:disabled {
  background: transparent; border-color: var(--border); color: var(--ink-muted);
}
/* Le chevron dit le sens, et seulement où il est vrai : « Continuer » avance
   d'un écran, « Publier mon offre » conclut — celle-là n'en porte pas. */
.book-nav .book-fwd::after { content: '→'; font-size: 1.05em; line-height: 1; transition: transform var(--motion-fast) var(--ease-ui); }
.book-nav .book-fwd:not(:disabled):hover::after { transform: translateX(2px); }
@media (max-width: 480px) {
  /* Sur téléphone le rail garde les numéros et laisse tomber les mots : quatre
     libellés sur 375 px ne tiennent qu'en s'ellipsant tous. */
  .book-rail-t { display: none; }
  .book-rail-b { justify-content: center; padding-inline: 4px; }
}
@media (prefers-reduced-motion: reduce) {
  .day-book[data-at] > [data-screen] { animation: none; }
  .book-rail-b::after { transition: none; }
}
/* Pricing criteria woven into the booking flow (dossier merged with the process). */
.o-criteria { display: flex; flex-direction: column; gap: 10px; }
.crit-row { display: flex; flex-direction: column; gap: 6px; }
.crit-flag { display: flex; align-items: flex-start; gap: 9px; cursor: pointer; }
/* La case se pose SUR le rail des libellés : les marges par défaut du
   navigateur (4 px à gauche, 3 px à droite) la poussaient de quatre pixels
   vers l'intérieur, si bien qu'une question à cocher ne commençait pas où
   commencent les autres questions de la même carte. */
.crit-flag input { margin: 3px 0 0; flex: none; accent-color: var(--brand); }
.crit-text { display: flex; flex-direction: column; gap: 1px; }
.crit-label { font-size: var(--type-h4); font-weight: 600; color: var(--ink); }
/* The question's own line. Everything that used to print itself UNDER a
   question and grow the card — the awaited-answer mark, the situational
   notes — rides here instead, on a line the label already occupies. */
.crit-head { display: flex; align-items: baseline; flex-wrap: wrap; min-width: 0; }
/* Les réponses d'une question sont des BOUTONS, pas un affichage segmenté
   (retour du propriétaire, 2026-09-05 : « our button and form is hard to
   understand — keep it really straight forward »). Sur la piste commune, une
   option non choisie n'avait ni fond ni cadre : trois libellés posés sur une
   barre, dont on ne devinait ni qu'ils étaient cliquables ni lequel était pris.
   Ici chaque option porte son propre cadre, la piste s'efface, et la réponse
   retenue est peinte à la marque. */
.crit-row .seg { display: flex; flex-wrap: wrap; gap: var(--space-3); padding: 0; border: 0; background: none; }
/* Une réponse se dimensionne sur CE QU'ELLE DIT (retour du propriétaire,
   2026-09-07 : « button are too big »). Étirées sur la ligne entière
   (flex: 1 1 auto), « Non » et « Oui » devenaient deux pavés de 400 px pour
   trois lettres : la cible était énorme et le mot perdu au milieu, si bien que
   deux réponses opposées se lisaient comme deux bandeaux. Elles gardent un
   plancher — une cible reste une cible, même pour « Oui » — et s'arrêtent là
   où leur libellé s'arrête. La piste, elle, reste alignée à gauche : c'est une
   suite de choix, pas une barre à remplir. */
.crit-row .seg-btn {
  flex: 0 1 auto; min-width: 92px; max-width: 100%; text-align: center;
  padding: 6px 14px; border-color: var(--border); background: var(--surface); color: var(--ink);
}
.crit-row .seg-btn:hover { border-color: var(--border-strong); background: var(--action-tint); }
/* Sauf quand le rang PREND LA LIGNE ENTIÈRE (retour du propriétaire,
   2026-09-07 : « this is looking empty ») : trois réponses serrées contre le
   bord gauche d'une ligne de 900 px laissaient les deux tiers de la carte nus.
   Là, elles se répartissent sur la ligne — mais BORNÉES : au-delà d'environ un
   tiers de la ligne, un bouton redevient le pavé qu'on vient de retirer. Un
   rang qui PARTAGE sa ligne, lui, garde ses réponses à la taille du libellé :
   c'est la place disponible qui décide, jamais la question. */
.crit-row[data-large] .seg-btn { flex: 1 1 auto; max-width: 320px; }
/* À partir d'une grande feuille, une rangée explicitement large utilise
   l'espace disponible : deux choix opposés se lisent comme deux colonnes
   équilibrées, sans laisser un quart de carte vide après les libellés. Les
   rangées compactes gardent leur largeur naturelle. */
@media (min-width: 1100px) {
  .crit-row[data-large] .seg-btn { flex: 1 1 0; max-width: none; }
  /* On a wide sheet, a segmented answer is a question-wide decision. Keeping
     it in the two-column track leaves a conspicuous empty half-card whenever
     its neighbour has one more option. The controls share the full row and
     stay bounded to a comfortable reading width. */
  .crit-sec-grid > .crit-row:has(.seg) { grid-column: 1 / -1; }
  .crit-sec-grid > .crit-row:has(.seg) .seg-btn { flex: 1 1 0; max-width: 420px; min-height: 44px; font-size: 14px; }
  .dossier-pricing .crit-sec[data-groupe='pret'] .crit-row[data-crit='valeur_pret'] { grid-column: 1; grid-row: 1; }
  .dossier-pricing .crit-sec[data-groupe='pret'] .crit-row[data-crit='preteur'] { grid-column: 2; grid-row: 1; }
  .dossier-pricing .crit-sec[data-groupe='pret'] .crit-row[data-crit='contexte'] { grid-column: 1 / -1; grid-row: 2; }
  .dossier-pricing .crit-sec[data-groupe='pret'] .crit-row[data-crit='approbation_bancaire'] { grid-column: 1 / -1; grid-row: 3; }
  .crit-sec-t { font-size: 16px; }
  .crit-sec-aide { font-size: var(--type-h4); }
  .crit-label { font-size: 14px; }
}
/* La réponse retenue se RECONNAÎT, elle ne se REMPLIT pas (même retour : « do
   not fill them »). L'aplat de marque faisait de chaque réponse choisie une
   tuile verte pleine — six questions répondues et la carte n'était plus qu'une
   grille de blocs, où le prix « +400 $ » se lisait moins bien que le fond
   derrière lui. Ici : le cadre passe à la marque et double d'épaisseur (un
   `inset` plutôt qu'une bordure de 2 px, qui déplacerait le texte), l'encre
   suit, et une coche dit lequel est pris. Le registre du bouton primaire de
   l'application — couleur et graisse, jamais un aplat — vaut donc aussi pour
   les réponses.

   La coche a sa place RÉSERVÉE sur chaque option (registre ADR 0039) : elle
   n'apparaît pas, elle se dévoile — répondre, se raviser, répondre ailleurs ne
   fait jamais respirer la piste sous la main du client. */
.crit-row .seg-btn::before, .crit-row .seg-btn::after {
  content: '✓'; font-size: 11px; font-weight: 800; color: var(--brand); visibility: hidden;
}
.crit-row .seg-btn::before { margin-right: 6px; }
/* Le jumeau muet de la coche, à droite : la coche est posée AVANT le libellé,
   donc à elle seule elle décentrerait le mot de sept pixels sur une piste
   centrée. Il ne se montre jamais — il ne fait que rendre la boîte symétrique,
   à l'arrêt comme choisie. */
.crit-row .seg-btn::after { margin-left: 6px; }
.crit-row .seg-btn.is-on {
  background: var(--surface); border-color: var(--action-ink);
  box-shadow: inset 0 0 0 1px var(--action-ink);
  color: var(--action-ink); font-weight: 700;
}
.crit-row .seg-btn.is-on::before { visibility: visible; }
/* When the options cannot share one line (settleSegTracks measures), the
   track restacks as full-width rows — answer left, price right — instead of
   a ragged 2+1 wrap that reads as two controls. */
.crit-row .seg.seg-stack { flex-direction: column; align-items: stretch; }
.crit-row .seg-stack .seg-btn { display: flex; align-items: baseline; justify-content: flex-start; text-align: left; }
.crit-row .seg-stack .crit-add { margin-left: auto; padding-left: 12px; }
/* A required question still awaiting its answer carries a quiet amber dot —
   the same signal the hint line and the step tally speak. Direct child only:
   a companion field's own label must not double the mark. */
.crit-row.crit-missing > .crit-head > .crit-label::after {
  content: ''; display: inline-block; width: 6px; height: 6px; margin-left: 7px;
  border-radius: 50%; background: var(--warn); vertical-align: 1px;
}
/* One flash when the hint's door lands here, so the eye finds the line. */
@keyframes crit-attn {
  from { box-shadow: 0 0 0 4px color-mix(in srgb, var(--warn) 40%, transparent); }
  to { box-shadow: 0 0 0 4px transparent; }
}
.crit-row.crit-attn { animation: crit-attn 1.4s ease-out; border-radius: var(--radius-sm); }
@media (prefers-reduced-motion: reduce) { .crit-row.crit-attn { animation: none; } }
/* Déplacement: two compact segmented bars — where it signs, then the radius —
   each under the question it answers (audit §3.2), then the online opt-in
   line (§3.3) and the static caveats (§1.1) toggled by the direction. */
.crit-dep { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.crit-dep-km[hidden], .crit-sublabel[hidden] { display: none; }
.crit-sublabel { font-size: var(--type-h4); font-weight: 600; color: var(--ink-muted); margin-top: 4px; }
.crit-urgence { margin-top: 4px; }
/* The client's perimeter slider (ADR 0017 addendum, 2026-09-24): the stop and
   its price read above the track, the two ends under it — the price slider's
   grammar (.slider-row, .slider-bounds), one register for every continuous
   answer (brand guide §8.2). */
.crit-dep-range { display: flex; flex-direction: column; gap: var(--space-3); }
.crit-dep-range[hidden] { display: none; }
.crit-dep-range-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
.crit-dep-range-value { font-family: var(--font-display); font-size: var(--type-h3); line-height: var(--type-h3-lh); font-weight: var(--weight-display); color: var(--ink); font-variant-numeric: tabular-nums; }
/* Chambre des notaires — la demande de signature à distance apparaît sous
   l'opt-in urgence. Cadre discret, pas de remplissage : la parole est au
   client, pas à la marque. */
.crit-dsd { display: flex; flex-direction: column; gap: var(--space-4); margin-top: var(--space-4); padding: var(--space-6); border: 1px solid var(--border); border-radius: var(--radius); }
.crit-dsd[hidden] { display: none; }
.crit-dsd__heading { font-size: var(--type-h4); font-weight: 600; color: var(--ink); margin: 0; }
.crit-dsd__intro { font-size: var(--type-h4); color: var(--ink-muted); margin: 0; line-height: 1.5; }
.crit-dsd__check { display: flex; align-items: flex-start; gap: var(--space-4); font-size: var(--type-h4); cursor: pointer; }
.crit-dsd__motif-lbl { font-size: var(--type-h4); font-weight: 600; color: var(--ink-muted); }
.crit-dsd__motif { font: inherit; font-size: var(--type-h4); padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); width: 100%; }
.crit-dsd__motif-text { display: flex; flex-direction: column; gap: var(--space-2); }
.crit-dsd__motif-text[hidden] { display: none; }
.crit-dsd__motif-text-lbl { font-size: var(--type-h4); color: var(--ink-muted); }
.crit-dsd__motif-text-input { font: inherit; font-size: var(--type-h4); padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); }
.crit-dsd__text-lbl { font-size: var(--type-h4); font-weight: 600; color: var(--ink-muted); }
.crit-dsd__text-input { font: inherit; font-size: var(--type-h4); padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); resize: vertical; min-height: 60px; }
/* A default the client has not touched reads as a suggestion: the pressed
   answer keeps its place but drops the accent fill until it is confirmed. */
/* Un défaut que le client n'a pas touché reste une SUGGESTION : cadre en
   pointillé, pas de trait doublé, et pas de coche — la coche dit « vous avez
   répondu », ce qui serait faux tant qu'on n'y a pas touché. */
.crit-row[data-default='true'] .seg-btn.is-on { background: transparent; border-style: dashed; box-shadow: none; }
.crit-row[data-default='true'] .seg-btn.is-on::before { visibility: hidden; }
/* The awaited-answer note (audit 2.5), beside the dot, once the form is started. */
/* Its OWN line inside the head (the question columns are too narrow to carry
   the label and the message side by side), reserved from the first paint and
   emptied by visibility — so a question that is answered, then cleared, then
   answered again never moves the sheet under the client's hand. */
/* Elle se range CONTRE le libellé quand la colonne le permet, et ne bascule
   sur sa propre ligne que si elle n'y tient pas — l'aide étant passée derrière
   le « i », une ligne réservée par question obligatoire était devenue le
   principal gaspillage de hauteur de la carte (2026-09-05). Réservée dans les
   deux cas : visible ou non, elle occupe la même boîte. */
.crit-req { flex: 0 1 auto; margin-left: var(--space-5); font-size: var(--type-h4); font-weight: 600; color: var(--warn); }
.crit-req[data-on='false'] { visibility: hidden; }
.crit-row input[type='number'] { max-width: 180px; }
/* The bracket figure and its unit read as one control (like the offer box). */
.crit-unit-wrap { display: inline-flex; align-items: center; gap: 6px; }
.crit-unit { font-size: var(--type-h4); font-weight: 700; color: var(--ink-muted); }
/* Long option lists (the lender catalogue) render as a select (domain ui hint),
   enhanced into a Nota select — the width cap rides on the wrapper. */
.crit-row .nselect { max-width: 320px; }
/* The select's free-text companion (« Autre prêteur » → its name), revealed
   only while the opening option is chosen. */
/* Le compagnon d'une liste (« Autre prêteur » → son nom) occupe la place que
   l'aide vient de libérer : une ligne pour une ligne, donc répondre à la
   question ne déplace rien de ce qui suit. */
/* Le compagnon d'une liste (« Autre prêteur » → son nom) se range À CÔTÉ de la
   liste, sur la place que la grille laisse libre : l'aide étant passée derrière
   le « i », il n'y a plus de ligne à céder — et une réponse ne doit toujours pas
   pousser ce qui la suit (ADR 0039). */
.crit-row:has(> .crit-other) { flex-flow: row wrap; align-items: flex-start; column-gap: var(--space-8); }
.crit-row:has(> .crit-other) > .crit-head, .crit-row:has(> .crit-other) > .crit-effet { flex: 1 0 100%; }
.crit-row:has(> .crit-other) > .nselect, .crit-row:has(> .crit-other) > .help { flex: 0 1 300px; }
.crit-row:has(> .crit-other) > .crit-other { flex: 1 1 240px; }
.crit-other { display: flex; flex-direction: column; gap: 6px; margin-top: 0; }
.crit-other[hidden] { display: none; }
.crit-other input[type='text'] { max-width: 320px; }
/* Flat dollar effect of an answer, printed on the answer itself; inherits the
   chip's on-state colour so it stays readable on the accent background. */
.crit-add { margin-left: 6px; font-size: var(--type-h4); font-weight: 600; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.chip.is-on .crit-add, .seg-btn.is-on .crit-add { color: inherit; }
/* bookGoTo pose le focus sur le titre de l'écran (tabindex -1) pour que le
   clavier et le lecteur d'écran repartent du haut. Ce titre n'est PAS un
   contrôle : on ne l'atteint jamais en tabulant, et l'anneau de focus générique
   dessinait une boîte verte autour de « Les questions du notaire » qui se lisait
   comme une sélection restée coincée. Le focus reste posé — seule sa bague
   part. */
.book-step-lbl:focus-visible, .book-step-lbl:focus { outline: none; }
/* Même raison pour le dialogue lui-même : showModal() y pose le focus quand
   rien n'est autofocusable, et l'anneau générique cernait TOUTE la feuille d'un
   trait vert qu'on lisait comme une sélection coincée. */
dialog:focus, dialog:focus-visible { outline: none; }
/* Wrap allowed: on a phone the parenthetical drops to its own line instead of
   squeezing the title into a ragged two-column fold. */
.book-step-lbl { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: 17px; font-weight: 700; color: var(--ink); }
.book-opt { color: var(--ink-muted); font-weight: 600; font-size: var(--type-h4); }
/* Step 2's live tally — amber while answers are awaited, brand once complete. */
.crit-count { margin-left: auto; font-size: var(--type-h4); font-weight: 600; color: var(--warn); white-space: nowrap; font-variant-numeric: tabular-nums; }
.crit-count[data-state='done'] { color: var(--brand); }
/* L'écran 1 ne pose qu'UNE question, alors la réponse prend la place (ADR
   0040, 2026-09-05 : « il y a trop d'info, c'est trop petit »). Deux cartes
   à parts égales, assez hautes pour se viser du pouce, jamais trois par
   ligne : sous 360 px de large elles s'empilent plutôt que de rétrécir. */
#o-service-chips {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  gap: var(--space-6);
  min-width: 0;
}
@media (max-width: 380px) { #o-service-chips { grid-template-columns: 1fr; } }
/* Booking chips are two lines — the act, then its own offre à battre for the
   date (no offer → no sub line: every act is always bookable, so only an
   amount is worth a line) — so the acts compare at a glance before one is
   chosen. */
#o-service-chips .chip {
  position: relative;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  justify-content: center; flex-direction: column; align-items: center; gap: var(--space-3);
  padding: var(--space-8) var(--space-6); border-radius: var(--radius);
}
/* Le crochet du choix : il n'apparaît que sur la carte retenue, et il dit le
   choix sans dépendre de la couleur (le cadre et la teinte, eux, en dépendent). */
#o-service-chips .chip-svc-check {
  position: absolute; top: var(--space-4); right: var(--space-5);
  /* The same ink as the chosen frame: --brand alone fell to ~2.7:1 on the
     dark theme's tinted card. */
  font-size: 13px; font-weight: 700; line-height: 1; color: var(--action-ink);
  opacity: 0; transform: scale(.7); transition: opacity var(--motion-fast) var(--ease-ui), transform var(--motion-fast) var(--ease-ui);
}
#o-service-chips .chip.is-on .chip-svc-check { opacity: 1; transform: none; }
/* La pastille du glyphe : la marque de l'acte, visée avant que le nom se lise.
   Elle porte l'état de la carte (survol, choix) — le cadre de la carte ne
   change alors que d'un cheveu, et le choix se voit quand même de loin. */
#o-service-chips .chip-svc-badge {
  width: 36px; height: 36px; flex: none; display: inline-grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-inset);
  transition: background var(--motion-fast), border-color var(--motion-fast);
}
#o-service-chips .chip:hover .chip-svc-badge { border-color: var(--action-ink); }
/* La carte retenue : la teinte, le cadre, le crochet et la pastille disent le
   choix — le NOM, lui, est un titre et reste à l'encre. */
#o-service-chips .chip.is-on { background: var(--action-tint); color: var(--ink); }
#o-service-chips .chip.is-on .chip-svc-badge { background: var(--surface); border-color: var(--action-ink); }
#o-service-chips .chip-svc-main { font-size: 15px; }
.chip-svc-main { display: inline-flex; align-items: center; gap: var(--space-3); font-size: 14.5px; font-weight: 700; }
.chip-svc-sub { font-size: 12px; font-weight: 700; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
/* Réservée seulement si le jour porte un chiffre : les deux puces gardent la
   même hauteur quand on change d'acte, sans qu'un jour sans offre paie une
   ligne vide dans chaque carte. */
#o-service-chips[data-sub='true'] .chip-svc-sub { min-height: 1.45em; }
#o-service-chips .chip.is-on .chip-svc-sub { color: inherit; }
/* Le catalogue annoncé (D.ACTES_A_VENIR) : même carte, même hauteur, même
   place — mais un trait pointillé, une encre sourde et aucun état survolé,
   pour que l'œil aille d'abord aux deux actes qui, eux, se réservent. */
#o-service-chips .chip-soon {
  cursor: default; background: transparent; color: var(--ink-muted);
  border-style: dashed; border-color: var(--border); box-shadow: none;
}
/* Rien ne s'y survole ni ne s'y anime : une annonce n'est pas une cible. */
#o-service-chips .chip-soon:hover { background: transparent; border-color: var(--border); color: var(--ink-muted); transform: none; }
#o-service-chips .chip-soon .chip-svc-main { font-weight: 600; }
#o-service-chips .chip-soon .chip-svc-badge { background: transparent; border-style: dashed; }
/* Sur une feuille assez LARGE, les cartes d'acte se couchent : le glyphe à gauche, le
   nom à sa droite, le prix (ou « Bientôt ») au bout de la ligne. Debout, elles
   encaissaient toute la largeur gagnée en VIDE — près de 400 px de carte pour
   un mot, et deux actes seulement annoncés qui pesaient autant que les deux
   qui se réservent (capture du propriétaire, 2026-09-06).
   C'est la largeur de la FEUILLE qui décide, pas celle de l'écran : la feuille
   est un dialogue centré, sa largeur ne suit pas la fenêtre au même rythme.
   Là où le pouce vise — téléphone, feuille étroite — rien ne change : les
   cartes restent debout et hautes. */
.day-book { container-type: inline-size; container-name: book; }
@container book (min-width: 600px) {
  #o-service-chips .chip {
    flex-direction: row; justify-content: flex-start; align-items: center;
    gap: var(--space-6); padding: var(--space-6) var(--space-7); text-align: left;
  }
  /* Le montant du jour ferme la ligne, aligné d'une carte à l'autre. */
  #o-service-chips .chip-svc-main { min-width: 0; overflow-wrap: anywhere; }
  #o-service-chips .chip-svc-sub { margin-left: auto; padding-right: var(--space-6); flex: none; }
  /* Le crochet du choix reprend sa place au coin, dégagé du montant. */
  #o-service-chips .chip-svc-check { top: 50%; transform: translateY(-50%) scale(.7); }
    #o-service-chips .chip.is-on .chip-svc-check { transform: translateY(-50%); }
}
/* On narrow phones the four acts intentionally stay in one column, but they
   are still simple choices — not four tall poster cards. Keep the 44px touch
   floor while placing the icon, name and day figure on a compact two-line
   rail. One column also gives long names their full reading width. */
@media (max-width: 520px) {
  #o-service-chips { grid-template-columns: 1fr; grid-auto-rows: auto; gap: 8px; }
  #o-service-chips .chip {
    display: grid; grid-template-columns: 36px minmax(0, 1fr);
    grid-template-rows: auto auto; align-items: center; justify-content: stretch;
    gap: 2px 10px; min-height: 76px; box-sizing: border-box;
    padding: 10px 12px; text-align: left; white-space: normal;
  }
  #o-service-chips .chip-svc-badge { grid-column: 1; grid-row: 1 / span 2; }
  #o-service-chips .chip-svc-main {
    grid-column: 2; grid-row: 1; min-width: 0; font-size: 14px;
    line-height: 1.25; overflow-wrap: anywhere;
  }
  #o-service-chips .chip-svc-sub {
    grid-column: 2; grid-row: 2; min-height: 0 !important; margin: 0;
    padding: 0; line-height: 1.2;
  }
  #o-service-chips .chip-svc-check { top: 8px; right: 8px; font-size: 12px; }
}
#o-service-chips .chip-soon .svc-ic { opacity: .7; }
/* « Bientôt » tient sur la ligne RÉSERVÉE AU PRIX, sous le nom (2026-09-06) :
   sur la ligne du nom, la pastille poussait « Procuration » hors de l'axe de
   son glyphe, et la rangée annoncée se lisait de travers sous une rangée
   centrée. La carte garde la hauteur d'une carte réservable — la ligne
   existait déjà — et la mention se lit comme une note, pas un bouton. */
.chip-soon-tag {
  font-size: 9.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-muted); border: 1px solid var(--border);
  padding: 1px var(--space-3); border-radius: var(--radius-xs); white-space: nowrap;
}
.book-options { margin: 0; }
.book-options .form-row:last-of-type { margin-bottom: 0; }
/* The identity block of the booking sheet (ADR 0033): name, courriel,
   téléphone — set apart from the notary's questions by a rule, never folded.
   Les trois champs s'apparient sur la MÊME grille que les questions de
   l'écran 2 (2026-09-06) : trois champs à la file dans une feuille qui
   s'élargit devenaient trois barres de 700 px, et rien ne disait que le nom et
   le courriel vont ensemble. Même seuil que .crit-sec-grid, donc les deux
   écrans se replient au même endroit. */
/* TROIS colonnes tant que la feuille les porte (2026-09-07) : à deux, le
   téléphone fermait le bloc seul et traînait une demi-carte nue à sa droite —
   le dernier vide de l'écran 4. Les trois champs sont courts et se lisent d'une
   seule ligne ; `auto-fit` les replie à deux puis à un, sans palier écrit à la
   main. */
.book-identity {
  margin-top: var(--space-6); padding-top: var(--space-6); border-top: 1px solid var(--border);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6) var(--space-9); align-items: start;
}
/* Le sélecteur d'écran impose `display: block !important` à chaque bloc de la
   feuille (.day-book > [data-screen]) : le bloc d'identité le redit au même
   poids, sur le même sélecteur porté d'une classe, sans quoi la grille
   ci-dessus n'est jamais appliquée. */
.day-book[data-at='4'] > .book-identity[data-screen='4'] { display: grid !important; }
@media (max-width: 560px) { .book-identity { grid-template-columns: minmax(0, 1fr); } }
.book-identity .form-row { margin: 0; }
.book-identity-signed-in {
  grid-column: 1 / -1; display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-6); padding: var(--space-5) var(--space-6);
  border: 1px solid var(--border-strong); border-left: 3px solid var(--brand);
  border-radius: var(--radius); background: var(--surface-inset);
}
.book-identity-signed-in[hidden] { display: none; }
.book-identity-signed-in-copy { min-width: 0; }
.book-identity-kicker {
  margin: 0 0 3px; color: var(--subtitle-ink); font-size: var(--type-h4); font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
}
.book-identity-title { margin: 0; line-height: 1.3; }
.book-identity-signed-in .help { margin: 5px 0 0; }
@media (max-width: 560px) {
  .book-identity-signed-in { align-items: flex-start; flex-direction: column; }
  .book-identity-signed-in .btn { width: 100%; }
}
/* Un champ se dimensionne sur ce qu'on y écrit : une barre de 700 px pour dix
   chiffres promet autre chose que ce qu'elle attend. */
@media (min-width: 561px) {
  .book-identity #o-telephone { max-width: 260px; }
}
/* Les trois lignes d'aide de l'écran 4 sont des PROMESSES (à qui va ce champ,
   ce qui reste privé) : elles se lisent, donc elles restent — mais serrées
   sous leur champ, jamais espacées comme un paragraphe. */
.book-identity .help { margin: var(--space-3) 0 0; }
/* « Où signez-vous ? » : trois caractères dans un champ court suivis d'un
   demi-écran nu, et l'explication qui prenait deux lignes pleines sous lui. Le
   champ et son explication se rangent CÔTE À CÔTE — la carte tient sur deux
   lignes au lieu de quatre. */
#prefix-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  align-items: center; gap: var(--space-3) var(--space-8);
}
#prefix-row > .lbl, #prefix-row > .prefix-preview { grid-column: 1 / -1; }
#prefix-row > #o-prefix { width: 96px; }
#prefix-row > .help { margin: 0; }
@media (max-width: 560px) { #prefix-row { grid-template-columns: minmax(0, 1fr); } }

/* Brand utility classes — replace ad-hoc inline styles, keep markup on-brand */
.w-full { width: 100%; }
.uppercase { text-transform: uppercase; }
.mb-xl { margin-bottom: 32px; }
.mt-sm { margin-top: 14px; }
.mt-md { margin-top: 0; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.actions-inline { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.offer-success { margin-top: 14px; padding: 12px 14px; background: var(--surface-inset); border-radius: var(--radius); border-left: 3px solid var(--ok); }
.offer-success-head { display: flex; align-items: flex-start; gap: 10px; }
.offer-success-logo { flex: none; display: block; }
.offer-success .mt-sm { margin-top: 0; }
.switch-title { font-weight: 600; font-size: 14px; }
/* Le panneau d'un document légal remplit la gouttière : une liste de clauses à
   560 px laissait la moitié droite de la page vide (propriétaire, 2026-09-27 :
   « BIG EMPTY SECTIONS »). La largeur de lecture est tenue clause par clause,
   sur le texte ouvert, pas sur le bloc. */
.legal-panel { margin: var(--space-9) 0 var(--space-16); }
.h-sub { font-size: var(--type-h3); line-height: var(--type-h3-lh); letter-spacing: var(--type-h3-ls); margin-bottom: 12px; }

/* Participation and subscription share the same visual weight. */
.participation-options { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.participation-options .eyebrow { display: block; margin-bottom: 12px; }
.participation-options .card p { line-height: 1.6; }
.participation-lifecycle { margin-top: 24px; }
.participation-lifecycle p { max-width: 80ch; color: var(--ink-muted); font-size: 14px; line-height: 1.6; margin: 0 0 16px; }
/* Running text shares the rail instead of leaving half of it blank: as many
   reading columns as the panel holds, never a paragraph cut across two. The
   panel's title spans them. */
.prose-columns { columns: 34ch; column-gap: var(--space-16); }
.prose-columns > :is(h2, h3) { column-span: all; }
.prose-columns > p { break-inside: avoid; }
.participation-lifecycle a { display: inline-flex; align-items: center; min-height: 44px; font-size: 14px; }
.participation-availability { margin: 20px 0 12px; }
@media (max-width: 700px) {
  .participation-options { grid-template-columns: minmax(0, 1fr); }
}

/* Cette ligne est de l'AIDE, pas une deuxième ligne de pitch. `.intro p`
   (0,1,1) battait `.help` (0,1,0) et la rendait à la taille du lede : en
   français elle tenait QUATRE lignes de corps — 102 px posés entre les boutons
   du héros et le calendrier, sur un téléphone (mesuré à 390 px, 2026-09-14).
   Elle reprend le registre de `.help` (styles.css:1851), celui que la ligne
   d'éligibilité de la porte Partenaires tient déjà à côté de son propre lede.
   Le lien reste cliquable en ligne : e2e/participation.spec.js le suit. */

/* --- Notary console ------------------------------------------------------ */
/* Two faces on one pane, toggled by whether the console (#notary-authed) is
   showing:
   • SIGNED-OUT → the Carnet's hero grammar (agenda providers = the action row,
     the month as the aside), then requests and account access on the page.
   • SIGNED-IN → the working console alone, in a single wide column. */
#pane-notaires {
  /* The four agendas' measure, held as a property so the row, its short labels
     and the sync rail all keep ONE reading width. */
  --nc-providers-width: 47ch;
  --action-ink-mix: 40%;
  --nc-offer-surface: transparent;
  --nc-offer-hover: transparent;
}
#pane-notaires > .wrap { display: flex; flex-direction: column; }
/* CONNECTÉ : la porte est un plan de travail, pas une vitrine. La face
   publique se replie — l'aparté du héros (le héros lui-même ne garde que son
   h1, hors écran, pour la navigation : voir plus bas), l'anatomie et la
   preuve, les deux voies, le bouton des obligations. `display: none` et non
   un simple masquage visuel : un lien d'agenda caché ne doit pas rester dans
   l'ordre de tabulation. La console porte ses propres boutons d'agenda (flux
   privé). LA RANGÉE DES QUATRE AGENDAS RESTE : le carnet des demandes est
   l'outil du notaire, et depuis le 2026-09-26 la porte n'est plus ouverte
   qu'à lui — ce serait le punir de sa connexion que de la replier. */
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .hero-cta,
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) #notary-requests,
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .nc-sync,
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .notary-carnet,
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .intro--hero p,
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) #notary-live,
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) #notary-ai-voies { display: none; }
/* Signed in, the hero is the four agendas (the title is kept for assistive
   reading only). Packed to the left they left the rest of the rail blank
   (owner, 2026-10-12): from a tablet up they share it in four equal tracks,
   the same tracks as the console's choices below. */
@media (min-width: 768px) {
  #pane-notaires > .wrap:has(#notary-authed:not([hidden])) .landing-hero { grid-template-columns: minmax(0, 1fr); }
  #pane-notaires > .wrap:has(#notary-authed:not([hidden])) :is(.calendar-subscription, #notary-providers) { width: 100%; max-width: none; }
}
/* The landing's centrepiece: the live open inventory, directly under the one
   line of pitch. Cards mirror the console's .nc-card surface; the whole card
   is a button into the sign-in. */
/* The optional comparison shares the calendar's page surface. Borders inside
   the comparison separate the choices without another opaque panel behind. */
.nc-voies {
  margin-top: var(--gap); padding: var(--space-9); border: 0;
  background: transparent;
}
.nc-live-head { display: flex; align-items: baseline; gap: var(--space-6); flex-wrap: wrap; margin-bottom: var(--space-5); }
.nc-live-h, .nc-live-sub { font-size: var(--type-h4); line-height: var(--type-h4-lh); letter-spacing: var(--type-h4-ls); margin: 0; color: var(--accent-blue); }
.nc-live-sub { margin: var(--space-12) 0 var(--space-5); }

/* --- La face publique de la porte (refonte du 2026-09-18) -------------------
   Héros : la mention de synchronisation ferme l'aparté. */
/* La rangée d'action du héros EST la rangée des agendas : elle garde le pas du
   héros (.hero-cta) plutôt que celui d'une carte (.sub-card-actions, qu'elle
   porte aussi pour la console et ses tests), et ses boutons gardent leur
   largeur naturelle. Au téléphone : quatre de front — ICS ne tombe jamais seul
   sur une rangée ; sous 360px, deux rangées de deux. */
#notary-providers { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-4); margin-top: 0; width: 100%; max-width: var(--nc-providers-width); }
/* The providers stay quiet at rest; the section accent marks interaction. */
#notary-providers .btn { min-width: 0; min-height: 44px; gap: var(--space-3); padding-inline: var(--space-4); font-weight: 700; color: var(--ink); border-color: var(--border-strong); }
#notary-providers .btn:is(:hover, :focus-visible) { color: var(--action-ink); border-color: var(--action-ink); background: var(--action-tint); transform: none; box-shadow: none; }
#notary-providers .btn:active { transform: none; box-shadow: none; }
@media (max-width: 520px) {
  #notary-providers { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-2); width: 100%; }
  /* 44px : la cible tactile (WCAG 2.5.5) — au doigt, c'est LE geste de la page. */
  #notary-providers .btn { min-width: 0; min-height: 44px; gap: var(--space-2); padding-inline: var(--space-1); font-weight: 600; letter-spacing: -.01em; }
  #notary-providers .brand-ic { width: var(--space-7); height: var(--space-7); }
}
@media (max-width: 359.98px) {
  #notary-providers { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.landing-copy > .nc-sync-note { margin: var(--space-4) 0 0; font-size: var(--type-body); line-height: var(--type-body-lh); }
/* Le second geste du héros, discret : lire les demandes avant de s'abonner. */
.nc-see-requests { margin-top: var(--space-4); }
/* LE RAIL DE SYNCHRONISATION (2026-09-20) — le carnet de Nota à gauche, l'agenda
   du notaire à droite, et entre les deux des demandes qui voyagent. C'est un
   DÉCOR (aria-hidden) : il montre que l'agenda RELIT le flux, il ne promet
   aucune cadence — la phrase sous le mois la dit. Mouvement d'ambiance : sa
   boucle se mesure en secondes et vit dans SON jeton, hors des trois barreaux
   (brand.html, « Ce qui ne suit pas le registre »).
   2026-10-17 (propriétaire) : plus de carte autour. Un décor n'a rien à
   encadrer — il repose sur le fond, comme le mois et le pouls du carnet.
   2026-10-18 (propriétaire) : il prend la MESURE de la colonne, pas sa
   largeur. À 2 466 px la colonne de copie fait 1 050 px, et le rail à elle
   seule traversait tout le reste de la page en pointillés — le léde, les
   quatre agendas et SA phrase restent à 47 caractères — pour finir à 20 px
   du « Prêt entre particuliers » du calendrier. */
.nc-sync {
  --nc-sync-loop: 3.6s;
  display: grid; grid-template-columns: auto minmax(var(--space-16), 1fr) auto; align-items: center;
  gap: var(--space-6); width: 100%;
  max-width: var(--nc-providers-width);
  /* Keep synchronization beside the actions even when a selected calendar
     date grows the neighbouring column. */
  margin: var(--space-8) 0 0;
}
/* On a phone the month must stay in the first screen: the decorative rail
   (aria-hidden) steps aside; its sentence stays. */
@media (max-width: 680px) {
  #pane-notaires .landing-copy > .nc-sync { display: none; }
  #pane-notaires .nc-sync-note { margin-block-start: var(--space-8); }
}
.nc-sync-end { display: inline-flex; align-items: center; gap: var(--space-3); min-width: 0; font-size: var(--type-eyebrow); font-weight: 700; letter-spacing: var(--type-eyebrow-ls); text-transform: uppercase; color: var(--ink); white-space: nowrap; }
.nc-sync-mark, .nc-sync-glyph { flex: none; width: var(--space-10); height: var(--space-10); }
.nc-sync-glyph { color: var(--ink-muted); }
.nc-sync-end--agenda { animation: nc-sync-land var(--nc-sync-loop) var(--ease-ui) infinite; }
.nc-sync-track { position: relative; height: var(--space-3); overflow: hidden; }
.nc-sync-track::before { content: ''; position: absolute; inset-inline: 0; top: 50%; border-top: 2px dotted var(--border-strong); transform: translateY(-50%); }
/* Chaque paquet est une bande de la largeur du rail, son carré posé à gauche :
   translateX(100 %) le fait donc voyager d'un bout à l'autre, sans mesurer. */
.nc-sync-packet { position: absolute; inset: 0; opacity: 0; animation: nc-sync-travel var(--nc-sync-loop) var(--ease-ui) infinite; }
.nc-sync-packet::before { content: ''; position: absolute; left: 0; top: 0; width: var(--space-3); height: var(--space-3); border-radius: var(--radius-xs); background: var(--ink-muted); }
.nc-sync-packet:nth-child(2) { animation-delay: calc(var(--nc-sync-loop) / 3); }
.nc-sync-packet:nth-child(3) { animation-delay: calc(var(--nc-sync-loop) * 2 / 3); }
@keyframes nc-sync-travel {
  0% { opacity: 0; transform: translateX(0); }
  12% { opacity: 1; }
  82% { opacity: 1; }
  100% { opacity: 0; transform: translateX(calc(100% - var(--space-3))); }
}
@keyframes nc-sync-land {
  0%, 88%, 100% { transform: none; }
  94% { transform: translateY(calc(var(--space-1) * -1)); }
}
/* Les demandes « atterrissent » dans le mois : le point de chaque jour ouvert
   arrive à son tour (--i posé par agenda-demo.js), sur le barreau des entrées. */
.nc-mobile-day.has-request::after { animation: nc-dot-land var(--motion-enter) var(--ease-ui) both; animation-delay: calc(var(--i, 0) * var(--menu-stagger)); }
@keyframes nc-dot-land { from { opacity: 0; transform: translateX(-50%) translateY(calc(var(--space-3) * -1)) scale(.4); } to { opacity: 1; transform: translateX(-50%); } }
/* La fiche du jour choisi se repose à chaque sélection (classe reposée par
   agenda-demo.js) : on voit QUE quelque chose a changé. */
.nc-mobile-offer.is-swapping > * { animation: fadeUp var(--motion-ui) var(--ease-ui) both; }
/* Equal columns balance the subscription and its live calendar. Content
   determines their height, and both columns start on ONE top line: the
   eyebrow beside the sync rail, the h1 beside the month (propriétaire,
   2026-09-24 : centrée, la copie laissait le calendrier « au-dessus » du
   titre). The base .landing-copy already packs to the top. */
@media (min-width: 1280px) {
  #pane-notaires .landing-hero { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gap); align-items: stretch; }
  /* The month's title lines up with the h1, heading beside heading: the offset
     is the kicker's line (--type-eyebrow at .eyebrow's 1.5, the body rung),
     its margin (.landing-copy > .eyebrow) and the h1's own top margin. */
  #pane-notaires .notary-carnet { display: flex; flex-direction: column; align-self: stretch; padding-top: calc(var(--type-eyebrow) * var(--type-body-lh) + var(--space-8) + var(--space-2)); }
  /* …and the month grows to the column's height: its rows share whatever the
     copy column needs beyond the natural 44px, so the last week meets the
     sync note on the left (propriétaire, 2026-09-24 : « both should look
     aligned »). */
  #pane-notaires .notary-carnet > .nc-calendar-teaser { flex: 1 1 auto; display: flex; flex-direction: column; margin: 0; }
  #pane-notaires .notary-carnet :is(.nc-calendar-mobile, .nc-mobile-month-view) { flex: 1 1 auto; display: flex; flex-direction: column; }
  #pane-notaires .notary-carnet .nc-mobile-grid { flex: 1 1 auto; grid-auto-rows: minmax(44px, 1fr); }
  #pane-notaires .notary-carnet .nc-mobile-day { height: auto; }
}

.nc-service-detail { margin: 0; padding-block: var(--space-2); font-size: var(--type-eyebrow); line-height: var(--type-body-lh); color: var(--ink-muted); overflow-wrap: anywhere; }

/* Three equal, optional preparation choices — the band's four page tracks are
   the door (in .nc-bottom) plus these three, so the choices land under the
   request tiles two to four (propriétaire, 2026-09-26 : « row split in 5 and
   4 »). The section's own title sits ABOVE the row, as .nc-live-head does
   above the demands, never as a text column inside it. */
#notary-ai-voies { display: flex; flex-direction: column; gap: var(--space-5); min-width: 0; }
.nc-voies { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-7) var(--nc-col-gap); align-items: start; }
.nc-voies-intro { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.nc-voies-title, .nc-live-title { margin: 0; font-family: var(--font-display); font-size: var(--type-h2); line-height: var(--type-h2-lh); letter-spacing: var(--type-h2-ls); font-weight: var(--weight-display); color: var(--ink); text-wrap: balance; }
.nc-voies-intro > .eyebrow { margin: 0; }
.nc-voies-status { margin: 0; max-width: 52ch; font-size: var(--type-body); line-height: var(--type-body-lh); color: var(--ink-muted); text-wrap: pretty; }
.nc-voie { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; padding: var(--space-6) 0; border: 0; background: transparent; }
/* Vertical rules between the three choices: the 2nd and 3rd cards get a left
   border that separates them from their left neighbour, with padding so the
   text never touches the line. */
/* The choices are separated by their gutter alone (propriétaire, 2026-09-26 :
   « trop de bordures ») — a rule beside each price read as a table, not a
   row of the page grid. */
.nc-voie[hidden] { display: none; }
.nc-voie-k { font-family: var(--font-display); font-size: var(--type-h3); line-height: var(--type-h3-lh); letter-spacing: var(--type-h3-ls); font-weight: var(--weight-display); color: var(--ink); }
.nc-voie-price { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-2); font-family: var(--font-display); font-size: var(--type-h2); line-height: var(--type-h2-lh); font-weight: var(--weight-display); font-variant-numeric: tabular-nums; color: var(--ink); }
.nc-voie-price:empty { display: none; }
.nc-voie-per { font-family: var(--font-sans); font-size: var(--type-body); line-height: var(--type-body-lh); font-weight: 600; color: var(--ink-muted); }
/* The contribution discount, beside the price it lowers: the section's action
   ink, no chip (brand guide §8.3 — outlined, never filled). */
.nc-voie-off { font-family: var(--font-sans); font-size: var(--type-body); line-height: var(--type-body-lh); font-weight: 700; color: var(--action-ink); }
/* The pre-discount price, struck through so the saving reads at a glance: the
   muted ink keeps it present without competing with the actual price. */
.nc-voie-was { font-family: var(--font-sans); font-size: var(--type-body); line-height: var(--type-body-lh); font-weight: 600; color: var(--ink-muted); text-decoration: line-through; text-decoration-color: var(--ink-muted); }
.nc-voie-p { margin: 0; font-size: var(--type-body); line-height: var(--type-body-lh); color: var(--ink); text-wrap: pretty; }
/* Availability follows the comparison; each choice keeps its own link (the
   bounds of the exchange live in the contribution card, where they apply). */
.nc-voie > .btn { align-self: flex-start; }

/* Only visible quotes occupy tracks. The source keeps all three stable
   action hooks so a configured contribution discount can appear on refresh. */
.nc-voies[data-plans="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.nc-voies[data-plans="1"] { grid-template-columns: minmax(0, 1fr); }

/* The notary modals carry real reading (art. 32.1, 29.1, 49…): the shared
   440px dialog cap made them a thin column (propriétaire, 2026-09-24). They
   take a reading width, and the compliance one lays its three articles out
   side by side as soon as they fit. */
.nc-dialog { width: min(720px, calc(100vw - var(--space-16))); max-width: none; }
#notary-obligations-dialog { width: min(1040px, calc(100vw - var(--space-16))); }
@media (min-width: 900px) {
  #notary-obligations-dialog .nc-conformite-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* The guarantee closes the page and carries the way to the obligations: the
   sentence, then its link, on one line when there is room. */
#pane-notaires .nota-guarantee { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-4) var(--space-8); margin-top: var(--section-gap); }
#pane-notaires .nota-guarantee > .btn { flex: none; }
#pane-notaires .nota-guarantee > span { flex: 1 1 0; max-width: 80ch; }
.nc-dialog .nc-conformite { margin-top: 0; padding-top: 0; border-top: 0; }

@media (max-width: 680px) {
  .nc-voies { padding: var(--space-7); }
  .nc-voies, .nc-voies[data-plans] { grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
}
@media (min-width: 681px) and (max-width: 1024px) {
  .nc-voies { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* La preuve tient sur UNE rangée : le nombre de colonnes est la constante
   NC_LIVE_ROW d'app.js, lue par --nc-live-row (4 par défaut, sans script). Les
   vraies cartes et les cases vides inertes partagent la même taille. Sous
   900px la rangée se plie en deux colonnes ; le téléphone a sa propre liste. */
/* A band of its own (propriétaire, 2026-09-24 : « trop près du calendrier ») :
   the section gap above, a kicker naming it, and the page's own gap between
   tiles — four tracks at --gap put the third tile exactly under the calendar,
   so the row reads as the page grid rather than one broken rule. */
#notary-live { margin-top: var(--section-gap); padding: var(--space-7) 0 var(--space-6); }
#notary-live .nc-live-head { flex-direction: column; align-items: flex-start; gap: var(--space-4); }
#notary-live .nc-live-head > .eyebrow { margin: 0; }
.nc-live-grid { position: relative; display: grid; grid-template-columns: repeat(var(--nc-live-row, 4), minmax(0, 1fr)); grid-auto-rows: minmax(128px, 1fr); gap: var(--nc-col-gap); }
@media (max-width: 900px) {
  .nc-live-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* The act's name never shares a line with the tier pill. It used to: at four
   up on a desktop the name had ~84px and read « Refinance… », at two up on a
   phone ~60px and read « Refin… » — refinancement and financement became the
   same card (owner, 2026-09-03). The act takes its own line, then the amount
   and its tier (the pairing a notary scans), then the date.

   container-type: the tile decides its own three- or four-line shape from ITS
   width, not from the viewport's — the same tile is 216px in a desktop rail of
   four and 175px two up on a phone, and it is the tile's width, never the
   screen's, that says whether « 9 410 $ » and « Prioritaire » fit on one line. */
.nc-live-card {
  container-type: inline-size;
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "svc svc" "amt pill" "meta meta";
  /* Keep the date at the foot of the fixed inventory tile, so its spare
     height does not accumulate with the following section's own gap. */
  align-content: space-between; row-gap: 6px; column-gap: 8px; min-width: 0;
  padding: var(--space-6) 0; text-align: left; font: inherit; color: var(--ink);
  border: 0; border-top: 1px solid var(--border); border-radius: 0;
  background: transparent; cursor: pointer;
  transition: border-color var(--motion-fast);
}
.nc-live-card .nc-live-svc { grid-area: svc; }
.nc-live-card .pill { grid-area: pill; justify-self: end; align-self: center; }
.nc-live-card .nc-live-amt { grid-area: amt; }
.nc-live-card .nc-live-meta { grid-area: meta; }
/* A narrow tile: the widest amount (« 12 000 $ », 82px) plus the widest tier
   (« Prioritaire », 74px) plus the gap needs ~164px, so under 168px of tile
   CONTENT (what an inline-size container query measures — the padding is
   already out) the pill drops to its own line rather than landing on top of
   the money. Explicit line placement, which outranks the named areas above. */
@container (max-width: 168px) {
  .nc-live-card .nc-live-amt { grid-column: 1 / -1; }
  .nc-live-card .pill { grid-row: 3; grid-column: 1 / -1; justify-self: start; }
  .nc-live-card .nc-live-meta { grid-row: 4; grid-column: 1 / -1; }
}
.nc-live-card:is(:hover, :focus-visible) { border-color: var(--border-strong); }
.nc-live-card:is(:hover, :focus-visible) .nc-live-amt { color: var(--ink); }
/* display: contents — the name and the pill become tiles of the card's own
   grid, each on the line it belongs to. The wrapper stays in the markup so
   ncLiveCard() keeps building one "top" row. */
.nc-live-top { display: contents; }
/* The span's colour is the act's --svc-* token (set inline) — it paints the
   glyph via currentColor; the act's NAME stays ink. Same grammar as the carnet. */
.nc-live-svc { display: flex; align-items: center; gap: 7px; min-width: 0; }
.nc-live-svc-name { font-size: 12.5px; font-weight: 700; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nc-live-amt { font-size: 19px; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; transition: color var(--motion-fast); }
.nc-live-meta { font-size: 12px; color: var(--ink-muted); }
/* Overflow request: a quiet link into the full inventory. It keeps the card's
   own grid — mark and name, count, invitation (ncLiveCard's three lines) — and
   only gains the accent on interaction. */

/* Phones: a compact grid — eight single-column tiles pushed the sign-in a full
   screen down, so the tiles run two up and the funnel's gate stays in reach.
   auto-fit, not a hard two: at 320px two tiles cannot hold an amount and a
   tier side by side at all, so the grid falls to one full-width tile instead.
   AFTER the base rules — same specificity, so order is what decides. */
@media (max-width: 640px) {
  .nc-live-grid { grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr)); gap: var(--space-4) var(--space-8); }
  .nc-live-card { padding: var(--space-5) 0; row-gap: 4px; }
  .nc-live-amt { font-size: 17px; }
  .nc-live-meta { font-size: 11px; }
}

.nc-live-more:is(:hover, :focus-visible) { border-style: solid; border-color: var(--action-ink); background: var(--action-tint); }

/* The hero's ONE pitch line, shown at every width. The old full paragraph and
   the numbered step list repeated what this line (and the guide) already
   say — one sentence carries the model, the CTAs carry the action. */
.hero-tagline strong { color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .footer-social-link, .footer-contact-link { transition: none; }
  .footer-social-link:hover, .footer-contact-link:hover { transform: none; }
}

#notary-console { margin-top: 12px; max-width: none; }

/* Paid AI preparation is an optional work surface, not a gate on the free
   dossier. Keep its conversion card compact and let the normal checklist lead. */
.nc-ai-product {
  /* The actions column never takes more than half the card: an `auto` track
     grew to the plan menu's one-line width and left the copy 0 px wide. */
  display: grid; grid-template-columns: minmax(0, 1fr) fit-content(50%); gap: 12px 20px;
  align-items: center; margin: 0 0 var(--space-6); padding: 16px 18px;
  border: 1px solid color-mix(in srgb, var(--brand) 28%, var(--border));
  border-radius: var(--radius); background: transparent;
}
.nc-ai-product-copy { min-width: 0; }
.nc-ai-kicker { margin: 0 0 3px; color: var(--subtitle-ink); font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
.nc-ai-title { margin: 0; color: var(--ink); font-size: var(--type-h3); line-height: var(--type-h3-lh); letter-spacing: var(--type-h3-ls); }
.nc-ai-lead { margin: 5px 0 0; color: var(--ink-muted); font-size: 13px; line-height: 1.45; }
.nc-ai-product-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 7px; }
.nc-ai-product-actions select, .nc-ai-product-actions input { min-height: 34px; max-width: 260px; padding: 6px 8px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); font: inherit; font-size: 12px; }
.nc-ai-product-actions input { width: 60px; }
@media (pointer: coarse) {
  .nc-ai-product-actions select, .nc-ai-product-actions input { min-height: var(--control-height); }
  /* Native Safari selects ignore min-height; an explicit height keeps the
     same control target without replacing the platform's picker. */
  .nc-ai-product-actions select { height: var(--control-height); }
}
.nc-ai-quota { padding: 7px 10px; border-radius: var(--radius-sm); background: var(--surface); color: var(--brand-on-surface); font-size: 12px; font-weight: 750; }
.nc-ai-product-status { grid-column: 1 / -1; margin: 0; }
.nc-ai-contribution { grid-column: 1 / -1; }
/* B3/B6 — one short consent line: the checkbox, then a state or a link. */
.nc-ai-consent-line { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin: 6px 0 0; }
.nc-ai-consent-line .help { margin: 0; }
.nc-ai-questions { margin: 8px 0 16px; padding-left: 20px; }
.nc-ai-questions > li { margin: 0 0 14px; }
.nc-ai-question-feedback { margin-top: 8px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-inset); }
.nc-ai-question-feedback .field { margin: 0 0 8px; }
.nc-ai-question-feedback textarea { width: 100%; min-height: 44px; resize: vertical; }
.nc-ai-question-feedback .btn { margin-top: 2px; }
.nc-ai-question-feedback [role='status'] { margin: 7px 0 0; }
@media (max-width: 680px) {
  .nc-ai-product { grid-template-columns: 1fr; }
  .nc-ai-product-actions { justify-content: flex-start; }
}
/* Gate headings (« Accédez aux demandes », « Vérifiez votre boîte courriel »)
   speak in the panel-heading register the partners form panel set: 18px,
   tightened, with the same breath below before the form begins. */
#notary-console .h-sub { margin-bottom: var(--space-8); }
/* Sign-in stays on the page surface; inputs and their actions keep their own
   control surfaces. Its height follows the form instead of the AI comparison. */
.nc-gate { display: block; max-width: 440px; }
.nc-gate-main { min-width: 0; }
/* The gate's kicker is the page's .eyebrow — the hero's « Pour les notaires »
   and the AI column's « Facultatif » — sitting the same --space-4 above its
   heading as the AI kicker does in .nc-voies-intro, so both headings align. */
.nc-gate-kicker { margin-bottom: var(--space-4); }
/* On the brand-poured card the gate heading carries the column: a step up
   from the console's working 18px, still under the hero's h1. */
#notary-console .nc-gate .h-sub { font-size: var(--type-h2); line-height: var(--type-h2-lh); letter-spacing: var(--type-h2-ls); margin-bottom: var(--space-7); }
/* Panes move focus to their <h1> on navigation so screen readers announce the new
   page; that programmatic focus must NOT paint a ring on a non-interactive heading. */
.tab-pane h1:focus, .intro h1:focus { outline: none; box-shadow: none; }
/* Live echo of what the carnet will publish for this bid, colour-keyed to the
   state of the entry (valid Quebec sector / still typing / off-format). */
.prefix-preview { margin: 6px 0 0; min-height: 1.5em; font-size: 12px; line-height: 1.5; color: var(--ink-muted); }
.prefix-preview[data-state='ok'] { color: var(--brand); }
.prefix-preview[data-state='warn'] { color: var(--status-retenue); }
.nc-auth .form-row { margin-bottom: var(--space-7); }
/* The email field matches the button under it: at 120px it sat in the middle of
   an otherwise empty column. Field and CTA share one taller, 15px register —
   the pair must weigh as much as a demand card across the gap. */
.nc-auth .form-row input { width: 100%; padding: 10px 12px; font-size: 15px; }
/* Opaque surface under the brand outline: on the poured card a transparent
   button let the tint bleed through and the door dissolved into its wall. */
.nc-auth-cta { width: 100%; padding: 10px 16px; font-size: 15px; background: transparent; color: var(--brand-on-surface); border-color: var(--brand); }
.nc-auth-fine { margin-top: 10px; }
/* New-notary welcome: the gate's second step. It REPLACES the email step (the
   "Continuer" action branched here), so it must read as a welcome, not as an
   error bolted under a login form. The "Première visite" marker is the same
   outline pill as "Bientôt" — labelled, never alarming. */
.nc-signup { margin-top: 16px; max-width: 560px; padding: var(--pad-card-lg); display: flex; flex-direction: column; gap: 12px; border: 1px solid var(--brand); border-radius: var(--radius); background: transparent; }
.nc-signup:focus { outline: none; }
/* In its modal the signup is the dialog's body, not a card inside a card. */
.nc-dialog .nc-signup { margin: 0; max-width: none; padding: 0; border: 0; }
.nc-dialog .nc-signup[hidden] { display: none; }
.nc-signup-tag { margin: 0; align-self: flex-start; padding: 1px 9px; border: 1px solid var(--brand); border-radius: var(--radius-sm); font-size: 11px; font-weight: 800; letter-spacing: .03em; color: var(--brand); text-transform: uppercase; }
.nc-signup-back { align-self: flex-start; }
.nc-signup-lead { margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink); }
.nc-signup-steps { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 6px; font-size: 13.5px; line-height: 1.45; color: var(--ink); }
/* No identity row in the console: the header account menu already says who is
   connected and carries Se déconnecter — the panel opens on the agenda. */
.nc-h { font-size: var(--type-h4); line-height: var(--type-h4-lh); letter-spacing: var(--type-h4-ls); margin: 18px 0 10px; }
.nc-list { display: flex; flex-direction: column; gap: 10px; }
/* Console lists are grids: a 1240px-wide strip per demand wastes the row and
   caps how many dossiers fit one screen. Open demands pack ~3-up; retained
   cards carry the client dossier and the completion form, so they get a wider
   2-up track. Cards stretch to equal row height, actions pinned to the foot. */
/* 320px track: two demands fit side by side from tablet width (768px) up —
   the more open bids on one screen, the stronger the pane's one message. */
.nc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); gap: 12px; }
.nc-grid--wide { grid-template-columns: repeat(auto-fill, minmax(min(480px, 100%), 1fr)); }

.nc-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-inset); padding: var(--space-7);
  transition: transform var(--motion-fast), background-color var(--motion-fast), border-color var(--motion-fast), box-shadow var(--motion-fast);
}
.nc-card:hover { transform: translateY(-1px); background: var(--action-tint); border-color: var(--border-strong); box-shadow: var(--shadow-sm); }
.nc-card.is-retained { border-left: 3px solid var(--brand); }
.nc-card-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-2) var(--space-6); }
.nc-card-title { min-width: 0; flex: 1 1 auto; font-size: var(--type-h4); font-weight: 700; letter-spacing: var(--type-h4-ls); overflow-wrap: anywhere; }
.nc-card-amount { font-size: 19px; font-weight: 800; letter-spacing: -0.02em; white-space: nowrap; }
/* Signals row: tier / complexity / readiness — the only pills on the card.
   Everything descriptive (lender, perimeter, code) drops to the quiet facts
   line below, so the pills that remain actually mean "compare me". */
.nc-card-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }
.nc-date { font-size: 12px; color: var(--ink-muted); }
/* Case-complexity signal on a notary's open-bid card (outline, colour by level). */
.nc-complexity { font-size: 11px; font-weight: 700; padding: 1px 8px; border-radius: var(--radius-sm); border: 1px solid var(--border); color: var(--ink-muted); }
.nc-complexity[data-level='simple'] { color: var(--ok); border-color: var(--ok); }
.nc-complexity[data-level='complexe'] { color: var(--danger); border-color: var(--danger); }
/* The facts line: one muted sentence-row, dot-separated — reads as context,
   never competes with the signal pills. Children keep their classes (tests,
   urgence accent) but shed the pill chrome here. */
.nc-card-facts { margin-top: 7px; font-size: 12px; line-height: 1.6; color: var(--ink-muted); }
.nc-card-facts > * + *::before { content: '·'; margin: 0 7px 0 3px; color: var(--border-strong); font-weight: 400; }
.nc-card-facts .nc-lender, .nc-card-facts .nc-deplacement, .nc-card-facts .nc-prefixe, .nc-card-facts .nc-distance, .nc-card-facts .nc-date {
  display: inline-block; padding: 0; border: 0; background: none;
  font-size: 12px; font-weight: 600; color: var(--ink-muted); white-space: nowrap;
}
.nc-card-facts .nc-date { font-weight: 700; color: var(--ink); }
.nc-card-facts .nc-date::first-letter { text-transform: uppercase; }
.nc-card-facts .nc-lender-virt { margin-left: 5px; }
.nc-card-facts .nc-deplacement[data-urgence='true'] { font-weight: 700; }
/* The complexity answers, quoted under the facts: a hairline rule instead of
   a floating paragraph, so the block reads as "the file says…". */
.nc-factors {
  margin-top: 8px; padding: 2px 0 2px 10px;
  border-left: 2px solid var(--border-strong);
  font-size: 12px; line-height: 1.55; color: var(--ink-muted);
}
.nc-prefixe {
  font-size: 11px; font-weight: 700; color: var(--ink-muted);
  border: 1px dashed var(--border-strong); border-radius: var(--radius-sm); padding: 1px 6px;
}
.nc-ready {
  font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: var(--radius-sm); white-space: nowrap;
  background: var(--surface-inset); color: var(--ink-muted); border: 1px solid var(--border-strong);
}
.nc-ready[data-ready='true'] { color: var(--ok); border-color: var(--ok); }
/* Retenir owns the card FOOT — the quiet toolbar (.nc-card-more) sits just
   above it, so the primary confirm is the last thing the card says. The
   auto-margin that pins the foot rides the toolbar (it comes first in DOM)
   and only in the open agenda: retained cards keep flowing content. */
/* flex-wrap: the armed confirm (« Confirmer · N $ » + Annuler) widens the
   row — Détails wraps under instead of overflowing the card border. */
.nc-card-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-top: 8px; }
/* ADR 0076 — la ligne qui remplace le geste de travail sur une démonstration :
   discrète, jamais un bouton désactivé qu'on ne peut pas lire. */
.nc-demo-note { font-size: var(--type-h4); color: var(--ink-muted); }
/* Every offer uses the same natural height, across rows as well as within a
   row. Text can wrap or grow with a larger font without clipping information. */
#notary-open-list .nc-card { min-width: 0; padding: 14px; cursor: pointer; overflow-wrap: anywhere; }
#notary-open-list .nc-agenda-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); align-items: stretch; grid-auto-rows: 1fr; }
#notary-open-list .nc-agenda-grid > .nc-card { align-self: stretch; }
#notary-open-list .nc-card-head { flex-wrap: wrap; gap: 4px 12px; }
#notary-open-list .nc-card-title { flex: 1 1 170px; }
#notary-open-list .nc-card-facts > * { max-width: 100%; white-space: normal; }
#notary-open-list .nc-card-meta > *, #notary-open-list .nc-status > * { white-space: normal; }
.nc-card-body { display: flex; flex-direction: column; gap: 2px; }
.nc-card-body[hidden] { display: none; }
.nc-card-location { display: flex; flex-wrap: wrap; gap: 4px 10px; color: var(--ink-muted); font-size: 13px; }
.nc-card-parameters { margin: 6px 0; color: var(--ink-muted); font-size: 13px; line-height: 1.5; }
.nc-card-parameters > div { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; padding-block: 5px; border-bottom: 1px solid var(--border); }
.nc-card-parameters dd { margin: 0; color: var(--ink); font-weight: 600; }
#notary-open-list .nc-card-body { border-top: 1px solid var(--border); padding-top: 10px; margin-top: 4px; }
#notary-open-list .nc-card-actions { margin-top: auto; }
.nc-card-actions .nc-toggle { margin-left: auto; color: var(--ink-muted); }
/* Retenir owns the card foot at every width — the confirm is the card's one
   job; Décliner tucks beside it as a small secondary. The cap keeps it a
   button, not a banner, when a card runs wide. */
.nc-card-actions .nc-accept { flex: 1 1 auto; max-width: 340px; }

/* --- Open agenda: one grid, the date on the card (ADR 0020) --------------- */
/* The date is an attribute of the demand, not a layout axis. A full-width
   band per day left the console 70 % empty whenever a day held one demand,
   and the lone card read as a bare price. Now the day strip sums the week in
   one slim row (each tile doubles as a day filter) and every card packs one
   responsive grid, chronological, saying its own signing day. */
.nc-open-tools {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px 14px; flex-wrap: wrap; margin: 0 0 14px;
}
.nc-filter { margin: 0; }
.nc-open-tools .nc-view-seg { margin-left: auto; }
/* « Filtres » opens the filter MODAL (brand guide §8.2); its badge counts the
   active filters, and the chips and the day rail live in the modal. */
.nc-filters-open { gap: 8px; min-height: 44px; }
.nc-filter-count { min-width: 20px; padding: 1px 5px; border-radius: var(--radius-xs); background: var(--brand-tint-solid); color: var(--brand-on-surface); }
.nc-filters-open[hidden], .nc-filter-count[hidden] { display: none; }
.nc-filter-days { margin-top: var(--space-8); }
@media (max-width: 420px) {
  .nc-open-tools { gap: 8px; }
  .nc-open-tools .nc-view-seg { flex: 1 1 auto; }
  .nc-open-tools .nc-view-seg .seg-btn { flex: 1; padding-inline: 8px; }
}
.nc-view-seg[hidden] { display: none; }
.nc-filter .chip-svc-sub { margin-left: 2px; }
#notary-open-reset[hidden], .nc-offer-notice[hidden] { display: none; }
.nc-offer-notice { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; padding: 14px; margin-bottom: 16px; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }
.nc-offer-notice p { flex: 1 1 260px; margin: 0; font-size: 14px; }
.nc-first-contact { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 12px 0; }
.nc-first-contact p { flex: 1 1 240px; margin: 0; font-size: 13px; color: var(--ink-muted); }
.nc-first-contact .btn { min-height: 44px; }
.nc-h .nc-h-amt { font-weight: 800; color: var(--brand); }
.nc-h .nc-h-sum { font-weight: 600; color: var(--ink-muted); }
.nc-agenda-list { display: flex; flex-direction: column; gap: 14px; }
/* The day rail: mini calendar cells — weekday over the day number, the day's
   money under it, the count as a corner badge. ONE scrollable row at every
   width (scroll-snap, thin scrollbar): a dozen wrapped tiles were a wall
   pushing the first demand below the fold. A pressed cell keeps only its day
   and the rest of the rail dims. */
.nc-days {
  display: flex; flex-wrap: nowrap; gap: 8px;
  overflow-x: auto; padding-bottom: 4px; scrollbar-width: thin;
  scroll-snap-type: x proximity;
}
.nc-daytile {
  /* Grow to share the full rail width (owner, 2026-08-27: no dead space at
     the end of the row); the 84px basis still overflows into scroll when a
     busy month packs more days than the row can hold. */
  position: relative; flex: 1 0 84px; scroll-snap-align: start;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  min-width: 84px; padding: 8px 14px 9px; font: inherit; text-align: center; cursor: pointer;
  color: var(--ink); background: var(--surface-inset);
  border: 1px solid var(--border); border-radius: var(--radius);
  transition: background-color var(--motion-fast), border-color var(--motion-fast), box-shadow var(--motion-fast), opacity var(--motion-fast);
}
.nc-daytile:hover { background: var(--action-tint); border-color: var(--border-strong); }
.nc-daytile.is-on { border-color: var(--action-ink); box-shadow: inset 0 0 0 1px var(--action-ink); background: var(--action-tint); }
/* Holding a day mutes the rest of the rail — the choice reads at a glance. */
.nc-days.has-day .nc-daytile:not(.is-on) { opacity: .55; }
.nc-days.has-day .nc-daytile:not(.is-on):hover { opacity: 1; }
.nc-daytile-cal { display: flex; flex-direction: column; align-items: center; line-height: 1.1; }
.nc-daytile-wd { font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-muted); }
.nc-daytile-num { font-size: 19px; font-weight: 800; letter-spacing: -0.02em; }
.nc-daytile-mo { font-size: 10px; font-weight: 600; color: var(--ink-muted); }
/* "Today" earns the brand accent — it's the day a notary can still fill. */
.nc-daytile[data-today='true'] { border-color: var(--brand); }
.nc-daytile[data-today='true'] .nc-daytile-wd { color: var(--brand); }
.nc-daytile-sum { font-size: 12px; white-space: nowrap; }
.nc-daytile-sum.nc-day-total { font-weight: 800; color: var(--brand); }
/* The per-day count: a small square badge tucked in the cell's corner. */
.nc-day-badge {
  position: absolute; top: 4px; right: 4px; min-width: 16px; padding: 1px 4px;
  font-size: 10px; font-weight: 800; line-height: 1.3;
  color: var(--ink-muted); background: var(--surface-hover);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.nc-daytile.is-on .nc-day-badge { color: var(--action-ink); border-color: var(--action-ink); }
/* The card says its own day — a quiet kicker over the title; today keeps the
   brand accent so the fillable day pops out of the grid. */
.nc-card-when {
  display: flex; align-items: baseline; gap: 7px; margin-bottom: 6px;
  font-size: 11px; font-weight: 700; color: var(--ink-muted);
}
.nc-when-date::first-letter { text-transform: uppercase; }
.nc-when-rel { font-weight: 600; }
.nc-card-when[data-today='true'] .nc-when-rel { color: var(--brand); font-weight: 800; }

/* --- Card: status pills, secondary actions, inline forms ------------------ */
.nc-status { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.nc-pill {
  font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: var(--radius-sm); white-space: nowrap;
  background: var(--surface-inset); color: var(--ink-muted); border: 1px solid var(--border-strong);
}
/* The lender behind a demand — name + virtual (branchless) flag. The notary
   reads this before deciding; the roster in the préférences filters on it. */
.nc-lender {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: var(--radius-sm); white-space: nowrap;
  background: var(--surface-inset); color: var(--ink); border: 1px solid var(--border-strong);
}
.nc-lender-virt, .chip-lender-virt {
  font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .03em;
  padding: 0 6px; border-radius: var(--radius-sm); color: var(--warn, var(--ink-muted));
  border: 1px solid currentColor;
}
.chip-lender-virt { margin-left: 6px; }
/* Who travels for the in-person signature (ADR 0017). The 100 %-online urgency
   band is the outlier a notary opted into — it earns the warning accent. */
.nc-deplacement {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: var(--radius-sm); white-space: nowrap;
  background: var(--surface-inset); color: var(--ink); border: 1px solid var(--border-strong);
}
.nc-deplacement[data-urgence='true'] { color: var(--warn, var(--ink-muted)); border-color: currentColor; }
/* ADR 0035 — la garantie de paiement, sur la rangée de décision du notaire.
   Même registre que les autres signaux (même hauteur, même rayon) : ce n'est
   pas une alarme, c'est un fait à lire avant de retenir. Seul un refus ou une
   réservation expirée prend l'accent d'alerte — ce sont les deux seuls états
   qui appellent un geste. */
.nc-caution {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: var(--radius-sm); white-space: nowrap;
  background: var(--surface-inset); color: var(--ink-muted); border: 1px solid var(--border-strong);
}
.nc-caution[data-caution='posee'] { color: var(--ok); border-color: var(--ok); }
.nc-caution[data-caution='refusee'], .nc-caution[data-caution='expiree'] { color: var(--danger); border-color: var(--danger); }
.nc-caution-line { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.nc-caution-sub { font-size: 12px; color: var(--ink-muted); }
.nc-caution-row { margin-top: 6px; }
.nc-prop-pill[data-status='en_attente'] { color: var(--brand); border-color: var(--brand); }
.nc-prop-pill[data-status='acceptee'], .nc-docs-pill[data-fournie='true'], .nc-via-prop { color: var(--ok); border-color: var(--ok); }
.nc-prop-pill[data-status='refusee'] { color: var(--danger); border-color: var(--danger); }
/* Secondary actions read as a quiet toolbar — link-weight ghosts, no borders:
   three outlined buttons under an outlined primary was four boxes fighting.
   Hover restores the affordance. */
.nc-card-more { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; margin-top: 10px; }
.nc-card-more .btn {
  border-color: transparent; background: transparent;
  color: var(--ink-muted); font-weight: 600; padding-inline: 9px;
}
.nc-card-more .btn:not(:disabled):hover { background: var(--action-tint); color: var(--ink); }
/* In the toolbar the agenda flows with its siblings — pushed right it dangled
   alone whenever the row wrapped. */
.nc-card-more .nc-agenda { margin-left: 0; }
.nc-card.is-declining { opacity: .75; }
.nc-undo-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: auto; padding-top: 12px; }
.nc-undo-lbl { font-size: 12px; font-weight: 700; color: var(--ink-muted); }

/* Agenda ▾ menu: a <details> that drops the three deeplinks under its button. */
.nc-agenda { position: relative; margin-left: auto; }
.nc-agenda > summary { list-style: none; cursor: pointer; }
.nc-agenda > summary::-webkit-details-marker { display: none; }
.nc-agenda > summary::after { content: ' ▾'; font-size: 10px; }
.nc-agenda-menu {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 3; min-width: 140px;
  display: flex; flex-direction: column; padding: 4px;
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow);
}
.nc-agenda-item { display: flex; align-items: center; gap: 8px; padding: 7px 10px; font-size: 13px; font-weight: 600; color: var(--ink); text-decoration: none; border-radius: var(--radius-sm); }
.nc-agenda-item:hover { background: var(--action-tint); }

/* The proposition and documents forms, each in its modal (§8.2); a sent
   form gives way to its own result. */
.nc-dialog-form { display: flex; flex-direction: column; gap: var(--space-4); }
.nc-form-done { margin: 0; font-weight: 700; color: var(--ok); }
.nc-form-done:focus { outline: none; }
/* A request's details, read in their modal: the date and amount, the
   signals, the body and the actions, one rhythm. */
.nc-offer-detail { display: flex; flex-direction: column; gap: var(--space-4); }
.nc-offer-detail > .nc-dialog-act { margin: 0; }
.nc-form-row { display: flex; flex-wrap: wrap; align-items: end; gap: 8px; }
.nc-form-lbl { display: flex; flex-direction: column; gap: 4px; font-size: 12px; font-weight: 600; color: var(--ink-muted); }
.nc-propose-amt { max-width: 140px; }
.nc-propose-delta { font-size: 13px; font-weight: 800; color: var(--ok); padding-bottom: 8px; }
.nc-form-msg { width: 100%; resize: vertical; font: inherit; font-size: 13px; }
.nc-form-foot { display: flex; align-items: center; gap: 8px; }
.nc-form-errors { min-height: 1.5em; }
.nc-docs-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); gap: 6px; }
.nc-docs-item { display: flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; }
.nc-docs-name { flex: 1; min-width: 0; }
/* The request checklist in two groups, documents then answers (ADR 0073
   review, 2026-10-03): a heading sits closer to its own list than to the
   group before it. */
.nc-docs-group + .nc-docs-group { margin-top: var(--space-4); }
/* A team draft's title and its « Vous · 19:26 » stamp: one head, never glued. */
.team-draft-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-4); }
/* La marque de l’étude (2026-10-13). A document dressed by app.js
   etudeMarqueHabiller() wears a LETTERHEAD: a strip of printed paper carrying
   the étude's logo and name between its two colours. The strip is paper in
   every theme — the domain measured both colours against exactly that
   (MARQUE_ETUDE.papier = --paper, held by marque-etude.test.mjs), so the dark
   theme can never put them on a ground they were not checked on. The colours
   arrive as --etude-primaire / --etude-accent on the document; absent, the
   strip falls back to the tokens the print block uses on paper. */
.etude-entete {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-6);
  margin-bottom: var(--space-6); padding: var(--space-5) var(--space-6);
  background: var(--paper); color: var(--etude-primaire, var(--nota-blue-950));
  border-top: var(--space-2) solid var(--etude-primaire, var(--nota-blue-700));
  border-bottom: var(--space-1) solid var(--etude-accent, var(--etude-primaire, var(--nota-blue-700)));
  border-radius: var(--radius-xs);
}
.etude-logo { flex: none; width: auto; height: var(--control-height); max-width: 50%; object-fit: contain; }
.etude-nom { flex: 1 1 12rem; min-width: 0; font-weight: 800; overflow-wrap: anywhere; }
/* The « Marque de l’étude » modal: the logo box is paper too (a logo is drawn
   for paper), the colour row pairs the swatch with its typed value, and the
   preview is the letterhead itself. */
.etude-marque-vignette { display: flex; align-items: center; min-height: var(--control-height); padding: var(--space-5) var(--space-6); background: var(--paper); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.etude-marque-vignette .help { margin: 0; color: var(--canvas-ink-muted); }
.etude-marque-couleur { display: flex; align-items: center; gap: var(--space-4); }
.etude-marque-couleur input[type='color'] { flex: none; width: var(--control-height); height: var(--control-height); padding: var(--space-1); background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); cursor: pointer; }
.etude-marque-couleur input[type='text'] { flex: 1 1 8rem; min-width: 0; }
.etude-marque-apercu { padding: var(--space-6); border: 1px dashed var(--border-strong); border-radius: var(--radius-sm); }
.etude-marque-apercu .help { margin: 0; }
.nc-missing { font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--danger); border: 1px solid var(--danger); border-radius: var(--radius-sm); padding: 0 6px; }

.nc-dossier { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.nc-dossier-h { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--subtitle-ink); margin-bottom: 8px; }
.nc-workflow { margin: 12px 0 16px; padding: 12px 14px; background: var(--surface-inset); border: 1px solid var(--border); border-radius: var(--radius); }
.nc-workflow .nc-dossier-h { margin-bottom: 6px; }
.nc-workflow-ai { margin: 8px 0; font-size: 13px; color: var(--ink); }
.nc-workflow-actions { display: grid; gap: 7px; margin: 8px 0 0; padding-left: 22px; }
.nc-workflow-action { padding-left: 3px; color: var(--ink); }
.nc-workflow-action-meta { display: block; margin-top: 2px; font-size: 12px; color: var(--ink-muted); }
.nc-kv { display: grid; grid-template-columns: minmax(120px, 38%) 1fr; gap: 4px 12px; margin: 0; }
.nc-kv dt { font-size: 12px; font-weight: 600; color: var(--ink-muted); }
.nc-kv dd { font-size: 13px; color: var(--ink); margin: 0; overflow-wrap: anywhere; }
.nc-cal { margin-top: 18px; }
.nc-cal-h { font-size: 13px; font-weight: 700; margin-bottom: 8px; }
.nc-complete { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.nc-complete-h { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-muted); margin-bottom: 8px; }
.nc-complete-row { display: flex; flex-wrap: wrap; align-items: end; gap: 8px; }
.nc-complete-amt { white-space: nowrap; }
.nc-complete-lbl { display: flex; flex-direction: column; gap: 4px; font-size: 12px; font-weight: 600; color: var(--ink-muted); }
.nc-actval { max-width: 140px; }
.nc-complete-done { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.nc-done-badge { font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: var(--radius-sm); white-space: nowrap; background: transparent; color: var(--ok); border: 1px solid var(--ok); }
.nc-done-fee { font-size: 12px; color: var(--ink-muted); }

/* Dossier post-publish bridge (in the day modal). */
.dossier-next { margin-top: 12px; padding: 12px 14px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.dossier-next-row { display: flex; align-items: flex-start; gap: 10px; }
.dossier-next-txt { flex: 1; min-width: 0; }
.dossier-next-h { font-weight: 700; font-size: 14px; }
.dossier-next-badge { flex: none; font-size: var(--type-h4); font-weight: 800; padding: 2px 9px; border-radius: var(--radius-sm); background: var(--surface-inset); color: var(--ink); }
.dossier-next .dossier-bar { margin: 10px 0 0; }

/* Notaires: onboarding form + console side by side (use the horizontal space). */
/* Single full-width notary console (the page's one tool) + get-paid step. */
.nota-guarantee { margin-top: var(--gap); }
/* Lead-delivery preferences — how (in-app/email/SMS) + pace + which acts. */
/* Settings doors — the relevé, the évaluations, the profile, the alerts:
   settings, not the working surface. Each is one row, its heading, its hint
   and ONE button that opens its modal (brand guide §8.2): the console never
   unfolds a panel under the button. */
.nc-door { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-8); margin: var(--space-9) 0 var(--space-4); padding: var(--space-6) var(--space-9); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-inset); }
.nc-door[hidden] { display: none; }
.nc-door-copy { flex: 1 1 16rem; min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2) var(--space-5); }
.nc-door .nc-h { margin: 0; }
.nc-door-hint { margin: 0; }
.nc-door-open { flex: none; }
/* Workspace summaries need room for their figures; tool doors use shorter
   reading columns. Both reflow from their available width, keep DOM order,
   and leave unused tracks intact instead of stretching the last door. */
.nc-summary-grid {
  --nc-summary-min: 26rem;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, max(var(--nc-summary-min), calc((100% - var(--gap)) / 2))), 1fr));
  gap: var(--gap);
  align-items: start;
  margin-block: var(--section-gap);
}
.nc-summary-grid > section { min-width: 0; }
.nc-summary-grid .nc-h { margin: 0 0 var(--space-5); }
.nc-tools-grid {
  --nc-tool-min: 18rem;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, max(var(--nc-tool-min), calc((100% - 3 * var(--gap)) / 4))), 1fr));
  gap: var(--gap);
  align-items: start;
  margin-block: var(--section-gap);
}
.nc-tools-grid > section {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--space-8); min-width: 0; margin: 0; padding: 0;
  border: 0; border-radius: 0; background: transparent; box-shadow: none;
  overflow-wrap: anywhere;
}
.nc-tools-grid > section[hidden] { display: none; }
/* In the Hub each door is its own destination group (data-primary-legacy-view
   on the door, index.html): a destination that hides all six leaves no empty
   grid band behind it. */
.nc-tools-grid:not(:has(> :not([hidden]))) { display: none; }
.nc-tools-grid .nc-door-copy {
  flex: none; display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--space-4); width: 100%;
}
.nc-tools-grid .nc-h, .nc-tools-grid > section > p { margin: 0; }
.nc-tools-grid .btn {
  max-width: 100%; min-height: var(--control-height);
  white-space: normal; overflow-wrap: anywhere; text-align: left;
}
/* The console's working modals carry forms, tables and lists, not a lead
   paragraph: their body keeps the dialog padding without the prose register
   of `.dialog-pad p`, so help lines, errors and saved notes keep their own.
   The title clears the sticky ✕. */
.nc-dialog-body { padding: var(--pad-dialog); }
.nc-dialog-body > h3:first-child { margin: 0 0 var(--space-4); padding-right: var(--space-12); }
.nc-dialog-body > .help { margin-top: var(--space-5); }
.nc-dialog-body .dialog-actions { margin-top: var(--space-8); }
/* The act a modal is about, in one line under its title. */
.nc-dialog-act { margin: 0 0 var(--space-4); font-weight: 700; color: var(--ink); }
/* « Vos évaluations » (ADR 0021): the notary's track record, in its modal.
   Each evaluation carries the facteurs' hairline left rule — the client's
   words, quoted in quiet type under the stars. */
.nc-evals { margin-top: 10px; }
.nc-evals-own { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; margin-bottom: var(--space-4); }
.nc-eval { margin-top: 10px; padding: 2px 0 2px 10px; border-left: 2px solid var(--border-strong); }
.nc-eval-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.nc-eval-stars { color: var(--brand); font-weight: 700; letter-spacing: 2px; white-space: nowrap; }
.nc-eval-svc { font-size: 13px; font-weight: 600; }
.nc-eval-date { font-size: 12px; color: var(--ink-muted); }
.nc-eval-date::first-letter { text-transform: uppercase; }
.nc-eval-comment { margin: 4px 0 0; font-size: 13px; line-height: 1.55; color: var(--ink-muted); }
.nc-prefs-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: 8px 36px; margin-top: 8px; align-items: start; }
.nc-pref-block { min-width: 0; }
.nc-pref-title { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-muted); margin-bottom: 4px; }
.nc-pref-lbl { font-size: 13.5px; }
.nc-pref-pace { display: inline-flex; flex-wrap: wrap; margin: 2px 0 6px; max-width: 100%; }
.nc-pref-pace .seg-btn { font-size: 12.5px; }
/* The line is held whether or not the confirmation is on it — a note that
   appears for two seconds must not shove the preferences under it. */
.nc-prefs-saved { min-height: 1.5em; color: var(--ok); font-weight: 700; margin-top: 10px; }
.nc-pref-lenders { margin-top: 4px; }
/* The full lender roster reads better on its own row of the prefs grid. */
.nc-pref-block--wide { grid-column: 1 / -1; }

/* --- Retained-act conversation (client ↔ notaire) ------------------------ */
/* One construction for both sides: thread of bubbles (mine right, theirs
   left), then a write row. Tokens only — the theme swaps for free. */
.chat { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
.chat-thread { display: flex; flex-direction: column; gap: 6px; max-height: 260px; overflow-y: auto; }
.chat-empty { font-size: 12.5px; color: var(--ink-muted); margin: 0; }
.chat-msg { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.chat-msg.is-mine { align-items: flex-end; }
.chat-bubble {
  max-width: 85%; padding: 7px 11px; font-size: 13.5px; line-height: 1.45;
  background: var(--surface-inset); color: var(--ink);
  border: 1px solid var(--border); border-radius: var(--radius-lg) var(--radius-lg) var(--radius-lg) var(--radius-xs);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.chat-msg.is-mine .chat-bubble {
  /* Grey on light, brand on the night (--brand-pour); the brand-tinged edge
     and the right alignment still say « mine ». */
  background: color-mix(in srgb, var(--brand-pour) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--brand) 35%, var(--border));
  border-radius: var(--radius-lg) var(--radius-lg) var(--radius-xs) var(--radius-lg);
}
.chat-when { font-size: 10.5px; color: var(--ink-muted); }
/* Who wrote it, when a team member wrote under the notary's side (ADR 0073):
   one line under the bubble — « Équipe de l’étude · 14:32 » — in the time
   stamp's quiet register. */
.chat-stamp { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); }
.chat-who { font-size: var(--type-eyebrow); font-weight: 600; color: var(--ink-muted); }
.chat-who:not(:last-child)::after { content: '·'; margin-left: var(--space-2); font-weight: 400; }
/* « Vu » (2026-09-04): the other side's read receipt, one quiet word under the
   last own message they have read — the same register as the time stamp. */
.chat-msg .chat-seen { display: block; font-size: 11px; color: var(--ink-muted); margin-top: 2px; }
.chat-msg.is-mine .chat-seen { text-align: right; }
.chat-row { display: flex; align-items: flex-end; gap: 8px; }
.chat-input { flex: 1 1 auto; resize: vertical; min-height: 40px; }
/* The shared composer (chatComposer): the write row, then a meta line — the
   refusal on the left, the « N / 500 » counter on the right from 400 on. */
.chat-write { display: flex; flex-direction: column; gap: 4px; }
.chat-composer .chat-input { resize: none; overflow-y: auto; max-height: 160px; }
.chat-meta { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); min-height: 0; }
.chat-error { margin: 0; font-size: 12.5px; color: var(--danger); }
.chat-count { margin-left: auto; font-size: 11px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.chat-error, .chat-count { min-height: 1.5em; }
.nc-chat .nc-dossier-h, .my-offer-chat-h { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-muted); }

/* --- Withdrawal (désistement) after acceptance --------------------------- */
.nc-release { margin-top: 10px; padding-top: 8px; border-top: 1px dashed var(--border); }
.nc-release-open { font-size: 12.5px; color: var(--ink-muted); }
/* The withdrawal confirms in its own short modal (§8.2): one column, one gap. */
.nc-release-form { display: flex; flex-direction: column; gap: var(--space-4); }
.nc-release-form > p { margin: 0; }
.nc-release-motif { resize: vertical; min-height: 40px; }
.nc-release-actions { display: flex; align-items: center; gap: 8px; }
.nc-release-terms { margin: 0; }

/* --- La mise en relation est complète (ADR 0033) ------------------------- */
/* The contact gate, said over the working surface: a brand-tinted band
   naming the missing fields, one door into the form. Square register. */
.nc-banner {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: var(--space-6) var(--space-9); margin: 0 0 var(--space-7);
  padding: var(--space-6) var(--space-8);
  border: 1px solid var(--brand); border-radius: var(--radius); background: var(--brand-tint-solid);
}
.nc-banner[hidden] { display: none; }
.nc-banner-body { flex: 1 1 280px; min-width: 0; }
.nc-banner-h { font-size: 14px; font-weight: 800; letter-spacing: -0.01em; color: var(--subtitle-ink); }
.nc-banner-p { margin: var(--space-2) 0 0; font-size: 13px; color: var(--ink-muted); }
.nc-banner-list { margin: var(--space-2) 0 0; padding-left: 18px; font-size: 13px; color: var(--ink); }
.nc-banner-list li { padding: 1px 0; }
/* The identity fields (nom / étude / téléphone / adresse) sit two-up. */
.nc-profil-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: 0 var(--space-8); }
.nc-field-err { min-height: 1.5em; color: var(--danger); }
/* « Votre client » leads the retained card: the name, the two contact links
   (the phone glyph rides the tel:), then the facts that frame the signing. */
.nc-client { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.nc-client-nom { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.nc-client-links { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-8); margin-top: var(--space-2); }
.nc-client-tel, .nc-client-mail {
  display: inline-flex; align-items: center; gap: 6px; min-height: 28px;
  font-size: 13px; font-weight: 600; color: var(--brand); text-decoration: none; overflow-wrap: anywhere;
}
.nc-client-tel:hover, .nc-client-mail:hover { text-decoration: underline; }
.nc-client-facts { margin-top: var(--space-3); }
/* Retained files: a compact overview in the list; the working surface opens
   in the wide file modal (§8.2). auto-fit releases empty tracks when there is
   only one retained file. */
#notary-retained-list {
  grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
  grid-auto-rows: auto;
  align-items: stretch;
}
#notary-retained-list .nc-card { min-width: 0; container-type: inline-size; }
/* The file modal lays the conversation and the dossier side by side as soon
   as they fit: its body is the container the workspace queries. */
#nc-file-dialog { width: min(1040px, calc(100vw - var(--space-16))); }
.nc-file { min-width: 0; container-type: inline-size; }
#notary-retained-list .nc-card:hover { transform: none; }
.nc-retained-summary, .nc-retained-overview, .nc-retained-file { min-width: 0; }
.nc-retained-summary .nc-card-head { flex-wrap: wrap; }
.nc-retained-summary .nc-card-title { flex: 1 1 170px; overflow-wrap: anywhere; }
.nc-retained-summary .nc-client-links > * { min-width: 0; overflow-wrap: anywhere; }
.nc-retained-summary .nc-client-links svg { flex: 0 0 auto; }
.nc-retained-summary .nc-card-facts > * { max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
.nc-retained-actions { margin-top: auto; padding-bottom: 0; }
.nc-retained-buttons { display: flex; flex-wrap: wrap; gap: 8px; width: 100%; }
.nc-retained-buttons > .btn { flex: 1 1 150px; }
.nc-retained-workspace {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-10);
  margin-top: var(--space-10); padding-top: var(--space-10); border-top: 1px solid var(--border);
}
.nc-retained-workspace[hidden] { display: none; }
.nc-file > .nc-retained-workspace { margin-top: 0; padding-top: 0; border-top: 0; }
.nc-retained-workspace > .nc-chat, .nc-retained-file > .nc-dossier { margin-top: 0; padding-top: 0; border-top: 0; min-width: 0; }
.nc-retained-workspace .chat-bubble, .nc-retained-workspace .nc-kv dd { overflow-wrap: anywhere; }
.nc-retained-workspace .chat-row { flex-wrap: wrap; }
.nc-retained-workspace .chat-input { min-width: 0; flex-basis: 180px; }
.nc-retained-workspace .nc-card-more { flex-wrap: wrap; }
.nc-retained-workspace .nc-release-open { white-space: normal; text-align: left; min-height: 44px; }
@container (min-width: 720px) {
  .nc-retained-summary { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; }
  .nc-retained-summary .nc-client { margin: 0; padding: 0 0 0 24px; border-top: 0; border-left: 1px solid var(--border); }
  .nc-retained-actions { justify-content: space-between; padding-top: 20px; }
  .nc-retained-buttons { width: auto; }
  .nc-retained-buttons > .btn { flex: 0 1 auto; }
  .nc-retained-workspace { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; }
  .nc-retained-file { border-left: 1px solid var(--border); padding-left: 24px; }
}
/* Unread: a filled square badge on the card head and in the heading. */
.nc-unread {
  display: inline-flex; align-items: center; gap: 4px; vertical-align: middle;
  font-size: 11px; font-weight: 800; padding: 2px 8px; white-space: nowrap;
  border-radius: var(--radius-sm); background: var(--brand); color: var(--brand-ink);
}
.nc-card-head .nc-unread { margin: 0 auto 0 var(--space-4); }
/* The console's own composer (used until the shared one ships): the counter
   under the box, the refusal under the row. */
.nc-composer { display: flex; flex-direction: column; gap: 4px; }
.nc-composer-main { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.nc-composer .chat-input { resize: none; overflow-y: auto; max-height: 180px; }
.nc-chat-count { align-self: flex-end; font-size: 11px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.nc-chat-count, .nc-chat-err { min-height: 1.5em; }
.nc-chat-err { margin: 0; font-size: 12.5px; color: var(--danger); }
/* A deep-linked card (« #notaires&acte=… ») flashes once, then rests. */
.nc-card.is-flash { animation: ncFlash 1.6s ease-out both; }
/* The client's offer band flashes the same way when the bell or a deep link lands on it. */
.my-offer-detail.is-flash { animation: ncFlash 1.6s ease-out both; }
@keyframes ncFlash {
  0% { box-shadow: 0 0 0 4px var(--focus-ring); border-color: var(--brand); }
  100% { box-shadow: none; }
}
@media (prefers-reduced-motion: reduce) { .nc-card.is-flash { animation: none; border-color: var(--brand); } }
/* The Retenir sheet: a wider dialog (it reads back a whole engagement), the
   retained card's key/value register, left-rule sections for the terms. */
.nc-retenir-dialog { max-width: 880px; max-height: calc(100dvh - 32px); }
.nc-retenir-dialog[open] { display: flex; flex-direction: column; overflow: hidden; }
.nc-retenir-dialog > .nc-retenir { overflow-y: auto; min-height: 0; overscroll-behavior: contain; }
.nc-retenir-dialog .nc-retenir > h3 { padding-right: 24px; }
.nc-retenir-act { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.nc-retenir-svc { font-size: 19px; font-weight: 700; }
.nc-retenir-date { font-size: 13px; color: var(--ink-muted); }
.nc-retenir-date::first-letter { text-transform: uppercase; }
.nc-retenir-signals { margin: 0 0 20px; }
.nc-retenir-columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
.nc-retenir-section { min-width: 0; padding: 18px 0; border-top: 1px solid var(--border); overflow-wrap: anywhere; }
.nc-retenir-section h4 { margin: 0 0 14px; font-size: 15px; font-weight: 700; color: var(--ink); }
.nc-retenir-section h5 { margin: 16px 0 8px; font-size: 13px; font-weight: 700; color: var(--ink); }
.nc-retenir-kv { grid-template-columns: minmax(100px, 35%) minmax(0, 1fr); gap: 12px; margin: 0; }
.nc-retenir-kv [hidden] { display: none; }
.nc-retenir-kv dd { line-height: 1.5; }
#nc-retenir-fee { display: block; font-size: 24px; line-height: 1.2; font-variant-numeric: tabular-nums; color: var(--ink); }
.nc-retenir-kv .nc-deplacement, .nc-retenir-kv .nc-prefixe, .nc-retenir-kv .nc-lender, .nc-retenir-kv .nc-distance {
  display: inline; padding: 0; border: 0; background: none; font-size: 13px; font-weight: 600; color: var(--ink); white-space: normal;
}
.nc-retenir-sub { color: var(--ink-muted); font-weight: 400; }
.nc-retenir-missing { margin-top: 12px; padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-inset); }
.nc-retenir-missing h5 { margin-top: 0; }
.nc-retenir-count { display: inline-block; margin-left: 8px; font-size: 12px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.nc-retenir-checklist, .nc-retenir-factor-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 24px; margin: 0; padding-left: 18px; font-size: 13px; line-height: 1.55; }
.nc-retenir-checklist li, .nc-retenir-factor-list li { padding-left: 2px; }
.nc-retenir-checklist li::marker { color: var(--ink-muted); }
.nc-retenir-details { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; margin: 0; }
.nc-retenir-detail { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 13px; line-height: 1.5; }
.nc-retenir-detail dt { color: var(--ink-muted); }
.nc-retenir-detail dd { margin: 0; font-weight: 600; }
.nc-retenir-more { margin-top: 6px; }
.nc-retenir-footer { flex: none; padding: 12px 22px; border-top: 1px solid var(--border); background: var(--surface); }
.nc-retenir-footer .btn { min-height: 44px; min-width: 0; }
.nc-retenir-error { margin: 0 0 10px; color: var(--danger); font-size: 13px; }
.nc-retenir-sec { margin: 0 0 var(--space-6); padding: 2px 0 2px 10px; border-left: 2px solid var(--border-strong); }
.nc-retenir-sec[hidden] { display: none; }
.nc-retenir-h { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-muted); margin-bottom: 4px; }
.nc-retenir .nc-retenir-p { margin: 0; font-size: 13px; line-height: 1.55; color: var(--ink-muted); }
.nc-retenir .nc-retenir-readiness, .nc-retenir .nc-retenir-appointment { margin: 10px 0 0; }
.nc-retenir .nc-retenir-contact { margin: 0; }
.nc-retenir-bareme { list-style: none; margin: 0 0 var(--space-2); padding: 0; font-size: 13px; }
.nc-bareme-row { display: flex; flex-wrap: wrap; gap: 0 4px; padding: 2px 0; }
.nc-bareme-days { font-weight: 700; font-variant-numeric: tabular-nums; }
.nc-bareme-rate { font-weight: 700; color: var(--ink); }
.nc-bareme-amt { font-weight: 800; color: var(--brand); }
.nc-bareme-free .nc-bareme-rate { color: var(--ok); }
.nc-retenir-dialog .dialog-actions .btn-primary { flex: 1 1 auto; flex-wrap: wrap; }
.nc-retenir-go-amt { white-space: nowrap; }
@media (max-width: 640px) {
  .nc-retenir-columns, .nc-retenir-checklist, .nc-retenir-factor-list, .nc-retenir-details { grid-template-columns: minmax(0, 1fr); }
  .nc-retenir-columns { gap: 0; }
  .nc-retenir-dialog > .nc-retenir { padding: 20px 16px; }
  .nc-retenir-footer { padding: 12px 16px; }
  .nc-retenir-footer .dialog-actions { gap: 8px; }
  .nc-retenir-footer .btn { padding: 10px 12px; }
}
.nc-pay { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border); }
.nc-pay .btn { margin-top: 4px; }
/* Notary earnings roll-up: realised value, the price the CLIENT paid for the
   Nota service, and what the notary keeps — never a cut of their fees. */
.nc-earn { margin-top: 6px; }
.nc-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.nc-stat { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; background: var(--surface); }
.nc-stat-v { font-size: 22px; font-weight: 800; letter-spacing: -0.02em; color: var(--ink); }
.nc-stat-k { font-size: 12px; font-weight: 600; color: var(--ink-muted); margin-top: 3px; }
.nc-stat-net { border-color: var(--brand); }
.nc-stat-net .nc-stat-v { color: var(--brand); }
/* Kill the default <p> margins so the empty state + the money facts read as
   one quiet block, not three floating paragraphs. */
.nc-earn .help { margin: 8px 0 0; }

/* --- « Votre cote » (ADR 0028) -------------------------------------------
   The measure of the SERVICE, published whole — since ADR 0031 it decides no
   dollar: the notary keeps 100 % of their fees. The number leads at display
   size; the axes are the arithmetic under it. Square register throughout —
   no pill, no circle; every colour is a token. */
.nc-cote { margin-top: var(--space-3); }
.nc-cote-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-3) var(--space-8); }
.nc-cote-n {
  display: inline-flex; align-items: baseline; gap: var(--space-1);
  padding: var(--space-2) var(--space-6);
  border: 1px solid var(--brand); border-radius: var(--radius);
  background: var(--brand-tint-solid); color: var(--brand);
}
.nc-cote-v { font-size: var(--type-h2); font-weight: 800; letter-spacing: -0.03em; line-height: 1.1; color: var(--brand-on-surface); }
.nc-cote-max { font-size: 14px; font-weight: 600; color: var(--ink-muted); }
.nc-cote-head .help { margin: 0; flex: 1 1 240px; }

.nc-cote-axes { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: var(--space-5); margin-top: var(--space-6); }
.nc-cote-axe { border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-5) var(--space-6); background: var(--surface); }
.nc-cote-axe-h { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
.nc-cote-axe-nom { font-size: 13px; font-weight: 600; color: var(--ink); }
.nc-cote-axe-pts { font-size: 13px; font-weight: 700; color: var(--brand-on-surface); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* A thin bar, not a gauge: it ranks the four axes at a glance, the figures
   below carry the truth. --radius-xs keeps it in the square register. */
.nc-cote-bar { height: 5px; margin-top: var(--space-3); border-radius: var(--radius-xs); background: var(--surface-inset); overflow: hidden; }
.nc-cote-bar-fill { display: block; height: 100%; border-radius: var(--radius-xs); background: var(--brand-bright); }
.nc-cote-detail { margin-top: var(--space-3); font-size: 12px; line-height: 1.6; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
/* The separator is CSS, never a text node: the figures stay translatable
   fragment by fragment. */
.nc-cote-f + .nc-cote-f::before { content: ' · '; color: var(--border-strong); }

.nc-bareme-row {
  margin-top: var(--space-3); padding: var(--space-2) var(--space-5);
  border-left: 2px solid var(--border); border-radius: var(--radius-xs);
  font-size: 13px; color: var(--ink-muted); font-variant-numeric: tabular-nums;
}

/* The per-service record, inside « Vos évaluations ». */
.nc-svc { margin-top: var(--space-6); border-top: 1px solid var(--border); padding-top: var(--space-5); }
.nc-svc-h { font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-muted); }
.nc-svc-row { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-3) var(--space-6); margin-top: var(--space-4); }
.nc-svc-nom { font-size: 13px; font-weight: 600; color: var(--ink); }
.nc-svc-actes { font-size: 12.5px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.nc-svc-none { font-size: 12.5px; color: var(--ink-muted); font-style: italic; }

/* The act-by-act statement: the full disclosure, act by act. Wide content
   scrolls inside its own box — the console never scrolls sideways. */
.nc-actes { margin-top: var(--space-5); overflow-x: auto; }
.nc-actes-table { width: 100%; border-collapse: collapse; font-size: 12.5px; font-variant-numeric: tabular-nums; }
.nc-actes-table th, .nc-actes-table td { padding: var(--space-3) var(--space-5); text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
.nc-actes-table thead th { font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-muted); }
.nc-acte-num { text-align: right; }
.nc-acte-net { font-weight: 700; color: var(--brand); }
.nc-acte-total th, .nc-acte-total td { border-bottom: 0; border-top: 2px solid var(--border-strong); font-weight: 700; color: var(--ink); }
/* ADR 0029 — a line settled OFF the platform. The money never reached Nota and
   the service fee is still owed, so the net must not read as money banked: the
   figure drops out of brand ink into the quiet register the rest of the row
   speaks in. No new shape, no new colour — the same weight, one tone down. */
.nc-actes-table .nc-acte-row[data-paye='false'] .nc-acte-net { color: var(--ink-muted); }
/* The marker stacks UNDER the act's name inside a `white-space: nowrap` cell,
   so it has to opt back into wrapping and take a measure of its own — without
   one it sets the column's width and pushes the money columns off the box. */
.nc-acte-etat { white-space: normal; max-width: 30ch; }
/* « Frais de service à percevoir » is a total that has NOT been settled. It
   keeps the totals row's geometry but steps down twice — a hairline instead of
   the strong rule, muted instead of full ink — so the eye reads it as an
   appendix to the settled totals rather than a sixth column of takings. */
.nc-acte-du th, .nc-acte-du td { border-top: 1px solid var(--border); color: var(--ink-muted); }
.nc-actes .help { margin: var(--space-4) 0 0; }

/* --- Demonstration data, declared ---------------------------------------
   store.listMonth falls back to domain fixtures when the API is unreachable.
   That is legitimate; being indistinguishable from the real market is not.
   The register is deliberately NOT an alarm — --warn carries "read this",
   --danger would carry "something broke". Square, tokens only. */
.demo-banner {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2) var(--space-5);
  margin: var(--space-5) auto 0; width: min(1200px, calc(100% - var(--space-12)));
  padding: var(--space-4) var(--space-7);
  border: 1px solid var(--border); border-left: 3px solid var(--warn);
  border-radius: var(--radius); background: var(--surface-inset);
}
.demo-banner[hidden] { display: none; }
.demo-banner-t { font-size: 12.5px; font-weight: 800; letter-spacing: 0.02em; color: var(--warn); }
.demo-banner-b { font-size: 12.5px; line-height: 1.5; color: var(--ink-muted); }

/* The per-region mark: an aggregate figure is never bare. Small, quiet, and
   right where the numbers are. */
.demo-mark {
  /* `justify-self` et non `display` : la marque tombe aussi dans des GRILLES
     (.pulse-head en a deux colonnes), et un élément de grille s'étire par
     défaut — inline-block n'y change rien. */
  display: inline-block; justify-self: start; padding: 0 var(--space-3);
  border: 1px solid var(--warn); border-radius: var(--radius-sm);
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.04em;
  line-height: 16px; white-space: nowrap; vertical-align: middle;
  color: var(--warn); background: var(--surface);
}
/* Local development discloses the demonstration elsewhere: the banner and the
   marks step aside. This override follows the base rules it hides, so the
   first rule a reader (or a scan) meets for the mark is its own register. */
html[data-local-demo-disclosures] #demo-banner,
html[data-local-demo-disclosures] .demo-mark { display: none; }

/* Mark slots: they take no room until a mark lands in them. */
.nc-live-head:empty { display: none; }
.nc-live-head { margin: 0 0 var(--space-4); }
.day-head .demo-mark { margin-left: var(--space-4); }

/* The offline "publication" note: nothing left the device. */
.demo-note {
  margin: var(--space-4) 0 0; padding: var(--space-4) var(--space-5);
  border-left: 3px solid var(--warn); border-radius: var(--radius-xs);
  background: var(--surface-inset);
  font-size: 12.5px; line-height: 1.55; color: var(--ink-muted);
}

/* --- Legal accordions ------------------------------------------------------
   The five legal panes fold each clause into one outlined row (owner,
   2026-09-26 : « a lot of text nobody will read »). Native <details>, so the
   fold works without JS and a deep link (#t=conditions&section=…) can spring
   a clause. Same grammar as .disclosure, at legal reading size: title
   on the row, chevron on the trailing edge, body under the fold. */
.legal-list { display: grid; gap: var(--space-3); margin: 4px 0 0; align-items: start; }
/* Une colonne à 1 360 px faisait courir chaque ligne vide jusqu'au chevron et
   chaque texte ouvert sur 130 caractères. À pleine largeur, les plis vont deux
   par deux et chaque clause ouverte garde sa mesure de lecture. */
@media (min-width: 1100px) {
  .legal-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .legal-overview {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-16); align-items: start; padding-block: var(--intro-pad-top);
  }
  .legal-overview > .intro { display: block; min-width: 0; padding-block: 0; }
  .legal-overview > .intro > h1 + p { max-width: 47ch; margin-top: var(--space-8); }
  .legal-overview > .cards { min-width: 0; grid-template-columns: 1fr; margin-top: 0; }
}
/* Keyword first, detail on click (owner, 2026-10-12). A long paragraph shows
   its opening lines once legal-sections.js has armed it (`.is-clamped`), and
   its control opens the whole paragraph in the reading dialog. Unarmed (no
   script, no <dialog>) it reads in full, and the control stays hidden. */
[data-read-more].is-clamped { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.read-more { margin-top: var(--space-4); }
#read-more-dialog { max-width: min(720px, 94vw); width: 94vw; }
#read-more-dialog .dialog-pad p { max-width: 68ch; margin: var(--space-6) 0 0; font-size: var(--type-lead); line-height: var(--type-lead-lh); color: var(--ink); }
/* In the two-column intro the control follows its lede, in the lede's column. */
@media (min-width: 700px) {
  .intro:not(.intro--hero) > .read-more { grid-column: 2; grid-row: 5; }
  .intro:not(.intro--hero) > h1 + p[data-read-more] { grid-row: 2 / span 3; }
}
.legal-item { border: 1px solid var(--border); border-radius: var(--radius-sm); }
.legal-item > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: baseline; gap: 10px;
  min-height: var(--control-height); padding: 10px 14px;
  font-size: var(--type-h3); line-height: var(--type-h3-lh); font-weight: 600; color: var(--ink);
}
.legal-item > summary::-webkit-details-marker { display: none; }
.legal-item > summary::after {
  content: ''; flex: none; width: 7px; height: 7px; align-self: center;
  border-right: 1.5px solid var(--ink-muted); border-bottom: 1.5px solid var(--ink-muted);
  transform: rotate(-45deg); transition: transform var(--motion-fast);
}
.legal-item[open] > summary::after { transform: rotate(45deg); }
.legal-item > summary:hover { color: var(--action-ink, var(--ink)); }
.legal-item > summary:focus-visible {
  outline: none; border-radius: var(--radius-sm);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
.legal-item-body { padding: 0 14px 14px; max-width: 68ch; font-size: var(--type-body); line-height: var(--type-body-lh); color: var(--ink-muted); }
.legal-item-body a { color: var(--ink); }
/* A deep link lands: the opened clause says so once, in the focus ring's ink. */
.legal-item.is-flash { box-shadow: 0 0 0 3px var(--focus-ring); }
/* --- Security and compliance -------------------------------------------
   A quiet trust block: one outer surface, with no stack of nested borders or
   certification-looking badges. The wording deliberately distinguishes an
   active programme from a completed audit. */
.compliance-section {
  margin-top: var(--space-8); padding: clamp(20px, 3vw, 28px);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface-inset);
}
.compliance-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px;
}
.compliance-head .eyebrow { display: block; margin-bottom: 6px; }
.compliance-head h2 { max-width: 680px; margin: 0; }
.compliance-head p { max-width: 760px; margin: 10px 0 0; color: var(--ink-muted); font-size: 14px; line-height: 1.55; }
.compliance-status {
  flex: none; display: inline-flex; align-items: center; min-height: 32px;
  padding: 6px 11px; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--brand-pour) 12%, var(--surface));
  color: var(--brand); font-size: 12px; font-weight: 800;
  white-space: nowrap;
}
.compliance-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px; margin-top: 20px;
}
.compliance-card {
  display: flex; flex-direction: column; justify-content: center; gap: 4px;
  min-width: 0; min-height: 70px; padding: 14px 16px;
  border-radius: var(--radius); background: var(--surface);
}
.compliance-card strong { color: var(--ink); font-size: 15px; line-height: 1.25; overflow-wrap: anywhere; }
.compliance-card span { color: var(--ink-muted); font-size: 13px; line-height: 1.3; }
.compliance-note { margin: 16px 0 0; color: var(--ink-muted); font-size: 12px; line-height: 1.5; }
@media (max-width: 680px) {
  .compliance-head { display: block; }
  .compliance-status { margin-top: 14px; }
  .compliance-grid { grid-template-columns: 1fr; gap: 8px; }
  .compliance-card { min-height: 0; }
}
/* The legal panes' version line: an unreviewed draft says so, and when it changed. */
.legal-stamp { margin: var(--space-4) 0 0; font-size: var(--type-body); font-weight: 600; letter-spacing: .01em; color: var(--ink-muted); }

/* Notary cards cascade in like agenda rows; disabled under reduced-motion */
.nc-card { animation: rowIn var(--motion-fast) var(--ease-ui) both; }

@media (max-width: 480px) {
  .nc-kv { grid-template-columns: 1fr; gap: 1px 0; }
  .nc-kv dd { margin-bottom: 6px; }
}

/* Le tapis général. Il est !important, donc toute LIGNE TENUE (registre plus
   haut) doit répondre !important elle aussi — c'est le seul endroit du
   registre où c'est inévitable, et un test le vérifie. */
.hidden, [hidden] { display: none !important; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); contain: paint; }

.tab-pane { display: none; }
.tab-pane.is-active { display: block; animation: paneInForward var(--motion-ui) var(--ease-ui) both; }
.tab-pane.is-active > .wrap, .tab-pane.is-active > .layout { will-change: transform, opacity; }
html[data-nav-direction='back'] .tab-pane.is-active { animation-name: paneInBack; }

/* ==========================================================================
   Motion — subtle micro-interactions. All disabled under reduced-motion.
   ========================================================================== */
@keyframes paneIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes paneInForward { from { opacity: 0; transform: translateX(6px); } to { opacity: 1; transform: none; } }
@keyframes paneInBack { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
/* Nav active indicator: accent line grows from zero width under the selected tab. */
@keyframes navIndicator { from { width: 0; opacity: 0; } to { width: 60%; opacity: 1; } }
/* Drawer item stagger: each row slides up and fades in. */
@keyframes menuItemIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes navIndicatorIn { from { opacity: 0; transform: scaleX(.45); } to { opacity: 1; transform: scaleX(1); } }
@keyframes dialogIn { from { opacity: 0; transform: translateY(10px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes backdropIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes rowIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes pop { 0% { transform: scale(.9); } 60% { transform: scale(1.04); } 100% { transform: scale(1); } }
/* The last frame must leave no transform standing: `translateY(0)` with
   fill-mode `both` keeps the sheet a containing block, and the arrival frame
   (position: fixed, and inside the sheet while the demo walks it) would be
   measured against the sheet instead of the viewport. `none` is the same
   position and frees the descendant again — provided nothing HOLDS it: a
   `none` interpolated from a transform list and kept by fill-mode `both`
   computes as matrix(1, 0, 0, 1, 0, 0), still a containing block (2026-09-24:
   the film's last caption was cut by the sheet's edge). So the entrances fill
   backwards only: the first frame before they start, the dialog's own style
   once they end — at rest every dialog is opaque and untransformed. */
@keyframes sheetUp { from { transform: translateY(100%); } to { transform: none; } }

/* A dialog opens on the same curve a pane does. It used to carry its own
   cubic-bezier(.2,.8,.2,1), so modals and sections did not move alike. */
dialog[open] { animation: dialogIn var(--motion-ui) var(--ease-ui) backwards; }
dialog[open]::backdrop { animation: backdropIn var(--motion-ui) var(--ease-ui) both; }
.bid-row { animation: rowIn var(--motion-fast) var(--ease-ui) both; }
.gauge-fill { transition: width var(--motion-enter) var(--ease-ui); }
.dossier-bar > span { transition: width var(--motion-enter) var(--ease-ui); }
.dossier-check { transition: background var(--motion-fast), border-color var(--motion-fast), transform var(--motion-fast); }
.dossier-check[data-on='true'] { animation: pop var(--motion-fast) var(--ease-ui); }
.btn, .nav-tab, .icon-btn, .cal-cell, .pill { transition: background var(--motion-fast), border-color var(--motion-fast), color var(--motion-fast), transform var(--motion-fast), box-shadow var(--motion-fast); }
.card { transition: transform var(--motion-fast), box-shadow var(--motion-fast), border-color var(--motion-fast); }
.card:hover { transform: translateY(-2px); border-color: var(--border-strong); box-shadow: var(--shadow); }

/* Layered entrance — the landing plays like the brand films: the headline
   cascades in with a breath of blur, the market card pops with overshoot,
   feed rows stack in, reward amounts glow once. Panes are display:none when
   inactive, so the sequence replays on each door the visitor walks through.
   Inside .intro--hero the cascade must run on .intro-main's CHILDREN (the
   eyebrow/h1/p live one level down), so the column itself stays still. */
@keyframes fadeUp { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
@keyframes heroIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes cardPop { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes amtGlow { 0%, 100% { text-shadow: none; } 50% { text-shadow: 0 0 7px color-mix(in srgb, var(--brand-bright) 22%, transparent); } }
.intro > *, .intro--hero .intro-main > * { animation: heroIn var(--motion-enter) var(--ease-ui) both; }
.intro--hero > .intro-main { animation: none; }
.intro .eyebrow { animation-delay: .04s; }
.intro h1 { animation-delay: .12s; }
.intro p { animation-delay: .24s; }
.intro .hero-cta { animation-delay: .36s; }
/* The market pulse is the films' demand card: it pops, then its rows stack. */
.intro--hero .pulse { animation: cardPop var(--motion-enter) var(--ease-ui) var(--motion-enter) both; }
.pulse-rows > * { animation: fadeUp var(--motion-enter) var(--ease-ui) both; }
.pulse-rows > *:nth-child(2) { animation-delay: .08s; }
.pulse-rows > *:nth-child(3) { animation-delay: .16s; }
.pulse-rows > *:nth-child(4) { animation-delay: .24s; }
/* Notaires: the live open demands stagger in like the film's feed. */
.nc-live-grid > * { animation: fadeUp var(--motion-enter) var(--ease-ui) both; }
.nc-live-grid > *:nth-child(2) { animation-delay: .09s; }
.nc-live-grid > *:nth-child(3) { animation-delay: .18s; }
.nc-live-grid > *:nth-child(4) { animation-delay: .27s; }
.cards .card { animation: fadeUp var(--motion-enter) var(--ease-ui) both; }
.cards .card:nth-child(2) { animation-delay: .08s; }
.cards .card:nth-child(3) { animation-delay: .16s; }

/* ENTRÉES AU DÉFILEMENT — un registre PARTAGÉ par les portes (2026-09-20).
   `.reveal` n'a d'effet que si le script a posé `html.has-reveal` (wireReveal,
   app.js) : sans script, sans IntersectionObserver ou en mouvement réduit, rien
   n'est jamais caché. Un bloc monte de --space-6 et s'éclaircit sur le barreau
   des entrées ; ses enfants marqués `--reveal-i` suivent à --menu-stagger.
   `.accent-line` est le trait d'accent de 3 px du guide (brand.html, « Le trait
   d'accent ») qui se TRACE de gauche à droite à l'arrivée du bloc : droit,
   plein, arrêté au bord du conteneur — la carte le rogne à ses coins. */
.has-reveal .reveal { opacity: 0; transform: translateY(var(--space-6)); }
.has-reveal .reveal.is-in { opacity: 1; transform: none; transition: opacity var(--motion-enter) var(--ease-ui), transform var(--motion-enter) var(--ease-ui); transition-delay: calc(var(--reveal-i, 0) * var(--menu-stagger)); }
.accent-line { position: relative; overflow: hidden; }
.accent-line::before { content: ''; position: absolute; inset: 0 0 auto; height: 3px; background: var(--accent-line, var(--action-ink)); transform-origin: left center; transition: transform var(--motion-enter) var(--ease-ui); transition-delay: calc(var(--motion-ui) + var(--reveal-i, 0) * var(--menu-stagger)); }
.has-reveal .reveal:not(.is-in) .accent-line::before, .has-reveal .reveal.accent-line:not(.is-in)::before { transform: scaleX(0); }
/* `.reveal-1` … `.reveal-6` posent le rang d'une cascade (--reveal-i ×
   --menu-stagger) sur un bloc `.reveal` ou sur un enfant tracé ; la variable
   s'hérite. `.rail-draw` est le MONTANT d'une échelle : un filet vertical de
   2 px (le « séparateur » du guide) qui se TRACE de haut en bas à l'arrivée de
   son bloc `.reveal` — par clip-path, pour qu'un pointillé se découvre sans
   s'étirer ; l'appelant le place par --rail-x / --rail-top / --rail-bottom, et
   `.rail-draw--goal` le tire en pointillé : un objectif, pas un acquis. */
.reveal-1 { --reveal-i: 1; } .reveal-2 { --reveal-i: 2; } .reveal-3 { --reveal-i: 3; }
.reveal-4 { --reveal-i: 4; } .reveal-5 { --reveal-i: 5; } .reveal-6 { --reveal-i: 6; }
.rail-draw { position: relative; }
.rail-draw::after { content: ''; position: absolute; left: var(--rail-x, 0); top: var(--rail-top, 0); bottom: var(--rail-bottom, 0); width: 2px; background: var(--rail-ink, var(--action-ink)); clip-path: inset(0); transition: clip-path var(--motion-enter) var(--ease-ui); transition-delay: calc(var(--motion-ui) + var(--reveal-i, 0) * var(--menu-stagger)); }
.rail-draw--goal::after { background: repeating-linear-gradient(to bottom, var(--rail-ink, var(--action-ink)) 0 var(--space-2), transparent var(--space-2) var(--space-4)); }
.has-reveal .reveal:not(.is-in) .rail-draw::after, .has-reveal .reveal.rail-draw:not(.is-in)::after { clip-path: inset(0 0 100% 0); }
/* `.stroke-draw` : un glyphe au trait qui se DESSINE à l'arrivée de son bloc
   `.reveal`. Chaque forme du SVG porte pathLength="1" : un seul tiret de
   longueur 1 suffit alors à toutes, quelle que soit leur taille. L'état final
   (décalage 0) est le glyphe entier — c'est aussi l'état sans script. */
.stroke-draw :is(path, rect, circle, line) { stroke-dasharray: 1; stroke-dashoffset: 0; transition: stroke-dashoffset var(--motion-enter) var(--ease-ui); transition-delay: calc(var(--motion-enter) + var(--reveal-i, 0) * var(--menu-stagger)); }
.has-reveal .reveal:not(.is-in) .stroke-draw :is(path, rect, circle, line) { stroke-dashoffset: 1; }
@media (prefers-reduced-motion: reduce) {
  .tab-pane.is-active, .intro > *, .intro--hero .intro-main > *, .intro--hero .pulse,
  .pulse-rows > *, .nc-live-grid > *, .cards .card { animation: none; }
  dialog[open] .dialog-pad > *, dialog[open] .auth-body > *,
  .acct-action, .acct-panel { animation: none !important; }
}
/* Mobile performance (propriétaire, 2026-09-16) : les écrans tactiles n'ont
   pas de survol — le translateY(-1px) des cartes déclenche un flash visible
   au toucher. Les GPU mobiles paient cher les will-change et backdrop-filter. */
@media (pointer: coarse) {
  .nc-card:hover { transform: none; box-shadow: none; }
}
@media (max-width: 768px) {
  .tab-pane.is-active > .wrap, .tab-pane.is-active > .layout { will-change: auto; }
  .nc-live-grid > * { animation-delay: 0s !important; }
}

/* More hover micro-interactions across client surfaces */
.bid-row { transition: background-color var(--motion-fast) var(--ease-ui); }
.bid-row:hover { background: var(--action-tint); }
.dossier-item { transition: background-color var(--motion-fast) var(--ease-ui); }
.dossier-item:hover { background: var(--action-tint); }
.legend-item { transition: color var(--motion-fast) var(--ease-ui); }

.calendar-subscription-field { display: grid; gap: 6px; margin: 10px 0; }
.calendar-subscription-field input { width: 100%; min-width: 0; }
.calendar-subscription-field .btn { justify-self: start; }
/* LES RANGÉES D'AGENDAS. Le carnet public (#notary-providers, la rangée
   d'action du héros) et « Vos signatures dans votre agenda » de la console
   sont une seule rangée : la vraie marque de chaque agenda (le sprite #cal-*
   d'index.html), son nom, la hauteur de contrôle du système. Chaque bouton a
   la largeur de son icône et de son libellé, et la rangée part de la gauche
   (propriétaire, 2026-10-03 : « improve the disposition, use real provider
   icons ») : `flex: 1 1 auto` étirait les quatre boutons de la console en
   barres de 470 px sur un écran large. Au repos ils restent discrets ;
   l'accent de la section marque l'interaction (le registre du héros, qui garde
   sa grille de quatre et son pas, plus bas). Un bouton désactivé — aucun flux
   encore — garde l'encre éteinte de .btn:disabled. */
.sub-card-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-top: var(--space-6); }
.sub-card-actions .btn { flex: none; min-height: var(--control-height); gap: var(--space-3); padding-inline: var(--space-6); font-size: var(--type-body); font-weight: 700; }
.sub-card-actions .btn:not(:disabled) { color: var(--ink); border-color: var(--border-strong); }
.sub-card-actions .btn:not(:disabled):is(:hover, :focus-visible) { color: var(--action-ink); border-color: var(--action-ink); background: var(--action-tint); }
.brand-ic { flex: none; display: block; }

/* The manual subscription link lives in the ICS modal (#sub- / #notary-
   subscription-dialog); the inline disclosure that used to hold it is gone. */
.calendar-subscription-field label { color: var(--ink-muted); font-size: var(--type-h4); font-weight: 700; }
.subscription-input-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 8px; }
.subscription-input-row input { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.subscription-input-row .btn { justify-self: stretch; white-space: nowrap; }
/* Reserve the longest phone recovery message so the centred modal and its
   controls stay still as copying moves between pending, success and failure. */
.dialog-pad .calendar-subscription-status { min-block-size: 4.5em; font-size: var(--type-body); line-height: var(--type-body-lh); margin-block: var(--space-2) 0; }
.sub-card-footnote { margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--border); }
/* LES DEUX FENÊTRES D'ABONNEMENT ICS (propriétaire, 2026-09-26 : « make this
   modal wider … bigger fonts … better border … use the color »). À 440 px le
   lien d'abonnement se tronquait lui-même (« …/carnet/fee… ») et le mode
   d'emploi Google/Outlook était le plus petit corps du site. Elles prennent
   donc 640 px, le titre au barreau h2, les boutons à la hauteur de contrôle du
   système, et la MARQUE comme cadre : le filet de 3 px du haut est repris sur
   tout le contour. Adressées par identifiant, comme #beta-details-dialog —
   c'est une fenêtre réglée, pas la géométrie partagée des modales. */
:is(#sub-subscription-dialog, #notary-subscription-dialog) {
  max-width: min(640px, 92vw); width: 92vw;
  border: 1px solid var(--brand); border-top: 3px solid var(--brand);
}
:is(#sub-subscription-dialog, #notary-subscription-dialog) h3 { font-size: var(--type-h2); line-height: var(--type-h2-lh); letter-spacing: var(--type-h2-ls); }
:is(#sub-subscription-dialog, #notary-subscription-dialog) .btn { min-height: var(--control-height); padding: var(--space-5) var(--space-9); font-size: var(--type-body); }
:is(#sub-subscription-dialog, #notary-subscription-dialog) .subscription-input-row input { min-block-size: var(--control-height); }
/* À 640 px le message de repli (« la copie automatique a échoué… ») tient en
   deux lignes : la réserve de 4,5 em, calibrée sur le mode étroit, n'y fait
   qu'ouvrir un trou entre le lien et le mode d'emploi. */
@media (min-width: 560px) { .dialog-pad .calendar-subscription-status { min-block-size: 3em; } }

@media (max-width: 520px) {
  .sub-card-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sub-card-actions .btn { min-width: 0; }
}
@media (max-width: 400px) {
  .subscription-input-row { grid-template-columns: 1fr; }
  .subscription-input-row .btn { width: 100%; }
}
.nc-card-actions .mini-btn { margin-left: auto; }
/* Compact card everywhere: on the signed-out landing the grid places it in
   the right column under the gate; signed-in it keeps this small step down. */
/* Borderless notification bell in the header. */

/* Calendar loading: an indeterminate top bar while the carnet fetches (real
   latency on the live API). Scoped to the carnet panel; overlay only. */
#carnet-panel { position: relative; }
#carnet-panel.is-loading { pointer-events: none; }
#carnet-panel.is-loading::after {
  content: ''; position: absolute; top: 0; left: -40%; height: 2px; width: 40%;
  background: var(--brand-bright); border-radius: var(--radius-xs);
  box-shadow: 0 0 8px var(--brand-bright);
  animation: loadbar 1.05s ease-in-out infinite;
}
#carnet-panel.is-loading #cal-grid, #carnet-panel.is-loading #agenda { opacity: .55; transition: opacity var(--motion-fast); }
@keyframes loadbar { 0% { left: -40%; } 55% { left: 62%; } 100% { left: 100%; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
  .brand:hover .brand-mark img { transform: none; }
}

/* ==========================================================================
   Responsive — mobile / tablet / desktop, no horizontal overflow anywhere.
   ========================================================================== */
@media (max-width: 900px) {
  /* 14 top / 4 bottom inverted the desktop asymmetry and let the market copy
     nearly touch the calendar card. */
  .intro { padding-block: var(--space-7); }
}
/* THE HERO'S TWO-COLUMN THRESHOLD, and everything that follows from it.
   Below this the market panel cannot sit beside the copy, so it stacks under
   it. 768px is where two columns still leave the pulse ~340px: enough for the
   act name, its two fr-CA amounts ("1 250 $ · 1 950 $") AND the volume bar on
   one row. At 720–767 a column is ~300px, and the amounts painted over the
   bar and each other — prices must never jam, so the hero stacks instead.

   Below it the pitch is trimmed: the eyebrow goes (the h1, the tagline and
   the two CTAs still say what this is) and the market panel keeps its
   FIGURES and drops its counts caption, so the carnet lands near the fold. */
/* 768–900 — the copy still holds two columns; the market strip beside it does
   not. The threshold below was measured in English: in French a half-column at
   768 leaves the strip ~330px, and « repère du mois » over « 2 279 $ » ran 19px
   under the book arrow while the kicker painted over the month. So the strip
   takes the WHOLE width under the copy from 900 down, which is what the rules
   two blocks up have claimed since they were written. Everything else stays
   desktop here — the eyebrow, the gauges, the counts caption; the phone's
   two-card strip still starts below 768. */
@media (min-width: 768px) and (max-width: 899.98px) {
  .intro--hero { grid-template-columns: 1fr; gap: clamp(18px, 3vw, 26px); }
}
@media (max-width: 767.98px) {
  .intro--hero { grid-template-columns: 1fr; gap: clamp(18px, 3vw, 26px); }
  /* The tagline (and the notary lede) IS the phone copy; only the eyebrow goes. */
  .intro .eyebrow { display: none; }
  /* Side by side rather than stacked. */
  .hero-cta { flex-direction: row; flex-wrap: wrap; }
  /* Size to content — no text wrapping inside buttons. They stack on narrow screens. */
  .hero-cta .btn { flex: 0 1 auto; white-space: nowrap; }
  /* THE PHONE MARKET STRIP (owner, 2026-08-27: « pour la version mobile,
     financing et refinancing, on peut utiliser un autre UI — le but c'est de
     focusser sur les availabilities et le calendrier »). The stacked rows
     cost ~300px of the first screen and pushed the calendar under the fold;
     the two acts now sit SIDE BY SIDE as a pair of compact cards — the act's
     name over its two captioned figures. The volume bar and the counts
     caption stay desktop-only (market colour, not a booking decision), and
     the per-row book arrow goes: the hero CTA right above already books.
     Same DOM as the desktop rows — a card still filters the carnet below,
     and the active act keeps .is-on, indicated by its underlined name. */
  .pulse-rows { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
  /* The item wrapper dissolves so each ROW is the grid child; its book arrow
     is display:none, so nothing else surfaces from the wrapper. */
  .pulse-item { display: contents; }
  .pulse-item .mini-btn { display: none; }
  /* Compact filters retain their own spacing on phones. */
  .pulse-item .pulse-row { padding: 10px 12px; margin: 0; }
  .pulse-row {
    display: flex; flex-direction: column; align-items: stretch; gap: 7px;
  }
  .pulse-svc { font-size: clamp(12px, 3.5vw, 13.5px); }
  /* The captions STAY: two bare amounts stacked read as one confusing price.
     Each figure takes a full line of its card — caption left, amount right. */
  .pulse-figs { flex-direction: column; align-items: stretch; gap: 3px; }
  /* « repère du mois » + a five-figure amount do not fit one 115px line at
     320px: let the amount drop under its caption instead of past the card. */
  .pulse-fig { flex-direction: row; flex-wrap: nowrap; justify-content: space-between; align-items: baseline; gap: 8px; min-width: 0; }
  .pulse-fig-k { font-size: 10.5px; }
  .pulse-fig-v { font-size: 14px; }
  .pulse-bar, .pulse-meta { display: none; }
}

/* At phone widths, give the offering strip one calm reading column. Two
   narrow cards force both the service name and the caption/value pair onto
   extra lines (especially « Power of attorney » and « month’s reference »).
   One wider card keeps each fact on one line and lets the four acts scan as a
   consistent list without clipping or ellipses. */
@media (max-width: 480px) {
  .pulse-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }

  .pulse-rows { grid-template-columns: minmax(0, 1fr); }
  .pulse-fig { flex-wrap: nowrap; }
  .pulse-head { display: flex; }
  .pulse-svc { white-space: nowrap; }
}
/* The DRAWER threshold is 900: above it the header carries the full control
   set (compact band 900–1099.98); below it everything hands off to the
   hamburger — the narrow header is brand → avatar → burger. Theme and language
   keep their drawer rows (#mnav-theme / #mnav-lang), so hiding them here
   loses nothing; the guide « ? » is the fixed bubble on desktop and the
   footer link / first-visit guide on phones (see ux-nav.test.mjs). */
@media (max-width: 899.98px) {
  .nav-burger { display: grid; }
  .nav-tabs, .header-auth, body:not([data-nota-surface="acquisition"]) #theme-toggle { display: none; }
  #lang-toggle { display: none; }
  /* Language and theme move to the drawer, and nothing else lives on the
     strip — the voice door that kept it here left on 2026-09-24 (ADR 0069). */
  .header-tools { display: none; }
}

/* The quiet language control and the compact consent notice live once, in the
   « Final visual pass » further down: an identical earlier copy (and an unused
   igChooserReveal keyframe) sat here, fully overridden (audit 2026-10-03). */

/* In the connected Hub, action feedback takes document space rather than
   covering calendar dates or form controls. feedback.js owns its contextual
   parent; this component keeps the shared palette and dismissal control. */
.nota-feedback.nota-feedback-hub {
  position: static;
  inset: auto;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  margin-block: 0 var(--space-8);
  align-items: flex-start;
  transform: none;
  z-index: auto;
  animation: none;
  transition: none;
}
.nota-feedback.nota-feedback-hub .nota-feedback-message {
  max-block-size: var(--hub-feedback-message-max-block, min(calc(var(--control-height) * 4), 30dvh));
  overflow: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}
.nota-feedback.nota-feedback-hub .nota-feedback-message:focus-visible {
  outline: var(--space-1) solid var(--action-ink);
  outline-offset: var(--space-1);
}

/* Above the drawer threshold the drawer can never appear, open class or not. */
@media (min-width: 900px) {
  .mnav, .mnav-scrim { display: none; }
}

@media (max-width: 680px) {
  /* THE LEGEND WAS ONE CRAMMED PARAGRAPH. Everything lived in a single
     wrapping flex row, so "SERVICE" landed mid-line right after "Extreme 8x"
     with a vertical rule in front of it, reading as one run-on block. Each
     label now takes its own line and its keys wrap underneath it, and the rows
     get room to breathe. */
  .legend { gap: var(--space-4) var(--space-5); }
  .legend-label { flex: 1 0 100%; margin-top: var(--space-2); }
  /* The rule and the indent only make sense inline, which it no longer is. */
  .legend-label--sep { margin-left: 0; padding-left: 0; border-left: 0; }
  /* Four lines restating what the cell already prints, on the screen with the
     least room for it. */
  .legend-note { display: none; }

  /* No phone gutter of its own: the page keeps `--layout-gutter`, the rail the
     header and footer logos start on. A fixed 16 px here left the content up
     to 11 px left of both logos between 400 and 680 px (owner, 2026-10-03:
     « the logo must be as on the left as any body content »;
     logo-content-rail.test.mjs). The token itself bottoms out at 16 px. */
  .site-header .wrap { gap: 8px; } /* height follows --header-h below */
  /* The header shrinks here, so the scroll offset it feeds must shrink with it,
     or a scrolled-to cell lands under the header instead of below the gap. */
  :root { --header-h: 56px; --sticky-top: 76px; }
  .brand { --lockup-tile: 32px; font-size: 17px; }
  .nav-tab { padding: 6px 9px; font-size: 13px; }
  .intro h1 { font-size: var(--type-h1-compact); }
  /* No padding here. The cal container queries own cell padding, and a
     viewport rule this late in the file silently outranks all of them. */
  /* No min-height: @container cal owns cell sizing, and a viewport rule this
     far down the file outranks it at equal specificity. */
  /* .cal-avg deliberately has NO font-size here. The container query compacts it
     by zeroing the font and reprinting attr(data-compact) from ::after; a
     viewport rule that restores a visible size cannot remove the ::after, so the
     two used to print together as "3 285 $3,3k". Sizing belongs to the container
     queries, which is what this cell's content responds to anyway. */
    /* Sides stay at 14 so the grid lines up with the toolbar, filter bar and
     legend that bracket it inside the same card. */
  /* Padding only. gap:4px here outranked the 3-column reflow's own gap, so
     the phone grid packed 2px tighter than the token chose. */
  .cal-grid { padding: var(--space-4) var(--space-7) var(--space-6); }

  .cal-row { gap: 4px; }
  .cal-dow { font-size: 10px; }
  .filters { padding: 10px 14px; gap: 8px; }
  .field { flex: 1 1 40%; }
  /* :not(.visually-hidden): this attribute selector outranks the class, and a
     full-width HIDDEN date input was silently widening the booking sheet's
     scroll box by 16px on phones. */
  select:not(.visually-hidden), input[type='text'], input[type='email'], input[type='tel'], input[type='url'], input[type='number'], input[type='date']:not(.visually-hidden) { min-width: 0; width: 100%; }
  /* Phone: the lender dropdown stretches with its sheet like every other field. */
  .crit-row .nselect { max-width: none; }
  .cards { grid-template-columns: 1fr; }
  .section { padding: 28px 0; }
  #day-dialog, dialog { width: 96vw; }
  /* The day-booking dialog becomes a bottom sheet on phones (thumb-reachable,
     slides up). Ordered after the 96vw rule so width:100% wins. */
  #day-dialog { margin: 0; inset: auto 0 0 0; width: 100%; max-width: 100%; max-height: 92dvh; border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
  #day-dialog[open] { animation: sheetUp var(--motion-ui) var(--ease-ui) backwards; }
  .day-head { padding: 16px 60px 10px 16px; }
  /* The offer rows shrink with the bands that bracket them; the 2px difference
     compensates .bid-row's own left border. */
  .day-bids .bid-row { padding-left: 14px; padding-right: var(--space-8); }
  .day-bids-count { padding-left: var(--space-8); padding-right: var(--space-8); }
  .day-book { padding: 14px 16px 18px; }
  /* The day dialog becomes a full-width sheet on phones, so its rows are as tight
     as the list cards — give them the same two-line treatment. */
  .day-bids .bid-row { flex-wrap: wrap; row-gap: 3px; }
  .day-bids .bid-amount { order: 1; }
  .day-bids .pill, .day-bids .status-chip { order: 2; }
  .day-bids .bid-meta { order: 4; flex: 1 0 100%; }
  .day-bids .status-chip { max-width: none; }
  /* Explainer stacks the badge over the text on narrow screens. */
  .hero-cta { gap: 8px; }
  .hero-cta .btn-lg { flex: 1 1 auto; }
  /* (Auth buttons left the narrow header entirely — they live in the
     hamburger drawer below 900px.) */
}
/* (The theme toggle leaves the header with the tabs at 899.98px — it lives in
   the hamburger drawer on phones, so it stays reachable at every width.) */
/* Comfortable touch targets on phones (>=44px), gated to coarse pointers so the
   desktop density is untouched. */
/* Comfortable 44px touch targets on ANY coarse pointer (phones AND tablets, e.g.
   iPad portrait at 768) — not width-gated. Fine pointers (mouse/trackpad) keep the
   desktop density untouched. Includes the smaller controls that were being missed. */
@media (pointer: coarse) {
  .btn, .nav-tab, .chip, .seg-btn, .acct-item, .acct-legal-link, input[type='range'], .nselect-btn { min-height: 44px; }
  /* Le sélecteur FR|EN et l'interrupteur de thème (.mini-seg-btn) manquaient à
     l'appel (2026-09-06) : sur téléphone l'en-tête les range dans le tiroir, et
     ils y tenaient 35 × 24 px — sous la cible tactile, pour le seul geste qui
     change la langue de tout le site. La piste garde sa hauteur d'origine à la
     souris ; au doigt, les deux boutons montent à 44. */
  .mini-seg-btn { min-height: 44px; }
  /* Et la piste suit : à 28 px de haut elle laissait ses propres boutons
     dépasser de 8 px en haut et en bas. 44 + les deux marges de 2. */
  .mini-seg { height: 48px; }
  .nselect-opt { min-height: 44px; display: flex; align-items: center; }
  /* Repeated at .header-auth .btn specificity: the ≤680px header rule sets a
     32px floor on the SAME selector, and on a phone (coarse + narrow) it was
     out-ranking the plain .btn floor above — leaving the only sign-up button
     under the 44px touch target. */
  .header-auth .btn { min-height: 44px; }
  /* Footer links and the brand are plain <a>s (~20px tall) — they need the same
     thumb-sized hit area as the buttons above. inline-flex so the gap-based
     footer row keeps its alignment. */
  .site-footer a, .brand { min-height: 44px; display: inline-flex; align-items: center; }
  /* « Comment ça marche » is a BUTTON among those links (it opens the guide
     dialog), so the rule above never reached it — and on a phone, where the
     « ? » bubble is deliberately absent (ADR 0022), the footer is the only
     door left to the guide. It was 26px tall. */
  .site-footer .footer-link { min-height: 44px; display: inline-flex; align-items: center; }
  /* A text field is a touch target like any other: the pane's own inputs came
     out 42px from their padding alone, two short of the promise. */
  input[type='text'], input[type='email'], input[type='tel'], input[type='url'], input[type='number'],
  input[type='search'], input[type='password'], input[type='date'], textarea { min-height: 44px; }
  .mini-btn { width: 44px; height: 44px; }
  .cal-nav .btn { min-width: 44px; }
  /* flex:none — in the drawer head the lockup grew and squeezed the close
     button to 32px wide (android lens, 2026-09-11); a thumb target does not shrink. */
  .icon-btn { width: 44px; height: 44px; flex: none; }
  /* « Offrir autant · N $ » is a 26px text button on the booking sheet; it only
     appears when the day already has an offer, which is why the lens caught it
     in CI and not locally. A thumb target is 44px whatever the data. */
  .day-beat { min-height: 44px; }
}
@media (max-width: 420px) {
  .nav-tabs { gap: 0; }
  .nav-tab { padding: 6px 7px; font-size: 12px; }
  /* .cal-cell and .cal-daynum are sized by @container cal, not from here. */
}

/* Never let the page scroll sideways */
html { max-width: 100%; overflow-x: hidden; overflow-x: clip; }
body { max-width: 100%; overflow-x: hidden; overflow-x: clip; }
/* Smooth scrolling lives here, once. Under prefers-reduced-motion the universal
   `scroll-behavior: auto !important` reset (see the reduce block after
   @keyframes loadbar) overrides it. Every anchored/focus/programmatic scroll
   stops BELOW the sticky header. */
html { scroll-behavior: smooth; scroll-padding-top: var(--sticky-top); }

/* ==========================================================================
   Carnet view switcher (Calendrier · Liste)
   ========================================================================== */
.view-panel { min-width: 0; }
.view-panel[hidden] { display: none; }


/* ==========================================================================
   Phone compaction — calendar toolbar (≤680px).
   Last in the file on purpose: these reflow rules must outrank the component
   defaults above at equal specificity.
   ========================================================================== */
@media (max-width: 680px) {
  /* THE TOOLBAR WAS A TOWER. Every control wrapped to its own line — nav row,
     title row, dispo pill row, count row, Filtres row — ~230px of chrome
     before the first date. Three tight rows instead: [nav … Filtres],
     [title], [dispo … count]. */
  .cal-toolbar { padding: var(--space-5) var(--space-7) var(--space-3); row-gap: var(--space-3); }
  /* The spacer goes back to being a spacer (the 680px rule above turns it
     into a row break; here the break comes from the title's own 100% basis). */
  .cal-toolbar .header-spacer { flex: 1 1 auto; height: auto; order: 2; }
  .cal-nav { order: 1; }
  #filters-toggle { order: 3; }
  .cal-title { order: 4; flex: 1 0 100%; font-size: 14px; }
  .cal-avail { order: 5; padding-block: 2px; }
  .result-count { order: 6; margin-left: auto; }
}

/* ==========================================================================
   Conformité (ADR 0033 §5 · ADR 0031) — the notary landing's compliance block.
   Detailed articles stay under the sign-in gate, outside the short film.
   Folds away with the gate: signed in, the console is a workspace.
   ========================================================================== */
.nc-conformite { margin-top: var(--space-16); padding-top: var(--space-12); border-top: 1px solid var(--border); }
/* Signed in, the console is a workspace and the compliance band folds away
   with the gate. The band is no longer the form's next sibling (it sits in the
   pane's grid, one level up), so the fold reads the gate through the wrap. */
.nc-conformite-kicker { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--subtitle-ink); margin-bottom: var(--space-3); }
.nc-conformite-h { font-size: var(--type-h4); line-height: var(--type-h4-lh); letter-spacing: var(--type-h4-ls); color: var(--subtitle-ink); margin: 0 0 var(--space-5); }
.nc-conformite-tiles { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); }
.nc-conformite-tile {
  display: grid; grid-template-columns: auto 1fr; column-gap: var(--space-5); row-gap: var(--space-1); align-items: start;
  /* Side by side the tiles share one height: the rows stay at the top instead
     of splitting the extra space between the label and its text. */
  align-content: start;
  padding: var(--space-5) var(--space-6);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
}
.nc-conformite-ic {
  grid-row: 1 / span 2; width: 30px; height: 30px; border-radius: var(--radius-sm);
  display: grid; place-items: center; color: var(--brand); background: var(--brand-tint);
}
.nc-conformite-ic svg { width: 18px; height: 18px; }
.nc-conformite-art { font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--subtitle-ink); }
.nc-conformite-txt { font-size: 13px; line-height: 1.45; color: var(--ink); }
.nc-conformite-order { margin: var(--space-8) 0 0; font-size: 12.5px; font-weight: 700; color: var(--ink-muted); }
.nc-conformite-link { display: inline-block; margin-top: var(--space-3); font-size: 13px; font-weight: 700; color: var(--brand); }

/* ==========================================================================
   Les promesses du notaire (ADR 0052 §A) — le bloc qui dit, avant le compte,
   ce que Nota donne : l'agenda, les trous comblés, ce que l'urgence paie, la
   fiche complète. Même registre que la bande Conformité plus bas (kicker,
   sur-titre, tuiles carrées sur --surface) : deux blocs d'annonce, un seul
   dessin. Le titre d'une tuile est de l'encre, jamais du bleu (registre
   d'encre) ; le bleu ne porte ici que le glyphe et le MONTANT.
   ========================================================================== */
/* Signé, la console est un plan de travail : le bloc se replie avec la porte,
   exactement comme #nc-conformite (il lit la porte à travers le wrap). */
/* Quatre promesses veulent un carré 2×2, jamais trois et une orpheline : un
   auto-fit donnait 3 + 1 dès que le rail passait 900px (constaté au
   navigateur, 2026-09-12). Deux colonnes fixes dès qu'il y a la place. */
/* Le chiffre de l'urgence : la seule chose bleue de la tuile, parce que c'est
   un montant. Vide tant que app.js ne l'a pas écrit — la phrase qui suit se
   tient seule, elle ne commence pas par une espace orpheline. */
/* Le chiffre de l'urgence ouvre la phrase, en encre : c'est une PHRASE, pas un
   montant isolé, et le registre d'encre réserve le bleu à une information qui
   se montre seule (un montant, un lien, une pastille). Une phrase entière en
   bleu se lisait comme un lien. */

/* ==========================================================================
   Live support chat (ADR 0026 · ADR 0033 §5) — the floating question door.
   Its own `sup-*` namespace: the retained-act `.chat-*` rules (a flex column,
   start-aligned) once reached in and dropped the visitor's bubbles on the
   wrong side. Same square scale as every control; brand blue only on the
   trigger and the visitor's own bubbles. Fixed above everything, footer
   included — and STACKED above the guide « ? » bubble on desktop: bottom
   70px clears the bubble's 16px + 42px (ux-nav.test.mjs pins the sum).
   The panel opens from the speed-dial, the drawer's « Messagerie » row and
   an emailed #messagerie link — no fab of its own (markup gone 2026-09-17),
   so the calendar corner ADR 0022 cleared stays clear.
   ========================================================================== */
.sup-wrap { --sup-edge: var(--space-8); --sup-bottom: calc(var(--control-height) + var(--space-12) + var(--space-1)); }
.sup-panel {
  /* One inset for every row: header, thread, notes, starters, topics and
     composer start on the same left edge. */
  --sup-inset: var(--space-8);
  position: fixed; inset: auto var(--sup-edge) var(--sup-bottom) auto; margin: 0; padding: 0;
  width: min(380px, calc(100vw - var(--sup-edge) * 2)); max-width: none;
  max-height: calc(100dvh - var(--sup-bottom) - var(--sup-edge)); overscroll-behavior: contain;
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); overflow-x: hidden; overflow-y: auto;
}
.sup-panel:not([open]) { display: none; }
.sup-head { padding: var(--space-6) calc(var(--space-6) + var(--control-height)) var(--space-5) var(--sup-inset); border-bottom: 1px solid var(--border); }
.sup-title { font-weight: 800; letter-spacing: -0.01em; }
.sup-sub { margin: var(--space-1) 0 0; font-size: 12.5px; line-height: 1.4; color: var(--ink-muted); }
/* La fermeture est une icône carrée du registre, pas un caractère posé dans le
   texte : même gabarit que les autres boutons-icônes, et une cible de 44 px. */
.sup-close {
  appearance: none; border: 1px solid var(--border); background: transparent; color: var(--ink-muted);
  font: inherit; font-size: 14px; line-height: 1; cursor: pointer; flex: none;
  width: var(--control-height); height: var(--control-height); display: inline-grid; place-items: center; border-radius: var(--radius-sm);
}
.sup-close:hover { background: transparent; border-color: var(--action-ink); color: var(--ink); }
.sup-close:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 1px; }
/* ADR 0046 — le journal ne réserve plus 96 px de vide. Un fil neuf n'a rien à
   montrer : ce sont les questions d'amorce, juste dessous, qui occupent la
   place, et elles, elles mènent quelque part. Le journal ne prend donc de la
   hauteur qu'une fois qu'il porte des bulles. */
.sup-log { max-height: 300px; overflow-y: auto; padding: var(--space-7) var(--sup-inset); display: flex; flex-direction: column; justify-content: flex-end; gap: var(--space-5); }
.sup-log:empty { padding: 0; }
.sup-log-op { min-height: 80px; max-height: 240px; border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 10px; }
.sup-empty { font-size: 13px; line-height: 1.5; color: var(--ink-muted); }
/* A message is a column — the bubble, then its « who · when » line — on the
   end side for the reader's own, the start side for the other. */
.sup-msg { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.sup-msg.is-mine { align-items: flex-end; }
.sup-bubble {
  max-width: 85%; padding: 7px 10px; font-size: 13.5px; line-height: 1.4;
  border-radius: var(--radius); white-space: pre-wrap; word-break: break-word;
  background: var(--surface-inset); color: var(--ink); border: 1px solid var(--border);
}
.sup-msg.is-mine .sup-bubble { background: var(--brand); color: var(--brand-ink); border-color: transparent; }
/* L'assistant a SA bulle : le même registre que celle de Nota, avec un filet
   de marque à gauche qui dit d'un coup d'œil que c'est la machine qui a
   répondu — jamais un déguisement en personne. */
.sup-msg[data-de='assistant'] .sup-bubble { border-left: 2px solid var(--brand); }
/* « L'assistant écrit… » : trois points qui respirent, du côté de l'assistant.
   La bulle garde la hauteur d'une ligne pour que le fil ne saute pas quand la
   vraie réponse la remplace. */
.sup-bubble-typing { display: inline-flex; align-items: center; gap: 4px; min-height: 20px; }
.sup-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ink-muted); opacity: 0.35; animation: sup-pulse 1.2s ease-in-out infinite; }
.sup-dot:nth-child(2) { animation-delay: 0.15s; }
.sup-dot:nth-child(3) { animation-delay: 0.3s; }
@keyframes sup-pulse { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 0.9; } }
@media (prefers-reduced-motion: reduce) { .sup-dot { animation: none; opacity: 0.5; } }
/* Les questions d'amorce — des cibles, pas de la décoration : elles occupent
   le vide qu'ouvrait le journal et disent ce que l'assistant sait traiter. */
.sup-label {
  margin: 0; padding: var(--space-6) var(--sup-inset) 0;
  font-size: var(--type-eyebrow); letter-spacing: var(--type-eyebrow-ls); text-transform: uppercase;
  font-weight: 700; color: var(--ink-muted);
}
.sup-suggest { display: flex; flex-wrap: wrap; gap: var(--space-3); padding: var(--space-4) var(--sup-inset) 0; }
.sup-chip {
  appearance: none; cursor: pointer; font: inherit; font-size: 12.5px; line-height: 1.3;
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface-inset); color: var(--ink);
}
.sup-chip:hover { border-color: var(--border-strong); background: var(--surface); }
.sup-chip:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 1px; }
/* L'escalade se lit sous le fil qu'elle concerne, à l'encre : c'est la ligne
   qui dit au visiteur ce qui se passe maintenant. Une note de situation, sans
   filet — même registre que « conversation terminée ». */
.sup-escalade { margin: 0; padding: 0 var(--sup-inset) var(--space-6); font-size: 12.5px; line-height: 1.45; color: var(--ink); }
.sup-meta { display: flex; gap: var(--space-3); font-size: 11.5px; color: var(--ink-muted); padding: 0 2px; }
.sup-who { font-weight: 700; }
/* Un bloc, pas une liste de fiches : deux messages de suite du même côté se
   serrent, et la signature « qui · quand » ne se pose qu'au PIED du bloc —
   une conversation de six lignes cesse d'être six fois « Vous · 15:33 ». */
.sup-log .sup-msg.is-cont { margin-top: calc(var(--space-5) * -1 + var(--space-2)); }
.sup-msg.is-mid .sup-meta { display: none; }
.sup-ended { margin: 0; padding: 0 var(--sup-inset) var(--space-6); font-size: 12.5px; line-height: 1.45; color: var(--ink-muted); }
.sup-form { padding: var(--space-6) var(--sup-inset) var(--space-3); border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: var(--space-4); }
/* Le composeur est UN champ, pas deux boîtes côte à côte : la rangée porte le
   cadre et l'anneau de focus, le champ et le bouton vivent dedans. */
.sup-row {
  display: flex; align-items: flex-end; gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-5);
  border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--surface);
}
.sup-row:focus-within { border-color: var(--ring); box-shadow: 0 0 0 3px var(--focus-ring); }
/* Grows with the text (field-sizing where the browser has it, app.js
   otherwise), capped so the log keeps its room. */
.sup-text {
  flex: 1; resize: none; min-height: 26px; max-height: 160px; overflow-y: auto; field-sizing: content;
  min-width: 0; padding: var(--space-3) 0; border: 0; border-radius: 0; background: transparent;
}
.sup-text:focus { border: 0; outline: none; box-shadow: none; }
.sup-send { flex: none; padding: var(--space-4) var(--space-6); font-size: 13px; }
.sup-count { margin: 0; min-height: 1.5em; font-size: 12px; color: var(--ink-muted); text-align: right; font-variant-numeric: tabular-nums; }
.sup-error { margin: 0; font-size: 13px; color: var(--danger); }
/* La porte du courriel : un lien discret tant qu'il ne sert pas. Elle ne prend
   qu'une ligne, et le champ n'apparaît qu'une fois ouverte. */
.sup-mail-open {
  appearance: none; border: 0; background: transparent; padding: 0; font: inherit;
  font-size: 12px; color: var(--ink-muted); text-align: left; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px; align-self: flex-start;
}
.sup-mail-open:hover { color: var(--ink); }
.sup-mail-open:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 2px; border-radius: var(--radius-xs); }
.sup-mail-open[aria-expanded='true'] { display: none; }
/* The two secondary doors share one row under the caution; they wrap rather
   than shrink when the French runs long. */
.sup-actions { display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--space-6); }
/* The optional courriel, one line under the composer: label · field, help beneath. */
.sup-courriel-row { display: flex; flex-direction: column; gap: var(--space-2); }
.sup-courriel-lbl { font-size: 12px; font-weight: 600; color: var(--ink-muted); }
.sup-opt { font-weight: 500; }
.sup-courriel { width: 100%; min-width: 0; padding: var(--space-4) var(--space-5); font-size: 13px; }
.sup-courriel-help { margin: 0; font-size: 12px; }
.sup-reply-courriel { margin: 0 0 8px; min-height: 1.5em; }
#chat-reply-error, #chat-reply-sent { min-height: 1.5em; }
#chat-reply-dialog textarea { width: 100%; resize: vertical; }
@media (max-width: 767.98px) {
  /* The calendar owns its corner on phones (ADR 0022). The panel opens from
     the drawer's « Messagerie » row and from an emailed #messagerie link. */
  .sup-wrap { --sup-edge: var(--space-5); --sup-bottom: var(--space-5); }
  .sup-panel { width: calc(100vw - var(--sup-edge) * 2); }
}

/* --- Les documents de la conversation (ADR 0032) --------------------------
   Sous le fil, au même endroit des deux côtés : ce que l'un envoie, l'autre le
   lit là. Aucune couleur d'accent — un document n'est ni une alerte ni une
   récompense, c'est une pièce du dossier. L'état de l'envoi se dit en mots
   (aria-live), parce qu'une barre de progression sans phrase ne dit pas ce qui
   se passe quand elle s'arrête. */
.chat-docs { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.chat-doc-list { list-style: none; margin: 0 0 8px; padding: 0; display: grid; gap: 4px; }
.chat-doc { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: 13px; }
.chat-doc-open {
  font: inherit; padding: 0; border: 0; background: none; color: var(--brand);
  text-decoration: underline; cursor: pointer; text-align: left;
}
.chat-doc-open:hover { text-decoration: none; }
.chat-doc-meta { color: var(--ink-muted); font-size: 11.5px; }
.chat-doc-vide, .chat-doc-ferme { color: var(--ink-muted); font-size: 12.5px; }
/* The exchange is closed on this deployment (documentsActifs: false): one
   sentence where the upload controls would be, in the empty state's register. */
.chat-doc-ferme { margin: 0; }
.chat-doc-input { display: none; }
.chat-doc-etat { margin: 6px 0 0; font-size: 12px; color: var(--ink-muted); min-height: 1em; }
.chat-doc-etat[data-etat="erreur"] { color: var(--danger); }
/* The act's checklist inside the document block (ADR 0032 × audit 2.1): one
   row per expected piece, its own Joindre, marked sent once confirmed. */
.chat-doc-checklist { list-style: none; margin: 0 0 8px; padding: 0; display: grid; gap: 6px; }
.chat-doc-item { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; }
.chat-doc-item-name { font-weight: 600; }
.chat-doc-item[data-sent='true'] .chat-doc-item-name::before { content: '✓ '; color: var(--ok); }
.chat-doc-item .chat-doc-etat { margin: 0; flex-basis: 100%; }
.chat-doc-free { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; margin-top: 4px; }
.chat-doc-free-lbl { color: var(--ink-muted); }
/* Back from Stripe (audit 2.2): the success card the redirect skipped. */
.checkout-success { margin-top: 10px; padding: 12px 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.checkout-success-h { font-weight: 700; font-size: 14px; display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; }
.checkout-success-h .money { color: var(--brand); }
.checkout-success .dossier-next { margin-top: 10px; }
.checkout-success .actions-inline { margin-top: 10px; }

/* =============================================================================
   Print. Paper is white: the light canvas tokens are restated for the dark
   theme and the system-dark media block (both outrank :root), the fixed
   chrome goes — header, drawer, bubbles, backdrop dice, footer nav, gate,
   dialogs — and the active pane keeps its content. The demo banner STAYS: a
   printed fictional carnet must still say so. Only canvas tokens are
   restated; the brand ramp is shared, everything else follows.
   ========================================================================== */
@media print {
  :root, :root[data-theme='dark'], :root:not([data-theme='light']) {
    color-scheme: light;
    --brand: var(--nota-blue-700); --brand-bright: var(--nota-blue-500); --brand-ink: var(--paper); --brand-hover: var(--nota-blue-800);
    --brand-on-surface: var(--brand); /* sur papier, l'état reprend la marque elle-même */
    --ok-on-surface: var(--nota-blue-600); /* paper's --ok, whatever the screen theme */
    --brand-word-ink: var(--ink);
    --bg: var(--paper); --surface: var(--paper); --surface-hover: var(--canvas-surface-hover);
    --surface-inset: var(--canvas-bg); /* on white paper the recess is the lightest grey; it was --nota-blue-50, a blue as light — muted ink keeps its contrast */
    --brand-tint-pour: var(--canvas-wash); --brand-pour: var(--canvas-wash); /* paper is grey-washed, never blue, whatever the screen theme */
    --ink: var(--nota-blue-950); --ink-muted: var(--canvas-ink-muted); --border: var(--canvas-border); --border-strong: var(--canvas-border-strong); --ring: var(--nota-blue-600);
    --subtitle-ink: var(--canvas-subtitle-ink);
    --shadow-sm: none; --shadow: none; --shadow-lg: none; --shadow-brand: none;
  }
  .site-header, .site-footer nav, .speed-dial, .sup-wrap, .mnav, .mnav-scrim,
  .toast, .skip, .nav-burger, dialog { display: none !important; }
  body { background: var(--bg); color: var(--ink); }
  * { box-shadow: none !important; text-shadow: none !important; -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
  .wrap { max-width: none; padding: 0; }
  .tab-pane[hidden] { display: none !important; }
  /* Paper does not scroll: every scroll entry (« ENTRÉES AU DÉFILEMENT ») is
     printed in its final state — the block in place, its lines drawn. */
  .has-reveal .reveal { opacity: 1; transform: none; }
  .has-reveal .reveal:not(.is-in) .accent-line::before, .has-reveal .reveal.accent-line:not(.is-in)::before { transform: none; }
  .has-reveal .reveal:not(.is-in) .rail-draw::after, .has-reveal .reveal.rail-draw:not(.is-in)::after { clip-path: inset(0); }
  .has-reveal .reveal:not(.is-in) .stroke-draw :is(path, rect, circle, line) { stroke-dashoffset: 0; }
}

/* ADR 0041 — l'acte annulé par le client, en attente de la décision du notaire
   sur l'indemnité : une carte à part, sans client ni fil, avec sa réclamation. */
.pill-annulee { background: transparent; color: var(--ink-muted); border: 1px solid var(--border); }
.nc-card.is-annulee { border-style: dashed; }
.nc-indemnite { margin-top: 10px; padding-top: 8px; border-top: 1px dashed var(--border); }
.nc-indemnite-form { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.nc-indemnite-amt { max-width: 160px; }
.nc-indemnite-just { resize: vertical; min-height: 56px; }
.nc-indemnite-err { color: var(--danger, var(--ink)); }
.devis-annulation { margin: 6px 0 0; }

.coverage-note { grid-column: 1 / -1; padding: 1rem; border: 1px solid currentColor; border-radius: var(--radius); max-width: 100%; }
.coverage-note .help { margin: 0 0 .75rem; }

/* Email preferences use the same surface and controls as the profile. */
#email-preferences-dialog {
  width: min(640px, calc(100vw - 32px));
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
}
#email-preferences-dialog .check-row { display: flex; align-items: center; gap: 12px; min-height: 44px; }
#email-preferences-dialog input { width: 18px; height: 18px; accent-color: var(--brand); }
#email-preferences-dialog button { margin-block: 12px; }

/* Final booking step: keep the contact form readable on wide screens. */
.day-book[data-at='4'] {
  width: 100%; max-width: 1120px; margin-inline: auto; box-sizing: border-box;
  gap: 20px;
}
.day-book[data-at='4'] #o-where-step { padding: 20px; }
.day-book[data-at='4'] #prefix-row { margin-bottom: 0; }
.day-book[data-at='4'] #prefix-row > .coverage-note { grid-column: 1 / -1; }
.day-book[data-at='4'] .book-identity {
  margin-top: 0; padding-top: 24px;
  grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 32px;
}
.day-book[data-at='4'] .book-identity .form-row { min-width: 0; }
/* The contact FIELDS fill their column; the SMS consent is a checkbox and keeps
   its own box (.check-line input). Stretched with the fields, it became a bar
   as wide as the sheet, pushed its sentence past the sheet's edge and left a
   blank band under the phone — the film's last stop (2026-09-24). */
.day-book[data-at='4'] #identity-rows input:not([type='checkbox']) { width: 100%; max-width: none; box-sizing: border-box; }
/* Two columns and no half row left empty: the referral code sits beside the
   phone, and the SMS consent — the phone's own option (ADR 0051) — right under
   the phone. Name and courriel hold the first row; the signed-in summary hides
   every field row (renderOfferIdentity), so it never competes for these lines. */
.day-book[data-at='4'] .book-identity > .book-referral { grid-column: 2; grid-row: 2 / span 2; }
.day-book[data-at='4'] .book-identity .help { font-size: 14px; line-height: 1.5; }
.day-book[data-at='4'] .offer-hint { margin: 0; }
.day-book[data-at='4'] .consent-line { margin: 0; font-size: var(--type-h4); line-height: 1.5; }
.day-book[data-at='4'] .book-nav { margin-top: 4px; }
@media (max-width: 760px) {
  .day-book[data-at='4'] .book-identity { grid-template-columns: minmax(0, 1fr); }
  .day-book[data-at='4'] .book-identity > .book-referral { grid-column: auto; grid-row: auto; }
  .day-book[data-at='4'] #o-where-step { padding: 16px; }
}
/* Inactive validation messages must not reserve a flex row or its gap. */
.day-book[data-at] > [data-screen][hidden] { display: none !important; }

/* One tour surface for both account roles; native dialog contains keyboard focus.
   The card sits beside the section it describes (app.js sets data-place and the
   --tour-anchor-* geometry); CSS owns the padding, the gap and the fallbacks. */
.account-tour, .account-tour-spot {
  --tour-pad: var(--space-4);     /* breathing room the cut-out adds around its target */
  --tour-gap: var(--space-6);     /* between the cut-out and the card */
  --tour-edge: var(--space-8);    /* the card never touches the viewport edge */
}
.account-tour {
  /* Placeholders: app.js writes the measured target geometry inline. */
  --tour-anchor-top: 0px; --tour-anchor-bottom: 0px; --tour-anchor-left: 0px;
  --tour-card-w: min(460px, calc(100vw - 2 * var(--tour-edge)));
  position: fixed; inset: auto var(--tour-edge) var(--tour-edge) auto; margin: 0;
  width: var(--tour-card-w); max-height: min(50dvh, 420px);
  box-sizing: border-box; overflow: auto; padding: 24px;
  color: var(--ink); background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
}
.account-tour[data-place="below"], .account-tour[data-place="above"] {
  right: auto;
  left: max(var(--tour-edge), min(calc(var(--tour-anchor-left) - var(--tour-pad)), calc(100vw - var(--tour-card-w) - var(--tour-edge))));
}
.account-tour[data-place="below"] { top: calc(var(--tour-anchor-top) + var(--tour-pad) + var(--tour-gap)); bottom: auto; }
.account-tour[data-place="above"] { bottom: calc(var(--tour-anchor-bottom) + var(--tour-pad) + var(--tour-gap)); top: auto; }
.account-tour[open] { animation: account-tour-in var(--motion-fast) var(--ease-ui); }
/* The tour guides, it does not seal the page: no page-wide blur or scrim. The
   veil is the cut-out's spread shadow, so the target stays sharp. */
.account-tour::backdrop { background: transparent; -webkit-backdrop-filter: none; backdrop-filter: none; }
.account-tour-spot {
  position: fixed; z-index: 64; pointer-events: none; box-sizing: content-box;
  margin: calc(-1 * var(--tour-pad)); padding: var(--tour-pad);
  border-radius: var(--radius); outline: 2px solid var(--brand); outline-offset: 0;
  box-shadow: 0 0 0 100vmax var(--backdrop-soft);
  transition: top var(--motion-fast) var(--ease-ui), left var(--motion-fast) var(--ease-ui),
    width var(--motion-fast) var(--ease-ui), height var(--motion-fast) var(--ease-ui);
}
.account-tour-spot[hidden] { display: none; }
.account-tour h2 { margin: 10px 0; }
/* Focus lands on the title so a screen reader reads the step; it is not a
   control, so it carries no focus frame (the owner's capture showed one). */
.account-tour h2:focus { outline: none; }
.account-tour p { margin: 0 0 20px; line-height: 1.6; }
.account-tour-progress { color: var(--brand-on-surface); font-size: 12px; font-weight: 700; }
.account-tour-dots { display: flex; gap: var(--space-3); margin-top: var(--space-4); }
.account-tour-dot { width: var(--space-6); height: var(--space-2); border-radius: var(--radius-sm); background: var(--border-strong); transition: background var(--motion-fast) var(--ease-ui); }
.account-tour-dot.is-on { background: var(--brand); }
.account-tour-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
.account-tour-actions .btn { min-height: 44px; }
.account-tour-actions .btn:first-child { margin-right: auto; }
.account-tour-target { scroll-margin-top: 110px; }
.account-tour-replay { justify-self: start; margin-bottom: 16px; }
@keyframes account-tour-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
@media (max-width: 560px) { .account-tour, .account-tour[data-place] { inset: auto var(--tour-edge) var(--tour-edge); padding: 18px; } }
@media (prefers-reduced-motion: reduce) { .account-tour[open] { animation: none; } .account-tour-spot, .account-tour-dot { transition: none; } }
.account-tour h2 { animation: account-tour-in var(--motion-ui) var(--ease-ui); }
@media (prefers-reduced-motion: reduce) { .account-tour h2 { animation: none; } }

/* Shared help affordance: visible hit area, readable copy, both themes. */
.itip { vertical-align: middle; margin-left: 8px; }
.itip-btn {
  width: 32px; height: 32px; min-height: 32px; padding: 0;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface-inset); color: var(--brand);
}
.itip-btn svg { width: 18px; height: 18px; }
.itip-btn::after { inset: 0; }
.itip-btn:hover, .itip[data-open='true'] .itip-btn {
  color: var(--action-ink); border-color: currentColor; background: var(--action-tint);
}
.itip[data-tone='warn'] .itip-btn { color: var(--warn); }
.itip-pop {
  box-sizing: border-box; width: 360px; max-width: calc(100vw - 32px);
  padding: var(--space-8); font-size: 14px; line-height: 1.6; font-weight: 400;
  border-radius: var(--radius); overflow-y: auto; overflow-wrap: anywhere;
  pointer-events: auto;
}
.itip[data-dismissed='true'] > .itip-pop { display: none; }
/* La flèche se ferme avec son panneau — même règle, même rang : Escape rend
   le focus au « i », et le pseudo d’État visible ne doit pas lui survivre. */
.itip[data-dismissed='true']::after { display: none; }
@media (pointer: coarse) {
  .itip-btn { width: 44px; height: 44px; min-height: 44px; }
}

/* Profile fields share an input baseline; supporting text sits below it. */
.profil-fields { align-items: start; gap: 20px; }
.profil-fields .form-row { min-width: 0; }
/* The row's own help only: a sentence waiting in a label's « i » takes the
   panel's voice, not the field's. */
.profil-fields .form-row > .help { margin: 6px 0 0; font-size: 13px; line-height: 1.5; }
/* A label that carries its « i » (labelTip) sits on the « i »'s line; every
   label of the grid takes that line's height (the .itip-btn's 32 px, 44 px on
   a coarse pointer), so two fields side by side start their inputs level. */
.profil-fields .form-row > .tip-line { margin-bottom: var(--space-3); }
.profil-fields .form-row > .tip-line > label.lbl { margin-bottom: 0; }
.profil-fields .form-row > :is(label.lbl, .tip-line) { min-height: var(--space-16); }
@media (pointer: coarse) { .profil-fields .form-row > :is(label.lbl, .tip-line) { min-height: var(--control-height); } }
@media (max-width: 860px) {
  /* An offer summary and its detail row form one card, never adjacent cards. */
  .my-offers tbody { grid-template-columns: minmax(0, 1fr); }
  .my-offers .c-acte, .my-offers .c-date, .my-offers .c-montant, .my-offers .c-statut {
    width: auto; min-width: 0;
  }
  .my-offers .c-statut { justify-self: stretch; }
  .my-offers .my-offer-status { display: inline-block; max-width: 100%; box-sizing: border-box; }
}

/* Shared questionnaire cards, including the dossier and optional details. */
#day-dialog:has(.day-book[data-at='2']) { width: min(96vw, 1040px); max-width: 1040px; }
.day-book[data-at='2'] { padding: 20px; gap: 16px; }
.day-book[data-at='2'] .book-rail { margin-bottom: 4px; }
.o-criteria .crit-sec { padding: 16px; gap: 16px; background: var(--surface); }
.o-criteria .crit-sec-h {
  margin: -16px -16px 0; padding: 14px 16px;
  gap: 8px 12px;
}
.o-criteria .crit-sec-grid { gap: 12px; }
.o-criteria .crit-sec-grid > .crit-row {
  padding: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-inset); min-width: 0;
}
.o-criteria .crit-head { margin-bottom: 8px; }
/* Le « $ » entre DANS le champ (2026-09-12) : posé à côté, il repoussait le
   cadre du montant de dix pixels vers l'intérieur — deux champs l'un sous
   l'autre, « Montant du nouveau prêt » et « Prêteur hypothécaire », ne
   finissaient donc pas sur la même verticale. Le champ va jusqu'au rail, comme
   la liste, et l'unité se lit dans sa marge droite. */
.o-criteria .crit-unit-wrap { display: flex; position: relative; width: 100%; }
.o-criteria .crit-unit-wrap input[type='number'] { width: 100%; max-width: none; min-width: 0; padding-right: 28px; }
.o-criteria .crit-unit-wrap > .crit-unit {
  position: absolute; top: 0; right: 12px; height: 100%;
  display: inline-flex; align-items: center; pointer-events: none;
}
.o-criteria .crit-row .nselect { width: 100%; max-width: none; }
.o-criteria .crit-sec-grid > .crit-row .seg-btn { flex: 1 1 0; max-width: none; min-height: 44px; }
.o-criteria .crit-row .seg.seg-stack .seg-btn { flex: auto; }
#day-dialog .o-criteria .crit-row .seg.seg-stack .seg-btn { flex: 0 1 auto; }
.o-criteria .crit-more { margin-top: 0; }
.o-criteria .crit-sec[data-fold='true'] { padding-bottom: 0; gap: 0; }
@media (max-width: 600px) {
  #day-dialog:has(.day-book[data-at='2']) { width: 100%; max-width: 100%; }
  .day-book[data-at='2'] { padding: 14px; }
  .dossier-pricing .o-criteria .crit-sec-grid { grid-template-columns: minmax(0, 1fr); }
  .dossier-pricing .o-criteria .crit-sec-grid > .crit-row { grid-column: 1 / -1; grid-row: auto; }
}
/* The form's usable width, rather than the window, decides whether its
   compact loan fields can share a row. Long answer choices use the full
   section so their price rail cannot squeeze labels into vertical letters. */
@container book (width >= 520px) {
  #o-criteria > .crit-sec[data-groupe='pret'] > .crit-sec-grid > .crit-row[data-crit='valeur_pret'] { grid-column: 1; grid-row: 1; }
  #o-criteria > .crit-sec[data-groupe='pret'] > .crit-sec-grid > .crit-row[data-crit='preteur'] { grid-column: 2; grid-row: 1; }
  #o-criteria > .crit-sec[data-groupe='pret'] > .crit-sec-grid > .crit-row[data-crit='contexte'] { grid-column: 1 / -1; grid-row: 2; }
  #o-criteria > .crit-sec[data-groupe='pret'] > .crit-sec-grid > .crit-row[data-crit='approbation_bancaire'] { grid-column: 1 / -1; grid-row: 3; }
}
@container book (width < 520px) {
  #o-criteria .crit-sec-grid { grid-template-columns: minmax(0, 1fr); }
  #o-criteria .crit-sec-grid > .crit-row { grid-column: 1 / -1; grid-row: auto; }
}
.o-criteria .crit-row .nselect { min-width: 100%; align-self: stretch; box-sizing: border-box; }

/* Additional text belongs below its choice, with the same usable width. */
.o-criteria .crit-row:has(> .crit-other) { flex-direction: column; flex-wrap: nowrap; gap: 8px; }
.o-criteria .crit-row:has(> .crit-other) > :is(.crit-head, .crit-effet, .nselect, .help, .crit-other) {
  flex: none; width: 100%; min-width: 0; box-sizing: border-box;
}
.o-criteria .crit-other input[type='text'] { width: 100%; max-width: none; box-sizing: border-box; }
/* The optional rows share the same question rails as their section's required
   rows. Keep the vertical separation, but do not add a second horizontal
   inset inside the disclosure. */
.o-criteria .crit-sec > .crit-more .crit-sec-grid { padding: var(--space-6) 0 0; }
/* UN SEUL RAIL POUR TOUTE LA SECTION (2026-09-12, retour du propriétaire :
   « a lot of fields in the form that are not aligned »). Le volet des
   précisions portait son propre cadre AUTOUR des rangs : sa bordure d'un pixel
   poussait chaque question facultative d'un pixel vers la droite et lui coupait
   un pixel à droite — deux questions de la même section ne commençaient donc
   pas sur la même verticale, et les deux colonnes de la grille intérieure
   tombaient à côté de celles du dessus. Le cadre passe sur la BARRE, qui
   devient une carte de la même famille que les questions ; le volet, lui, ne
   pèse plus rien dans la mise en page. */
.o-criteria .crit-sec > .crit-more { border: 0; border-radius: 0; background: none; }
.o-criteria .crit-more > summary {
  padding: 12px; min-height: 44px; box-sizing: border-box;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-inset);
}
.o-criteria .crit-more > summary:hover { border-color: var(--border-strong); }
.o-criteria .crit-sec-grid > .crit-row { margin: 0; }

/* Keep every questionnaire track inside its card. Without an explicit
   shrinkable width, a nested flex track can preserve its intrinsic content
   width at browser zoom and pull the right-hand choices away from the shared
   rail. */
.o-criteria .crit-sec-grid,
.o-criteria .crit-row,
.o-criteria .crit-dep,
.o-criteria .crit-dep .seg {
  width: 100%;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

/* Postal lookup: a compact input followed by a clear geographic result. */
#o-where-step .coverage-note { background: var(--surface-inset); border-color: var(--border); padding: 16px; }
#o-where-step .coverage-note[data-state='none'] { border-left: 3px solid var(--warn); }
#o-where-step .coverage-note[data-state='covered'] { border-left: 3px solid var(--brand); }
.coverage-summary { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.coverage-count { font-size: var(--type-h2); line-height: 1; color: var(--brand); font-variant-numeric: tabular-nums; }
.coverage-caption { display: grid; gap: 4px; min-width: 0; }
.coverage-caption > span { font-size: 13px; color: var(--ink-muted); }
/* Coverage without a dead end (2026-10-13): the déplacements that do have a
   notary, one click each — or the two ways forward when none has one. */
.coverage-alternatives, .coverage-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-top: var(--space-6); }
.coverage-note .coverage-next, .coverage-note .coverage-anyway { margin: 0 0 var(--space-4); }
.coverage-alt { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2) var(--space-4); text-align: left; white-space: normal; }
.coverage-alt-label { font-weight: 700; }
.coverage-alt-meta { color: var(--ink-muted); font-variant-numeric: tabular-nums; }
#o-where-step #prefix-row { gap: 10px 16px; }

/* Real Québec basemap. Leaflet owns the geographic geometry; tokens paint
   its paths. The panel sits below the picture so every pin stays visible.
   Canvas isolation contains Leaflet’s pane stack beneath the panel and the
   booking flow’s own controls. */
.qc-map-wrap { grid-column: 1 / -1; }
.qc-map {
  --qc-map-height: calc(var(--space-16) * 10);
  --qc-map-line: calc(var(--space-1) / 2);
  --qc-map-wash-opacity: .1;
  --qc-map-precision-opacity: .18;
  position: relative; isolation: isolate; width: 100%; margin: var(--space-6) auto 0;
  border-radius: var(--radius-lg); overflow: hidden; background: var(--surface-inset);
  border: var(--qc-map-line) solid var(--border);
}
.qc-map__canvas { position: relative; z-index: 0; width: 100%; height: var(--qc-map-height); background: var(--surface-inset); font-family: var(--font-sans); }
.qc-map.is-unavailable .qc-map__canvas { display: none; }
.qc-map__unavailable { margin: 0; padding: var(--space-4) var(--space-6); color: var(--ink); background: var(--surface); font-size: var(--type-body); line-height: var(--type-body-lh); overflow-wrap: anywhere; }
.qc-map__ring { stroke: var(--brand); stroke-width: var(--space-1); fill: var(--brand); fill-opacity: var(--qc-map-wash-opacity); }
.qc-map__precision { stroke: var(--brand-bright); stroke-width: var(--qc-map-line); fill: var(--brand); fill-opacity: var(--qc-map-precision-opacity); }
.qc-map__guess { stroke: var(--brand-bright); stroke-width: var(--space-1); stroke-dasharray: var(--space-3) var(--space-2); fill: var(--brand); fill-opacity: var(--qc-map-wash-opacity); }
.qc-map__here { stroke: var(--paper); stroke-width: var(--space-1); fill: var(--brand-hover); fill-opacity: 1; }
.qc-map__pin { stroke: var(--paper); stroke-width: var(--space-1); fill: var(--warn); fill-opacity: 1; }
.qc-map .qc-map__tooltip { box-sizing: border-box; width: max-content; white-space: normal; overflow-wrap: anywhere; padding: var(--space-3) var(--space-4); background: var(--surface); color: var(--ink); border-color: var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow-sm); font-family: var(--font-sans); font-size: var(--type-h4); }
.qc-map .qc-map__tooltip::before { border-top-color: var(--border); }
.qc-map__panel { position: relative; z-index: 1; padding: var(--space-6); background: var(--surface); border-top: var(--qc-map-line) solid var(--border); font-size: var(--type-h4); transition: opacity var(--motion-ui) var(--ease-ui), transform var(--motion-ui) var(--ease-ui); opacity: 0; transform: translateY(calc(var(--space-2) * -1)); pointer-events: none; }
.qc-map__panel.is-open { opacity: 1; transform: translateY(0); pointer-events: auto; }
.qc-map__panel-header { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-4); }
.qc-map__panel-prefix { font-weight: 700; font-size: var(--type-body); color: var(--brand); font-variant-numeric: tabular-nums; }
.qc-map__panel-region { color: var(--ink-muted); font-size: var(--type-h4); text-transform: capitalize; }
.qc-map__panel-body { display: grid; gap: var(--space-3); }
.qc-map__stat { display: flex; align-items: center; gap: var(--space-4); }
.qc-map__stat-count { font-size: var(--type-h2); font-weight: 700; color: var(--brand); line-height: 1; font-variant-numeric: tabular-nums; }
.qc-map__stat-label { color: var(--ink-muted); font-size: var(--type-h4); }
.qc-map__note { color: var(--ink-muted); font-size: var(--type-h4); }
.qc-map__visible { color: var(--ink-muted); font-size: var(--type-h4); }
.qc-map__price { color: var(--ink); font-size: var(--type-h4); padding-top: var(--space-2); border-top: var(--qc-map-line) solid var(--border); margin-top: var(--space-2); }
/* Plancher de lisibilité (brief short-nota-30 1b, 2026-09-25) : « Changer le
   lieu de signature » est un VRAI bouton de la feuille — 44 px de haut, un
   libellé à la taille du corps, le rayon du produit — plus une languette. */
/* Aucun bouton n’est un aplat (propriétaire, 2026-09-12) : l’orange de
   l’élargissement se lit au contour, à l’encre et à la graisse ; le voile ne
   vient qu’au survol. --warn sur la surface du panneau tient 4,9:1 en clair,
   7:1 en sombre. */
.qc-map__cta { margin-top: var(--space-4); padding: var(--space-4) var(--space-6); min-height: 44px; background: transparent; color: var(--warn); border: 1px solid var(--warn); border-radius: var(--radius); font-size: var(--type-body); font-weight: 700; cursor: pointer; width: 100%; text-align: center; }
.qc-map__cta:hover { text-decoration: underline; background: color-mix(in srgb, var(--warn) 10%, transparent); }
.qc-map__credit { margin: var(--space-2) 0 0; color: var(--ink-muted); font-size: var(--type-h4); text-align: right; }
.qc-map__credit a { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) {
  .qc-map__panel { transition: none; }
}

/* PLANCHER DE LISIBILITÉ DE LA FEUILLE (brief short-nota-30 1b, 2026-09-25) —
   le texte secondaire du dialogue de réservation se lit à hauteur moyenne
   (≥ 14 px, règle R1 de la spec du 2026-09-24). Les classes ci-dessous sont
   partagées avec le reste du site (carnet, profil, partenaires) : elles ne
   montent pas à la source, elles montent DANS la feuille, par ancrage
   #day-dialog. Spécificité : l’ancre id gagne sur toute règle de base ou
   contextuelle antérieure ; le texte du § 4 de l’écran (`[data-at='4']`) est
   repris à l’identique. Garde : apps/web/test/modal-lisibilite.test.mjs ;
   règle : docs/brand-guidelines.md § 8.5.3. */
#day-dialog .pill,
#day-dialog .help,
#day-dialog .hint,
#day-dialog .chip,
#day-dialog .btn-sm,
#day-dialog .check-line,
#day-dialog .prefix-preview,
#day-dialog .errors li,
#day-dialog .seg-btn,
#day-dialog .seg-btn .crit-add,
#day-dialog .disclosure > summary,
#day-dialog .disclosure-body p { font-size: var(--type-h4); }

/* Equal-weight, nonblocking analytics choice. Token-driven so it follows
   both themes instead of forcing a white card onto a dark canvas. */
.analytics-consent { position: fixed; inset: auto 16px 16px; z-index: 10000; max-width: 680px; margin: auto; padding: 18px; background: var(--surface); color: var(--ink); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); }
.analytics-consent[hidden] { display: none; }
.analytics-consent p { margin: 0 0 12px; font-size: 14px; line-height: 1.5; }
.analytics-consent button, #analytics-preferences:not(.footer-link) { font: inherit; cursor: pointer; padding: 8px 12px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); margin-right: 8px; }
.analytics-consent button:hover, #analytics-preferences:not(.footer-link):hover { background: var(--action-tint); }

/* Help discovery remains available without crowding the conversation: one
   summary line with the register's chevron (see .disclosure), then a
   searchable list, one question per row — a topic is a sentence, not a tag. */
.sup-topics { padding: var(--space-2) var(--sup-inset) 0; font-size: 12.5px; flex-shrink: 0; }
.sup-topics > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: var(--space-4); min-height: 44px;
  color: var(--ink-muted); font-weight: 600;
}
.sup-topics > summary:hover { color: var(--ink); }
.sup-topics > summary:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 2px; }
.sup-topic-list {
  display: flex; flex-direction: column; max-height: 200px; overflow-y: auto;
  margin: var(--space-4) 0; border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.sup-topic-list .sup-chip {
  width: 100%; text-align: left; background: transparent;
  border: 0; border-bottom: 1px solid var(--border); border-radius: 0;
}
.sup-topic-list .sup-chip:last-child { border-bottom: 0; }
.sup-topic-list .sup-chip:hover { background: var(--surface-inset); }
/* The caution sits in the composer, under the field it describes. */
.sup-privacy { margin: 0; font-size: 12px; line-height: 1.4; color: var(--ink-muted); }

/* Search and delivery recovery stay inside the dependency-free messenger. */
.sup-topic-search { width: 100%; margin-top: 6px; min-height: 36px; }
.sup-topic-empty { margin: 6px 0; color: var(--ink-muted); }

/* --- Booking component consistency --------------------------------------
   One header pattern for every criterion: the label owns the left rail, the
   information affordance owns the top-right rail, and answer effects own a
   separate right rail. The question card itself is a surface, not another
   outlined box inside the outlined section. */
#day-dialog .o-criteria .crit-head,
#day-dialog .o-criteria .crit-text {
  position: relative;
  min-width: 0;
  min-height: 44px;
  padding-right: 52px;
  align-items: center;
}

#day-dialog .o-criteria .crit-head > .itip,
#day-dialog .o-criteria .crit-text > .itip,
#day-dialog .o-criteria .crit-sec-h > .itip {
  position: absolute;
  top: 0;
  right: 0;
  margin: 0;
  transform: none;
}

#day-dialog .o-criteria .crit-sec-h:has(> .itip) {
  position: relative;
  padding-right: 52px;
}

#day-dialog .o-criteria .crit-head > .crit-label,
#day-dialog .o-criteria .crit-text > .crit-label {
  min-width: 0;
  overflow-wrap: anywhere;
  line-height: 1.35;
}

#day-dialog .o-criteria .crit-req {
  max-width: 100%;
  margin-left: 8px;
  white-space: normal;
}

#day-dialog .o-criteria .crit-flag { align-items: center; }
#day-dialog .o-criteria .crit-flag input { margin-top: 0; }
/* One line: the box, the sentence and its price share the row's centre. The
   text column keeps its 44 px target, so its content centres in it too —
   packed at the top, the sentence rode above a box centred on 44 px
   (propriétaire, 2026-09-24 : « this must be a line »). */
#day-dialog .o-criteria .crit-flag > .crit-text { justify-content: center; }

/* Labels and prices are separate flex items. This keeps « Pas encore
   demandée » and « +200 $ » readable at every width. */
#day-dialog .o-criteria .crit-row .seg-btn {
  display: flex;
  flex: 1 1 180px;
  align-items: center;
  justify-content: flex-start;
  min-width: 0;
  min-height: 44px;
  line-height: 1.35;
  text-align: left;
  overflow-wrap: normal;
}

#day-dialog .o-criteria .crit-row .seg-btn .crit-add {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  margin-left: auto;
  padding-left: 12px;
  white-space: nowrap;
}

/* Keep controls readable without adding another visual frame around the
   question: the section is the grouping boundary, the surface is the row. */
#day-dialog .o-criteria .crit-sec-grid > .crit-row {
  border: 0;
  background: var(--surface);
}

#day-dialog .o-criteria .crit-sec {
  /* A confident wash separates each subsection from the white answer
     surfaces without adding another outline around the question: grey on
     light, the brand on the night (the dark strong tint IS this 20 % pour). */
  background: var(--brand-tint-solid-strong);
}

/* Dans le dialogue, une question ne porte pas de cadre : la section est la
   frontière, la surface EST le rang. La barre des précisions suit ce registre
   — même surface, mêmes rails, aucun trait de plus — sinon elle se lisait
   comme un contrôle d'une autre famille, posé un pixel à côté des questions
   qu'elle ouvre. */
#day-dialog .o-criteria .crit-more > summary {
  border: 0;
  background: none;
}
#day-dialog .o-criteria .crit-more > summary:hover { color: var(--ink); }
#day-dialog .o-criteria .crit-more[open] > summary { margin-bottom: 0; }

/* Narrow forms keep each choice on its own row. Wider forms can share the
   space, with the minimum flex basis above preserving readable labels. */
@container book (width < 520px) {
  #day-dialog .o-criteria .crit-row .seg {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-3);
  }

  #day-dialog .o-criteria .crit-row .seg-btn {
    width: 100%;
    max-width: none;
    flex: 1 1 auto;
  }
}

/* Offer sections: the white parent is the deliberate separator. The tinted
   cards remain part of the Nota palette, but their spacing is now a real gap
   instead of blue meeting blue at a border. */
#day-dialog .book-step-offer,
#day-dialog .o-criteria {
  background: var(--surface);
}

#day-dialog .book-step-offer {
  gap: var(--space-12);
}

/* The offer step is intentionally allowed to remain a block in the base
   layout; use a sibling margin as well so the white separator survives that
   cascade and remains visible between each tinted section. */
#day-dialog .book-step-offer > .offer-sec + .offer-sec {
  margin-top: var(--space-12);
}

#day-dialog .o-criteria {
  gap: var(--space-12);
}

#day-dialog .offer-sec,
#day-dialog .o-criteria .crit-sec {
  background: var(--surface-inset);
}

#day-dialog .offer-sec .crit-sec-h,
#day-dialog .o-criteria .crit-sec-h {
  background: var(--surface);
}

/* Section pictograms are brand signals, not controls: no enclosing border or
   tint, including after the section is completed. */
#day-dialog .crit-sec-ic {
  border: 0;
  background: transparent;
  color: var(--brand);
  transition: color var(--motion-fast);
}

.sup-msg[data-delivery='unconfirmed'] .sup-bubble { border: 1px dashed var(--border-strong); }
.sup-delivery { color: var(--ink-muted); font-size: 12px; }
.sup-recover { font: inherit; font-size: 12px; padding: 6px 8px; color: var(--ink); background: var(--surface); border: 1px solid var(--border); cursor: pointer; }
.sup-recover:focus-visible { outline: 2px solid var(--action-ink); outline-offset: 2px; }
#chat-reply-recovery .help { white-space: pre-wrap; overflow-wrap: anywhere; }

/* ==========================================================================
   LA SALLE DE SIGNATURE — ADR 0047

   Plein écran, et volontairement sombre : une cérémonie n'est pas une page du
   site. Ce qui doit rester lisible à distance d'un écran — la chaîne
   d'authentification, l'état des quatre portes, le texte de conduite — l'est
   avant tout le reste. Le bandeau bêta est au-dessus de tout et ne se cache
   jamais.
   ========================================================================== */
.salle { position: fixed; inset: 0; z-index: 11000; display: flex; flex-direction: column; background: var(--nota-blue-950); color: var(--midnight-ink); }
.salle[hidden] { display: none; }
/* Le fond de la page ne défile plus derrière la salle. */
body.salle-ouverte { overflow: hidden; }

/* Le bandeau. Aucune règle ne le masque, aucun rendu ne le touche. */
.salle-beta { margin: 0; padding: 10px 16px; text-align: center; font-size: 13px; line-height: 1.45; background: var(--warn); color: var(--on-warn); }
.salle-beta strong { font-weight: 700; }

.salle-scene { flex: 1; display: grid; grid-template-columns: minmax(0, 1fr) 380px; min-height: 0; }

.salle-videos { position: relative; min-height: 0; background: var(--midnight-matte); }
.salle-video { display: block; width: 100%; height: 100%; object-fit: cover; background: var(--midnight-matte); }
.salle-video-distant { width: 100%; height: 100%; }
/* La vignette locale : assez grande pour se vérifier, assez petite pour ne pas
   voler l'attention de la personne d'en face. */
.salle-video-local { position: absolute; right: 16px; bottom: 16px; width: 22%; min-width: 140px; max-width: 260px; height: auto; aspect-ratio: 4 / 3; border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow-lg); }

.salle-rail { display: flex; flex-direction: column; gap: 14px; padding: 18px; overflow-y: auto; background: var(--nota-blue-900); border-left: 1px solid var(--nota-blue-800); }
.salle-bloc { padding: 14px; background: var(--midnight-veil-soft); border: 1px solid var(--nota-blue-800); border-radius: var(--radius); }
.salle-etape-nom { margin: 0 0 8px; font-size: var(--type-h3); line-height: var(--type-h3-lh); letter-spacing: var(--type-h3-ls); }
.salle-etape-conduite { margin: 0 0 8px; font-size: 15px; line-height: 1.5; }
.salle-etape-constat { margin: 0; font-size: 13px; line-height: 1.45; color: var(--nota-blue-300); }

/* La chaîne d'authentification. C'est la ligne qu'on lit à voix haute : elle a
   droit à la plus grosse typographie de l'écran, en chasse fixe pour que S et 5
   ne se confondent pas. */
.salle-bloc-sas { text-align: center; }
.salle-sas-titre { margin: 0 0 6px; font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--midnight-ink); }
.salle-sas { margin: 0; font-family: var(--font-mono); font-size: var(--type-h2); font-weight: 700; letter-spacing: 0.12em; }
.salle-sas.is-confirme { color: var(--nota-blue-400); }
.salle-sas-aide { margin: 6px 0 0; font-size: 12px; line-height: 1.4; color: var(--nota-blue-300); }

.salle-portes-titre { margin: 0 0 8px; font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--midnight-ink); }
.salle-portes { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.salle-porte { display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: 4px 8px; font-size: 14px; line-height: 1.4; }
.salle-porte-marque { color: var(--warn); font-weight: 700; }
.salle-porte.is-ouverte .salle-porte-marque { color: var(--nota-blue-400); }
.salle-porte-nom { font-weight: 600; }
/* Une porte fermée DIT pourquoi. Le motif s'aligne sous le nom, pas sous la
   marque : c'est une phrase, pas une puce. */
.salle-porte-motif { grid-column: 2; margin: 0; font-size: 13px; line-height: 1.4; color: var(--nota-blue-200); }

/* La décision sur la demande de signature à distance. Un cadre à elle, hors de
   la barre d'actions : la salle se redessine à chaque sondage, et un champ de
   texte reconstruit en cours de frappe n'est plus un champ de texte. */
.salle-decision { display: flex; flex-direction: column; gap: 8px; }
.salle-decision[hidden] { display: none; }
.salle-decision-titre { margin: 0; font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--midnight-ink); }
.salle-decision-etiquette { font-size: 12px; color: var(--nota-blue-300); }
.salle-decision-select, .salle-decision-champ { width: 100%; font: inherit; font-size: 14px; padding: 9px 10px; color: var(--midnight-ink); background: var(--midnight-veil); border: 1px solid var(--nota-blue-700); border-radius: var(--radius-sm); }
.salle-decision-champ { resize: vertical; min-height: 60px; line-height: 1.45; }
.salle-decision-champ::placeholder { color: var(--nota-blue-300); opacity: 1; }
/* Le verdict est un fait établi, pas une alerte : seule la ligne « pas encore
   statué » crie, et elle a déjà sa classe is-alerte. */
.salle-decision-verdict { color: var(--midnight-ink); font-weight: 700; }

.salle-statut { display: flex; flex-direction: column; gap: 8px; }
.salle-statut-ligne { margin: 0; font-size: 13px; line-height: 1.45; color: var(--nota-blue-200); }
.salle-statut-ligne.is-alerte { padding: 10px; background: var(--danger-wash); border: 1px solid var(--danger); border-radius: var(--radius-sm); color: var(--danger-pale); }
.salle-statut-ligne.is-enregistre { color: var(--warn); font-weight: 600; }
.salle-empreinte { display: block; overflow-wrap: anywhere; font-family: ui-monospace, "SFMono-Regular", "Menlo", "Consolas", monospace; font-size: 11px; line-height: 1.5; color: var(--nota-blue-300); }

.salle-refus { display: flex; flex-direction: column; gap: 6px; padding: 10px; background: var(--danger-wash); border: 1px solid var(--danger); border-radius: var(--radius-sm); }
.salle-refus[hidden] { display: none; }
.salle-refus-ligne { margin: 0; font-size: 13px; line-height: 1.45; color: var(--danger-pale); }

.salle-actions { display: flex; flex-direction: column; gap: 8px; }
.salle-btn { font: inherit; font-size: 14px; font-weight: 600; padding: 11px 14px; cursor: pointer; text-align: left; color: var(--midnight-ink); background: var(--midnight-veil); border: 1px solid var(--nota-blue-700); border-radius: var(--radius-sm); }
.salle-btn:hover:not(:disabled) { background: var(--midnight-veil-strong); }
.salle-btn.is-primaire { background: var(--midnight-veil); border-color: var(--nota-blue-400); color: var(--midnight-ink); font-weight: 700; }
.salle-btn.is-primaire:is(:hover, :focus-visible):not(:disabled) { background: var(--midnight-veil); border-color: var(--action-ink); }
.salle-btn.is-discret { background: transparent; font-weight: 500; }
.salle-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.salle-quitter { margin-top: auto; }

/* Sur un téléphone la vidéo passe au-dessus et le rail dessous : le notaire
   conduit rarement depuis un téléphone, le client souvent. */
@media (max-width: 860px) {
  .salle-scene { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(180px, 40vh) minmax(0, 1fr); }
  .salle-rail { border-left: 0; border-top: 1px solid var(--nota-blue-800); }
  .salle-sas { font-size: 28px; }
}

/* L'annonce, dans le carnet et dans l'espace notaire. */
/* Coins carrés comme tout le reste : la marque a tranché contre l'arrondi, et
   une pastille en pilule ferait une exception que rien ne justifie ici. */
.salle-annonce-pastille { display: inline-block; margin: 0 0 8px; padding: 3px 10px; font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--on-warn); background: var(--accent-warm); border-radius: var(--radius-xs); }
.salle-annonce-actions { margin-top: 14px; display: flex; flex-wrap: wrap; gap: 8px; }

/* --- Porte « Signature » : la phase 3 --------------------------------------
   La porte n'a PAS de registre à elle : le rail (.wrap), le héros
   (.landing-hero / .landing-copy), le sur-titre (.eyebrow), le lede
   (.landing-lede), la rangée d'actions (.hero-cta), les blocs (.panel), les
   petites lignes (.help) et la modale (dialog / .dialog-pad) sont ceux du
   Carnet. Ne vivent ici que les trois contenus que le Carnet n'a pas :
   l'échelle des phases, les deux tuiles et la liste de faits. Sept couches
   #pane-beta empilées (1 884 → 1 600 → 1 280 px de mesure, une entrée privée,
   une ombre de bouton) ont été SUPPRIMÉES plutôt que corrigées par une
   huitième — n'en rajoutez pas : si quelque chose cloche ici, c'est la règle
   partagée qu'il faut lire (porte-signature-phase3.test.mjs compte ces
   règles et refuse tout sélecteur #pane-beta).
   Refonte 2026-09-20 (« tout l'espace est utilisé », « pas de components trop
   gros », « animations custom ») : la porte n'a toujours AUCUNE entrée à elle.
   Son mouvement est celui du registre partagé — .reveal, .accent-line,
   .rail-draw (« ENTRÉES AU DÉFILEMENT ») — et sa composition une classe
   partagée, .layout-duo, ci-dessous. */

/* `.layout-duo` — une COMPOSITION partagée, pas une règle de la porte : deux
   panneaux côte à côte sur les colonnes du héros (.landing-hero : même partage,
   même seuil de 900 px), étirés à la même hauteur. Ce qui n'est pas un panneau
   (note de garantie, assistance) garde la rangée entière ; un panneau orphelin
   — le dernier d'un nombre impair — aussi, plutôt qu'une case vide à sa droite. */
.layout-duo { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); align-items: stretch; }
.layout-duo > .panel:last-of-type:nth-of-type(odd), .layout-duo > :not(.panel) { grid-column: 1 / -1; }
@media (max-width: 900px) {
  .layout-duo { grid-template-columns: minmax(0, 1fr); }
}

.beta-phases { min-width: 0; display: flex; flex-direction: column; }
.beta-phase-list { list-style: none; margin: var(--space-2) 0 0; padding: 0; }
/* Une VRAIE échelle : le marqueur carré de la phase, son nom, puis l'état en
   texte nu sous le nom. Ni pastille ni badge. Le montant (.rail-draw, registre
   partagé) part du marqueur et rejoint le suivant — plein entre deux acquis,
   pointillé vers l'objectif ; la ligne ne fait que le PLACER, par ses trois
   variables. */
.beta-phase {
  --rail-x: calc(var(--space-16) / 2 - 1px); --rail-top: calc(var(--space-16) + var(--space-2)); --rail-bottom: var(--space-2);
  display: grid; grid-template-columns: var(--space-16) minmax(0, 1fr); column-gap: var(--space-8);
  padding-bottom: var(--space-12); text-wrap: pretty;
}
.beta-phase-n {
  grid-row: 1 / 3; align-self: start; display: grid; place-items: center; height: var(--space-16);
  border: 2px solid var(--action-ink); border-radius: var(--radius-sm); background: var(--action-tint);
  font-family: var(--font-display); font-weight: var(--weight-display); font-size: var(--type-h4); line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--action-ink);
}
/* La phase où l'on se trouve est un OBJECTIF : son marqueur est tireté comme le
   montant qui y mène, vide, et cerné d'un halo carré — « vous êtes ici ». */
.beta-phase.is-current > .beta-phase-n { border-style: dashed; background: transparent; box-shadow: 0 0 0 var(--space-2) var(--action-tint); }
.beta-phase-name {
  padding-top: var(--space-2); font-family: var(--font-display); font-weight: var(--weight-display);
  font-size: var(--type-h4); line-height: var(--type-h4-lh); letter-spacing: var(--type-h4-ls); color: var(--ink);
}
/* Les deux blocs : une colonne — le titre, son contenu, et l'action au PIED de
   la carte (.layout-duo étire les deux cartes à la même hauteur). Les tuiles
   sont des cartes en retrait coiffées du trait d'accent de 3 px du guide, qui
   se trace à l'arrivée du bloc (.accent-line) : indigo, puis violet. */
.beta-block { display: flex; flex-direction: column; gap: var(--space-10); min-width: 0; padding: var(--space-12); }
.beta-block > .help { margin: 0 0 var(--space-3); }
.beta-tiles { flex: 1; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); gap: var(--space-8); padding-top: var(--space-4); }
.beta-tile {
  --accent-line: var(--accent-blue);
  display: flex; flex-direction: column; justify-content: flex-start; gap: var(--space-3); min-width: 0; min-height: calc(var(--space-16) * 5); padding: var(--pad-card-lg);
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-inset);
}
.beta-tile:nth-child(2) { --accent-line: var(--accent-purple); }
.beta-tile-mark { width: var(--space-16); height: var(--space-16); flex: none; margin-bottom: var(--space-4); }
.beta-tile > p { margin: 0; max-width: 52ch; color: var(--ink-muted); }
/* La modale : le sous-titre de la démonstration respire, les petites lignes
   ferment leur paragraphe, et le cartouche de la salle d'essai se lit comme un
   cartouche — une carte en retrait, pas un sixième paragraphe. */
.beta-details > h4 {
  font-family: var(--font-display); font-weight: var(--weight-display);
  font-size: var(--type-h4); line-height: var(--type-h4-lh); letter-spacing: var(--type-h4-ls);
  margin: var(--space-12) 0 var(--space-4);
}
.beta-details > .help { margin: 0 0 var(--space-6); }
/* Les deux actions ouvrent la modale ; leurs notes d'état, vides, ne prennent
   pas de place — la marge vit donc sous la rangée, pas sur les notes. Le lien
   vers la démonstration technique ferme son paragraphe de la même façon. */
.beta-details > .salle-annonce-actions, .beta-details > .btn { margin: 0 0 var(--space-8); }
.beta-trust-collapsed { padding: var(--pad-card-lg); border-radius: var(--radius); background: var(--surface-inset); }
/* fin — porte « Signature » (refonte 2026-09-20) */

.beta-inscription { flex: 1; justify-content: space-between; }
.beta-inscription-fields {
  display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap;
}
.beta-inscription-fields > input[type="email"] {
  flex: 1 1 240px; min-width: 0;
  font: inherit; padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink);
}
.beta-inscription-fields > input[type="email"]:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 1px;
}
.beta-inscription-msg { margin: var(--space-3) 0 0; }
.subscription-form { display: flex; flex-direction: column; gap: var(--space-3); }
.subscription-form > .btn { align-self: flex-start; }
.subscription-consent-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); min-width: 0; }
.subscription-consent { align-items: center; min-height: var(--control-height); min-width: 0; gap: var(--space-4); font-size: var(--type-h4); line-height: var(--type-body-lh); }
.subscription-consent > input {
  width: var(--space-10); height: var(--space-10); margin: 0;
  outline: var(--space-1) solid var(--ink-muted); outline-offset: calc(var(--space-1) * -1);
  border-radius: var(--radius-xs); accent-color: var(--brand);
}
.subscription-consent > input:focus-visible { outline-color: var(--action-ink); outline-offset: var(--space-2); }
.subscription-consent-row > .itip { margin: 0; }
.subscription-consent-row .itip-btn { width: var(--control-height); height: var(--control-height); min-height: var(--control-height); }
/* Newsletter and beta: a section of the page, between the panes and the footer
   (owner, 2026-10-11: « does not need to have a blue background […] does not
   need to be part of the footer neither »). Two cards on the page rail, on the
   page's own ground and in the card register of the guides above them; app.js
   hides the band with the header's sign-in pair once the visitor is signed in
   (renderAccountMenu). Height comes from content, with one token gap per group. */
.newsletter-signup {
  box-sizing: border-box; width: 100%;
  /* Visible space below both panels before the footer band (owner, 2026-10-11:
     « there's no margin below »): one token step, never a reserved height. */
  padding-block: 0 var(--space-16);
}
/* An author `display` would beat the UA's `[hidden]`; the band has none, the
   rule is said so a later one cannot bring a signed-in visitor's band back. */
.newsletter-signup[hidden] { display: none; }
.newsletter-panels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; gap: var(--space-6); min-width: 0; }
.newsletter-panel { display: flex; flex-direction: column; gap: var(--space-6); min-width: 0; padding: var(--space-6); border: calc(var(--space-1) / 2) solid var(--border); border-radius: var(--radius); background: var(--surface); }
.newsletter-intro, .newsletter-controls { min-width: 0; }
.newsletter-intro > h2 { margin: 0 0 var(--space-3); font-size: var(--type-h3); }
.newsletter-intro > .help { margin: 0; font-size: var(--type-body); line-height: var(--type-body-lh); }
/* One deliberate subscription action (owner, 2026-10-11: « still not a fan of
   the checkbox […] too much word around it »): the email field and « S’abonner »
   share one row, the withdrawal line stays visible and the details open natively
   below. The short field keeps a reading measure instead of absorbing the rail. */
.newsletter-controls .subscription-form { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: var(--space-4); max-inline-size: calc(var(--control-height) * 12); }
.newsletter-controls .beta-inscription-fields { min-width: 0; }
.newsletter-controls input[type="email"] { box-sizing: border-box; width: 100%; height: var(--control-height); min-height: var(--control-height); padding: var(--space-4) var(--space-6); font-size: var(--type-body); }
.newsletter-controls .subscription-form > .btn, .newsletter-beta-link { width: auto; justify-self: start; align-self: end; }
.newsletter-controls .subscription-form > .errors { grid-column: 1 / -1; margin: 0; }
.newsletter-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--space-6); margin-block-start: var(--space-2); color: var(--ink-muted); }
.newsletter-meta > .help { margin: 0; font-size: var(--type-control); }
.newsletter-details { min-width: 0; font-size: var(--type-control); }
.newsletter-details > summary { box-sizing: border-box; min-height: var(--control-height); padding-block: var(--space-6); cursor: pointer; }
.newsletter-details > summary:focus-visible { outline: var(--space-1) solid var(--action-ink); outline-offset: calc(var(--space-2) * -1); border-radius: var(--radius-sm); }
.newsletter-details[open] { flex-basis: 100%; }
.newsletter-disclosure { display: grid; gap: var(--space-4); padding-block: 0 var(--space-4); }
.newsletter-disclosure > .help { margin: 0; }
.newsletter-disclosure > a { display: inline-flex; align-items: center; justify-self: start; min-height: var(--control-height); }
.newsletter-panel--beta > .newsletter-beta-link { align-self: start; }
@media (max-width: 768px) {
  .newsletter-panels { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 480px) {
  .newsletter-controls .subscription-form { grid-template-columns: minmax(0, 1fr); }
  .newsletter-controls .subscription-form > .btn, .newsletter-beta-link { width: 100%; }
}
/* The field sits in a card of the page's surface: the recess and the stronger
   rule keep its edge readable in both themes, the placeholder its contrast. */
.newsletter-controls input[type="email"] { background: var(--surface-inset); border-color: var(--border-strong); color: var(--ink); }
.newsletter-controls input[type="email"]::placeholder { color: var(--ink-muted); opacity: 1; }
/* The secondary action below the form: an outlined chip, not a bare text
   link — the owner asked for a visible click target (2026-09-26). Muted ink
   and a chevron keep it secondary to the primary CTA above it. */
#beta-details-open {
  align-self: flex-start; color: var(--ink-muted);
  padding: var(--space-2) var(--space-4);
}
#beta-details-open::after {
  content: "›"; font-weight: 400;
  transition: transform var(--motion-fast);
}
#beta-details-open:hover { color: var(--ink); }
#beta-details-open:hover::after { transform: translateX(2px); }

/* --- Notary landing polish ----------------------------------------------
   The door opens on the SAME surface the Carnet opens on (owner, 2026-09-12).
   ONE copy of each rule (the three stacked « final » layers that stood here
   redefined the card surface four times and stripped the panel the base rule
   gives .nc-live — removed 2026-09-18, the base rules now say it once). */

/* A heading is ink on every surface. Blue is for an informational mention (an
   amount, a link, a badge), never for the label a section is read by. */
#pane-notaires #notary-console .h-sub {
  color: var(--ink);
}

#pane-notaires .nc-live-card {
  border-color: var(--border);
  border-radius: 0;
}

/* Service, price and action labels use the same neutral reading ink. */
#pane-notaires .nc-live-amt {
  color: var(--ink);
  font-weight: 700;
}

#pane-notaires .nc-auth-cta {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--ink);
  box-shadow: none;
}
#pane-notaires .nc-auth-cta:hover {
  background: var(--action-tint);
  border-color: var(--action-ink);
  color: var(--action-ink);
}

#pane-notaires .nc-signup-open {
  color: var(--ink);
}



/* --- Account menu refinement ---------------------------------------------
   Keep the account hub calm and flat: the shadow separates it from the page,
   while the identity row remains part of the menu instead of becoming a card
   inside another card. */
.acct-panel {
  width: min(368px, calc(100vw - 24px));
  padding: 10px;
  border: 0;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}
.acct-panel-head {
  align-items: center;
  gap: 8px;
  padding: 2px;
}
.acct-panel-head .acct-head {
  gap: 11px;
  padding: 10px;
  border: 0;
  background: transparent;
}
.acct-panel-head .acct-head:hover,
.acct-panel-head .acct-head:focus-visible {
  background: var(--action-tint);
}
.acct-avatar {
  width: 40px;
  height: 40px;
  background: var(--surface);
  border-color: var(--border-strong);
}
.acct-item-title { font-size: var(--type-h4); line-height: var(--type-body-lh); letter-spacing: var(--type-h4-ls); }
.acct-item-sub { font-size: var(--type-eyebrow); line-height: var(--type-body-lh); }
.acct-role { margin-top: 3px; padding: 2px 7px; background: transparent; }
/* The close is an .icon-btn inside .site-header, whose bar rules weigh up to
   (0,3,0): `:is(.icon-btn, .header-auth .btn, …)` counts its heaviest
   argument. These selectors tie or outweigh them and come later, so the ✕
   keeps the menu's ink, hairline and hover — not a header-muted ✕ (1.4:1),
   a navy hairline and a navy hover (account-menu-palette.test.mjs). */
.site-header .acct-panel .acct-close {
  width: 34px;
  height: 34px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--ink-muted);
}
.site-header .acct-panel .acct-close:is(:hover, :focus-visible) {
  border-color: var(--border);
  background: var(--action-tint);
  color: var(--ink);
}
.acct-sep { margin: 8px 4px; background: color-mix(in srgb, var(--border-strong) 70%, transparent); }
.acct-actions { gap: 3px; padding: 1px 2px; }
.acct-action {
  min-height: var(--control-height);
  padding: 8px 10px;
  border: 1px solid transparent;
  transition: background-color var(--motion-fast) var(--ease-ui), border-color var(--motion-fast) var(--ease-ui), transform var(--motion-fast) var(--ease-ui);
}
.acct-action svg {
  width: 18px;
  height: 18px;
  color: var(--action-ink);
}
.acct-action:hover,
.acct-action:focus-visible {
  border-color: var(--border);
  background: var(--action-tint);
}
.acct-action:hover { transform: translateX(2px); }
/* Sign-out closes the account group behind a straight hairline. The row's own
   top border drew a curved half-frame on its rounded corners; the line is
   counter-shifted so the hover nudge moves the row, not the separator. */
.acct-action:last-child {
  position: relative;
  margin-top: var(--space-4);
  color: var(--ink-muted);
}
.acct-action:last-child::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: calc(-1 * var(--space-2));
  height: 1px;
  background: var(--border);
}
.acct-action:last-child:hover::before { translate: calc(-1 * var(--space-1)) 0; }
.acct-action:last-child svg { color: var(--ink-muted); }
/* The profile's sections under « Mon profil » (owner, 2026-10-03): compact
   rows indented to its label, quieter at rest (muted ink, regular weight),
   the same tint, hairline and focus as every menu row when pointed at. */
.acct-subnav {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin: 0 0 var(--space-2) var(--space-14);
}
.acct-subitem {
  gap: var(--space-4);
  min-height: var(--control-height);
  padding: var(--space-2) var(--space-5);
  border: 1px solid transparent;
  color: var(--ink-muted);
  transition: background-color var(--motion-fast) var(--ease-ui), border-color var(--motion-fast) var(--ease-ui), color var(--motion-fast) var(--ease-ui);
}
.acct-subitem .acct-item-title { font-weight: 500; }
.acct-subitem-ic { display: grid; place-items: center; flex: none; }
.acct-subitem:is(:hover, :focus-visible) {
  border-color: var(--border);
  background: var(--action-tint);
  color: var(--ink);
}
.acct-subitem:is(:hover, :focus-visible) svg { color: var(--action-ink); }
.acct-notifs { margin-top: 2px; }
.acct-panel .notif-head {
  padding: 5px 10px 4px;
  color: var(--ink-muted);
}
/* Notifications read as a list on the menu's own paper, not a card inside the
   card: hairlines between rows, no fill. Unread is a dot in its own column and
   a heavier title, never a wash (owner, 2026-10-03: no light-blue fills). Every
   summary keeps to its lines; the full text stays in the tooltip and in
   « Détails ». The dot and the actions sit on the first line, whatever the
   summary's length. Proofs: account-menu-palette.test.mjs. */
.acct-panel .notif-list {
  overflow-x: hidden;
  overflow-y: auto;
  border-block: 1px solid var(--border);
}
.acct-panel .notif-item {
  grid-template-columns: var(--space-3) minmax(0, 1fr) auto;
  align-items: start;
  column-gap: var(--space-4);
  padding: var(--space-5) var(--space-4) var(--space-5) var(--space-5);
  border-bottom-color: var(--border);
}
.acct-panel .notif-item::before {
  content: '';
  width: var(--space-3);
  height: var(--space-3);
  margin-top: calc((var(--type-h4) * var(--type-body-lh) - var(--space-3)) / 2);
  border-radius: 50%;
}
.acct-panel .notif-item.is-unread::before { background: var(--brand-on-surface); }
.acct-panel .notif-item.is-unread .notif-title::before { content: none; }
.acct-panel .notif-item:last-child { border-bottom: 0; }
.acct-panel .notif-title { font-weight: 500; color: var(--ink-muted); }
.acct-panel .notif-item.is-unread .notif-title { font-weight: 700; color: var(--ink); }
.acct-panel .notif-item:not(.is-expanded) .notif-title {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--notif-summary-lines);
  overflow: hidden;
}
.acct-panel .notif-open { min-height: 0; }
.acct-panel .notif-actions { margin-block: calc((var(--type-h4) * var(--type-body-lh) - var(--control-height)) / 2); }
.acct-panel .notif-body { grid-column: 2 / -1; }
/* A row answers the pointer with the menu tint and an inset bar, so the state
   never rests on a tint alone. */
.acct-panel .notif-item.has-destination:hover,
.acct-panel .notif-item:focus-within { background: var(--action-tint); box-shadow: inset var(--space-1) 0 0 var(--action-ink); }
.acct-legal {
  gap: 2px;
  padding: 0 2px 1px;
}
.acct-legal-link { padding: 5px 7px; }

@media (max-width: 899.98px) {
  .acct-panel {
    position: fixed;
    top: calc(var(--header-h) + 8px);
    right: 8px;
    width: min(360px, calc(100vw - 16px));
  }
}

/* Final visual pass: these rules intentionally sit after the responsive intro
   rules so the calm chooser treatment wins at every viewport size. */
.language-control { opacity: 0 !important; pointer-events: auto !important; }
#lang-toggle.language-control {
  position: relative !important;
  top: auto; right: auto; width: 1px; height: 1px;
  margin: 0; padding: 0; overflow: visible; border: 0;
  z-index: 1;
}
.mnav-pref.language-control {
  min-height: 1px; height: 1px; margin-block: -1px !important; overflow: visible;
}
#lang-toggle.language-control { min-width: 0 !important; }
.analytics-consent {
  left: 50%; right: auto; bottom: 16px;
  width: min(560px, calc(100vw - 32px)); max-width: none;
  padding: 12px 14px;
  transform: translateX(-50%);
  background: var(--surface);
  border-color: var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.analytics-consent p { margin-bottom: 8px; font-size: 12px; line-height: 1.45; }
.analytics-consent button, #analytics-preferences:not(.footer-link) { padding: 7px 10px; margin-right: 4px; font-size: 12px; }
.analytics-consent a { font-size: 12px; }

/* Keep the locale control discoverable in the real header and mobile drawer.
   The arrival-film copies remain intentionally quiet, but the application
   shell must always expose the FR | EN choice promised by the product. */
.language-control {
  opacity: 1 !important;
  pointer-events: auto !important;
}

#lang-toggle.language-control {
  position: relative !important;
  width: auto;
  height: auto;
  min-width: auto !important;
  margin: 0;
  padding: 2px;
  overflow: visible;
}

.mnav-pref.language-control {
  min-height: 0;
  height: auto;
  margin-block: 0 !important;
  overflow: visible;
}

/* The section tint should whisper against the white card — grey on light
   (owner, 2026-10-03: no light-blue ground), blue on the night through
   --brand-tint-pour. The white parent and the 24 px sibling margin are the
   visual separator. */
#day-dialog .offer-sec,
#day-dialog .o-criteria .crit-sec {
  background: color-mix(in srgb, var(--brand-tint-pour) 6%, var(--surface));
}

/* Information is an affordance, but the glyph itself is not another boxed
   card: keep it brand-coloured and remove the enclosing ring. */
#day-dialog .itip-btn,
#day-dialog .itip-btn:hover,
#day-dialog .itip[data-open='true'] .itip-btn {
  border: 0;
  background: transparent;
  color: var(--action-ink);
}

/* The beta note is a signed-out discovery cue; an authenticated notary gets
   the account-scoped preparation card instead. */

/* Lead with calendar subscription; account access follows it in the rail. */

/* Touch targets apply at every width; keep these outside the wide-only
   authenticated landing layout above. */
.nc-conformite-link,
.sup-mail-open {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}
.sup-text { min-height: 44px; }




/* Calendar subscription is the first action on compact notary landings. */

/* The public notary landing leads with the calendar and available offers.
   Keep these rules scoped to the signed-out state; the working console retains
   its full-width layout after authentication. */
/* Render offers together: staggered fades left temporary holes on refresh. */
.nc-access { min-width: 0; }
.nc-access[hidden] { display: none; }
/* Four balanced tracks: a compact access form and three preparation choices.
   Below the hero breakpoint the form shares a row with its actual benefits. */
:root { --form-compact-max: 28rem; }
.nc-bottom { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--nc-col-gap); align-items: start; margin-top: var(--section-gap); }
/* Four tracks at --nc-col-gap, as the request row above: the account entry
   takes the first, the AI column the other three, and its three choices (same
   gutter) fall exactly under request tiles two to four. 1fr/3fr looked the
   same but shifted the choices half a gap off the tiles. */
@media (min-width: 1280px) {
  .nc-bottom { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .nc-bottom > #notary-ai-voies { grid-column: span 3; }
  /* Two sections side by side, not two cards of one: the account column keeps
     its track (the AI choices stay on the tile grid) and gives up the rest of
     a section gap on its inner side (propriétaire, 2026-09-24). */
  .nc-bottom > .notary-console-wrap { padding-inline-end: calc(var(--section-gap) - var(--gap)); }
  /* Side by side, the two sections read on one line: the account kicker and
     heading sit inside their card, one register inset (--space-8) under its
     crown, so the AI kicker and heading take the same inset on the page. The
     card arrived on 2026-10-06 without it and the two headings stood 16 px
     apart (e2e/balanced-layout.spec.js). Stacked, below this width, each
     section keeps its own air. */
  .nc-bottom .nc-voies-intro { padding-block-start: var(--space-8); }
}
.nc-bottom > .notary-console-wrap,
.nc-bottom .notary-landing-right { display: flex; flex-direction: column; min-width: 0; }
.nc-bottom #notary-console { display: flex; flex-direction: column; margin-top: 0; }
.nc-bottom .nc-access { display: flex; flex-direction: column; }
.nc-bottom .nc-gate { margin-bottom: var(--space-7); }
.nc-bottom .nc-gate { width: 100%; max-width: var(--form-compact-max); }
.nc-bottom .nc-auth-cta { min-height: var(--feedback-control-size, 44px); }
/* The two section containers stay on the page background: the hierarchy is
   carried by their tiles, not by a panel behind them. */
.nc-bottom #notary-ai-voies {
  border-radius: 0;
  background: transparent; -webkit-backdrop-filter: none; backdrop-filter: none;
  box-shadow: none;
}
/* Stacked under the account column the section keeps its own air; beside it,
   that padding (--space-9, 18px measured) is exactly how far short the three
   tiles used to bottom out against the account card. The track's edge aligns. */
@media (max-width: 1279.98px) { .nc-bottom #notary-ai-voies { padding: var(--space-9) 0; } }
.nc-bottom .nc-voies { margin-top: 0; padding: 0; background: none; -webkit-backdrop-filter: none; backdrop-filter: none; box-shadow: none; align-items: stretch; flex: 1 1 auto; }
/* Each tile of the band is a card of the shared register (the partner door's
   .pr-card): a veiled surface, a radius, and the 3 px accent line that draws
   itself on arrival (.accent-line) — not a plain border around the fields.
   Owner, 2026-10-06, option B: the three AI choices are quiet siblings, crowned
   with the register's neutral line — a crown is a signal, so it holds the 3:1
   --border-strong could not. The register carries no border-top anywhere — the
   crown *is* the .accent-line bar, and a border under it stacks into a 6 px
   line. Signed in, the console is the working surface and wears the same
   register with the brand crown: it is the one primary on that screen. */
.nc-bottom #notary-console,
.nc-bottom .nc-voie {
  padding: var(--space-8) var(--space-8) var(--space-7);
  border: 0; border-radius: var(--radius-lg);
  background: var(--surface-veil);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.nc-bottom #notary-console { --accent-line: var(--brand); box-shadow: var(--shadow-sm); }
.nc-bottom .nc-voie { --accent-line: var(--accent-line-neutral); box-shadow: none; }
/* SIGNED OUT, the account door is a heading and a form on the page ground
   (propriétaire, 2026-10-18 : « the white background around Accédez aux
   demandes — only the title and the form »). His own 2026-09-22 note still
   holds — « prend beaucoup de place pour un simple input » — and the field and
   its button are already outlined controls, so the card was a frame around a
   frame. The inline inset and the crown-height of top padding stay: they are
   what put the two section headings on one line and their content on the tiles'
   content line. */
#pane-notaires .nc-bottom:not(:has(#notary-authed:not([hidden]))) #notary-console {
  padding: var(--space-8) var(--space-8) 0;
  border-radius: 0;
  background: transparent;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  box-shadow: none;
}
#pane-notaires .nc-bottom:not(:has(#notary-authed:not([hidden]))) #notary-console::before { display: none; }
/* One text line per column: the section label settles on the same tab as the
   cards it announces, the way the account card's title sits on the line of its
   own fields. The offset is the register's interior padding (--space-8, the
   partner card's), measured by the e2e guard — two copies of one register are
   compared, not each checked (rule 10). */
.nc-bottom .nc-voies-intro { padding-inline-start: var(--space-8); }
/* Aligned footers: the choice with the least text still ends its button on the
   same line as its neighbours. */
.nc-bottom .nc-voie > .btn { margin-top: auto; }
.nc-bottom .nc-voie:nth-child(2) { --reveal-i: 3; }
.nc-bottom .nc-voie:nth-child(3) { --reveal-i: 6; }
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .nc-bottom { display: block; margin-top: 0; }
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .notary-landing-right { display: contents; }
/* Keep the pane's navigation target available to assistive technology while
   letting the work start immediately below the navigation bar. Since the
   2026-09-26 gate the four-agenda row IS the signed-in notary's tool, so the
   sr-only box moves onto the h1 alone: a clip on the whole hero
   (overflow: hidden + clip-path) hides every descendant with it, and the
   folded row would leave the notary punished for signing in. */
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .intro--hero .eyebrow { display: none; }
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) #notary-page-title {
  position: absolute;
  width: 1px;
  height: 1px;
  min-height: 0;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) #notary-console {
  width: 100%;
  margin: 0;
  border: 0;
  /* The October 18 owner preserves the connected card register above.
     Keep the full-width workspace and its task overflow without replacing
     that shared frame or suppressing its primary crown. */
  overflow: visible;
}
#pane-notaires .nc-card,
#pane-notaires .nc-live-card {
  background: var(--nc-offer-surface);
  box-shadow: none;
  animation: none;
  transition: background-color var(--motion-fast) var(--ease-ui), border-color var(--motion-fast) var(--ease-ui), box-shadow var(--motion-fast) var(--ease-ui);
}
#pane-notaires .nc-card:hover,
#pane-notaires .nc-live-card:hover {
  transform: none;
  background: var(--nc-offer-hover);
  border-color: var(--border-strong);
  box-shadow: none;
}
@media (pointer: coarse) {
  .nc-calendar-teaser .nc-calendar-play { min-height: 44px; }
}
/* The help launcher (.speed-dial) is fixed to the bottom-right. While the
   calendar is stacked under the notary hero (up to 1279.98px: above 1280 it
   is the right column, at the top) its last column reaches that corner on
   first paint — a covered date is a date nobody can click
   (e2e/calendar-seamless.spec.js). The calendar reserves the launcher's
   column rather than the launcher moving: .speed-dial is global, and nudging
   it to dodge one grid at one width only pushes the collision somewhere
   else. The reserve READS the launcher's own tokens — a literal 64px (the
   old 48px bubble + 16px rail) went stale silently when the control grew to
   56 + 24 and gained its word « Aide » from 1024px (measured at 1024×768:
   launcher 914→1000, grid to 920). The .wrap gutter absorbs the word.
   The phone is in the range since 2026-10-12. Up to 2026-10-10 the launcher
   sat in the flow at ≤600px on this pane; that left 64px of canvas under
   the footer, so it is fixed at every width again — and the request the
   month now opens at mount put its price and its action in that corner
   (measured at 390×844: launcher 326→374, price to 377). */
@media (max-width: 1279.98px) {
  #nc-calendar-teaser { padding-right: calc(var(--help-control-size) + var(--help-edge-gap)); }
}
/* From 1280px the calendar is the hero's right column and the request it
   opens at mount ends that column: the price, then the action. The page
   gutter is 40px and the launcher, with its word, spans 112px from the edge,
   so the two share 72px of rail until the page stops growing (--layout-width).
   In a window too short to hold the hero above the launcher it rests on the
   price (measured at 1280×580: launcher 1168→1256 × 500→556, price to
   1240 × 482→515). A 1920×1080 laptop at 150 % is that window. Only the
   request and its action step aside, by a margin so their rule stops short
   too: the month keeps the column's full width, and the two columns keep one
   width and one bottom line (e2e/notaire-equilibre.spec.js). The second gap
   is the clearance the wider launcher needs on the request's side. 720px is
   the tallest hero (a six-week month ends near 618px) plus the launcher's
   80px and one gap: above it the launcher rests under the hero and nothing
   moves. */
@media (min-width: 1280px) and (max-height: 720px) {
  #nc-calendar-teaser :is(.nc-mobile-offer, .nc-mobile-details) { margin-right: calc(var(--help-control-size) + var(--help-edge-gap) * 2); }
}


/* The native agenda shares the page surface; only the standalone film needs
   rounded clipping. Native controls and keyboard outlines remain visible. */
.nc-calendar-teaser {
  position: relative; isolation: isolate; margin: 20px auto; width: 100%; max-width: 1100px;
  min-width: 0; align-self: center;
  /* Sans cadre (propriétaire, 2026-09-14 : « avoid border like this… make them
     seamless ») : un trait à la couleur de marque plus une ombre encadraient le
     calendrier comme une image collée sur la page. Il n'a plus de bord — il
     repose sur le fond de la page et c'est la grille elle-même qu'on lit. */
  border: 0; background: transparent; box-shadow: none;
}
.nc-calendar-teaser:not([data-native]) { overflow: hidden; border-radius: var(--radius-lg); }
.nc-calendar-teaser[data-native] { margin-block: 0; }

/* The phone calendar keeps its month readable; the offer gets its own row
   instead of shrinking the desktop film's text into seven tiny columns. */
/* Sans couture : le calendrier repose sur le fond de la page, sans plaque ni
   trait qui le découpe. Seule la grille et ses chiffres portent la scène. */
.nc-mobile-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 16px; margin-bottom: 16px; }
.nc-mobile-month { font-family: var(--font-display); font-size: 22px; line-height: 1.3; font-weight: var(--weight-display); white-space: nowrap; }
.nc-mobile-legend, .nc-mobile-today { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 12px; color: var(--ink-muted); font-weight: 500; white-space: nowrap; }
.nc-mobile-legend::before { content: ''; flex-shrink: 0; width: 6px; height: 6px; border-radius: 50%; background: var(--ink-muted); }
.nc-mobile-week, .nc-mobile-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.nc-mobile-week { padding-bottom: 10px; color: var(--ink-muted); font-size: 11px; text-align: center; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.nc-mobile-grid { gap: 2px 0; }
.nc-mobile-day { appearance: none; display: grid; place-items: center; align-content: center; gap: 3px; min-width: 0; height: 44px; padding: 0; font: inherit; font-size: 14px; font-variant-numeric: tabular-nums; color: inherit; background: transparent; border: 1px solid transparent; border-radius: var(--radius-sm); transition: background-color var(--motion-enter) var(--ease-ui), border-color var(--motion-enter) var(--ease-ui); position: relative; cursor: default; }
.nc-mobile-day::before { content: ''; position: absolute; inset: 0; border: 1px solid var(--border); border-radius: var(--radius-sm); pointer-events: none; opacity: 0.4; }
.nc-mobile-day::after { content: ''; width: 5px; height: 5px; border-radius: 50%; background: transparent; transition: background var(--motion-enter) var(--ease-ui); position: absolute; bottom: 6px; left: 50%; transform: translateX(-50%); }
.nc-mobile-day.has-request { cursor: pointer; }
.nc-mobile-day.has-request:is(:hover, :focus-visible) { background: var(--action-tint); }
.nc-mobile-day.has-request:hover::before { opacity: 0.6; border-color: var(--action-ink); }
.nc-mobile-day.has-request::after { background: var(--ink-muted); }
.nc-mobile-day.is-outside { color: var(--ink-muted); opacity: .35; }
.nc-mobile-day.is-outside::before { opacity: 0.2; }
.nc-mobile-day.is-selected { font-weight: 700; border-color: var(--action-ink); background: var(--action-tint); color: var(--ink); box-shadow: none; }
.nc-mobile-day.is-selected::before { opacity: 0; }
.nc-mobile-day.is-selected::after { background: var(--action-ink); }
.nc-mobile-number { font-size: 14px; font-variant-numeric: tabular-nums; }
/* Aujourd'hui (jour ouvrable québécois) : l'encre du texte, en gras —
   le repère qui fait lire un mois vide comme un vrai mois, pas un gabarit. */
.nc-mobile-day.is-today { font-weight: 800; color: var(--ink); }
.nc-mobile-day.is-today::before { opacity: 1; border-color: var(--border-strong); }
/* La fiche d'état vide prend la place de la fiche du jour : même filet, une
   phrase, et le geste de la porte en dessous. */
.nc-mobile-offer.nc-mobile-empty { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
.nc-mobile-offer .nc-mobile-empty-p { margin: 0; font-size: var(--type-eyebrow); line-height: var(--type-body-lh); color: var(--ink-muted); text-wrap: pretty; }
.nc-mobile-offer { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px 12px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }
.nc-mobile-date { grid-column: 1 / -1; font-size: 12px; color: var(--ink-muted); font-weight: 500; }
.nc-mobile-date::first-letter { text-transform: uppercase; }
.nc-mobile-service { align-self: center; font-size: 15px; font-weight: 700; overflow-wrap: anywhere; }
.nc-mobile-amount { align-self: center; font-size: 22px; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--ink); font-weight: 700; }
.nc-mobile-details { margin-top: var(--space-6); }
.nc-mobile-cta { min-height: 44px; max-width: 100%; white-space: normal; color: var(--ink); border-color: var(--border-strong); }
.nc-mobile-cta:is(:hover, :focus-visible) { color: var(--action-ink); border-color: var(--action-ink); }

@media (max-width: 600px) {
  /* Au téléphone la preuve est une LISTE : une demande par ligne, le montant
     à droite, comme le pouls du carnet. */
  #pane-notaires #notary-live-grid { grid-template-columns: minmax(0, 1fr); grid-auto-rows: auto; gap: 8px; }
  #pane-notaires .nc-live-card { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: 'svc amt' 'meta pill'; align-items: center; align-content: center; gap: 8px 12px; padding: var(--space-7) 0; }
  #pane-notaires .nc-live-card .nc-live-svc-name { font-size: 14px; white-space: normal; overflow: visible; }
  #pane-notaires .nc-live-card .nc-live-amt { justify-self: end; font-size: 20px; }
  #pane-notaires .nc-live-card .nc-live-meta { font-size: 12px; }
  #pane-notaires .nc-live-more { min-height: 76px; }
}

/* ONE HEADER LAYOUT — for every public entry point, not for three of them by
   name. Keyed on the classes alone: a door that carries .landing-hero gets the
   register, wherever it lives. The ID-scoped form this replaces silently left
   #pane-beta outside the block, which is how the four doors drifted apart. */
.landing-hero {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: clamp(16px, 2vw, 28px);
  align-items: start;
  min-height: 0;
  margin: 0;
  padding: var(--intro-pad-top) 0;
  text-align: left;
}
/* A soft radial glow fills the empty dark space behind the hero copy — two
   overlapping gradients at low opacity so the surface breathes without
   distracting from the content. Scoped to the carnet pane. */
#pane-carnet .landing-hero {
  background:
    radial-gradient(ellipse 60% 50% at 18% 40%, var(--brand-tint) 0%, transparent 70%),
    radial-gradient(ellipse 40% 45% at 75% 60%, rgb(var(--nota-blue-400-rgb) / 0.03) 0%, transparent 70%);
}
.landing-copy {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: flex-start;
  min-width: 0;
  width: 100%;
  max-width: none;
}
.landing-copy > .eyebrow { margin: 0 0 var(--space-8); }
.landing-copy > h1 {
  width: 100%;
  max-width: none;
  margin: 4px 0 0;
  font-size: var(--type-h1);
  line-height: var(--type-h1-lh);
  text-wrap: balance;
}
.landing-copy > .landing-lede {
  width: 100%;
  max-width: 47ch;
  margin: var(--space-8) 0 0;
  font-size: var(--type-lead);
  line-height: var(--type-lead-lh);
  color: var(--ink-muted);
  text-wrap: pretty;
}
/* The market and native agenda get their own row before the right column
   becomes cramped. The homepage market keeps two readable service columns. */
@media (min-width: 768px) and (max-width: 1279.98px) {
  #pane-carnet .landing-hero,
  #pane-notaires .landing-hero { grid-template-columns: minmax(0, 1fr); }
}
/* On that single column the copy would hold half the rail and leave the other
   half blank (owner, 2026-10-12): the kicker, the title and the lede keep the
   first half, the two doors, their note and the replay take the second. Two
   reading columns rather than a grid, so each half keeps the text rhythm of
   the single column and the two start on one line. */
@media (min-width: 768px) and (max-width: 1279.98px) {
  #pane-carnet .landing-copy { display: block; columns: 2; column-gap: var(--space-16); }
  #pane-carnet .landing-copy > .eyebrow { display: block; }
  #pane-carnet .landing-copy > .hero-cta { break-before: column; margin-top: 0; }
  #pane-carnet .landing-copy > * { break-inside: avoid; }
}
/* Six market services use the available width instead of six tall rows.
   The copy keeps its reading measure; the wider market can hold two columns
   without reducing type or the booking targets. Both columns end near the
   calendar instead of leaving a blank band below the replay action. */
@media (min-width: 768px) {
  #pane-carnet .pulse-rows { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6) var(--space-8); }
  #pane-carnet .pulse-row { grid-template-columns: auto minmax(24px, 1fr); row-gap: var(--space-2); }
  #pane-carnet .pulse-figs { grid-column: 1 / -1; grid-row: 3; justify-content: space-between; }
  #pane-carnet .pulse-fig { align-items: flex-start; }
  #pane-carnet .pulse-fig:last-child { align-items: flex-end; }
}
@media (min-width: 1280px) {
  #pane-carnet .landing-hero { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
  #pane-carnet .pulse-rows { row-gap: var(--space-2); }
  #pane-carnet .pulse-item .pulse-row { padding-block: var(--space-2); }
}
@media (max-width: 900px) {
  .landing-hero {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(18px, 3vw, 26px);
    padding-block: var(--space-7);
  }
}
@media (max-width: 680px) {
  .landing-copy > h1 { font-size: var(--type-h1-compact); }
}

/* The header and subscription share the left column; the film starts beside
   the header. A shared grid avoids reserving an empty title row above it.
   De-scoped with the block above: the notary hero reads as one column in both
   the signed-out and signed-in states, rather than only while the gate shows. */

/* Profile workspace: a quiet navigation rail and readable, consistent sections. */
/* The outer rail stays the page's (left edge on the logos' gutter); the
   readable width caps the content inside it, left-aligned — a capped, centred
   rail put the profile 80 px right of the logos at 1440 px. */
#pane-profil > .wrap > :is(.profil-intro, .profil-layout) { max-width: calc(1280px - 2 * var(--layout-gutter)); }
#pane-profil .profil-intro { margin-bottom: 28px; }
#pane-profil .profil-intro h1 { font-size: var(--type-h1-compact); }
#pane-profil .profil-intro p { max-width: 58ch; }
.profil-layout { display: grid; grid-template-columns: 204px minmax(0, 1fr); gap: 32px; align-items: start; }
.profil-nav { position: sticky; top: calc(var(--sticky-top) + 16px); display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.profil-nav > .eyebrow { margin: 0 0 12px 12px; font-size: 10px; }
.profil-nav-item {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 48px; padding: 8px 12px;
  border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent;
  font: inherit; font-size: 14px; font-weight: 600; text-align: left; color: var(--ink-muted); cursor: pointer;
}
.profil-nav-item .profil-card-ic { background: transparent; color: inherit; }
.profil-nav-item:hover, .profil-nav-item:focus-visible { background: var(--action-tint); color: var(--action-ink); border-color: var(--border-strong); }
.profil-nav .account-tour-replay { order: 1; margin-top: 20px; white-space: normal; align-self: stretch; }
/* The door (no connection): one column, one card, nothing of the space. */
#pane-profil:is([data-access="closed"], [data-access="checking"]) .profil-layout { grid-template-columns: minmax(0, 1fr); }
#pane-profil .profil-door { max-width: 62ch; gap: var(--space-6); }
.profil-door-text { margin: 0; font-size: var(--type-body); line-height: var(--type-body-lh); color: var(--ink-muted); }
.profil-door-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.profil-door-actions .btn { min-height: var(--control-height); }
.profil-door-pro { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
.profil-door > .btn-ghost { align-self: flex-start; }
/* The dossier's gate, once complete: one line and the door to its answers. */
.dossier-gate-summary { align-items: center; }
.dossier-gate-summary .dossier-body { flex: 1; min-width: 0; }
.dossier-gate-summary .btn { flex: none; min-height: var(--control-height); }
#pane-profil .profil-stack { gap: 24px; min-width: 0; }
/* On a wide desk the profile uses its rail (owner, 2026-10-12) without
   widening a section past a reading width: the dossier, the tallest section,
   takes the second column and the others keep the first, the referral
   directly under them. A profile without a dossier keeps its single column. */
@media (min-width: 1600px) {
  #pane-profil > .wrap > :is(.profil-intro, .profil-layout) { max-width: none; }
  #pane-profil #profil-body:has(> #profil-documents) { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: auto 1fr; column-gap: var(--space-16); align-items: start; }
  #pane-profil #profil-body:has(> #profil-documents) > * { grid-column: 1; }
  #pane-profil #profil-body:has(> #profil-documents) > #profil-documents { grid-column: 2; grid-row: 1 / -1; }
}
/* A short lede closes the title's row at the end of the rail instead of
   stopping in the middle of it. */
@media (min-width: 700px) {
  #pane-profil .profil-intro > h1 + p { justify-self: end; }
}
#pane-profil .profil-card {
  min-width: 0; padding: clamp(18px, 2.5vw, 28px); gap: 20px;
  border-radius: var(--radius-lg); background: var(--surface-veil);
  scroll-margin-top: calc(var(--sticky-top) + 20px);
}
#pane-profil .profil-card:focus { outline: 2px solid var(--brand); outline-offset: 4px; }
#pane-profil .profil-card-head { display: grid; grid-template-columns: 36px minmax(0, 1fr); gap: 4px 12px; }
#pane-profil .profil-card-head > .profil-card-ic { grid-row: 1 / span 2; width: 36px; height: 36px; background: var(--brand-tint-solid); }
#pane-profil .profil-card-title { font-size: var(--type-h3); line-height: var(--type-h3-lh); letter-spacing: var(--type-h3-ls); }
#pane-profil .profil-card-sub { grid-column: 2; max-width: 64ch; font-size: 13px; line-height: 1.55; }
#pane-profil #profil-colors {
  gap: var(--space-8);
  padding: var(--space-12);
}
#pane-profil #profil-colors .profil-card-sub {
  font-size: var(--type-body);
  line-height: var(--type-body-lh);
}
#pane-profil #profil-colors .profil-color-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(calc(var(--control-height) * 5), 100%), 1fr));
  gap: var(--space-6);
}
#pane-profil #profil-colors .profil-color-reset {
  align-self: flex-end;
  min-height: var(--control-height);
  white-space: normal;
  max-width: 100%;
  font-size: var(--type-body);
}
#pane-profil .profil-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; }
#pane-profil .profil-fields input { min-height: 46px; background: var(--surface); }
#pane-profil .profil-fields .form-row > .help { font-size: 12px; }
/* --- A profile section states its values (owner, 2026-10-13; § 8.5.4) --------
   Label and value on one line each, then « Modifier »: the form itself is in
   its modal (#profil-contact-dialog, #profil-notifications-dialog). */
.profil-values { display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: var(--space-2) var(--space-8); margin: 0; font-size: var(--type-body); line-height: var(--type-body-lh); }
.profil-values dt { color: var(--ink-muted); }
.profil-values dd { margin: 0; min-width: 0; color: var(--ink); font-weight: 600; overflow-wrap: anywhere; }
.profil-value-empty { color: var(--ink-muted); font-weight: 400; }
.profil-value-note { margin-inline-start: var(--space-4); font-weight: 400; }
.profil-values-line { margin: 0; font-size: var(--type-body); line-height: var(--type-body-lh); color: var(--ink); }
.profil-values-n { font-variant-numeric: tabular-nums; font-weight: 700; }
.profil-values-off { color: var(--ink-muted); }
/* « Mon dossier »: the price questions read as answers, one per line. */
.dossier-answers { margin-top: var(--space-5); }
.dossier-answers dd[data-todo='true'] { font-weight: 400; }
#dossier-answers-dialog .dossier-price { margin-bottom: 0; }
.profil-edit { align-self: flex-start; min-height: var(--control-height); font-size: var(--type-body); }
.profil-edit-body { display: flex; flex-direction: column; gap: var(--space-6); }
#pane-profil .profil-edit-body :is(.profil-fields, .profil-switches) { grid-template-columns: 1fr; max-width: none; }
.profil-edit-body .switch-row { padding: var(--space-2) 0; justify-content: flex-start; gap: var(--space-6); min-height: var(--control-height); font-size: var(--type-body); }

.profil-save-status { padding-top: 14px; border-top: 1px solid var(--border); font-size: 12px; color: var(--ink-muted); }
.profil-save-status::before { content: '✓'; margin-right: 8px; color: var(--ok); }
.profil-fields .profil-field-error { color: var(--danger); }
.profil-fields input[aria-invalid='true'] { border-color: var(--danger); }
/* As many columns as the card holds: eleven switches are a list to scan, not
   a page to scroll. */
#pane-profil .profil-switches { grid-template-columns: repeat(auto-fill, minmax(min(calc(var(--control-height) * 6), 100%), 1fr)); gap: 0 24px; }
#pane-profil .switch-row { min-height: var(--control-height); padding: var(--space-4) 0; border-bottom: 1px solid var(--border); gap: 12px; }
#pane-profil .switch-title { flex: 1; margin: 0; font-size: 13px; line-height: 1.45; cursor: pointer; }
#pane-profil .switch { flex-shrink: 0; }
.profil-email-prefs { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; }
.profil-email-prefs .btn { min-height: 44px; }
#pane-profil .profil-empty { gap: 20px; padding: 18px; background: var(--surface-inset); border: 1px solid var(--border); }
#pane-profil .profil-empty-text { max-width: 48ch; line-height: 1.6; }
#pane-profil .profil-empty .btn { flex-shrink: 0; min-height: 44px; white-space: normal; }
#pane-profil .profil-doc-chips { gap: 8px; margin: 0; }
#pane-profil .profil-doc-chips .chip { min-height: 42px; white-space: normal; text-align: left; }
#pane-profil .my-offers { margin: 0; }
#pane-profil .profil-referral { border-color: var(--border-strong); }
.profil-rewards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); border: 1px solid var(--border-strong); background: var(--brand-tint-solid); }
.profil-reward { display: grid; gap: 8px; padding: 20px; min-width: 0; }
.profil-reward + .profil-reward { border-left: 1px solid var(--border-strong); background: var(--brand-tint-solid-strong); }
.profil-reward-label { font-size: 12px; font-weight: 600; color: var(--ink-muted); }
.profil-reward-amount { font-family: var(--font-mono); font-size: var(--type-h2); line-height: 1.2; letter-spacing: -.04em; color: var(--brand-on-surface); }
.profil-reward .help { font-size: 12px; line-height: 1.5; }
.profil-referral .parr-code-row { flex-wrap: wrap; gap: 8px 14px; }
.profil-referral .parr-code { font-size: 24px; overflow-wrap: anywhere; }
.profil-status { display: inline-flex; align-items: center; gap: 6px; padding: 4px 8px; width: fit-content; font-size: 11px; font-weight: 700; color: var(--ink); background: var(--surface-inset); border: 1px solid var(--border); }
.profil-status::before { content: ''; width: 5px; height: 5px; background: currentColor; border-radius: 50%; }
.profil-referral .partner-link-row { margin: 0; gap: 10px; }
.profil-referral .partner-link-row code { flex: 1 1 100%; min-width: 0; padding: 14px 16px; font-size: 14px; user-select: all; }
.profil-referral .btn { min-height: 44px; }
/* The referral message, read in its modal: the message itself in ink. */
#profil-message-dialog .partner-msg-text { margin: 0 0 var(--space-8); font-size: 14px; line-height: 1.65; color: var(--ink); }
.profil-referral-followup { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; }
/* The address and its « i » take the row; « Voir le programme » closes it. */
.profil-referral-followup > .tip-line { flex: 1 1 180px; }
.profil-referral-email { font-size: 13px; overflow-wrap: anywhere; color: var(--ink-muted); }

@media (max-width: 900px) {
  .profil-layout { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  .profil-nav { position: static; flex-direction: row; flex-wrap: wrap; gap: 6px; padding-bottom: 18px; border-bottom: 1px solid var(--border); }
  .profil-nav > .eyebrow { flex-basis: 100%; margin: 0 0 6px; }
  .profil-nav-item { width: auto; min-height: 44px; padding: 6px 10px; border-color: var(--border); background: var(--surface-inset); font-size: 12px; }
  .profil-nav-item .profil-card-ic { width: 18px; height: 18px; }
  .profil-nav .account-tour-replay { margin: 0; min-height: 44px; }
}
@media (max-width: 560px) {
  #pane-profil .profil-intro { margin-bottom: 20px; }
  #pane-profil .profil-card { gap: 18px; padding: 18px 16px; }
  #pane-profil .profil-fields, #pane-profil .profil-switches { grid-template-columns: minmax(0, 1fr); }
  #pane-profil .profil-empty { flex-direction: column; align-items: stretch; padding: 16px; }
  #pane-profil .profil-card-head { grid-template-columns: 30px minmax(0, 1fr); gap: 8px 10px; }
  #pane-profil .profil-card-head > .profil-card-ic { width: 30px; height: 30px; grid-row: 1; }
  #pane-profil .profil-card-sub { grid-column: 1 / -1; }
  .profil-reward { padding: 14px 12px; }
  .profil-email-prefs .btn { width: 100%; }
  .profil-referral .partner-link-row .btn { flex: 1; }
  .profil-referral .partner-link-row code { font-size: 12px; }
}

/* Calm depth and responsive feedback across the public experience.
   Only the panel paint is translucent; readable text and inputs stay solid. */
.nc-card, #pane-profil .profil-card {
  box-shadow: var(--shadow);
  transition: border-color var(--motion-ui) var(--ease-ui), box-shadow var(--motion-ui) var(--ease-ui);
}
#pane-profil .profil-card:focus-within { border-color: var(--border-strong); box-shadow: var(--shadow-lg); }
h1, h2, h3 { text-wrap: balance; }
@media (prefers-reduced-motion: no-preference) {
  .btn, .sub-btn, .profil-nav-item {
    transition: background-color var(--motion-fast) var(--ease-ui), border-color var(--motion-fast) var(--ease-ui),
      color var(--motion-fast) var(--ease-ui), box-shadow var(--motion-ui) var(--ease-ui), transform var(--motion-fast) var(--ease-ui);
  }
  #pane-profil.is-active .profil-card { animation: heroIn var(--motion-enter) var(--ease-ui) both; }
  #pane-profil.is-active .profil-card:nth-child(2) { animation-delay: 50ms; }
  #pane-profil.is-active .profil-card:nth-child(3) { animation-delay: 100ms; }
}

/* Calendar controls and dates share the hero’s left rail. */
#carnet-panel :is(.cal-toolbar, .cal-grid, .legend) { padding-inline: 0; }
#carnet-panel .legend { padding-left: var(--space-3); }

/* ==========================================================================
   Speed-dial FAB: one button expands to reveal guide and chat
   ========================================================================== */
.speed-dial {
  --action-accent: var(--accent-blue);
  --action-ink-mix: 60%;
  position: fixed;
  bottom: var(--help-edge-gap);
  right: var(--help-edge-gap);
  z-index: 50;
  display: flex;
  flex-direction: column-reverse;
  align-items: flex-end;
  gap: var(--space-5);
}
body:has(#pane-notaires:not([hidden])) .speed-dial { --action-accent: var(--accent-purple); }
body:has(#pane-partenaires:not([hidden])) .speed-dial { --action-accent: var(--accent-gold); --action-ink-mix: 40%; }

.speed-dial-main {
  position: relative;
  width: auto; /* .icon-btn fixe 34 px : le mot « Aide » doit tenir */
  min-width: var(--help-control-size);
  height: var(--help-control-size);
  padding: 0 var(--space-5);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  cursor: pointer;
  transition: background var(--motion-fast) var(--ease-ui), color var(--motion-fast) var(--ease-ui), border-color var(--motion-fast) var(--ease-ui), transform var(--motion-ui) var(--ease-spring);
  box-shadow: var(--shadow-sm);
  z-index: 2;
}

/* Le mot visible : un « ? » seul ne dit pas « aide » à tout le monde. */
.speed-dial-main-label {
  font-size: var(--type-h4);
  font-weight: 700;
  letter-spacing: .01em;
}

.speed-dial-main svg {
  width: 26px;
  height: 26px;
  flex: none;
  overflow: visible;
}

/* « ? » au repos, « ✕ » ouvert : le même dessin tourne et se remplace. */
.speed-dial-main :is(.sd-glyph-q, .sd-glyph-x) {
  transform-box: fill-box;
  transform-origin: center;
  transition: opacity var(--motion-fast) var(--ease-ui), transform var(--motion-ui) var(--ease-spring);
}
.speed-dial-main .sd-glyph-x { opacity: 0; transform: rotate(-90deg) scale(.6); }
.speed-dial-main[aria-expanded="true"] .sd-glyph-q { opacity: 0; transform: rotate(90deg) scale(.6); }
.speed-dial-main[aria-expanded="true"] .sd-glyph-x { opacity: 1; transform: none; }

.speed-dial-main:is(:hover, :focus-visible) {
  background: var(--action-tint);
  color: var(--action-ink);
  border-color: var(--action-ink);
}
.speed-dial-main:active { transform: scale(var(--help-press)); }

.speed-dial-main[aria-expanded="true"] {
  background: var(--action-tint);
  color: var(--action-ink);
  border-color: var(--action-ink);
}

.speed-dial-options {
  position: absolute;
  bottom: calc(100% + var(--space-5));
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2);
  min-width: 180px;
  max-width: calc(100vw - 2 * var(--space-8));
  padding: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.speed-dial-options[hidden] {
  display: none;
}

.speed-dial-option {
  width: 100%;
  min-height: 44px;
  padding: var(--space-4) var(--space-6);
  gap: var(--space-6);
  border-radius: var(--radius);
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit; /* Inter, not the browser's Arial (audit 2026-09-22) */
  display: flex;
  align-items: center;
  justify-content: flex-start;
  cursor: pointer;
  transition: background var(--motion-fast) var(--ease-ui), color var(--motion-fast) var(--ease-ui);
  position: relative;
}

.speed-dial-option:hover, .speed-dial-option:focus-visible {
  background: var(--action-tint);
  color: var(--action-ink);
}
.speed-dial-main:focus-visible { outline: 2px solid var(--ring); outline-offset: 3px; }
.speed-dial-option:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.speed-dial-option svg { flex: none; color: currentColor; }

.speed-dial-label {
  white-space: nowrap;
  font-size: var(--type-h4);
  font-weight: 600;
  color: inherit;
}

/* Stagger animation for options appearing */
.speed-dial-options:not([hidden]) .speed-dial-option {
  animation: speed-dial-in var(--motion-fast) var(--ease-ui) both;
}
.speed-dial-options:not([hidden]) .speed-dial-option:nth-child(1) { animation-delay: 0s; }
.speed-dial-options:not([hidden]) .speed-dial-option:nth-child(2) { animation-delay: 0.05s; }
.speed-dial-options:not([hidden]) .speed-dial-option:nth-child(3) { animation-delay: 0.1s; }

@keyframes speed-dial-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (max-width: 768px) {
  :root { --help-control-size: 48px; --help-edge-gap: 16px; }
}
@media (max-width: 1023.98px) {
  .speed-dial-main { padding: 0; }
  .speed-dial-main-label { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .speed-dial-main, .speed-dial-option, .speed-dial-main :is(.sd-glyph-q, .sd-glyph-x) { transition: none; }
  .speed-dial-options:not([hidden]) .speed-dial-option { animation: none; }
}

/* ==========================================================================
   AI training disclosure
   ========================================================================== */

/* At the page end, the floating launcher has its own clear strip.
   Standalone pages need no reserve. */
body:has(> .speed-dial) > .site-footer { padding-bottom: calc(var(--help-control-size) + var(--help-edge-gap) + var(--space-8)); }

/* The homepage leaves a safe strip beside prices for its visible launcher.
   Help stays fixed on every pane; the footer reserve keeps its text clear. */
@media (max-width: 600px) {
  #pane-carnet .pulse-rows { padding-inline-end: calc(44px + var(--space-6)); }
}

/* Public hero entry variant: public-entry-actions.css; canonical outlines,
   intrinsic tracks and stable targets preserve the original audience hooks. */
/* Hero actions keep a stable hit target, including at the pointer's edge.
   Colour and shadow provide feedback without lifting the button away. */
.hero-cta > .btn { color: var(--action-ink); border: 2px solid var(--action-ink); background: transparent; font-weight: 700; font-size: 15px; min-height: 52px; height: auto; white-space: normal; text-align: center; padding: 12px 24px; transition: background var(--motion-fast), box-shadow var(--motion-fast); }
.hero-cta > .btn:not(:disabled):is(:hover, :focus-visible) { background: var(--action-tint); transform: none; box-shadow: var(--shadow-sm); }
/* La rangée des agendas tient la place de la rangée d'action : même air au-dessus
   que .hero-cta. */
.calendar-subscription { width: 100%; margin-top: var(--space-10); }
/* Le film joue à toute largeur : la carte se pose où la commande laisse de la
   place (app.js, `placeNote`), y compris sur un téléphone. L'ancien seuil de
   900 px était un jeton que plus rien ne lisait — retiré (règle 14). */
.arrival-showcase { position: fixed; inset: 0; z-index: var(--arrival-layer); pointer-events: none; }
.arrival-showcase[hidden], .arrival-frame[hidden], .arrival-cursor[hidden], .arrival-note[hidden] { display: none; }
#pane-carnet.is-active { animation-name: arrival-pane-in; }
@keyframes arrival-pane-in { from { opacity: 0; } to { opacity: 1; } }
.intro .experience-paths { animation-delay: 0s; }
/* The stop being presented (ADR 0075): the control keeps a quiet action
   outline, and a viewfinder FRAME — two corner brackets, the brand's
   chasing-corner grammar — draws itself over it and holds. The pointer ADR 0075
   retired came back with ADR 0077 (propriétaire, 2026-09-28): an arrow a little
   above the system cursor's size, with no halo and no pulse — the two features
   that made the old one read as a « generic AI demo cursor ». Outline,
   brackets and arrow are pointer-events: none; nothing on the page moves to
   make room for them. */
.arrival-frame {
  position: fixed; left: 0; top: 0; z-index: var(--arrival-layer-frame); pointer-events: none;
  width: var(--arrival-frame-w, 0); height: var(--arrival-frame-h, 0);
  transform: translate(var(--arrival-frame-x, 0), var(--arrival-frame-y, 0));
  color: var(--action-ink);
  animation: arrival-frame-in var(--arrival-enter) var(--ease-ui) both;
}
.arrival-frame::before, .arrival-frame::after {
  content: ''; position: absolute; width: var(--arrival-frame-corner); height: var(--arrival-frame-corner);
}
.arrival-frame::before { top: 0; left: 0; border-top: var(--arrival-frame-weight) solid currentColor; border-left: var(--arrival-frame-weight) solid currentColor; }
.arrival-frame::after { bottom: 0; right: 0; border-bottom: var(--arrival-frame-weight) solid currentColor; border-right: var(--arrival-frame-weight) solid currentColor; }
/* Drawn from a hair inside, then it breathes: the eye follows the frame and
   the arrow that rests on it, never a mark that beats on its own. */
@keyframes arrival-frame-in { from { opacity: 0; scale: .94; } to { opacity: 1; scale: 1; } }
.arrival-frame.is-held { animation: arrival-frame-in var(--arrival-enter) var(--ease-ui) both, arrival-frame-breathe var(--arrival-pulse) var(--ease-ui) var(--arrival-enter) infinite; }
@keyframes arrival-frame-breathe {
  0%, 100% { opacity: 1; }
  50% { opacity: .55; }
}
/* THE MOUSE (ADR 0077). A real arrow, `--arrival-cursor-size` tall — a little
   above the browser's own pointer, so the visitor reads it as the demo's hand
   and not as their own — whose tip rests on the control the frame brackets. It
   glides between stops inside the scene that holds them, then stays put: no
   halo, no pulse, no simulated press. `--arrival-cursor-ratio` is the arrow's
   own width over its height, so one token sizes the whole mark. */
.arrival-cursor {
  position: fixed; left: 0; top: 0; z-index: var(--arrival-layer-cursor); pointer-events: none;
  width: calc(var(--arrival-cursor-size) * var(--arrival-cursor-ratio));
  height: var(--arrival-cursor-size);
  transform: translate(var(--arrival-cursor-x, 0), var(--arrival-cursor-y, 0));
  color: var(--ink); opacity: 0;
  transition: transform var(--arrival-travel) var(--ease-ui), opacity var(--motion-ui) var(--ease-ui);
}
.arrival-cursor.is-live { opacity: 1; }
/* A film's first stop is placed, not flown in from the corner of the page. */
.arrival-cursor.is-placed { transition-property: opacity; }
.arrival-cursor svg {
  display: block; width: 100%; height: 100%; overflow: visible;
  fill: currentColor; stroke: var(--surface); stroke-width: var(--arrival-cursor-stroke); paint-order: stroke fill;
}
/* The control being presented: a square outline in the action colour. An
   outline, so nothing on the page moves to make room for it. */
.arrival-target {
  outline: 2px solid var(--action-ink); outline-offset: 4px; border-radius: var(--radius);
}
.experience-path.arrival-target { background: var(--action-tint-strong); }
/* A compact guide follows the active theme. Its accent identifies the coach;
   a white inverted rectangle must never flash over the night canvas. */
:root { --guide-caption-max: 24rem; --guide-border-width: 2px; --guide-travel: var(--space-4); }
.arrival-note {
  position: fixed; left: 0; top: 0; z-index: var(--arrival-layer-note); margin: 0;
  transform: translate(var(--arrival-note-x, 0), var(--arrival-note-y, 0));
  max-width: min(var(--guide-caption-max), calc(100vw - var(--space-12))); padding: var(--space-6) var(--space-8);
  background: var(--surface); color: var(--ink); border-radius: var(--radius);
  border: var(--guide-border-width) solid var(--border-strong); border-inline-start-color: var(--action-ink);
  font-size: var(--type-body); font-weight: 600; line-height: var(--type-body-lh); letter-spacing: var(--type-h4-ls);
  box-shadow: var(--shadow-lg); animation: arrival-note-in var(--arrival-enter) var(--ease-ui) both;
}
@keyframes arrival-note-in { from { opacity: 0; translate: 0 var(--guide-travel); } to { opacity: 1; translate: 0 0; } }
/* The replay link is a low-emphasis utility — styled as a ghost text action,
   not a button, so it never competes with the hero CTAs. */
.arrival-replay {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--control-height);
  margin-top: var(--space-5); padding: 4px 0;
  background: none; border: 0; color: var(--ink-muted); font: inherit; font-size: 13px;
  cursor: pointer; opacity: 0.7; transition: opacity var(--motion-fast), color var(--motion-fast);
}
.arrival-replay:hover { opacity: 1; color: var(--action-ink); }
.arrival-replay::before { content: '↺'; font-size: 15px; }
.nc-ai-consent-line > .goto-link { display: inline-flex; align-items: center; min-height: var(--control-height); }
/* Reduced motion: the overview never starts on its own (app.js). Asked for by
   name, it still answers — placed, not flown, and nothing breathes. */
@media (prefers-reduced-motion: reduce) {
  .arrival-frame, .arrival-frame.is-held { animation: none; }
  .arrival-cursor { transition: none !important; }
  .arrival-note { animation: none; }
}


#notary-live, #notary-open-list { scroll-margin-top: var(--sticky-top); }
.landing-copy > .hero-free { font-size: var(--type-h4); line-height: var(--type-body-lh); margin-top: var(--space-5); }
/* The pricing line is a reassurance, not fine print — give it a subtle
   left accent bar and slightly richer color so it reads at a glance. */
#pane-carnet .hero-free {
  border-left: 2px solid var(--brand-tint-solid-strong);
  padding-left: var(--space-5);
  color: var(--ink-muted);
  max-width: 52ch;
}
#pane-carnet .hero-free strong { color: var(--ink); }

/* Sign-up is also available within the shared sign-in sheet. */
@media (pointer: coarse) and (min-width: 900px) and (max-width: 999.98px) {
  #header-signup { display: none; }
}

/* The account menu chooses a retained case before opening its private chat. */
.nc-chat-case-list { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
.nc-chat-case-list .btn { width: 100%; min-height: var(--control-height); white-space: normal; }

/* Connected working files follow the dialog legibility floor (§8.5.3).
   Compact list kickers stay compact; actual file content and actions do not. */
.nc-retained-workspace .nc-kv dd,
.my-offer-detail .dossier-name { font-size: var(--type-body); line-height: var(--type-body-lh); }
.nc-retained-workspace .nc-kv dt,
:is(.nc-retained-workspace, .my-offer-chat) .chat-empty,
:is(.nc-retained-workspace, .my-offer-chat) .chat-when,
:is(.nc-retained-workspace, .my-offer-chat) .chat-who,
:is(.nc-retained-workspace, .my-offer-chat) .chat-seen,
:is(.nc-retained-workspace, .my-offer-chat) .chat-error,
:is(.nc-retained-workspace, .my-offer-chat) .chat-count,
.my-offer-detail .dossier-missing,
.my-offer-detail .dossier-prep-count,
.my-offer-detail .dossier-item .help { font-size: var(--type-h4); line-height: var(--type-body-lh); }
:is(.nc-retained-workspace, .my-offer-chat) .chat-bubble { font-size: var(--type-body); line-height: var(--type-body-lh); padding: var(--space-4) var(--space-6); }
:is(.nc-retained-workspace, .my-offer-chat) .chat-input,
:is(.nc-retained-workspace, .my-offer-chat) .chat-send,
.nc-retained-workspace .btn { font-size: var(--type-body); min-height: var(--control-height); }
.my-offer-detail .dossier-name,
.nc-retained-workspace .nc-kv dd { overflow-wrap: anywhere; }

.nc-retained-summary .nc-client-mail,
.nc-retained-summary .nc-client-tel { min-height: var(--control-height); font-size: var(--type-body); }

.nc-retained-workspace .nc-service-detail,
:is(.nc-retained-workspace, .my-offer-chat) :is(.chat-doc-vide, .chat-doc-ferme) { font-size: var(--type-h4); line-height: var(--type-body-lh); }

.nc-retained-workspace .nc-complete .help,
.nc-retained-workspace .nc-complete-cap { font-size: var(--type-h4); line-height: var(--type-body-lh); }
.nc-retained-workspace .nc-actval { font-size: var(--type-body); min-height: var(--control-height); }

/* The customer’s connected file is a working surface, not a compact list row. */
.my-offer-detail td { font-size: var(--type-body); line-height: var(--type-body-lh); padding: var(--space-6) var(--space-7); }
.my-offer-detail .my-offer-contact-name,
.my-offer-detail .my-offer-next-v,
.my-offer-detail .my-offer-expiration,
.my-offer-detail .chat-doc-item-name { font-size: var(--type-body); line-height: var(--type-body-lh); overflow-wrap: anywhere; }
.my-offer-detail .chat-doc-free-lbl,
.my-offer-detail .chat-doc-meta,
.my-offer-detail .chat-doc-etat,
.my-offer-detail .my-offer-cnq-annuaire { font-size: var(--type-h4); line-height: var(--type-body-lh); }
/* The connected file reads at body scale, its compact actions included
   (brand guidelines § 4.4 item 22: actionable controls use canonical body
   type); the shared action pattern reads this alias for .btn-sm. */
.my-offer-detail { --action-compact-type: var(--type-body); }
.my-offer-detail .btn,
.my-offer-detail .my-offer-contact-row { min-height: var(--control-height); font-size: var(--type-body); max-width: 100%; white-space: normal; }
.my-offer-detail .my-offer-actions { gap: var(--space-4); margin-top: var(--space-6); align-items: stretch; }

/* The shared disclosure includes its trigger-to-panel gap in the hover area. */
.itip::before { content: ''; position: absolute; left: var(--itip-bridge-left, 0); right: var(--itip-bridge-right, 0); top: 100%; height: var(--space-6); display: none; }
.itip[data-side='top']::before { top: auto; bottom: 100%; }
@media (hover: hover) { .itip:hover::before { display: block; } }
.itip:has(:where(.itip-btn:focus-visible))::before, .itip[data-open='true']::before { display: block; }
.itip[data-dismissed='true']::before { display: none; }

/* A private callback does not publish or request contract/SMS/account consent. */
.day-book[data-intent='true'] > .legal-contract-gate,
.day-book[data-intent='true'] > .consent-line,
.day-book[data-intent='true'] > .book-options { display: none !important; }
