/* ==========================================================================
   Nota — design tokens and styles.
   Brand aligned with Tablix: hunter-green palette, Inter type, slate ink.
   Deviation from Tablix on purpose: squared corners (Tablix uses rounded-xl).
   All colors are tokens — no literal color values below the :root blocks.
   ========================================================================== */

:root {
  /* Fir green — the brand ramp (owner, 2026-09-01: « le vert est trop
     intense »). Rebuilt in OKLCH on ONE hue axis (150→158, a fir/sage green)
     instead of the old ramp's yellow-green 142 with a chroma spike of 0.179
     at step 500 — a near-gamut-edge kelly that burns on the dark canvas.
     Chroma is now capped at 0.092 (a 48 % cut) and PEAKS on steps 400/500,
     the two the single vivid accent (--brand-bright) resolves to; every
     other step is quieter still, so borders, status ink and bars read as
     ink-with-a-hue rather than paint. Lightness is monotonic, which the old
     ramp was not (400 and 500 sat 0.005 apart and differed only in chroma).
     Method: Radix's 12-step role split (1-2 backgrounds, 3-5 component
     fills, 6-8 borders, 9-10 solids, 11-12 text) mapped onto this 50-950
     scale; the cooler hue also pulls the green away from the warm alert
     ramp under deuteranopia (dE 6.6 -> 26.4 on the worst pair). */
  --hunter-50: #f0f8f1;
  --hunter-100: #ddeee0;
  --hunter-200: #bbd9c2;
  --hunter-300: #97c0a2;
  --hunter-400: #73b189;
  --hunter-500: #599a71;
  --hunter-600: #467759;
  --hunter-700: #315b43;
  --hunter-800: #254633;
  --hunter-900: #183123;
  --hunter-950: #0b1911;
  /* 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. */
  --hunter-400-rgb: 115 177 137;
  --hunter-500-rgb: 89 154 113;
  --hunter-700-rgb: 49 91 67;

  /* Semantic tokens (light) */
  --brand: var(--hunter-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: #ffffff; /* le « G » doit rester sur fond blanc */
  --marque-facebook: #1877f2;
  --marque-linkedin: #0a66c2;
  --brand-bright: var(--hunter-500);
  --brand-ink: #ffffff;
  --brand-hover: var(--hunter-800);
  --on-accent: #ffffff; /* text/knob on saturated accents (tier pills, toggle) — white in every theme */

  --bg: #f0f2f1; /* neutral canvas with a whisper of the brand hue — panels float cleanly */
  /* A whisper off pure white (owner, 2026-08-27 evening: « the white is too
     intense ») — the faintest green cast keeps components clearly readable
     on the washed background without the clinical glare of #fff. */
  --surface: #fdfdfd;
  --surface-inset: #f3f5f4;
  --surface-hover: #e9edeb;
  --ink: #111614; /* hunter-black: the brand hue at near-zero chroma, not a blue slate */
  --ink-muted: #4f5e57; /* hunter-grey, AA (>=7:1) on every light surface */
  --border: #dde2df;
  --border-strong: #b9c2bc;
  --ring: var(--hunter-600);

  --danger: #b42318;
  --warn: #b54708;
  --ok: var(--hunter-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-bright / --surface. */
  --brand-tint: rgb(var(--hunter-500-rgb) / 0.05); /* faint brand wash — background layers only */
  --brand-tint-mix: 6%;
  --brand-tint-solid: color-mix(in srgb, var(--brand-bright) 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-bright) 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 .pr-kicker / .pr-amount / .pr-step-ic never
     have to know which theme they are in. */
  --brand-on-tint-strong: var(--brand);
  /* The 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. */
  --surface-veil-mix: 95%;
  --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-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: #3f654d;
  --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 green; 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);

  /* Per-service categorical hues — deliberately distinct from the green brand
     and the green→red tier ramp; used only as calendar data-viz fills (the
     per-service mix bar + its legend). TWO acts of the financing family
     (ADR 0010): each act owns its token here, and every theme block below
     redefines BOTH — a service colour must never fall back to a light value
     in the dark themes. */
  --svc-refinancement: #0f766e; /* darker teal → AA (>=4.5:1) for the small per-service count text */
  --svc-financement: #4f46e5; /* indigo → AA on light surfaces; clearly apart from the teal sibling */

  /* 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(17 22 20 / 0.05);
  --shadow: 0 6px 20px -8px rgb(17 22 20 / 0.12);
  --shadow-lg: 0 24px 50px -16px rgb(var(--hunter-700-rgb) / 0.24);
  --shadow-brand: 0 6px 16px -6px rgb(var(--hunter-700-rgb) / 0.5); /* brand-primary hover lift */
  --logo-shadow: 0 1px 2px rgb(17 22 20 / 0.15);       /* header brand mark */
  --focus-ring: rgb(var(--hunter-500-rgb) / 0.3);      /* brand focus glow (3px) */
  --backdrop: rgb(13 18 15 / 0.55);                    /* dialog scrim */

  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, monospace;

  --maxw: 1600px; /* maximized width — fills wide screens, side padding via .wrap */
  --gap: 20px;
  /* 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;
  /* 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, --wash-glow behind sections,
     the #site-bg drift. 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);

  --header-h: 52px;   /* sticky .site-header height — thin band, submenus carry the rest */
  --sticky-top: 72px; /* header height + breathing gap — used for scroll-padding / scroll-margin */

  /* 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. */
  --logo-google-blue: #4285f4;
  --logo-google-green: #34a853;
  --logo-google-yellow: #fbbc05;
  --logo-google-red: #ea4335;
  --logo-outlook-blue: #0f6cbd;
  --logo-on-brand: #ffffff;
}

/* 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(--hunter-500);
    --brand-bright: var(--hunter-400);
    --brand-ink: #08130a;
    --brand-hover: var(--hunter-400);

    /* Lifted off true black (owner, 2026-08-27 evening: « the black on the
       dark theme is too intense ») — the whole dark ramp steps up one notch
       and stays NEUTRAL graphite (owner, 2026-08-28: « trop vert » — the
       green cast is gone; brand green lives on controls, not the canvas). */
    --bg: #0f110f;
    --surface: #171918;
    --surface-inset: #1d201e;
    --surface-hover: #232624; /* one notch down (2026-09-01): at #252827 the quieter --brand ink on a hovered row measured 4.45:1, just under AA */
    --ink: #f6f9f7;
    --ink-muted: #a8b1ab;
    --border: #2e332f;
    --border-strong: #474c48;
    --ring: var(--hunter-400);

    --danger: #f97066;
    --warn: #f79009;
    --ok: var(--hunter-500);
    --danger-solid: #b42318;
    --brand-tint: rgb(var(--hunter-400-rgb) / 0.06); /* softened three times (owner, 2026-08-28 « trop vert », 2026-09-01 « trop intense ») */
    --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);

    --tier-standard: #64748b;
    --tier-rapide: #3f654d;
    --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;
    --svc-refinancement: #6dc3b9;
    --svc-financement: #818cf8;

    /* Dark shadows are cast in black, not light ink, so they read on #0a0a0a. */
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.5);
    --shadow: 0 8px 24px -10px rgb(0 0 0 / 0.6);
    --shadow-lg: 0 20px 40px -12px rgb(0 0 0 / 0.6);
    --shadow-brand: 0 6px 16px -6px rgb(var(--hunter-400-rgb) / 0.4);
    --focus-ring: rgb(var(--hunter-400-rgb) / 0.45);
    color-scheme: dark;
  }
}

/* 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(--hunter-500);
  --brand-bright: var(--hunter-400);
  --brand-ink: #08130a;
  --brand-hover: var(--hunter-400);

  /* Lifted off true black (owner, 2026-08-27 evening: « the black on the
     dark theme is too intense ») — the whole dark ramp steps up one notch
     and stays NEUTRAL graphite (owner, 2026-08-28: « trop vert » — the
     green cast is gone; brand green lives on controls, not the canvas). */
  --bg: #0f110f;
  --surface: #171918;
  --surface-inset: #1d201e;
  --surface-hover: #232624; /* one notch down (2026-09-01): at #252827 the quieter --brand ink on a hovered row measured 4.45:1, just under AA */
  --ink: #f6f9f7;
  --ink-muted: #a8b1ab;
  --border: #2e332f;
  --border-strong: #474c48;
  --ring: var(--hunter-400);

  --danger: #f97066;
  --warn: #f79009;
  --ok: var(--hunter-500);
  --danger-solid: #b42318;
  --brand-tint: rgb(var(--hunter-400-rgb) / 0.06); /* softened three times (owner, 2026-08-28 « trop vert », 2026-09-01 « trop intense ») */
  --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);

  --tier-standard: #64748b;
  --tier-rapide: #3f654d;
  --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;
  --svc-refinancement: #6dc3b9;
  --svc-financement: #818cf8;

  /* Dark shadows are cast in black, not light ink, so they read on #0a0a0a. */
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.5);
  --shadow: 0 8px 24px -10px rgb(0 0 0 / 0.6);
  --shadow-lg: 0 20px 40px -12px rgb(0 0 0 / 0.6);
  --shadow-brand: 0 6px 16px -6px rgb(var(--hunter-400-rgb) / 0.4);
  --focus-ring: rgb(var(--hunter-400-rgb) / 0.45);
  color-scheme: dark;
}

* { 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 hunter-green 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(--bg);
  color: var(--ink);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  transition: background-color .25s ease, color .25s ease;
}

/* 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); }

h1, h2, h3 { letter-spacing: -0.02em; line-height: 1.15; margin: 0; }

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

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 clamp(16px, 4vw, 28px); }

/* --- 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(--surface);
  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);
}
.brand {
  display: flex; align-items: center; gap: 8px;
  font-weight: 800; font-size: 18px; letter-spacing: -0.03em; color: var(--ink);
}
.brand:hover { text-decoration: none; }
.brand-mark { width: 26px; height: 26px; display: grid; place-items: center; }
.brand-mark img { display: block; width: 26px; height: 26px; filter: drop-shadow(var(--logo-shadow)); }
.brand-word { font-weight: 800; }
.brand-sub {
  color: var(--brand); font-weight: 700; font-size: 10px; text-transform: uppercase;
  letter-spacing: .08em; padding: 2px 6px; background: var(--surface-inset); border-radius: var(--radius-sm);
}
.result-count { font-size: 12px; font-weight: 600; color: var(--ink-muted); white-space: nowrap; }
/* Next-availability cue in the calendar toolbar — a live green 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-bright); 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: 50%; 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.
   Three flat doors (ADR 0010 §2) — no submenu layer, so no wrappers either. */
.nav-tabs { display: flex; gap: 2px; flex: none; }
.nav-tab {
  appearance: none; border: 1px solid transparent; background: transparent;
  color: var(--ink-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;
  /* Same quiet colour ease as the language segment (.12s): the header's
     controls change state at one shared tempo. */
  transition: background-color .12s, color .12s, border-color .12s;
}
.nav-tab:hover { background: var(--surface-inset); color: var(--ink); }
.nav-tab[aria-selected='true'] {
  background: var(--surface-inset); color: var(--brand);
  border-color: var(--border);
}
/* Header compact band: between the drawer threshold (720) and a roomy desktop
   (900) the full control set — brand, two tabs, language, theme, both auth
   buttons, avatar — only 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: 720px) and (max-width: 899.98px) {
  .site-header .wrap { gap: 8px; }
  .brand-sub { 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; border: 1px solid transparent; 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 .12s, color .12s, border-color .12s;
}
.icon-btn:hover { background: var(--surface-inset); }
.icon-btn[aria-pressed='true'] { background: transparent; color: var(--brand); border-color: var(--brand); }
.icon-btn svg { display: block; }
.icon-btn:active { transform: scale(0.94); }
/* Header tool group: guide "?" · FR | EN · theme switch sit TOGETHER on one
   shared quiet track, every control the same 28px height (owner's ask,
   2026-08-26: « les mettre ensemble et même grosseur »). The track is a
   background, never a border; the segment's and the switch's own inset
   tracks blend into it, and thin hairlines SEGMENT the strip in three
   (same ask: guide | language | theme), each centred in the gap. */
.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);
}
/* The guide « ? » — its own standalone bubble (owner's asks, 2026-08-26 +
   2026-08-27: always one tap away so people understand, but NEVER inside a
   menu). Fixed bottom-right on every DESKTOP band, signed in or out — phones
   drop it (ADR 0022: it painted over the calendar tiles; the first-visit
   onboarding and the footer link stay the phone doors). z-index 50:
   over content and the sticky header (40), under the drawer scrim (65),
   toasts (60) and native dialogs (top layer). */
.guide-fab {
  position: fixed; right: 16px; bottom: 16px; z-index: 50;
  width: 42px; height: 42px;
  background: var(--surface); border: 1px solid var(--border-strong);
  color: var(--ink-muted); box-shadow: var(--shadow);
}
.guide-fab:hover { background: var(--surface-hover); color: var(--ink); box-shadow: var(--shadow-lg); }

/* 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 .12s, color .12s;
}
.mini-seg-btn:hover { color: var(--ink); }
.mini-seg-btn[aria-pressed='true'] {
  background: var(--surface); color: var(--brand);
  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; position: relative; display: inline-flex; align-items: center;
  flex: none; 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; margin: 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 .16s ease;
}
: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 .16s cubic-bezier(.2, .8, .2, 1) 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: 24px 14px; text-align: center; color: var(--ink-muted); font-size: 13px; }
.notif-item {
  position: relative; padding: 11px 34px 11px 14px; border-bottom: 1px solid var(--border); cursor: default;
  transition: background-color .12s;
}
/* Per-item dismiss — a quiet ✕ that firms up on hover/focus, sized like the
   calendar's cell chevron so small round actions stay one family. */
.notif-x {
  position: absolute; top: 8px; right: 6px; display: grid; place-items: center;
  width: 24px; height: 24px; padding: 0; appearance: none; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-muted); font-size: 11px; line-height: 1; cursor: pointer;
}
.notif-x:hover { background: var(--surface-hover); color: var(--ink); }
.notif-x:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.notif-item:last-child { border-bottom: 0; }
.notif-item[role='button'] { cursor: pointer; }
.notif-item[role='button']:hover { background: var(--surface-inset); }
.notif-item[role='button']:focus-visible { background: var(--surface-inset); outline: 2px solid var(--ring); outline-offset: -2px; }
.notif-item.is-unread { background: var(--surface-hover); }
.notif-item.is-unread .notif-title::before {
  content: ''; display: inline-block; width: 7px; height: 7px; margin-right: 7px;
  border-radius: 50%; background: var(--brand); vertical-align: middle;
}
.notif-title { font-size: 13px; font-weight: 600; color: var(--ink); }
.notif-body { font-size: 12px; color: var(--ink-muted); margin-top: 2px; }

/* 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(--brand); }
.acct-btn[aria-expanded='true'] { border-color: var(--brand); color: var(--brand); }
.acct-panel { padding: 6px; }
.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; }
.acct-item:hover { background: var(--surface-inset); }
.acct-item:focus-visible { background: var(--surface-inset); 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: 34px; height: 34px; border-radius: var(--radius); border: 1px solid var(--border); color: var(--brand); flex: none; }
.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. */
.acct-role { align-self: flex-start; margin-top: 4px; font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--brand); 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; }
/* 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: 6px 4px; }
.acct-panel .notif-head { padding: 6px 10px; border-bottom: 0; }
.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(--surface-inset); color: var(--ink); }
.acct-legal-link:focus-visible { background: var(--surface-inset); 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); }

/* --- Mobile navigation drawer (hamburger) -------------------------------- */
/* Phones only: the burger and its right-side drawer replace the inline tabs,
   the login button and the theme toggle below 720px (the same threshold where
   the hero stacks). Desktop and tablet never see either. */
.nav-burger { display: none; }
.mnav-scrim {
  position: fixed; inset: 0; z-index: 65; background: var(--backdrop);
  opacity: 0; pointer-events: none; transition: opacity .2s ease;
}
.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 .24s cubic-bezier(.2, .8, .2, 1), visibility 0s .24s;
}
.mnav.is-open { transform: none; visibility: visible; transition: transform .24s cubic-bezier(.2, .8, .2, 1); }
/* 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 { 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-title { font-weight: 800; font-size: 15px; letter-spacing: -0.01em; }
.mnav-group { display: flex; flex-direction: column; }
.mnav-link {
  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 .12s, color .12s;
}
.mnav-link:hover { background: var(--surface-inset); text-decoration: none; color: var(--ink); }
.mnav-link.is-on { background: var(--surface-inset); color: var(--brand); }
.mnav-link svg { flex: none; color: var(--ink-muted); }
.mnav-link.is-on svg { color: var(--brand); }
/* 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 .16s; }
.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(--surface-inset); text-decoration: none; color: var(--ink); }
.mnav-subitem:focus-visible { background: var(--surface-inset); 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); }

/* --- Sign-in / sign-up modal -------------------------------------------- */
.auth-modal { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); color: var(--ink); box-shadow: var(--shadow-lg); padding: 0; width: min(420px, 92vw); 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); }
.auth-title { font-size: 20px; font-weight: 800; letter-spacing: -0.02em; text-align: center; margin: 0; }
.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(--surface-hover); 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(--surface-hover); 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(--brand); cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.auth-switch-btn:hover { color: var(--brand-hover); }

/* --- 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); }

/* --- Portes à venir ------------------------------------------------------ */
.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 .12s, background .12s;
}
.auth-soc-btn:hover { border-color: var(--border-strong); background: var(--surface-inset); }
.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); }
/* « Bientôt » : le mot est sur le bouton, pas seulement dans une infobulle —
   la couleur seule ne dirait rien à qui ne la distingue pas. */
.auth-soc-soon {
  flex: none; font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em;
  padding: 2px 7px; border-radius: var(--radius-sm);
  color: var(--ink-muted); background: var(--surface-inset); border: 1px solid var(--border);
}
.auth-social-note { text-align: center; margin: 4px 0 0; }
/* Sous 420px la pastille volait la largeur du libellé, qui passait sur deux
   lignes pour les trois fournisseurs. Elle rétrécit — le mot reste lisible et
   reste DANS le bouton, ce qui est ce qui compte pour un lecteur d'écran. */
@media (max-width: 420px) {
  .auth-soc-btn { padding: 8px 10px; gap: 8px; }
  .auth-soc-t { font-size: 13px; }
  .auth-soc-soon { font-size: 9px; padding: 2px 5px; letter-spacing: .03em; }
}
/* 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);
}

/* --- First-visit onboarding guide --------------------------------------- */
/* Reuses the .auth-modal shell (card + backdrop); two views live in the DOM
   and are shown/hidden. Tokens only, so it tracks every theme automatically. */
/* 560px: wide enough for the week vignette's five columns to carry an act's
   name and the exact amount — "Refinancement" ellipsizes, still named by its
   glyph, colour and tooltip. The ≤380px container fallback covers phones,
   where 94vw wins. max-width included: the base `dialog` rule caps every
   dialog at 440px and would clamp it back. */
.onb-modal { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); color: var(--ink); box-shadow: var(--shadow-lg); padding: 0; width: min(560px, 94vw); max-width: 560px; max-height: 92vh; overflow: auto; }
.onb-body { padding: 30px 26px 24px; display: flex; flex-direction: column; }
.onb-title { font-size: 20px; font-weight: 800; letter-spacing: -0.02em; text-align: center; margin: 0; }
.onb-sub { font-size: 13.5px; color: var(--ink-muted); text-align: center; margin: 6px 0 18px; line-height: 1.5; }

/* VIEW 1 — two outline choice cards, side by side, stacking on narrow screens. */
.onb-choices { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.onb-choice { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; text-align: left; padding: 16px 15px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--ink); cursor: pointer; transition: border-color .12s, background .12s, transform .06s; }
.onb-choice:hover { border-color: var(--brand); background: var(--surface-inset); }
.onb-choice:active { transform: translateY(1px); }
.onb-choice-ic { display: grid; place-items: center; width: 40px; height: 40px; border-radius: var(--radius); background: var(--surface-inset); border: 1px solid var(--border); color: var(--brand); }
.onb-choice-t { font-size: 14.5px; font-weight: 700; letter-spacing: -0.01em; }
.onb-choice-d { font-size: 12.5px; color: var(--ink-muted); line-height: 1.45; }
.onb-choice-live { min-height: 1.5em; font-size: 12px; font-weight: 650; color: var(--brand); margin-top: 2px; }

/* VIEW 2 — a numbered steps list with a brand-outline number chip. */
.onb-steps-title { margin-top: 2px; }
.onb-steps { list-style: none; margin: 4px 0 20px; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.onb-step { display: flex; align-items: flex-start; gap: 12px; }
.onb-step-n { flex: none; width: 26px; height: 26px; display: grid; place-items: center; border: 1px solid var(--brand); border-radius: var(--radius-sm); color: var(--brand); font-size: 13px; font-weight: 700; }
.onb-step-body { flex: 1; min-width: 0; }
.onb-step-t { font-size: 14px; font-weight: 700; letter-spacing: -0.01em; }
.onb-step-d { font-size: 12.5px; color: var(--ink-muted); line-height: 1.45; margin-top: 2px; }
.onb-step-ic { flex: none; display: grid; place-items: center; color: var(--ink-muted); margin-top: 2px; }
.onb-cta { width: 100%; }
/* Where-am-I marker for the two-view flow. */
.onb-progress { font-size: 11px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; color: var(--ink-muted); text-align: center; margin: 0 0 16px; }
/* The low-commitment exit: real enough to see, quiet enough not to compete
   with the primary CTA above it. */
.onb-alt { width: 100%; margin-top: 10px; appearance: none; border: 1px solid var(--border); border-radius: var(--radius); background: transparent; color: var(--ink); cursor: pointer; padding: 10px 14px; font: inherit; font-size: 13.5px; font-weight: 600; transition: border-color .12s, background .12s; }
.onb-alt:hover { border-color: var(--brand); background: var(--surface-inset); }
.onb-back { align-self: flex-start; appearance: none; border: 0; background: transparent; color: var(--ink-muted); cursor: pointer; padding: 2px 0; margin-bottom: 8px; font: inherit; font-size: 12.5px; }
.onb-back:hover { color: var(--brand); }
.onb-skip { align-self: center; appearance: none; border: 0; background: transparent; color: var(--ink-muted); cursor: pointer; padding: 6px; margin-top: 12px; font: inherit; font-size: 12.5px; text-decoration: underline; }
.onb-skip:hover { color: var(--ink); }

@media (max-width: 420px) {
  .onb-choices { grid-template-columns: 1fr; }
}

/* --- Buttons ------------------------------------------------------------- */
.btn {
  appearance: none; font: inherit; font-weight: 500; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 6px 13px; border-radius: var(--radius);
  border: 1px solid var(--border); background: transparent; color: var(--ink);
  transition: background .12s, border-color .12s, transform .06s;
}
.btn:not(:disabled):hover { background: var(--surface-inset); }
.btn:not(:disabled):active { transform: translateY(1px); }
/* Primary action reads as primary through brand colour + weight, never a fill. */
.btn-primary { background: transparent; color: var(--brand); border-color: var(--brand); font-weight: 600; transition: background .12s, border-color .12s, transform .12s ease, box-shadow .12s ease; }
/* The primary action lifts on hover — the same shadow-brand cue the hero CTA
   already uses, so "this is the one" reads consistently across the app. */
.btn-primary:not(:disabled):hover { background: var(--surface-inset); border-color: var(--brand); transform: translateY(-1px); box-shadow: var(--shadow-brand); }
.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(--surface-inset); color: var(--ink); }
.btn-sm { padding: 5px 11px; font-size: 12.5px; }
.btn-lg { padding: 7px 14px; 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; border: 0; border-radius: var(--radius);
  background: none; color: var(--ink-muted); cursor: pointer;
  text-decoration: none; transition: background-color .12s ease, color .12s ease, transform .12s ease;
}
.mini-btn:hover { background: var(--surface-hover); color: var(--brand); text-decoration: none; }
/* Pressing compresses — the standard tactile cue for an icon-only control. */
.mini-btn:active { background: var(--surface-inset); 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): inset surface, muted ink, flat. */
.btn:disabled { cursor: not-allowed; background: var(--surface-inset); border-color: var(--border); color: var(--ink-muted); box-shadow: none; }
.btn svg { flex: none; }

/* Hero call-to-action — the buyer's first, obvious action */
/* margin-top:auto pins the buttons to the BOTTOM of the copy column, so the
   two hero columns end on the same line instead of one trailing off. */
.hero-cta { display: flex; align-items: center; gap: var(--space-5); flex-wrap: wrap; margin-top: auto; padding-top: var(--space-9); }
/* 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. */
@media (min-width: 681px) {
  .hero-cta .btn { height: 32px; padding-top: 0; padding-bottom: 0; }
}

/* Guided step badges (1 · 2 · 3) in the offer panel */
.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: 11px; 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: var(--intro-pad-top) 0 clamp(16px, 2.2vw, 26px); }
/* 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, 1fr) minmax(0, 1fr); gap: clamp(20px, 4vw, 48px); align-items: stretch; }
.intro-main { min-width: 0; display: flex; flex-direction: column; }
/* 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(--brand); 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 { min-width: 0; display: flex; flex-direction: column; }
.pulse-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.pulse .hero-steps-title { margin-bottom: 0; }
/* 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-3); align-content: start; }
/* The filter button takes the width; the book button sits at its right edge. */
.pulse-item { display: flex; align-items: center; gap: 2px; }
/* The ROW may shrink (min-width:0, else it pushed the book button past a
   320px viewport) — but the FIGURES may not: an amount that shrinks either
   clips or paints over its neighbour, and a price must always be whole. Their
   min-content width is the floor; the hero stacks (≤767.98) before any
   viewport gets narrow enough to challenge it. */
.pulse-item { min-width: 0; }
/* The right padding/margin grow in LOCKSTEP (34px = 2px gap + 30px button +
   its 8px overhang), so the figures stay put while the row's veil runs on
   under the book arrow: the hover card must contain the arrow it points to,
   not stop 10px short and leave it floating beside the card. */
.pulse-item .pulse-row { flex: 1; min-width: 0; padding-right: 34px; margin-right: -34px; }
.pulse-item .mini-btn { margin-right: -8px; }
/* The book action wakes with the row: brand colour plus a 2px nudge toward
   where the click leads. transform joins the mini-btn transition below. */
.pulse-item:hover .mini-reserver, .pulse-item:focus-within .mini-reserver { color: var(--brand); transform: translateX(2px); }
.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
     while the hover/active tint still reads as a full-width row. 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;
  transition: background-color .12s ease;
}
/* One veil for the whole gesture: the row and its book arrow are SIBLINGS, so
   a veil keyed on the row alone dropped the instant the pointer crossed onto
   the arrow — the item-level :hover holds it steady, and :focus-within gives
   the keyboard the same wash. The veil carries the register's blur (a veil is
   always paired with one), so the drifting marks behind read as light, not
   shapes. A retained row keeps its inset — the wash only visits the others. */
.pulse-item:hover .pulse-row:not(.is-on), .pulse-item:focus-within .pulse-row:not(.is-on), .pulse-row:hover {
  background: var(--surface-hover-veil);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.pulse-row.is-on { background: var(--surface-inset); }
.pulse-item:hover .pulse-svc, .pulse-item:focus-within .pulse-svc, .pulse-row.is-on .pulse-svc { color: var(--brand); }
.pulse-svc { grid-column: 1 / 3; grid-row: 1; display: flex; align-items: center; gap: 7px; min-width: 0; font-size: 14px; font-weight: 700; transition: color .12s ease; }
.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.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: 18px; 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; }
.pulse-fig-v { font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; }
.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: 6px; 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 .45s cubic-bezier(.22, 1, .36, 1); }
/* 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 { font-size: clamp(21px, 2.4vw, 28px); font-weight: 800; line-height: 1.15; margin-top: 4px; }
/* 68ch, not 60: the hero copy owns half the page, and the shorter measure left
   a visible blank gutter down its right edge. */
.intro p { color: var(--ink-muted); max-width: 68ch; margin: 8px 0 0; font-size: 14.5px; line-height: 1.5; }
/* 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: 12px; font-weight: 700; line-height: 1.5; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--brand); margin-bottom: 8px;
}


/* --- 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(--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(--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='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 ---------------------------------------------------------- */
/* The brand-styled dropdown drawn over a native <select> (enhanceSelect in
   app.js). The OS paints the native options popup with the OS appearance, not
   the page theme — a dark Nota page could open a white system menu — so the
   popup is ours to draw. The native control stays inside the wrapper, visually
   retired but still the form's source of truth. */
.nselect { position: relative; }
.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: 8px;
  width: 100%; min-width: 120px; padding: 8px 10px; text-align: left; cursor: pointer;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--ink);
  transition: border-color .15s, box-shadow .15s;
}
.nselect-btn:hover { border-color: var(--brand); }
.nselect-btn:focus-visible { border-color: var(--ring); outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }
/* The chevron is two border strokes, so it reads in the ink of every theme. */
.nselect-btn::after {
  content: ''; flex: none; width: 7px; height: 7px; margin-top: -3px;
  border-right: 2px solid var(--ink-muted); border-bottom: 2px solid var(--ink-muted);
  transform: rotate(45deg); transition: transform .15s;
}
.nselect.is-open .nselect-btn::after { transform: rotate(-135deg); margin-top: 3px; }
.nselect-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nselect-value.is-placeholder { color: var(--ink-muted); }
.nselect-list {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 40;
  margin: 0; padding: 4px; list-style: none; max-height: min(280px, 40vh); overflow: auto;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
}
.nselect-list[hidden] { display: none; }
.nselect-opt { padding: 7px 10px; border-radius: var(--radius-sm); cursor: pointer; font-size: 13.5px; color: var(--ink); }
.nselect-opt:hover, .nselect-opt.is-active { background: var(--surface-hover); }
.nselect-opt[aria-selected='true'] { color: var(--brand); font-weight: 600; }

/* --- Filter panel: hidden until opened from the toolbar ------------------ */
.filterbar {
  margin: 0 0 var(--space-3); padding: var(--pad-card); display: flex; flex-direction: column; gap: var(--space-5);
  background: var(--surface-inset); border: 1px solid var(--border); border-radius: var(--radius);
}
.filterbar[hidden] { display: none; }
.filterbar-row { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: center; }
.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 .15s, color .15s, border-color .15s, box-shadow .15s, transform .1s;
}
.chip:hover { border-color: var(--brand); color: var(--ink); transform: translateY(-1px); }
.chip:active { transform: translateY(0); }
.chip.is-on { background: transparent; border-color: var(--brand); color: var(--brand); 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 .15s, color .15s, box-shadow .15s, border-color .15s;
}
.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(--surface-hover); color: var(--brand); 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-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 .1s ease, box-shadow .1s ease, border-color .1s;
}
/* 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(--surface-hover); 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. No
   border, no prices, and 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; border-color: 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: blanked and inert, but kept in the grid so no row is lost. */
.cal-cell.is-past { background: transparent; border-color: transparent; cursor: default; pointer-events: none; box-shadow: none; }
.cal-cell.is-past .cal-daynum { color: var(--border-strong); }
.cal-cell.is-today { border-color: transparent; }
.cal-cell.is-selected { border-color: transparent; box-shadow: none; background: var(--surface-inset); }
.cal-cell:focus-visible { outline: none; border-color: var(--brand); 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 green rectangles. */
.cal-cell.has-bids { background: transparent; }
.cal-cell.has-bids:hover { background: var(--surface-hover); }
/* 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-ink); }
.cal-urgency[data-tier='urgence'] { color: var(--tier-urgence-ink); }
/* The steepest step is the one a client must not miss, so it is the only one
   that fills rather than tints. */
.cal-urgency[data-tier='extreme'] { color: var(--on-accent); background: var(--tier-extreme); padding: 0 var(--space-1); border-radius: var(--radius-sm); box-shadow: var(--shadow-sm); }
.legend-mult { font-weight: 800; }
.cal-cell[data-tier]::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm); 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: 4px; }
/* 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 gets the classic filled date-circle (a data indicator, not a button) —
   the clearest "you are here" affordance, matching the count badge's fill. */
.cal-cell.is-today .cal-daynum {
  align-self: flex-start; display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 5px; border-radius: var(--radius-sm);
  background: var(--brand); color: var(--on-accent); 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(--surface-hover); }
/* --- 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-bar > span, .pulse-item .mini-reserver, .cal-cell { transition: none; }
  .btn-primary:not(:disabled):hover, .cal-cell:hover { transform: none; }
  .pulse-item:hover .mini-reserver, .pulse-item:focus-within .mini-reserver { 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; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-muted); cursor: pointer;
  transition: transform .15s ease, background .12s ease, color .12s ease;
}
.cell-chevron:hover { background: var(--surface-inset); color: var(--ink); }
.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); }
/* 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: 11px; 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: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bid-sub { font-size: 12px; 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); }
.tag-anon { font-size: 11px; 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: 14px; font-weight: 700; margin-bottom: 8px; }
.help { font-size: 12px; color: var(--ink-muted); margin-top: 4px; }

/* --- 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], .onb-choice-live[hidden],
.sup-count[hidden], .sup-reply-courriel[hidden], .ct-count[hidden],
.day-market-line[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. */
/* Les deux qui sont des conteneurs flex gardent leur flex : en `block` leur
   contenu se recomposerait, et une ligne tenue changerait de hauteur — soit
   exactement ce qu'on est venu empêcher. */
.day-market-line[hidden], .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[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[data-tone='warn'] .itip-btn { color: var(--warn); }
.itip-btn:hover, .itip[data-open='true'] .itip-btn { color: var(--ink); background: var(--surface-hover); }
.itip-btn:focus-visible { outline: 2px solid var(--brand); 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% + 6px); left: 50%; transform: translateX(-50%);
  width: max-content; max-width: min(280px, calc(100vw - 16px));
  padding: 8px 10px; text-align: left;
  font-size: 12.5px; font-weight: 500; line-height: 1.5; color: var(--ink);
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  opacity: 0; visibility: hidden; pointer-events: none;
}
.itip[data-side='top'] > .itip-pop { top: auto; bottom: calc(100% + 6px); }
/* 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 { opacity: 1; visibility: visible; display: block; }
}
.itip-btn:focus-visible ~ .itip-pop,
.itip[data-open='true'] > .itip-pop { opacity: 1; visibility: visible; display: block; }
.itip[data-open='true'] > .itip-pop { pointer-events: auto; }
@media (prefers-reduced-motion: no-preference) {
  .itip-pop { transition: opacity .12s ease, visibility .12s; }
  /* 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: 11px; }
.tier-preview .tp-text { font-size: 12px; color: var(--ink-muted); }

.slider-row { display: flex; align-items: center; gap: 12px; }
input[type='range'] { flex: 1; 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(--brand); outline-offset: 1px; }
.amount-input-unit { font-size: 13px; 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: 11.5px; 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: 12.5px; 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: 11.5px; line-height: 1.5; }
.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: 12px; 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: 13px; 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: 14px; }
.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: 12.5px; font-weight: 600; line-height: 1.5; color: var(--ink); }
.devis-note { margin: 6px 0 0; font-size: 11.5px; 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 .12s; 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 .12s; }
.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; }
/* « 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) ---------------------------------------------- */
/* 2026-08-27 redesign: one washed hero band carries the pitch AND the two
   reward stats (the whole offer in a single glance), then how-it-works — a
   connected timeline — beside the claim form, sticky on desktop so the CTA
   rides along the story. Narrow screens stack hero → story → form. */
.pr-hero {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: clamp(var(--gap), 3vw, 48px); align-items: center;
  margin-top: var(--intro-pad-top);
  /* Vertical breathing only (owner, 2026-08-27: « move to left ») — no side
     padding, so the copy starts flush on the wrap's left edge exactly like
     the intro of every other pane. */
  padding: clamp(20px, 3vw, 40px) 0;
  border-radius: var(--radius-lg);
  /* No box at all (owner, 2026-08-27: « add some gradient… no corner…
     smooth for the eyes ») — no surface backing, no shadow, no seam. The
     band is only light: two brand-tint glows that fade to transparent well
     inside the bounds, so there is no edge for the eye to catch. Tokens
     only — the tint carries both themes. */
  background: var(--wash-glow);
}
/* Same voice as every other pane (owner, 2026-08-27: « même taille et même
   style que espace notaire ») — the headline and lede mirror .intro h1/.intro p
   exactly, so the three doors open on one consistent register. */
.pr-hero h1 { font-size: clamp(21px, 2.4vw, 28px); font-weight: 800; line-height: 1.15; margin-top: 4px; }
.pr-hero p { color: var(--ink-muted); max-width: 60ch; margin: 8px 0 0; font-size: 14.5px; line-height: 1.5; }
/* The hero's own door to the claim form — the one action the pane sells. */
.pr-hero-cta { margin-top: var(--space-8); align-self: flex-start; }
@media (max-width: 900px) {
  .pr-hero { grid-template-columns: minmax(0, 1fr); align-items: start; }
}
/* Two columns: the pitch on the left, the two reward cards AND the art. 33
   line stacked on the right — the note under the cards it qualifies, which is
   what its own comment below has always said.

   It has to be said explicitly. .pr-eligibility is a direct child of this
   grid, so auto-placement dropped it into a NEW ROW in the LEFT column: the
   note was orphaned bottom-left at a 52ch measure with ~1070px of empty page
   beside it, and the right column held nothing under the cards. `align-items:
   center` then floated the 217px card block inside a 457px row, for ~276px of
   dead space under the money (owner, 2026-09-04, at 1990px). The copy spans
   both rows because it is the tall one — the second pass grew it (audience
   row, moment line, CTA, estimator) — and `auto 1fr` keeps row 1 hugging the
   cards so the note lands right under them instead of drifting down with the
   copy. Explicit placement is scoped ABOVE the stack breakpoint: a
   `grid-column: 2` leaking into the one-column layout would invent a second
   column out of nothing. */
/* 2026-09-05 — l'estimateur passe SOUS les deux cartes qu'il calcule. Il
   était en bas de la colonne de gauche, laquelle courait 179 px plus bas que
   la droite : la moitié droite du héros finissait sur du vide, et la question
   « combien de clients par mois ? » était loin des deux montants qui lui
   répondent. Trois rangées : les cartes, l'estimateur, la mention d'article 33
   — la dernière absorbe ce qui reste plutôt que de le laisser sous la
   colonne. */
@media (min-width: 901px) {
  .pr-hero { align-items: start; grid-template-rows: auto auto 1fr; }
  .pr-hero-copy { grid-column: 1; grid-row: 1 / span 3; }
  .pr-rewards { grid-column: 2; grid-row: 1; }
  .pr-estimate { grid-column: 2; grid-row: 2 / span 2; }
  /* Devenu une case de la grille, il n'a plus à porter sa propre marge : elle
     s'ajoutait au `gap` et ouvrait 64 px de blanc entre deux blocs qui se
     suivent. */
  .pr-hero > .pr-estimate { margin-top: 0; }
}
/* Two tracks, one stat card each: kicker, big figure, when. The mechanics
   live in the timeline — never repeated per card. The notary track pays more
   and carries the highlight (the STRONGER brand tint — never a ring) — tokens
   only. */
.pr-rewards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); gap: var(--gap); }
/* Who may claim (art. 33 C. déont.), one quiet line under the two cards. */
.pr-eligibility { margin: var(--space-5) 0 0; max-width: 60ch; font-size: 12.5px; line-height: 1.5; color: var(--ink-muted); }
.pr-card {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-9) var(--space-10);
  border-radius: var(--radius-lg);
  /* Borderless (owner: « remove those big borders ») — the card reads as a
     plain island against the hero's wash, lifted only by its shadow. The FULL
     shadow token, not -sm: in light mode the card is white inside a white
     band, and the faint -sm float is all but invisible there.
     The shared 90 % skin (owner, 2026-08-27: « we can do like 95 or 90
     opacity ») lets the hero's wash seat the cards into the band instead of
     punching two hard islands out of it.
     BOTH cards pour brand tint (owner, 2026-08-27 soir: « these are looking
     weird » — on the dark themes the plain-surface sibling read as a black
     hole beside the tinted one, two unrelated slabs). The pair now shares
     one green register; the notaire highlight is carried by DEPTH of pour
     (the strong mix below), never by a different kind of skin. */
  background: color-mix(in srgb, var(--brand-tint-solid) var(--surface-veil-mix), transparent);
  box-shadow: var(--shadow);
  /* Blur what bleeds through: the drifting site-bg marks pass behind the
     cards, and unblurred they read as stains through the 90% skin. */
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  /* A plain stat (audit P1-3): the hero CTA is the one door to the claim
     form. A div that looked pressable was unreachable by keyboard and
     unnamed to a screen reader — no pointer, no lift, no corner arrow. */
}
.pr-card--notaire {
  /* The highlight is the tint alone, never a ring — the OPAQUE tint token,
     so the hero's glow can never bleed through the card. The STRONG pour:
     at the component mix the dark themes rendered this card as washed grey
     beside its --surface sibling (owner, 2026-08-27: « this background look
     weird ») — the deep mix keeps it unmistakably brand-green in both. Same
     90 % skin as its sibling, so the pair sits into the band on one finish. */
  background: color-mix(in srgb, var(--brand-tint-solid-strong) var(--surface-veil-mix), transparent);
}
/* The kicker is a label with intent, not a whisper: brand ink (the readable
   pairing .auth-soon already proves over the strong tint in both themes) and
   a wider track. The right padding keeps it clear of the corner « → » on the
   narrowest card. */
.pr-kicker { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .1em; color: var(--brand-on-tint-strong); padding-right: var(--space-12); }
/* The figure IS the card (owner, 2026-08-27: « do better for 50 and 250 ») —
   display size, dense tracking, on its own line. */
.pr-amount { margin-top: var(--space-2); font-size: clamp(36px, 3.6vw, 50px); font-weight: 800; letter-spacing: -0.035em; color: var(--brand-on-tint-strong); font-variant-numeric: tabular-nums; line-height: 1; }
/* The cadence tag — the one fact that separates the two offers (the 50 $
   repeats forever, the 250 $ pays once per referred notary), surfaced from
   the FAQ into the card. STACKED under the amount on both cards: the two
   tags differ in length, and an inline seat beside the figure fit one card
   but wrapped on the other — the pair is read line against line, so the
   anatomy must match. A quiet outlined chip: hairline brand mix, never a
   fill — over both tint depths a filled chip disappears on one card or
   shouts on the other. */
.pr-freq {
  align-self: flex-start;
  margin-bottom: var(--space-4);
  font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .07em;
  color: var(--brand);
  border: 1px solid color-mix(in srgb, var(--brand) 40%, transparent);
  border-radius: var(--radius-xs);
  padding: 3px 7px;
  white-space: nowrap;
}
/* Pinned to the card foot (margin-top auto in the flex column): the grid
   stretches the pair to one height, so the one-line and two-line bodies end
   on the same base line instead of leaving the shorter card bottom-empty.
   The hairline above it turns the card into header / figure / foot — the
   sentence stops floating loose under the big number. */
.pr-when {
  margin: auto 0 0; padding-top: var(--space-4);
  border-top: 1px solid color-mix(in srgb, var(--brand) 16%, transparent);
  font-size: 13px; color: var(--ink-muted); line-height: 1.45;
}

/* The reorganisation (owner, 2026-08-27, evening pass: « reorder the
   components »): the full-width steps band spread three short lines across
   the whole page and pushed the claim form below the fold. ONE grid now sits
   under the hero — the story column (steps, then FAQ, then the fine print)
   on the left, the claim form docked top-right like the notary gate, on
   screen from the first scroll. No sticky and no order swaps — phones read
   the markup as written: story first, the hero CTA jumping to the form. */
.pr-steps-band { container: prsteps / inline-size; }
.pr-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(min(340px, 100%), 440px);
  gap: clamp(var(--gap), 3vw, 56px); align-items: start; margin-top: var(--gap);
}
.pr-pitch { min-width: 0; container: prpitch / inline-size; }
/* The story column breathes on one beat: steps, FAQ, fine print. */
.pr-pitch .pr-faq { margin-top: var(--space-10); }
/* Top margin = the form panel's own top padding (--pad-card-lg), so « Comment
   ça marche » seats on the same line as « Réclamez votre code » across the
   grid — the text-to-text alignment every pane's two columns share. */
.pr-how-title { margin: var(--space-8) 0 var(--space-8); font-size: 18px; letter-spacing: -0.01em; }
.pr-form-panel {
  padding: var(--pad-card-lg); border: 0; scroll-margin-top: var(--sticky-top);
  /* The landing-panel register: the 90 % skin over the ambient glow. */
  background: var(--surface-veil);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.pr-form-panel input { width: 100%; }
.pr-form-panel .h-sub { margin-bottom: var(--space-8); font-size: 18px; letter-spacing: -0.01em; }
.pr-form-panel .form-row { margin-bottom: var(--space-7); }
.pr-form-panel span.lbl { display: block; font-size: 14px; font-weight: 700; margin-bottom: var(--space-4); }
@media (max-width: 900px) {
  .pr-grid { grid-template-columns: minmax(0, 1fr); }
}

/* The three steps as a connected timeline: glyph chips on a dotted spine —
   each chip drawn in the site's stroke idiom with its order riding the
   shoulder as a small square tag — a bold verb line plus its single caveat
   per step. */
.pr-steps { list-style: none; counter-reset: prstep; margin: 0; padding: 0; display: grid; gap: var(--space-10); }
.pr-steps li {
  counter-increment: prstep; position: relative;
  display: grid; grid-template-columns: 54px minmax(0, 1fr);
  grid-template-rows: auto auto; row-gap: 2px; align-content: start;
}
/* The chip takes the STRONG tint — on the drifting dark background the
   regular mix reads as an empty outline, and this row is the pitch. */
.pr-step-ic {
  grid-row: 1 / span 2; position: relative; width: 40px; height: 40px;
  border-radius: var(--radius);
  background: var(--brand-tint-solid-strong); border: 1px solid var(--brand-on-tint-strong);
  color: var(--brand-on-tint-strong); display: flex; align-items: center; justify-content: center;
}
.pr-step-ic svg { width: 20px; height: 20px; }
/* The order tag rides the chip's shoulder — solid brand, like the header's
   notification count, so the 1-2-3 pops instead of whispering. */
.pr-step-ic::after {
  content: counter(prstep); position: absolute; top: -6px; right: -6px;
  min-width: 16px; height: 16px; padding: 0 3px; border-radius: var(--radius-xs);
  background: var(--brand); color: var(--brand-ink);
  font-size: 10px; font-weight: 800; line-height: 16px; text-align: center;
}
.pr-steps li:not(:last-child)::after {
  content: ''; position: absolute; left: 19px; top: 46px; bottom: calc(-1 * var(--space-10) + 4px);
  border-left: 2px dotted var(--border-strong);
}
.pr-steps strong { font-size: 15px; letter-spacing: -0.01em; align-self: end; }
.pr-steps li > span:last-child { font-size: 13px; color: var(--ink-muted); line-height: 1.5; max-width: 46ch; }
/* The FAQ under the steps: the pre-claim questions in the shared .disclosure
   idiom, promoted from fine-print register to content register (the column's
   job is to sell the program, not to whisper) — sized like the steps' own
   verb + caveat pair. A grid so the wide reflow below only swaps the column
   count; align-items so an opened answer never stretches its row neighbour. */
/* UNE colonne, pleine largeur (2026-09-05). En deux colonnes, les deux
   réponses ouvertes tombaient du même côté et l'autre colonne restait à
   180 px : ~520 px de vide que rien ne pouvait combler, puisque la hauteur
   dépend de ce que le lecteur ouvre. Une colonne ne laisse aucun trou —
   ouvrir ne déplace que ce qui suit, ce qu'un dépliant est censé faire. */
.pr-faq { display: flex; flex-direction: column; gap: var(--space-4); }
.pr-faq-title { margin: 0 0 var(--space-2); font-size: 18px; letter-spacing: -0.01em; }
.pr-faq .disclosure { margin: 0; background: var(--surface); }
/* La question EST la ligne : le texte part toujours du même bord, le chevron
   ferme la rangée à droite, et la rangée entière est la cible. */
.pr-faq .disclosure { background: var(--surface); transition: border-color .15s, background .15s; }
.pr-faq .disclosure:hover { border-color: var(--border-strong); background: var(--surface-hover); }
.pr-faq .disclosure[open] { background: var(--surface-inset); }
.pr-faq .disclosure > summary {
  font-size: 14px; font-weight: 700; color: var(--ink);
  padding: var(--space-7) var(--space-8); gap: var(--space-5);
}
.pr-faq .disclosure > summary::before { order: 2; margin-left: auto; color: var(--ink-muted); }
/* Pleine largeur, mesure bornée : la réponse ne devient pas une longue ligne. */
.pr-faq .disclosure-body p { max-width: 78ch; font-size: 13px; color: var(--ink-muted); line-height: 1.5; }
/* An answer eases open instead of snapping (owner: « smooth for the eyes »).
   Progressive enhancement — browsers without interpolate-size /
   ::details-content keep the instant native toggle — and motion-guarded. */
@media (prefers-reduced-motion: no-preference) {
  .pr-faq .disclosure { interpolate-size: allow-keywords; }
  .pr-faq .disclosure::details-content {
    opacity: 0; block-size: 0; overflow-y: clip;
    transition: content-visibility .25s allow-discrete, opacity .25s ease, block-size .25s ease;
  }
  .pr-faq .disclosure[open]::details-content { opacity: 1; block-size: auto; }
}
/* When its band is wide enough the timeline rides ONE row (chips on top,
   no spine — sequence is carried by the order tags); narrow widths keep the
   stacked spine. Container queries, not the viewport, so each region judges
   its own room. The li rows come from the PARENT grid (subgrid), so the
   three verb lines share one baseline row and the three caveats another —
   a short step never lets its text drift down its stretched column.
   Browsers without subgrid keep per-item rows: top-aligned, still tidy.
   The FAQ column splits in two on the same logic. */
@container prsteps (min-width: 640px) {
  .pr-steps {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: auto auto auto;
    gap: var(--gap); row-gap: var(--space-2);
  }
  .pr-steps li {
    grid-template-columns: none; grid-row: span 3;
    grid-template-rows: subgrid; row-gap: 0; align-content: start;
  }
  .pr-step-ic { grid-row: 1; margin-bottom: var(--space-5); }
  /* The spine turns horizontal: a dotted run from each chip to the next,
     centred on the chips, so the row still reads as ONE timeline. */
  .pr-steps li:not(:last-child)::after {
    left: 52px; right: calc(-1 * var(--gap) + 12px); top: 19px; bottom: auto;
    border-left: 0; border-top: 2px dotted var(--border-strong);
  }
}
/* Inside this pane the guarantee is a quiet closing line, not another boxed
   card — the boxed .note voice stays for panes that need a warning register.
   Its measure is capped like the intro's: fine print that runs the pitch's
   full width is one more « longue ligne ». */
.pr-pitch .note { margin-top: var(--gap); border-left: 0; background: transparent; padding: 0; max-width: 92ch; }

/* 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(--border); border-radius: var(--radius); background: var(--surface-inset); }
.partner-success[hidden] { display: none; }
/* « Vous êtes… » (2026-09-03): the claim form's first question asked in the
   hero, so a courtier or an accountant recognises themself before anything
   scrolls. One tinted chip per profession — the picked one takes the strong
   tint like the step glyphs — and, under the row, that profession's own
   « moment »: the cue that turns a reader into a referrer. Tokens only. */
.pr-audience { margin-top: var(--space-7); }
.pr-audience-lbl { display: block; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .1em; color: var(--ink-muted); margin-bottom: var(--space-3); }
.pr-audience-row { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.pr-aud {
  appearance: none; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px;
  border-radius: var(--radius); border: 1px solid var(--border);
  background: color-mix(in srgb, var(--brand-tint-solid) var(--surface-veil-mix), transparent);
  color: var(--ink);
  transition: background .15s, color .15s, border-color .15s;
}
.pr-aud:hover { border-color: var(--brand); }
.pr-aud:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.pr-aud[aria-pressed='true'] { background: var(--brand-tint-solid-strong); border-color: var(--brand-on-tint-strong); color: var(--brand-on-tint-strong); }
.pr-aud-ic { display: inline-flex; width: 18px; height: 18px; color: var(--brand); }
.pr-aud-ic svg { width: 18px; height: 18px; }
.pr-aud[aria-pressed='true'] .pr-aud-ic { color: currentColor; }
/* The moment line keeps its height before a pick, so the CTA below never jumps. */
.pr-moment { margin: var(--space-4) 0 0; padding-left: var(--space-4); border-left: 3px solid var(--brand); font-size: 14px; line-height: 1.5; color: var(--ink); max-width: 60ch; min-height: 3em; }
/* The hero estimator (2026-09-03): one row under the CTA — the slider, then
   the sentence whose two numbers the domain fills. The yearly figure takes
   the brand ink like the reward amounts, at a smaller size; tokens only. */
.pr-estimate { margin-top: var(--space-8); max-width: 60ch; }
.pr-estimate-lbl { display: block; font-size: 13px; font-weight: 700; margin-bottom: var(--space-3); }
.pr-estimate-row { display: flex; align-items: center; gap: var(--space-5); flex-wrap: wrap; }
.pr-estimate-row input[type='range'] { flex: 1 1 140px; min-width: 120px; margin: 0; }
.pr-estimate-out { display: inline-flex; align-items: baseline; gap: var(--space-3); font-size: 14px; color: var(--ink-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pr-estimate-out b { color: var(--ink); font-weight: 800; }
.pr-estimate-year strong { font-size: 20px; font-weight: 800; letter-spacing: -0.02em; color: var(--brand); }
.pr-estimate-arrow { color: var(--border-strong); }
.pr-estimate-fine { margin: var(--space-3) 0 0; font-size: 12.5px; color: var(--ink-muted); line-height: 1.45; }
/* 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-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; }
.partner-link-row code { 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; }

/* --- 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: name + help 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 help text. */
.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 > .help { margin-bottom: 8px; }
.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)); } }
@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 .12s, background-color .12s;
}
.crit-sec-fold:hover { color: var(--ink); background: var(--surface-hover); }
.crit-sec-fold .crit-sec-chev { transition: transform .15s ease; }
.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: 15px; 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 .15s, border-color .15s, background .15s;
}
.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: color-mix(in srgb, var(--brand) 10%, 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: 12px; 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: color-mix(in srgb, var(--warn) 14%, transparent);
  font-size: 11.5px; 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: color-mix(in srgb, var(--brand) 12%, transparent); }
/* 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: 11px; 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: 12px; 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(--brand); 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(--brand); outline: none; }
/* Documents inside the profile: upload / remove / mark-validated. */
.profil-doc-chips { margin: 2px 0 6px; }
/* Flex (not grid) so the LAST row's cards grow to fill the width — no empty
   right-side slots when the document count doesn't divide evenly. */
.profil-doc-list { display: block; }
/* The checklist collapses behind its progress line. .doc-rows carries the wrap
   that .profil-doc-list used to, so rows still pack 2-up and 3-up as they widen. */
.doc-rows { display: flex; flex-wrap: wrap; gap: var(--space-5); align-items: stretch; padding-top: var(--space-5); }
.profil-doc-list .doc-row { flex: 1 1 300px; }
.doc-disclose { border: 0; }
/* The whole progress line is the control, so it gets the thumb-sized target and
   the chevron the rest of the app uses for the same bargain. */
.doc-summary {
  display: flex; align-items: center; gap: var(--space-5);
  min-height: 44px; padding: var(--space-2) 0; cursor: pointer; list-style: none;
  border-radius: var(--radius-sm);
}
.doc-summary::-webkit-details-marker { display: none; }
.doc-summary .doc-progress { flex: 1; margin-bottom: 0; }
.doc-summary::after {
  content: ''; flex: none; width: 9px; height: 9px; margin-right: var(--space-2);
  border-right: 2px solid var(--ink-muted); border-bottom: 2px solid var(--ink-muted);
  transform: rotate(45deg) translate(-2px, -2px); transition: transform .15s ease;
}
.doc-disclose[open] > .doc-summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.doc-summary:hover::after { border-color: var(--ink); }
.doc-summary:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }
/* Documents progress header: labelled bar, full-width above the checklist. */
.doc-progress { flex: 1 1 100%; display: flex; flex-direction: column; gap: 6px; margin-bottom: 2px; }
.doc-count { font-size: 12.5px; font-weight: 600; color: var(--ink-muted); }
.doc-count[data-complete='true'] { color: var(--ok); }
.doc-bar { height: 6px; background: var(--surface-inset); border-radius: var(--radius-xs); overflow: hidden; }
.doc-bar span { display: block; height: 100%; background: var(--brand-bright); border-radius: var(--radius-xs); transition: width .25s ease; }
.doc-progress[data-complete='true'] .doc-bar span { background: var(--ok); }
/* 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(--brand); 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; }
/* 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(--brand); 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. */
.my-offers-past { margin-top: 6px; }
.my-offers-past-sum { padding: 8px 0; font-size: 12.5px; color: var(--ink-muted); cursor: pointer; }
.my-offers-past tbody tr { color: var(--ink-muted); }

/* 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); } }
@media (pointer: coarse) { .my-offers-past-sum { min-height: 44px; display: flex; align-items: center; } }

/* 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; }
.profil-empty-copy .help { margin: 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-row { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; display: flex; flex-direction: column; gap: 5px; background: var(--surface); }
/* Push the control to the bottom so every card in a row ends on the same line,
   whatever the length of its help text. */
.doc-row > .file-field, .doc-row > input[type='text'] { margin-top: auto; }
.doc-row[data-done='true'] { border-color: var(--brand); }
.doc-row-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.doc-row-name { font-weight: 600; font-size: 13.5px; }
.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 green 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; }
.dossier-item .help { margin-top: 2px; }
.dossier-item input[type='text'] { width: 100%; margin-top: 8px; }
.dossier-item[data-done='true'] .help { display: none; }
.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 .15s, color .15s; }
.file-field:hover .file-cta { border-color: var(--brand); 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'], .doc-row[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: 1px solid var(--border); }
.section h2 { font-size: 22px; font-weight: 800; 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 { font-size: 16px; font-weight: 700; margin-bottom: 6px; }
.card p { color: var(--ink-muted); font-size: 14px; margin: 0; }
.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 { display: none; }
.disclosure > 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 .15s;
}
.disclosure[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; }

/* Structured footer: identity + contact on the left, two link columns beside
   it, and a base line (©, hosting note) under a hairline. Stacks on phones. */
.site-footer { border-top: 1px solid var(--border); padding: 28px 0 18px; color: var(--ink-muted); font-size: 13px; }
.site-footer a { color: var(--ink-muted); }
.site-footer a:hover { color: var(--brand); text-decoration: none; }
.footer-grid { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr); gap: 24px 32px; align-items: start; }
.footer-id { min-width: 0; }
.site-footer .footer-brand { color: var(--ink); font-weight: 700; }
.footer-tag { margin: 8px 0 0; max-width: 40ch; font-size: 12.5px; line-height: 1.55; }
.footer-col { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.footer-col-h { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ink); margin-bottom: 6px; }
.footer-col a { padding: 3px 0; }
/* A footer ACTION (the guide opens a dialog) dressed like its link neighbours. */
.footer-col .footer-link { appearance: none; background: none; border: 0; padding: 3px 0; font: inherit; color: var(--ink-muted); cursor: pointer; text-align: left; }
.footer-col .footer-link:hover { color: var(--brand); text-decoration: none; }
.footer-base { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; margin-top: 22px; padding-top: 14px; border-top: 1px solid var(--border); font-size: 12px; }
@media (max-width: 680px) {
  .footer-grid { grid-template-columns: 1fr 1fr; gap: 20px 24px; }
  .footer-id { grid-column: 1 / -1; }
}

/* --- Toast --------------------------------------------------------------- */
.toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%) translateY(20px);
  background: var(--ink); color: var(--bg); padding: 12px 18px; border-radius: var(--radius);
  box-shadow: var(--shadow-lg); opacity: 0; pointer-events: none; transition: opacity .18s, transform .18s;
  z-index: 60; font-size: 14px; font-weight: 600; max-width: 90vw;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* --- Dialog -------------------------------------------------------------- */
dialog {
  border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 0;
  background: var(--surface); color: var(--ink); max-width: 440px; width: 92vw; box-shadow: var(--shadow-lg);
}
/* 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: var(--backdrop); backdrop-filter: blur(2px); }
/* 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. */
.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: 0; 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; }
.dlg-x:hover { background: var(--surface-hover); color: var(--ink); }
/* 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; }
.dialog-pad { padding: 22px; }
.dialog-pad h3 { font-size: 18px; margin-bottom: 8px; }
.dialog-pad p { color: var(--ink-muted); font-size: 14px; margin: 0 0 16px; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* 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; }
#notary-profil .form-row { margin-bottom: var(--space-4); }
#nc-profil-form .btn { margin-top: var(--space-3); }

/* --- 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: var(--danger); border-color: var(--danger); color: var(--surface); }
.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); font-weight: 700; white-space: nowrap;
}
.my-offer-help:hover { background: var(--brand); color: var(--brand-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: 50%;
  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 .form-row { margin-bottom: var(--space-5); }
#contact-dialog .lbl { margin-bottom: var(--space-2); }
/* 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 .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 { max-width: min(94vw, clamp(760px, 78vw, 1180px)); width: 94vw; max-height: 90vh; overflow-y: auto; overscroll-behavior: contain; }
/* 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: var(--surface); display: flex; align-items: flex-start; gap: 12px; padding: var(--space-8) 64px var(--space-5) var(--space-10); border-bottom: 1px solid var(--border); }
.day-title { font-size: 20px; font-weight: 800; letter-spacing: -0.02em; }
.day-title::first-letter { text-transform: uppercase; }
.day-sub { font-size: 13px; 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-pick { margin-left: auto; display: flex; flex-direction: column; gap: 4px; flex: none; font-size: 11.5px; font-weight: 600; color: var(--ink-muted); }
.day-date-input { font: inherit; font-size: 13px; padding: 6px 8px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); }
.day-date-input:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
/* 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: 12px; 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: 12px; color: var(--ink-muted); margin: 2px 0 0; }
/* The live line flips green 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: 12px; 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);
  transition: background .15s, color .15s;
}
.day-beat:hover { background: var(--brand); color: var(--surface); }
/* 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.pulse { animation: day-best-in .25s ease-out; }
.amount-big.flash { animation: day-best-in .45s ease-out; }
@media (prefers-reduced-motion: reduce) { .day-value-v.pulse, .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: 12px; 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: 12px; 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: 12px; 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); 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; }
/* 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: 12px; 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[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: 11px; 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: var(--brand); color: var(--brand-ink); }
.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: var(--brand); color: var(--brand-ink); }
/* 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: var(--warn); color: var(--surface); }
.book-rail-b:focus-visible { outline: 2px solid var(--brand); 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 #offer-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 #offer-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 #offer-submit {
  background: transparent; border-color: var(--brand); color: var(--brand); font-weight: 700;
}
.book-nav .book-fwd:not(:disabled):hover, .book-nav #offer-submit:not(:disabled):hover {
  background: var(--brand-tint-solid); border-color: var(--brand);
}
.book-nav .book-fwd:disabled, .book-nav #offer-submit:disabled {
  background: var(--surface-inset); 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 .12s ease; }
.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; }
}
/* 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; }
.crit-flag input { margin-top: 3px; flex: none; accent-color: var(--brand); }
.crit-text { display: flex; flex-direction: column; gap: 1px; }
.crit-label { font-size: 13.5px; 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(--surface-hover); }
/* 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; }
  /* The financing group asks for the amount, then the context, then the
     lender in DOM order. Pin the two short fields to the first track so the
     wide context row cannot strand the lender below an empty half-card. */
  .crit-sec[data-groupe='pret'] .crit-row[data-crit='valeur_pret'] { grid-column: 1; grid-row: 1; }
  .crit-sec[data-groupe='pret'] .crit-row[data-crit='preteur'] { grid-column: 2; grid-row: 1; }
  .crit-sec[data-groupe='pret'] .crit-row[data-crit='contexte'] { grid-column: 1 / -1; grid-row: 2; }
  .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: 13px; }
  .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(--brand);
  box-shadow: inset 0 0 0 1px var(--brand);
  color: var(--brand); 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: 12.5px; font-weight: 600; color: var(--ink-muted); margin-top: 4px; }
.crit-urgence { margin-top: 4px; }
/* 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: 12px; 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: 13px; 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: 11px; 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: 14px; font-weight: 700; color: var(--ink); }
.book-opt { color: var(--ink-muted); font-weight: 600; font-size: 12px; }
/* Step 2's live tally — amber while answers are awaited, brand once complete. */
.crit-count { margin-left: auto; font-size: 11.5px; 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); }
@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;
  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);
  font-size: 13px; font-weight: 700; line-height: 1; color: var(--brand);
  opacity: 0; transform: scale(.7); transition: opacity .15s ease, transform .15s ease;
}
#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 .15s, border-color .15s;
}
#o-service-chips .chip:hover .chip-svc-badge { border-color: var(--brand); }
#o-service-chips .chip.is-on { background: var(--brand-tint-solid); }
#o-service-chips .chip.is-on .chip-svc-badge { background: var(--surface); border-color: var(--brand); }
#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 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-sub { margin-left: auto; padding-right: var(--space-6); }
  /* 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%); }
}
#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(auto-fit, minmax(220px, 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; }
/* 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: 16px; }
.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; }
.panel-narrow { max-width: 560px; margin: 18px 0 32px; }
/* Legal pages: the long-form list panel fills the width (its list is 2-column)
   so there is no blank right half. */
#pane-confidentialite .panel-narrow, #pane-conditions .panel-narrow, #pane-charte .panel-narrow { max-width: none; }
.h-sub { font-size: 16px; font-weight: 700; margin-bottom: 12px; }

/* --- Notary console ------------------------------------------------------ */
/* Two layouts on one pane, toggled by whether the sign-in gate is showing:
   • SIGNED-OUT → a two-column landing that FILLS the page width (value on the
     left, the tight sign-in on the right) so the pane no longer sits in a narrow
     strip with wide empty margins.
   • SIGNED-IN → the working console (demand lists + two-column prefs grid) needs
     room, so it uses a single wide column. */
#pane-notaires > .wrap { display: flex; flex-direction: column; }
/* Signed-out: the INVENTORY is the pitch. One line of copy, then the live open
   demands, with the sign-in sticky beside them — everything else (value grid,
   agenda band, guarantee) is supporting material, full width below the fold. */
/* The gate card seats ON the demand grid's own top line (owner's call,
   2026-08-27 evening: « Accédez aux demandes semble haut et pas aligné avec
   le reste » — superseding the morning's « must go up »). The hero owns the
   full first row; the right column then opens level with the first row of
   cards, edge to edge with them, so the gate reads as part of the inventory
   grid instead of floating alone beside the headline. */
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  /* Six rows: a two-column band (demands | gate + agenda), then two FULL-WIDTH
     lines — the compliance band, then the guarantee. Each column is sized by
     its own content and simply ends; whichever is shorter runs out against a
     band that spans the page, so a ragged bottom never reads as a hole.

     History, so this is not re-broken: the layout used to chase an exact
     bottom-to-bottom match between the two columns (five rows, the guarantee
     tucked into the left one) and the teaser was padded to twelve tiles to
     reach it. That balance held for exactly one content height — ADR 0033's
     compliance block landed INSIDE the gate card, the rail grew ~400px, and
     the content column opened a 586px hole (owner, 2026-09-03: « le UI UX
     semble brisé »). Nothing below now depends on how many demands the month
     holds, or on how tall the gate is. */
  /* 2026-09-05 — l'agenda descend SOUS la grille, dans la colonne de gauche.
     Sous la grille des demandes s'ouvrait un trou de 137 px pendant que la
     colonne de droite continuait : le contenu s'arrêtait, la page non. La
     colonne de gauche va maintenant jusqu'au bout de son propre contenu, et
     ce qui reste se retrouve sous la PORTE D'ACCÈS — sous une carte qui se
     termine par son bouton, du blanc se lit comme du calme, pas comme un
     oubli. */
  grid-template-areas:
    "hero       hero"
    "livehead   ."
    "live       console"
    "carnet     ."
    "conformite conformite"
    "guarantee  guarantee";
  column-gap: clamp(32px, 5vw, 80px);
  row-gap: 0;
  align-items: start;
}
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) .nc-landing-head { grid-area: livehead; margin-top: var(--space-2); }
/* A month with nothing open (renderNotaryLive hides the inventory): no header
   for an inventory that is not there, and no half-empty two-column spread —
   the pane folds into one centred column: gate, then agenda. */
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])):has(#notary-live[hidden]) {
  grid-template-columns: minmax(0, 520px);
  grid-template-areas: "hero" "console" "conformite" "carnet" "guarantee";
  justify-content: center;
}
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])):has(#notary-live[hidden]) .nc-landing-head { display: none; }
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])):has(#notary-live[hidden]) #notary-console { margin-top: var(--space-6); }
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) .intro--hero   { grid-area: hero; margin: 0; }
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) .intro-main     { max-width: none; }
/* Keep a readable measure on the lede without capping the whole hero block. */
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) .intro p { max-width: 78ch; }
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) .nc-live        { grid-area: live; align-self: start; }
/* L'agenda ferme la colonne de gauche, sous la grille des demandes — jamais
   une bande pleine largeur (retour du propriétaire, 2026-08-25 : à pleine
   largeur ce n'était que de l'air). Il garde la largeur de sa colonne. */
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) .notary-carnet  {
  grid-area: carnet; align-self: start;
  /* Sa propre bande, pas la queue de la grille des demandes : le même filet
     que « Conformité » emploie plus bas, donc la colonne se lit en trois
     temps — les demandes, l'agenda, la conformité. */
  margin-top: var(--space-16); padding-top: var(--space-12);
  border-top: 1px solid var(--border);
}
/* The console anchors to the TOP of the "live" row — no margin at all: its
   top edge sits exactly on the first demand cards' top edge (card aligned to
   cards, the strongest line on the page), and it scrolls with the page like
   every other card (owner's call, 2026-08-25: a pinned money panel followed
   the reader around). */
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) #notary-console {
  grid-area: console; align-self: start;
  margin: 0;
  /* The gate is the page's ONE door, seated beside a grid of money — on the
     plain 90 % skin it read as furniture (owner, 2026-08-27: « ça semble
     faible versus la partie à gauche »). It takes a brand pour: tint alone,
     never a ring, same 90 % skin — the REGULAR tint, not the strong pour
     (owner, 2026-08-27 soir: the deep green read too intense beside the
     grid). Signed in, the console is a workspace and stays fully opaque. */
  background: color-mix(in srgb, var(--brand-tint-solid) var(--surface-veil-mix), transparent);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
/* Conformité (ADR 0033 §5) — the compliance band: the three articles, run as
   one full-width row under the two-column band, the way a trust strip closes a
   marketplace landing. Inside the gate card it made the rail ~400px taller
   than the demand grid beside it; full width it is out of the balance entirely
   and its tiles get a readable measure instead of a 420px gutter. */
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) #nc-conformite {
  grid-area: conformite; align-self: start;
  max-width: none; margin-top: var(--space-10);
}
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) .nc-conformite-tiles {
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
}
/* The guarantee closes BOTH columns on one line — the page's last word, not a
   footnote hanging off the bottom of one column. */
#pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) .nota-guarantee { grid-area: guarantee; }
/* Hero collapses to a single column on THIS pane only (the client carnet hero
   keeps its two columns). */
#pane-notaires .intro--hero { display: block; }
/* SIGNED-IN: the pane is a workspace, not a pitch. The hero keeps only its
   heading (panes focus their h1 on navigation), the lede goes, and the
   prospecting band slides behind the console (flex order — the wrap is a flex
   column signed-in) so the open demands are the first thing on screen. */
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .intro--hero p { display: none; }
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .intro--hero { margin-bottom: 0; }
/* Signed-in, the headline is a room label, not a pitch: it shrinks and the
   intro's air tightens so the first CONFIRMABLE demand lands on the first
   screen at every width. */
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .intro--hero h1 { font-size: 18px; }
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .intro { padding-bottom: 6px; }
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .notary-carnet { order: 1; }
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .nota-guarantee { order: 2; }
/* Narrow screens: one column, in selling order — a line of pitch, the open
   demands, then the sign-in the cards funnel into; the rest follows.

   The threshold is 1000px, not 900: the rail is a fixed 420px, so under ~1000
   the content column falls below ~460px and the demand grid drops to ONE card
   per row — a tablet in portrait got a single file of tiles running six rows
   deep beside a short gate. One column is the honest layout there.

   2026-09-06 — cette colonne unique était plafonnée à 620 px et centrée : à
   900 px de fenêtre, l'espace notaire se lisait comme une bande étroite avec
   140 px de vide de chaque côté, sous un en-tête qui, lui, allait d'un bord à
   l'autre (retour du propriétaire : « la section notaire doit prendre toute la
   largeur »). La colonne prend maintenant toute la largeur de .wrap — les
   demandes gagnent une tuile par rangée au passage — et c'est le FORMULAIRE de
   la porte qui garde sa mesure lisible, centré dans sa carte. */
@media (max-width: 1000px) {
  #pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "hero" "livehead" "live" "console" "conformite" "carnet" "guarantee";
  }
  /* The two-column rule sizes this margin to seat the console on the hero
     H1's line. Stacked, the card steps down the page on the same --space-10
     beat as every other section instead. Longhand so the other three sides
     of the shorthand survive. */
  #pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) #notary-console { align-self: stretch; margin-top: var(--space-10); }
  /* La carte va d'un bord à l'autre ; le formulaire, non — 440 px collés à
     gauche dans une carte de 940 px se lirait comme un oubli. */
  #pane-notaires > .wrap:has(#notary-auth-form:not([hidden])) #notary-console .nc-gate { margin-inline: auto; }
}

/* 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. */
.nc-live[hidden] { display: none; }
.nc-live-head { display: flex; align-items: baseline; gap: var(--space-6); flex-wrap: wrap; margin-bottom: var(--space-5); }
.nc-live-h { font-size: 15px; font-weight: 800; letter-spacing: -0.01em; margin: 0; }
/* Signed-in the wrap is a flex column and the inventory is the console's own
   list — the landing header row has nothing to head. */
#pane-notaires > .wrap:has(#notary-authed:not([hidden])) .nc-landing-head { display: none; }
/* auto-rows 1fr: every row — including the lone "+N autres" overflow card on
   narrow grids — shares the same height, so no card breaks the row format.
   auto-FIT (not fill): a sparse month collapses the empty tracks, so three
   tiles widen into a full, deliberate row instead of huddling beside a void. */
.nc-live-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(215px, 100%), 1fr)); grid-auto-rows: 1fr; gap: var(--space-5); }
/* 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";
  align-content: start; row-gap: 6px; column-gap: 8px; min-width: 0;
  padding: var(--pad-card); text-align: left; font: inherit; color: var(--ink);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface-inset); cursor: pointer;
  transition: transform .14s, background-color .14s, border-color .14s, box-shadow .14s;
}
.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:hover { transform: translateY(-2px); background: var(--surface-hover); border-color: var(--border-strong); box-shadow: var(--shadow); }
.nc-live-card:hover .nc-live-amt { color: var(--brand); }
.nc-live-card:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
/* 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 .14s; }
.nc-live-meta { font-size: 12px; color: var(--ink-muted); }
/* Overflow card: "+N autres" — dashed, centred, brand-inked, same footprint.
   On hover the dashes resolve into a solid brand door with a faint wash — the
   card reads as the entrance it is, not a disabled placeholder. */
.nc-live-more {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center; gap: 4px;
  border-style: dashed; border-color: var(--border-strong); background: transparent; color: var(--brand);
}

/* 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-fit, minmax(min(150px, 100%), 1fr)); gap: var(--space-4); }
  .nc-live-card { padding: var(--space-5) var(--space-6); row-gap: 4px; }
  .nc-live-amt { font-size: 17px; }
  .nc-live-meta { font-size: 11px; }
}
.nc-live-more:hover { border-style: solid; border-color: var(--brand); background: var(--brand-tint-solid); }
.nc-live-more strong { font-size: 14px; font-weight: 800; }
.nc-live-more .nc-live-meta { color: inherit; }

/* 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); }

/* Week vignette — the marketplace played out with live data, in the onboarding
   dialog only. Real demands (D.weekAgenda) drop onto a Mon–Fri board one by one
   while the money counts up; the board then clears and replays the next batch.
   Borderless: the inset surface alone seats it. */
.nc-week[hidden] { display: none; }
.nc-week {
  /* ONE height for filled chips and empty ghost slots — a half-filled column
     must read as one grid, never as two misaligned ones. */
  --wk-cell-h: 46px;
  display: flex; flex-direction: column; gap: 12px;
  padding: var(--space-8) var(--space-9) 13px;
  border-radius: var(--radius);
  background: var(--surface-inset);
}
.nc-week-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.nc-week-kicker { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--brand); }
.nc-week-total { position: relative; display: inline-flex; align-items: baseline; gap: var(--space-3); font-size: 13px; color: var(--ink-muted); }
.nc-week-total strong { display: inline-block; color: var(--brand); font-weight: 800; font-variant-numeric: tabular-nums; }
/* A small pop each time money lands on the board. */
.nc-week-total strong.is-tick { animation: nc-week-tick .35s ease; }
@keyframes nc-week-tick { 40% { transform: scale(1.12); } }
/* The landed amount floats up off the counter and dissolves. Its whole travel
   (top offset + peak translate) stays under the box's 16px top padding, so the
   float never escapes the vignette onto whatever sits above it. */
.nc-week-delta {
  position: absolute; right: 100%; margin-right: var(--space-4); top: -3px;
  font-size: 11px; font-weight: 800; color: var(--brand);
  white-space: nowrap; opacity: 0; pointer-events: none;
}
.nc-week-delta.is-live { animation: nc-week-delta .8s ease forwards; }
@keyframes nc-week-delta {
  0% { opacity: 0; transform: translateY(4px); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-10px); }
}
/* The board leaves with a little weight, not a flat fade. */
.nc-week-board { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; transition: opacity .45s ease, transform .45s ease; }
.nc-week-board.is-out { opacity: 0; transform: translateY(6px) scale(.985); }
.nc-week-col { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.nc-week-day { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-muted); text-align: center; }
/* The day that just received a chip lights up for a beat. */
.nc-week-day.is-hit { animation: nc-week-day-hit .5s ease; }
@keyframes nc-week-day-hit { 30% { color: var(--brand); } }
/* Ghost slot: the empty half-hour a demand has not claimed (yet). */
.nc-week-slot { height: var(--wk-cell-h); border: 1px dashed var(--border); border-radius: var(--radius); }
/* A demand chip. `color` carries the act's --svc-* token (set inline) and the
   DOT says the service — the same visual grammar as the carnet's cells, where
   coloured borders belong to urgency, never to the act. */
.nc-week-chip {
  position: relative; height: var(--wk-cell-h); padding: 0 7px; min-width: 0;
  display: flex; flex-direction: column; justify-content: center; gap: 1px;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface);
  /* --tilt (set per chip by the builder) gives each drop its own slight lean,
     so eight landings read as eight objects instead of one stamped pattern. */
  opacity: 0; transform: translateY(-14px) scale(.88) rotate(var(--tilt, 0deg));
}
.nc-week-chip.is-in {
  opacity: 1; transform: none;
  transition: opacity .28s ease, transform .55s cubic-bezier(.18, 1.5, .35, 1);
}
.nc-week-chip.is-in:hover { transform: translateY(-1px); box-shadow: var(--shadow); transition: transform .15s ease, box-shadow .15s ease; }
.nc-week-chip-top { display: flex; align-items: center; gap: 4px; min-width: 0; }
.nc-week-chip-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
/* 10px + tight tracking: a short act name fits a five-column dialog cell
   whole; longer ones ("Refinancement") ellipsize, with the glyph, colour and
   tooltip still naming them. */
.nc-week-chip-name { font-size: 10px; letter-spacing: -0.01em; font-weight: 600; color: var(--ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nc-week-chip-amt { font-size: 12.5px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
/* A taken demand flips once the board settles: the chip calms down and its ✓
   appears — the marketplace clearing, shown rather than claimed. */
.nc-week-chip-check {
  position: absolute; top: 3px; right: 6px;
  font-size: 10px; font-weight: 800; color: var(--brand);
  opacity: 0; transform: scale(.4);
}
.nc-week-chip.is-taken { opacity: .72; transition: opacity .4s ease; animation: nc-week-take .45s ease; }
@keyframes nc-week-take { 35% { transform: scale(.96); } }
.nc-week-chip.is-taken .nc-week-chip-check {
  opacity: 1; transform: none;
  transition: opacity .3s ease, transform .45s cubic-bezier(.2, 1.4, .4, 1);
}
.nc-week-note { margin: 0; font-size: 11.5px; line-height: 1.5; color: var(--ink-muted); }
.nc-week-note[hidden] { display: none; }
.nc-week-check-key { color: var(--brand); font-weight: 800; }
@media (prefers-reduced-motion: reduce) {
  .nc-week-board { transition: none; transform: none; }
  .nc-week-chip, .nc-week-chip-check, .nc-week-chip.is-taken { opacity: 1; transform: none; transition: none; animation: none; }
  .nc-week-total strong.is-tick, .nc-week-delta.is-live, .nc-week-day.is-hit { animation: none; }
}
/* In the welcome dialog: sits under both views, left-aligned against the
   dialog's centred text. At dialog width the chip keeps the base two-line
   grammar — glyph + act name over the EXACT amount — so the board says what
   is sold and for how much without a hover. The vignette is a container so
   the chips re-lay by the DIALOG's width, not the viewport's. */
.nc-week--onb { margin-top: 18px; text-align: left; container: ncweek / inline-size; }
.nc-week--onb .nc-week-chip { overflow: hidden; }
/* Only a phone-narrow container trades words for the compact form: the name
   goes (the glyph + colour still say the act, the tooltip keeps the words),
   the amount swaps to the calendar's data-compact ("2,9k"), and the glyph
   stacks centred over it on a shorter cell — slots shrink with the chips
   through the shared --wk-cell-h, so the grid never splits in two. */
@container ncweek (max-width: 380px) {
  .nc-week--onb { --wk-cell-h: 38px; }
  .nc-week--onb .nc-week-board { gap: 5px; }
  .nc-week--onb .nc-week-chip { align-items: center; gap: 2px; padding: 0 4px; }
  .nc-week--onb .nc-week-chip-name { display: none; }
  .nc-week--onb .nc-week-chip-amt { font-size: 0; white-space: nowrap; }
  .nc-week--onb .nc-week-chip-amt::after { content: attr(data-compact); font-size: 10.5px; font-weight: 800; letter-spacing: -0.01em; }
}
/* Notary flavour only: the same board also lands in their agenda. Decorative
   (it lives inside the aria-hidden vignette) — brand marks + one muted line. */
.nc-week-providers { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; font-size: 11.5px; line-height: 1.5; color: var(--ink-muted); }
.nc-week-providers[hidden] { display: none; }
.nc-week-providers .brand-ic { flex: none; }
/* The three agenda marks file in one after the other when the row appears. */
.nc-week-providers.is-live .brand-ic { animation: nc-prov-in .45s ease backwards; }
.nc-week-providers.is-live .brand-ic:nth-of-type(2) { animation-delay: .12s; }
.nc-week-providers.is-live .brand-ic:nth-of-type(3) { animation-delay: .24s; }
@keyframes nc-prov-in { from { opacity: 0; transform: translateY(4px) scale(.8); } }

/* Bid vignette — the CLIENT onboarding step: ONE real demand played out end to
   end. The price climbs from the act's starting price to the offered amount,
   the demand is published au carnet, then a notaire retains it. Same inset
   surface as the week board; ob-bid-* names its own parts. */
.ob-bid[hidden] { display: none; }
.ob-bid {
  display: flex; flex-direction: column; gap: 12px;
  margin-top: 18px; padding: var(--space-8) var(--space-9) var(--space-7);
  border-radius: var(--radius);
  background: var(--surface-inset);
  text-align: left;
}
/* One bid's story fades out as a block before the next one begins. */
.ob-bid-scene { display: flex; flex-direction: column; gap: 10px; transition: opacity .45s ease; }
.ob-bid-scene.is-out { opacity: 0; }
.ob-bid-card {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  padding: 10px 12px;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface);
}
/* Each bid's card settles in fresh… */
.ob-bid-scene.is-live .ob-bid-card { animation: ob-bid-card-in .5s cubic-bezier(.2, 1.2, .4, 1) backwards; }
@keyframes ob-bid-card-in { from { opacity: 0; transform: translateY(8px) scale(.96); } }
/* …and rings once, the moment it lands au carnet. */
.ob-bid-card.is-pub { animation: ob-bid-pub .8s ease; }
@keyframes ob-bid-pub { 25% { border-color: var(--brand); box-shadow: 0 0 0 3px var(--ring); } }
/* `color` carries the act's --svc-* token (set inline); the glyph inherits it. */
.ob-bid-ic { flex: none; display: inline-flex; }
.ob-bid-main { flex: 1; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ob-bid-name { font-size: 12.5px; font-weight: 700; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ob-bid-date { font-size: 11px; color: var(--ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ob-bid-amt { flex: none; display: inline-block; font-size: 18px; font-weight: 800; color: var(--brand); font-variant-numeric: tabular-nums; }
.ob-bid-amt.is-tick { animation: nc-week-tick .35s ease; }
.ob-bid-stamps { display: flex; flex-direction: column; gap: 5px; }
/* Step stamps fade-and-rise in sequence: published, then retained/open. Each
   carries a dot that turns brand the moment its step is reached. */
/* flex-start + a nudged dot: when the stamp wraps to two lines on a narrow
   dialog, the dot stays with the FIRST line instead of floating between them. */
.ob-bid-stamp { display: flex; align-items: flex-start; gap: 7px; font-size: 11.5px; line-height: 1.5; color: var(--ink-muted); opacity: 0; transform: translateY(4px); }
.ob-bid-stamp::before { content: ''; flex: none; width: 5px; height: 5px; margin-top: 6px; border-radius: 50%; background: var(--border-strong); transition: background-color .3s ease; }
.ob-bid-stamp.is-on { opacity: 1; transform: none; transition: opacity .35s ease, transform .45s ease; }
.ob-bid-stamp.is-on::before { background: var(--brand); }
/* The closing stamp (retenue / ouverte) arrives with a touch more spring. */
#ob-bid-stamp-fin.is-on { animation: ob-bid-fin .5s cubic-bezier(.2, 1.3, .4, 1); }
@keyframes ob-bid-fin { from { transform: translateY(6px) scale(.97); } }
@media (prefers-reduced-motion: reduce) {
  .ob-bid-scene { transition: none; }
  .ob-bid-scene.is-out { opacity: 1; }
  .ob-bid-stamp { opacity: 1; transform: none; transition: none; }
  .ob-bid-scene.is-live .ob-bid-card, .ob-bid-card.is-pub, #ob-bid-stamp-fin.is-on,
  .ob-bid-amt.is-tick, .nc-week-providers.is-live .brand-ic { animation: none; }
}

#notary-console { margin-top: 12px; max-width: none; }
/* 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 { font-size: 18px; letter-spacing: -0.01em; margin-bottom: var(--space-8); }
/* Sign-in gate: ONE narrow column, shown as a solid card that anchors the right
   column of the landing (no bare floating field). The signed-in view keeps the
   same .panel .panel-pad card. */
.nc-gate { display: block; max-width: 440px; }
.nc-gate-main { min-width: 0; }
/* The gate's brand kicker — same grammar as the carnet card's « Votre outil
   de prospection », so the right column speaks with one voice. */
.nc-gate-kicker { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--brand); margin-bottom: var(--space-3); }
/* 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: 21px; 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: var(--surface); }
.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: var(--surface-inset); }
.nc-signup:focus { outline: 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: 15px; font-weight: 800; letter-spacing: -0.01em; 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 .14s, background-color .14s, border-color .14s, box-shadow .14s;
}
.nc-card:hover { transform: translateY(-1px); background: var(--surface-hover); 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; align-items: baseline; justify-content: space-between; gap: 12px; }
.nc-card-title { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.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; }
/* Progressive disclosure (ADR 0019): the open card's verbose half folds into
   .nc-card-body; the decision row (title, amount, pills, facts, Retenir)
   stays. Cards align-start so one unfolded card never stretches its row. */
#notary-open-list .nc-card { padding: 12px 14px; cursor: pointer; }
#notary-open-list .nc-agenda-grid { align-items: start; }
.nc-card-body { display: flex; flex-direction: column; gap: 2px; }
.nc-card-body[hidden] { display: none; }
#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; }

/* Signed in as a notary, the chrome drops the client doors: the console is
   the whole app, nothing in the header competes with the agenda (ADR 0019).
   Signing out restores the full menu. */
body.is-notary-session #tab-carnet,
body.is-notary-session #tab-partenaires,
body.is-notary-session .mnav-link[data-tab='carnet'],
body.is-notary-session .mnav-link[data-tab='partenaires'] { display: none; }
/* With its two siblings gone the strip holds ONE pressed tab — a dead
   button. Let it read as the place name instead: same selected chip, no
   hover invitation, no hand cursor. (Clicking a selected tab was already a
   no-op, so only the affordance changes.) */
body.is-notary-session #tab-notaires { pointer-events: none; }

/* --- 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-filter[hidden] { display: none; }
.nc-view-seg[hidden] { display: none; }
.nc-filter .chip-svc-sub { margin-left: 2px; }
.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 .14s, border-color .14s, box-shadow .14s, opacity .14s;
}
.nc-daytile:hover { background: var(--surface-hover); border-color: var(--border-strong); }
.nc-daytile.is-on { border-color: var(--brand); box-shadow: inset 0 0 0 1px var(--brand); background: var(--brand-tint-solid); }
/* 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(--brand); border-color: var(--brand); }
/* 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(--surface-hover); 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: block; 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(--surface-hover); }

/* Inline forms (proposition / documents) open under the card's actions. */
.nc-inline { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 8px; }
.nc-form-h { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--brand); }
.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; }
.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(--brand); margin-bottom: 8px; }
.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: 12px; 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. */
/* Alert preferences — settings, not the working surface: a collapsed panel
   below the money. The summary row is the whole control; a chevron flips open. */
.nc-prefs { margin: 18px 0 8px; padding: var(--space-6) var(--space-9); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-inset); }
.nc-prefs[open] { padding-bottom: 16px; }
.nc-prefs-sum { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; cursor: pointer; list-style: none; }
.nc-prefs-sum::-webkit-details-marker { display: none; }
.nc-prefs-sum::after { content: '▾'; margin-left: auto; color: var(--ink-muted); font-size: 12px; transition: transform .14s; }
.nc-prefs[open] .nc-prefs-sum::after { transform: rotate(180deg); }
.nc-prefs-sum .nc-h { margin: 0; }
.nc-prefs-hint { font-size: 12px; color: var(--ink-muted); }
.nc-prefs > .help { margin-top: 10px; }
/* « Vos évaluations » (ADR 0021): the notary's track record inside the same
   collapsed register. 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 {
  background: color-mix(in srgb, var(--brand) 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); }
/* « 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); }
.nc-release-form { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.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(--brand); }
.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); }
/* 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: 560px; }
.nc-retenir-act { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2) var(--space-5); margin-bottom: var(--space-6); }
.nc-retenir-svc { font-size: 15px; font-weight: 700; }
.nc-retenir-date { font-size: 13px; color: var(--ink-muted); }
.nc-retenir-date::first-letter { text-transform: uppercase; }
.nc-retenir-kv { margin: 0 0 var(--space-6); }
.nc-retenir-kv dd { line-height: 1.5; }
.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 { font-size: 12.5px; color: var(--danger); }
.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; color: var(--ink-muted); }
.nc-retenir .nc-retenir-contact { margin-bottom: var(--space-8); }
.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; }
.nc-retenir-go-amt { white-space: nowrap; }
.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: 34px; font-weight: 800; letter-spacing: -0.03em; line-height: 1.1; }
.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); 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-cote-next { margin: var(--space-6) 0 0; font-weight: 600; color: var(--ink); }
.nc-bareme { margin-top: var(--space-6); border-top: 1px solid var(--border); padding-top: var(--space-5); }
.nc-bareme-h { font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-muted); }
.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;
}
.nc-bareme-row.is-current { border-left-color: var(--brand); background: var(--brand-tint-solid); color: var(--ink); font-weight: 600; }

/* 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 {
  display: inline-block; 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);
}

/* Mark slots: they take no room until a mark lands in them. */
.nc-live-head:empty, .onb-live-host:empty { display: none; }
.nc-live-head, .onb-live-host { 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);
}

/* --- Privacy (Law 25) list ---------------------------------------------- */
/* Two-column on wide screens so the long-form list fills the panel width
   (readable measure per item) instead of leaving a big blank right side. */
.privacy-list { list-style: none; margin: 4px 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(330px, 100%), 1fr)); gap: 12px 32px; align-content: start; }
.privacy-list li { position: relative; padding-left: 20px; font-size: 14px; line-height: 1.5; color: var(--ink-muted); }
.privacy-list li strong { color: var(--ink); }
.privacy-list li::before {
  content: ''; position: absolute; left: 0; top: 8px; width: 8px; height: 8px;
  border-radius: var(--radius-xs); background: var(--brand);
}
/* The legal panes' version line: an unreviewed draft says so, and when it changed. */
.legal-stamp { margin: var(--space-4) 0 0; font-size: 12px; 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 .2s ease 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); }

.tab-pane { display: none; }
.tab-pane.is-active { display: block; animation: paneIn .22s ease both; }

/* ==========================================================================
   Motion — subtle micro-interactions. All disabled under reduced-motion.
   ========================================================================== */
@keyframes paneIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@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); } }
@keyframes sheetUp { from { transform: translateY(100%); } to { transform: translateY(0); } }

dialog[open] { animation: dialogIn .2s cubic-bezier(.2, .8, .2, 1) both; }
dialog[open]::backdrop { animation: backdropIn .2s ease both; }
.bid-row { animation: rowIn .2s ease both; }
.gauge-fill { transition: width .35s cubic-bezier(.2, .8, .2, 1); }
.dossier-bar > span { transition: width .35s cubic-bezier(.2, .8, .2, 1); }
.dossier-check { transition: background .15s, border-color .15s, transform .15s; }
.dossier-check[data-on='true'] { animation: pop .2s ease; }
.btn, .nav-tab, .icon-btn, .cal-cell, .pill { transition: background .14s, border-color .14s, color .14s, transform .1s, box-shadow .14s; }
.card { transition: transform .14s, box-shadow .14s, border-color .14s; }
.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(10px); } to { opacity: 1; transform: none; } }
@keyframes heroIn { from { opacity: 0; transform: translateY(12px); filter: blur(5px); } to { opacity: 1; transform: none; filter: none; } }
@keyframes cardPop { 0% { opacity: 0; transform: scale(.6); } 65% { opacity: 1; transform: scale(1.04); } 100% { opacity: 1; transform: none; } }
@keyframes amtGlow { 0%, 100% { text-shadow: none; } 50% { text-shadow: 0 0 12px color-mix(in srgb, var(--brand-bright) 45%, transparent); } }
.intro > *, .intro--hero .intro-main > * { animation: heroIn .5s cubic-bezier(.2, .7, .2, 1) 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 .5s cubic-bezier(.2, .9, .3, 1.3) .4s both; }
.pulse-rows > * { animation: fadeUp .38s cubic-bezier(.2, .8, .2, 1) 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 .42s cubic-bezier(.2, .8, .2, 1) 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; }
/* Partenaires: the two reward cards pop in turn, their amounts glow once. */
.pr-rewards .pr-card { animation: cardPop .45s cubic-bezier(.2, .9, .3, 1.3) .35s both; }
.pr-rewards .pr-card--notaire { animation-delay: .5s; }
.pr-amount { animation: amtGlow .9s ease 1s 2; }
.cards .card { animation: fadeUp .42s cubic-bezier(.2, .8, .2, 1) both; }
.cards .card:nth-child(2) { animation-delay: .08s; }
.cards .card:nth-child(3) { animation-delay: .16s; }
@media (prefers-reduced-motion: reduce) {
  .tab-pane.is-active, .intro > *, .intro--hero .intro-main > *, .intro--hero .pulse,
  .pulse-rows > *, .nc-live-grid > *, .pr-rewards .pr-card, .pr-amount, .cards .card { animation: none; }
}

/* More hover micro-interactions across client surfaces */
.bid-row { transition: background-color .14s ease; }
.bid-row:hover { background: var(--surface-inset); }
.dossier-item { transition: background-color .14s ease; }
.dossier-item:hover { background: var(--surface-inset); }
.legend-item { transition: color .14s ease; }
.brand-mark img { transition: transform .2s cubic-bezier(.2, .8, .2, 1); }
.brand:hover .brand-mark img { transform: rotate(-6deg) scale(1.06); }

/* Subscribe card — borderless floating panel (white surface, soft shadow):
   the "Le carnet dans votre agenda" band on the Notaires page. */
.sub-card {
  background: var(--surface-veil);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: var(--pad-card-lg);
}
.sub-card-head { display: flex; align-items: center; gap: 11px; }
.sub-card-logo { flex: none; display: grid; place-items: center; }
.sub-card-logo img { display: block; }
.sub-card-kicker { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--brand); margin-bottom: 3px; }
.sub-card-title { font-weight: 700; font-size: 13.5px; line-height: 1.2; }
.sub-card-sub { font-size: 12px; color: var(--ink-muted); margin-top: 2px; line-height: 1.45; }
.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; }
.sub-card-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.sub-card-actions .btn { flex: 1 1 auto; gap: 6px; padding-left: 10px; padding-right: 10px; }
.brand-ic { flex: none; display: block; }
/* Sits under the footer tagline now (no auto margin — the grid places it). */
.footer-contact { display: flex; align-items: center; gap: 2px; margin-top: 8px; }
.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. */
.notary-carnet { margin-top: 12px; }
/* 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 .2s; }
@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; }
}

/* ==========================================================================
   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: var(--space-7) 0 var(--space-6); }
}
/* 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. */
@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; }
  .pr-hero .eyebrow { display: none; }
  /* Side by side rather than stacked. */
  .hero-cta { flex-direction: row; }
  /* flex:1 alone still refuses to go under min-content, so two btn-lg wrapped
     to two rows and the pair cost 114px instead of 44px. */
  .hero-cta .btn { flex: 1 1 0; min-width: 0; }
  /* 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, named here by its brand edge. */
  .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; }
  /* Same selector strength as the desktop bleed rule (.pulse-item .pulse-row),
     later in the file — the -34px arrow overhang must not survive in a card. */
  .pulse-item .pulse-row { padding: 10px 12px; margin: 0; }
  .pulse-row {
    display: flex; flex-direction: column; align-items: stretch; gap: 7px;
    border: 1px solid var(--border);
  }
  .pulse-row.is-on { border-color: var(--brand); }
  .pulse-svc { font-size: 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: wrap; justify-content: space-between; align-items: baseline; gap: 2px 8px; }
  .pulse-fig-k { font-size: 10.5px; }
  .pulse-fig-v { font-size: 14px; }
  .pulse-bar, .pulse-meta { display: none; }
  /* The floating « Comment ça marche » bubble goes on phones (owner,
     2026-08-27: « remove this ») — it sat over the calendar tiles on the
     very screen the phone now centres on. The guide keeps its phone doors:
     the first-visit onboarding and the footer link. Desktop keeps the
     bubble, one tap from anywhere. */
  .guide-fab { display: none; }
}
/* The DRAWER threshold stays at 720: above it the header carries the full
   control set (compact band 720–899.98); below it everything hands off to the
   hamburger — the phone 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: 719.98px) {
  .nav-burger { display: grid; }
  .nav-tabs, .header-auth, #theme-toggle { display: none; }
  #lang-toggle { display: none; }
  /* Both controls moved to the drawer: an empty strip is not a control. */
  .header-tools { display: none; }
}
/* Above the drawer threshold the drawer can never appear, open class or not. */
@media (min-width: 720px) {
  .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; }

  /* Longhand: a shorthand would reset .layout's vertical padding to 0 and the
     carnet would butt against the hero and against the footer. */
  .wrap { padding-inline: var(--space-8); }
  .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: 48px; --sticky-top: 68px; }
  .brand-sub { display: none; }
  .brand { font-size: 17px; }
  .nav-tab { padding: 6px 9px; font-size: 13px; }
  .intro h1, .pr-hero h1 { font-size: clamp(20px, 5.6vw, 25px); }
  /* 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='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 .28s cubic-bezier(.2, .8, .2, 1) both; }
  .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 phone header entirely — they live in the
     hamburger drawer below 720px.) */
}
/* (The theme toggle leaves the header with the tabs at 719.98px — it lives in
   the hamburger drawer on phones, so it stays reachable at every width.) */
/* Smallest phones (320-360): the header wordmark yields to the logo mark, which
   stays the home affordance. Scoped to the header — the footer reuses
   .brand-word and has room for it. */
@media (max-width: 360px) {
  .site-header .brand-word { display: none; }
}
/* 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; }
  .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; }
  /* Clickable notification rows and their dismiss ✕ join the 44px floor; the
     row's right padding grows so text never slides under the bigger target. */
  .notif-item[role='button'] { min-height: 44px; }
  .notif-item { padding-right: 48px; }
  .notif-x { width: 44px; height: 44px; top: 2px; right: 0; }
  /* 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; }
  .mini-btn { width: 44px; height: 44px; }
  .cal-nav .btn { min-width: 44px; }
  .icon-btn { width: 44px; height: 44px; }
}
@media (max-width: 420px) {
  .nav-tabs { gap: 0; }
  .nav-tab { padding: 6px 7px; font-size: 12px; }
  .brand-word { font-size: 17px; }
  /* .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; }
body { max-width: 100%; }
/* Smooth scrolling centralized in CSS (so the reduced-motion reset above wins),
   and 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 and filter bar (≤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; }

  /* THE FILTER BAR SPRAWLED OVER SIX RAGGED ROWS. Two single-line rows now:
     the act chips, then statut + montant + tri together — each row scrolls
     sideways instead of wrapping, bled to the card edge so the scroll reaches
     the viewport like the calendar grid does. */
  .filterbar { padding: var(--space-5) var(--space-7); gap: var(--space-4); }
  .filterbar > .chip-group, .filterbar-row {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    margin-inline: calc(-1 * var(--space-7)); padding-inline: var(--space-7);
  }
  .filterbar > .chip-group::-webkit-scrollbar, .filterbar-row::-webkit-scrollbar { display: none; }
  /* Nothing inside a scroll row may shrink or wrap — the row scrolls instead. */
  .filterbar .chip, .filterbar .seg, .filterbar .seg-btn, .filterbar-row .btn { flex: none; }
  .filterbar-row { gap: var(--space-4); }
  /* The nested montant group rides the row's scroll — one scroll container. */
  .filterbar-row .chip-group { flex-wrap: nowrap; margin-inline: 0; padding-inline: 0; }
  /* A flexible spacer is meaningless inside a scroll row. */
  .filterbar-row .header-spacer { display: none; }
}

/* ==========================================================================
   Intro gate — the first-arrival pitch films (clients 20 s / notaires 15 s).
   Fully token-driven so it follows both themes; every animation lives under
   .run so a film restarts cleanly; the whole gate is inert under
   prefers-reduced-motion (the app never shows it there).
   ========================================================================== */
.ig {
  position: fixed; inset: 0; z-index: 200;
  background: var(--bg);
  display: flex; align-items: center; justify-content: center;
  padding: 14px;
}
.ig.ig-out { opacity: 0; transition: opacity 0.3s ease; }
body.ig-open { overflow: hidden; }
/* Ambient mark drift — the Nota logo, twenty times, adrift far behind the
   content (app.js driftBuild deals each copy its place and tempo). One fixed
   site-wide layer (#site-bg) rides behind ALL panes — no band boundary ever
   slices a mark again — and the intro gate keeps a copy of its own (#ig-bg)
   above its opaque overlay. Whisper-faint so neither theme loses contrast;
   still under prefers-reduced-motion. */
.mark-drift { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
/* Behind everything, edge to edge, and it arrives smoothly — a slow fade-in
   so the scene breathes into place instead of popping. */
.mark-drift--site { position: fixed; z-index: -1; animation: mark-fade 1.6s ease-out both; }
@keyframes mark-fade { from { opacity: 0; } to { opacity: 1; } }
/* Two clocks per mark, both endless and seamless: the wrapper wanders a
   closed loop through two waypoints (out, across, home — never retracing
   itself) while the mark itself tumbles like a thrown die — one full, even
   turn around its own random 3D axis (linear — a roll has no resting point
   to ease toward). Splitting them keeps both loops smooth. Depth (app.js:
   one draw per mark) sets the size, the weight (--o), the softness (--blur)
   and how far it roams — near marks glide boldly in front of small,
   blurred, barely-stirring distant ones: parallax from a single random
   number. */
.mark-drift i {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--s); height: var(--s);
  /* Half the die's edge — every face folds out to this depth. */
  --h: calc(var(--s) / 2);
  opacity: var(--o, 0.06);
  filter: blur(var(--blur, 0px));
  /* The stage depth that turns the tumble from a flat spin into a die
     rolling toward and away from the eye. */
  perspective: 400px;
  animation: mark-wander var(--d) ease-in-out var(--dl) infinite;
}
/* The die's body: six faces folded into a cube, tumbling as one. Hidden
   backfaces keep it reading as a solid object, not layered glass. */
.mark-drift .cube {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  animation: mark-tumble var(--sd) linear var(--dl) infinite;
}
.mark-drift .cube svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  backface-visibility: hidden;
}
.mark-drift .cube svg:nth-child(1) { transform: translateZ(var(--h)); }
.mark-drift .cube svg:nth-child(2) { transform: rotateY(180deg) translateZ(var(--h)); }
.mark-drift .cube svg:nth-child(3) { transform: rotateY(90deg) translateZ(var(--h)); }
.mark-drift .cube svg:nth-child(4) { transform: rotateY(-90deg) translateZ(var(--h)); }
.mark-drift .cube svg:nth-child(5) { transform: rotateX(90deg) translateZ(var(--h)); }
.mark-drift .cube svg:nth-child(6) { transform: rotateX(-90deg) translateZ(var(--h)); }
@keyframes mark-wander {
  0%, 100% { transform: translate(0, 0); }
  33% { transform: translate(var(--dx1), var(--dy1)); }
  66% { transform: translate(var(--dx2), var(--dy2)); }
}
@keyframes mark-tumble {
  from { transform: rotate3d(var(--ax), var(--ay), var(--az), 0); }
  to { transform: rotate3d(var(--ax), var(--ay), var(--az), var(--spin)); }
}
@media (prefers-reduced-motion: reduce) { .mark-drift i, .mark-drift .cube { animation: none; } }
/* The in-flow column hosts only the CHOOSER (the film frame is its own
   fixed, full-viewport layer) — a plain centred measure is enough. */
.ig-inner { position: relative; z-index: 1; width: min(1040px, 96vw); display: flex; flex-direction: column; align-items: center; }

.ig-chooser { display: flex; flex-direction: column; align-items: center; gap: 18px; text-align: center; }
.ig-mark { width: 64px; height: 64px; }
.ig-title { font-size: clamp(22px, 3.2vw, 30px); font-weight: 800; letter-spacing: -0.02em; margin: 0; }
.ig-doors { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }
.ig-door {
  font: inherit; cursor: pointer; text-align: left;
  display: flex; flex-direction: column; gap: 4px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 18px 26px; min-width: 250px;
  transition: border-color 0.15s, transform 0.12s, box-shadow 0.15s;
}
.ig-door:hover { border-color: var(--brand); transform: translateY(-2px); box-shadow: var(--shadow); }
.ig-door:focus-visible { outline: 3px solid var(--ring); outline-offset: 2px; }
.ig-door-t { font-size: 17px; font-weight: 800; }
.ig-door-s { font-size: 16px; font-weight: 500; color: var(--ink-muted); }
.ig-enter {
  font: inherit; cursor: pointer; background: none; border: none;
  color: var(--ink-muted); font-size: 16px; font-weight: 600; padding: 10px; min-height: 44px;
}
.ig-enter:hover { color: var(--ink); }
.ig-enter:focus-visible { outline: 3px solid var(--ring); outline-offset: 2px; border-radius: var(--radius); }

/* TRULY full screen (owner, 2026-08-27: « full screen ») — while a film
   plays the frame is its own fixed, edge-to-edge viewport layer: no
   gutters, no rounded card, no border, no shadow. A column: the stage takes
   every pixel above the skip bar, and « Passer → » rides BELOW the film in
   normal flow (intro-gate-ui.test.mjs pins it out of the picture). */
.ig-frame {
  position: fixed; inset: 0;
  display: flex; flex-direction: column; align-items: center;
  background: var(--surface);
}
.ig-frame[hidden] { display: none; }
.ig-stage {
  container-type: size;
  flex: 1; min-height: 0; width: 100%;
  background: var(--surface); position: relative; overflow: hidden;
}
.ig-stage::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse at 85% 10%, var(--brand-tint), transparent 65%);
}
/* Lightweight brand scenery: two dollar signs and two existing Nota marks.
   Decoration stays behind the reading layer and never receives pointer input. */
.ig-atmosphere { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.ig-dollar { position: absolute; width: clamp(110px, 16cqw, 240px); height: auto; stroke: var(--brand); stroke-width: 3; stroke-linecap: round; opacity: .2; }
.ig-dollar-a { top: 3%; left: 1%; --ig-turn: -14deg; }
.ig-dollar-b { bottom: 3%; right: 1%; --ig-turn: 12deg; }
.ig-brand-mark { position: absolute; width: clamp(32px, 5cqw, 64px); height: auto; opacity: .28; }
.ig-brand-mark-a { top: 10%; right: 8%; --ig-turn: 10deg; }
.ig-brand-mark-b { bottom: 10%; left: 8%; --ig-turn: -10deg; }
.run .ig-atmosphere > svg { animation: igBrandDrift 8s ease-in-out infinite; }
.run .ig-dollar-b, .run .ig-brand-mark-a { animation-delay: -4s; }
@keyframes igBrandDrift {
  0%, 100% { transform: translateY(0) rotate(var(--ig-turn)); }
  50% { transform: translateY(-32px) rotate(6deg); }
}
.ig-scene {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; justify-content: safe center; overflow-y: auto;
  text-align: center; gap: clamp(18px, 3cqh, 32px);
  padding: max(24px, env(safe-area-inset-top)) max(24px, env(safe-area-inset-right)) 24px max(24px, env(safe-area-inset-left));
  opacity: 0; visibility: hidden;
}
.ig-scene .ig-mark { width: 48px; height: 48px; flex: none; }
.ig-kicker {
  font-size: clamp(16px, 1.6cqw, 20px); font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--brand);
}
.ig-h {
  font-size: clamp(32px, 5.4cqw, 72px); font-weight: 800;
  letter-spacing: -.03em; line-height: 1.08; margin: 0;
  text-wrap: balance; max-width: 18ch;
}
.ig-sub {
  font-size: clamp(18px, 2.2cqw, 28px); font-weight: 500;
  line-height: 1.45; color: var(--ink-muted); margin: 0; max-width: 48ch;
  text-wrap: pretty;
}
.ig-steps {
  display: flex; justify-content: center; gap: 16px;
  width: 100%; max-width: 1000px; margin-top: 8px;
}
.ig-step {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px;
  padding: clamp(16px, 2.5cqw, 32px);
  font-size: clamp(18px, 2cqw, 26px); font-weight: 700; line-height: 1.3;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
.ig-step-num {
  display: grid; place-items: center; flex: none; width: 44px; height: 44px;
  font-size: 18px; color: var(--brand); background: var(--brand-tint);
  border-radius: var(--radius);
}
.ig-progress {
  position: absolute; bottom: 0; left: 0; height: 4px;
  background: var(--brand-bright); width: 100%; z-index: 5;
  transform: scaleX(0); transform-origin: left center;
}
.ig-controls {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 12px max(16px, env(safe-area-inset-right)) max(12px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  background: var(--surface); border-top: 1px solid var(--border);
}
.ig-skip {
  display: block; margin-left: auto;
  font: inherit; font-size: 16px; font-weight: 700; cursor: pointer;
  color: var(--brand-ink); background: var(--brand);
  border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 16px; min-height: 44px;
}
.ig-pause {
  font: inherit; font-size: 16px; font-weight: 700; cursor: pointer;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; min-height: 44px;
}
.ig [data-set-lang] { min-width: 44px; min-height: 44px; font-size: 16px; }
.ig-skip:focus-visible, .ig-pause:focus-visible { outline: 3px solid var(--ring); outline-offset: 2px; }
.ig-stage.is-paused, .ig-stage.is-paused * { animation-play-state: paused !important; }
@keyframes igIn { from { opacity: 0; visibility: hidden; transform: translateY(12px); } to { opacity: 1; visibility: visible; transform: none; } }
@keyframes igOut { from { opacity: 1; visibility: visible; } to { opacity: 0; visibility: hidden; } }
@keyframes igBarAnim { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.run .ig-progress { animation: igBarAnim 20s linear both; }
.run .ig-scene { animation: igIn .3s ease var(--ig-at) both, igOut .2s ease calc(var(--ig-at) + 3.8s) forwards; }
.run .ig-c5, .run .ig-n5 { animation: igIn .3s ease var(--ig-at) both; }
/* Each scene carries its start time; entrances finish within its first second. */
.ig-c1, .ig-n1 { --ig-at: 0s; }
.ig-c2, .ig-n2 { --ig-at: 4s; }
.ig-c3, .ig-n3 { --ig-at: 8s; }
.ig-c4, .ig-n4 { --ig-at: 12s; }
.ig-c5, .ig-n5 { --ig-at: 16s; }
.ig-h { position: relative; }
.ig-h::after {
  content: ""; position: absolute; bottom: -10px; left: 35%; width: 30%; height: 3px;
  background: var(--brand-bright); border-radius: var(--radius);
}
.ig-step { border-top: 2px solid var(--brand); }
.run .ig-h::after { animation: igAccentIn .55s ease calc(var(--ig-at) + .12s) both; }
.run .ig-scene .ig-sub { animation: igDetailIn .4s ease calc(var(--ig-at) + .1s) both; }
.run .ig-step { animation: igDetailIn .45s cubic-bezier(.2,.7,.2,1) calc(var(--ig-at) + .18s) both, igCardCue 1.5s ease calc(var(--ig-at) + .8s) forwards; }
.run .ig-step:nth-child(2) { animation-delay: calc(var(--ig-at) + .28s), calc(var(--ig-at) + 1.15s); }
.run .ig-step:nth-child(3) { animation-delay: calc(var(--ig-at) + .38s), calc(var(--ig-at) + 1.5s); }
.run .ig-scene .ig-mark { animation: igLogoIn .7s cubic-bezier(.2,.7,.2,1) var(--ig-at) both; }
@keyframes igDetailIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes igAccentIn { from { transform: scaleX(0); opacity: 0; } to { transform: scaleX(1); opacity: 1; } }
@keyframes igLogoIn { from { opacity: 0; transform: rotate(-12deg) scale(.75); } to { opacity: 1; transform: none; } }
.ig-stage.is-paused *::before, .ig-stage.is-paused *::after { animation-play-state: paused !important; }
/* Story motion: the icon explains the scene while a travelling highlight
   leads the eye through the cards. Text itself settles after its entrance. */
.ig-kicker { display: inline-flex; align-items: center; justify-content: center; gap: 12px; }
.ig-story-icon { width: 34px; height: 34px; flex: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.ig-story-icon path { stroke-dasharray: 1; stroke-dashoffset: 1; }
.run .ig-story-icon path { animation: igSketch .8s ease calc(var(--ig-at) + .1s) forwards; }
.run .ig-story-icon { animation: igIconNod 1.6s ease-in-out calc(var(--ig-at) + .7s) 2; }
.ig-step { position: relative; overflow: hidden; }
.ig-step::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 35%, var(--brand-tint) 50%, transparent 65%);
  transform: translateX(-110%); opacity: 0;
}
.run .ig-step::after { animation: igCardSweep 1.15s ease calc(var(--ig-at) + .65s) both; }
.run .ig-step:nth-child(2)::after { animation-delay: calc(var(--ig-at) + 1s); }
.run .ig-step:nth-child(3)::after { animation-delay: calc(var(--ig-at) + 1.35s); }
.run .ig-step-num { animation: igNumberBeat 1.5s ease calc(var(--ig-at) + .6s) 2; }
.run .ig-step:nth-child(2) .ig-step-num { animation-delay: calc(var(--ig-at) + .85s); }
.run .ig-step:nth-child(3) .ig-step-num { animation-delay: calc(var(--ig-at) + 1.1s); }
.run .ig-scene .ig-mark { animation: igLogoIn .7s cubic-bezier(.2,.7,.2,1) var(--ig-at) both, igLogoCelebrate 1.4s ease calc(var(--ig-at) + .8s) 2; }
@keyframes igSketch { to { stroke-dashoffset: 0; } }
@keyframes igIconNod { 0%, 100% { transform: translateY(0) rotate(0); } 40% { transform: translateY(-4px) rotate(-6deg); } 65% { transform: translateY(-2px) rotate(4deg); } }
@keyframes igCardCue { 0%, 100% { transform: none; border-color: var(--border); border-top-color: var(--brand); } 35%, 65% { transform: translateY(-5px) scale(1.01); border-color: var(--brand-bright); } }
@keyframes igCardSweep { 0% { transform: translateX(-110%); opacity: 0; } 20%, 75% { opacity: 1; } 100% { transform: translateX(110%); opacity: 0; } }
@keyframes igNumberBeat { 0%, 100% { transform: scale(1); } 40% { transform: scale(1.14) rotate(-4deg); } 65% { transform: scale(1.06); } }
@keyframes igLogoCelebrate { 0%, 100% { transform: scale(1) rotate(0); } 35% { transform: scale(1.12) rotate(8deg); } 70% { transform: scale(1.04) rotate(-4deg); } }
@media (max-width: 640px) {
  .ig { padding: 16px; }
  .ig-inner { width: 100%; }
  .ig-door { min-width: 0; width: 100%; }
  .ig-doors { width: 100%; flex-direction: column; }
  .ig-steps { flex-direction: column; gap: 10px; margin-top: 0; }
  .ig-step { flex-direction: row; justify-content: flex-start; text-align: left; padding: 12px 16px; gap: 14px; }
  .ig-controls { gap: 6px; padding-inline: 12px; }
  .ig-pause { padding-inline: 8px; }
  .ig-skip { white-space: nowrap; padding-inline: 12px; }
}
@media (max-width: 640px) and (max-height: 700px) {
  .ig-scene { gap: 12px; padding: 16px; }
  .ig-steps { gap: 8px; }
  .ig-step { padding: 8px 12px; gap: 12px; }
  .ig-step-num { width: 36px; height: 36px; }
}
@media (max-height: 500px) and (min-width: 641px) {
  .ig-scene { gap: 12px; padding-block: 16px; }
  .ig-h { font-size: 32px; max-width: 30ch; }
  .ig-sub { font-size: 18px; max-width: 60ch; }
  .ig-step { flex-direction: row; padding: 12px; font-size: 18px; gap: 10px; }
  .ig-kicker { font-size: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .ig { display: none; }
}

/* ==========================================================================
   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); max-width: 440px; }
/* 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. */
#pane-notaires > .wrap:has(#notary-auth-form[hidden]) #nc-conformite { display: none; }
.nc-conformite-kicker { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--brand); margin-bottom: var(--space-3); }
.nc-conformite-h { font-size: 15px; font-weight: 800; letter-spacing: -0.01em; 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;
  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(--brand); }
.nc-conformite-txt { font-size: 13px; line-height: 1.45; color: var(--ink); }
.nc-conformite-order { margin: var(--space-5) 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); }

/* ==========================================================================
   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 green 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).
   Phones drop the fab (the drawer's « Messagerie » row opens the panel),
   so the calendar corner ADR 0022 cleared stays clear.
   ========================================================================== */
.sup-wrap { position: fixed; right: 16px; bottom: 70px; z-index: 60; display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
.sup-fab {
  appearance: none; border: 0; cursor: pointer; position: relative;
  width: 48px; height: 48px; border-radius: var(--radius);
  background: var(--brand); color: var(--brand-ink);
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-brand);
}
.sup-fab:hover { background: var(--brand-hover); }
.sup-fab:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
/* The unread mark — the one round thing here: an 8px dot, as the register allows. */
.sup-fab-dot { position: absolute; top: 6px; right: 6px; width: 8px; height: 8px; border-radius: 50%; background: var(--danger); box-shadow: 0 0 0 2px var(--brand); }
.sup-panel {
  width: min(380px, calc(100vw - 32px));
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); overflow: hidden;
}
.sup-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); padding: var(--space-6) var(--space-6) var(--space-5) var(--space-8); border-bottom: 1px solid var(--border); }
.sup-title { font-weight: 800; letter-spacing: -0.01em; }
.sup-sub { margin: 2px 0 0; font-size: 12.5px; line-height: 1.4; color: var(--ink-muted); max-width: 30ch; }
/* 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 28 px. */
.sup-close {
  appearance: none; border: 0; background: transparent; color: var(--ink-muted);
  font: inherit; font-size: 14px; line-height: 1; cursor: pointer; flex: none;
  width: 28px; height: 28px; display: inline-grid; place-items: center; border-radius: var(--radius-sm);
}
.sup-close:hover { background: var(--surface-inset); color: var(--ink); }
.sup-close:focus-visible { outline: 2px solid var(--brand); 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(--space-8); 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-suggest { display: flex; flex-wrap: wrap; gap: var(--space-3); padding: var(--space-6) var(--space-8) 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(--brand); outline-offset: 1px; }
/* L'escalade se lit — même registre que « conversation terminée », en marque. */
.sup-escalade { margin: 0; padding: 8px 14px; font-size: 12.5px; line-height: 1.45; color: var(--ink-muted); border-top: 1px dashed var(--border); }
.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: 8px 14px; font-size: 12.5px; color: var(--ink-muted); border-top: 1px dashed var(--border); }
.sup-form { padding: var(--space-6) var(--space-8) var(--space-7); 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(--brand); outline-offset: 2px; border-radius: var(--radius-xs); }
.sup-mail-open[aria-expanded='true'] { display: none; }
/* 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 guide « ? » left phones (ADR 0022) so the calendar owns its corner —
     the chat fab must not take its place. The panel still opens from the
     drawer's « Messagerie » row and from an emailed #messagerie link. */
  .sup-fab { display: none; }
  .sup-wrap { right: 10px; bottom: 10px; left: 10px; align-items: flex-end; }
  .sup-panel { width: 100%; }
}

/* --- 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 { color: var(--ink-muted); font-size: 12.5px; }
.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(--hunter-700); --brand-bright: var(--hunter-500); --brand-ink: #ffffff; --brand-hover: var(--hunter-800);
    --bg: #ffffff; --surface: #ffffff; --surface-inset: #f3f5f4; --surface-hover: #e9edeb;
    --ink: #111614; --ink-muted: #4f5e57; --border: #dde2df; --border-strong: #b9c2bc; --ring: var(--hunter-600);
    --shadow-sm: none; --shadow: none; --shadow-lg: none; --shadow-brand: none;
  }
  .site-header, .site-footer nav, .guide-fab, .sup-wrap, .mark-drift, .mnav, .mnav-scrim,
  .toast, .skip, .nav-burger, #intro-gate, 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; }
}

/* 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::backdrop { background: rgb(0 0 0 / 55%); }
#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: 20px;
  grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px;
}
.day-book[data-at='4'] .book-identity .form-row { min-width: 0; }
.day-book[data-at='4'] #identity-rows input { width: 100%; max-width: none; box-sizing: border-box; }
.day-book[data-at='4'] .book-identity .help { font-size: 13px; line-height: 1.5; }
.day-book[data-at='4'] .offer-hint { margin: 0; }
.day-book[data-at='4'] .consent-line { margin: 0; font-size: 12px; 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'] #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. */
.account-tour {
  position: fixed; inset: auto 20px 20px auto; margin: 0;
  width: min(460px, calc(100vw - 32px)); max-height: min(50dvh, 420px);
  box-sizing: border-box; overflow: auto; padding: 24px;
  color: var(--ink); background: var(--surface); border: 1px solid var(--brand);
  border-radius: var(--radius); box-shadow: 0 12px 48px #0003;
}
.account-tour[open] { animation: account-tour-in .2s ease-out; }
.account-tour::backdrop { background: rgb(0 0 0 / .16); }
.account-tour h2 { margin: 10px 0; font-size: 21px; line-height: 1.25; }
.account-tour p { margin: 0 0 20px; line-height: 1.6; }
.account-tour-progress { color: var(--brand); font-size: 12px; font-weight: 700; }
.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 { outline: 3px solid var(--brand); outline-offset: 5px; 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 { inset: auto 16px 16px; padding: 18px; } }
@media (prefers-reduced-motion: reduce) { .account-tour[open] { animation: none; } }
.account-tour::backdrop { backdrop-filter: none; -webkit-backdrop-filter: none; }
.account-tour h2 { animation: account-tour-in .2s ease-out; }
@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: 50%;
  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(--brand); border-color: currentColor; background: var(--surface-hover);
}
.itip[data-tone='warn'] .itip-btn { color: var(--warn); }
.itip-pop {
  box-sizing: border-box; width: 360px; max-width: calc(100vw - 32px);
  padding: 16px 18px; 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; }
@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; }
.profil-fields .help { margin: 6px 0 0; font-size: 13px; line-height: 1.5; }
@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; }
.o-criteria .crit-unit-wrap { display: flex; width: 100%; }
.o-criteria .crit-unit-wrap input[type='number'] { width: 100%; max-width: none; min-width: 0; }
.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; }
.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; }
  .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; }
.o-criteria .crit-sec > .crit-more .crit-sec-grid { padding: 12px; }
.o-criteria .crit-more > summary { padding: 12px; min-height: 44px; box-sizing: border-box; }
.o-criteria .crit-more[open] > summary { border-bottom: 1px solid var(--border); margin-bottom: 0; }
.o-criteria .crit-sec-grid > .crit-row { margin: 0; }

/* 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: 36px; 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); }
#o-where-step #prefix-row { gap: 10px 16px; }

/* The confirmed referral is a shared workspace above the signup and FAQ. */
.pr-grid > .partner-share-panel {
  grid-column: 1 / -1; margin: 0; padding: clamp(20px, 3vw, 32px);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 40px); background: var(--surface);
  border-color: var(--brand); scroll-margin-top: var(--sticky-top);
}
.pr-grid > .partner-share-panel[hidden] { display: none; }
.partner-share-panel > * { min-width: 0; }
.partner-share-panel .partner-msg { margin: 0; padding: 0 0 0 var(--space-6); border-top: 0; border-left: 1px solid var(--border); }
.partner-share-panel .partner-link-row code { flex-basis: 100%; padding: 14px; font-size: 15px; }
.partner-share-panel .partner-msg-text { margin: 12px 0; padding: 16px; font-size: 14px; line-height: 1.65; }
.partner-share-panel .btn { min-height: 44px; }
@media (max-width: 700px) {
  .pr-grid > .partner-share-panel { grid-template-columns: minmax(0, 1fr); }
  .partner-share-panel .partner-msg { padding: 20px 0 0; border-left: 0; border-top: 1px solid var(--border); }
}

/* Equal-weight, nonblocking analytics choice. */
.analytics-consent { position: fixed; inset: auto 16px 16px; z-index: 10000; max-width: 680px; margin: auto; padding: 18px; background: #fff; color: #182422; border: 1px solid #aab7b0; border-radius: var(--radius-lg); box-shadow: 0 4px 24px #0002; }
.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 #64756b; border-radius: var(--radius-sm); background: #fff; color: #182422; margin-right: 8px; }
