/* ============================================================
   modus. — base.css
   Tokens, reset, shared components (buttons, header, footer,
   loader, cursor). Section styles live in home.css.
   ============================================================ */

:root{
  --navy:#16283C;
  --navy-2:#101d2c;
  --green:#1D9E75;
  --green-lit:#29c78f;
  /* green as TEXT on navy: #1D9E75 lands at 4.42:1, just under AA for the
     12.5px mono it's used at. --green-lit clears it at 6.89:1. Fills, rules
     and the logo dot keep --green; only type on navy uses this. */
  --green-on-navy:#29c78f;
  /* and its mirror: green as TEXT on paper/white. #1D9E75 is 3.21:1 there,
     which fails AA at anything under 18.66px. #177F5E clears it at 4.70:1. */
  --green-ink:#177F5E;
  --paper:#F8F9F7;
  --light:#E3E7E2;
  /* --slate is the NAVY-side muted grey (6.35:1 on --navy).
     --slate-ink is its counterpart on paper/white (5.48:1 / 5.79:1).
     The old single #7E8A87 failed AA on both surfaces (3.39 / 4.19). */
  --slate:#A2AAB0;
  --slate-ink:#5C6866;
  --muted:#46555F;
  --white:#FFFFFF;
  --sans:'DM Sans','DM Sans Fallback',Inter,system-ui,sans-serif;
  --mono:'JetBrains Mono',ui-monospace,monospace;
  --ease:cubic-bezier(.22,.8,.28,1);
}

/* Metric-matched fallback. The hero is vertically centred, so when DM Sans
   swapped in, the h1's line box changed height and the whole centred block
   jumped 76px — one shift worth CLS 0.141, over Google's 0.1 threshold.
   Values derived from the real font tables, not guessed:
     DM Sans  upm 1000, asc 992, desc 310, x-height 526
     Arial    upm 2048, asc 1854, desc 434, x-height 1062
   size-adjust matches x-height (526/1000 ÷ 1062/2048 = 101.44%); the
   overrides are then expressed against that adjusted em. Keeps
   font-display:swap, so the brand face still renders — it just lands
   on top of a fallback that already occupies the same space. */
@font-face{
  font-family:'DM Sans Fallback';
  src:local('Arial'),local('Helvetica Neue'),local('Liberation Sans');
  size-adjust:101.44%;
  ascent-override:97.79%;
  descent-override:30.56%;
  line-gap-override:0%;
}

*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:auto}
body{
  font-family:var(--sans);background:var(--navy);color:var(--paper);
  line-height:1.5;font-size:17px;-webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
body.loading{overflow:hidden;height:100vh}
a{color:inherit;text-decoration:none}
img,svg,canvas{max-width:100%;display:block}
.wrap{max-width:1200px;margin:0 auto;padding:0 32px}
.mono{font-family:var(--mono)}
/* the wordmark's period only ever renders on navy, and appears at 12px in
   .dquote-src where --green lands at 4.42:1 */
.dot{color:var(--green-on-navy)}

/* Language toggle — leftover from when language was runtime state on <body>.
   It is now a URL (/ = FR, /en/ = EN) and nothing sets .lang-en any more, so
   these two rules are inert. Kept as a safety net in case a page is ever
   served with the old markup mid-migration.
   The stray `}` that used to sit below them is gone (found 2026-08-16): the
   stylesheet carried one more closing brace than opening from line 53 down.
   Browsers discard an unmatched brace at top level so nothing rendered wrong,
   but it silently ends the block anything appended near it would land in. */
body:not(.lang-en) .en{display:none!important}
body.lang-en .fr{display:none!important}

/* ---- shared typography ---- */
h1,h2,h3{font-weight:700;letter-spacing:-.03em}
h2{line-height:1.04;font-size:clamp(32px,5vw,58px);max-width:17ch}
.lead{font-size:clamp(17px,1.5vw,20px);color:rgba(248,249,247,.72);max-width:60ch;margin-top:22px}
.sec-eyebrow{font-family:var(--mono);font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--slate);
  display:flex;align-items:center;gap:14px;margin-bottom:26px}
.sec-eyebrow::before{content:"";width:34px;height:1.5px;background:var(--green);flex:none}
.paper{background:var(--paper);color:var(--navy)}
.paper .lead{color:var(--muted)}
/* the same muted role, inverted for the light sections */
.paper .sec-eyebrow{color:var(--slate-ink)}

/* ============ FOCUS ============
   Before this rule the only authored focus style on the site was
   .cm-field input:focus. Every other control fell back to the UA's ~1px
   near-black ring, which is invisible on the navy header and dark sections,
   and computed to outline-width:0 on the FR/EN buttons. */
:focus-visible{outline:2px solid var(--green-lit);outline-offset:3px;border-radius:3px}
.paper :focus-visible{outline-color:var(--green)}
/* keyboard users get the ring; pointer users never see it */
:focus:not(:focus-visible){outline:none}
.pad{padding:96px 0}
section{position:relative}

/* reveal helpers — hidden only when JS + animation are actually running */
.js-anim .rv{opacity:0;transform:translateY(40px)}
.js-anim .rv-l{opacity:0;transform:translateX(-40px)}

/* ============ PRELOADER ============ */

/* ============ CUSTOM CURSOR ============ */

/* ============ HEADER ============ */
header{position:fixed;top:0;left:0;right:0;z-index:90;transition:transform .5s var(--ease)}
header.hidden{transform:translateY(-110%)}
header .bar{
  position:absolute;inset:14px 14px auto 14px;height:calc(100% - 20px);border-radius:16px;
  /* was rgba(16,29,44,.6) — over the navy hero that composites near-opaque
     (slate lang button 6.9:1, nav links 8.6:1), but the same .6 over a paper
     section (#F8F9F7) composites to a mid grey ~rgb(109,117,125), dropping
     the lang button to 1.98:1 and nav links to 3.14:1 — both fail AA. .88
     keeps the glass-pill look (still blurred, still a hair translucent) but
     composites close enough to navy on every section: slate 5.1–7.1:1,
     nav links 6.8–8.7:1 depending on what's behind it. */
  background:rgba(16,29,44,.88);-webkit-backdrop-filter:blur(14px) saturate(1.3);backdrop-filter:blur(14px) saturate(1.3);
  border:1px solid rgba(255,255,255,.08);opacity:0;transition:opacity .4s;pointer-events:none;
}
header.solid .bar{opacity:1}
.nav{display:flex;align-items:center;justify-content:space-between;height:82px}
.logo{font-weight:700;font-size:26px;letter-spacing:-.03em;position:relative;z-index:2}
.nav-right{display:flex;align-items:center;gap:30px;position:relative;z-index:2}
.nav-links{display:flex;gap:30px}
.nav-links a{font-size:14.5px;font-weight:500;color:rgba(248,249,247,.72);position:relative;transition:color .25s}
.nav-links a::after{content:"";position:absolute;left:0;bottom:-6px;width:100%;height:1.5px;background:var(--green);
  transform:scaleX(0);transform-origin:right;transition:transform .35s var(--ease)}
.nav-links a:hover{color:var(--paper)}
.nav-links a:hover::after{transform:scaleX(1);transform-origin:left}
.lang{font-family:var(--mono);font-size:12px;display:flex;gap:2px;border:1px solid rgba(255,255,255,.14);border-radius:6px;overflow:hidden}
/* was `transition:.2s` (shorthand = all properties), which animated the focus
   outline in from 0 — the ring read as absent for the first ~200ms */
/* The switcher is two <a> now, not two <button> — each language is a real URL,
   so switching navigates. `button` stays in the selectors only so a page that
   still carries the old markup doesn't lose its styling mid-migration. */
.lang button,.lang a{font-family:inherit;font-size:inherit;border:0;background:transparent;color:var(--slate);padding:6px 10px;
  display:inline-flex;align-items:center;justify-content:center;text-decoration:none;
  transition:background-color .2s,color .2s}
.lang button.on,.lang a.on{background:var(--paper);color:var(--navy)}
.lang a:hover{color:var(--paper)}
.lang a.on:hover{color:var(--navy)}
/* the ring needs to sit outside the clipping parent, or overflow:hidden eats it */
.lang{overflow:visible}
.lang button:first-child,.lang a:first-child{border-radius:5px 0 0 5px}
.lang button:last-child,.lang a:last-child{border-radius:0 5px 5px 0}
/* 34×28 is 41% of the WCAG 2.5.5 target area, and the two sit adjacent.
   Touch pointers get the full 44×44; fine pointers keep the compact pill. */
@media(pointer:coarse){
  .lang button,.lang a{min-width:44px;min-height:44px;padding:6px}
  /* block links in the chrome; inline text links are exempt from 2.5.5 */
  .logo{display:inline-flex;align-items:center;min-height:44px}
  /* Footer links were the two this block missed. Measured 2026-08-16 at 390px
     with a coarse pointer: .foot-nav a rendered 74×38.8, .foot-reach a 99×29.5,
     and the phone and email sat 16.8px apart centre-to-centre — the two most
     mis-tappable links on the site, on every page. */
  .foot-nav a,.foot-reach a{display:inline-flex;align-items:center;min-height:44px;min-width:44px}
  .foot-reach{gap:0}
}

/* Screen-reader-only text. Stays in the accessibility tree and in the
   accessible name, takes no visual space. Not `display:none`, which would
   remove it from both. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

/* ============ BUTTONS (pill, magnetic) ============ */
.btn{display:inline-flex;align-items:center;gap:10px;font-weight:700;font-size:15px;padding:15px 28px;border-radius:100px;
  position:relative;overflow:hidden;isolation:isolate;border:0;font-family:var(--sans);
  transition:color .35s var(--ease);will-change:transform}
.btn::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;transition:transform .45s var(--ease);transform-origin:bottom}
.btn-primary{background:var(--green);color:#04140d}
.btn-primary::before{background:var(--green-lit);transform:scaleY(0)}
.btn-primary:hover::before{transform:scaleY(1)}
.btn-ghost{border:1.5px solid rgba(255,255,255,.24);color:var(--paper);background:transparent}
.btn-ghost::before{background:var(--paper);transform:scaleY(0)}
.btn-ghost:hover{color:var(--navy)}
.btn-ghost:hover::before{transform:scaleY(1)}
.btn .arw{transition:transform .35s var(--ease)}
.btn:hover .arw{transform:translateX(4px)}
.btn-sm{padding:11px 20px;font-size:13.5px}

/* ============ MOBILE MENU ============ */
.hamburger{display:none}
.mobile-menu{display:none}
@media(max-width:960px){
  header{z-index:110}
  .nav-links{display:none}
  .hamburger{
    display:inline-flex;flex-direction:column;justify-content:center;gap:5px;
    width:44px;height:44px;padding:10px;border:0;background:transparent;
    -webkit-tap-highlight-color:transparent;position:relative;z-index:2;
  }
  .hamburger span{display:block;height:2px;width:100%;background:var(--paper);border-radius:2px;transition:transform .3s ease,opacity .25s ease}
  .hamburger.open span:nth-child(1){transform:translateY(7px) rotate(45deg)}
  .hamburger.open span:nth-child(2){opacity:0}
  .hamburger.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

  .mobile-menu{
    display:flex;flex-direction:column;justify-content:center;gap:6px;
    position:fixed;inset:0;z-index:100;background:var(--navy-2);
    padding:96px 28px 40px;
    opacity:0;visibility:hidden;transform:translateY(-8px);
    transition:opacity .3s ease,transform .3s ease,visibility .3s;
  }
  .mobile-menu.open{opacity:1;visibility:visible;transform:none}
  .mm-links{display:flex;flex-direction:column}
  .mm-links a{
    color:var(--paper);font-weight:700;letter-spacing:-.02em;
    font-size:30px;padding:16px 0;border-bottom:1px solid rgba(255,255,255,.1);
  }
  .mm-links a:active{color:var(--green)}
  .mm-cta{margin-top:28px;justify-content:center;padding:16px 26px;font-size:17px}
  /* the header CTA is hidden under 560px; the phone must not be */
  .mm-tel{display:flex;flex-direction:column;gap:4px;margin-top:22px;padding:14px 0;
    text-align:center;min-height:44px;justify-content:center}
  .mm-tel-k{font-family:var(--mono);font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--slate)}
  .mm-tel-v{font-weight:700;font-size:26px;letter-spacing:-.02em;color:var(--paper)}
  .mm-tel:active .mm-tel-v{color:var(--green)}
  body.menu-open{overflow:hidden}
}
@media(max-width:560px){
  .wrap{padding:0 22px}
  .header-cta{display:none}
  .mm-links a{font-size:26px;padding:14px 0}
}

/* ============ FOOTER ============ */
/* Two rows. The top row is navigation and how to reach a human; the bottom
   row is the legal identity line. Before 2026-08-15 the footer carried the
   wordmark, a tagline and the copyright and nothing else — no link out of it
   at all, which meant the privacy policy had nowhere to live. */
footer{background:var(--navy-2);border-top:1px solid rgba(255,255,255,.08);padding:40px 0 34px}
footer .wrap{display:flex;flex-direction:column;gap:26px}
footer .logo{font-size:22px}
footer small{font-size:12.5px;color:var(--slate)}

.foot-top{display:flex;justify-content:space-between;align-items:flex-start;gap:28px 40px;flex-wrap:wrap}

.foot-nav{display:flex;gap:8px 26px;flex-wrap:wrap;align-items:center;flex:1 1 320px}
.foot-nav a{
  font-size:14px;color:var(--light);
  /* 44px tap target on touch without pushing the desktop row taller */
  padding:11px 0;line-height:1.2;
}
/* The comment above was the intent; the arithmetic fell 5px short — 14px text
   at line-height 1.2 is 16.8px, plus 22px of padding, renders 38.8px. Measured
   on all 14 pages at 390px with a coarse pointer. min-height closes it without
   touching the desktop row, which is governed by padding, not height. */
.foot-nav a:hover{color:var(--green-on-navy)}
.foot-nav a[aria-current="page"]{color:var(--green-on-navy)}

.foot-reach{display:flex;flex-direction:column;gap:2px;align-items:flex-end;text-align:right}
.foot-reach a{
  font-family:var(--mono);font-size:13.5px;letter-spacing:.01em;
  color:var(--light);padding:6px 0;white-space:nowrap;line-height:1.3;
}
.foot-reach a:hover{color:var(--green-on-navy)}

.foot-bot{
  display:flex;justify-content:space-between;align-items:center;gap:10px 20px;flex-wrap:wrap;
  border-top:1px solid rgba(255,255,255,.07);padding-top:20px;
}
/* NEQ 1182420712 — Conseil Modus inc., constituted 2026-08-03.
   Public and checkable at the Registraire des entreprises; it is the
   cheapest proof-of-existence a firm that stays faceless can offer. */

@media(max-width:720px){
  .foot-top{flex-direction:column;align-items:flex-start;gap:14px}
  .foot-nav{flex-direction:column;align-items:flex-start;gap:0;flex:none}
  .foot-reach{align-items:flex-start;text-align:left}
  .foot-bot{flex-direction:column;align-items:flex-start}
}

/* ============ REDUCED MOTION — everything visible, nothing moves ============ */
@media(prefers-reduced-motion:reduce){
  .js-anim .rv,.js-anim .rv-l{opacity:1;transform:none}
  html{scroll-behavior:auto}
}

/* ============ SKIP LINK ============ */
/* Every page put 4 (mobile) to 6 (desktop) header controls in front of the
   content, on every single page, with no way past them from the keyboard.
   Off-screen until focused — the one pattern where hiding from sight but not
   from the tab order is correct. Sits above the fixed header (z 90) and the
   mobile action bar (z 120). */
.skip-link{
  position:absolute;left:16px;top:-64px;z-index:200;
  display:inline-flex;align-items:center;min-height:44px;
  padding:12px 20px;border-radius:0 0 10px 10px;
  background:var(--green-lit);color:#04140d;
  font-weight:700;font-size:14.5px;
  transition:top .18s var(--ease);
}
.skip-link:focus-visible{top:0;outline-offset:-4px}
@media(prefers-reduced-motion:reduce){ .skip-link{transition:none} }

/* ============================================================
   Newsletter slide-in (.nlc) — injected by js/app/newsletter.js.
   A corner card, never an interstitial: it must not cover content,
   so it stays small, bottom-right, below the mobile action bar's
   z-index conflicts are avoided by sitting at z 110 (header 90,
   action bar 120 — the bar may overlap it on mobile, which is fine:
   the card sits above the bar's height there). */
.nlc{
  position:fixed;right:20px;bottom:20px;z-index:110;
  width:min(360px,calc(100vw - 40px));
  background:var(--navy-2);color:var(--paper);
  border:1px solid rgba(255,255,255,.12);border-radius:14px;
  padding:22px 22px 18px;box-shadow:0 18px 50px rgba(0,0,0,.45);
  opacity:0;transform:translateY(24px);pointer-events:none;
  transition:opacity .45s var(--ease),transform .45s var(--ease);
}
.nlc.is-on{opacity:1;transform:none;pointer-events:auto}
@media(prefers-reduced-motion:reduce){ .nlc{transition:none} }
.nlc-x{
  position:absolute;top:6px;right:6px;width:36px;height:36px;
  background:none;border:0;color:var(--slate);font-size:22px;
  line-height:1;cursor:pointer;border-radius:8px;
}
.nlc-x:hover,.nlc-x:focus-visible{color:var(--paper)}
.nlc-tag{
  font-family:var(--mono);font-size:11px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--green-on-navy);margin-bottom:8px;
}
.nlc-title{font-size:18.5px;font-weight:700;line-height:1.25;margin-bottom:8px}
.nlc-body{font-size:14px;color:var(--slate);line-height:1.5;margin-bottom:14px}
.nlc-input{
  width:100%;padding:11px 14px;border-radius:9px;
  border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.06);
  color:var(--paper);font-family:var(--sans);font-size:15px;
}
.nlc-input::placeholder{color:var(--slate)}
.nlc-input:focus-visible{outline:2px solid var(--green-lit);outline-offset:1px}
.nlc-hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
.nlc-row{display:flex;gap:10px;align-items:center;margin-top:10px}
.nlc-go{
  flex:0 0 auto;padding:11px 18px;border:0;border-radius:9px;cursor:pointer;
  background:var(--green);color:#fff;font-weight:700;font-size:14.5px;
  font-family:var(--sans);
}
.nlc-go:hover{background:var(--green-lit);color:#04140d}
.nlc-no{
  background:none;border:0;color:var(--slate);font-size:13.5px;
  cursor:pointer;font-family:var(--sans);padding:10px 6px;
}
.nlc-no:hover,.nlc-no:focus-visible{color:var(--paper);text-decoration:underline}
.nlc-status{font-size:13px;margin-top:8px;min-height:1em;color:var(--green-on-navy)}
.nlc-status.err{color:#ff9d8f}
.nlc-fine{font-size:11.5px;color:var(--slate);margin-top:8px}
.nlc-fine a{text-decoration:underline}
.nlc-fine a:hover{color:var(--paper)}
@media print{ .nlc{display:none} }
@media(max-width:640px){
  /* clear the sticky mobile action bar (Book a diagnostic) */
  .nlc{right:12px;left:12px;width:auto;bottom:96px;padding:18px 18px 14px}
}

/* ====== LANGUAGE SUGGESTION (other-domain nudge — js/app/lang-suggest.js) ======
   Bottom-RIGHT on purpose: the consent banner owns bottom-left, and the two must
   never overlap. This one also waits until consent has been answered. */
.langsug{position:fixed;right:24px;bottom:24px;z-index:1190;width:min(340px,calc(100vw - 48px));
  background:var(--navy);color:var(--paper);border-radius:18px;padding:20px 20px 16px;
  box-shadow:0 24px 60px rgba(8,16,26,.35);border:1px solid rgba(255,255,255,.08);
  font-family:var(--sans);opacity:0;transform:translateY(14px);
  transition:opacity .4s var(--ease),transform .4s var(--ease)}
.langsug.on{opacity:1;transform:none}
.langsug-t{margin:0 0 6px;font-family:var(--mono);font-size:11.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--green-on-navy)}
.langsug-b{margin:0 0 14px;font-size:14px;line-height:1.5;color:rgba(248,249,247,.86)}
.langsug-a{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.langsug-a .btn{padding:10px 18px;font-size:13.5px}
.langsug-x{background:none;border:0;padding:8px 4px;cursor:pointer;font:inherit;font-size:13.5px;
  color:rgba(248,249,247,.62);text-decoration:underline;text-underline-offset:3px}
.langsug-x:hover{color:var(--paper)}
.langsug-x:focus-visible,.langsug-go:focus-visible{outline:2px solid var(--green-lit);outline-offset:3px}
@media(max-width:640px){ .langsug{left:12px;right:12px;bottom:12px;width:auto;padding:18px 18px 14px} }
@media(prefers-reduced-motion:reduce){ .langsug{transition:none;transform:none} }

/* ============ CONSENT (analytics banner — js/app/consent.js) ============ */
.consent{position:fixed;left:24px;bottom:24px;z-index:1200;width:min(380px,calc(100vw - 48px));
  background:var(--navy);color:var(--paper);border-radius:18px;padding:22px 22px 18px;
  box-shadow:0 24px 60px rgba(8,16,26,.35);border:1px solid rgba(255,255,255,.08);
  font-family:var(--sans);opacity:0;transform:translateY(14px);
  transition:opacity .4s var(--ease),transform .4s var(--ease)}
.consent.on{opacity:1;transform:none}
.consent-t{margin:0 0 6px;font-family:var(--mono);font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--green-on-navy)}
.consent-b{margin:0 0 16px;font-size:14px;line-height:1.5;color:rgba(248,249,247,.86)}
.consent-b a{color:var(--paper);text-decoration:underline;text-underline-offset:3px}
.consent-a{display:flex;gap:10px;flex-wrap:wrap}
.consent-a .btn{padding:11px 20px;font-size:13.5px}
@media(max-width:640px){ .consent{left:12px;right:12px;bottom:96px;width:auto;padding:18px 18px 14px} }
@media(prefers-reduced-motion:reduce){ .consent{transition:none;transform:none} }
