/* Fidé’iti showcase, "Eclipse" layout: production stylesheet for index.html and
   merci.html. Implements the design handoff (vitrine 1c) at its stated values.

   Static by design, zero JavaScript (ADR 0027). Two consequences shape this
   sheet:
   1. The contact drawer is a pure CSS disclosure: one visually hidden checkbox
      (#contact-toggle) plus three <label> triggers (open CTA, scrim, close).
   2. The showcase CSP (deploy/Caddyfile, snippet csp_showcase) sets
      style-src 'self', which blocks inline stylesheets and inline style
      attributes alike. Every declaration therefore lives here, including the
      per-sphere orbital values the handoff reference carried inline: see the
      .n1 to .n12 classes. */

/* ---- Fonts (self-hosted, OFL; URLs relative to this sheet, served at
   /site-assets/styles.css) ---- */
@font-face{font-family:'Space Grotesk';src:url('fonts/SpaceGrotesk-Variable.ttf') format('truetype');font-weight:300 700;font-style:normal;font-display:swap}
@font-face{font-family:'Hanken Grotesk';src:url('fonts/HankenGrotesk-Variable.ttf') format('truetype');font-weight:100 900;font-style:normal;font-display:swap}

/* ---- Design tokens (dark theme, handoff values) ---- */
:root{
  --bg:#1C1C1A; --surface:#262624; --surface-2:#30302C; --surface-strong:#121211;
  --text:#F4F2EA; --text-muted:#A6A29A;
  --accent:#2BE6D2; --on-accent:#06231F;
  --line:rgba(244,242,234,.06); --line-strong:rgba(244,242,234,.14);
  --elev-2:0 10px 26px -14px rgba(0,0,0,.70);
  --elev-3:0 18px 40px -18px rgba(0,0,0,.78);
  --elev-4:0 14px 40px -12px rgba(0,0,0,.62);
  --elev-accent:0 10px 24px -6px rgba(43,230,210,.42);
  --ease:cubic-bezier(.2,0,0,1);
  --font-display:'Space Grotesk','Hanken Grotesk',system-ui,-apple-system,'Segoe UI',sans-serif;
  --font-text:'Hanken Grotesk',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  /* Fractal grain, inline data URI: no network request, allowed by img-src data: */
  --grain:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  color-scheme:dark;
}

/* ---- Base ---- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{margin:0;min-height:100vh;min-height:100dvh;
  font-family:var(--font-text);color:var(--text);
  background-color:var(--bg);
  background-image:radial-gradient(120% 80% at 50% -10%,rgba(255,253,245,.015),transparent 60%),
                   radial-gradient(140% 120% at 50% 120%,rgba(0,0,0,.28),transparent 55%);
  isolation:isolate;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
body::before{content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
  background-image:var(--grain);background-size:140px 140px;opacity:.045;mix-blend-mode:overlay}
img{display:block;max-width:100%}
h1,h2,h3,p{margin:0}
a{color:var(--accent)}
:focus-visible{outline:3px solid var(--accent);outline-offset:2px}
::selection{background:var(--accent);color:var(--on-accent)}
/* Visually hidden, still read by assistive tech */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap}

/* ---- Stage ---- */
.stage{position:relative;min-height:100vh;min-height:100dvh;overflow:hidden;z-index:1}

/* ---- Top bar: the merchant entrance, anchored to the gutter ----
   right:7% mirrors the hero gutter, top:20px mirrors the footer offset, at
   both breakpoints. z-index 4 keeps it above the whole stage (hero and footer
   are 3, the orbit lower) and below the contact overlay, which is 30. */
.topbar{position:absolute;top:20px;right:7%;z-index:4}

/* ---- Orbital system: 680x680 box anchored at 73%/50% ---- */
.orbit{position:absolute;left:73%;top:50%;transform:translate(-50%,-50%);width:680px;height:680px}
/* One ring per sphere; the ring spins, the sphere rides on its radius. The
   initial angle comes from a negative delay: -(angle / 360) * duration. */
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes spinRev{to{transform:rotate(-360deg)}}
.ring{position:absolute;inset:0;animation:spin var(--d) linear infinite;animation-delay:var(--dl)}
.ring.rev{animation-name:spinRev}
.orb{position:absolute;left:50%;top:50%;width:var(--s);height:var(--s);
  margin:calc(var(--s) / -2) 0 0 calc(var(--s) / -2);transform:translateX(var(--r));
  border-radius:999px;background:var(--surface);border:1px solid var(--line);
  box-shadow:var(--elev-2);opacity:var(--o,1)}
/* Per-sphere values (handoff table): --s size, --r radius, --d duration,
   --dl start delay, --o opacity, --a the frozen angle used when motion is
   reduced (negative on counter-clockwise rings, which start mirrored). */
.n1 {--s:96px; --r:168px;--d:118s;--dl:-7.2s;  --a:22deg}
.n2 {--s:62px; --r:200px;--d:176s;--dl:-36.2s; --a:-74deg; --o:.9}
.n3 {--s:78px; --r:232px;--d:142s;--dl:-46.5s; --a:118deg}
.n4 {--s:56px; --r:158px;--d:210s;--dl:-93.9s; --a:-161deg;--o:.8}
.n5 {--s:88px; --r:252px;--d:158s;--dl:-87.3s; --a:199deg}
.n6 {--s:66px; --r:216px;--d:128s;--dl:-86.4s; --a:-243deg;--o:.95}
.n7 {--s:106px;--r:240px;--d:196s;--dl:-156.2s;--a:287deg}
.n8 {--s:58px; --r:258px;--d:236s;--dl:-215.7s;--a:-329deg;--o:.85}
.n9 {--s:72px; --r:182px;--d:164s;--dl:-21.4s; --a:-47deg; --o:.95}
.n10{--s:52px; --r:246px;--d:108s;--dl:-41.7s; --a:139deg; --o:.8}
.n11{--s:84px; --r:208px;--d:224s;--dl:-137.5s;--a:-221deg}
.n12{--s:64px; --r:230px;--d:186s;--dl:-157.6s;--a:305deg; --o:.9}

.core{position:absolute;left:50%;top:50%;width:200px;height:200px;margin:-100px 0 0 -100px;
  border-radius:999px;background:var(--surface-strong);border:1px solid var(--line);
  box-shadow:var(--elev-3),0 0 90px rgba(43,230,210,.16);display:grid;place-items:center;z-index:2}
.core img{width:104px;height:104px}

/* ---- Hero ---- */
.hero{position:absolute;left:7%;top:50%;transform:translateY(-50%);width:400px;z-index:3;
  display:grid;gap:18px}
.eyebrow{font-size:12px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--accent)}
h1{font-family:var(--font-display);font-weight:600;font-size:54px;line-height:1.06;letter-spacing:-.02em}
.lead{font-size:16.5px;line-height:1.6;color:var(--text-muted)}
/* Subtitle plus paragraph. The two are wrapped in .pitch with their own 9px
   gap: the 18px hero gap separates the title, the pitch and the button, while
   these two lines read as one block. */
.pitch{display:grid;gap:9px}
.tagline{font-family:var(--font-display);font-weight:600;font-size:22px;line-height:1.25;
  letter-spacing:-.02em;color:var(--text)}
.lead strong{font-weight:600;color:var(--text)}
/* Price note, a child of the button wrapper rather than of .hero, so it is not
   spaced by the 18px hero gap. Muted on the page background, 6.7:1. */
.cta-note{margin-top:10px;font-size:13px;color:var(--text-muted)}

/* ---- Buttons (the open CTA is a <label>, the submit a <button>) ---- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;height:54px;padding:0 28px;
  border:none;border-radius:999px;background:var(--accent);color:var(--on-accent);
  font-family:var(--font-text);font-weight:600;font-size:17px;line-height:1;text-decoration:none;
  cursor:pointer;user-select:none;
  transition:transform .14s var(--ease)}
.btn:hover{transform:translateY(-2px)}
.btn:active{transform:scale(.97)}
.btn-send{height:52px;font-size:16px}
.btn-ghost{background:transparent;color:var(--text);border:1px solid var(--line-strong);box-shadow:none}
.btn-ghost:hover{border-color:var(--text-muted)}
/* Merchant entrance: recessed, darker than the page, never the aqua of the
   CTA, which stays the single primary action. The fill is opaque on purpose:
   below 900px an orbital sphere can pass right under this pill. */
.btn-login{height:44px;padding:0 20px;font-size:15px;
  background:var(--surface-strong);color:var(--text);border:1px solid var(--line-strong);
  box-shadow:var(--elev-2)}
.btn-login:hover{border-color:var(--text-muted)}

/* ---- Scroll affordance ----
   The stage is exactly one viewport tall, so this link is the only thing that
   tells the visitor the content bands exist. Same gutter as the hero, and it
   takes the corner the footer used to occupy in the hero composition. Hidden
   below 900px (see the media query), where the hero already sits low. */
.scroll-cue{position:absolute;left:7%;bottom:24px;z-index:3;
  display:inline-flex;align-items:center;gap:8px;padding:8px 0;
  font-size:13px;font-weight:600;letter-spacing:.02em;
  color:var(--text-muted);text-decoration:none}
.scroll-cue:hover{color:var(--text)}
.scroll-cue svg{animation:cueBob 2.6s var(--ease) infinite}
@keyframes cueBob{0%,100%{transform:translateY(0)}50%{transform:translateY(3px)}}

/* ---- Content bands (below the hero) ----
   One band per copy section: 7% gutter, a 1060px measure inside, so every
   heading lines up with the hero gutter at any width. .band-alt is the single
   background alternation of the page (the darker --surface-strong field). */
.band{position:relative;z-index:1;padding:120px 7%}
.band-alt{background:var(--surface-strong);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.wrap{max-width:1060px;margin:0 auto}
.band-title{margin-top:14px;max-width:24ch;
  font-family:var(--font-display);font-weight:600;font-size:clamp(28px,3.4vw,42px);
  line-height:1.1;letter-spacing:-.025em;text-wrap:pretty}
/* Two-column bands: a sticky head on the left, the content on the right. */
.duo{display:grid;grid-template-columns:minmax(260px,5fr) minmax(0,7fr);gap:48px 72px;align-items:start}
.duo-head{position:sticky;top:48px}
/* A note placed directly in a .duo is a grid item, not flow content. It takes
   the steps column and fills its width, so it closes the band on the same
   measure as the list right above it. stretch is what a grid item does by
   default; it is spelled out because the pill is inline-flex, which blockifies
   here, and because it used to be pinned shrink-to-fit. The row gap alone
   spaces it from the list, hence no margin. Column 2 only exists above 900px,
   where the grid has two: the mobile block puts the note back in column 1. */
.duo>.band-note{grid-column:2;justify-self:stretch;margin-top:0}
/* Steps and benefits: the leading chip is a numeral in one and an icon in the
   other. .num stays for the client band. */
.num{display:grid;place-items:center;width:38px;height:38px;
  border-radius:999px;background:var(--surface-2);border:1px solid var(--line);
  font-family:var(--font-display);font-weight:600;font-size:17px;
  font-variant-numeric:tabular-nums;color:var(--accent)}
.num-ink{color:var(--text)}
.flow-text h3,.client-step h3{font-family:var(--font-display);font-weight:600;font-size:21px;line-height:1.25;
  letter-spacing:-.015em;color:var(--text);text-wrap:pretty}
.flow-text p,.client-step p{font-size:15.5px;line-height:1.6;color:var(--text-muted);text-wrap:pretty}
/* Closing line of a step band: a pill, the tappable-control shape of the
   system used here as a statement, in full ink rather than muted. */
.band-note{display:inline-flex;align-items:center;gap:11px;margin-top:26px;
  padding:14px 22px;border-radius:999px;
  background:var(--surface);border:1px solid var(--line-strong);
  font-family:var(--font-display);font-weight:600;font-size:16px;letter-spacing:-.01em;
  color:var(--text)}
.band-note svg{flex:none;color:var(--accent)}

/* ---- Merchant flow: ruled rows, a display numeral, the text, an artefact ---- */
.flow{list-style:none;margin:0;padding:0;border-bottom:1px solid var(--line-strong)}
.flow-step{display:grid;grid-template-columns:76px minmax(0,1fr) auto;gap:10px 22px;align-items:center;
  padding:40px 0;border-top:1px solid var(--line-strong)}
.flow-text{display:grid;gap:10px}
.big-num{font-family:var(--font-display);font-weight:500;font-size:84px;line-height:.85;
  letter-spacing:-.05em;color:var(--accent);font-variant-numeric:tabular-nums;align-self:start}
.art{display:grid;place-items:center;justify-self:end;min-height:150px}

/* Skeleton lines: content stand-ins inside the artefacts. */
.sk{display:block;height:6px;width:100%;border-radius:999px;background:var(--line-strong)}
.sk-m{width:72%}.sk-s{width:46%}
.sk-title{height:10px;width:60%;background:rgba(244,242,234,.22)}

/* Artefact 1: the merchant window */
.win{width:200px;display:grid;gap:12px;padding:14px;border-radius:16px;
  background:var(--surface);border:1px solid var(--line-strong);box-shadow:var(--elev-3)}
.win-bar{display:flex;align-items:center;gap:10px;padding-bottom:12px;border-bottom:1px solid var(--line)}
.win-bar .sk{width:54%}
.sk-btn{display:block;width:78px;height:22px;border-radius:999px;background:var(--accent);margin-top:2px}

/* The loyalty pass, after the real wallet pass: charcoal ground, header with
   the mark tile, the enseigne and the points, the aqua strip with the spiral,
   the card field, the QR code in its white panel. */
.pass{position:relative;overflow:hidden;width:200px;border-radius:12px;display:grid;
  background:#1B1B1A;color:#F4F2EA;box-shadow:var(--elev-3)}
.pass-head{display:flex;align-items:center;gap:8px;padding:10px 12px 10px 10px}
.pass-head img{width:22px;height:22px;padding:3px;border-radius:4px;background:#0E0E0D}
.pass-org{flex:1;font-family:var(--font-display);font-weight:600;font-size:13px;letter-spacing:-.01em}
.pass-strip{display:grid;place-items:center;height:64px;margin:0 2px;background:var(--accent);
  background-image:radial-gradient(circle at 12% 60%,rgba(6,35,31,.10) 0 26px,transparent 27px)}
.pass-strip img{width:38px;height:38px;filter:brightness(.18)}
.fld{display:grid;gap:1px}
.fld-r{text-align:right}
.fld-l{font-size:9px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:rgba(244,242,234,.72);white-space:nowrap}
.fld-v{font-family:var(--font-display);font-weight:500;font-size:20px;line-height:1;font-variant-numeric:tabular-nums}
.pass-card{padding:10px 12px 0}
.fld-id{font-weight:400;font-size:18px;letter-spacing:.02em}
.pass-qr{position:relative;width:92px;height:92px;margin:18px auto 14px;border-radius:8px;background:#fff}
.pass-qr .qr{position:absolute;left:50%;top:50%;background:#fff}
.qr-xs{transform:translate(-50%,-50%) scale(.58)}
/* The programme rules beside the pass: the two real tiers. */
.art-prog{grid-auto-flow:column;gap:22px;align-items:center;justify-self:end}
.tiers{list-style:none;margin:0;padding:0;display:grid;gap:10px;width:200px}
.tier{display:grid;gap:3px;padding:10px 12px;border-radius:12px;background:var(--surface);border:1px solid var(--line-strong)}
.tier-pts{font-family:var(--font-display);font-weight:600;font-size:14px;letter-spacing:-.01em;color:var(--accent);
  font-variant-numeric:tabular-nums}
.tier-gift{font-size:12.5px;line-height:1.35;color:var(--text);text-wrap:pretty}

/* Artefact 3: the counter placard, cream, carrying the merchant's real code
   (see .qr-real below): the SVG of https://pro.fide-iti.fr/, which scans, on
   the cream field that doubles as its quiet zone. Only the pass codes are
   still the decorative stand-in drawn in CSS. */
.placard{display:grid;justify-items:center;gap:10px;width:190px;padding:18px 20px 20px;border-radius:14px;
  background:#F3F0E8;box-shadow:var(--elev-3)}
/* The placard is the one artefact whose copy a passer-by would really read, so
   it carries the actual caption instead of the two skeleton lines it used to
   show. balance splits the two lines evenly under the code. */
.placard-caption{font-weight:600;font-size:13px;line-height:1.3;
  text-align:center;text-wrap:balance;color:#121211}
.qr{position:relative;width:126px;height:126px;background:#F3F0E8;flex:none}
.qr::after{content:"";position:absolute;inset:0;background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 21 21' fill='%23121211' shape-rendering='crispEdges'%3E<rect x='10' y='0' width='1' height='1'/><rect x='11' y='0' width='1' height='1'/><rect x='9' y='1' width='1' height='1'/><rect x='12' y='1' width='1' height='1'/><rect x='8' y='2' width='1' height='1'/><rect x='11' y='2' width='1' height='1'/><rect x='8' y='3' width='1' height='1'/><rect x='10' y='3' width='1' height='1'/><rect x='8' y='4' width='1' height='1'/><rect x='9' y='4' width='1' height='1'/><rect x='12' y='4' width='1' height='1'/><rect x='10' y='5' width='1' height='1'/><rect x='11' y='5' width='1' height='1'/><rect x='8' y='6' width='1' height='1'/><rect x='10' y='6' width='1' height='1'/><rect x='12' y='6' width='1' height='1'/><rect x='9' y='7' width='1' height='1'/><rect x='10' y='7' width='1' height='1'/><rect x='0' y='8' width='1' height='1'/><rect x='2' y='8' width='1' height='1'/><rect x='3' y='8' width='1' height='1'/><rect x='5' y='8' width='1' height='1'/><rect x='6' y='8' width='1' height='1'/><rect x='7' y='8' width='1' height='1'/><rect x='8' y='8' width='1' height='1'/><rect x='10' y='8' width='1' height='1'/><rect x='13' y='8' width='1' height='1'/><rect x='15' y='8' width='1' height='1'/><rect x='18' y='8' width='1' height='1'/><rect x='19' y='8' width='1' height='1'/><rect x='0' y='9' width='1' height='1'/><rect x='1' y='9' width='1' height='1'/><rect x='2' y='9' width='1' height='1'/><rect x='7' y='9' width='1' height='1'/><rect x='14' y='9' width='1' height='1'/><rect x='15' y='9' width='1' height='1'/><rect x='17' y='9' width='1' height='1'/><rect x='18' y='9' width='1' height='1'/><rect x='6' y='10' width='1' height='1'/><rect x='7' y='10' width='1' height='1'/><rect x='8' y='10' width='1' height='1'/><rect x='9' y='10' width='1' height='1'/><rect x='10' y='10' width='1' height='1'/><rect x='11' y='10' width='1' height='1'/><rect x='14' y='10' width='1' height='1'/><rect x='16' y='10' width='1' height='1'/><rect x='17' y='10' width='1' height='1'/><rect x='20' y='10' width='1' height='1'/><rect x='5' y='11' width='1' height='1'/><rect x='9' y='11' width='1' height='1'/><rect x='10' y='11' width='1' height='1'/><rect x='11' y='11' width='1' height='1'/><rect x='16' y='11' width='1' height='1'/><rect x='18' y='11' width='1' height='1'/><rect x='19' y='11' width='1' height='1'/><rect x='20' y='11' width='1' height='1'/><rect x='0' y='12' width='1' height='1'/><rect x='4' y='12' width='1' height='1'/><rect x='6' y='12' width='1' height='1'/><rect x='7' y='12' width='1' height='1'/><rect x='8' y='12' width='1' height='1'/><rect x='10' y='12' width='1' height='1'/><rect x='11' y='12' width='1' height='1'/><rect x='12' y='12' width='1' height='1'/><rect x='13' y='12' width='1' height='1'/><rect x='15' y='12' width='1' height='1'/><rect x='16' y='12' width='1' height='1'/><rect x='17' y='12' width='1' height='1'/><rect x='19' y='12' width='1' height='1'/><rect x='20' y='12' width='1' height='1'/><rect x='8' y='13' width='1' height='1'/><rect x='10' y='13' width='1' height='1'/><rect x='11' y='13' width='1' height='1'/><rect x='15' y='13' width='1' height='1'/><rect x='19' y='13' width='1' height='1'/><rect x='20' y='13' width='1' height='1'/><rect x='8' y='14' width='1' height='1'/><rect x='10' y='14' width='1' height='1'/><rect x='11' y='14' width='1' height='1'/><rect x='12' y='14' width='1' height='1'/><rect x='13' y='14' width='1' height='1'/><rect x='14' y='14' width='1' height='1'/><rect x='17' y='14' width='1' height='1'/><rect x='9' y='15' width='1' height='1'/><rect x='18' y='15' width='1' height='1'/><rect x='15' y='16' width='1' height='1'/><rect x='19' y='16' width='1' height='1'/><rect x='13' y='17' width='1' height='1'/><rect x='14' y='17' width='1' height='1'/><rect x='18' y='17' width='1' height='1'/><rect x='20' y='17' width='1' height='1'/><rect x='8' y='18' width='1' height='1'/><rect x='10' y='18' width='1' height='1'/><rect x='11' y='18' width='1' height='1'/><rect x='14' y='18' width='1' height='1'/><rect x='15' y='18' width='1' height='1'/><rect x='16' y='18' width='1' height='1'/><rect x='18' y='18' width='1' height='1'/><rect x='20' y='18' width='1' height='1'/><rect x='10' y='19' width='1' height='1'/><rect x='12' y='19' width='1' height='1'/><rect x='13' y='19' width='1' height='1'/><rect x='14' y='19' width='1' height='1'/><rect x='17' y='19' width='1' height='1'/><rect x='18' y='19' width='1' height='1'/><rect x='20' y='19' width='1' height='1'/><rect x='8' y='20' width='1' height='1'/><rect x='10' y='20' width='1' height='1'/><rect x='11' y='20' width='1' height='1'/><rect x='12' y='20' width='1' height='1'/><rect x='17' y='20' width='1' height='1'/><rect x='18' y='20' width='1' height='1'/><rect x='19' y='20' width='1' height='1'/><rect x='20' y='20' width='1' height='1'/>%3C/svg%3E") 0 0/100% 100% no-repeat}
.fp{position:absolute;width:42px;height:42px;border:6px solid #121211;display:grid;place-items:center}
.fp::after{content:"";width:18px;height:18px;background:#121211}
.fp-tl{left:0;top:0}.fp-tr{right:0;top:0}.fp-bl{left:0;bottom:0}
.qr-sm{transform:scale(.6)}
/* The merchant's own code, in the two places that stage it: the counter
   placard and the camera viewfinder. This one is REAL and scans, unlike the
   decorative field the pass codes keep: it is the version 2 symbol of
   https://pro.fide-iti.fr/, 25 modules over 50 viewBox units, served from
   site-assets. inset carves the quiet zone out of the cream box: 16px around
   a 94px symbol = 3.76px per module, so 4.26 modules of silence, above the 4
   the format asks for. No .fp corners here, the symbol draws its own; .fp
   stays for the two pass codes. Note for gate V3-C1: these two nodes now DO
   decode, by the PO's decision, and the criterion has to say so. */
.qr-real::after{inset:16px;background-image:url("qr-pro.svg")}

/* ---- Client flow: three phone screens ---- */
.phones{list-style:none;margin:56px 0 0;padding:0;
  display:grid;gap:40px 32px;grid-template-columns:repeat(3,minmax(0,1fr))}
.client-step{display:grid;gap:10px;align-content:start;justify-items:start}
.client-step .num{margin:18px 0 4px}
/* The frame is the containing block of its screen and clips it: whatever an
   engine makes of the sizes inside, nothing can paint outside the bezel. An
   element's own box-shadow is not clipped by its own overflow, so the frame
   keeps its elevation. The mobile blocks override position, absolute at 900px
   and relative at 360px, both of which keep the containing block. */
.phone{position:relative;overflow:hidden;
  width:min(100%,232px);aspect-ratio:9/17.6;padding:9px;border-radius:38px;justify-self:start;
  background:#0E0E0D;border:1px solid var(--line-strong);box-shadow:var(--elev-3)}
/* inset 9px lands the screen exactly on the frame's content box, since offsets
   resolve against the padding box and the bezel is that same 9px. Sizing it
   this way rather than with height:100% removes a dependency on a percentage
   resolved against an aspect-ratio height, which WebKit iOS long resolved to
   auto: the screen then took the height of its content and the card stack left
   the frame, which is the defect reported on iOS and only there. */
.screen{position:absolute;inset:9px;overflow:hidden;border-radius:30px;background:var(--bg)}
.screen::before{content:"";position:absolute;left:50%;top:10px;z-index:3;width:64px;height:16px;margin-left:-32px;
  border-radius:999px;background:#0E0E0D}
/* Screen 1: the camera, a viewfinder with aqua corner brackets. */
.scr-cam{background:radial-gradient(90% 70% at 50% 40%,#2A2A27,#141412 80%)}
.vf{position:absolute;left:50%;top:50%;width:130px;height:130px;transform:translate(-50%,-50%);
  display:grid;place-items:center;background-repeat:no-repeat;background-image:
  linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),
  linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),
  linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),
  linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent));
  background-size:24px 2px,2px 24px,24px 2px,2px 24px,24px 2px,2px 24px,24px 2px,2px 24px;
  background-position:0 0,0 0,100% 0,100% 0,0 100%,0 100%,100% 100%,100% 100%}
.vf .qr{border-radius:6px;box-shadow:0 0 0 8px #F3F0E8}
/* Screen 2: the wallet, the pass slides in above a second, muted card. */
.scr-wallet{display:grid;grid-auto-rows:max-content;align-content:start;gap:12px;padding:44px 14px 0}
.scr-wallet .pass{width:100%;box-shadow:var(--elev-2)}
.scr-wallet .sk-s{width:34%}
.ghost{height:120px;border-radius:12px;background:#1B1B1A;border:1px solid var(--line)}
/* Screen 3: at the register, the pass fills the screen with the balance just
   credited; the confirmation pill sits under it. */
.scr-reg{display:grid;grid-auto-rows:max-content;align-content:center;justify-items:center;gap:14px;padding:40px 14px 0}
.scr-reg .pass{width:100%}
.credit{display:inline-flex;align-items:center;height:30px;padding:0 14px;border-radius:999px;
  background:var(--accent);color:var(--on-accent);
  font-family:var(--font-display);font-weight:600;font-size:13px;letter-spacing:-.01em}

/* ---- Benefits ledger ---- */
.ledger{list-style:none;margin:52px 0 0;padding:0;border-bottom:1px solid var(--line-strong)}
.ledger-row{display:grid;grid-template-columns:64px minmax(0,1fr) minmax(0,1fr);gap:8px 32px;align-items:baseline;
  padding:34px 0;border-top:1px solid var(--line-strong)}
.idx{font-family:var(--font-display);font-weight:500;font-size:14px;letter-spacing:.08em;
  color:var(--text-muted);font-variant-numeric:tabular-nums;transition:color .18s var(--ease)}
.ledger-row:hover .idx{color:var(--accent)}
.ledger-row h3{font-family:var(--font-display);font-weight:600;font-size:clamp(22px,2.6vw,32px);
  line-height:1.15;letter-spacing:-.025em;color:var(--text);text-wrap:pretty}
.ledger-row p{font-size:16.5px;line-height:1.6;color:var(--text-muted);text-wrap:pretty;max-width:40ch}

/* ---- FAQ: native <details>, no JavaScript ----
   The default disclosure marker is removed in both engines and replaced by the
   chevron inside each <summary>, which CSS rotates on [open]. A ruled index,
   no card boxes. */
.duo-faq{grid-template-columns:minmax(220px,4fr) minmax(0,8fr)}
.faq{display:grid;border-top:1px solid var(--line-strong)}
.faq-more{margin-top:22px;font-size:15.5px;color:var(--text-muted)}
.lnk{color:var(--accent);font-weight:600;white-space:nowrap;text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px;cursor:pointer}
.lnk:hover{color:var(--text)}
.qa{border-bottom:1px solid var(--line-strong)}
.qa summary{display:grid;grid-template-columns:48px minmax(0,1fr) 18px;align-items:baseline;gap:14px;
  min-height:60px;padding:22px 0;cursor:pointer;list-style:none;
  font-family:var(--font-display);font-weight:600;font-size:18px;line-height:1.35;
  letter-spacing:-.015em;color:var(--text);transition:color .18s var(--ease)}
.qa summary::-webkit-details-marker{display:none}
.qa summary::marker{content:""}
.qa summary:hover{color:var(--accent)}
.q-idx{font-weight:500;font-size:13px;letter-spacing:.08em;color:var(--text-muted);font-variant-numeric:tabular-nums}
.qa-ico{flex:none;align-self:center;color:var(--text-muted);transition:transform .18s var(--ease)}
.qa[open] summary{color:var(--accent)}
.qa[open] .qa-ico{transform:rotate(180deg);color:var(--accent)}
.qa-body{display:grid;gap:10px;max-width:60ch;padding:0 0 26px 62px;
  font-size:15.5px;line-height:1.65;color:var(--text-muted);text-wrap:pretty}
.qa-list{list-style:none;margin:0;padding:0;display:grid;gap:7px}
.qa-list li{position:relative;padding-left:17px}
.qa-list li::before{content:"";position:absolute;left:2px;top:.62em;
  width:5px;height:5px;border-radius:999px;background:var(--accent)}

/* ---- Closing CTA: the same drawer label as the hero ----
   Background of the printed loyalty card, rebuilt in CSS: a near-black green
   ground and two overlapping teal rings. The rings are two spans with a thick
   translucent border rather than radial-gradient stops, so they stay true
   circles at any card width (a gradient radius cannot be a percentage on a
   circle, and an ellipse would squash on a phone). */
.band-cta{padding-bottom:84px}
.cta-card{position:relative;overflow:hidden;isolation:isolate;
  display:grid;justify-items:center;gap:22px;text-align:center;
  padding:48px 44px;border-radius:24px;
  background:#0E1312;border:1px solid var(--line-strong);box-shadow:var(--elev-3)}
.cta-ring{position:absolute;border-radius:999px;border:150px solid rgba(43,230,210,.16);
  pointer-events:none}
.cta-ring-a{left:38%;top:-190px;width:1060px;height:1060px}
.cta-ring-b{left:-150px;top:-330px;width:560px;height:560px;border-width:110px}
.cta-card .band-title,.cta-card>div{position:relative;z-index:1}
.cta-card .band-title{margin-top:0;max-width:22ch}
.cta-card .cta-note{margin-top:12px}

/* ---- Footer (page end, below the bands) ---- */
.footer{position:relative;z-index:1;padding:26px 7% 34px;
  border-top:1px solid var(--line);font-size:13px;color:var(--text-muted)}
.foot-in{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 20px}
.legal{display:flex;gap:16px}
.legal a{display:inline-block;padding:6px 0;font-size:12.5px;color:var(--text-muted);
  text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
.legal a:hover{color:var(--text)}

/* ---- Contact drawer: CSS-only disclosure ----
   The checkbox is the single keyboard control (Space toggles it); the labels
   are the pointer targets. It is visually hidden, so its focus ring is painted
   on whichever label is on screen at the time. */
.toggle{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.toggle:not(:checked):focus-visible ~ .stage .cta,
.toggle:not(:checked):focus-visible ~ .band .cta,
.toggle:checked:focus-visible ~ .overlay .close{outline:3px solid var(--accent);outline-offset:3px}
.overlay{position:fixed;inset:0;z-index:30;display:none}
.toggle:checked ~ .overlay{display:block}
.scrim{position:absolute;inset:0;background:rgba(16,16,14,.45);cursor:pointer}
.drawer{position:absolute;top:0;right:0;bottom:0;width:440px;max-width:100vw;
  background:var(--surface);border-left:1px solid var(--line-strong);box-shadow:var(--elev-4);
  padding:34px 34px 26px;overflow:auto}
.close{position:absolute;top:20px;right:22px;width:34px;height:34px;border-radius:999px;
  border:1px solid var(--line-strong);background:transparent;color:var(--text-muted);
  cursor:pointer;display:grid;place-items:center}
.close:hover{color:var(--text);border-color:var(--text-muted)}
.drawer h2{font-family:var(--font-display);font-weight:600;font-size:26px;letter-spacing:-.02em;
  margin:0 0 6px}
.drawer .sub{font-size:15px;color:var(--text-muted);margin:0 0 20px}

/* ---- Contact form (plain POST, no JavaScript) ---- */
.contact-form{display:grid;gap:14px}
.contact-form label{display:block;font-weight:600;font-size:.9rem;margin-bottom:7px}
.contact-form input[type="text"],.contact-form input[type="email"],.contact-form input[type="tel"],.contact-form textarea{width:100%;font-family:var(--font-text);font-size:1rem;
  color:var(--text);background:var(--surface-2);border:1px solid var(--line-strong);
  border-radius:14px;padding:12px 15px}
.contact-form ::placeholder{color:var(--text-muted);opacity:1}
.contact-form input:focus,.contact-form textarea:focus{border-color:var(--accent)}
.contact-form textarea{min-height:126px;resize:vertical;line-height:1.5}
/* Optional markers and help lines. .opt sits inside a bold label, so it resets
   the weight it inherits. */
.contact-form .opt{font-weight:400;color:var(--text-muted)}
.contact-form .aide{margin:6px 0 0;font-size:.8rem;color:var(--text-muted)}
/* Request type. min-width:0 keeps the fieldset from taking its intrinsic width
   inside the grid. */
.choice{margin:0;padding:0;border:0;min-width:0}
.choice legend{padding:0;margin-bottom:7px;font-weight:600;font-size:.9rem}
/* label.opt-row, not .opt-row alone: .contact-form label is one class plus one
   type, so it would otherwise win and restore display:block and the bold. */
.contact-form label.opt-row{display:flex;align-items:center;gap:10px;min-height:44px;margin:0;
  font-weight:400;font-size:1rem;cursor:pointer}
.opt-row input[type="radio"]{flex:none;width:20px;height:20px;margin:0;accent-color:var(--accent)}
/* TAHITI number: present only for an account request. No JavaScript, the form
   reacts to the state of the radio button. :has() is required here, since ~
   would only reach the field if the radios were direct children of the form,
   which would break the group and its legend. */
.champ-tahiti{display:none}
.contact-form:has(#rt-compte:checked) .champ-tahiti{display:block}

/* Scroll lock while the sheet is open. No JavaScript: :has() reaches the root
   from the checkbox, which ~ cannot do. */
html:has(#contact-toggle:checked){overflow:hidden}

/* Web3Forms honeypot: off screen, never focused */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* ---- Confirmation page (merci.html) ---- */
.thanks{position:relative;z-index:1;min-height:100vh;min-height:100dvh;
  display:grid;align-content:center;justify-items:center;gap:18px;
  padding:32px 7%;text-align:center}
.thanks img{width:72px;height:72px}
.thanks h1{font-size:40px}
.thanks .lead{max-width:34ch}

/* ---- Responsive ----------------------------------------------------------
   Source order is load-bearing: the wide breakpoint comes first, so the phone
   rules below, which share its specificity, always win. (In the previous
   order the 1180px block re-imposed its 76px numeral column on a 360px
   screen, which is what left the step copy on a 248px measure.) */

/* Tablet: the flow keeps its two columns, but the artefact drops under the
   text instead of squeezing it. */
@media (max-width:1180px){
  .flow-step{grid-template-columns:76px minmax(0,1fr)}
  .art{grid-column:2;justify-self:start;min-height:0;margin-top:10px}
}

/* ---- Phone ----
   One centred column. The three composition changes that matter:
   1. The hero returns to normal flow, anchored to the bottom of the stage by
      align-content, instead of being pinned at bottom:14%: no combination of
      viewport height and text length can push it off screen any more.
   2. The orbit is anchored to the TOP of the stage in px and scaled from that
      edge, so the mark always clears the hero copy rather than landing behind
      the title on a short screen.
   3. Every numeral pairs with its heading and the copy runs the full measure,
      which is what makes French text readable at this width. */
@media (max-width:900px){
  .stage{display:grid;align-content:end;padding:0 22px 56px}
  .orbit{left:50%;top:-24px;transform:translateX(-50%) scale(.52);transform-origin:top center}
  /* position:relative, not static: the hero returns to the flow but keeps its
     stacking (z-index only applies to positioned boxes), otherwise the orbit,
     which paints later, covers the copy with its opaque spheres and its core. */
  /* left/top must be reset: in position:relative they would still apply as an
     offset, and the base left:7% pushed the whole hero 24px to the right. */
  .hero{position:relative;left:auto;top:auto;bottom:auto;transform:none;
    width:auto;max-width:400px;margin:0 auto;
    gap:16px;text-align:center;justify-items:center}
  h1{font-size:clamp(42px,13vw,54px)}
  .tagline{font-size:20px}
  .lead{font-size:16px}
  .topbar{top:16px;right:22px}
  /* The stage no longer fills the screen down to its bottom edge here, and the
     hero sits low: the hint has nothing left to point at. */
  .scroll-cue{display:none}

  /* Bands: same 22px gutter as the stage, tighter vertical rhythm. */
  .band{padding:68px 22px}
  .band-cta{padding-bottom:60px}
  .band-title{margin-top:12px;max-width:none}
  .duo{grid-template-columns:minmax(0,1fr);gap:32px}
  .duo-head{position:static}
  /* One column here, so the steps column IS column 1: without this the note
     would land in an implicit second column beside the grid. */
  .duo>.band-note{grid-column:1}
  .band-note{margin-top:22px;padding:13px 18px;font-size:15px}

  /* Merchant flow: display:contents lifts the heading and the paragraph into
     the row grid, so the numeral sits beside the heading while the paragraph
     and the artefact keep the full width. */
  .flow-step{grid-template-columns:auto minmax(0,1fr);gap:10px 14px;padding:30px 0;align-items:center}
  .flow-text{display:contents}
  .flow-text h3{grid-column:2;font-size:20px}
  .flow-text p{grid-column:1/-1}
  .big-num{font-size:42px;line-height:1;align-self:center}
  /* The artefacts are centred under their step: aligned to the numeral column
     they read as a fourth, orphaned element in the row. */
  .art{grid-column:1/-1;justify-self:center;min-height:0;margin-top:14px}
  .art-prog{grid-auto-flow:row;justify-items:center}

  /* Client steps: a real row, copy on the left, the device on the right. The
     mockup is taken out of the row grid (position:absolute) rather than made
     to span two rows: a spanning item would have stretched the two text rows
     to its own height and pulled the heading away from its paragraph. The row
     reserves its width with padding-right and its height with min-height, and
     align-content centres the copy block against the device. */
  .phones{margin-top:36px;gap:40px;grid-template-columns:minmax(0,1fr)}
  .client-step{position:relative;grid-template-columns:auto minmax(0,1fr);gap:9px 12px;
    padding-right:156px;min-height:272px;align-content:center}
  .client-step .num{grid-row:1;grid-column:1;margin:0;width:32px;height:32px;font-size:15px}
  .client-step h3{grid-row:1;grid-column:2;font-size:19px}
  .client-step p{grid-row:2;grid-column:1/-1;font-size:15px}
  /* The device is DRAWN at 230px and shown at .58, i.e. 133px wide, because the
     pass inside it needs its 230px design width: laid out at 200px the
     « POINTS » field overflowed the card by 5px and was clipped by its
     overflow:hidden. The reduction is a transform, not zoom: Safari lays out
     absolutely positioned descendants and the overflow:hidden of .screen
     differently under zoom, which let the cards paint outside the frame on iOS
     while Chromium clipped them. A transform only scales the painted result,
     so every engine keeps the same box arithmetic. Right edge and vertical
     centring are preserved: transform-origin pins the right edge that right:0
     placed, and the -50% translate, applied after the scale in unscaled units,
     brings the box centre back onto the row's mid-line whatever its height. */
  .phone{position:absolute;right:0;top:50%;
    transform:translateY(-50%) scale(.58);transform-origin:right center;
    width:230px;margin:0}
  /* The wallet stack was taller than the screen it sits in, so the frame cut
     the second card in half. The screen box is fixed: 230px wide at 9/17.6
     leaves 449.8px of frame, minus the 1px border and the 9px bezel = a
     210x429.8px screen. The stack asked for 44 + 6 + 12 + 10 + 12 + 281 (pass)
     + 12 + 120 (ghost) = 497px, i.e. 67px past the bottom edge, clipped by
     .screen. Shorter lead-in and a card that peeks rather than a full second
     card bring it to 419px, which fits with about 10px to spare. The 30px
     lead-in still clears the notch, which ends at y=26. */
  .scr-wallet{padding-top:30px}
  .ghost{height:56px}

  /* Ledger: the claim keeps the numeral beside it, the gloss runs full width. */
  .ledger{margin-top:32px}
  .ledger-row{grid-template-columns:auto minmax(0,1fr);gap:8px 14px;padding:26px 0}
  .ledger-row h3{font-size:22px}
  .ledger-row p{grid-column:1/-1;max-width:none}

  .qa summary{grid-template-columns:28px minmax(0,1fr) 18px;gap:10px;font-size:16px;padding:18px 0}
  .qa-body{padding:0 0 22px 38px}

  .cta-card{padding:36px 22px}
  .cta-ring-a{left:26%;top:-150px;width:700px;height:700px;border-width:100px}
  .cta-ring-b{left:-130px;top:-210px;width:380px;height:380px;border-width:74px}

  /* Compact footer, two lines that never wrap between 320px and 390px.
     Root cause of the wrap: at left:50% the box was absolutely positioned with
     right:auto, so its shrink-to-fit width was capped at half the viewport
     (180px at 360px) while the copyright line alone needs about 250px. Full
     width restores the room, 12px and the dropped place name give the margin
     that makes nowrap safe. */
  .footer{padding:22px 22px 30px;font-size:12px}
  .foot-in{flex-direction:column;justify-content:center;gap:6px}
  .footer p{white-space:nowrap}
  .place{display:none}
  .legal{justify-content:center;gap:16px}
  /* 12.5px text plus 10px of padding keeps these two links a 34px tall target. */
  .legal a{padding:10px 0;font-size:12.5px;white-space:nowrap}

  /* Contact sheet: full width, no left border to fake a panel edge, 44px close
     target, and the home-bar inset added to the bottom padding. */
  .drawer{width:100%;border-left:0;padding:26px 22px calc(26px + env(safe-area-inset-bottom))}
  .drawer h2{font-size:24px}
  .close{top:14px;right:14px;width:44px;height:44px}
  .contact-form{gap:16px}
  .btn-send{width:100%;height:54px}
  .thanks h1{font-size:32px}
}

/* Very narrow screens: the side-by-side client row would leave the copy on a
   130px measure, so the device drops back under it, right-aligned. */
@media (max-width:360px){
  .client-step{padding-right:0;min-height:0}
  /* Same reduction by transform rather than zoom, see the block above. In flow
     here, so the un-reduced box would reserve its full height: 230px at 9/17.6
     is 449.8px, of which the scale hides 34%, and the negative bottom margin
     gives that back so row 3 reserves the 296.9px actually painted. The origin
     is the top right corner, so what is painted starts at the row's top edge
     and at the right edge that justify-self:end placed. */
  /* relative, not static: back in the flow like static, but still the
     containing block of the absolutely positioned .screen. The transform below
     would establish one too, so static would work today; naming it here keeps
     the screen from depending on the reduction staying in place. */
  .phone{position:relative;grid-row:3;grid-column:1/-1;justify-self:end;
    transform:scale(.66);transform-origin:top right;
    margin-top:8px;margin-bottom:calc(-449.8px * .34)}
}

/* Short phone screens (small devices, and landscape): the orbit gives way
   rather than sliding behind the hero copy. */
@media (max-width:900px) and (max-height:680px){
  .orbit{top:-64px;transform:translateX(-50%) scale(.42)}
  .hero{gap:13px}
  h1{font-size:40px}
  .tagline{font-size:19px}
  .stage{padding-bottom:44px}
}

/* ---- Reduced motion: freeze each ring at its design angle ----
   (Cutting the duration instead would stack all twelve spheres on the same
   axis, which is not the composition.) */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .ring{animation:none;transform:rotate(var(--a))}
  .btn{transition:none}
  .scroll-cue svg{animation:none}
  .qa-ico{transition:none}
}
