/*
 * Bubble Invest site layer, loaded on every page after the page's own styles.
 * The brand lives in Bubble-invest/bubble-brand; this file brings it to the site, one pull request at a time.
 * Step 1: keyboard and layout fixes.
 */

/* Buttons wrap on phones instead of widening the page (Button component). */
@media (max-width: 480px) {
  .btn { white-space: normal; max-width: 100%; height: auto; text-align: center; }
}

/* « Bientôt » shop cards stay inert, but their waitlist link works. */
.agent-card.soon a { pointer-events: auto; }

/* Links inside running text are underlined, so they never rely on colour alone (WCAG 1.4.1). */
main p a:not([class]), main li a:not([class]) {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .15em;
}

/* Logo: the master lockup, never typed (D9); black on light, white on dark (D14); 30px high, about 128px wide. */
.logo { color: #000; gap: 0; line-height: 0; }
.logo svg { width: auto; height: 30px; display: block; color: inherit; }
.footer .logo, .logo.on-dark { color: #fff; }

/* Step 3: the brand's colours.
   tokens.css (from bubble-brand) now defines every colour; the pages' own variables already match its values. */

/* Primary buttons: black at rest, violet-ink on hover and while pressed (D22, D55). */
.btn-primary:hover, .btn-primary:active { background: var(--violet-ink); color: var(--white); }

/* The newsletter's submit button is a pill like every button. */
.nl-submit { border-radius: var(--r-button); }

/* Featured tier: an ink border and the featured shadow, no gradient edge (D54, PricingTier). */
.tier.featured { border-color: var(--ink); box-shadow: var(--shadow-tier-featured); }
.tier.featured::before { display: none; }

/* Accent pills: the brand's opaque tint with violet-ink text, readable on every section (D34). */
.pill.accent { background: var(--card); background: color-mix(in srgb, var(--violet) 8%, var(--card)); color: var(--violet-ink); }

/* « Bientôt » cards in the shop stay fully readable: their badge already says they are not available yet. */
.agent-card.soon { opacity: 1; }

/* Step 4: badges, punctuation and icons.
   Legal notices are plain cards with a Lucide icon: never yellow, never a coloured left border (Card component, D54). */
.legal-box, .disclaimer-box, .disclaimer-notice { background: var(--card); border: 1px solid var(--hair); border-radius: var(--r); }
.disclaimer-box p, .disclaimer-notice p { display: flex; align-items: flex-start; gap: .55rem; color: var(--ink); }
.notice-icon { flex-shrink: 0; width: 1.15em; height: 1.15em; margin-top: .15em; }

/* Step 7: the brand's typefaces (D6, D18), self-hosted in fonts.css.
   Hanken Grotesk (--sans, from tokens.css) for titles, figures, buttons, navigation and labels; Spectral (--serif) for running text.
   No monospace and no handwriting on the site: --mono and --hand point to Hanken Grotesk.
   Code keeps a system monospace, so its columns stay aligned. */
:root { --mono: var(--sans); --hand: var(--sans); --code: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
button, input, select, textarea { font-family: inherit; }

/* Step 7, part 2: no gradient fills (D54, Q-16). Heroes sit on the page's own background; placeholders are warm;
   badges on images are ink; featured cards take the featured tier's ink border. The dark band, the agent avatars,
   the drawn bubbles and the Particuliers hero change with their redesign (step 8). */
.hero-aura, .blog-hero-aura,
.hero-about::before, .gh-hero::before, .labs-hero::before, .pro-hero::before, .boutique-hero::before, .cta-band::before,
.path-card.featured::before { background: none; }
.founder-photo-fallback, .article-hero-image, .featured-tile-top, .featured-image, .blog-card-image { background: var(--warm); }
.featured-tile-badge, .featured-tag { background: var(--ink); color: var(--white); }
.pillar-marker { background: var(--violet); }
.session-themes li::before { background: var(--violet); }
.lm-card.center, .newsletter-card.substack { background: var(--card); }
.path-card.featured { background: var(--card); border-color: var(--ink); }
.agent-card.featured { background: var(--card); border: 1px solid var(--ink); box-shadow: var(--shadow-tier-featured); }
.labs-counters-band::before, .labs-counters-band::after { background: rgba(255, 255, 255, .12); }
.process-step:not(:last-child) .step-num-col::after, .process-step:not(:last-child)::after { background: var(--hair); }

/* Step 8: components for the page layouts, as in bubble-brand's components.css.
   Light button for the night band (D22); glass on night (D29, D37); drawn film bubbles, the flat version of the
   bubbles on dark backgrounds where 3D cannot run (D27, D37). */
:root {
  --film-ring: conic-gradient(from 210deg, var(--peach), var(--pink), var(--film-violet), var(--mint), var(--peach));
  --film-edge: color-mix(in srgb, var(--night) 55%, var(--film-violet));
}
.btn-light { background: var(--white); color: var(--ink); border-color: transparent; }
.btn-light:hover { transform: translateY(-2px); background: var(--white); color: var(--ink); }
.glass { position: relative; border-radius: var(--r); padding: 1.1rem 1.25rem; background: rgba(255, 255, 255, .55); border: 1px solid rgba(255, 255, 255, .85); backdrop-filter: blur(14px) saturate(1.4); -webkit-backdrop-filter: blur(14px) saturate(1.4); }
.glass.on-night { background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .22); color: var(--white); box-shadow: 0 20px 50px -24px rgba(0, 0, 0, .6), inset 0 1px 0 rgba(255, 255, 255, .25); backdrop-filter: blur(16px) saturate(1.5); -webkit-backdrop-filter: blur(16px) saturate(1.5); }
.glass::after { content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1px; pointer-events: none;
  background: linear-gradient(135deg, color-mix(in srgb, var(--peach) 55%, transparent), color-mix(in srgb, var(--pink) 45%, transparent) 22%, color-mix(in srgb, var(--film-violet) 35%, transparent) 45%, color-mix(in srgb, var(--mint) 45%, transparent) 75%, rgba(255, 255, 255, .2));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; }
.glass .g-title { font: var(--type-card-title); letter-spacing: var(--type-card-title-tracking); margin: 0 0 .45rem; }
.glass p { margin: 0; }
.glass.on-night p:not(.g-title) { font: var(--type-card-body); color: rgba(255, 255, 255, .86); }
.film { --s: 120px; position: relative; display: block; width: var(--s); height: var(--s); border-radius: 50%; flex: none;
  background: radial-gradient(circle at 33% 27%, rgba(255, 255, 255, .95) 0%, rgba(255, 255, 255, 0) 24%),
              radial-gradient(circle at 70% 76%, rgba(255, 255, 255, .55) 0%, rgba(255, 255, 255, 0) 14%),
              radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0) 52%, rgba(255, 255, 255, .28) 100%);
  box-shadow: inset 0 0 calc(var(--s) * .1) rgba(255, 255, 255, .12); }
.film::before { content: ""; position: absolute; inset: 0; border-radius: 50%; opacity: .95; background: var(--film-ring);
  -webkit-mask: radial-gradient(circle closest-side, transparent 62%, #000 92%); mask: radial-gradient(circle closest-side, transparent 62%, #000 92%); }
/* A small soap bubble's film (D27): highlight, milky veil, the four bubble colours as a ring. */
:root { --film: radial-gradient(circle at 34% 30%, #fff 0 18%, rgba(255, 255, 255, 0) 32%), linear-gradient(rgba(255, 255, 255, .32), rgba(255, 255, 255, .32)), var(--film-ring); }

/* Headings in order for screen readers (D124, F14): the footer's column titles are h2, styled like the former h4;
   .sr-only carries a heading only screen readers need. */
.footer-col h2 { font-family: var(--sans); font-size: 0.69rem; letter-spacing: 0.1em; text-transform: uppercase; opacity: .55; margin: 0 0 1rem; font-weight: 600; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Pre-launch, motion and devices (D132, D133, F16 to F25).
   Sections rise lightly into view (D132): visible by default and without JavaScript; site.js holds back only
   what starts below the screen, never a hero. This replaces each page's 22px, 0.8s fade. */
html .reveal { opacity: 1; transform: none; }
@media (prefers-reduced-motion: no-preference) {
  html .reveal { transition: opacity .4s var(--ease), transform .4s var(--ease); }
  html .reveal.rv-pending { opacity: 0; transform: translateY(8px); }
}
@media print { html .reveal.rv-pending { opacity: 1; transform: none; } }

/* A swipe that starts on the hero bubble scrolls the page; a sideways drag still steers it (F18). */
.hero-orb .orb-hit { touch-action: pan-y; }

/* Tap targets of at least 24px, 44px where there is room (WCAG 2.5.8, F21). */
.lang button { min-width: 28px; min-height: 26px; display: inline-flex; align-items: center; justify-content: center; }
.mobile-menu .lang button { min-width: 44px; min-height: 44px; }
.mobile-close { min-width: 44px; min-height: 44px; display: inline-grid; place-items: center; margin: -.6rem -.75rem 0 0; }
.burger { min-width: 44px; min-height: 44px; align-items: center; justify-content: center; }

/* Anchors land below the sticky header (F22). */
main [id] { scroll-margin-top: 76px; }

/* Page changes (D133): the header stays, the page cross-fades and the hero bubble glides to its place on the next page
   (View Transitions; a browser without them changes page as before). Nothing moves with reduced motion. */
@view-transition { navigation: auto; }
.header { view-transition-name: site-header; }
.hero-orb { view-transition-name: hero-bubble; }
::view-transition-group(*) { animation-duration: .3s; animation-timing-function: var(--ease); }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .25s; }
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

/* Blog cards are real links (F25): the title's link stretches over the whole card, for the keyboard and for search engines. */
.blog-card, .featured-card { position: relative; }
.blog-card .card-link, .featured-card .card-link { color: inherit; text-decoration: none; }
.blog-card .card-link::after, .featured-card .card-link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; z-index: 1; }
.blog-card .card-link:focus-visible, .featured-card .card-link:focus-visible { outline: none; }
.blog-card:has(.card-link:focus-visible), .featured-card:has(.card-link:focus-visible) { outline: 2px solid var(--violet); outline-offset: 3px; }

/* « Preparing, then ready » (D28, D133): while the lab's live figures load, the live dot circles; it lands once they arrive
   and turns grey if they cannot load (labs.js sets data-lab on <html>). The newsletter button shows the same mark while it
   signs you up, and the welcome's check lands. Every loop stops with reduced motion. */
@keyframes prep-orbit { from { transform: rotate(0deg) translateX(3px) rotate(0deg); } to { transform: rotate(360deg) translateX(3px) rotate(-360deg); } }
@keyframes prep-land { from { transform: translateY(-5px) scale(.5); opacity: .4; } }
html[data-lab="preparing"] .counters-live .dot, html[data-lab="preparing"] .topbar .dot, html[data-lab="preparing"] .labs-live-dot { animation: prep-orbit .9s linear infinite; }
html[data-lab="ready"] .counters-live .dot, html[data-lab="ready"] .topbar .dot, html[data-lab="ready"] .labs-live-dot { animation: prep-land .45s var(--spring), pulse 2.6s var(--ease) .45s infinite; }
html[data-lab="offline"] .counters-live .dot, html[data-lab="offline"] .topbar .dot, html[data-lab="offline"] .labs-live-dot { animation: none; background: var(--faint); box-shadow: none; }
.prep { position: relative; display: inline-block; flex: none; width: 14px; height: 14px; margin-right: .5rem; border-radius: 50%; box-shadow: inset 0 0 0 1.5px color-mix(in srgb, currentColor 40%, transparent); vertical-align: -2px; }
.prep::after { content: ""; position: absolute; left: 50%; top: 50%; width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; border-radius: 50%; background: currentColor; transform: translateX(4.5px); animation: prep-ring .9s linear infinite; }
@keyframes prep-ring { from { transform: rotate(0deg) translateX(4.5px); } to { transform: rotate(360deg) translateX(4.5px); } }
.nl-success-icon { animation: prep-land .5s var(--spring) both; }
@media (prefers-reduced-motion: reduce) {
  html[data-lab] .counters-live .dot, html[data-lab] .topbar .dot, html[data-lab] .labs-live-dot, .prep::after, .nl-success-icon { animation: none; }
}

/* Stacked deck (Jade, 2026-10-20, D139): the cards sit one behind the other like pages; site.js moves them.
   Only the front card has a soft shadow (it can be swiped). Without JavaScript the grid stays. */
.is-deck { display: block !important; }
.is-deck .deck-stage { position: relative; max-width: 620px; margin: 0 auto; padding-right: 24px; touch-action: pan-y; }
.is-deck .deck-stage > :not(.is-front) { pointer-events: none; }
.is-deck .deck-stage > * { position: absolute; top: 0; left: 0; right: 24px; box-sizing: border-box; transform-origin: 50% 0;
  transition: transform .45s var(--ease), opacity .45s var(--ease); }
.is-deck .deck-stage > .is-front { box-shadow: var(--shadow-form); }
.is-deck .deck-controls { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 16px; }
.deck-btn { width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--hair); background: var(--card); color: var(--ink);
  display: inline-grid; place-items: center; cursor: pointer; padding: 0; }
.deck-btn svg { width: 18px; height: 18px; }
.deck-btn:hover { border-color: var(--violet-line); }
.deck-btn:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }
.deck-count { font-family: var(--sans); font-weight: 600; font-size: .9rem; color: var(--muted); font-variant-numeric: tabular-nums; min-width: 3.5em; text-align: center; }
@media (prefers-reduced-motion: reduce) { .is-deck .deck-stage > * { transition: none; } }

/* Booking buttons inside a page (Jade, 2026-10-20, D139): the goal of the page is a booked diagnostic. */
.cta-strip { margin-top: 2rem; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem 1.5rem;
  background: var(--card); border: 1px solid var(--hair); border-radius: var(--r); padding: 1.1rem 1.4rem; }
.cta-strip p { margin: 0; font-family: var(--serif); font-size: 1.08rem; line-height: 1.45; color: var(--ink); max-width: 46ch; }
.cta-strip p strong { font-family: var(--sans); font-weight: 700; }
.method-cta { margin-top: 1.8rem; display: flex; justify-content: center; }
/* One key word per tile (D139), in violet-ink because tile titles are small text. */
.key { color: var(--violet-ink); }


/* Results set large on one glass panel, in front of the bubble's still frame (P3, Jade, 2026-10-20): the figures
   stay violet (D139, large text), their labels are ink; the glass sits over real depth (D29); the still frame stands
   in for the 3D bubble, so no extra engine (D25). On the home page it is also the reduced-motion form of the film (P5). */
/* The still frame bleeds off the right edge without widening the page: unclipped, on phones it pushed the layout
   viewport to 434px (sideways panning, a fixed bar wider than the screen). Clip, not hidden: no scroll container.
   A class on each results section (home, Professionnels), not :has(), so older browsers clip it too (D190). */
.results-section { overflow-x: clip; }
.results { position: relative; display: grid; align-items: center; min-height: clamp(360px, 40vw, 540px); }
.results-bubble { position: absolute; right: -5%; top: 50%; width: min(62%, 600px); height: auto; transform: translateY(-50%); pointer-events: none; z-index: 0; }
.results-glass { position: relative; z-index: 1; max-width: 1000px; padding: clamp(1.6rem, 3.2vw, 2.6rem) clamp(1.3rem, 3vw, 2.6rem); border-radius: var(--r-lg);
  background: rgba(255, 255, 255, .62); border-color: rgba(255, 255, 255, .9); box-shadow: 0 30px 60px -36px rgba(34, 29, 22, .35); }
.results-glass .proofs { margin: 0; gap: clamp(1.3rem, 3vw, 2.6rem); }
.results-glass .proofs li { background: none; border: 0; padding: 0; gap: .55rem; }
.results-glass .proof-num { font-size: clamp(2.1rem, 3.5vw, 3.2rem); line-height: 1; letter-spacing: -.035em; white-space: nowrap; }
.results-glass .proof-lbl { color: var(--ink); font-size: 1.02rem; }
@media (min-width: 760px) { .results-glass .proofs { grid-template-columns: repeat(3, minmax(min-content, 1fr)); } }
@media (max-width: 759px) {
  .results { min-height: 0; padding-top: clamp(150px, 46vw, 230px); }
  .results-bubble { right: -18%; top: 0; transform: none; width: min(92%, 380px); }
}
.sec-head + .results { margin-top: -.4rem; }

/* « Le film » on the home page (P5; Jade, 2026-10-20, D177; film.js). The first time the pointer rests on the hero
   bubble (on a touch screen, the first time it is touched), the bubble opens onto its film, a fixed layer under the
   header drawn from the bubble outwards; a copy of the results lands on its glass panel in the middle (figures in
   violet-ink, 4.5:1 at least over the film; labels in ink), then the film closes back into the bubble. Once per page
   view. Reduced motion and no JavaScript get no film; the results section keeps its still form (P3) for everyone. */
.film-layer { position: fixed; inset: 0; z-index: 40; pointer-events: none; opacity: 0; visibility: hidden; }
.film-layer canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.film-layer .film-glass { position: absolute; left: 50%; top: 50%; width: min(1000px, calc(100vw - 48px)); margin: 0; opacity: 0;
  transform: translate(-50%, -50%); background: rgba(255, 255, 255, .74); box-shadow: 0 30px 70px -40px rgba(34, 29, 22, .45); }
.film-layer .film-glass .proof-num { color: var(--violet-ink); }
@media (prefers-reduced-motion: reduce) { .film-layer { display: none; } }

/* Fewer template tics (P4, Jade, 2026-10-20). Eyebrows are written in sentence case, a little larger and without
   letter-spacing; labels follow. Capitals stay only for tables and pricing badges (the pricing tiers' tags and badges,
   the catalogue's badges). Section numbers are gone: numbering marks a real sequence only, like the method's steps.
   No middle dot inside a label; offer names keep theirs (D80, D125). The arrow stays on the primary button only. */
.eyebrow, .sec-num, .sec-num-badge, .hero-eyebrow, .hero-part-tag, .story-when { text-transform: none; letter-spacing: 0; font-size: .95rem; }
.hero-tag, .pro-hero-tag { text-transform: none; letter-spacing: 0; font-size: .86rem; }
.topbar, .counters-live, .founder .role, .lm-label, .step-meta, .mission-meta, .path-num, .path-meta, .install-label, .skill-gh-link,
.lab-live-badge, .lab-label, .lab-panel-title, .cred-item, .founder-tag, .founder-role, .story-caption, .stat-label, .org-tier-label,
.org-support-sep span, .org-card-role, .footer-col h2, .footer-col h4 { text-transform: none; letter-spacing: 0; font-size: .84rem; }
.topbar { font-size: .78rem; }

/* The logo's ring and dot as a small mark (P6, P8): the ring is the loop of work, the dot the client's validation (D39).
   Monochrome, in the text's colour (D14). */
.mark { width: 20px; height: 20px; flex: none; overflow: visible; }
.mark-ring { fill: none; stroke: currentColor; stroke-width: 34; stroke-linecap: round; }
.mark-dot { fill: currentColor; }
.doc-foot .mark { width: 18px; height: 18px; }

/* Colour means « ready », and the dot that validates (P8, Jade, 2026-10-20).
   In the primary buttons the arrow becomes the logo's ring (D39): its dot waits above and lands on hover, on touch and
   on focus, like the logo's dot (D51); with reduced motion it is already landed. White ring on the black button,
   which turns violet-ink on hover (D14, D55); ink ring on the light button of the night bands. */
.btn-primary .mark, .btn-light .mark { width: 20px; height: 20px; }
.header .btn-primary .mark { width: 16px; height: 16px; } /* the header's small button keeps the ring in proportion */
.btn-primary:hover .mark, .btn-light:hover .mark { transform: none; }
.btn-primary .mark-dot, .btn-light .mark-dot { transform: translate(40px, -170px) scale(.82); transform-box: fill-box; transform-origin: center; transition: transform .55s var(--spring); }
.btn-primary:hover .mark-dot, .btn-primary:focus-visible .mark-dot, .btn-primary:active .mark-dot,
.btn-light:hover .mark-dot, .btn-light:focus-visible .mark-dot, .btn-light:active .mark-dot { transform: none; }
@media (prefers-reduced-motion: reduce) { .btn-primary .mark-dot, .btn-light .mark-dot { transform: none; transition: none; } }
/* Ready states are small film bubbles: the lab's live dot once its figures are in (D28, D38; grey when offline), the Labo
   page's live dot too, and the Professionnels trust row's bullets (D26). */
.counters-live .dot, .lab-dot, .labs-live-dot, .pro-trust-item .trust-dot { width: 10px; height: 10px; background: var(--film); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .6); }
.topbar .dot { width: 8px; height: 8px; background: var(--film); }

/* A title that translates (O1, Jade, 2026-10-20; slower and in place, her comment the same evening; site.js). The words
   sit in a slot of their own that never wraps, so a passing word always shows in the same place and nothing around it
   moves. While a word passes, CSS draws it in place of the original, which stays readable by screen readers only; the
   passing words are ink (the page keeps one accent, D54). */
.swap { display: inline-block; white-space: nowrap; vertical-align: bottom; text-align: inherit; } /* a shorter word follows the title: left on laptops, centred on phones */
.swap.is-passing > .swap-text { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.swap.is-passing::before { content: attr(data-word) / ""; display: inline-block; animation: swap-in .45s var(--ease) both; }
.swap.is-back > .swap-text { display: inline-block; animation: swap-in .45s var(--ease) both; }
@keyframes swap-in { from { opacity: 0; transform: translateY(.3em); } }

/* Booking bar on phones (D173, Jade, 2026-10-20; site.js). Below 900px the header's button goes into the menu, so a thin,
   still bar in glass (D52) sits at the foot of the screen once the hero is passed. It hides while another booking button,
   the cookie banner or the footer is on screen, so it never covers what it repeats, nor the end of the page. It only fades,
   never pulses (D45), and not at all with reduced motion; without JavaScript it stays hidden. While hidden it stays in the
   keyboard order, after the page's content, and shows as soon as it takes focus. */
.book-bar { display: none; }
@media (max-width: 899px) {
  .book-bar.is-ready { position: fixed; left: 0; right: 0; bottom: 0; z-index: 45; display: flex; justify-content: center;
    padding: .5rem max(.75rem, env(safe-area-inset-right)) calc(.5rem + env(safe-area-inset-bottom)) max(.75rem, env(safe-area-inset-left));
    background: color-mix(in srgb, var(--base) 88%, transparent); backdrop-filter: blur(18px) saturate(1.4); -webkit-backdrop-filter: blur(18px) saturate(1.4);
    box-shadow: 0 -12px 30px -22px color-mix(in srgb, var(--night) 45%, transparent);
    opacity: 0; pointer-events: none; transition: opacity .25s var(--ease); }
  /* the glass's edge in the four bubble colours (Glass, D27, D29), along the top */
  .book-bar.is-ready::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; pointer-events: none;
    background: linear-gradient(90deg, color-mix(in srgb, var(--peach) 70%, transparent), color-mix(in srgb, var(--pink) 60%, transparent) 33%, color-mix(in srgb, var(--film-violet) 50%, transparent) 66%, color-mix(in srgb, var(--mint) 60%, transparent)); }
  .book-bar.is-shown, .book-bar.is-ready:focus-within { opacity: 1; pointer-events: auto; }
  .book-bar .btn { width: 100%; max-width: 440px; min-height: 44px; height: auto; justify-content: center; padding: .6rem 1rem; font-size: .88rem;
    line-height: 1.25; white-space: normal; text-align: center; }
}
@media (prefers-reduced-motion: reduce) { .book-bar.is-ready { transition: none; } }

/* A line under a booking button (D173, Jade, 2026-10-20): small and muted, never a second button. */
.cta-note { margin: .75rem 0 0; font-family: var(--sans); font-size: .84rem; font-weight: 400; line-height: 1.45; color: var(--muted); }
.price-card .cta-note, .tier .cta-note { text-align: center; }
.cta-band .cta-note { margin: 1rem auto 0; max-width: none; font-family: var(--sans); font-size: .84rem; color: rgba(255, 255, 255, .78); }

/* The closing block speaks of getting ahead (D173, Jade, 2026-10-20): its line under the title on the home page's night band. */
.band-dark .band-sub { margin: -.6rem 0 1.6rem; max-width: 44ch; font-family: var(--serif); font-size: 1.1rem; line-height: 1.6; color: rgba(255, 255, 255, .8); }

/* The 72 % figure (D175, Jade, 2026-10-20): a fact, dated and sourced, above « Vous avez déjà essayé l'IA. »;
   the figure in violet-ink (D139), its source in small type, linked. */
.stat-lead { max-width: 660px; margin: 0 auto 2rem; text-align: center; }
.stat-lead-line { margin: 0; font-family: var(--sans); font-weight: 600; font-size: clamp(1.1rem, 2.6vw, 1.32rem); line-height: 1.4; letter-spacing: -.01em; color: var(--ink); text-wrap: balance; }
.stat-lead-figure { color: var(--violet-ink); font-weight: 700; }
.stat-lead-source { margin-top: 0; font: var(--type-caption); color: var(--muted); }
/* the source link is a 44px tap target: padding, not a bigger type */
.stat-lead-source a { display: inline-block; padding: .8rem 0; color: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .15em; }
.stat-lead-source a:hover { color: var(--ink); }

/* The cost of waiting (D173, Jade, 2026-10-20; site.js): two fields with real labels, the result in a polite live line,
   then the booking button. Hours spent today only, never a saving or a gain (D76). Fields as in the Form component (D34):
   base fill, faint border, violet on focus; 16px text or more, so phones do not zoom in. */
hr.hair { height: 1px; background: var(--hair-soft); border: 0; margin: 0; }
.cost-card { max-width: 720px; display: grid; gap: 1.3rem; justify-items: start; padding: clamp(1.4rem, 4vw, 2.2rem);
  background: var(--card); border: 1px solid var(--hair); border-radius: var(--r-lg); box-shadow: var(--shadow-form); }
.cost-fields { display: grid; gap: 1rem; width: 100%; }
@media (min-width: 560px) { .cost-fields { grid-template-columns: 1fr 1fr; } }
.cost-field { display: grid; gap: .45rem; }
.cost-field label { font-family: var(--sans); font-size: .85rem; font-weight: 600; letter-spacing: -.01em; color: var(--ink); }
.cost-field input { width: 100%; min-height: 44px; margin: 0; padding: .7rem 1rem; border: 1px solid var(--faint); border-radius: var(--r-control);
  background: var(--base); font-family: var(--sans); font-size: 1.05rem; font-variant-numeric: tabular-nums; color: var(--ink);
  transition: border-color .2s var(--ease); }
.cost-field input:focus-visible { border-color: var(--violet); outline: 2px solid var(--violet); outline-offset: -1px; }
.cost-result { margin: 0; max-width: 56ch; font-family: var(--serif); font-size: 1.1rem; line-height: 1.6; color: var(--ink); }
.cost-result strong { font-family: var(--sans); font-weight: 700; }

/* « Par quoi voulez-vous commencer ? » (D173, Jade, 2026-10-20): five answers, each a ghost button (never a pill: pills
   are not actions) that opens the booking with the answer prefilled; the plain booking button follows. */
.start-pick { margin-top: 2.2rem; }
.start-pick-title { margin: 0 0 .9rem; font: var(--type-card-title); font-size: 1.15rem; letter-spacing: var(--type-card-title-tracking); color: var(--ink); }
.start-choices { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .6rem; }
.start-choices li { margin: 0; padding: 0; }
.start-choices li::before { content: none; }
.start-choices .btn { min-height: 44px; justify-content: center; }

/* The end of every blog article (D173, Jade, 2026-10-20): after the article body, outside the Notion content, the question
   asked of the reader's own firm, then the booking button. */
.post-end { margin: 3rem 0 0; padding: clamp(1.4rem, 4vw, 2rem); background: var(--card); border: 1px solid var(--hair); border-radius: var(--r); }
.post-end-title { margin: 0 0 .5rem; font-family: var(--sans); font-weight: 700; font-size: clamp(1.3rem, 3vw, 1.6rem); line-height: 1.2; letter-spacing: -.02em; color: var(--ink); }
.post-end-text { margin: 0 0 1.3rem; max-width: 52ch; font-family: var(--serif); font-size: 1.1rem; line-height: 1.6; color: var(--muted); }

/* The research on the home page (D179; Jade chose it in our colours on 2026-10-20, rather than the word « Substack » in
   orange): a block of its own under the newsletter. */
.research-block { max-width: 860px; margin: 2.6rem auto 0; padding: clamp(1.4rem, 4vw, 2.2rem); display: grid; gap: 1.2rem 2.4rem;
  grid-template-columns: minmax(0, 1fr); background: var(--card); border: 1px solid var(--violet-line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-form); text-align: left; }
@media (min-width: 760px) { .research-block { grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); align-items: center; } }
.research-eyebrow { display: block; margin-bottom: .45rem; font: var(--type-eyebrow); color: var(--violet-ink); }
.research-title { margin: 0 0 .6rem; font-family: var(--sans); font-weight: 700; font-size: clamp(1.5rem, 3.2vw, 1.85rem); line-height: 1.2;
  letter-spacing: -.02em; color: var(--ink); text-wrap: balance; }
.research-text p { margin: 0; max-width: 56ch; font-family: var(--serif); font-size: 1.05rem; line-height: 1.6; color: var(--muted); }
.research-actions { display: flex; flex-direction: column; align-items: stretch; gap: .4rem; }
.research-actions .btn { justify-content: center; }
.research-link { display: inline-block; padding: .7rem 0; font-family: var(--sans); font-weight: 600; font-size: .92rem; text-align: center;
  color: var(--violet-ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .15em; }
.research-link:hover { color: var(--ink); }
.research-block .research-note { grid-column: 1 / -1; margin: 0; font-family: var(--serif); font-style: italic; font-size: .9rem; color: var(--muted); }

/* Left over from before D159 and D54, found by the second design round's audit (2026-10-21). The last labels in
   capitals (404, FAQ, articles, blog cards, the Labo's feed) go to sentence case; tables and pricing badges keep theirs.
   The arrow stays on the primary button only, so secondary links lose theirs. The Labo counters band's two gradient
   lines become plain hairlines. The html prefix wins over the pages' own rules whatever the stylesheet order. */
html .not-found-eyebrow, html .faq-cat-title, html .faq-hero-chip, html .breadcrumb, html .meta-item, html .article-category,
html .back-link, html .toc-label, html .share-label, html .share-btn, html .related-card-cat, html .related-card-arrow,
html .loading-state, html .article-body blockquote cite, html .blog-card-category, html .featured-tag, html .reference-header,
html .feed-card-type { text-transform: none; letter-spacing: 0; }
html .feed-card-link svg, html .related-card-arrow svg { display: none; }
html .labs-counters-band::before, html .labs-counters-band::after { height: 1px; background: rgba(255, 255, 255, .14); }

/* Our range, by audience: the home page's four doors to the range page (D191) */
.audience-doors { padding: clamp(1.8rem, 4vw, 2.8rem) 0; }
.audience-doors .ad-label { font-family: var(--sans); font-size: .8rem; font-weight: 600; color: var(--violet-ink); margin: 0 0 .8rem; }
.audience-doors .ad-grid { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: clamp(1rem, 3vw, 2rem); border-top: 1px solid var(--hair); }
@media (min-width: 640px) { .audience-doors .ad-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .audience-doors .ad-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.audience-doors .ad-grid a { display: block; padding: 1rem 0; border-bottom: 1px solid var(--hair); text-decoration: none; min-height: 44px; }
.audience-doors .ad-grid b { display: block; font-family: var(--sans); font-weight: 600; font-size: 1rem; color: var(--ink); }
.audience-doors .ad-grid span { display: block; font-family: var(--serif); font-size: .95rem; line-height: 1.45; color: var(--muted); margin-top: .2rem; }
.audience-doors .ad-grid a:hover b, .audience-doors .ad-grid a:focus-visible b { color: var(--violet-ink); }
.audience-doors .ad-all { display: inline-block; margin-top: .9rem; font-family: var(--sans); font-size: .9rem; color: var(--violet-ink); text-decoration: underline; text-underline-offset: 3px; }

/* Five menu links (D195): the French header needs 1000px for them, so the burger stays until then, in both languages */
@media (min-width: 900px) and (max-width: 999px) {
  html .nav-links { display: none; }
  html .burger { display: flex; }
}

/* Design round 2 (Jade, 2026-10-21): titles that impose themselves (D186) and an editorial layout (D187).
   The `display` style, above `hero` (A1): Hanken Grotesk ExtraBold 800, as large as the screen allows (up to 140px),
   tight leading and tracking. bubble-brand's tokens take it at the brand book's next sync. */
:root {
  --type-display: 800 clamp(2.2rem, min(11.4vw, 13.4svh), 8.75rem)/.9 var(--sans);
  --type-display-tracking: -.045em;
}

/* A1 · L'affiche, on the home page (D186). The title in `display` spans the page and crosses the bubble, which grows to
   640px and leaves by the right edge; it keeps its 3D and its film (D177). The translating slot keeps a line of its own
   (D180). « votre métier » is violet-ink, since violet over the bubble's rim falls to 2.5:1 (D34). One ink rule under the
   title; on laptops the kicker and the buttons share the row below it, so both buttons stay on the first screen (D135,
   amended). On phones the hero aligns left, the bubble sits behind the title, and both buttons stay on the first screen.
   One title size rule: it replaces the short-laptop sizes of the first round (df7c871). */
.hero h1 { font: var(--type-display); letter-spacing: var(--type-display-tracking); max-width: none; text-wrap: wrap; position: relative; z-index: 2; margin: 0; }
html[lang="en"] .hero h1 { font-size: clamp(2.1rem, min(10.4vw, 12.6svh), 8.2rem); }
.hero h1 .swap { display: block; min-width: 0 !important; } /* the slot is a whole line here: no width to hold */
.hero h1 .hand { white-space: nowrap; letter-spacing: inherit; color: var(--violet-ink); }
.hero .wrap { position: relative; }
.hero-tag, .hero p.kicker, .hero .hero-cta-group, .hero .counters { position: relative; z-index: 2; }
/* The bubble still pops on click and opens its film on the first hover (D25, D177): the tag, the title and the kicker
   cross it and let pointer events through (none holds a link); the buttons and the counters keep theirs. */
.hero-tag, .hero h1, .hero p.kicker { pointer-events: none; }
@media (max-width: 1023px) {
  .hero { text-align: left; padding-bottom: 2.5rem; }
  .hero-tag { margin-bottom: 1.1rem; }
  .hero .wrap > .hero-orb { position: absolute; z-index: 1; margin: 0; right: -20vw; top: 2.6rem; width: min(66vw, 340px); height: min(66vw, 340px); }
  .hero h1 { padding-bottom: 1.1rem; border-bottom: 1px solid var(--ink); }
  .hero p.kicker { margin: 1.1rem 0 0; max-width: 36ch; }
  .hero .hero-ctas { align-items: flex-start; }
}
@media (min-width: 600px) and (max-width: 1023px) { .hero .hero-ctas { justify-content: flex-start; } }
/* phones: the spacing follows the screen's height (F17), and a little less side padding keeps the booking button on one
   line, so both buttons fit a 375x667 screen. Home page only (.hero): À propos has hero buttons of its own. */
@media (max-width: 599px) {
  .hero { padding-top: clamp(1rem, 3.5svh, 1.75rem); }
  .hero-tag { margin-bottom: clamp(.7rem, 2svh, 1.1rem); }
  .hero h1 { padding-bottom: clamp(.8rem, 2svh, 1.1rem); }
  .hero p.kicker { margin-top: clamp(.8rem, 2svh, 1.1rem); }
  .hero .hero-ctas { margin-top: clamp(1.1rem, 3svh, 2.1rem); }
  .hero .hero-ctas .btn { padding-left: 1.25rem; padding-right: 1.25rem; }
}
@media (min-width: 1024px) {
  .hero { padding-top: clamp(1.4rem, 4svh, 3rem); }
  .hero .wrap { grid-template-columns: minmax(20rem, 1fr) auto; column-gap: clamp(2rem, 4vw, 4rem); align-items: start; }
  .hero .wrap > * { grid-column: 1 / -1; }
  .hero .wrap > .hero-tag { grid-row: 1; margin-bottom: clamp(.8rem, 2svh, 1.4rem); }
  .hero .wrap > h1 { grid-row: 2; padding-bottom: clamp(1rem, 2.6svh, 1.6rem); border-bottom: 1px solid var(--ink); }
  .hero .wrap > p.kicker { grid-row: 3; grid-column: 1; margin: clamp(1rem, 2.6svh, 1.5rem) 0 0; max-width: 46ch; font-size: 1.12rem; }
  .hero .wrap > .hero-cta-group { grid-row: 3; grid-column: 2; margin: clamp(1rem, 2.6svh, 1.5rem) 0 0; }
  .hero .hero-cta-group .hero-ctas { margin-top: 0; align-items: center; justify-content: flex-start; flex-wrap: wrap; } /* they stack below 1280px */
  .hero .wrap > .hero-orb { position: absolute; grid-row: auto; z-index: 1; margin: 0; right: calc(-1 * clamp(6rem, 13vw, 14rem)); top: clamp(-1rem, -1svh, 0rem);
    width: min(42vw, 64svh, 640px); height: min(42vw, 64svh, 640px); }
  .hero .wrap > .counters { grid-row: 4; }
}

/* A1 on Professionnels: its own first screen, « le sommaire » (D186). The title in `display` in the left column, over an
   ink rule; the right column holds the bubble and, under it, the trust row as a cover's lines (ruled, Hanken 600). The
   kicker, the buttons and their line follow the title. On phones the bubble gets smaller, so the bigger title keeps the
   booking button on the first screen. */
.pro-hero h1 { font: 800 clamp(2.3rem, min(13vw, 11.6svh), 7rem)/.92 var(--sans); letter-spacing: -.045em; max-width: none; text-wrap: wrap; }
@media (min-width: 580px) and (max-width: 1023px) { .pro-hero h1 { font-size: clamp(2.3rem, 7vw, 4.6rem); } } /* tablets: the bubble stays beside the text */
.pro-hero h1 .accent-hand { font-size: 1em; letter-spacing: inherit; display: inline; white-space: nowrap; }
.pro-hero-trust { display: grid; gap: 0; margin-top: 2rem; padding: 0; border: 0; }
.pro-trust-item { font: 600 1rem/1.35 var(--sans); color: var(--ink); padding: .75rem 0; border-top: 1px solid var(--ink); gap: .75rem; }
.pro-trust-item:last-child { border-bottom: 1px solid var(--ink); }
.pro-trust-item .trust-dot { width: 12px; height: 12px; }
.pro-trust-item strong { font-weight: 800; }
@media (max-width: 579px) {
  .pro-hero-text { max-width: none; }
  .pro-hero .hero-orb-wrap { width: clamp(150px, min(46vw, 20svh), 200px); height: clamp(150px, min(46vw, 20svh), 200px); margin-bottom: 1rem; }
}
@media (max-width: 1023px) { .pro-hero h1 { padding-bottom: 1rem; border-bottom: 1px solid var(--ink); margin-bottom: 1.1rem; } }
@media (min-width: 1024px) {
  .pro-hero { padding: clamp(1.4rem, 4.5svh, 3.4rem) 0 clamp(2rem, 5svh, 4rem); }
  .pro-hero-inner { display: grid; grid-template-columns: minmax(0, 1fr) min(29vw, 46svh, 400px); column-gap: clamp(2.5rem, 5vw, 5rem); align-items: start; }
  .pro-hero-text { display: contents; }
  .pro-hero-eyebrow { grid-column: 1; grid-row: 1; margin-bottom: clamp(.8rem, 2svh, 1.4rem); }
  .pro-hero h1 { grid-column: 1; grid-row: 2; font-size: clamp(2.3rem, min(6.6vw, 11.6svh), 6rem); margin: 0 0 clamp(1rem, 2.6svh, 1.6rem); padding-bottom: clamp(1rem, 2.6svh, 1.6rem); border-bottom: 1px solid var(--ink); }
  .pro-hero-kicker { grid-column: 1; grid-row: 3; margin-bottom: clamp(1rem, 2.8svh, 1.8rem); max-width: 54ch; font-size: 1.12rem; }
  .pro-hero-ctas { grid-column: 1; grid-row: 4; }
  .pro-hero-text > .cta-note { grid-column: 1; grid-row: 5; }
  .pro-hero .hero-orb-wrap { position: relative; grid-column: 2; grid-row: 1 / span 2; right: auto; top: auto; transform: none; width: 100%; height: auto; aspect-ratio: 1; align-self: end; margin-bottom: clamp(1rem, 2.6svh, 1.6rem); }
  .pro-hero-trust { grid-column: 2; grid-row: 3 / span 3; margin: 0; align-self: start; }
}

/* A1 on the Labo and the FAQ (D186): the same system, so their empty right halves fill with their own content. The title
   in `display` across the width over an ink rule, then two columns from 1024px: the standfirst, and the page's links as
   ruled lines (the FAQ's categories with small film bubbles as bullets, D26). */
.labs-hero h1, .faq-hero h1 { font: var(--type-display); letter-spacing: var(--type-display-tracking); max-width: none; text-wrap: wrap; margin: 0; padding-bottom: clamp(1rem, 2.6svh, 1.6rem); border-bottom: 1px solid var(--ink); }
.labs-hero h1 .hand, .faq-hero h1 .hand { letter-spacing: inherit; white-space: nowrap; }
.labs-hero { padding-top: clamp(2rem, 6svh, 4.5rem); }
.faq-hero { padding-top: clamp(2rem, 6svh, 4.5rem); }
.faq-hero-chips { display: grid; gap: 0; margin: 0; }
.faq-hero-chip { font: 600 1rem/1.35 var(--sans); letter-spacing: 0; text-transform: none; color: var(--ink); border: 0; border-top: 1px solid var(--ink); border-radius: 0; padding: .7rem 0; display: flex; align-items: center; gap: .7rem; }
.faq-hero-chip:last-child { border-bottom: 1px solid var(--ink); }
.faq-hero-chip::before { content: ""; width: 12px; height: 12px; border-radius: 50%; flex: none; background: var(--film); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .6); }
.faq-hero-chip:hover, .faq-hero-chip:focus-visible { background: none; border-color: var(--ink); color: var(--violet-ink); }
.labs-hero-meta { font-size: .95rem; }
@media (min-width: 1024px) {
  .labs-hero-inner, .faq-hero-inner { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .72fr); column-gap: clamp(2.5rem, 6vw, 6rem); row-gap: 0; align-items: start; }
  .labs-hero-inner > *, .faq-hero-inner > * { grid-column: 1 / -1; }
  .labs-hero-label, .faq-hero-inner > .sec-num { margin-bottom: clamp(.8rem, 2svh, 1.4rem); }
  .labs-hero-inner > .labs-hero-kicker, .faq-hero-inner > p { grid-column: 1; margin-top: clamp(1.1rem, 3svh, 1.8rem); max-width: 52ch; }
  .labs-hero-inner > .labs-hero-meta, .faq-hero-inner > .faq-hero-chips { grid-column: 2; grid-row: 3; margin-top: 0; }
  .labs-hero-meta { display: grid; gap: 0; }
  .labs-hero-meta > * { padding: .7rem 0; border-top: 1px solid var(--ink); }
  .labs-hero-meta > .sep { display: none; }
  .labs-hero-meta > :last-child { border-bottom: 1px solid var(--ink); }
  .labs-hero-meta > :first-child, .faq-hero-chip:first-child { border-top: 0; }
}

/* A3 · Le chiffre en géant (D186). One result is the headline: « 3 h → 10 min » in `display` weight at poster size, in
   violet (D139), its label in Spectral italic under it, the two other results large on a ruled row below. Still on the one
   glass panel in front of the bubble's still frame (D158), on the home page and Professionnels; the film's copy (D177)
   follows. The headline breaks around its arrow only where it cannot hold one line (English, on phones). On the Labo,
   the four live counters fill the night band, without the panel: white 15.3:1, units violet-night 8.2:1, labels 80 %. */
.results { min-height: clamp(420px, 50vw, 700px); }
.results-glass { max-width: none; }
.results-bubble { width: min(66%, 680px); right: -9%; }
.results-glass .proofs { grid-template-columns: 1fr 1fr; column-gap: 0; row-gap: 0; }
.results-glass .proofs li { padding-top: clamp(1rem, 2.2vw, 1.5rem); border-radius: 0; align-content: start; } /* labels line up when one wraps */
.results-glass .proofs li.is-headline { grid-column: 1 / -1; order: -1; padding: 0 0 clamp(1.1rem, 2.4vw, 1.7rem); border-bottom: 1px solid var(--ink); gap: .7rem; }
.results-glass .proofs li.is-headline .proof-num { font: 800 clamp(3.3rem, 11.2vw, 10.6rem)/.9 var(--sans); letter-spacing: -.05em; white-space: normal; text-wrap: balance; }
.results-glass .proofs li.is-headline .proof-lbl { font: italic 400 clamp(1.15rem, 1.9vw, 1.6rem)/1.35 var(--serif); color: var(--ink); }
/* The English figure, « 3 hours → 10 min », is a third longer than the French (7.4 em against 5.5): its own size keeps
   it on one line on every laptop (the panel holds 1031 px from 1280 px up, 897 at 1024). In the film the panel is
   1000 px wide at most, 917 inside, so both figures take a lower cap there and the panel stays below the header. */
html[lang="en"] .results-glass .proofs li.is-headline .proof-num { font-size: clamp(3.3rem, 10.4vw, 8rem); }
.film-layer .results-glass .proofs li.is-headline .proof-num { font-size: clamp(3.3rem, 11.2vw, 9.6rem); }
html[lang="en"] .film-layer .results-glass .proofs li.is-headline .proof-num { font-size: clamp(3.3rem, 10.4vw, 7.2rem); }
.results-glass .proofs li:not(.is-headline) .proof-num { font: 800 clamp(2.3rem, 5.4vw, 5.2rem)/.95 var(--sans); letter-spacing: -.045em; }
.results-glass .proofs li:not(.is-headline):last-child { border-left: 1px solid var(--hair); padding-left: clamp(1rem, 3vw, 2.4rem); }
.results-glass .proofs li:not(.is-headline) { padding-right: clamp(.8rem, 2vw, 1.6rem); }
@media (max-width: 759px) {
  .results { padding-top: clamp(120px, 38vw, 200px); }
  .results-glass .proofs { grid-template-columns: 1fr; }
  .results-glass .proofs li:not(.is-headline):last-child { border-left: 0; padding-left: 0; border-top: 1px solid var(--hair); margin-top: 1rem; }
}
.labs-counters-band .counters-grid { grid-template-columns: 1fr 1fr; background: none; border-radius: 0; gap: 0; border-top: 1px solid rgba(255, 255, 255, .3); }
.labs-counters-band .counter-cell { background: none; padding: clamp(1.2rem, 3vw, 2.2rem) clamp(.6rem, 2vw, 1.6rem) clamp(1.2rem, 3vw, 2.2rem) 0; border-bottom: 1px solid rgba(255, 255, 255, .3); }
.labs-counters-band .counter-cell:nth-child(even) { border-left: 1px solid rgba(255, 255, 255, .3); padding-left: clamp(1rem, 4vw, 3rem); }
.labs-counters-band .counter-num { font: 800 clamp(2.6rem, 8.6vw, 7.6rem)/.9 var(--sans); letter-spacing: -.05em; }
.labs-counters-band .counter-lbl { font-size: clamp(.9rem, 1.4vw, 1.05rem); color: rgba(255, 255, 255, .8); max-width: none; }

/* B1 · La grille magazine (D187). Each section opens on an ink rule across the page; the rubric (the eyebrow) sits in the
   left margin, three columns of twelve, in ink; the title and its lead take the other nine, one step larger (up to
   3.9rem), always aligned left. Violet stays for key words, figures and links (D139). Section heads only: a head nested in
   a column keeps its place and takes the ink rubric. What sat centred under a centred head follows it to the left: the
   72 % fact above « Le problème », and the newsletter blocks of the home page, the blog and the articles. */
main .wrap > .sec-head { max-width: none; text-align: left; margin-left: 0; margin-right: 0; border-top: 1px solid var(--ink); padding-top: clamp(.9rem, 2vw, 1.3rem); }
main .wrap > .sec-head h2 { font-size: clamp(2rem, 4.6vw, 3.9rem); line-height: 1.02; letter-spacing: -.035em; max-width: 20ch; text-wrap: balance; margin-left: 0; margin-right: 0; }
/* À propos: the mission is a statement, not a section title; it steps up one size only (2.1 to 2.6rem) */
main .wrap > .mission-head h2 { font-size: clamp(1.6rem, 3.6vw, 2.6rem); line-height: 1.2; max-width: 32ch; }
main .wrap > .sec-head p { margin-left: 0; margin-right: 0; max-width: 56ch; font-size: 1.18rem; }
main .sec-head .sec-num, main .sec-head .sec-num-badge { color: var(--ink); font-weight: 600; }
main .wrap > .sec-head .sec-num, main .wrap > .sec-head .sec-num-badge { margin-bottom: .9rem; }
@media (min-width: 900px) {
  main .wrap > .sec-head { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 9fr); column-gap: clamp(1.5rem, 3vw, 2.5rem); align-items: start; margin-bottom: clamp(2.4rem, 4vw, 3.4rem); }
  main .wrap > .sec-head > * { grid-column: 2; }
  main .wrap > .sec-head > .sec-num, main .wrap > .sec-head > .sec-num-badge { grid-column: 1; grid-row: 1 / span 4; margin: .55rem 0 0; }
  main .wrap > .sec-head > h2 { grid-row: 1; }
}
.stat-lead { margin-left: 0; margin-right: 0; text-align: left; }
.newsletter, .newsletter-section { text-align: left; }
.newsletter-icon { margin-left: 0; margin-right: 0; justify-items: start; }
.newsletter .btns, .newsletter-btns { align-items: flex-start; justify-content: flex-start; }
.newsletter .research-block { margin-left: 0; }

/* A2 · Deux voix (D186): the second part of a two-part section title in Spectral italic, the voice of the trade, at 1.1em
   (Spectral's x-height is small). Section titles of the level of « Vous avez déjà essayé l'IA. » only, never a hero; for
   now « Le problème » alone, the other titles are chosen with Jade. It slides into place once as the title comes into view
   (site.js); reduced motion and no JavaScript show it in place. The page keeps its one violet word (D54). */
.voice-2 { font-family: var(--serif); font-style: italic; font-weight: 400; letter-spacing: -.01em; font-size: 1.1em; line-height: inherit; }
h2 .voice-2 { position: relative; top: 0; transition: opacity .45s var(--ease) .18s, top .45s var(--ease) .18s; }
h2 .voice-2.voice-2-wait { opacity: 0; top: .3em; }
@media (prefers-reduced-motion: reduce) { h2 .voice-2 { transition: none; } h2 .voice-2.voice-2-wait { opacity: 1; top: 0; } }

/* D159, the last leftovers (design review, 2026-10-21). The blog's, the newsletter page's and the legal notice's labels go
   to sentence case without letter-spacing, at the labels' size (badges on images keep theirs). Separators between label
   items go and the items keep their spacing: the top strip, À propos' credentials, the blog's cadence. */
html .blog-hero-eyebrow, html .blog-cadence, html .filter-label, html .filter-btn, html .articles-section-label,
html .featured-tile-badge, html .substack-badge, html .profile-label, html .legal-hero-back { text-transform: none; letter-spacing: 0; }
html .blog-cadence, html .filter-label, html .filter-btn, html .profile-label, html .legal-hero-back { font-size: .84rem; }
html .blog-hero-eyebrow { font-size: .86rem; }
html .articles-section-label { font-size: .95rem; }
.topbar .sep, .cred-sep { display: none; }
.topbar-inner { column-gap: 1.75rem; }
.blog-cadence > span { visibility: hidden; } /* the cadence's words are bare text: the spans stay as spacers */
html .blog-cadence { gap: .3rem .6rem; flex-wrap: wrap; justify-content: center; }
/* the arrow stays on the primary button only: the blog's featured tiles and the newsletter page's Substack link lose theirs */
html .featured-tile-cta svg, html .substack-link svg { display: none; }

/* The Professionnels bubble (D156, D54). On laptops it fills the summary's column (A1, which amends D156's size). On
   phones it takes D156's size, about 230px, and sits at the top right behind the title, as on the home page, so the
   booking button stays on the first screen (F17); « comme vous » turns violet-ink where it crosses the bubble (D34). The
   violet-to-peach aura around it goes, as on the home page: no gradient fill (D54); the bubble keeps its own halo, under
   the text. */
html .hero-orb-wrap .hero-aura { display: none; }
.pro-hero-text, .pro-hero-text > * { position: relative; z-index: 3; }
@media (max-width: 579px) {
  .pro-hero-inner { position: relative; }
  .pro-hero .hero-orb-wrap { position: absolute; margin: 0; right: -18vw; top: 1.4rem; transform: none;
    width: clamp(190px, min(60vw, 28svh), 280px); height: clamp(190px, min(60vw, 28svh), 280px); }
  .pro-hero h1 .accent-hand { color: var(--violet-ink); }
  /* the text column crosses the bubble: it lets pointer events through, so the bubble pops on click (D25); the buttons
     and the lines under them keep theirs */
  .pro-hero-text { pointer-events: none; }
  .pro-hero-ctas, .pro-hero-text > .cta-note, .pro-hero-trust { pointer-events: auto; }
  /* the spacing follows the screen's height, as on the home page (the page's own phone padding came before its base rule
     and never applied) */
  .pro-hero { padding-top: clamp(1rem, 3.5svh, 1.75rem); }
  .pro-hero-eyebrow { margin-bottom: clamp(.8rem, 2svh, 1.6rem); }
  .pro-hero-kicker { margin-bottom: clamp(1.2rem, 3.5svh, 2.4rem); }
}

/* The Ops Loop's still frame (D25). Where its 3D cannot run (reduced motion, no WebGL, three.js not loaded), the loop
   shows a still rendered from its own 3D scene, the comet at rest, instead of a ring drawn in CSS: never another drawing
   of the big bubble. One transparent WebP, 640px, 7 KB. */
.loop-fallback { border-radius: 0; box-shadow: none; background: url("/assets/brand/loop-still.webp") center / contain no-repeat; }

/* The phone menu is glass, like the header and the booking bar (D52): the page shows, blurred, behind it, instead of a
   98.5 % opaque sheet; the -webkit- line gives the blur to Safari 17 and older (D190). */
.mobile-menu { background: rgba(253, 252, 249, .88); background: color-mix(in srgb, var(--base) 88%, transparent);
  backdrop-filter: blur(18px) saturate(1.4); -webkit-backdrop-filter: blur(18px) saturate(1.4); }

/* AI-generated covers keep their picture with a credit (D207, refining D188): « Illustration générée par IA » sits on
   the picture itself, so it is never read as saying something about the article; the caption style in muted ink, on a
   cream backing that keeps it legible on any picture. blog-2026.js and blog-post.js add it to covers from the AI image
   service only. */
.ai-credit { position: absolute; left: .85rem; bottom: .85rem; z-index: 1; font: var(--type-caption); color: var(--muted);
  background: rgba(253, 252, 249, .92); padding: .2rem .6rem; border-radius: var(--r-chip); pointer-events: none; }

/* ============================================================================================================
   B2 · Moins de cartes, plus de page (D235, design round 2). The white boxes go (D161: the card kit is the enemy).
   Groups of points become ruled columns, like a magazine's three-column page: an ink rule on top, the label, a bigger
   title, the text in ink. The icon squares went with the boxes (their markup is gone from these sections): the words
   carry the point. Home (« Le problème », « Comment nous traduisons »), Professionnels (« Pourquoi nous »), the Labo
   (« Pourquoi tout partager ») and À propos (« Ce que nous croyons »). Long series become lists: the Labo's articles
   become a table of contents (type, title, summary, « Lire »); its rows keep their hairline on hover.
   ============================================================================================================ */
.lastmile.problem .lm-card, .approche-pillars .pillar, .why-grid .why-card, .pillars-grid .pillar-card {
  background: none; border: 0; border-radius: 0; box-shadow: none; transform: none; padding: clamp(.9rem, 2vw, 1.3rem) 0 clamp(1.2rem, 2vw, 1.6rem); border-top: 1px solid var(--ink); }
.lastmile.problem .lm-label { font: 600 .95rem/1.4 var(--sans); color: var(--muted); text-transform: none; letter-spacing: 0; margin-bottom: .4rem; }
.lastmile.problem h3, .approche-pillars .pillar h3, .why-grid .why-card h3, .pillars-grid .pillar-card h3 {
  font: 800 clamp(1.45rem, 2.3vw, 2rem)/1.08 var(--sans); letter-spacing: -.03em; margin: 0 0 .75rem; max-width: 18ch; text-wrap: balance; }
.lastmile.problem p, .approche-pillars .pillar p, .why-grid .why-card p, .pillars-grid .pillar-card p { font-size: 1.06rem; line-height: 1.6; color: var(--ink); }
.lastmile.problem, .approche-pillars, .why-grid, .pillars-grid { column-gap: clamp(1.5rem, 3.2vw, 2.8rem); row-gap: clamp(.6rem, 2vw, 1.4rem); }
/* the Labo's articles: a table of contents (the arrows of « Lire » already went with D159) */
.feed-grid { display: block; border-top: 1px solid var(--ink); }
.feed-grid .feed-card { display: grid; grid-template-columns: 1fr; gap: .35rem; background: none; border: 0; border-bottom: 1px solid var(--hair); border-radius: 0; padding: 1.05rem 0; transform: none; box-shadow: none; }
.feed-card-type { padding: 0; border: 0; background: none; text-transform: none; letter-spacing: 0; font-size: .9rem; color: var(--muted); }
.feed-type-dot { width: 10px; height: 10px; background: var(--film); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .6); }
.feed-card h3 { font: 700 clamp(1.1rem, 1.6vw, 1.3rem)/1.25 var(--sans); letter-spacing: -.015em; text-wrap: balance; }
.feed-card p { margin: 0; color: var(--ink); font-size: 1rem; }
.feed-card-footer { border: 0; padding: 0; justify-content: flex-start; }
/* from 900px, one row per article; « Lire » gets a column of its own, as wide as « Lire sur Substack », so the columns line
   up from row to row, and the type joins the row's baseline */
@media (min-width: 900px) {
  .feed-grid .feed-card { grid-template-columns: minmax(0, 2fr) minmax(0, 5fr) minmax(0, 5fr) 7rem; column-gap: clamp(1rem, 2.5vw, 2rem); align-items: baseline; }
  .feed-card-type { align-self: baseline; }
  .feed-card-footer { justify-content: flex-end; }
}

/* ============================================================================================================
   B3 · Le long texte, en magazine (D235, design round 2). A drop cap opens long texts: the articles (blog-post.js puts
   it on the first real paragraph) and the story on À propos. In the articles, quotes become pull quotes in large Spectral
   italic between two ink rules, hanging into the margin on wide screens; headings get a rule; « Dans cet article »
   becomes a ruled table of contents; the disclaimer becomes a margin note. The home page's line under « Construit par »
   stays a small pull quote (Jade, D235): it takes nothing from here.
   ============================================================================================================ */
.r2-dropcap { display: flow-root; } /* review R3: the paragraph contains its drop cap; a short opening never lets it spill */
.r2-dropcap::first-letter { float: left; font-family: var(--serif); font-weight: 600; font-style: normal; font-size: 4.6em; line-height: .8; margin: .06em .09em 0 -.03em; color: var(--ink); }
/* articles */
.article-title { font: 800 clamp(1.9rem, 7vw, 3.5rem)/.98 var(--sans); letter-spacing: -.04em; max-width: none; } /* review R2: three lines at most from 480 px; 18ch forced four to five */
.article-lead { font: italic 400 clamp(1.25rem, 2.2vw, 1.6rem)/1.4 var(--serif); color: var(--ink); max-width: 46ch; }
.article-meta { border-top: 1px solid var(--ink); border-bottom: 1px solid var(--ink); border-radius: 0; }
.article-body h2 { font-size: clamp(1.5rem, 2.6vw, 2.1rem); border-top: 1px solid var(--ink); padding-top: .8rem; margin-top: 3.4rem; text-wrap: balance; }
.article-body blockquote { background: none; border: 0; border-top: 1px solid var(--ink); border-bottom: 1px solid var(--ink); border-radius: 0; padding: 1.3rem 0 1.4rem; margin: 2.8rem 0; font-size: clamp(1.45rem, 2.5vw, 2.05rem); line-height: 1.28; }
.article-body ul li::before { width: 10px; height: 10px; top: .62em; opacity: 1; background: var(--film); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .6); }
/* p.: the English template has the disclaimer inside the article body, whose own `.article-body p` would keep it at body size */
p.article-disclaimer { background: none; border: 0; border-radius: 0; padding: .6rem 0 0; border-top: 1px solid var(--ink); max-width: 40ch; font-family: var(--sans); font-style: normal; font-size: .82rem; line-height: 1.5; color: var(--muted); }
/* the English template sets the date above the title and the cover inside the article's column: the title keeps a gap
   under the date's rule, and the cover one above the disclaimer's rule, which would otherwise read as its caption */
.article-meta + .article-title { margin-top: 1.6rem; }
.article-grid .article-hero-image { margin-bottom: 2.2rem; }
.toc { background: none; border: 0; border-top: 1px solid var(--ink); border-radius: 0; padding: .8rem 0 0; position: static; } /* review R1: transparent, it slid over « Partager » when sticky */
.toc-label { text-transform: none; letter-spacing: 0; font-size: .95rem; color: var(--ink); }
.toc-list li { border-left: 0; margin-left: 0; border-bottom: 1px solid var(--hair); }
.toc-list li.toc-h3 { padding-left: 1rem; }
.toc-list a { border-left: 0; margin-left: 0; padding: .55rem 0; font-size: .86rem; color: var(--ink); }
.toc-list a.active, .toc-list a:hover { color: var(--violet-ink); }
/* the quote hangs 4.5rem into the margin only where the margin can take it with the page's gutter: from 1340px it starts
   40px from the screen's edge; from 1100px, as first drawn, it lost its first 40px off the screen up to 1180px */
@media (min-width: 1340px) { .article-body blockquote { margin-left: -4.5rem; } }
/* the story on À propos opens on a drop cap */
.story-beat:first-child p:first-of-type::first-letter { float: left; font-family: var(--serif); font-weight: 600; font-size: 4.4em; line-height: .8; margin: .06em .09em 0 -.03em; color: var(--ink); }

/* ============================================================================================================
   C2 · Le duotone (D235, design round 2). The founders' real photos in two of the brand's colours, ink and warm: on
   À propos, a portrait diptych beside the title gives the page its own first screen; on phones it follows the buttons.
   WebP, 600x800, 10 and 28 KB. The home page's night band in night and violet waits for the photo session: not here.
   ============================================================================================================ */
.r2-c2-dip { display: grid; margin: 0; grid-template-columns: 1fr 1fr; gap: clamp(.5rem, 1vw, .8rem); }
.r2-c2-dip img { display: block; width: 100%; height: auto; aspect-ratio: 3 / 4; object-fit: cover; }
.r2-c2-dip figcaption { grid-column: 1 / -1; font: 400 .82rem/1.45 var(--sans); color: var(--muted); text-align: left; margin-top: .2rem; }
@media (min-width: 1024px) {
  .hero-about { text-align: left; padding-top: clamp(2.5rem, 6svh, 4.5rem); }
  .hero-about .wrap { display: grid; grid-template-columns: minmax(0, 1fr) min(40%, 470px); column-gap: clamp(2.5rem, 5vw, 5rem); align-items: center; }
  .hero-about .wrap > * { grid-column: 1; }
  .hero-about .wrap > .r2-c2-dip { grid-column: 2; grid-row: 1 / span 4; }
  .hero-about h1, .hero-about .hero-sub { margin-left: 0; margin-right: 0; }
  .hero-about .hero-ctas { justify-content: flex-start; }
}
@media (max-width: 1023px) { .r2-c2-dip { max-width: 420px; margin: 2.2rem auto 0; } }

/* ============================================================================================================
   C3 · Des documents (D235, design round 2). What runs and is public is shown as a document: on the home page, in the
   newsletter section, a capture of our own article « Le dernier kilomètre », set like a printed page, with its caption.
   The document only (the porting notes' « document only » variant): the « Photo à faire » and « Capture à faire »
   frames were for the mock-ups and never ship. One column of 520px at most, aligned left like the rest of the section
   since B1 (D187); the prototype centred it because its section was still centred.
   ============================================================================================================ */
.r2-c3-docs { display: grid; grid-template-columns: minmax(0, 520px); margin: 0 0 clamp(2rem, 4vw, 3rem); }
.r2-doc { margin: 0; }
.r2-doc img { display: block; width: 100%; height: auto; background: var(--card); border: 1px solid var(--hair); border-radius: 4px; box-shadow: 0 30px 60px -38px rgba(34, 29, 22, .45), 0 2px 6px -2px rgba(34, 29, 22, .12); }
.r2-doc figcaption { font: 400 .86rem/1.45 var(--sans); color: var(--muted); margin-top: .7rem; text-align: left; }
/* review R5: the document never separates the promise from its button: in the markup it follows the buttons. From
   900 px, the head spans the section, then the envelope and the buttons on the left, the document on the right. */
.newsletter .r2-c3-docs { margin: clamp(1.8rem, 4vw, 2.8rem) 0 0; }
@media (min-width: 900px) {
  .newsletter .wrap { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 460px); grid-template-rows: auto auto 1fr; column-gap: clamp(2rem, 5vw, 5rem); align-items: start; }
  .newsletter .sec-head { grid-column: 1 / -1; }
  .newsletter .newsletter-icon { grid-column: 1; grid-row: 2; }
  .newsletter .btns { grid-column: 1; grid-row: 3; align-self: start; margin-top: .4rem; } /* grid margins do not collapse: keeps the block-flow gap under the envelope */
  .newsletter .r2-c3-docs { grid-column: 2; grid-row: 2 / span 2; margin: 0; }
}

/* ============================================================================================================
   Particuliers and Individuals, first screen (wording team review, 22 October, section 5; D241). On laptops the two
   hero buttons sat below the fold (their bottom at 902 to 911 px in French from 1024 x 700 to 1536 x 730). The title
   keeps its words at a smaller size on a wider measure, and from 1000 px the section starts higher and the lead
   sits closer to the buttons. .hero-part exists only on these two pages; phones keep their 2rem title.
   ============================================================================================================ */
.hero-part h1 { font-size: clamp(2rem, 3.9vw, 2.8rem); max-width: 20ch; }
@media (min-width: 1000px) {
  .hero-part { padding-top: clamp(2.5rem, 5vw, 4rem); }
  .hero-part-kicker { margin-bottom: 1.25rem; }
}

/* The pricing tiers' buttons may wrap: at exactly 900 px the longer « Réserver un diagnostic » label pushed the third
   tier 1 px past the page (wording team, C13). */
.pricing .tier .btn { white-space: normal; min-width: 0; text-align: center; }
