
/* ============================================================
   Polices de repli calées sur les métriques réelles.
   Sorties du HTML, les polices se chargent en swap : sans ces
   surcharges, la bascule décalait toute la page (CLS 0,144).
   ============================================================ */
@font-face{font-family:'TH Display FB';src:local('Georgia'),local('Times New Roman'),local('Times');
  size-adjust:104.4%;ascent-override:73.5%;descent-override:26.5%;line-gap-override:0%}
@font-face{font-family:'TH Sans FB';src:local('Helvetica Neue'),local('Arial'),local('Liberation Sans');
  size-adjust:102.1%;ascent-override:100.5%;descent-override:29.5%;line-gap-override:0%}
@font-face{font-family:'TH Mono FB';src:local('Menlo'),local('Consolas'),local('DejaVu Sans Mono');
  size-adjust:99.7%;ascent-override:100.5%;descent-override:29.5%;line-gap-override:0%}

@font-face{font-family:'TH Display';src:url(fonts/th-display.dd7eaf7063.woff2) format('woff2');font-weight:400;font-style:normal;font-display:swap;unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+20AC,U+2122,U+2190-2194,U+21C4,U+2212,U+FEFF}
@font-face{font-family:'TH Display';src:url(fonts/th-display-2.0ba5994975.woff2) format('woff2');font-weight:400;font-style:italic;font-display:swap;unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+20AC,U+2122,U+2190-2194,U+21C4,U+2212,U+FEFF}
@font-face{font-family:'TH Sans';src:url(fonts/th-sans.b49c09448e.woff2) format('woff2');font-weight:100 900;font-style:normal;font-display:swap;unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+20AC,U+2122,U+2190-2194,U+21C4,U+2212,U+FEFF}
@font-face{font-family:'TH Mono';src:url(fonts/th-mono.850b00d89e.woff2) format('woff2');font-weight:100 900;font-style:normal;font-display:swap;unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+20AC,U+2122,U+2190-2194,U+21C4,U+2212,U+FEFF}
/* ============================================================
   TianhuiDigital — v2
   China-safe: system font stack, zero third-party requests.
   Production: self-host Geist (woff2) and swap --font-sans.
   ============================================================ */
:root{
  /* respiration au-dessus et au-dessous des scènes photographiques */
  --air-scene:72px;
  /* Toute la photographie est maintenant à l'heure dorée. Une interface
     bleu-gris posée sur cette lumière lit comme deux mondes séparés : les
     neutres suivent donc la lumière. Le fond clair est un ivoire chaud, les
     fonds sombres passent d'un bleu de nuit franc (#07121F) à un encre
     profond légèrement chaud. Le bleu de marque et le rouge du sceau, eux,
     ne bougent pas d'un point — c'est l'identité, pas l'ambiance. */
  --paper:#F6F4EF; --white:#FFFEFB;
  --ink:#13130F; --char:#20201B; --muted:#61615A; --faint:#6D6D66; --faint-lg:#818178;
  --line:#E4E1D9; --line-2:#D1CDC3;
  /* Les photographies sont à l'heure bleue : les sections sombres reviennent
     vers un bleu de nuit profond pour leur répondre, mais réchauffé par
     rapport au bleu glacé d'origine (#07121F). Le papier reste ivoire —
     c'est le contraste entre le papier chaud et la nuit bleue qui fait
     l'impression éditoriale. */
  --navy:#0C1017; --navy-2:#141A24; --navy-line:rgba(198,206,222,.14);
  --on-dark:#EFEDE7; --on-dark-muted:#9BA0AA;
  /* Les gris de la nuit. Ils existaient déjà — mais écrits en dur au fil
     des règles : huit valeurs que rien ne déclarait, invisibles pour qui
     lit :root pour connaître la charte. Elles sont nommées ici, à leur
     valeur exacte. Deux couples se ressemblaient à moins de cinq points
     sur 255 (imperceptible sur du texte) : ils sont fondus en un seul ton,
     ce qui retire deux nuances de plus à retenir.
     Les deux dernières ne sont pas des gris : ce sont les seules couleurs
     posées SUR la photographie du port, où le texte doit gagner contre une
     image et non contre un aplat. */
  --nuit-mut:#7E8590;        /* second membre des grands titres, sur la nuit */
  --nuit-sourdine:#8A909C;   /* listes et mentions secondaires */
  --nuit-lead:#B7BDC7;       /* le chapeau du hero */
  --nuit-filigrane:#8C9AAC;  /* le filigrane chinois de la carte */
  --bande-k:#CBDBFF;         /* les villes, sur la photographie */
  --bande-s:#DCE2EA;         /* la ligne qui la referme */
  --accent:#2A56C6; --accent-text:#2450BF; --accent-dark:#8FB0FF;
  --cn:#C1291F; --cn-text:#B02418; --cn-dark:#F08375;
  /* --warm a été retiré : déclaré, jamais lu depuis que la photographie
     porte elle-même la chaleur du site. */
  /* Le miel des coordonnées, et son jumeau pour le papier : même teinte
     (36°), assez sombre pour tenir sur le fond clair. Mesuré : 4,71:1,
     soit exactement le contraste du gris qu'il remplace — on change la
     couleur de la légende, pas sa lisibilité. */
  --honey:#F7D6A6; --honey-ink:#8F6527;
  /* Le champagne des boutons — deuxième version, et la première était trop
     timide. Mesurée : l'ancienne valeur #F1E8DA tenait 90 % de clarté quand
     le papier du site en tient 95. Cinq points d'écart : ce n'était pas une
     couleur, c'était un blanc tiédi, et ça se voyait. Le site portait en
     plus DEUX chaleurs sans rapport — ce champagne à 45 % de saturation et
     le miel des légendes (--honey) à 84 %.

     Le nouveau ton est le frère désaturé du miel : même teinte, 37°, la
     clarté descendue à 80 % et la saturation à 50 %. En aplat, une couleur
     se lit bien plus forte que sur un filet de texte — d'où la saturation
     volontairement plus basse que celle du miel : au-delà, le bouton vire
     au blé, ce qui n'est plus du champagne mais de l'or.

     Contrôlé : 12,6:1 contre l'encre du libellé, 12,9:1 contre le navy des
     sections sombres, et 15 points de clarté d'écart avec le papier. Le
     bouton est enfin une couleur du site, pas une absence de couleur. */
  --champ:#E6D2B3; --champ-hi:#ECDCC3; --champ-lo:#DDC59F;
  /* ====================================================================
     LE VERRE CHAMPAGNE — la matière des boutons.
     Tout se règle ici : la couleur de base, les trois alphas, les deux
     flous. Rien d'autre n'a besoin d'être touché pour essayer une autre
     teinte ou une autre transparence.
     ==================================================================== */
  /* La teinte, en composantes, pour pouvoir faire varier l'alpha seul.
     Deux valeurs : la teinte posée sur une surface CLAIRE, et celle posée
     sur une surface SOMBRE. Pour le champagne elles sont identiques — il
     est déjà clair, il se voit sur les deux. Pour une teinte foncée (le
     rouge du sceau, par exemple) il faut sa version claire sur la nuit,
     sinon elle disparaît. Changer de couleur ne demande que ces deux
     lignes. */
  --btn-rvb:230,210,179;      /* --champ #E6D2B3 */
  --btn-rvb-d:230,210,179;
  /* Les trois alphas. Ce sont eux qu'on bouge en premier. */
  --btn-a:.09;          /* repos   — 0.06 à 0.10 */
  --btn-a-h:.14;        /* survol  — 0.10 à 0.14 */
  --btn-a-p:.17;        /* appui   */
  --btn-a-plein:.18;    /* repli quand backdrop-filter n'existe pas */
  /* Le flou et la saturation. La saturation compte autant que le flou :
     c'est elle qui donne au verre sa densité optique.

     Le FLOU monte (18 -> 24 px) : c'est lui qui rend la transparence
     visible, puisqu'on lit ce qu'il y a derrière, déformé.
     La SATURATION descend (180 -> 150 %), et c'est contre-intuitif. Elle
     n'amplifie pas la couleur du bouton — le bouton n'en a presque pas —
     mais celle de ce qu'il y a DERRIÈRE. Or derrière, sur ce site, il y a
     la nuit bleue et le globe. Plus on saturait, plus on tirait du bleu à
     travers le verre : la saturation travaillait contre le champagne.
     Mesuré sur le bouton rendu avant de trancher. */
  --btn-flou:24px; --btn-flou-h:29px; --btn-sat:150%;
  /* Le liseré et l'ombre. Le liseré blanc ne fonctionne QUE sur fond
     sombre ; sur le papier ivoire il est blanc sur blanc, donc invisible,
     et un bouton sans bord lisible n'est plus un bouton. Il y a donc deux
     valeurs, et c'est la seule chose qui change d'une surface à l'autre
     avec la couleur du texte. */
  /* CORRECTIF — les boutons « faisaient gris » sur fond noir.
     La raison n'était pas l'alpha du fond mais la couleur du LISERÉ : il
     était BLANC. Du blanc à 25 % sur du quasi-noir ne peut donner qu'un
     gris neutre, quelle que soit la teinte du fond derrière. Le champagne
     était là, mais le seul trait que l'œil suit vraiment — le contour —
     n'en avait pas une goutte. Il passe au champagne, et avec lui le
     reflet d'arête, qui était blanc pour la même raison. */
  --btn-rim-sombre:rgba(233,212,177,.66);
  --btn-rim-sombre-h:rgba(240,224,197,.82);
  /* Le reflet interne de l'arête haute. Blanc, il posait une ligne froide
     sur le bord supérieur ; en champagne clair il fait la même chose en
     chaud, et c'est lui qui fait lire « verre » plutôt que « voile ». */
  --btn-eclat-sombre:rgba(248,236,213,.58);
  --btn-eclat-sombre-h:rgba(250,240,220,.70);
  /* Le halo. Un liseré seul se lit comme un trait tracé ; le même liseré
     posé sur une lueur très basse se lit comme une arête ÉCLAIRÉE, donc
     comme du verre. Deux couches : un cerne d'un pixel qui adoucit la
     coupure, et une lueur large et faible dessous. C'est ce qui sépare le
     bouton du noir sans lui ajouter d'opacité. */
  --btn-halo-sombre:0 0 0 1px rgba(230,210,179,.10),
                    0 2px 22px -6px rgba(214,187,143,.30);
  --btn-halo-sombre-h:0 0 0 1px rgba(230,210,179,.14),
                      0 3px 28px -6px rgba(214,187,143,.42);
  --btn-rim-clair:rgba(143,101,39,.34);
  --btn-ombre:0 8px 32px rgba(0,0,0,.06);
  --btn-ombre-h:0 12px 40px rgba(0,0,0,.09);
  /* Le filet froid des boutons secondaires : même famille. */
  --glass-line:rgba(154,180,216,.26); --glass-line-hi:rgba(178,202,235,.46);
  --glass-veil:rgba(154,180,216,.07);
  /* L'arête. Le bouton portait un liseré blanc à 14 % qui lui posait un
     halo froid sur le bord — le seul endroit du système où le bleu revenait.
     Il prend un champagne plus profond : l'arête définit la forme sans
     éclaircir le pourtour. */
  --champ-edge:#D6BB8F;
  /* Le filet du bouton secondaire est ce qui le rend identifiable : son
     opacité n'est pas choisie à l'œil mais calculée. Le ton ayant foncé, il
     faut un peu plus d'opacité pour tenir le même rapport de 3:1 exigé sur
     le contour d'un élément d'interface : 0,46 au repos, 0,72 au survol. */
  /* Le champagne des étiquettes du tiroir : la même teinte, un peu
     rentrée, pour qu'une étiquette reste une étiquette. */
  --champ-k:#D8C09A;
  --champ-line:rgba(230,210,179,.46); --champ-line-hi:rgba(230,210,179,.72);
  --champ-veil:rgba(230,210,179,.08);
  /* Sur fond clair, le champagne disparaîtrait dans le papier : le principal
     s'y retourne en encre pleine, et c'est le filet du secondaire qui porte
     la chaleur. Même système, surface inversée.
     Ce filet est un charbon légèrement ambré, pas un brun doré. Tiré du
     doré, il fallait 0,78 pour atteindre 3:1 et il rendait une ligne de
     chroma 32 : un tan de bon de réduction, bien plus bruyant que son
     homologue sombre, qui lui compose un gris quasi neutre. Ici, 3,3:1 pour
     une chroma de 5,5 — chaud à la lecture, silencieux à l'œil. */
  --warm-line:rgba(49,42,29,.54); --warm-line-hi:rgba(49,42,29,.78);
  --warm-veil:rgba(49,42,29,.04);
  --seal:#C1291F; --seal-dark:#E3594B;
  --font-sans:"TH Sans",'TH Sans FB',system-ui,-apple-system,sans-serif;
  --font-display:"TH Display",'TH Display FB',Georgia,serif;
  --font-zh-serif:"Songti SC","Noto Serif SC","Source Han Serif SC","STSong","PingFang SC",serif;
  --font-zh:"PingFang SC","Hiragino Sans GB","Microsoft YaHei","Noto Sans SC","Source Han Sans SC",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --font-mono:"TH Mono",'TH Mono FB',ui-monospace,Menlo,monospace;
  --gutter:clamp(16px,4vw,44px); --maxw:1240px;
  --r-sm:8px; --r-md:14px; --r-lg:22px;
  --ease:cubic-bezier(.16,1,.3,1); --t-fast:.16s; --t-med:.45s;
  --hd:64px;
  color-scheme:light;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--font-sans);font-size:16px;line-height:1.55;-webkit-font-smoothing:antialiased;overflow-x:hidden}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit}
img,canvas,svg{max-width:100%}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:4px}
.dark :focus-visible,.drawer :focus-visible,.ft :focus-visible{outline-color:var(--accent-dark)}
@media (prefers-contrast:more){
  :root{--muted:#3C4652;--faint:#4A5462;--faint-lg:#5A6572;--line:#B9C1CA;--line-2:#93a0ad;--on-dark-muted:#BFCAD6;--navy-line:rgba(190,210,235,.35)}
  .btn-ghost{border-width:1.5px}
  .swap .from,.swap .to,.ticks li,.steps li,.svc li{border-color:var(--line-2)}
}
.wrap{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gutter)}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.sr:focus{position:fixed;left:16px;top:16px;width:auto;height:auto;clip:auto;z-index:99;background:var(--ink);color:#fff;padding:10px 14px;border-radius:8px}
section[id]{scroll-margin-top:calc(var(--hd) + 16px)}
.zh-line{font-family:var(--font-zh);letter-spacing:.04em}
/* 印章 — a carved vermilion seal, drawn as SVG so it stays sharp at any size */
.seal{display:inline-block;width:40px;height:40px;flex:none;color:var(--seal);transform:rotate(-3deg);opacity:.92}
.seal-sm{width:27px;height:27px}
.seal-lg{width:86px;height:86px;transform:rotate(-5deg)}
.dark .seal,.ft .seal{color:var(--seal-dark);opacity:.9}
.js .seal-lg{opacity:0;transform:rotate(-14deg) scale(1.28)}
.js .seal-lg.stamped{opacity:1;transform:rotate(-6deg) scale(1);transition:opacity .28s ease-out,transform .5s cubic-bezier(.2,1.5,.35,1)}
@media (prefers-reduced-motion:reduce){.js .seal-lg{opacity:1;transform:rotate(-6deg)}}
/* 竖排 — vertical Chinese, used only where it is typographically right */
.vtext{position:absolute;top:18px;right:16px;writing-mode:vertical-rl;text-orientation:upright;font-family:var(--font-zh);letter-spacing:.34em;font-size:13px;color:var(--faint);pointer-events:none;user-select:none}
/* Le filigrane employait sa teinte en rgba, donc invisible à une recherche
   de couleurs hexadécimales : c'est la dernière valeur du site qui échappait
   à la charte. Même teinte, désormais nommée. */
.dark .vtext{color:color-mix(in srgb,var(--nuit-filigrane) 55%,transparent)}
/* 水墨 — a soft ink wash, never a texture image */
.wash{position:absolute;inset:-12%;pointer-events:none;background:radial-gradient(58% 48% at 28% 32%,rgba(160,190,225,.11),transparent 70%),radial-gradient(48% 40% at 76% 66%,rgba(143,176,255,.09),transparent 70%),radial-gradient(40% 36% at 60% 18%,rgba(194,56,44,.05),transparent 72%);filter:blur(26px)}

/* ---------- type ---------- */
.eyebrow{display:inline-flex;align-items:center;gap:10px;font-family:var(--font-mono);font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);margin:0 0 22px}
.eyebrow::before{content:"";width:30px;height:30px;margin-block:-9px;flex:none;background:var(--ink);-webkit-mask:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2032%2032'%3E%3Cpath%20d='M16%2015.2%20C13.2%209%205.4%209.4%205.4%2015.2%20C5.4%2021%2013.2%2021.4%2016%2015.2%20C18.8%209%2026.6%209.4%2026.6%2015.2%20C26.6%2021%2018.8%2021.4%2016%2015.2%20Z'%20fill='none'%20stroke='%23000'%20stroke-width='3'%20stroke-linecap='round'%20stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2032%2032'%3E%3Cpath%20d='M16%2015.2%20C13.2%209%205.4%209.4%205.4%2015.2%20C5.4%2021%2013.2%2021.4%2016%2015.2%20C18.8%209%2026.6%209.4%2026.6%2015.2%20C26.6%2021%2018.8%2021.4%2016%2015.2%20Z'%20fill='none'%20stroke='%23000'%20stroke-width='3'%20stroke-linecap='round'%20stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat}
.dark .eyebrow{color:var(--on-dark-muted)}
.dark .eyebrow::before{background:var(--on-dark)}
h1,h2,h3,h4{margin:0;font-weight:620;letter-spacing:-.035em;text-wrap:balance}
.h1,.h2,.h3,.final-h,.hero-cap h2,.path h3,.cap-core h3,.contact h3,.phase h3,.stage h3,.pillar h3,.wechat h4,.wechat h3,.dr-nav b,.proj-head h3{font-family:var(--font-display);font-weight:400;font-optical-sizing:auto}
:lang(zh) .h1,:lang(zh) .h2,:lang(zh) .h3,:lang(zh) .final-h,:lang(zh) .hero-cap h2,:lang(zh) .path h3,:lang(zh) .cap-core h3,:lang(zh) .contact h3,:lang(zh) .phase h3,:lang(zh) .stage h3,:lang(zh) .pillar h3,:lang(zh) .wechat h4,.wechat h3,:lang(zh) .dr-nav b,:lang(zh) .proj-head h3{font-family:var(--font-zh-serif);font-weight:500}
.h2{font-size:clamp(33px,4.7vw,66px);line-height:1.06;letter-spacing:-.011em;max-width:17ch}
.h2 span{color:var(--faint-lg);font-style:italic}
.dark .h2 span{color:var(--nuit-mut)}
.h3{font-size:clamp(24px,2.3vw,33px);line-height:1.14;letter-spacing:-.008em}
.lead{font-size:clamp(16.5px,1.45vw,19px);line-height:1.58;color:var(--muted);max-width:54ch;margin:0}
.dark .lead{color:var(--on-dark-muted)}
.mono{font-family:var(--font-mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase}

/* ---------- Chinese typography: no negative tracking, taller leading ---------- */
:lang(zh) body{font-family:var(--font-zh);line-height:1.75}
:lang(zh) h1,:lang(zh) h2,:lang(zh) h3,:lang(zh) h4{letter-spacing:.01em;font-weight:600}
:lang(zh) .h1{line-height:1.14;font-size:clamp(40px,5.4vw,80px);letter-spacing:.01em}
:lang(zh) .h2{line-height:1.22;font-size:clamp(31px,4.6vw,62px);letter-spacing:.01em}
:lang(zh) .final-h{line-height:1.18;font-size:clamp(34px,5.4vw,76px)}
:lang(zh) .lead{line-height:1.8}
:lang(zh) .h2 span,:lang(zh) .h1 .mut,:lang(zh) .final-h span{font-style:normal}
:lang(zh) .eyebrow,:lang(zh) .mono{letter-spacing:.1em}
:lang(zh) .j-word{letter-spacing:.06em}
:lang(zh) .path .zh-line{display:none}

/* ---------- buttons (instant press feedback) ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:48px;padding:0 20px;border-radius:var(--r-sm);font-size:15px;font-weight:560;border:1px solid transparent;cursor:pointer;transition:background var(--t-fast),color var(--t-fast),border-color var(--t-fast),transform .1s ease-out;-webkit-tap-highlight-color:transparent}
.btn:active{transform:scale(.97)}
.btn .arr{display:inline-block;transition:transform var(--t-med) var(--ease)}
.btn:hover .arr{transform:translateX(4px)}
.btn:hover .arr.l{transform:translateX(-4px)}
/* Deux rôles, une matière, deux poids :
   PRINCIPAL  une vitre dépolie en forme de gélule. Sur le papier c'est la
              pièce sombre, sur la nuit bleue la pièce claire — dans les
              deux cas la surface se voit à travers, floutée et saturée.
              Arête haute plus claire que le reste du liseré : c'est la
              lumière qui accroche le biseau, et c'est ce détail-là qui
              fait lire « verre » plutôt que « aplat translucide ».
              La gélule n'est pas une coquetterie : c'est la forme des
              boutons d'iOS, et le site en a déjà deux familles (les cinq
              pastilles du parcours, le sélecteur de langue). Le bouton
              cesse d'être la seule pièce à angles du site.
   SECONDAIRE fond transparent, filet de la même famille froide, le texte
              de la surface. */
.btn{border-radius:999px}
.btn:focus-visible{border-radius:999px}
/* ============================ LE BOUTON ============================
   La forme ne bouge pas : même hauteur, même arrondi, même padding, même
   typographie, mêmes classes, même balisage. Seul le FOND change.

   Le principe : le bouton n'a presque pas de couleur à lui. À 8 % d'alpha
   il ne fait qu'ourler ce qu'il y a derrière — c'est le flou et la
   saturation qui font l'objet, pas la peinture. C'est le fonctionnement
   d'une vitre : elle ne se voit que par ce qu'elle déforme.

   Deux choses seulement suivent la surface, et elles ne sont pas
   négociables : la couleur du TEXTE et celle du LISERÉ. Un texte clair
   sur du papier ivoire ne se lit pas ; un liseré blanc sur du papier
   ivoire n'existe pas. Le reste — teinte, alphas, flou, ombre — est
   strictement identique partout.
   =================================================================== */
/* Le dégradé. Un aplat à 8 % ne se voit pas ; le même 8 % réparti en
   dégradé se voit, parce que l'œil lit une VARIATION là où il ne lit pas
   une valeur. La teinte s'épaissit sur l'arête haute — là où la lumière
   accrocherait une vraie vitre — et s'efface en bas à droite. La moyenne
   reste basse : le bouton est aussi transparent, il est simplement
   modelé. Les trois coefficients (2,4 / 1,0 / 0,5) sont le modelé ;
   --btn-a reste la seule commande de transparence. */
.btn-primary{
  background:linear-gradient(152deg,
      rgba(var(--btn-rvb),calc(var(--btn-a) * 2.4)) 0%,
      rgba(var(--btn-rvb),calc(var(--btn-a) * 1.0)) 46%,
      rgba(var(--btn-rvb),calc(var(--btn-a) * 0.5)) 100%);
  -webkit-backdrop-filter:blur(var(--btn-flou)) saturate(var(--btn-sat));
  backdrop-filter:blur(var(--btn-flou)) saturate(var(--btn-sat));
  border:1px solid var(--btn-rim-clair);
  box-shadow:var(--btn-ombre),inset 0 1px 0 rgba(255,255,255,.22);
  color:var(--ink);
  transition:background var(--t-fast),border-color var(--t-fast),
             box-shadow var(--t-fast),backdrop-filter var(--t-fast),
             transform .1s ease-out}
.btn-primary:hover{
  background:linear-gradient(152deg,
      rgba(var(--btn-rvb),calc(var(--btn-a-h) * 2.4)) 0%,
      rgba(var(--btn-rvb),calc(var(--btn-a-h) * 1.0)) 46%,
      rgba(var(--btn-rvb),calc(var(--btn-a-h) * 0.5)) 100%);
  -webkit-backdrop-filter:blur(var(--btn-flou-h)) saturate(var(--btn-sat));
  backdrop-filter:blur(var(--btn-flou-h)) saturate(var(--btn-sat));
  box-shadow:var(--btn-ombre-h),inset 0 1px 0 rgba(255,255,255,.3)}
.btn-primary:active{
  background:linear-gradient(152deg,
      rgba(var(--btn-rvb),calc(var(--btn-a-p) * 1.4)) 0%,
      rgba(var(--btn-rvb),calc(var(--btn-a-p) * 1.0)) 46%,
      rgba(var(--btn-rvb),calc(var(--btn-a-p) * 0.8)) 100%);
  transform:scale(.98);
  box-shadow:inset 0 2px 6px rgba(0,0,0,.12)}
/* Le reflet : une vitre prend la lumière sur son arête haute et l'éteint
   avant la moitié. À 8 % de fond, c'est lui qui donne l'épaisseur. */
.btn-primary{position:relative;isolation:isolate}
.btn-primary::before{content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;z-index:-1;
  background:linear-gradient(152deg,rgba(255,255,255,.30) 0%,
             rgba(255,255,255,.08) 34%,transparent 58%);
  transition:opacity var(--t-fast)}
.btn-primary:active::before{opacity:.4}
/* Le grain du dépoli : une turbulence SVG embarquée et désaturée, très
   basse. Aucun fichier, aucune requête. C'est ce qui sépare « verre
   dépoli » de « fond semi-transparent ». */
.btn-primary::after{content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;z-index:-1;opacity:.07;mix-blend-mode:soft-light;
  background: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='.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E") 0 0/140px 140px}
/* La pastille de la flèche : la même vitre, un cran plus dense. */
.btn-primary .arr{display:inline-grid;place-items:center;
  width:27px;height:27px;flex:none;margin-right:-9px;margin-left:3px;
  border-radius:999px;font-size:13px;line-height:1;
  background:rgba(var(--btn-rvb),calc(var(--btn-a) * 2.2));
  border:1px solid var(--btn-rim-clair);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.3);
  transition:transform var(--t-med) var(--ease),background var(--t-fast),border-color var(--t-fast)}
.btn-primary .arr.l{margin-right:3px;margin-left:-9px}
.btn-primary:hover .arr{background:rgba(var(--btn-rvb),calc(var(--btn-a-h) * 2.2));
  transform:translateX(3px)}
.btn-primary:hover .arr.l{transform:translateX(-3px)}
.btn-primary.btn-sm .arr{width:23px;height:23px;font-size:12px;margin-right:-7px}
/* Repli : sans backdrop-filter, une vitre n'est plus qu'un voile. On
   compense par un fond plus dense — c'est la seule chose à faire. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .btn-primary{background:rgba(var(--btn-rvb),var(--btn-a-plein))}
  .btn-primary:hover{background:rgba(var(--btn-rvb),calc(var(--btn-a-plein) + .06))}
  .btn-primary:active{background:rgba(var(--btn-rvb),calc(var(--btn-a-plein) + .1))}
}
/* LE SECONDAIRE — la même vitre, deux fois plus légère, sans ombre.
   Il doit rester en retrait du principal : c'est l'alpha qui porte la
   hiérarchie, pas une autre couleur. */
.btn-ghost{
  background:linear-gradient(152deg,
      rgba(var(--btn-rvb),calc(var(--btn-a) * 1.2)) 0%,
      rgba(var(--btn-rvb),calc(var(--btn-a) * 0.5)) 46%,
      rgba(var(--btn-rvb),calc(var(--btn-a) * 0.25)) 100%);
  -webkit-backdrop-filter:blur(var(--btn-flou)) saturate(var(--btn-sat));
  backdrop-filter:blur(var(--btn-flou)) saturate(var(--btn-sat));
  border:1px solid var(--btn-rim-clair);color:var(--ink);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14);
  transition:background var(--t-fast),border-color var(--t-fast),
             box-shadow var(--t-fast),transform .1s ease-out}
.btn-ghost:hover{background:linear-gradient(152deg,
      rgba(var(--btn-rvb),calc(var(--btn-a-h) * 1.2)) 0%,
      rgba(var(--btn-rvb),calc(var(--btn-a-h) * 0.5)) 46%,
      rgba(var(--btn-rvb),calc(var(--btn-a-h) * 0.25)) 100%);
  border-color:var(--warm-line-hi)}
.btn-ghost:active{background:rgba(var(--btn-rvb),calc(var(--btn-a-p) / 2));transform:scale(.98)}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .btn-ghost{background:rgba(var(--btn-rvb),calc(var(--btn-a-plein) / 2))}
}

/* SUR FOND SOMBRE — la matière ne change pas d'un point : même teinte,
   mêmes alphas, même flou. Seuls le texte et le liseré basculent, parce
   qu'un texte encre sur une nuit bleue ne se lit pas et qu'un liseré
   miel sur une nuit bleue ne se voit pas. C'est là que la transparence
   se remarque le plus : le globe passe littéralement à travers. */
.dark .btn-primary,.hd.on-dark .btn-primary{
  color:var(--on-dark);border-color:var(--btn-rim-sombre);
  background:linear-gradient(152deg,
      rgba(var(--btn-rvb-d),calc(var(--btn-a) * 4.0)) 0%,
      rgba(var(--btn-rvb-d),calc(var(--btn-a) * 1.0)) 46%,
      rgba(var(--btn-rvb-d),calc(var(--btn-a) * 0.35)) 100%);
  box-shadow:var(--btn-ombre),var(--btn-halo-sombre),
             inset 0 1px 0 var(--btn-eclat-sombre)}
.dark .btn-primary:hover,.hd.on-dark .btn-primary:hover{
  border-color:var(--btn-rim-sombre-h);
  background:linear-gradient(152deg,
      rgba(var(--btn-rvb-d),calc(var(--btn-a-h) * 4.0)) 0%,
      rgba(var(--btn-rvb-d),calc(var(--btn-a-h) * 1.0)) 46%,
      rgba(var(--btn-rvb-d),calc(var(--btn-a-h) * 0.35)) 100%);
  box-shadow:var(--btn-ombre-h),var(--btn-halo-sombre-h),
             inset 0 1px 0 var(--btn-eclat-sombre-h)}
/* La pastille de la flèche suit : son liseré et son reflet étaient blancs
   eux aussi, et sur un bouton de 27 px c'est une pièce qu'on regarde. */
.dark .btn-primary .arr,.hd.on-dark .btn-primary .arr{
  border-color:var(--btn-rim-sombre);
  box-shadow:inset 0 1px 0 var(--btn-eclat-sombre)}
.dark .btn-primary:hover .arr,.hd.on-dark .btn-primary:hover .arr{
  border-color:var(--btn-rim-sombre-h)}
.dark .btn-ghost{border-color:var(--btn-rim-sombre);color:var(--on-dark);
  box-shadow:inset 0 1px 0 rgba(248,236,213,.30)}
.dark .btn-ghost:hover{border-color:var(--btn-rim-sombre-h);color:#F6F2EA}

/* Une carte claire posée dans une section sombre reste une surface claire.
   La carte « Develop in China » est blanche : son bouton héritait du
   traitement sombre, donc un blanc sur du blanc, que seule une ombre portée
   séparait encore du carton. C'était le dernier bouton blanc isolé du site.
   Le système suit désormais la surface et non la section. */
/* La carte « Europe → Chine » est une surface CLAIRE posée dans une
   section sombre. C'est la surface qui commande, pas la section : texte
   encre et liseré miel, comme sur le papier. */
.dark .path-cn .btn-primary{color:var(--ink);border-color:var(--btn-rim-clair);
  box-shadow:var(--btn-ombre),inset 0 1px 0 rgba(255,255,255,.22)}
.dark .path-cn .btn-primary:hover{border-color:rgba(143,101,39,.46);
  box-shadow:var(--btn-ombre-h),inset 0 1px 0 rgba(255,255,255,.3)}
.dark .path-cn .btn-primary .arr{border-color:var(--btn-rim-clair)}
.dark .path-cn .btn-ghost{background:transparent;border-color:var(--warm-line);color:var(--ink)}
.dark .path-cn .btn-ghost:hover{border-color:var(--warm-line-hi);background:var(--warm-veil)}
.dark .path-cn :focus-visible{outline-color:var(--accent)}

/* Le liseré du bouton principal suivait son propre arrondi de 4 px, hérité
   de la règle générale : sur un bouton de 8 px il flottait. */
.btn:focus-visible{border-radius:var(--r-sm);outline-offset:3px}
.btn[disabled],.btn[aria-disabled="true"]{opacity:.42;pointer-events:none;box-shadow:none}
.btn-sm{min-height:40px;padding:0 16px;font-size:14px}
.link{display:inline-flex;align-items:center;gap:8px;font-weight:540;font-size:15px;color:var(--accent-text);border-bottom:1px solid rgba(7,113,155,.35);padding:11px 0 5px;transition:color var(--t-fast),border-color var(--t-fast)}
.link:hover{color:var(--ink);border-color:var(--ink)}
.dark .link,.cap-core .link{color:var(--accent-dark);border-color:rgba(143,176,255,.4)}
.dark .link:hover,.cap-core .link:hover{color:var(--on-dark);border-color:var(--on-dark)}
.link .arr{transition:transform var(--t-med) var(--ease)}
.link:hover .arr{transform:translateX(4px)}

/* ============================ HEADER (translucent material) ============================ */
.hd{position:sticky;top:env(safe-area-inset-top,0px);z-index:50;background:rgba(244,246,248,.72);-webkit-backdrop-filter:saturate(1.8) blur(20px);backdrop-filter:saturate(1.8) blur(20px);box-shadow:inset 0 -1px 0 rgba(10,13,18,.04);transition:background var(--t-med),box-shadow var(--t-med)}
.hd.on-dark{background:rgba(7,11,18,.62);color:var(--on-dark);box-shadow:inset 0 -1px 0 rgba(233,238,244,.08),inset 0 1px 0 rgba(255,255,255,.05)}
.hd.on-dark .logo b,.hd.on-dark .nav{color:var(--on-dark-muted)}
.hd.on-dark .logo svg{color:var(--on-dark)}
.hd.on-dark .nav a:hover,.hd.on-dark .nav a.on{color:var(--on-dark)}

.hd.on-dark .lang{border-color:rgba(233,238,244,.18);background:linear-gradient(180deg,rgba(255,255,255,.12),rgba(255,255,255,.03));box-shadow:inset 0 1px 0 rgba(255,255,255,.14)}
.hd.on-dark .lang button{color:var(--on-dark-muted)}
.hd.on-dark .lang button[aria-pressed="true"]{background:var(--on-dark);color:var(--ink)}
/* L'en-tête sur fond sombre est couvert par la règle groupée plus haut :
   c'est la même vitre claire, posée sur la même nuit. */
.hd.on-dark .menu-btn span{background:var(--on-dark)}
.hd.on-dark .route{background:linear-gradient(180deg,rgba(233,238,244,.10),rgba(233,238,244,.16))}
.hd.on-dark .route-tick{background:var(--navy)}
.hd.on-dark .route-tick.cur i{color:var(--on-dark)}
@media (prefers-reduced-transparency:reduce){.hd{background:var(--paper);backdrop-filter:none}.hd.on-dark{background:var(--navy)}.drawer{background:var(--navy);backdrop-filter:none}.lang,.stops button{backdrop-filter:none}
  /* Transparence réduite : le flou tombe, donc le fond doit porter seul.
     On passe à l'alpha plein et on ferme encore d'un cran sur le sombre,
     où un champagne à 18 % ne suffit pas à poser le bouton. */
  .btn-primary,.btn-ghost{-webkit-backdrop-filter:none;backdrop-filter:none}
  .btn-primary{background:rgba(var(--btn-rvb),var(--btn-a-plein))}
  .btn-primary:hover{background:rgba(var(--btn-rvb),calc(var(--btn-a-plein) + .07))}
  .btn-ghost{background:rgba(var(--btn-rvb),calc(var(--btn-a-plein) / 2))}
  .dark .btn-primary,.hd.on-dark .btn-primary{background:rgba(var(--btn-rvb),.28)}
  .dark .btn-primary:hover,.hd.on-dark .btn-primary:hover{background:rgba(var(--btn-rvb),.38)}
  .btn-primary::after{display:none}}
/* Contraste renforcé : la vitre se ferme et le liseré se déclare. */
@media (prefers-contrast:more){
  .btn-primary{background:var(--champ);color:var(--ink);border-color:#5C431C;box-shadow:none}
  .btn-primary::before,.btn-primary::after{display:none}
  .dark .btn-primary,.hd.on-dark .btn-primary{background:var(--champ);color:var(--ink);
    border-color:#F6F2EA;box-shadow:none}
  .btn-primary .arr{background:rgba(92,67,28,.14);border-color:rgba(92,67,28,.5)}
  .btn-ghost{background:transparent;border-color:currentColor}}
.hd-in{height:var(--hd);display:flex;align-items:center;gap:24px}
.logo{display:inline-flex;align-items:center;gap:10px;padding-block:7px;font-size:17px;letter-spacing:-.02em;font-weight:650;flex:none;font-family:var(--font-sans)}
.logo b{font-weight:420;color:var(--muted);transition:color var(--t-med)}
.logo svg{width:30px;height:30px;flex:none;color:var(--ink)}
.logo svg{transition:color var(--t-med)}
.nav{display:flex;gap:26px;margin-left:auto;font-size:14px;color:var(--muted);transition:color var(--t-med)}
.nav a{position:relative;padding-block:11px;white-space:nowrap;transition:color var(--t-fast)}
.nav a::after{content:"";position:absolute;left:0;right:0;bottom:7px;height:1px;background:currentColor;transform:scaleX(0);transform-origin:0 50%;transition:transform .45s var(--ease)}
.nav a:hover::after,.nav a.on::after{transform:scaleX(1)}
.nav a:hover,.nav a.on{color:var(--ink)}
.hd-r{display:flex;align-items:center;gap:14px}
.hd-r .btn{white-space:nowrap}
.lang{display:flex;align-items:center;border:1px solid rgba(10,13,18,.1);border-radius:999px;padding:2px;font-size:12px;background:linear-gradient(180deg,rgba(255,255,255,.75),rgba(255,255,255,.35));-webkit-backdrop-filter:blur(12px) saturate(1.5);backdrop-filter:blur(12px) saturate(1.5);box-shadow:inset 0 1px 0 rgba(255,255,255,.9),0 1px 2px rgba(10,13,18,.04)}
.lang button{background:none;border:0;padding:0 11px;border-radius:999px;cursor:pointer;color:var(--muted);letter-spacing:.04em;font-family:var(--font-mono);font-size:12px;min-width:44px;min-height:40px;transition:background var(--t-fast),color var(--t-fast)}
.lang button.zhb{font-family:var(--font-zh);letter-spacing:0;font-size:13px}
.lang button[aria-pressed="true"]{background:var(--ink);color:var(--white)}
.menu-btn{display:none;width:44px;height:44px;border:0;background:none;cursor:pointer;margin-right:-10px}
.menu-btn span{display:block;width:20px;height:1.5px;background:var(--ink);margin:5px auto;transition:transform var(--t-med) var(--ease)}
.hd.open .menu-btn span:first-child{transform:translateY(3.25px) rotate(45deg)}
.hd.open .menu-btn span:last-child{transform:translateY(-3.25px) rotate(-45deg)}
/* signature: the route line under the nav */
.route{position:relative;height:2px;background:linear-gradient(180deg,rgba(10,13,18,.05),rgba(10,13,18,.09));transition:background var(--t-med)}
.route-fill{position:absolute;inset:0;transform-origin:0 50%;transform:scaleX(0);background:linear-gradient(90deg,var(--accent) 0%,#6C87CE 22%,#B9C7E2 38%,#FFF 50%,#EDB7AE 62%,#CE6153 78%,var(--cn) 100%);box-shadow:0 0 6px rgba(10,13,18,.08),0 0 5px rgba(255,255,255,.9)}
.route-ticks{position:absolute;top:0;bottom:0;left:var(--gutter);right:var(--gutter)}
.route-tick{position:absolute;top:1px;transform:translate(-50%,-50%);width:5px;height:5px;border-radius:50%;background:var(--paper);border:1px solid var(--line-2);transition:background var(--t-med),border-color var(--t-med)}
.route-tick.on{background:var(--accent);border-color:var(--accent)}
.route-tick:nth-child(3).on{background:#93A3CE;border-color:#93A3CE}
.route-tick:nth-child(4).on{background:#C98476;border-color:#C98476}
.route-tick:last-child.on{background:var(--cn);border-color:var(--cn)}
.route-tick:first-child.on{background:var(--accent);border-color:var(--accent)}
.route-tick i{position:absolute;top:8px;left:50%;transform:translateX(-50%);font:500 11px/1 var(--font-mono);letter-spacing:.14em;text-transform:uppercase;color:var(--faint);white-space:nowrap;font-style:normal;opacity:0;transition:opacity var(--t-med),color var(--t-med)}
:lang(zh) .route-tick i{font-family:var(--font-zh);font-size:11px;letter-spacing:.1em}
.route-tick.cur i{color:var(--ink);opacity:1}
.route-tick:first-child i{left:0;transform:none}
.route-tick:last-child i{left:auto;right:0;transform:none}
/* ---- mobile drawer: slides over part of the page ---- */
.scrim{position:fixed;left:0;right:0;bottom:0;top:calc(var(--hd) + env(safe-area-inset-top,0px) + 1px);z-index:48;background:rgba(7,11,18,.42);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);opacity:0;visibility:hidden;transition:opacity .35s var(--ease),visibility 0s linear .35s}
.hd.open~.scrim,body.menu-open .scrim{opacity:1;visibility:visible;transition:opacity .35s var(--ease)}
.drawer{position:fixed;z-index:49;top:calc(var(--hd) + env(safe-area-inset-top,0px) + 1px);right:0;bottom:0;border-top-left-radius:18px;width:min(86vw,410px);background:rgba(7,11,18,.88);-webkit-backdrop-filter:blur(24px) saturate(1.6);backdrop-filter:blur(24px) saturate(1.6);border-left:1px solid rgba(233,238,244,.08);color:var(--on-dark);box-shadow:-30px 0 60px -30px rgba(0,0,0,.6);transform:translateX(102%);visibility:hidden;transition:transform .5s var(--ease),visibility 0s linear .5s;overflow-y:auto;overscroll-behavior:contain;display:none}
.dr-in{padding:16px clamp(18px,5.5vw,26px) calc(18px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:6px;min-height:100%}
/* Les intitulés du tiroir — EXPLORER, CHOISISSEZ UNE DIRECTION, CONTACT,
   et les libellés EMAIL / WECHAT — étaient d'un gris froid un peu plus
   sombre que le texte. Ils passent au champagne : c'est la couleur de
   l'action sur ce site, et ce sont les seules lignes du tiroir dont le
   rôle est de guider plutôt que de nommer. Un champagne à peine retenu,
   pour qu'ils restent des étiquettes et ne concurrencent pas les entrées. */
.dr-k{margin:9px 0 2px;font-family:var(--font-mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--champ-k)}
:lang(zh) .dr-k{font-family:var(--font-zh);font-size:12px;letter-spacing:.1em}
.dr-k:first-child{margin-top:0}
.dr-nav a{display:grid;grid-template-columns:24px 1fr auto;column-gap:11px;padding:8px 0;border-top:1px solid var(--navy-line);align-items:baseline}
.dr-nav a:first-child{border-top:0}
.dr-nav i{font-style:normal;font-family:var(--font-mono);font-size:10.5px;color:var(--accent-dark);opacity:.75}
.dr-nav b{font-size:19px;letter-spacing:-.004em;font-weight:400;line-height:1.24}
.dr-nav small{grid-column:2/-1;color:var(--on-dark-muted);font-size:12px;line-height:1.3;margin-top:1px}
.dr-nav a:active b{color:var(--accent-dark)}
.dr-dirs{display:grid;grid-template-columns:1fr 1fr;gap:7px}
.dr-dirs a{display:flex;flex-direction:column;gap:2px;padding:9px 11px;border:1px solid var(--navy-line);border-radius:var(--r-md);background:linear-gradient(120deg,rgba(143,176,255,.06),transparent 70%);transition:border-color var(--t-fast),transform .12s ease-out}
.dr-dirs a:last-child{background:linear-gradient(120deg,rgba(227,89,75,.09),transparent 70%)}
.dr-dirs a:active{transform:scale(.99);border-color:var(--accent-dark)}
.dr-dirs .mono{font-size:11px;letter-spacing:.07em;color:var(--on-dark-muted)}
:lang(zh) .dr-dirs .mono{font-family:var(--font-zh);font-size:12px}
.dr-dirs b{font-size:14px;font-weight:580;line-height:1.25}
.dr-contact{display:grid;gap:0}
.dr-contact a,.dr-line{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:8px 0;border-top:1px solid var(--navy-line);font-size:13.5px}
.dr-contact .mono{font-size:11px;color:var(--champ-k);flex:none;
  letter-spacing:.12em;text-transform:uppercase}
:lang(zh) .dr-contact .mono{font-family:var(--font-zh);font-size:12px}
.dr-contact b{font-weight:500;text-align:right;word-break:break-word}
.dr-times{display:none}
.dr-times-unused{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:12px 0;margin-top:2px;border-top:1px solid var(--navy-line);border-bottom:1px solid var(--navy-line);font-size:13.5px;color:var(--on-dark-muted)}
.dr-times .mono{font-size:10.5px;white-space:nowrap;flex:none}
.dr-times span+span{text-align:right}
:lang(zh) .dr-times .mono{font-family:var(--font-zh);font-size:12px}
.dr-times b{color:var(--on-dark);font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-weight:500}
.dr-cta{margin-top:10px;width:100%}
.dr-foot{margin-top:auto;padding-top:14px;display:flex;align-items:center;justify-content:flex-start;gap:14px;flex-wrap:wrap}
.dr-legal{display:flex;align-items:center;gap:10px;margin:0;font-size:13px;color:var(--on-dark)}
.dr-legal{font-size:12px}
.dr-legal .seal{width:22px;height:22px}
/* staggered arrival, once the panel is in place */
.drawer .dr-in>*{opacity:0;transform:translateY(10px)}
.hd.open~.drawer .dr-in>*{opacity:1;transform:none;transition:opacity .4s var(--ease),transform .5s var(--ease)}
.hd.open~.drawer .dr-in>*:nth-child(2){transition-delay:.04s}
.hd.open~.drawer .dr-in>*:nth-child(3){transition-delay:.07s}
.hd.open~.drawer .dr-in>*:nth-child(4){transition-delay:.1s}
.hd.open~.drawer .dr-in>*:nth-child(5){transition-delay:.13s}
.hd.open~.drawer .dr-in>*:nth-child(6){transition-delay:.16s}
.hd.open~.drawer .dr-in>*:nth-child(7){transition-delay:.19s}
.hd.open~.drawer .dr-in>*:nth-child(8){transition-delay:.22s}
.hd.open~.drawer .dr-in>*:nth-child(9){transition-delay:.25s}
@media (prefers-reduced-motion:reduce){.drawer .dr-in>*{opacity:1;transform:none}}
/* the drawer earns its place by fitting one screen — it sheds detail as the screen shortens */
@media (max-height:900px){
  .dr-nav a{padding:6px 0}
  .dr-nav small{font-size:11.5px}
}
@media (max-height:870px){
  .dr-nav small{display:none}
  .dr-nav a{grid-template-columns:24px 1fr;padding:6px 0}
}
@media (max-height:760px){
  .dr-in{padding-top:12px;gap:4px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px))}
  .dr-k{margin:6px 0 1px;font-size:10.5px}
  .dr-nav a{padding:3px 0}
  .dr-nav b{font-size:17px}
  .dr-dirs a{padding:7px 10px}
  .dr-dirs b{font-size:13px}
  .dr-contact a,.dr-line{padding:6px 0;font-size:13px}
  .dr-cta{margin-top:7px}
  .dr-foot{padding-top:6px}
  .dr-legal{font-size:11.5px}
  .dr-legal .seal{width:19px;height:19px}
}

/* ============================ HERO — the pinned globe banner ============================ */
/* Deep space at the top of the frame, easing into the site's own blue where the
   atmosphere begins. Two stops, no visible banding, and nothing here that the
   rest of the site does not already use. */
.hero{position:relative;background:
  radial-gradient(120% 78% at 53% 104%,rgba(42,86,198,.34) 0%,rgba(24,50,116,.20) 34%,rgba(12,18,34,0) 66%),
  linear-gradient(180deg,#05070C 0%,#070B14 28%,#0A1020 58%,var(--navy) 100%)}
.js .hero{height:330vh}
.hero-stick{padding-block:clamp(28px,5vw,64px)}
.js .hero-stick{position:sticky;top:calc(var(--hd) + 1px + env(safe-area-inset-top,0px));height:calc(100vh - var(--hd) - 1px);height:calc(100svh - var(--hd) - 1px - env(safe-area-inset-top,0px));overflow:hidden;display:flex;padding-block:clamp(14px,2.4vh,36px)}
/* CORRECTIF — fin d'épinglage : voir le commentaire dans site.js. La découpe
   est posée par le script, au ras de l'en-tête, et seulement à la sortie. */
.js .hero-stick.cut{-webkit-clip-path:inset(var(--cut,0px) 0 0 0);clip-path:inset(var(--cut,0px) 0 0 0)}
.hero-canvas{position:absolute;inset:0;z-index:0}
.hero-canvas canvas{position:absolute;inset:0;width:100%;height:100%;display:block;touch-action:pan-y}
/* The scrim seats the copy without dimming the Earth: it is strongest at the
   left edge, where the type is, and gone by the middle of the frame. */
.hero-scrim{position:absolute;inset:0;z-index:1;pointer-events:none;background:
  linear-gradient(90deg,rgba(6,9,16,.95) 0%,rgba(6,9,16,.90) 24%,rgba(6,9,16,.74) 35%,rgba(6,9,16,.40) 45%,rgba(6,9,16,0) 60%),
  linear-gradient(180deg,rgba(5,7,12,.86) 2%,rgba(5,7,12,.44) 18%,rgba(5,7,12,0) 34%),
  linear-gradient(0deg,rgba(6,9,16,.93) 4%,rgba(6,9,16,.80) 15%,rgba(6,9,16,.48) 27%,rgba(6,9,16,.16) 38%,rgba(6,9,16,0) 52%)}
.hero-inner{position:relative;z-index:2;width:100%;height:100%;display:flex;flex-direction:column;gap:clamp(10px,1.6vh,20px)}
.hero-copy{max-width:min(660px,60%)}
.hero-caps{margin-top:auto;position:relative;display:grid;min-height:132px;max-width:min(560px,58%)}

.h1{font-size:clamp(42px,5.2vw,86px);line-height:1.04;letter-spacing:-.013em;font-weight:400;max-width:16ch}
.h1 .ln{display:block;overflow:hidden;padding-bottom:.08em;margin-bottom:-.08em}
.h1 .ln>span{display:inline-block;animation:rise 1s var(--ease) both}
.h1 .ln+.ln>span{animation-delay:.09s}
.h1 .mut{color:var(--faint-lg);font-style:italic}
@keyframes rise{from{transform:translateY(105%)}to{transform:none}}
.hero-lead{margin-top:20px;max-width:36ch;animation:fadeup .9s var(--ease) .25s both}
.hero .ctas{animation:fadeup .9s var(--ease) .35s both}
@keyframes fadeup{from{opacity:.001;transform:translateY(12px)}to{opacity:1;transform:none}}
.ctas{display:flex;flex-wrap:wrap;gap:12px;margin-top:clamp(20px,3vh,34px)}
.hero-cap{grid-area:1/1;max-width:48ch}
.hero-cap .mono{color:var(--accent-text);font-size:11px}
:lang(zh) .hero-cap .mono{font-family:var(--font-zh);font-size:12.5px}
.hero-cap h2{font-size:clamp(23px,2.3vw,34px);letter-spacing:-.008em;line-height:1.14;margin:10px 0 8px;font-weight:400}
.hero-cap p{margin:0;color:var(--muted);font-size:clamp(14.5px,1.15vw,16.5px);line-height:1.45}
.js .hero-cap{opacity:0;transform:translateY(14px);filter:blur(5px);transition:opacity .5s var(--ease),transform .6s var(--ease),filter .5s var(--ease);pointer-events:none}
.js .hero-cap.on{opacity:1;transform:none;filter:none;pointer-events:auto}
.hero-foot{position:relative;display:flex;align-items:center;justify-content:space-between;gap:12px 24px;flex-wrap:wrap}

/* ---------- écrans courts : les cinq étapes doivent rester visibles ----------
   Le bloc épinglé mesure 100svh moins l'en-tête, et il est en overflow:hidden :
   dès que son contenu dépasse, c'est le bas qui saute — donc précisément les
   cinq pastilles du parcours, qui sont le sommaire de toute la page.
   Mesuré : elles passaient jusqu'à 700 px de hauteur utile et se coupaient à
   660. Une barre des tâches fixe, une barre de favoris et un écran 768p
   suffisent à descendre là.
   On ne déplace rien : on resserre le rythme vertical à mesure que la place
   manque — le titre, les blancs, et le cartouche du parcours, qui peut céder
   sa hauteur minimale puisqu'il n'affiche qu'une légende à la fois. Au-dessus
   de 800 px, pas un pixel ne change. */
@media (min-width:821px) and (max-height:800px){
  .js .hero-stick{padding-block:clamp(10px,1.6vh,22px)}
  .hero-inner{gap:clamp(6px,1vh,12px)}
  .h1{font-size:clamp(38px,4.2vw,62px)}
  .hero-lead{margin-top:12px}
  .ctas{margin-top:clamp(14px,2vh,22px)}
  .hero-caps{min-height:0;overflow:hidden}
  .hero-cap h2{font-size:clamp(21px,1.9vw,27px);margin:7px 0 5px}
}
@media (min-width:821px) and (max-height:680px){
  .js .hero-stick{padding-block:8px}
  .h1{font-size:clamp(34px,3.6vw,48px)}
  .hero-lead{margin-top:9px;font-size:15px}
  .ctas{margin-top:12px}
  /* La phrase sous chaque titre du parcours était masquée ici pour gagner
     48 px. C'était le mauvais arbitrage : le titre seul (« Tout commence par
     une ambition ») ne dit rien sans elle. On la garde et on la resserre —
     la mesure montre qu'il restait 40 px de marge libre à 600 px de hauteur,
     largement de quoi la loger. */
  .hero-cap p{font-size:14px;line-height:1.42}
  .stops button{min-height:34px;font-size:10.5px;padding:0 11px}
}
/* Dernier recours, sous 560 px de fenêtre : l'heure locale des deux villes
   cède sa ligne. Elle est décorative et elle figure aussi dans le menu. */
@media (min-width:821px) and (max-height:560px){
  .hero-lead{display:none}
  .clocks{display:none}
}

.stops{display:flex;gap:6px;flex-wrap:wrap}
.stops button{background:linear-gradient(180deg,rgba(255,255,255,.8),rgba(255,255,255,.4));-webkit-backdrop-filter:blur(12px) saturate(1.4);backdrop-filter:blur(12px) saturate(1.4);border:1px solid rgba(10,13,18,.09);box-shadow:inset 0 1px 0 rgba(255,255,255,.85);color:var(--muted);border-radius:999px;padding:0 14px;min-height:40px;font-family:var(--font-mono);font-size:11.5px;letter-spacing:.1em;text-transform:uppercase;cursor:pointer;display:inline-flex;align-items:center;gap:8px;white-space:nowrap;transition:color var(--t-fast),border-color var(--t-fast),background var(--t-fast),transform .1s ease-out}
.stops button:active{transform:scale(.97)}
:lang(zh) .stops button{font-family:var(--font-zh);font-size:12.5px;letter-spacing:.08em}
.stops button::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.4}
.stops button.on{color:var(--ink)}
.stops button.cur{background:linear-gradient(180deg,#1B232E,#0A0D12);color:var(--white);border-color:#0A0D12;box-shadow:inset 0 1px 0 rgba(255,255,255,.14),0 2px 8px -4px rgba(10,13,18,.5)}
.stops button.cur::before{opacity:1;background:var(--accent-dark)}
.hero-foot .clocks{margin:0;padding:0;border:0}
.hero-prog{height:1px;background:var(--line);position:relative}
.hero-prog i{position:absolute;inset:0;transform-origin:0 50%;transform:scaleX(var(--hp,0));background:linear-gradient(90deg,var(--accent),#DCE1E9 52%,var(--cn))}
.clocks{display:flex;align-items:center;gap:14px;font-family:var(--font-mono);font-size:12px;letter-spacing:.08em;color:var(--muted);flex-wrap:wrap}
/* Le bloc garde sa place mais ne se montre qu'une fois l'heure calculée :
   sans JS, on ne voit pas « --:-- », et la mise en page ne bouge pas. */
.clocks{visibility:hidden}
.clocks.on{visibility:visible}
:lang(zh) .clocks{font-family:var(--font-zh)}
.clocks b{color:var(--ink);font-weight:500;font-variant-numeric:tabular-nums;font-family:var(--font-mono)}
.clocks .sep{flex:1;min-width:30px;height:1px;background:linear-gradient(90deg,var(--accent),#C9CFDA 50%,var(--cn));position:relative;opacity:.8}
.clocks .sep::after{content:"";position:absolute;top:-2px;left:0;width:5px;height:5px;border-radius:50%;background:var(--accent);animation:hop 5s var(--ease) infinite}

/* ---------- the hero reads on the night side of the planet ---------- */
.hero-canvas canvas{position:absolute;inset:0}
#globeEarth{z-index:0}
#globe{z-index:1}
.h1 .mut{color:#8B93A0}
.dark .h1 .mut{color:var(--nuit-mut)}
/* the lead runs closest to the lit limb, so it carries a little more light than
   a lead on a flat dark section would need */
.hero .hero-lead{color:var(--nuit-lead)}
.hero-cap .mono{color:var(--accent-dark)}
.hero-cap p{color:var(--on-dark-muted)}
.hero .clocks{color:var(--on-dark-muted)}
.hero .clocks b{color:var(--on-dark)}
.hero .clocks .sep{background:linear-gradient(90deg,var(--accent-dark),#5C6577 50%,var(--cn-dark));opacity:.65}
.hero .clocks .sep::after{background:var(--accent-dark)}
.hero-prog{background:rgba(198,206,222,.16)}
.hero-prog i{background:linear-gradient(90deg,var(--accent-dark),var(--nuit-mut) 52%,var(--cn-dark))}
.hero .stops button{background:linear-gradient(180deg,rgba(255,255,255,.10),rgba(255,255,255,.03));border-color:rgba(233,238,244,.18);box-shadow:inset 0 1px 0 rgba(255,255,255,.10);color:var(--on-dark-muted)}
.hero .stops button.on{color:var(--on-dark)}
.hero .stops button.cur{background:linear-gradient(180deg,rgba(255,255,255,.96),rgba(228,235,242,.88));color:var(--ink);border-color:rgba(255,255,255,.9);box-shadow:inset 0 1px 0 #fff,0 2px 12px -5px rgba(143,176,255,.7)}
.hero .stops button.cur::before{opacity:1;background:var(--accent)}
.hero .stops button:hover{border-color:rgba(233,238,244,.42);color:var(--on-dark)}
/* the credit the imagery asks for, small and out of the way */
.earth-credit{position:absolute;right:var(--gutter);top:8px;z-index:2;margin:0;
  font-family:var(--font-mono);font-size:9.5px;letter-spacing:.07em;text-transform:uppercase;
  color:rgba(155,160,170,.42);pointer-events:none}
@media (max-width:640px){.earth-credit{font-size:8px;letter-spacing:.04em;color:rgba(155,160,170,.34)}
  .earth-credit .ec-long{display:none}}
@keyframes hop{0%{left:0;opacity:0}10%{opacity:1}90%{opacity:1}100%{left:calc(100% - 5px);opacity:0}}

/* ============================ SECTIONS ============================ */
.sec{padding-block:clamp(72px,8.6vw,124px)}
.sec{position:relative}
.mtn{position:absolute;left:0;right:0;bottom:0;height:min(64%,440px);pointer-events:none;z-index:0;background:url(img/mountain.6de5775985.webp) no-repeat right bottom/auto 100%;mix-blend-mode:multiply;opacity:.54;-webkit-mask-image:linear-gradient(180deg,transparent 2%,#000 52%),linear-gradient(90deg,transparent 0%,#000 34%);-webkit-mask-composite:source-in;mask-image:linear-gradient(180deg,transparent 2%,#000 52%),linear-gradient(90deg,transparent 0%,#000 34%);mask-composite:intersect;transform:translate3d(0,calc((1 - var(--q,1)) * 26px),0);will-change:transform}
/* La seconde montagne était retournée (scaleX(-1)) et calée à gauche. Or la
   photographie est rognée net sur son bord droit : retournée, ce bord net
   retombait en plein milieu de la page et coupait la montagne à la verticale.
   Elle reprend donc le sens et le calage de la première — le bord rogné va
   se perdre contre le bord de l'écran, et le dégradé du masque adoucit
   le flanc gauche, qui est le côté tendre de l'image. */
.mtn-b{height:clamp(210px,52%,330px);opacity:.38;transform:translate3d(0,calc((1 - var(--q,1)) * 20px),0);-webkit-mask-image:linear-gradient(180deg,transparent 4%,#000 56%),linear-gradient(90deg,transparent 0%,#000 30%);mask-image:linear-gradient(180deg,transparent 4%,#000 56%),linear-gradient(90deg,transparent 0%,#000 30%)}
@media (prefers-reduced-motion:reduce){.mtn{transform:none}.mtn-b{transform:none}}
.sec>.wrap{position:relative;z-index:1}
.sec.tight{padding-block:clamp(58px,6.8vw,96px)}
.white{background:var(--white)}
.dark{background:var(--navy);color:var(--on-dark)}
.sh{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:22px 64px;align-items:end;margin-bottom:clamp(34px,4.4vw,62px)}

/* ---------- Paths: one bridge, two markets (pointer spotlight) ---------- */
.paths{display:grid;grid-template-columns:minmax(0,1fr) 72px minmax(0,1fr);align-items:stretch}
.path{position:relative;display:flex;flex-direction:column;padding:clamp(28px,3.4vw,48px);border-radius:var(--r-lg);overflow:hidden;isolation:isolate;transition:transform .5s var(--ease),box-shadow .5s var(--ease)}
.path::before{content:"";position:absolute;inset:0;z-index:-1;opacity:0;transition:opacity .4s;background:radial-gradient(420px circle at var(--mx,50%) var(--my,50%),rgba(42,86,198,.09),transparent 60%)}
.path-eu::before{background:radial-gradient(420px circle at var(--mx,50%) var(--my,50%),rgba(179,154,120,.14),transparent 60%)}
.path:hover::before{opacity:1}
.path:hover{transform:translateY(-4px)}
.path-cn{background:var(--white);border:1px solid var(--line)}
.path-cn:hover{box-shadow:0 30px 60px -34px rgba(10,30,60,.3)}
.path-eu{box-shadow:inset 0 1px 0 rgba(255,255,255,.07)}
.path-eu:hover{box-shadow:inset 0 1px 0 rgba(255,255,255,.07),0 30px 60px -28px rgba(7,11,18,.6)}
.dir{display:flex;align-items:center;gap:12px;font-family:var(--font-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
:lang(zh) .dir{font-family:var(--font-zh);font-size:13px}
.path-eu .dir{color:var(--on-dark-muted)}
.dir-line{flex:1;height:1px;position:relative;overflow:hidden;background:var(--line)}
.path-eu .dir-line{background:var(--navy-line)}
.dir-line::after{content:"";position:absolute;top:0;height:1px;width:34%;background:linear-gradient(90deg,transparent,var(--accent));left:-34%;animation:runR 2.8s var(--ease) infinite}
.path-eu .dir-line::after{background:linear-gradient(270deg,transparent,var(--cn-dark));left:auto;right:-34%;animation:runL 2.8s var(--ease) infinite}
.path:hover .dir-line::after{animation-duration:1.2s}
@keyframes runR{to{left:100%}}
@keyframes runL{to{right:100%}}
.path h3{margin-top:clamp(26px,3.2vw,44px);font-size:clamp(30px,3.3vw,46px);line-height:1.08;letter-spacing:-.01em;max-width:13ch}
.path .for{margin:14px 0 0;color:var(--muted);font-size:17px;max-width:36ch}
.path-eu .for{color:var(--on-dark-muted)}
.path .zh-line{margin:8px 0 0;font-size:14px;color:var(--on-dark-muted)}
.ticks{list-style:none;padding:0;margin:26px 0 30px}
.ticks li{display:flex;gap:14px;align-items:baseline;padding:11px 0;border-top:1px solid var(--line);font-size:16px;transition:transform .4s var(--ease)}
.path:hover .ticks li{transform:translateX(3px)}
.path-eu:hover .ticks li{transform:translateX(-3px)}
.path .ticks li:nth-child(2){transition-delay:.02s}.path .ticks li:nth-child(3){transition-delay:.04s}.path .ticks li:nth-child(4){transition-delay:.06s}.path .ticks li:nth-child(5){transition-delay:.08s}.path .ticks li:nth-child(6){transition-delay:.1s}.path .ticks li:nth-child(7){transition-delay:.12s}
.path-eu .ticks li{border-color:var(--navy-line)}
.ticks li::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--accent);flex:none;transform:translateY(-2px)}
.path-eu .ticks li::before{background:var(--cn-dark)}
.path .btn{margin-top:auto;align-self:flex-start}
/* L'écart entre les deux cartes « Europe -> Chine » et « Chine -> Europe ».
   Il a porté successivement deux flèches, puis un pivot, puis une gélule de
   verre. Plus rien aujourd'hui : à cet endroit, tout ornement se lisait
   comme une commande posée entre deux cartes, et aucun ne gagnait sa place.
   L'élément reste dans le balisage, vide, parce qu'il tient la colonne du
   milieu de la grille .paths ci-dessus. C'est une respiration. */
.bridge-mid{display:block}

/* ---------- What we do ---------- */
.caps{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
.cap{position:relative;background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);padding:clamp(28px,3.2vw,44px);display:flex;flex-direction:column;overflow:hidden;transition:box-shadow .5s var(--ease),transform .5s var(--ease)}
.cap:hover{box-shadow:0 30px 60px -40px rgba(10,30,60,.35);transform:translateY(-3px)}
.cap-core{grid-column:1/-1;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:24px 64px;background:var(--ink);color:var(--on-dark);border-color:var(--ink);box-shadow:inset 0 1px 0 rgba(255,255,255,.07)}
.cap-core canvas{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;opacity:.9}
.cap-core>*:not(canvas){position:relative}
.cap-top{display:flex;align-items:center;justify-content:space-between;gap:16px}
.cap-num{font-family:var(--font-mono);font-size:12px;letter-spacing:.12em;color:var(--faint)}
.chip{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;padding:5px 10px;border-radius:999px;border:1px solid var(--line-2);color:var(--muted)}
:lang(zh) .chip{font-family:var(--font-zh);font-size:12px}
.cap-core .chip{border-color:rgba(143,176,255,.45);color:var(--accent-dark)}
.cap-core .cap-num{color:var(--on-dark-muted)}
.cap h3{margin-top:24px}
.cap-core h3{font-size:clamp(34px,3.9vw,54px);letter-spacing:-.012em;line-height:1.06;max-width:11ch}
.cap p{color:var(--muted);margin:14px 0 0;max-width:46ch;font-size:17px}
.cap-core p{color:var(--on-dark-muted);font-size:18px}
.cap ul{list-style:none;padding:0;margin:24px 0 26px;display:flex;flex-wrap:wrap;gap:8px}
.cap li{font-size:14px;padding:7px 12px;border-radius:999px;background:var(--paper);color:var(--char)}
.cap-core ul{display:grid;grid-template-columns:1fr 1fr;gap:0 28px;margin:0;align-content:start}
.cap-core li{background:none;border-radius:0;padding:14px 0;border-top:1px solid rgba(233,238,244,.12);color:var(--on-dark);font-size:16px}
.cap .link{margin-top:auto;align-self:flex-start}
.cap-core .cap-l{display:flex;flex-direction:column}
.cap-core .link{margin-top:36px}
.glyph{position:absolute;right:clamp(20px,3vw,40px);top:clamp(64px,7vw,88px);width:120px;height:80px;pointer-events:none}
.glyph path,.glyph line,.glyph polyline{fill:none;stroke:var(--line-2);stroke-width:1}
.glyph circle{fill:var(--white);stroke:var(--muted);stroke-width:1}
.glyph circle.acc{fill:var(--accent);stroke:none}
.glyph .dash{stroke:var(--accent);stroke-dasharray:4 6;animation:dash 3s linear infinite paused}
.glyph rect{fill:none;stroke:var(--accent)}
.cap:hover .glyph .dash{animation-play-state:running}
@keyframes dash{to{stroke-dashoffset:-40}}

/* ---------- Services index ---------- */
.svc{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:0 32px;border-top:1px solid var(--ink)}
.svc details{border-bottom:1px solid var(--line)}
.svc summary{list-style:none;display:flex;align-items:center;justify-content:flex-start;gap:10px;padding:18px 0 16px;cursor:pointer;font-weight:600;font-size:17px;letter-spacing:-.01em}
.svc summary::-webkit-details-marker{display:none}
/* le compte reste calé à droite, le bouton le suit */
.svc summary .mono{color:var(--faint);font-size:11px;margin-left:auto}
/* Rien ne disait que ces lignes s'ouvraient. Elles reçoivent le bouton de la
   FAQ — même cercle, même croix tracée en deux dégradés, même rotation de
   135° à l'ouverture — d'un cran plus petit, la ligne étant plus dense. */
.svc summary::after{content:"";flex:none;width:26px;height:26px;border-radius:50%;
  border:1px solid var(--line-2);background:
    linear-gradient(var(--ink),var(--ink)) center/9px 1.5px no-repeat,
    linear-gradient(var(--ink),var(--ink)) center/1.5px 9px no-repeat;
  transition:transform var(--t-med) var(--ease),border-color var(--t-fast),background-color var(--t-fast)}
.svc summary:hover::after{border-color:var(--accent);background-color:rgba(42,86,198,.06)}
.svc details[open] summary::after{transform:rotate(135deg);border-color:var(--accent)}
.svc ul{list-style:none;margin:0;padding:0 0 24px}
.svc li{padding:7px 0;color:var(--muted);font-size:15px;border-top:1px dashed var(--line);transition:color var(--t-fast),padding var(--t-med) var(--ease)}
.svc li:hover{color:var(--ink);padding-left:6px}
.svc li:first-child{border-top:0}

/* ---------- Why ---------- */
.pillars{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:34px 30px}
.pillar{position:relative;padding-top:22px}
.pillar::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;background:var(--ink);transform-origin:0 50%;transform:scaleX(var(--q,1));transition:transform .1s linear}
.pillar .mono{color:var(--accent-text);font-size:11px}
.pillar h3{font-size:23px;letter-spacing:-.006em;margin:16px 0 10px;line-height:1.24}
.pillar p{margin:0;color:var(--muted);font-size:16px}

/* ---------- Shenzhen (plane lands with scroll) ---------- */
.sz-grid{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:clamp(32px,5vw,80px);align-items:stretch}
.sz-vis-wrap{perspective:1400px}
.sz-vis{position:relative;height:100%;min-height:540px;border-radius:var(--r-lg);overflow:hidden;background:var(--navy-2);border:1px solid var(--navy-line);transform-origin:50% 100%;transform:rotateX(calc((1 - var(--q,1)) * 38deg)) scale(calc(.9 + var(--q,1) * .1));will-change:transform}
.sz-vis canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
.sz-vis .coord{position:absolute;left:18px;bottom:16px;color:var(--on-dark-muted);font-size:11px}
.sz-list{list-style:none;padding:0;margin:32px 0 0}
.sz-list li{display:grid;grid-template-columns:150px minmax(0,1fr);gap:18px;padding:15px 0;border-top:1px solid var(--navy-line);font-size:16px;color:var(--on-dark-muted)}
.sz-list li b{font-weight:560;color:var(--on-dark)}

/* ---------- China digital: each European habit hands over to its Chinese counterpart ---------- */
.swap{display:grid;grid-template-columns:minmax(0,1fr) clamp(86px,11vw,178px) minmax(0,1fr);align-items:stretch}
.swap-row,.swap-head{display:contents}
.swap-head span{padding:0 0 14px;font-family:var(--font-mono);font-size:13px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);align-self:end}
:lang(zh) .swap-head span{font-family:var(--font-zh);font-size:15px}
/* Les colonnes sont désignées explicitement. Le bloc du milieu ayant été
   retiré de l'en-tête, le placement automatique aurait fait glisser
   « The China playbook » dans la colonne centrale — celle des connecteurs. */
.swap-head span:first-child{grid-column:1}
.swap-head .hb{grid-column:3;color:var(--cn-text);padding-left:clamp(16px,2vw,28px)}
.swap .from,.swap .to{padding:18px 0;border-top:1px solid var(--line);display:flex;flex-direction:column;gap:3px;justify-content:center;transition:opacity .6s var(--ease),transform .6s var(--ease),color .6s var(--ease)}
.swap .from{font-size:clamp(16px,1.4vw,18px);color:var(--char)}
.swap .to{padding-left:clamp(16px,2vw,28px);position:relative}
.swap-row.on .to b{letter-spacing:-.005em}
.swap .to b{font-weight:600;font-size:clamp(16px,1.4vw,18px);letter-spacing:-.01em}
.swap .to span{color:var(--muted);font-size:14px}
.swap .to::before{content:"";position:absolute;left:0;top:14px;bottom:14px;width:2px;border-radius:2px;background:var(--cn);opacity:0;transform:scaleY(.2);transform-origin:50% 50%;transition:opacity .5s var(--ease),transform .6s var(--ease)}
.swap .mid{border-top:1px solid var(--line);display:flex;align-items:center;justify-content:center;position:relative}
.swap .mid i{display:block;width:100%;height:1px;background:linear-gradient(90deg,var(--accent),#D3D9E3 50%,var(--cn));transform:scaleX(0);transform-origin:0 50%;transition:transform .8s var(--ease)}
.swap .mid::after{content:"";position:absolute;left:0;top:50%;width:5px;height:5px;margin-top:-2.5px;border-radius:50%;background:var(--cn);opacity:0}
.js .swap .from{opacity:1}
/* .42 mesuré à 2,75:1 sur fond clair, pour du texte de 18 px : sous le
   seuil AA de 4,5:1, alors que cette colonne porte la moitié de la
   comparaison et doit se lire. À .60 le ratio passe à 4,85:1 et l'effet
   reste entier — la colonne de gauche demeure visiblement en retrait de
   celle de droite, qui est à pleine opacité. */
.js .swap-row.on .from{opacity:.62;transform:translateX(-5px)}
.js .swap-row.on .mid i{transform:scaleX(1)}
.js .swap-row.on .mid::after{animation:cross 1s var(--ease) forwards}
@keyframes cross{0%{opacity:1;left:0}70%{opacity:1}100%{opacity:0;left:100%}}
.js .swap .to{opacity:.32;transform:translateX(10px)}
.js .swap-row.on .to{opacity:1;transform:none}
.js .swap-row.on .to::before{opacity:1;transform:scaleY(1)}
.note{margin-top:22px;color:var(--faint);font-size:12px}
/* ---------- Sourcing ---------- */
.src-track{position:relative;display:grid;grid-template-columns:repeat(4,minmax(0,1fr))}
.src-rail{position:absolute;left:0;right:0;top:28px;height:1px;background:var(--line-2)}
.src-rail i{position:absolute;inset:0;background:linear-gradient(90deg,var(--accent),#D3D9E3 52%,var(--cn));transform-origin:0 50%;transform:scaleX(var(--q,1))}
.phase{position:relative;padding-right:24px}
.phase-name{display:flex;align-items:center;gap:10px;height:56px;font-family:var(--font-mono);font-size:12px;letter-spacing:.14em;color:var(--faint);transition:color var(--t-med)}
.phase-name::before{content:"";width:9px;height:9px;border-radius:50%;background:var(--paper);border:1px solid var(--line-2);flex:none;transition:all var(--t-med)}
.phase.on .phase-name{color:var(--ink)}
.phase.on .phase-name::before{border-color:var(--accent);background:var(--accent);box-shadow:0 0 0 5px rgba(42,86,198,.14)}
.phase h3{font-size:clamp(26px,2.6vw,36px);letter-spacing:-.008em;margin:8px 0 18px;transition:color var(--t-med);color:var(--faint-lg)}
.phase.on h3{color:var(--ink)}
.steps{list-style:none;margin:0;padding:0}
.steps li{display:flex;gap:12px;padding:11px 0;border-top:1px solid var(--line);font-size:15px;color:var(--muted);transition:color var(--t-med)}
.steps li i{font-style:normal;font-family:var(--font-mono);font-size:11px;color:var(--faint);padding-top:3px}
.phase.on .steps li{color:var(--ink)}
.phase.on .steps li i{color:var(--accent-text)}
.flow{display:flex;align-items:center;gap:14px;margin-top:clamp(40px,5vw,64px);padding:18px 22px;border:1px solid var(--line);border-radius:999px;background:var(--white);font-family:var(--font-mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);overflow:hidden}
:lang(zh) .flow{font-family:var(--font-zh);font-size:13.5px}
.flow b{color:var(--ink);font-weight:500;white-space:nowrap}
.flow i{flex:1;height:1px;min-width:16px;background:var(--line-2);position:relative;overflow:hidden}
.flow i::after{content:"";position:absolute;top:0;left:-30%;width:30%;height:1px;background:var(--accent);animation:runR 2.4s var(--ease) infinite}
.flow i:nth-of-type(2)::after{animation-delay:.6s}.flow i:nth-of-type(3)::after{animation-delay:1.2s}

/* ---------- Process ---------- */
.proc{position:relative;display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:24px}
.proc-rail{position:absolute;left:0;right:0;top:6px;height:1px;background:var(--line)}
.proc-rail i{position:absolute;inset:0;background:linear-gradient(90deg,var(--accent),#D3D9E3 52%,var(--cn));transform-origin:0 50%;transform:scaleX(var(--q,1))}
/* L'étincelle du rail se déplaçait par `left` (et par `top` sur téléphone),
   c'est-à-dire en changeant sa position de mise en page à chaque image du
   défilement. Un élément qui bouge ainsi compte dans le Cumulative Layout
   Shift, même absolu et même sans rien déplacer d'autre : c'était la seule
   source de décalage mesurable du site.

   Elle se déplace maintenant par `transform`, que le navigateur compose sans
   recalculer la mise en page et que la mesure du CLS ignore par construction.
   La boîte fait toute la longueur du rail et le point est dessiné sur l'un de
   ses bords, si bien qu'un déplacement de 100 % de cette boîte promène le
   point d'un bout à l'autre du rail.

   Le point est accroché au bord DROIT et la boîte va de −100 % à 0, plutôt
   que l'inverse : le mouvement à l'écran est le même, mais la boîte déborde
   désormais à gauche du rail et non à sa droite. Un débordement vers la
   gauche n'agrandit pas la zone défilable ; vers la droite, il ajoutait
   1 000 px de large à la page, invisibles grâce au `overflow-x` du corps,
   mais bien réels pour un navigateur qui décide de son zoom initial. */
.proc-rail .spark{position:absolute;inset:0;margin:0;border-radius:0;background:none;
  box-shadow:none;pointer-events:none;
  transform:translate3d(calc((var(--q,1) - 1) * 100%),0,0);will-change:transform}
.proc-rail .dot{position:absolute;left:auto;right:0;top:50%;width:8px;height:8px;margin:-4px -4px 0 0;
  border-radius:50%;background:var(--accent);
  box-shadow:0 0 0 4px rgba(42,86,198,.16),0 0 16px 2px rgba(42,86,198,.45)}
.proc-rail .dot::before{content:"";position:absolute;right:4px;top:50%;width:64px;height:2px;margin-top:-1px;border-radius:2px;background:linear-gradient(270deg,rgba(42,86,198,.75),rgba(42,86,198,0))}
.proc-rail .dot::after{content:"";position:absolute;inset:-3px;border-radius:50%;border:1px solid var(--accent);opacity:.5;animation:sparkPulse 2.4s var(--ease) infinite}
@keyframes sparkPulse{0%{transform:scale(.7);opacity:.6}100%{transform:scale(2.6);opacity:0}}
.stage{position:relative;padding-top:36px}
.stage::before{content:"";position:absolute;top:1px;left:0;width:11px;height:11px;border-radius:50%;background:var(--white);border:1px solid var(--line-2);transition:background .4s,border-color .4s,box-shadow .4s}
.stage.on::before{background:var(--accent);border-color:var(--accent);box-shadow:0 0 0 5px rgba(42,86,198,.14)}
.stage .mono{color:var(--faint);font-size:11px}
.stage h3{font-size:24px;letter-spacing:-.006em;margin:6px 0 8px;line-height:1.2}
.stage p{margin:0;color:var(--muted);font-size:15px}

/* ---------- Ventures / projects ---------- */
/* La tête de la section Ventures tenait dans une colonne : titre et texte
   l'un sous l'autre, la moitié droite vide. Elle emprunte maintenant la
   grille des autres têtes (.sh) — titre à gauche, texte à droite — et ne
   garde en propre que la suppression de sa marge basse, puisque les
   étiquettes la suivent de près. */
.vent{margin-bottom:0}
.tags{display:flex;flex-wrap:wrap;gap:8px;margin-top:22px}
.tags span{font-size:14px;padding:8px 13px;border:1px solid var(--line-2);border-radius:999px;color:var(--char)}
.proj-head{display:flex;justify-content:space-between;align-items:baseline;gap:16px;border-bottom:1px solid var(--ink);padding-bottom:14px}
.proj-head h3{font-size:23px;letter-spacing:-.01em}
.proj-head p{margin:0;color:var(--faint);font-size:14px}

/* ---------- FAQ ---------- */
.faq{display:grid;grid-template-columns:minmax(0,4fr) minmax(0,7fr);gap:clamp(32px,6vw,96px)}
.faq-list{border-top:1px solid var(--ink)}
.faq-list details{border-bottom:1px solid var(--line)}
.faq-list summary{list-style:none;cursor:pointer;display:flex;justify-content:space-between;gap:20px;padding:22px 0;min-height:44px;align-items:center;font-size:clamp(17px,1.5vw,20px);font-weight:560;letter-spacing:-.015em}
.faq-list summary::-webkit-details-marker{display:none}
/* Le « + » de 12 px ne se voyait pas : rien n'indiquait que la ligne
   s'ouvrait. Il devient un vrai bouton rond, avec un chevron qui pivote. */
.faq-list summary::after{content:"";flex:none;width:32px;height:32px;border-radius:50%;
  border:1px solid var(--line-2);background:
    linear-gradient(var(--ink),var(--ink)) center/11px 1.6px no-repeat,
    linear-gradient(var(--ink),var(--ink)) center/1.6px 11px no-repeat;
  transition:transform var(--t-med) var(--ease),border-color var(--t-fast),background-color var(--t-fast)}
.faq-list summary:hover::after{border-color:var(--accent);background-color:rgba(42,86,198,.06)}
.faq-list details[open] summary::after{transform:rotate(135deg);border-color:var(--accent)}
.dark .faq-list summary::after{border-color:var(--navy-line);background:
  linear-gradient(var(--on-dark),var(--on-dark)) center/11px 1.6px no-repeat,
  linear-gradient(var(--on-dark),var(--on-dark)) center/1.6px 11px no-repeat}
/* L'anneau du « + » et de la croix était bleu, comme les accents clairs du
   site. Sur la nuit, l'accent de la maison n'est pas le bleu mais le
   champagne des boutons : c'est la même arête, reprise au même millième. */
.dark .faq-list summary:hover::after{border-color:var(--btn-rim-sombre-h);background-color:rgba(233,212,177,.08)}
.dark .faq-list details[open] summary::after{border-color:var(--btn-rim-sombre)}
.faq-list p{margin:0 0 24px;color:var(--muted);max-width:54ch;font-size:16px}

/* ---------- Final + contact ---------- */
.final{padding-block:clamp(78px,9.4vw,134px) clamp(58px,6.6vw,92px);position:relative;overflow:hidden}
.final-h{max-width:14ch;font-size:clamp(40px,6vw,94px);line-height:1.04;letter-spacing:-.014em}
.final-h span{color:var(--nuit-sourdine);font-style:italic}
.final-p{margin:0;font-size:clamp(17px,1.6vw,21px);color:var(--on-dark-muted);line-height:1.55}
.final .ctas{margin-top:34px}
.final-sig{display:flex;align-items:center;gap:clamp(16px,2vw,26px);margin-top:26px}
.final-sig .seal-lg{width:clamp(52px,4.4vw,64px);height:clamp(52px,4.4vw,64px);flex:none;transform:rotate(-5deg)}
.final-p{margin:0}
.final-vis{position:absolute;right:0;top:0;width:min(74%,980px);height:min(78vh,860px);pointer-events:none;
  -webkit-mask-image:linear-gradient(90deg,transparent 0%,#000 34%),linear-gradient(180deg,#000 62%,transparent 98%);
  -webkit-mask-composite:source-in;mask-image:linear-gradient(90deg,transparent 0%,#000 34%),linear-gradient(180deg,#000 62%,transparent 98%);mask-composite:intersect}
.final-vis canvas{width:100%;height:100%;display:block}
/* Le bloc de contact était en deux colonnes : le formulaire à gauche, les
   coordonnées et les deux cartes WeChat dans une colonne étroite à droite.
   Sur un écran large, cette colonne de droite faisait 400 px pour 1150 —
   les deux cartes y étaient serrées, le QR à l'étroit, et l'ensemble
   pendait au bord du formulaire sans lui répondre.
   Une seule colonne, centrée : le formulaire d'abord, puis les cartes,
   l'une sous l'autre, à la même mesure. Ce qui se lit (le titre, le
   chapeau, le bouton, la phrase de fin) se centre ; ce qui se remplit
   (les champs, les trois étapes) reste calé à gauche — on ne centre pas
   un formulaire, on centre le bloc qui le contient. */
.contact{display:grid;grid-template-columns:minmax(0,1fr);justify-items:center;
  gap:clamp(34px,3.6vw,50px);margin-top:clamp(52px,6.6vw,92px);
  padding-top:clamp(40px,4.6vw,60px);border-top:1px solid var(--navy-line)}
.contact > *{width:100%;max-width:720px}
/* Sur /contact, le formulaire passe devant la déclaration : la séparation
   horizontale descend avec lui, entre le formulaire et la note de fin. */
.contact.first{margin-top:clamp(24px,2.6vw,40px);padding-top:0;border-top:0}
.contact.first + .final-h{margin-top:clamp(64px,7vw,104px);
  padding-top:clamp(40px,4.6vw,60px);border-top:1px solid var(--navy-line)}
.contact h3{font-size:clamp(28px,3vw,42px);letter-spacing:-.01em;line-height:1.12;
  max-width:20ch;margin-inline:auto;text-align:center;text-wrap:balance}
.contact .sub{margin:14px auto 0;color:var(--on-dark-muted);font-size:17px;
  max-width:48ch;text-align:center;text-wrap:balance}
.choose{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:32px}
.choose input{position:absolute;opacity:0;pointer-events:none}
.choose label{display:flex;flex-direction:column;gap:6px;padding:18px 20px;border:1px solid var(--navy-line);border-radius:var(--r-md);cursor:pointer;transition:border-color var(--t-fast),background var(--t-fast),transform .1s ease-out}
.choose label:active{transform:scale(.985)}
.choose label .mono{color:var(--on-dark-muted);font-size:11px}
:lang(zh) .choose label .mono{font-family:var(--font-zh);font-size:12.5px}
.choose label b{font-weight:580;font-size:17px}
/* Chaque direction porte SA couleur quand on la choisit. Les deux teintes
   ne sont pas décoratives sur ce site : le bleu est l'Europe, le rouge du
   sceau est la Chine, et c'est la seule paire réservée aux deux sens. Le
   sélectionné disait « bleu » dans les deux cas, ce qui effaçait la moitié
   de l'information au moment précis où le visiteur la donne.
   L'effet ne change pas : même filet, même voile, même épaisseur. */
.choose input:checked+label{border-color:var(--accent-dark);background:rgba(143,176,255,.06)}
.choose input:checked+label .mono{color:var(--accent-dark)}
.choose input:focus-visible+label{outline:2px solid var(--accent-dark);outline-offset:2px}
.choose input[value="cn"]:checked+label{border-color:var(--cn-dark);background:rgba(240,131,117,.06)}
.choose input[value="cn"]:checked+label .mono{color:var(--cn-dark)}
.choose input[value="cn"]:focus-visible+label{outline-color:var(--cn-dark)}
/* Le formulaire prend la couleur de la direction choisie. Sur ce site le bleu
   est l'Europe et le rouge du sceau est la Chine : la règle valait déjà pour
   la carte cochée, mais le reste du formulaire restait bleu — le filet du
   champ actif, son voile, la ligne d'état. Dès que « I'm in China » est
   coché, tout cela passe au rouge ; « I'm in Europe » laisse tout en bleu,
   exactement comme aujourd'hui. Deux variables portent le basculement : les
   règles plus bas ne sont pas dupliquées. */
#form{--dir:var(--accent-dark);--dir-voile:rgba(143,176,255,.04)}
#form:has(.choose input[value="cn"]:checked){--dir:var(--cn-dark);--dir-voile:rgba(240,131,117,.05)}
.form{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}
.field{display:flex;flex-direction:column;gap:6px}
.field.full{grid-column:1/-1}
.field label{font-size:12px;font-family:var(--font-mono);letter-spacing:.1em;text-transform:uppercase;color:var(--on-dark-muted)}
:lang(zh) .field label{font-family:var(--font-zh);font-size:13px}
.field input,.field textarea,.field select{font:inherit;font-size:16px;color:var(--on-dark);background:rgba(255,255,255,.03);border:1px solid var(--navy-line);border-radius:var(--r-sm);padding:13px 14px;min-height:48px;width:100%;transition:border-color var(--t-fast),background var(--t-fast)}
.field textarea{min-height:120px;resize:vertical}
.field input:focus,.field textarea:focus,.field select:focus{outline:none;border-color:var(--dir,var(--accent-dark));background:var(--dir-voile,rgba(143,176,255,.04))}
/* Phase 8. La ligne ci-dessus coupait l'anneau de focus global pour tous
   les champs : a la souris c'etait le but — un clic ne doit pas dessiner
   un cadre — mais au clavier il ne restait qu'une bordure d'un pixel a
   41 % d'opacite. Sur un formulaire de cinq champs, on perd sa place.
   :focus-visible ne se declenche qu'au clavier : le clic reste net, la
   tabulation redevient lisible. (WCAG 2.4.7 et 2.4.11.) */
.field input:focus-visible,.field textarea:focus-visible,.field select:focus-visible{outline:2px solid var(--dir,var(--accent-dark));outline-offset:2px}
#form :focus-visible{outline-color:var(--dir,var(--accent-dark))}
/* Le champ facultatif : même boîte que les autres, avec le chevron dessiné
   plutôt que celui du système, qui ne suit pas le thème sombre. */
.field select{-webkit-appearance:none;appearance:none;cursor:pointer;
  padding-right:40px;color:var(--on-dark-muted);
  background-image:linear-gradient(45deg,transparent 50%,var(--on-dark-muted) 50%),
    linear-gradient(135deg,var(--on-dark-muted) 50%,transparent 50%);
  background-position:calc(100% - 20px) 21px,calc(100% - 14px) 21px;
  background-size:6px 6px,6px 6px;background-repeat:no-repeat}
.field select:valid,.field select:not([value=""]){color:var(--on-dark)}
.field select option{background:var(--navy-2);color:var(--on-dark)}
/* la mention « facultatif », dans le même registre que l'étiquette */
.field label i{font-style:normal;text-transform:none;letter-spacing:.02em;
  color:var(--nuit-mut);font-size:11px}
/* Une ligne sous le bouton, pas un argument : elle dit ce qui se passe
   ensuite, à l'endroit exact où la question se pose. */
.form-rea{margin:14px 0 0;font-size:13px;line-height:1.55;color:var(--on-dark-muted);max-width:46ch}
:lang(zh) .form-rea{font-family:var(--font-zh);line-height:1.8}

/* Les trois étapes qui suivent l'envoi. Elles se lisent comme la suite de la
   phrase de réassurance, pas comme un nouveau bloc : même colonne, même
   famille, un cran plus bas en couleur. Le numéro est en champagne — c'est le
   seul endroit du formulaire où la couleur d'identité revient, et elle y
   marque une progression. */
.next-steps{grid-column:1/-1;list-style:none;counter-reset:nx;margin:20px auto 0;padding:0;
  max-width:52ch;display:grid;gap:14px}
.next-steps li{counter-increment:nx;position:relative;padding-left:30px;
  font-size:13px;line-height:1.55;color:var(--on-dark-muted)}
.next-steps li::before{content:counter(nx,decimal-leading-zero);position:absolute;
  left:0;top:1px;font-family:var(--font-mono);font-size:10.5px;letter-spacing:.06em;
  color:var(--champ);opacity:.85}
.next-steps b{display:block;font-weight:560;color:var(--on-dark);margin-bottom:2px}
:lang(zh) .next-steps{font-family:var(--font-zh)}
:lang(zh) .next-steps li{line-height:1.8}
@media (max-width:700px){.next-steps{gap:12px;margin-top:16px}}
.field input[aria-invalid="true"]{border-color:#FF8A7A}
/* Bouton et mention côte à côte, le couple centré : le bouton se retrouvait
   à gauche du milieu et rien n'était aligné sur rien. Ils passent l'un sous
   l'autre, chacun centré — c'est le bouton qui doit tomber au milieu. */
.form-foot{grid-column:1/-1;display:flex;flex-direction:column;align-items:center;
  gap:12px;margin-top:10px;text-align:center}
.form-foot small{color:var(--on-dark-muted);font-size:13px}
.status{grid-column:1/-1;color:var(--dir,var(--accent-dark));font-size:14px;margin:0}
.status.err{color:#FF9D8F}
/* Les coordonnées et les deux cartes passent sous le formulaire, à la même
   mesure que lui. Le bloc des deux cartes se resserre un peu : à 720 px de
   large, le QR se retrouvait seul à droite d'une grande plage vide. */
.aside{display:flex;flex-direction:column;gap:14px}
.copy-wrap{display:inline-flex;align-items:center;gap:8px;min-width:0}
.copy-wrap a{word-break:break-all;padding-block:9px}
.copy{position:relative;width:40px;height:40px;flex:none;border:1px solid rgba(233,238,244,.14);border-radius:10px;background:linear-gradient(180deg,rgba(255,255,255,.08),rgba(255,255,255,.02));-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:inset 0 1px 0 rgba(255,255,255,.1);display:grid;place-items:center;cursor:pointer;color:var(--on-dark-muted);transition:color var(--t-fast),border-color var(--t-fast),transform .12s ease-out}
.copy:hover{color:var(--on-dark);border-color:rgba(233,238,244,.4)}
.copy:active{transform:scale(.94)}
.copy svg{position:absolute;width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;transition:opacity .2s var(--ease),transform .3s var(--ease)}
.copy .i-k{opacity:0;transform:scale(.6)}
.copy.ok{color:var(--accent-dark);border-color:var(--accent-dark)}
.copy.ok .i-c{opacity:0;transform:scale(.6)}
.copy.ok .i-k{opacity:1;transform:none}
.card-line{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:16px 0;border-bottom:1px solid var(--navy-line);font-size:15px;flex-wrap:wrap;width:100%;max-width:600px;margin-inline:auto}
.card-line .mono{color:var(--on-dark-muted);font-size:11px}
.card-line a{border-bottom:1px solid rgba(233,238,244,.3)}
.people{display:grid;gap:12px;margin-top:12px;width:100%;max-width:600px;margin-inline:auto}
.person{position:relative;border:1px solid var(--navy-line);border-radius:var(--r-lg);padding:18px 20px 20px;background:linear-gradient(160deg,rgba(143,176,255,.05),transparent 62%);box-shadow:inset 0 1px 0 rgba(255,255,255,.05);transition:border-color var(--t-med),transform .5s var(--ease),box-shadow var(--t-med)}
.person:nth-child(2){background:linear-gradient(160deg,rgba(227,89,75,.08),transparent 62%)}
.person:hover{transform:translateY(-2px);border-color:rgba(143,176,255,.32);box-shadow:inset 0 1px 0 rgba(255,255,255,.07),0 18px 40px -28px rgba(0,0,0,.8)}
.person:nth-child(2):hover{border-color:rgba(227,89,75,.42)}
.people.flash .person{box-shadow:0 0 0 1px var(--accent-dark)}
.p-dir{display:block;font-size:11px;color:var(--accent-dark);letter-spacing:.12em}
.person:nth-child(2) .p-dir{color:var(--cn-dark)}
:lang(zh) .p-dir{font-family:var(--font-zh);font-size:12px}
.p-main{display:grid;grid-template-columns:minmax(0,1fr) 120px;gap:16px;align-items:center;margin-top:12px}
@media (max-width:420px){.p-main{grid-template-columns:minmax(0,1fr) 104px;gap:12px}.qr{width:104px;height:104px}}
.person h4,.person h3{margin:0 0 3px;font-size:22px;letter-spacing:-.006em;font-weight:400;display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.zh-name{font-family:var(--font-zh);font-size:16px;font-weight:500;letter-spacing:.06em;color:var(--accent-dark)}
.person:nth-child(2) .zh-name{color:var(--cn-dark)}
.p-role{margin:0;color:var(--on-dark);font-size:14px;line-height:1.45;font-weight:500}
/* Les spécialités : un cran plus bas que le rôle, séparées par le même point
   médian que les cartouches des photographies et la ligne du bandeau. Elles
   passent à la ligne d'elles-mêmes sur un téléphone — d'où flex-wrap et le
   séparateur en élément propre plutôt qu'un caractère dans le texte, qui
   serait resté seul en début de ligne. */
/* Quatre compétences, donc DEUX PAR LIGNE — une grille, pas un flux qui se
   replie où il peut. Le flux donnait « Lecture du marché  Stratégie » puis
   « Digital  Développement » sur un écran et trois-et-un sur un autre :
   la même carte ne se lisait pas pareil deux fois. La grille fixe le
   rythme, et les quatre entrées s'alignent sur deux colonnes propres.
   La première colonne est un peu plus large que la seconde : les intitulés
   longs (« Lecture du marché », « Réseau local ») sont toujours à gauche,
   les courts à droite, et l'ensemble reste calé sur le texte au-dessus. */
.p-skills{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(0,1fr);
  align-items:baseline;gap:5px 12px;
  margin:8px 0 0;font-size:12.5px;line-height:1.45;color:var(--nuit-sourdine)}
.p-skills span{display:block}
/* Les puces médianes sont retirées du texte. Elles étaient des éléments
   propres justement pour ne pas rester seules en début de ligne — mais
   dans une boîte flexible chaque élément est un article à part entière,
   donc le retour à la ligne pouvait tomber JUSTE AVANT l'une d'elles, et
   c'est exactement ce qui se passait sur un téléphone : un point isolé
   ouvrant la ligne. La règle ne pouvait pas l'empêcher ; seul le retrait
   le pouvait. Seul le retrait le pouvait.

   Chaque compétence est donc devenue un élément à part entière, et c'est
   l'espacement de la grille qui les sépare — une séparation qui ne peut
   pas, elle, se retrouver seule en début de ligne. Masquer simplement les
   puces ne suffisait pas : deux textes voisins sans élément entre eux se
   fondent en un seul article de la boîte flexible, et l'espacement ne
   s'applique plus — les mots se collaient. Vérifié à l'image. */
.p-skills i{display:none}
:lang(zh) .p-skills{font-family:var(--font-zh);font-size:13px;line-height:1.7}
/* Ces deux colonnes étaient proportionnelles : réglées sur la largeur d'un
   téléphone, elles s'écartaient d'autant que la carte s'élargissait, jusqu'à
   laisser deux cents pixels de vide entre « Market understanding » et
   « Strategy ». Au-delà du téléphone, chaque colonne ne prend donc que la
   largeur de son mot le plus long, et les deux se rangent à gauche. */
@media (min-width:901px){
  .p-skills{grid-template-columns:repeat(2,minmax(0,max-content));
    justify-content:start;column-gap:clamp(16px,1.6vw,26px)}
}
/* La conclusion de la section : elle n'appartient à aucune des deux cartes,
   donc elle ne porte ni bordure ni fond — seulement le filet qui la rattache
   aux deux, et le corps d'une légende. */
/* La phrase qui referme les deux cartes WeChat passe au champagne — la même
   teinte que les boutons — et se centre sous elles. Elle n'est plus une note
   en bas de bloc mais la ligne qui les réunit, donc le filet se réduit à un
   court trait centré et la mesure se resserre pour que les deux lignes
   s'équilibrent. */
.people-note{margin:20px auto 0;padding-top:18px;position:relative;
  color:var(--champ);font-size:13.5px;line-height:1.7;max-width:40ch;
  font-style:italic;text-align:center;text-wrap:balance}
.people-note::before{content:"";position:absolute;top:0;left:50%;
  transform:translateX(-50%);width:56px;height:1px;background:var(--champ-line)}
:lang(zh) .people-note{font-family:var(--font-zh);font-style:normal;
  font-size:13.5px;line-height:1.85}
/* « 扫码添加微信 » — « scanne le code pour ajouter sur WeChat ». La ligne
   était écrite en dur dans le balisage, donc servie telle quelle dans les
   trois langues : un lecteur français ou anglais y voyait quatre caractères
   qu'il ne peut pas lire, juste sous le QR code qui, lui, se comprend sans
   un mot. Elle ne reste que là où elle informe : l'arbre chinois.
   Le nom chinois de chacun (罗曼, 萱萱) reste partout — c'est un nom, pas
   une consigne, et il dit quelque chose de la maison. */
.person .zh-line{margin-top:8px;font-size:12.5px;color:var(--on-dark-muted)}
html:not([lang|="zh"]) .person .zh-line{display:none}
.qr{width:120px;height:120px;border-radius:12px;background:var(--white);padding:7px;display:grid;place-items:center;transition:transform var(--t-med),box-shadow var(--t-med)}
.qr img{width:100%;height:100%;display:block;border-radius:5px}
.qr:hover,.qr:focus-visible{transform:scale(1.04);box-shadow:0 14px 34px -18px rgba(0,0,0,.85)}
@media (prefers-reduced-motion:reduce){.qr{transition:none}.qr:hover{transform:none}}
/* ---------- Footer ---------- */
.ft{background:#04090F;color:var(--on-dark-muted);padding-block:56px 40px;font-size:14px}
.ft-top{display:flex;justify-content:space-between;gap:40px;flex-wrap:wrap;padding-bottom:40px;border-bottom:1px solid var(--navy-line)}
.ft .logo{color:var(--on-dark)}
.ft .logo svg{color:var(--on-dark)}
.ft .logo b{color:var(--on-dark-muted)}
.ft-tag{margin:12px 0 0;font-family:var(--font-mono);font-size:11.5px;letter-spacing:.1em;text-transform:uppercase}
.ft-legal{display:flex;align-items:center;gap:14px;margin:18px 0 0;color:var(--on-dark);font-size:16px}
.ft-legal .zh-line{letter-spacing:.06em}
.ft-bot .zh-line{color:var(--on-dark-muted)}
:lang(zh) .ft-tag{font-family:var(--font-zh);font-size:13px}
.ft-links a,.ft-bot nav a{padding-block:9px}
.ft-links{display:flex;flex-wrap:wrap;gap:4px 28px;align-content:flex-start;max-width:520px}
.ft-links a:hover,.ft-bot a:hover{color:var(--on-dark)}
.ft-bot{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;padding-top:24px;font-size:13px}
.ft-bot nav{display:flex;gap:22px;flex-wrap:wrap}

/* ---------- reveal: resting state visible; below-fold blocks get one gentle arrival ---------- */
.js .rv{transition:transform 1s var(--ease),opacity 1s var(--ease),filter 1s var(--ease)}
.js .rv.pre{transform:translateY(24px);opacity:.25;filter:blur(4px)}

/* ============================ RESPONSIVE ============================ */
@media (max-width:1279px){
  .nav{display:none}
  .menu-btn{display:block}
  .hd-r{margin-left:auto}
  .hd-r .btn{display:none}
  .route-tick i{display:none}
  .drawer{display:block}
  .hd.open~.drawer{transform:none;visibility:visible;transition:transform .5s var(--ease)}
  .svc{grid-template-columns:repeat(2,minmax(0,1fr))}
  .pillars{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* la barre de progression descend à la verticale dès 1100 px : en
     trois colonnes elle n'avait plus de tracé possible et était masquée,
     la section perdait sa colonne vertébrale entre 621 et 1100 px */
  .proc{grid-template-columns:1fr;row-gap:0;position:relative}
  .proc-rail{display:block;left:5px;right:auto;top:6px;bottom:22px;width:1px;height:auto}
  .proc-rail i{background:linear-gradient(180deg,var(--accent),#D3D9E3 52%,var(--cn));transform-origin:50% 0;transform:scaleY(var(--q,1))}
  /* rail vertical : même dispositif, sur l'autre axe — le point passe au
     bord bas de la boîte, qui remonte de −100 % à 0, et le débordement
     part vers le haut, où il n'allonge pas non plus la page */
  .proc-rail .spark{transform:translate3d(0,calc((var(--q,1) - 1) * 100%),0)}
  .proc-rail .dot{left:50%;right:auto;top:auto;bottom:0;margin:0 0 -4px -4px}
  .proc-rail .dot::before{right:auto;top:auto;bottom:4px;left:50%;margin:0 0 0 -1px;width:2px;height:56px;background:linear-gradient(0deg,rgba(42,86,198,.75),rgba(42,86,198,0))}
  .stage{padding:0 0 28px 28px;margin-left:5px}
  .stage::before{top:0;left:-6px}
  .stage h3{margin-top:-4px}
  .sz-list li{grid-template-columns:130px minmax(0,1fr)}
}
@media (max-width:900px){
  .hero-copy{max-width:100%}
  .hero-caps{max-width:100%;min-height:96px}
  .hero-scrim{background:
    linear-gradient(180deg,rgba(5,7,12,.88) 4%,rgba(5,7,12,.52) 22%,rgba(5,7,12,0) 42%),
    linear-gradient(0deg,rgba(6,9,16,.95) 14%,rgba(6,9,16,.80) 26%,rgba(6,9,16,.44) 38%,rgba(6,9,16,0) 56%)}
  .hero-lead{display:none}
  .hero-foot{flex-direction:column;align-items:stretch;gap:10px;min-width:0}
  .hero-foot>*{min-width:0;max-width:100%}
  .stops{width:100%;flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
  .stops::-webkit-scrollbar{display:none}
  .hero-foot .clocks{justify-content:space-between;font-size:11px}
  .sh{grid-template-columns:1fr}
  .paths{grid-template-columns:1fr}
  /* Le pivot bascule à la verticale entre les deux cartes empilées. Ce
     n'est plus une rotation du dessin mais un changement de sens de la
     colonne : chaque pièce reste nette, et les pointes se redessinent au
     lieu d'être tournées. Le corail monte vers l'Europe, le bleu descend
     vers la Chine — le même sens qu'à l'horizontale. */
  /* Les cartes sont empilées : l'écart n'a plus à contenir quoi que ce
     soit, il n'a qu'à les séparer. 104 px laissaient un trou. */
  .bridge-mid{height:28px}
  .cap-core{grid-template-columns:1fr}
  .caps{grid-template-columns:1fr}
  .sz-grid{grid-template-columns:1fr}
  .sz-vis{min-height:380px}
  .sz-vis-wrap{order:-1}
  .vtext{display:none}
  .swap{grid-template-columns:1fr}
  .swap-head span{display:none}
  .swap .from{padding:16px 0 8px}
  .swap .to{padding:8px 0 20px 16px;border-top:0}
  .swap .mid{border-top:0;height:22px;justify-content:flex-start;padding-left:2px}
  .swap .mid i{width:1px;height:100%;background:linear-gradient(180deg,var(--accent),#D3D9E3 50%,var(--cn));transform:scaleY(0);transform-origin:50% 0;transition:transform .7s var(--ease)}
  .js .swap-row.on .mid i{transform:scaleY(1)}
  .swap .mid::after{left:-2px;top:0;animation:none!important}
  .js .swap .to{transform:translateX(6px)}
  .src-track{grid-template-columns:1fr 1fr;row-gap:36px}
  .src-rail{display:none}
  .phase h3,.phase .steps li{color:var(--ink)}
  .faq{grid-template-columns:1fr}
  .final-vis{width:100%;height:48vh;opacity:.62;-webkit-mask-image:linear-gradient(180deg,#000 58%,transparent 96%);mask-image:linear-gradient(180deg,#000 58%,transparent 96%)}
}
@media (max-width:400px){
  .hd-in{gap:10px}
  .logo{font-size:15.5px;gap:8px}
  .eyebrow{gap:8px}
  .eyebrow::before{width:26px;height:26px;margin-block:-7px}
  .logo svg{width:26px;height:26px}
  .lang button{min-width:38px;padding:0 6px;font-size:11px}
  .lang button.zhb{font-size:12px}
  .menu-btn{width:40px;margin-right:-8px}
}
@media (max-width:359px){
  /* the language switcher is the only one left, so the wordmark yields instead */
  .logo span{display:none}
  .logo{gap:0}
  .lang button{min-width:28px;padding:0 4px;font-size:10.5px}
  .lang button.zhb{font-size:11.5px}
  .hero-cap p{font-size:13px}
  .stops button{padding:0 10px;font-size:10.5px}
}
@media (max-width:620px){
  body{font-size:16px}
  .lang button{padding:5px 6px;min-width:30px}
  .ctas .btn{flex:1 1 100%}
  .svc{grid-template-columns:1fr}
  .pillars{grid-template-columns:1fr;gap:28px}
  .src-track{grid-template-columns:1fr}
  .flow{border-radius:var(--r-md);flex-wrap:wrap;gap:10px}
  .flow i{min-width:24px}
  .cap-core ul{grid-template-columns:1fr}
  .glyph{display:none}
  .sz-list li{grid-template-columns:1fr;gap:4px}
  .choose,.form{grid-template-columns:1fr}
  .p-main{grid-template-columns:minmax(0,1fr) 88px}
  .qr{width:88px;height:88px}
  .clocks{gap:10px;font-size:11px}
  .proj-head{flex-direction:column;gap:4px}
  .stops button{padding:0 12px;min-height:44px;font-size:11px}
  .lang button{min-height:44px;min-width:46px}
  .h1,:lang(zh) .h1{font-size:clamp(30px,8.4vw,46px);line-height:1.1}
  :lang(zh) .h1{line-height:1.16}
  .eyebrow{margin-bottom:12px}
  .hero-inner{gap:8px}
  .ctas{margin-top:14px;gap:8px}
  .final-sig{gap:14px}
  .final-sig .seal-lg{width:46px;height:46px}
  .ctas .btn{min-height:44px;font-size:14px}
  .hero-cap h2{font-size:19px;margin:6px 0 6px}
  .hero-cap p{font-size:14px;line-height:1.4}
  .clocks{font-size:10px;gap:7px}
  .clocks .sep{min-width:14px}
  .js .hero{height:300vh}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001s!important;animation-iteration-count:1!important;transition-duration:.001s!important}
  .sz-vis{transform:none}
  .js .rv.pre{transform:none;opacity:1;filter:none}
  .js .hero-cap{filter:none;transform:none}
  /* Le tableau « playbook européen / playbook chinois » arrive à .32
     d'opacité et n'est ramené à 1 que par la classe .on, posée par
     l'animation liée au défilement — laquelle ne tourne pas quand le
     visiteur demande moins de mouvement. Sa colonne de droite restait
     donc définitivement à 32 % : mesuré à 1,4:1 de contraste, c'est-à-dire
     illisible, et c'était le seul vrai échec WCAG du site. Les trois
     blocs voisins (.rv, .seal-lg, .hero-cap) avaient déjà leur état
     d'arrivée ici ; celui-ci manquait. */
  .js .swap .to{opacity:1;transform:none}
  .js .swap .to::before{opacity:1;transform:scaleY(1)}
  .js .swap .mid i{transform:scaleX(1)}
}

/* ============================================================
   Pages intérieures — fil d'Ariane, en-tête de page, prose
   ============================================================ */
.crumb{padding-block:calc(var(--hd) * .55) 0}
.crumb ol{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-family:var(--font-mono);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
:lang(zh) .crumb ol{font-family:var(--font-zh);font-size:12.5px;letter-spacing:.06em}
.crumb a{color:var(--faint);border-bottom:1px solid transparent;padding:6px 0;transition:color var(--t-fast),border-color var(--t-fast)}
.crumb a:hover{color:var(--ink);border-color:var(--line-2)}
.crumb [aria-current]{color:var(--ink)}
.crumb-sep{color:var(--faint-lg)}

.phead{padding:clamp(34px,5vw,66px) 0 clamp(26px,3.4vw,44px);position:relative;overflow:hidden}
.phead .h1{max-width:19ch}
.phead .lead{margin-top:20px;max-width:56ch}
.phead .ctas{margin-top:clamp(24px,3vw,36px)}

/* La mesure de lecture. `68ch` semblait dire « 68 caractères » : l'unité ch
   est la largeur du chiffre zéro, nettement plus large que la lettre moyenne
   d'une fonte proportionnelle. Mesuré sur la page rendue, ces 68ch donnaient
   93 caractères par ligne — bien au-delà des 65 à 75 où l'œil retrouve encore
   le début de la ligne suivante sans effort.
   Le conteneur garde sa largeur, parce qu'un tableau comparatif a besoin de
   place ; ce sont les paragraphes et les listes qui se resserrent. */
.prose{max-width:68ch}
.prose p,.prose li{max-width:52ch}
.prose h2.ph2{font-family:var(--font-display);font-weight:400;font-size:clamp(23px,2.3vw,31px);line-height:1.2;letter-spacing:-.008em;margin:clamp(30px,3.4vw,46px) 0 12px}
.prose h2.ph2:first-child{margin-top:0}
.prose p{margin:0 0 15px;color:var(--muted)}
.prose p strong{color:var(--ink);font-weight:580}
.prose ul,.prose ol{margin:0 0 18px;padding-left:0;list-style:none}
.prose ol{counter-reset:pr}
.prose li{position:relative;padding:9px 0 9px 26px;border-top:1px solid var(--line);color:var(--muted)}
.prose li:first-child{border-top:0}
.prose ul li::before{content:"";position:absolute;left:4px;top:17px;width:5px;height:5px;border-radius:50%;background:var(--accent)}
.prose ol li{counter-increment:pr}
.prose ol li::before{content:counter(pr,decimal-leading-zero);position:absolute;left:0;top:9px;font-family:var(--font-mono);font-size:11px;color:var(--accent-text)}
.prose li strong{color:var(--ink);font-weight:580}
/* ---- tableau de comparaison ------------------------------------------
   Le site n'avait aucun tableau : les articles comparatifs — société de
   négoce ou usine, KOL ou KOC, quelle structure juridique — étaient écrits
   en prose, où l'on ne peut pas lire deux colonnes côte à côte.
   Le tableau reprend exactement l'idiome des listes de la prose : un filet
   d'un pixel entre les lignes, pas de cadre, pas de fond alterné. La seule
   nouveauté est l'en-tête, en mono comme les autres étiquettes du site.
   Sur un écran étroit, c'est le tableau qui défile, jamais la page. */
/* Sur mobile, la troisième colonne sort du cadre. Sans indice, on ne sait
   pas qu'elle existe : une ombre apparaît au bord tant qu'il reste quelque
   chose à voir de ce côté, et disparaît quand on l'a atteint. Les deux
   dégradés opaques sont ancrés au contenu et masquent l'ombre aux extrémités
   (technique de Lea Verou) ; --surface est la couleur de la section. */
.sec{--surface:var(--paper)}
.white{--surface:var(--white)}
.prose .ptw:focus-visible{outline:2px solid var(--accent,#9a2b25);outline-offset:3px;border-radius:4px}
.prose .ptw{margin:0 0 20px;overflow-x:auto;overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;
  background:
    linear-gradient(to right, var(--surface) 42%, transparent) left center,
    linear-gradient(to left,  var(--surface) 42%, transparent) right center,
    radial-gradient(farthest-side at 0 50%, rgba(19,19,15,.13), transparent) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(19,19,15,.13), transparent) right center;
  background-repeat:no-repeat;
  background-size:38px 100%, 38px 100%, 13px 100%, 13px 100%;
  background-attachment:local, local, scroll, scroll}
.dark .prose .ptw{background-image:
    linear-gradient(to right, var(--navy) 42%, transparent),
    linear-gradient(to left,  var(--navy) 42%, transparent),
    radial-gradient(farthest-side at 0 50%, rgba(0,0,0,.34), transparent),
    radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.34), transparent)}
.prose table{width:100%;border-collapse:collapse;font-size:15px;
  font-variant-numeric:tabular-nums}
.prose th{font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint);font-weight:500;text-align:left;
  padding:0 20px 10px 0;border-bottom:1px solid var(--line-2);white-space:nowrap}
.prose td{padding:11px 20px 11px 0;border-top:1px solid var(--line);
  color:var(--muted);vertical-align:top}
.prose tbody tr:first-child td{border-top:0}
.prose td:first-child{color:var(--ink);font-weight:520}
.prose th:last-child,.prose td:last-child{padding-right:0}
.prose td strong{color:var(--ink);font-weight:580}
.dark .prose th{color:var(--on-dark-muted);border-bottom-color:var(--navy-line)}
.dark .prose td{color:var(--on-dark-muted);border-top-color:var(--navy-line)}
.dark .prose td:first-child,.dark .prose td strong{color:var(--on-dark)}
/* la police mono ne porte pas les idéogrammes */
:lang(zh) .prose th{font-family:var(--font-zh);font-size:12.5px;
  letter-spacing:.04em;text-transform:none}
@media (max-width:620px){
  .prose table{min-width:30rem}
  .prose th,.prose td{padding-right:16px}
}
/* Premier lien sortant du site : il doit se voir. La règle de base du site
   est a{color:inherit;text-decoration:none} — excellente pour la navigation,
   mais elle rend invisible une source qu'on veut justement pouvoir suivre. */
.prose a[href^="http"]{color:var(--accent-text);text-decoration:underline;
  text-decoration-thickness:1px;text-underline-offset:2.5px;
  text-decoration-color:rgba(36,80,191,.32);
  transition:text-decoration-color var(--t-fast)}
.prose a[href^="http"]:hover{text-decoration-color:currentColor}
.dark .prose a[href^="http"]{color:var(--accent-dark);
  text-decoration-color:rgba(143,176,255,.38)}
.dark .prose p,.dark .prose li{color:var(--on-dark-muted)}
.dark .prose p strong,.dark .prose li strong{color:var(--on-dark)}
.dark .prose li{border-color:var(--navy-line)}
.dark .prose ul li::before{background:var(--accent-dark)}

.two{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(28px,4vw,72px);align-items:start}
.aside-note{border-left:2px solid var(--accent);padding:2px 0 2px 18px;margin:26px 0;color:var(--muted);font-size:16px}
.aside-note.cn{border-color:var(--cn)}

/* Le bloc des deux cartes « pour aller plus loin ». Il portait une marge
   en haut et AUCUNE en bas. Quand il est suivi du bandeau de rappel
   (.cta-band, 353 pages) tout va bien : celui-ci a sa propre marge haute.
   Mais sur les 15 pages où il est suivi d'un titre de section, l'écart
   tombait à ZÉRO — mesuré — et « Questions qui reviennent » venait toucher
   la carte du dessus. La marge est donnée au titre qui suit, pas au bloc
   lui-même : le cas du bandeau, qui était correct, ne bouge pas. */
.rel{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:12px;margin-top:22px}
.rel + .sh{margin-top:clamp(46px,6vw,82px)}
.rel a{display:flex;flex-direction:column;gap:6px;padding:18px 20px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--white);transition:border-color var(--t-fast),transform var(--t-fast),box-shadow var(--t-med)}
.rel a:hover{border-color:rgba(10,13,18,.28);transform:translateY(-2px);box-shadow:0 18px 36px -30px rgba(10,30,60,.5)}
.rel .mono{font-size:11px;color:var(--faint)}
.rel b{font-weight:560;font-size:17px;letter-spacing:-.004em}
.rel span.d{color:var(--muted);font-size:14px;line-height:1.4}
.dark .rel a{background:rgba(255,255,255,.03);border-color:var(--navy-line)}
.dark .rel a:hover{border-color:rgba(233,238,244,.4)}
.dark .rel b{color:var(--on-dark)}
.dark .rel span.d,.dark .rel .mono{color:var(--on-dark-muted)}

.cta-band{border-top:1px solid var(--line);margin-top:clamp(40px,5vw,72px);padding-top:clamp(28px,3.4vw,46px);display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:20px}
.cta-band p{margin:0;max-width:44ch;color:var(--muted)}
.dark .cta-band{border-color:var(--navy-line)}
.dark .cta-band p{color:var(--on-dark-muted)}

@media (max-width:860px){
  .two{grid-template-columns:1fr;gap:26px}
}
@media (max-width:620px){
  .crumb ol{font-size:11px;gap:6px}
  .prose{max-width:none}
}

/* lien d'évitement : invisible jusqu'à la tabulation */
.skip{position:absolute;left:-9999px;top:0;z-index:60;background:var(--ink);color:var(--white);
  padding:12px 18px;border-radius:0 0 var(--r-md) 0;font-size:14px;font-weight:560;text-decoration:none}
.skip:focus{left:0}

/* l'en-tête de page et la section qui suit ne doivent pas cumuler leurs marges */
.phead + .sec{padding-top:0}
.phead + .sec.white{padding-top:clamp(40px,4.4vw,72px)}

/* la montagne sous l'en-tête de page : plus basse et plus discrète que sur l'accueil */
.phead .mtn{height:min(78%,360px);opacity:.4;
  -webkit-mask-image:linear-gradient(180deg,transparent 0%,#000 46%,#000 72%,transparent 99%),linear-gradient(90deg,transparent 0%,#000 40%);
  mask-image:linear-gradient(180deg,transparent 0%,#000 46%,#000 72%,transparent 99%),linear-gradient(90deg,transparent 0%,#000 40%);
  -webkit-mask-composite:source-in;mask-composite:intersect}
.phead .wrap{position:relative;z-index:1}

/* l'itinéraire France → Chine n'a de sens que sur l'accueil ;
   ailleurs la barre reste, comme simple progression de lecture */
.route-ticks{display:none}
body.has-journey .route-ticks{display:block}

@media (min-width:1520px){
  /* Sur les très grands écrans la montagne rentre vers la colonne de texte.
     Son bord droit — rogné net dans la photographie — cesse alors d'être
     caché par le bord de l'écran : le masque le reprend en dégradé. */
  .mtn{right:max(0px, calc((100% - var(--maxw)) / 2 - 140px));
    -webkit-mask-image:linear-gradient(180deg,transparent 2%,#000 52%),linear-gradient(90deg,transparent 0%,#000 34%,#000 93%,transparent 100%);
    mask-image:linear-gradient(180deg,transparent 2%,#000 52%),linear-gradient(90deg,transparent 0%,#000 34%,#000 93%,transparent 100%)}
  .mtn-b{-webkit-mask-image:linear-gradient(180deg,transparent 4%,#000 56%),linear-gradient(90deg,transparent 0%,#000 30%,#000 93%,transparent 100%);
    mask-image:linear-gradient(180deg,transparent 4%,#000 56%),linear-gradient(90deg,transparent 0%,#000 30%,#000 93%,transparent 100%)}
}

/* ============================================================
   Insights — index éditorial et articles
   ============================================================ */
.idx-filter{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 clamp(26px,3vw,40px)}
.idx-filter button{background:linear-gradient(180deg,rgba(255,255,255,.8),rgba(255,255,255,.4));
  -webkit-backdrop-filter:blur(12px) saturate(1.4);backdrop-filter:blur(12px) saturate(1.4);
  border:1px solid rgba(10,13,18,.09);box-shadow:inset 0 1px 0 rgba(255,255,255,.85);
  color:var(--muted);border-radius:999px;padding:0 14px;min-height:40px;cursor:pointer;
  font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  display:inline-flex;align-items:center;gap:8px;white-space:nowrap;
  transition:color var(--t-fast),border-color var(--t-fast),background var(--t-fast),transform .1s ease-out}
.idx-filter button:active{transform:scale(.97)}
.idx-filter button::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.4}
.idx-filter button[aria-pressed="true"]{background:linear-gradient(180deg,#1B232E,#0A0D12);color:var(--white);
  border-color:#0A0D12;box-shadow:inset 0 1px 0 rgba(255,255,255,.14),0 2px 8px -4px rgba(10,13,18,.5)}
.idx-filter button[aria-pressed="true"]::before{opacity:1;background:var(--accent-dark)}
:lang(zh) .idx-filter button{font-family:var(--font-zh);font-size:12.5px;letter-spacing:.08em}

/* Le compte, dans le bouton de filtre et dans le titre de domaine. C'est une
   donnée réelle, pas un ornement : elle dit la taille de la bibliothèque et
   celle de chaque rayon, et elle se met à jour quand on cherche. */
/* Ce compteur hérite déjà d'un gris en retrait — rgb(97,97,90). Lui
   ajouter .55 d'opacité par-dessus le faisait tomber à 2,4:1 pour un
   texte de 10 px. Le retrait vient de la couleur, pas besoin de le
   doubler d'une transparence : à .92 le chiffre reste secondaire à
   côté de son titre, et passe à 4,7:1. */
.idx-n{font-style:normal;font-variant-numeric:tabular-nums;opacity:.92;
  font-size:.92em;letter-spacing:.04em}
.idx-filter button[aria-pressed="true"] .idx-n{opacity:.8}

/* La recherche. Elle reprend la matière du filtre — même verre, même filet,
   même hauteur — pour qu'on lise les deux comme un seul outil. */
.idx-search{position:relative;display:flex;align-items:center;
  margin:0 0 clamp(22px,2.6vw,34px);max-width:440px}
.idx-search[hidden]{display:none}
.idx-mag{position:absolute;left:14px;width:17px;height:17px;pointer-events:none;
  fill:none;stroke:var(--faint);stroke-width:1.6;stroke-linecap:round}
.idx-search input{width:100%;min-height:44px;padding:0 64px 0 40px;
  border:1px solid rgba(10,13,18,.11);border-radius:var(--r-sm);
  background:linear-gradient(180deg,rgba(255,255,255,.78),rgba(255,255,255,.4));
  -webkit-backdrop-filter:blur(12px) saturate(1.4);backdrop-filter:blur(12px) saturate(1.4);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.85);
  font:inherit;font-size:15px;color:var(--ink);
  transition:border-color var(--t-fast),box-shadow var(--t-fast)}
.idx-search input::placeholder{color:var(--faint)}
.idx-search input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.idx-search input:focus{outline:none;border-color:var(--warm-line);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.85),0 0 0 3px rgba(143,101,39,.14)}
.idx-search input:focus + .idx-res,.idx-res{position:absolute;right:14px;
  font-size:11px;letter-spacing:.1em;color:var(--faint);pointer-events:none}
.idx-search input::-webkit-search-cancel-button{cursor:pointer}

/* Six rayons au lieu d'une pile. Le titre de domaine porte un vrai h2 : les
   160 titres d'articles étaient 160 h2 à la file, une page sans structure
   pour un moteur comme pour un lecteur d'écran. Ils sont passés en h3. */
.idx-group{margin:0 0 clamp(40px,4.4vw,72px)}
.idx-group[hidden]{display:none}
.idx-group:last-of-type{margin-bottom:0}
.idx-h{display:flex;align-items:baseline;gap:12px;margin:0 0 4px;
  font-family:var(--font-mono);font-size:11.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);font-weight:500}
:lang(zh) .idx-h{font-family:var(--font-zh);font-size:13px;letter-spacing:.08em}

.idx{list-style:none;margin:0;padding:0;border-top:1px solid var(--ink)}
.idx li{border-bottom:1px solid var(--line)}
.idx li[hidden]{display:none}
.idx h3{grid-column:1;font-family:var(--font-display);font-weight:400;
  font-size:clamp(21px,2.1vw,29px);line-height:1.18;letter-spacing:-.008em;max-width:30ch;
  margin:0}
.idx a{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 28px;align-items:baseline;
  padding:clamp(20px,2.2vw,28px) 0;transition:transform .4s var(--ease)}
.idx a:hover{transform:translateX(4px)}
.idx .k{grid-column:1;font-family:var(--font-mono);font-size:11px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--accent-text)}
:lang(zh) .idx .k{font-family:var(--font-zh);font-size:12.5px;letter-spacing:.08em}
.idx p{grid-column:1;margin:2px 0 0;color:var(--muted);font-size:15.5px;max-width:62ch;line-height:1.5}
/* Chaque ligne portait son domaine en étiquette. Maintenant que la section
   le dit en titre, au-dessus, c'était la même information deux fois — 160
   fois de suite. L'étiquette disparaît de la ligne, le titre de l'article
   prend la première place, et le lecteur d'écran l'annonce encore une fois
   par la section à laquelle il appartient. */
.idx-group .idx .k{display:none}
.idx .go{grid-column:2;grid-row:1/-1;align-self:center;font-family:var(--font-mono);
  font-size:11px;color:var(--faint);white-space:nowrap}
.idx a:hover .go{color:var(--ink)}
.idx-none{padding:34px 0;color:var(--muted)}

.art-meta{display:flex;flex-wrap:wrap;align-items:center;gap:10px 18px;margin:22px 0 0;
  font-family:var(--font-mono);font-size:11px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
:lang(zh) .art-meta{font-family:var(--font-zh);font-size:12.5px;letter-spacing:.06em}
.art-meta .dot{width:4px;height:4px;border-radius:50%;background:var(--line-2)}
.art-meta b{font-weight:500;color:var(--ink)}
.art-body{max-width:68ch}
.art-body>*:first-child{margin-top:0}

@media (max-width:620px){
  .idx a{grid-template-columns:1fr}
  .idx .go{display:none}
  .idx p{font-size:15px}
  /* La rangée de filtres défile au doigt et sa barre est masquée : rien ne
     disait qu'il restait des sujets à droite. Le masque éteint les vingt
     derniers pixels, ce qui se lit comme « ça continue » sans ajouter le
     moindre élément à l'écran. Il s'efface quand on arrive au bout, parce
     qu'un dégradé permanent est une décoration, pas une information. */
  .idx-filter{overflow-x:auto;flex-wrap:nowrap;scrollbar-width:none;-ms-overflow-style:none;
    scroll-snap-type:x proximity;scroll-padding-inline:2px;
    -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 26px),transparent);
    mask-image:linear-gradient(to right,#000 calc(100% - 26px),transparent)}
  .idx-filter[data-end="1"]{-webkit-mask-image:none;mask-image:none}
  .idx-filter button{scroll-snap-align:start}
  .idx-filter::-webkit-scrollbar{display:none}
}

/* --- page 404 : la liste des grandes sections ------------------------- */
.e404-links{list-style:none;margin:1.4rem 0 0;padding:0;display:grid;gap:.1rem}
.e404-links li{border-top:1px solid var(--line)}
.e404-links li:last-child{border-bottom:1px solid var(--line)}
.e404-links .link{display:flex;align-items:center;justify-content:space-between;
  gap:1rem;padding:.95rem 0;width:100%;min-height:44px}

/* ============================================================
   Titre de panneau — les sections utilitaires ne portent plus le
   grand titre de chapitre : le traitement dit désormais si une
   section ouvre un sujet ou le complète.
   ============================================================ */
.sh-quiet{margin-bottom:clamp(30px,3.6vw,48px);max-width:56ch}
.h3q{font-family:var(--font-sans);font-size:clamp(20px,2.3vw,27px);line-height:1.22;
  letter-spacing:-.008em;font-weight:500;margin:0;max-width:24ch}
.h3q-sub{margin:8px 0 0;color:var(--muted);font-size:15.5px;max-width:52ch}
.dark .h3q-sub{color:var(--on-dark-muted)}

/* ============================================================
   « Tout ce que nous savons, écrit » — la seule section pleine
   largeur de l'accueil : géométrie différente, donc respiration.
   ============================================================ */
.reading{background:var(--white);border-block:1px solid var(--line)}
.rd-top{max-width:60ch}
.rd-h{font-family:var(--font-display);font-size:clamp(28px,3.6vw,46px);line-height:1.12;
  letter-spacing:-.012em;font-weight:400;margin:0}
.rd-h em{font-style:italic;color:var(--faint-lg)}
.rd-sub{margin:14px 0 0;color:var(--muted);font-size:16px;max-width:54ch}

.rd-stats{list-style:none;display:flex;flex-wrap:wrap;gap:0;margin:clamp(28px,3.4vw,42px) 0 0;
  padding:0;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.rd-stats li{flex:1 1 140px;padding:14px 0 13px;display:flex;align-items:baseline;gap:.5em;
  font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint)}
.rd-stats li + li{border-left:1px solid var(--line);padding-left:18px}
.rd-stats b{font-family:var(--font-display);font-size:26px;font-weight:400;letter-spacing:-.01em;
  color:var(--ink);font-feature-settings:"tnum" 1}

.rd-grid{list-style:none;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:0;margin:0;padding:0;border-bottom:1px solid var(--line)}
.rd-card + .rd-card{border-left:1px solid var(--line)}
.rd-card a{display:grid;gap:10px;align-content:start;padding:26px 24px 24px;height:100%;
  text-decoration:none;color:inherit;transition:background var(--t-med)}
.rd-card:first-child a{padding-left:0}
.rd-card:last-child a{padding-right:0}
.rd-card a:hover{background:var(--paper)}
.rd-k{font-size:10.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--accent-text)}
.rd-card:nth-child(2) .rd-k{color:var(--cn-text)}
.rd-card:nth-child(3) .rd-k{color:var(--faint)}
.rd-card h3{font-family:var(--font-display);font-size:20px;line-height:1.24;font-weight:400;
  letter-spacing:-.006em;margin:0;text-wrap:balance}
.rd-card p{margin:0;color:var(--muted);font-size:15px;line-height:1.55}
.rd-go{font-size:10.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--faint);
  margin-top:4px}
.rd-card a:hover .rd-go{color:var(--accent-text)}
.rd-card a:hover h3{color:var(--accent-text)}
.rd-all{margin:22px 0 0}

@media (max-width:900px){
  .rd-grid{grid-template-columns:1fr}
  .rd-card + .rd-card{border-left:0;border-top:1px solid var(--line)}
  .rd-card a{padding:22px 0}
  .rd-stats li + li{padding-left:16px}
}
@media (max-width:560px){
  .rd-stats li{flex:1 1 50%;border-left:0!important;padding-left:0!important}
  .rd-stats li:nth-child(n+3){border-top:1px solid var(--line)}
}

/* ============================================================
   Visuels d'ambiance — photographies traitées en bichromie aux
   couleurs du site. Elles sont un fond, jamais un sujet : le texte
   reste le premier plan et le contraste n'est pas entamé.
   ============================================================ */
.has-amb{overflow:hidden}
.amb{position:absolute;inset:auto 0 0 0;pointer-events:none;z-index:0;
  background-repeat:no-repeat;background-position:center bottom;background-size:cover;
  transform:translate3d(0,calc((1 - var(--q,1)) * 22px),0);will-change:transform}

.amb-sz::after{content:"";position:absolute;inset:0;
  background:linear-gradient(95deg,var(--navy) 0%,rgba(7,11,18,.86) 34%,rgba(7,11,18,.25) 62%,transparent 84%)}
.amb-sz{height:min(88%,660px);background-image:url(img/amb-shenzhen.fa5326feb5.webp);opacity:.42;
  -webkit-mask-image:linear-gradient(180deg,transparent 0%,#000 55%);
  mask-image:linear-gradient(180deg,transparent 0%,#000 55%)}

.amb-eu{height:min(74%,430px);background-image:url(img/amb-europe.f8c67aecfb.webp);opacity:.22;
  background-position:right bottom;mix-blend-mode:multiply;
  -webkit-mask-image:linear-gradient(180deg,transparent 4%,#000 70%),linear-gradient(90deg,transparent 18%,#000 62%);
  -webkit-mask-composite:source-in;
  mask-image:linear-gradient(180deg,transparent 4%,#000 70%),linear-gradient(90deg,transparent 18%,#000 62%);
  mask-composite:intersect}

@media (prefers-reduced-motion:reduce){.amb{transform:none}}

/* ============================================================
   Respiration pleine largeur — la seule image que l'on regarde
   pour elle-même, une fois dans la page.
   ============================================================ */
.band{position:relative;overflow:hidden;background:var(--navy);color:var(--on-dark);
  padding-block:clamp(84px,11vw,168px);isolation:isolate}
/* Le port était deux fois assombri : l'image à 52 % d'opacité, puis un voile
   par-dessus. On ne distinguait plus ni les portiques ni les conteneurs, et
   la bande ne racontait plus rien. L'image remonte, le voile s'allège — il
   reste appuyé à gauche, là où se trouve le texte, et s'efface à droite où
   la photographie peut enfin se voir. */
/* L'image remonte encore et le voile s'allège : à 0,72 d'image sous 0,91 de
   voile, le tiers gauche recevait neuf pour cent de photographie — le
   terminal n'y existait pas. Le voile reste appuyé là où se trouve le
   texte et s'efface plus tôt à droite. Les contrastes sont remesurés
   après coup, à trois largeurs, et c'est eux qui ont fixé les valeurs. */
.band-img{position:absolute;inset:0;z-index:0;background:url(img/amb-port.43228d28db.webp) center/cover no-repeat;
  opacity:1;transform:scale(1.06) translate3d(0,calc((1 - var(--q,1)) * -18px),0);
  will-change:transform}
.band::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    radial-gradient(48% 106% at 26% 44%,rgba(7,11,18,.88) 0%,rgba(7,11,18,.70) 32%,
                    rgba(7,11,18,.28) 60%,rgba(7,11,18,0) 84%),
    linear-gradient(100deg,rgba(7,11,18,.34) 0%,rgba(7,11,18,.16) 44%,rgba(7,11,18,0) 78%),
    /* L'ombre basse de l'atelier, aux mêmes paliers. La bande était la seule
       photographie du site à poser son bord inférieur en pleine lumière ;
       elle se terminait net là où les autres s'éteignent. */
    linear-gradient(to top, rgba(7,11,18,.56) 0%, rgba(7,11,18,.30) 18%,
                            rgba(7,11,18,.09) 36%, transparent 54%)}
/* La bande est volontairement ouverte : on veut voir le port, ses oranges
   et son sodium, parce que c'est le lien visuel avec les cinq autres
   photographies. Le voile large est donc très léger.
   Ce qui porte la lisibilité, c'est un SECOND voile, elliptique et très
   flou, posé là où se trouve le texte. Il vit dans les coordonnées de la
   bande et non dans une boîte à lui : une boîte plus petite laissait voir
   son bord dès qu'on allégeait assez le voile large pour que la
   photographie existe. */
.band-in{position:relative;z-index:2;max-width:min(var(--maxw),100%)}
.band-k,.band-h,.band-s{text-shadow:0 1px 2px rgba(5,9,16,.55),0 2px 18px rgba(5,9,16,.5)}
/* Sur téléphone la bande est haute et le texte l'occupe presque entière :
   un voile assez fort pour le porter éteindrait la photographie, qui est
   précisément ce qu'on veut montrer. Le voile recule donc, et c'est
   l'ombre portée qui prend le relais — elle ne coûte pas un pixel de
   lumière à l'image, contrairement à un voile. */
@media (max-width:820px){
  .band-k,.band-h,.band-s{text-shadow:0 1px 2px rgba(5,9,16,.8),0 1px 5px rgba(5,9,16,.72),
    0 2px 22px rgba(5,9,16,.6)}
}
/* Le surtitre est la ligne la plus fragile de la bande : onze pixels, en
   capitales espacées, dans le bleu d'accent, et posée en haut de l'image
   là où le ciel est le plus clair. Sur une photographie ouverte, il passe
   à la version pâle du même bleu — c'est la même couleur, montée en
   clarté, pas une autre. */
.band-k{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--bande-k);
  margin:0 0 20px;display:flex;flex-wrap:wrap;gap:.35em .1em;align-items:center}
.band-k i{font-style:normal;opacity:.45;padding-inline:.5em}
.band-h{font-family:var(--font-display);font-size:clamp(25px,3.4vw,44px);line-height:1.16;
  letter-spacing:-.012em;margin:0;max-width:20ch;text-wrap:balance}
/* Le gris sourd des sections sombres ne tient pas sur une photographie :
   mesuré à 3,5:1 en 1920, là où le voile s'ouvre. Posé sur une image,
   un texte secondaire se relève d'un cran de clarté et d'un demi-cran
   de graisse plutôt que de faire rabaisser le voile — c'est la règle
   de lisibilité sur matière translucide, et elle laisse la
   photographie ouverte, ce qui est tout l'objet de la bande. */
.band-s{margin:16px 0 0;font-size:clamp(17px,1.5vw,21px);color:var(--bande-s);font-weight:450;
  max-width:32ch}
@media (prefers-reduced-motion:reduce){.band-img{transform:scale(1.06)}}
/* Sur téléphone, la photographie disparaissait. Le voile est orienté à 100° :
   utile sur un écran large, où le texte tient dans le tiers gauche et où la
   photographie respire à droite — inutile sur 390 px, où il n'y a pas de
   droite. Le texte recevait 0,91 de voile sur une image déjà à 0,72 : il
   restait six pour cent de photographie, c'est-à-dire du noir.
   Le voile bascule donc à la verticale : fort là où se trouve le texte, il
   se retire vers le bas où l'image peut enfin se voir, et l'image remonte.
   Le contraste du titre est mesuré après coup, pas estimé. */
@media (max-width:820px){
  /* Le cadrage aussi : `center` tombait sur la route et les cours
     d'ombre, la partie la plus sombre de la photographie. Décalé vers
     la gauche, il attrape les portiques et le ciel — cinq cadrages
     comparés côte à côte, c'est celui-là qui se lit comme un port. */
  .band-img{opacity:1;background-position:58% 52%}
  .band::after{background:
      radial-gradient(128% 62% at 48% 28%,rgba(7,11,18,.50) 0%,rgba(7,11,18,.34) 42%,
                      rgba(7,11,18,.10) 74%,rgba(7,11,18,0) 92%),
      linear-gradient(180deg,rgba(7,11,18,.14) 0%,rgba(7,11,18,.08) 54%,
      rgba(7,11,18,0) 78%)}
}

/* La carte « Europe → Chine » reste claire dans une section sombre :
   elle doit donc reprendre ses propres couleurs de texte, sinon elle
   hérite du blanc de la section et devient illisible. */
.dark .path-cn{color:var(--ink)}
.dark .path-cn .dir{color:var(--muted)}
.dark .path-cn .for{color:var(--muted)}
.dark .path-cn .ticks li{color:var(--char)}

/* Les quatre colonnes de services passent sur fond sombre : les filets et
   les gris du mode clair y disparaîtraient. */
.dark .svc{border-top-color:var(--on-dark)}
.dark .svc details{border-bottom-color:var(--navy-line)}
.dark .svc li{color:var(--on-dark-muted);border-top-color:rgba(160,190,225,.18)}
.dark .svc li:hover{color:var(--on-dark)}
.dark .svc summary .mono{color:var(--faint-lg)}
.dark .svc summary::after{border-color:var(--navy-line);background:
  linear-gradient(var(--on-dark),var(--on-dark)) center/9px 1.5px no-repeat,
  linear-gradient(var(--on-dark),var(--on-dark)) center/1.5px 9px no-repeat}
/* Même anneau que celui de la FAQ, même raison : sur la nuit l'accent de la
   maison est le champagne des boutons, pas le bleu. */
.dark .svc summary:hover::after{border-color:var(--btn-rim-sombre-h);background-color:rgba(233,212,177,.08)}
.dark .svc details[open] summary::after{border-color:var(--btn-rim-sombre)}

/* ============================================================
   « Rien ici, et rien d'inventé » — la section Ventures ne montre
   plus trois gabarits vides mais dit pourquoi elle est vide. C'est
   la règle du site, et c'est un argument, pas un manque.
   ============================================================ */
.pledge{margin-top:26px;max-width:56ch}
.pledge-h{font-family:var(--font-display);font-size:clamp(21px,2.4vw,29px);line-height:1.2;
  letter-spacing:-.01em;margin:0;text-wrap:balance}
.pledge-p{margin:14px 0 0;color:var(--muted);font-size:16px;line-height:1.6}
.pledge-list{list-style:none;margin:22px 0 0;padding:0;display:grid;gap:0}
.pledge-list li{padding:11px 0 11px 26px;border-top:1px solid var(--line);position:relative;
  font-size:15px;color:var(--char)}
.pledge-list li:last-child{border-bottom:1px solid var(--line)}
.pledge-list li::before{content:"";position:absolute;left:2px;top:19px;width:10px;height:1px;
  background:var(--accent)}
.dark .pledge-p{color:var(--on-dark-muted)}
.dark .pledge-list li{color:var(--on-dark);border-color:var(--navy-line)}

/* La FAQ passe sur fond sombre : filets et gris du mode clair adaptés. */
.dark .faq-list{border-top-color:var(--on-dark)}
.dark .faq-list details{border-bottom-color:var(--navy-line)}
.dark .faq-list summary{color:var(--on-dark)}
.dark .faq-list p{color:var(--on-dark-muted)}

/* ---------- 2 marchés · 1 pont · 3 capacités ----------
   Bande de respiration entre « Choose your direction » (sombre) et « What we
   do » (papier) : trois chiffres, trois mots. Pas de cartes, pas d'ombres —
   la section doit se lire d'un coup d'œil et ne rien réclamer. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
/* Colonnes inégales : la troisième porte trois mots liés, les deux autres un
   seul — à largeurs égales la dernière passait à la ligne et cassait la ligne
   de base. Elles se lisent 1 · 2 · 3 : le pont, puis les deux marchés qu'il
   relie, puis ce qu'on y fait. */
.scope{display:grid;grid-template-columns:1fr .94fr 1.26fr;gap:0;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.sc-cell{display:flex;align-items:baseline;gap:clamp(14px,1.6vw,22px);
  padding:clamp(22px,2.6vw,34px) clamp(18px,2.2vw,30px);border-left:1px solid var(--line)}
.sc-cell:first-child{border-left:0;padding-left:0}
.sc-n{font-family:var(--font-display);font-size:clamp(46px,5.4vw,76px);line-height:.82;
  font-weight:400;letter-spacing:-.03em;color:var(--accent-text);
  font-variant-numeric:lining-nums;flex:none}
.sc-k{margin:0;font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.sc-v{margin:7px 0 0;font-family:var(--font-display);font-size:clamp(18px,1.85vw,25px);
  line-height:1.22;letter-spacing:-.012em;color:var(--ink)}
.sc-v b{font-weight:400;font-size:1.08em;color:var(--accent-text);padding:0 .14em}
.sc-links{display:flex;flex-wrap:wrap;align-items:baseline;gap:.1em .34em}
.sc-links a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--line);
  padding-bottom:2px;transition:color .18s var(--ease),border-color .18s var(--ease)}
.sc-links a:hover{color:var(--accent-text);border-color:var(--accent)}
.sc-links i{font-style:normal;color:var(--faint)}
.sc-v .zh-name{font-size:.72em;color:var(--faint);margin-left:.18em}

@media (max-width:900px){
  .scope{grid-template-columns:1fr}
  .sc-cell{border-left:0;border-top:1px solid var(--line);padding-left:0;padding-right:0}
  .sc-cell:first-child{border-top:0}
  .sc-n{font-size:44px}
}
.dark .scope,.dark .sc-cell{border-color:var(--navy-line)}
.dark .sc-v{color:var(--on-dark)}
.dark .sc-k{color:var(--on-dark-muted)}
.dark .sc-n{color:var(--accent-dark)}
.dark .sc-links a{color:var(--on-dark);border-color:var(--navy-line)}

/* ---------- cibles tactiles ----------
   Onze éléments cliquables mesuraient entre 25 et 42 px de haut sur mobile,
   sous le seuil de 44 px. Ils deviennent des boîtes de 44 px de haut, dans le
   flux normal. Un pseudo-élément transparent aurait évité les 4 px de
   croissance, mais la section peinte juste après en recouvrait la moitié :
   la zone paraissait agrandie sans l'être. */
@media (max-width:900px){
  .link,.sc-links a,.ft-legal a,.ft-links a,.crumb a,
  .nav a,.dr-contact a,.dr-contact .dr-line,.dr-dirs a,
  .idx-filter button,.stops button,.lang button,.logo{
    min-height:44px;display:inline-flex;align-items:center}
  .dr-contact a,.dr-contact .dr-line,.dr-dirs a,.nav a{display:flex}
  .lang button{justify-content:center}
  .copy,.menu-btn{min-width:44px;min-height:44px}
}

/* ---------- deux compositions de tête, pas plus ----------
   Les treize sections démarraient toutes au même pixel : c'était la cause
   résiduelle de l'impression de monotonie, avant les couleurs et les
   animations. Deux sections changent de composition. Une troisième en
   ferait un motif, et le motif redeviendrait de la monotonie. */
.sh-flip{grid-template-columns:minmax(0,1fr) minmax(0,1.15fr)}
.sh-flip > div{order:2}
.sh-flip .lead{order:1;align-self:end}
.sh-mid{grid-template-columns:minmax(0,1fr);justify-items:center;text-align:center}
/* CORRECTIF — « Strategy is useful. Execution matters more. »
   Les deux propositions se partageaient la même ligne : la première s'arrêtait
   au milieu, la seconde repartait à côté dans l'autre couleur, et la coupure
   tombait n'importe où selon la langue. Le contraste des deux couleurs ne se
   lit que si chaque phrase tient sa ligne. On les sépare donc franchement —
   ici seulement, les autres titres gardent leur enchaînement — et la mesure
   s'élargit juste assez pour que la plus longue des trois langues tienne. */
.sh-mid .h2{max-width:30ch}
.sh-mid .h2 span{display:block}
.sh-mid .eyebrow{justify-content:center}
@media (max-width:900px){
  /* en une colonne, l'inversion remettrait le chapeau avant le titre.
     Et il faut redéclarer la colonne unique ici : ces règles sont écrites
     après la media query générale, à spécificité égale — sans cela le
     chapeau Sourcing restait sur deux colonnes serrées sur téléphone. */
  .sh-flip,.sh-mid{grid-template-columns:1fr}
  .sh-flip > div{order:0}
  .sh-flip .lead{order:0}
  .sh-mid{justify-items:start;text-align:left}
  .sh-mid .h2{max-width:none}
}

/* ============================ SCÈNES PHOTOGRAPHIQUES ============================
   Trois images, trois traitements différents : un carton de chapitre pleine
   largeur (Shenzhen), une respiration silencieuse qui sort de la grille à
   droite (architecture), un encart qui en sort à gauche (l'atelier). Aucune
   n'est une carte : pas de fond blanc, pas de coins arrondis, pas d'ombre.
   Le mouvement vient du même moteur --q que le reste du site : une dérive
   verticale lente, rien d'autre. */
/* ---------- l'ombre portée des photographies ----------
   Celle-ci ne vit pas DANS l'image : elle déborde d'une douzaine de pixels
   sous le cadre, sur le papier. C'est elle qui fait que la photographie est
   POSÉE sur la page au lieu d'y être découpée — et c'est le seul détail qui
   manquait aux deux cadres pleine largeur, Paris et le port.

   Une ombre de boîte, et non un pseudo-élément : overflow:hidden découpe les
   enfants, jamais l'ombre de la boîte elle-même. Le décalage (14) et
   l'étalement négatif (-14) la gardent strictement sous le cadre : rien ne
   dépasse sur les côtés ni au-dessus. Le z-index la fait passer par-dessus
   le fond de la section suivante, qui sinon la recouvrirait — elle est
   peinte après, dans l'ordre du document.

   Deux couches : une courte et dense qui tient le bord, une large et douce
   qui porte loin. La première version ne descendait que de 28 px et laissait
   le papier à 204 sur 254 — on la devinait plus qu'on ne la voyait. Mesurée
   maintenant : 131 au ras du cadre, éteinte au bout de 76 px. */
.band,.scene-plate,.scene .pl-fig{
  box-shadow:0 12px 20px -10px rgba(8,12,18,.42),
             0 34px 60px -22px rgba(8,12,18,.55)}
/* Une ombre portée n'a de sens que sur le papier. Posée sur une section
   bleu-nuit, elle ne se lit pas comme une ombre mais comme une salissure :
   une bande sombre de 76 px collée au bord, qui désigne la couture au lieu
   de la cacher. C'était elle, le « dégradé mal fait » sous Shenzhen — pas le
   fondu, qui venait juste d'être réécrit. Mesuré sous la couture : 11,17,26
   au ras du bord au lieu des 20,26,36 de la carte, et soixante-seize pixels
   pour y revenir. Là où la section suivante est sombre, pas d'ombre. */
.band:has(+ .dark),
.scene-plate:has(+ .dark),
.scene:has(+ .dark) .pl-fig{box-shadow:none}
.band,.scene-plate{z-index:1}
.scene{position:relative;background:var(--paper)}
.pl-fig{margin:0;overflow:hidden;position:relative;background:var(--navy)}
.pl-fig picture{display:block;width:100%;height:100%}
.pl-fig img{display:block;width:100%;height:100%;object-fit:cover;
  transform:scale(1.055) translate3d(0,calc((1 - var(--q,1)) * -22px),0);
  will-change:transform}
.pl-cap{display:flex;flex-wrap:wrap;align-items:baseline;gap:.1em .5em;margin:0;
  font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.pl-cap i{font-style:normal;opacity:.5}

/* ---------- la respiration : architecture ---------- */
.scene-quiet{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,min(64vw,1560px));
  align-items:end;gap:clamp(18px,2.6vw,40px);
  padding:clamp(54px,7vw,116px) 0 clamp(26px,3vw,48px) var(--gutter)}
.scene-quiet .pl-cap{padding-bottom:6px}
/* 16/9 sur tous les supports, comme l'encart de l'atelier : les quatre
   photographies encadrées portent le même rapport du téléphone au 2560.
   Seuls les deux cartons de chapitre y échappent, et volontairement —
   ils sont pleine largeur, donc plus larges par construction. */
.scene-quiet .pl-fig{height:auto;aspect-ratio:16/9}

/* ---------- le carton de chapitre : Shenzhen ---------- */
.scene-plate{background:var(--navy);color:var(--on-dark);isolation:isolate;overflow:hidden}
.scene-plate .pl-fig{height:clamp(420px,74vh,720px)}
/* Shenzhen : la pointe de la tour était coupée, et d'autant plus que l'écran
   est large. Le cadre est plafonné à 720 px de haut ; à 1920 px de large la
   photographie en fait 936 une fois mise à l'échelle, donc 216 px partent au
   recadrage — 108 en haut avec un centrage, auxquels s'ajoute l'agrandissement
   de 5,5 % du scrub. La tour y perdait sa tête.
   Le point d'ancrage remonte à 18 % : la tour passe entière, le ciel respire,
   et le cartouche se pose sur l'eau plutôt que sur les immeubles. Paris garde
   son centrage — c'est une vue de toits, elle n'a pas de sujet vertical. */
.scene-plate:not(.warm) .pl-fig img{object-position:50% 18%}
/* Voile bas-gauche. Mesuré sur le pixel de fond le plus clair sous chaque
   ligne de texte, pas à l'œil : les bases éclairées des tours remontaient
   jusque derrière le nom et le contraste tombait à 2:1. Deux dégradés
   croisés, l'un vertical, l'autre depuis la gauche — le ciel et la ville
   restent intacts sur les trois quarts de l'image. */
/* L'ombre basse est celle de l'atelier, au chiffre près : .48 au ras du bord,
   .26 à 18 %, .08 à 36 %, éteinte à 54 %. C'est la seule des six photographies
   où elle se LIT, parce que c'est la plus claire ; les cartons, plus sombres,
   la portaient trop faible pour qu'elle existe. Mesurée au rendu, et non
   déclarée : 0,54 d'assombrissement réel au pied de l'image.

   Le voile bas était écrit QUATRE fois — carton froid, carton chaud, grand
   écran, téléphone — et les quatre valeurs avaient divergé : sur téléphone
   Paris recevait un dégradé plus profond et plus long que Shenzhen. C'est
   très exactement l'écart que Romain voyait entre les deux images. Une seule
   courbe désormais, et neuf arrêts au lieu de quatre : la pente ne casse
   nulle part, ni au départ ni à l'extinction. C'est la CASSURE DE PENTE, et
   non le dégradé, que l'œil repère comme une ligne sur un ciel uni. */
.scene-plate{
  --voile-bas:linear-gradient(to top,
    rgba(7,11,18,.56) 0%,  rgba(7,11,18,.50) 6%,  rgba(7,11,18,.41) 13%,
    rgba(7,11,18,.31) 21%, rgba(7,11,18,.22) 29%, rgba(7,11,18,.14) 37%,
    rgba(7,11,18,.08) 45%, rgba(7,11,18,.03) 53%, rgba(7,11,18,0) 62%);
  --voile-cote:linear-gradient(to right,
    rgba(7,11,18,.14) 0%, rgba(7,11,18,.06) 17%, rgba(7,11,18,0) 34%)}
.scene-plate::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:var(--voile-coin,none),var(--voile-bas),var(--voile-cote)}
/* ---------- le raccord avec la section sombre voisine ----------
   Un carton de chapitre posé contre une section bleu-nuit donnait une ligne
   franche : d'un côté le ciel de la photographie, de l'autre le fond plein.
   Les deux sont proches sans être identiques, et c'est justement ce qui se
   voyait. La photographie s'éteint donc dans la couleur de la section, du
   côté où il y en a une — en haut pour Paris, en bas pour Shenzhen.

   Les deux dégradés sont toujours là, mais transparents par défaut : c'est
   la règle voisine qui leur donne une couleur et une longueur. Un carton
   entouré de papier clair ne reçoit rien, et aucune règle ne s'annule. */
.scene-plate .pl-fig::after{content:"";position:absolute;inset:0;z-index:2;
  pointer-events:none;
  background:
    linear-gradient(to bottom, var(--fade-c-top,transparent) 0%,
                               transparent var(--fade-top,1px)),
    linear-gradient(to top,    var(--fade-c-bot,transparent) 0%,
                               var(--fade-c-bot,transparent) var(--fade-hold,0px),
                               transparent var(--fade-bot,1px))}
.dark + .scene-plate{--fade-c-top:var(--navy);--fade-top:clamp(64px,11vh,130px)}
.scene-plate:has(+ .dark){--fade-c-bot:var(--navy);--fade-bot:clamp(64px,11vh,130px)}
/* Le raccord vers le bloc « réseau de Shenzhen ».
   La photographie descendait vers le noir, la bande d'après était du navy,
   et la carte remontait vers un bleu plus clair : trois teintes pour une
   seule couture, et l'œil voyait la ligne. Elle descend maintenant droit
   dans le bleu de la carte, et la bande qui les sépare porte ce même bleu,
   qui se dissout ensuite dans le fond de la section. Une seule couleur
   traverse le raccord ; il n'y a plus de bord à voir. */
/* La couture avec la carte, réécrite.

   Le fondu générique est une DROITE : aplat, puis rampe linéaire, puis plus
   rien. Une droite qui s'arrête a forcément un coude à son extrémité, et
   c'est ce coude que Romain voit juste sous le cartouche — l'eau du port
   garde ses reflets jusque-là, puis s'éteint d'un coup. Sur Paris le même
   dispositif ne se voit pas, mais c'est un hasard de sujet : là-haut la
   photographie est un ciel de nuit déjà à la couleur du fond, donc le fondu
   n'a rien à effacer.

   LE FONDU N'EST PLUS ICI. Il est dans la photographie : les trois fichiers
   sky-* portent dans leurs pixels une rampe cubique vers le bleu exact de la
   carte, tramée d'un demi-niveau de bruit avant quantification, suivie d'une
   bande pleine de 8 % de la hauteur. Raison : sur un téléphone le carton ne
   fait que 260 px et la photographie y tient entière — un dégradé CSS n'a
   pas la place de se déployer, et tout ce qui est court se voit. Dans le
   fichier, la place est celle de l'image, et surtout le tramage supprime les
   bandes du huit bits, ce qu'aucun dégradé CSS ne sait faire.

   LE VOILE DU CARTOUCHE NON PLUS N'EST PLUS ICI. Il est cuit dans les mêmes
   trois fichiers : une ellipse appuyée au coin bas-gauche, pleine force sur
   un plateau, éteinte par une smoothstep jusqu'à zéro. C'était la dernière
   démarcation visible, et la raison en est arithmétique : sous le cartouche,
   la photographie est déjà presque entièrement remplacée par le bleu de la
   carte — c'est un aplat. Un dégradé CSS qui s'éteint dans un aplat sombre
   se quantifie sur huit bits et laisse une bande d'un niveau tous les quatre
   ou cinq pixels. Dans le fichier, le voile est tramé avant l'arrondi : il
   n'y a plus de bande, plus de calque, plus de masque, plus de bord.

   Il ne reste donc à écrire ici que trois lignes : éteindre les voiles CSS
   de ce carton, et garder le filet de sécurité sous l'image. */
.scene-plate:has(+ #shenzhen) .pl-fig{background:var(--navy-2)}
/* Plus aucun voile CSS sur ce carton : la photographie porte le sien, tramé.
   Le ::after de .scene-plate dessine les trois voiles communs (coin, bas,
   côté) ; ici il n'a plus rien à dessiner, et une couche en moins est un
   bord en moins. L'ombre locale sous le texte disparaît pour la même
   raison : un flou plus un masque est une couche rastérisée à part, dont le
   bord reste visible sur une eau presque noire agrandie au doigt. */
.scene-plate:has(+ #shenzhen)::after{display:none}
.scene-plate:has(+ #shenzhen) .pl-over::before{display:none}
/* LE FONDU DU BAS EST CALÉ SUR LE CADRE, PAS SUR L'IMAGE.

   C'était l'erreur, et elle ne se voyait que sur les grands écrans. Le fondu
   cuit dans la photographie occupe une fraction de la HAUTEUR DE L'IMAGE :
   aplat sur 12 %, rampe jusqu'à 38 %. Mais le cadre recadre l'image en
   « cover », et le recadrage, lui, dépend de la largeur de la fenêtre. Avec
   object-position 50 % 18 %, quatre cinquièmes du surplus sont coupés EN BAS.
   Mesuré, à hauteur de fenêtre égale :

       1440 px   le cadre coupe  4,2 % de l'image   -> on est dans l'aplat
       1920 px   le cadre coupe 18,9 %              -> on est dans la rampe
       2106 px   le cadre coupe 24,5 %              -> on est dans la photo

   Sur un écran large, le bord bas visible tombe donc en pleine photographie —
   mesuré à (138,126,111) côté droit, presque clair — et le filet de sécurité
   de seize pixels, qui rétablissait la couleur de la carte, tranchait net
   dedans : une marche de 118 niveaux, seize pixels au-dessus de la couture.
   C'est le trait que Romain voyait, et la couture elle-même mesurait zéro :
   l'aplat la masquait, et reportait le défaut juste au-dessus.

   Un fondu posé en pourcentage du cadre, lui, suit le recadrage : il est
   toujours là où il faut, quelle que soit la fenêtre. Les arrêts suivent une
   smoothstep — pente nulle aux deux extrémités — donc il n'y a de coude ni
   au départ ni à l'arrivée, ce qu'une rampe linéaire ne peut pas offrir.

   Le fondu cuit dans les pixels n'est pas pour autant inutile : sous 700 px
   le cadre ne recadre presque rien, et c'est lui qui fait tout le travail,
   tramé, là où un dégradé CSS n'aurait pas la place de se déployer. Les deux
   se recouvrent : celui-ci ne fait que reprendre ce que l'autre ne peut plus
   atteindre quand le cadre a mangé le bas de l'image. */
.scene-plate:has(+ #shenzhen) .pl-fig::after{
  background:
    linear-gradient(to bottom, var(--fade-c-top,transparent) 0%,
                               transparent var(--fade-top,1px)),
    linear-gradient(to top,
      var(--navy-2)        0%,
      rgba(20,26,36,.972)  3%,
      rgba(20,26,36,.896)  6%,
      rgba(20,26,36,.784)  9%,
      rgba(20,26,36,.648) 12%,
      rgba(20,26,36,.500) 15%,
      rgba(20,26,36,.352) 18%,
      rgba(20,26,36,.216) 21%,
      rgba(20,26,36,.104) 24%,
      rgba(20,26,36,.028) 27%,
      rgba(20,26,36,0)    30%)}
#shenzhen{background-image:linear-gradient(180deg,
  var(--navy-2) 0%, rgba(20,26,36,.55) 42%, rgba(20,26,36,0) clamp(150px,22vh,280px))}

.pl-over{position:absolute;left:var(--gutter);right:var(--gutter);bottom:clamp(28px,4vw,58px);
  z-index:2;max-width:var(--maxw);margin-inline:auto;isolation:isolate}
/* Ombre locale, sous le texte seulement. Le voile général a été allégé pour
   que la photographie respire ; c'était lui qui garantissait la lisibilité.
   Elle est donc reprise ici, sur la seule zone qui en a besoin — mesurée
   sur le pixel de fond le plus clair, pas à l'œil. */
/* Règle historique, conservée pour tout cartouche qui ne serait ni sur une
   scène nommée ni sur un carton pleine largeur. Aujourd'hui les cinq scènes
   sont l'un ou l'autre : c'est la règle « .scene.named / .scene-plate » plus
   bas dans ce fichier qui s'applique réellement, et c'est là qu'est le
   correctif de l'ombre. */
.pl-over::before{content:"";position:absolute;z-index:-1;pointer-events:none;
  left:-40px;right:auto;width:min(820px,72%);top:-56px;bottom:-72px;
  background:linear-gradient(100deg,
    rgba(7,11,18,.95) 0%, rgba(7,11,18,.92) 34%,
    rgba(7,11,18,.78) 60%, rgba(7,11,18,.42) 82%, transparent 98%);
  filter:blur(22px)}
@media (max-width:700px){.pl-over::before{width:min(560px,104%);left:-16px}}
/* Les deux cartons portent le même liseré miel. Le bleu d'accent y lisait
   comme une pièce rapportée depuis que la photographie est à l'heure dorée,
   et il tombait de toute façon à 4,37:1 sur le ciel le plus clair. */
.pl-k{margin:0 0 10px;display:flex;flex-wrap:wrap;align-items:center;gap:.1em .5em;
  font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--honey)}
.pl-k i{font-style:normal;opacity:.5}
.pl-name{margin:0;font-family:var(--font-display);font-weight:400;
  font-size:clamp(42px,7.4vw,108px);line-height:.94;letter-spacing:-.028em;
  color:var(--on-dark)}

/* ---------- l'encart : l'atelier ---------- */
.scene-inset{display:grid;grid-template-columns:minmax(0,min(58vw,1420px)) minmax(0,1fr);
  align-items:end;gap:clamp(18px,2.6vw,40px);
  padding:0 var(--gutter) 0 0;margin:clamp(34px,4.2vw,68px) 0}
.scene-inset .pl-fig{height:auto;aspect-ratio:16/9}
.scene-inset .pl-cap{display:block;max-width:24ch;padding-bottom:6px;line-height:1.7}
.scene-inset .pl-cap i{display:none}
/* le séparateur « · » s'intercale entre les deux span : c'est span + i + span,
   pas span + span — le lieu restait collé à la phrase */
.scene-inset .pl-cap span:last-child{display:block;color:var(--faint);
  opacity:.72;margin-top:6px}

@media (max-width:820px){
  /* en une colonne, l'image prend toute la largeur et la légende passe dessous :
     le recadrage mobile est une autre photo, pas une réduction */
  .scene-quiet,.scene-inset{grid-template-columns:1fr;gap:12px;padding-inline:0}
  .scene-quiet .pl-cap,.scene-inset .pl-cap{order:2;padding:0 var(--gutter)}
  .scene-quiet .pl-fig,.scene-inset .pl-fig{order:1}

  /* Un seul format pour les cinq photographies : le 16/9.

     Il y en avait trois (3:4, 4:5, 4:3) : cinq images qui ne semblaient plus
     appartenir à la même série. Les recadrages mobiles ont été refaits à ce
     format, depuis les mêmes masters étalonnés — même photographie, même
     traitement, seul le cadre change.

     La hauteur suit le rapport et rien d'autre. Un `max-height` était posé
     ici : sur un conteneur qui porte déjà une proportion, c'est la LARGEUR
     qu'il bride. En 4:5 plafonné à 76vh, l'image ne pouvait pas dépasser
     547 px et laissait 273 px de papier vide à droite sur une tablette de
     820 px. */
  .scene-quiet .pl-fig,
  .scene-inset .pl-fig,
  .scene-plate .pl-fig{height:auto;aspect-ratio:3/2}

  /* La légende appartient à l'image : elle reste collée dessous. C'est à la
     section suivante de porter la respiration, pas à la scène de la doubler. */
  /* ---- le rythme des cinq photographies -------------------------------
     Mesuré sur les pixels rendus, le vide autour d'elles allait de 30 à
     130 px au-dessus et de 51 à 191 px en dessous. La raison est simple :
     il était laissé à la section voisine, et le rythme intérieur d'une
     section n'a aucune raison d'être le même d'un cas à l'autre. Une série
     ne tient pas à ce compte-là.

     Le vide appartient donc maintenant à la scène : on annule la marge que
     la scène se donnait, et on fixe la respiration des deux côtés. */
  .scene-quiet{padding-block:0}
  .sec:has(+ .scene){padding-bottom:var(--air-scene)}
  .scene + .sec{padding-top:var(--air-scene)}
  .scene-inset{margin-block:var(--air-scene)}

  /* Le voile de la bande sur téléphone est réglé plus haut, avec celui des
     grands écrans, dans le bloc qui porte aussi le recadrage. Il y avait
     ici une seconde règle, plus ancienne et plus sombre (0,62 → 0,90),
     dans le MÊME palier de largeur mais plus bas dans le fichier : elle
     gagnait donc à chaque fois, et c'est elle qui rendait la bande noire
     sur téléphone quoi qu'on change ailleurs. Retirée. */

  /* Deux cas gardent un écart à part, et pour une raison interne à leur
     voisin — pas à cause du réglage ci-dessus.

     Le rail du parcours laisse sous sa dernière ligne l'espace de ses
     jambages : 32 px de blanc qui s'ajoutent à la respiration et portaient
     le vide au-dessus de Hong Kong à 104 px. On les reprend ici.

     La section Shenzhen, elle, place son schéma en premier sur téléphone, et
     ce schéma est dessiné avec un large vide au-dessus de ses premières
     lignes. Comme le carton de chapitre et la section sont tous deux en
     bleu-nuit, les deux vides se lisent comme un seul : près de 200 px. On
     resserre la section d'autant, sans toucher au dessin lui-même. */
  #process:has(+ .scene){padding-bottom:40px}
  .scene + #shenzhen{padding-top:24px}
  /* L'atelier avait ici des marges dissymétriques — 34 px au-dessus, 20 en
     dessous — héritées de l'époque où sa légende était collée sous l'image.
     Elle est maintenant une ligne à part entière : il reprend la marge
     symétrique du grand écran, que la règle de base donne déjà. */
}
/* Les cartons de chapitre gardent leur nom SUR la photographie à toutes les
   largeurs — comme les trois autres scènes. Une version où il descendait sur
   le fond bleu-nuit en dessous de 820 px a existé : elle garantissait le
   contraste sans rien demander à l'image, mais elle donnait deux mises en
   page pour une même série. L'uniformité l'emporte, et le contraste est
   obtenu autrement : par un voile qui tient compte de la hauteur réelle du
   cadre.

   Car c'est là qu'était la difficulté. En 16/9 sur un téléphone, le carton
   ne fait plus que 219 px de haut : le voile du grand écran, calibré en
   pourcentage, ne couvrait plus assez de pixels. Il monte donc plus haut
   sur les cadres courts.

   Mesures relevées sous les glyphes eux-mêmes, pas sur la boîte du
   paragraphe : celle-ci fait toute la largeur de la page, et le pixel le
   plus clair qu'on y trouve peut se situer à 800 px du dernier caractère. */
@media (max-width:820px){
  /* Sur un téléphone le carton ne fait plus que 260 px de haut : la même
     courbe en pourcentages ne couvre que 140 px et se lit comme une marche.
     Elle monte donc plus haut et s'éteint plus loin — les deux cartons
     reçoivent exactement la même, ce qui n'était pas le cas avant. */
  .scene-plate{
    --voile-bas:linear-gradient(to top,
      rgba(7,11,18,.58) 0%,  rgba(7,11,18,.52) 8%,  rgba(7,11,18,.43) 17%,
      rgba(7,11,18,.33) 27%, rgba(7,11,18,.23) 37%, rgba(7,11,18,.15) 47%,
      rgba(7,11,18,.09) 57%, rgba(7,11,18,.04) 66%, rgba(7,11,18,0) 76%);
    --voile-cote:linear-gradient(to right,
      rgba(7,11,18,.13) 0%, rgba(7,11,18,.06) 18%, rgba(7,11,18,0) 36%)}
  .scene-plate .pl-over{bottom:clamp(14px,3.4vw,24px)}
  /* l'ombre locale reste : c'est elle qui porte la lisibilité depuis que le
     voile large a été réduit au minimum */
  .pl-k{margin-bottom:6px}
  .scene-plate .pl-name{font-size:clamp(34px,8.4vw,52px)}
}

/* Le recadrage 16/9 servi aux téléphones garde le sujet là où il était :
   on rogne l'eau et le ciel, pas le bâtiment ni la jonque. Au-dessus de
   700 px c'est le master paysage qui est servi, et c'est object-position
   qui reproduit le même cadrage — sinon l'image sauterait d'un pixel de
   largeur à l'autre. */
@media (max-width:820px){
  .scene-quiet [data-scrub="plate"] img{object-position:center 28%}
  .scene-quiet.flip [data-scrub="plate"] img{object-position:center 42%}
}
@media (prefers-reduced-motion:reduce){
  .pl-fig img{transform:scale(1.055)}
}

/* ---------- le carton chaud : Paris ----------
   Même dispositif que Shenzhen, voile chaud au lieu du voile bleu-nuit :
   un voile froid sur une lumière dorée tuerait exactement ce qui fait
   la chaleur de cette image. Contraste mesuré sur les pixels réels. */
.scene-plate.warm{background:var(--navy)}
.scene-plate.warm{--voile-cote:linear-gradient(to right,
  rgba(7,11,18,.16) 0%, rgba(7,11,18,.07) 17%, rgba(7,11,18,0) 34%)}
/* Paris est le plus clair des deux cartons : ciel rose, ville allumée jusqu'au
   bas du cadre. Sur un téléphone, où le carton ne fait que 219 px de haut, sa
   ligne de coordonnées tombait à 4,7:1 — la plus faible des cinquante mesures.
   Voile plus profond.

   Cette règle doit être écrite APRÈS celle du dessus : à spécificité égale,
   une requête média ne l'emporte pas sur l'ordre du fichier. Placée plus
   haut, elle n'avait aucun effet — et le chiffre mesuré ne bougeait pas. */


/* ---------- la scène calme, en miroir ----------
   L'architecture sort de la grille à droite ; celle-ci en sort à gauche.
   Deux respirations, deux sens : sans cela ce serait un motif. */
.scene-quiet.flip{grid-template-columns:minmax(0,min(64vw,1560px)) minmax(0,1fr);
  padding-left:0;padding-right:var(--gutter)}
.scene-quiet.flip .pl-fig{order:1}
.scene-quiet.flip .pl-cap{order:2;display:block;max-width:26ch;line-height:1.7}
.scene-quiet.flip .pl-cap i{display:none}
.scene-quiet.flip .pl-cap span:last-child{display:block;color:var(--faint);
  opacity:.72;margin-top:6px}
@media (max-width:820px){
  .scene-quiet.flip{grid-template-columns:1fr;padding-inline:0}
  .scene-quiet.flip .pl-cap{max-width:none;padding:0 var(--gutter)}
}

/* ---------- les scènes nommées : le lieu écrit sur la photo ----------
   Shenzhen et Hong Kong portent désormais leur nom sur l'image, comme les
   deux cartons de chapitre. La légende grise sous la photo disait la même
   chose deux crans plus bas dans la hiérarchie ; quatre photographies, une
   seule façon de les nommer.

   L'image et le cartouche occupent la MÊME case de la grille : c'est ce qui
   les superpose sans sortir l'un des deux du flux, et donc sans qu'aucune
   hauteur ne soit à maintenir à la main. */
/* L'image occupe la colonne de droite dans la scène d'architecture, celle de
   gauche dans les deux autres : le cartouche la suit, case pour case. */
.scene-quiet.named .pl-fig,
.scene-quiet.named .pl-over{grid-area:1/2}
.scene-quiet.flip.named .pl-fig,
.scene-quiet.flip.named .pl-over,
.scene-inset.named .pl-fig,
.scene-inset.named .pl-over{grid-area:1/1}
.scene.named .pl-over{position:relative;inset:auto;
  z-index:2;align-self:end;margin:0;max-width:none;
  padding:0 clamp(18px,2vw,32px) clamp(18px,2.2vw,38px) clamp(20px,2.4vw,44px)}
/* Sur les scènes nommées le cartouche n'est pas DANS la figure : il partage
   sa case de grille. Son ombre, qui descendait 26 px sous le texte avec
   22 px de flou, sortait donc par le bas de la photographie et déposait un
   voile gris sur le papier — juste à l'endroit où se pose la légende. Elle
   s'arrête au ras de la boîte et s'éteint sur ses 34 derniers pixels. */
.scene.named .pl-over::before{bottom:0;
  -webkit-mask-image:linear-gradient(to top,transparent 0,#000 34px);
  mask-image:linear-gradient(to top,transparent 0,#000 34px)}
/* Le voile vit DANS la figure, qui est en overflow:hidden : posé sur le
   cartouche, son flou débordait sur le papier à côté de la photo. */
/* La lisibilité ne vient plus d'un voile posé sur la photographie mais d'une
   ombre locale, dessinée sous le texte et nulle part ailleurs. C'est la
   différence entre assombrir une image et asseoir une légende dessus : la
   première se voit, la seconde ne se remarque pas. Elle est floutée pour
   n'avoir aucun bord, et ne dépasse pas la hauteur des deux lignes. */
/* CORRECTIF — l'ombre s'arrêtait net à gauche.
   Le cartouche est centré sur la grille du texte : à 1920 px son bord gauche
   tombe à 340 px du bord de la photographie. L'ombre partait de là, et ce
   départ franc se lisait comme une arête verticale en travers de l'image —
   sur Paris comme sur Shenzhen.
   Elle court maintenant jusqu'au bord de l'image, et 60 px au-delà pour que
   les 28 px de flou n'éclaircissent pas les tout derniers pixels. Le bord
   droit ne bouge pas (right:0), donc la rampe finit exactement où elle
   finissait ; sa longueur est celle de la boîte d'avant — --pl-fade reprend
   la formule du cartouche, min(--maxw, 100vw - 2 × --gutter) — et les arrêts
   sont mesurés depuis la droite, non plus en pourcentage d'une boîte dont la
   largeur vient de changer. La partie gagnée à gauche tient le premier palier,
   à plat. Un left négatif ne crée pas de défilement horizontal : c'est le
   débordement à droite qui en créait un, et il n'y en a pas. */
.scene.named .pl-over::before,
.scene-plate .pl-over::before{content:"";position:absolute;z-index:-1;
  pointer-events:none;inset:auto;
  --pl-fade:min(var(--maxw), 100vw - 2 * var(--gutter));
  left:calc(50% - 50vw - 60px);right:0;width:auto;top:-18px;bottom:-26px;
  background:linear-gradient(100deg,
    rgba(7,11,18,.92) 0%,
    rgba(7,11,18,.92) calc(100% - var(--pl-fade)),
    rgba(7,11,18,.86) calc(100% - .62 * var(--pl-fade)),
    rgba(7,11,18,.62) calc(100% - .36 * var(--pl-fade)),
    rgba(7,11,18,.22) calc(100% - .14 * var(--pl-fade)),
    rgba(7,11,18,0)   100%);
  filter:blur(28px)}
/* Le bord HAUT du voile. Vingt-huit pixels de flou sur une boîte à .92, cela
   laisse encore une arête : sur l'atelier — de loin la photographie la plus
   claire — on voyait franchement où le bandeau commençait, et il se lisait
   comme un rectangle posé sur l'image. Mesuré sur la courbure du profil
   d'assombrissement, il y avait là un pic de 0,0092 ; il tombe à 0,0021,
   c'est-à-dire au niveau du reste du dégradé : plus d'arête du tout.

   Le masque seul coûtait du contraste à la ligne de coordonnées, qui vit
   justement en haut de la boîte (4,59 → 4,07:1). La boîte est donc d'abord
   remontée de 108 px : le fondu de 180 px se déroule AU-DESSUS du texte, qui
   garde son voile plein. 4,41:1 après — la ligne ne perd rien de lisible.
   Le fondu haut est écrit une fois, en pourcentages et en huit arrêts. La
   version précédente tenait en deux arrêts — plein jusqu'à un point, puis
   rampe — et ce point-là était une cassure de pente : sur le ciel uni de
   Shenzhen, à 390 px où la boîte est courte et la rampe comprimée, elle se
   lisait comme un trait horizontal en travers de l'image. Mesuré : un saut
   de 19 niveaux de gris sur trois pixels. Avec la courbe ci-dessous, 2,6. */
.scene.named,.scene-plate{
  --fondu-haut:#000 42%, rgba(0,0,0,.93) 53%, rgba(0,0,0,.80) 63%,
               rgba(0,0,0,.58) 73%, rgba(0,0,0,.33) 83%,
               rgba(0,0,0,.14) 91%, rgba(0,0,0,.04) 96%, transparent 100%;
  /* Le fondu du BAS du voile, resté en deux arrêts — « transparent 0, #000 à
     tant de pixels ». Une rampe droite entre deux plats : la pente saute à ses
     deux bouts, et celui du haut tombait à quarante pixels du bord inférieur
     du carton. C'est le dernier trait que Romain voyait en zoomant sous
     « Shenzhen ». Il suit maintenant un smoothstep (3t² − 2t³), dont la pente
     est NULLE aux deux extrémités : il n'y a plus de bout à voir. La longueur
     reste réglable par --fondu-bas-h, et la courbe ne change pas de forme. */
  --fondu-bas-h:34px;
  --fondu-bas:transparent 0,
    rgba(0,0,0,.061) calc(var(--fondu-bas-h) * .15),
    rgba(0,0,0,.216) calc(var(--fondu-bas-h) * .30),
    rgba(0,0,0,.425) calc(var(--fondu-bas-h) * .45),
    rgba(0,0,0,.575) calc(var(--fondu-bas-h) * .55),
    rgba(0,0,0,.784) calc(var(--fondu-bas-h) * .70),
    rgba(0,0,0,.939) calc(var(--fondu-bas-h) * .85),
    rgba(0,0,0,.993) calc(var(--fondu-bas-h) * .95),
    #000 var(--fondu-bas-h)}
.scene.named .pl-over::before{top:-108px;
  -webkit-mask-image:linear-gradient(to top,var(--fondu-bas),var(--fondu-haut));
  mask-image:linear-gradient(to top,var(--fondu-bas),var(--fondu-haut))}
.scene-plate .pl-over::before{top:-108px;
  -webkit-mask-image:linear-gradient(to top,#000 0,var(--fondu-haut));
  mask-image:linear-gradient(to top,#000 0,var(--fondu-haut))}
/* Sur téléphone le carton tombe à 260 px : remonter le voile de 108 px le
   faisait grimper au tiers de l'image, loin au-dessus du cartouche qu'il est
   censé porter. Il reste près de lui ; le fondu, écrit en pourcentages, suit
   la boîte. Écrit APRÈS la règle du dessus : à spécificité égale, une requête
   média ne l'emporte pas sur l'ordre du fichier. */
@media (max-width:820px){
  .scene.named .pl-over::before,
  .scene-plate .pl-over::before{top:-54px}
}
/* Toutes les scènes nommées portent désormais l'ombre de l'atelier, aux mêmes
   paliers : c'est elle qui sert de référence à toute la série. */
.scene.named .pl-fig::after{content:"";position:absolute;inset:0;
  pointer-events:none;z-index:1;
  background:
    linear-gradient(to top, rgba(7,11,18,.48) 0%, rgba(7,11,18,.26) 18%,
                            rgba(7,11,18,.08) 36%, transparent 54%),
    linear-gradient(to right, rgba(7,11,18,.13) 0%, transparent 32%)}
.scene.named .pl-name{font-size:clamp(34px,5.2vw,86px)}
/* La phrase descriptive n'est plus dans le cartouche : celui-ci ne porte que
   les coordonnées et le nom. Elle passe sous la photographie, dans la même
   colonne qu'elle, une ligne plus bas — d'où la deuxième rangée. Écrit après
   les règles génériques de .pl-cap, qui sont d'une spécificité égale : à
   égalité, c'est l'ordre du fichier qui tranche. */
.scene-quiet.named .pl-cap,
.scene-quiet.flip.named .pl-cap,
.scene-inset.named .pl-cap{display:block;max-width:56ch;margin:0;line-height:1.7;
  color:var(--honey-ink);
  padding:clamp(11px,1.2vw,16px) clamp(20px,2.4vw,44px) 0}
.scene-quiet.named .pl-cap{grid-area:2/2}
.scene-quiet.flip.named .pl-cap,
.scene-inset.named .pl-cap{grid-area:2/1}
/* L'atelier est le plus petit cadre des trois : son nom se cale dessus. */
.scene-inset.named .pl-name{font-size:clamp(32px,4.2vw,68px)}
/* C'est aussi, et de loin, la photographie la plus claire de la série :
   lampes d'atelier, établi verni, cartes réfléchissantes. Avec le voile
   commun, la ligne de coordonnées — la plus longue, elle va chercher le
   bois éclairé vers la droite — tombait à 2,2:1. Elle en reçoit donc un
   plus appuyé, dans les deux sens. Ce n'est pas un réglage à l'œil : les
   quatre autres photos n'en ont pas besoin et le gardent léger. */
.scene-inset.named .pl-fig::after{
  background:
    linear-gradient(to top, rgba(7,11,18,.48) 0%, rgba(7,11,18,.26) 18%,
                            rgba(7,11,18,.08) 36%, transparent 54%),
    linear-gradient(to right, rgba(7,11,18,.18) 0%, transparent 38%)}

/* ---------- le blanc autour de la légende ----------
   La phrase sous une photographie appartient à la photographie, pas à la
   section qui suit. Elle doit donc se tenir plus près de l'image que du
   texte d'après — et l'écart entre les deux blancs est le nombre d'or :
   au-dessus 1, au-dessous φ.

   Sans cette règle la légende héritait de la respiration ordinaire d'une
   section (124 px sur grand écran, 72 px sur téléphone) et se retrouvait
   pour un tiers au-dessus, deux tiers au-dessous : elle flottait entre
   deux blocs au lieu d'appartenir à l'un des deux. Mesuré sur les glyphes,
   le rapport passe de 3,1 à 1,6. */
.scene.named + .sec{padding-top:clamp(34px,2.7vw,42px)}
@media (max-width:820px){
  /* une seule colonne : les deux reviennent dans la même case, la première */
  .scene.named .pl-fig,
  .scene.named .pl-over{grid-area:1/1}
  .scene.named .pl-over{padding:0 var(--gutter) clamp(13px,3.4vw,22px)}
  .scene-quiet.named .pl-cap,
  .scene-quiet.flip.named .pl-cap,
  .scene-inset.named .pl-cap{grid-area:2/1;max-width:none;
    padding:12px var(--gutter) 0}
  .scene.named .pl-name,
  .scene-inset.named .pl-name{font-size:clamp(28px,7.6vw,44px)}
  .scene.named .pl-k{margin-bottom:6px}
  /* En 16/9 sur un téléphone, l'image ne fait plus que 219 px de haut : le
     voile du grand écran, qui s'éteint à 58 % de la hauteur, ne couvrait
     plus que 90 px et la ligne de coordonnées — la plus haute des deux —
     tombait à 3,5:1. Il monte donc plus haut sur les cadres courts. */
  .scene.named .pl-fig::after{
    background:linear-gradient(to top,
      rgba(7,11,18,.44) 0%, rgba(7,11,18,.24) 22%, rgba(7,11,18,.07) 40%,
      transparent 56%)}
  /* L'atelier de nouveau : sur 320 px sa ligne de coordonnées passe à la
     ligne et remonte dans les lampes. Cette règle-ci est plus bas dans le
     fichier que celle du grand écran, à spécificité égale — sans elle, la
     version allégée du mobile l'emporterait et annulerait la correction. */
  .scene-inset.named .pl-fig::after{
    background:linear-gradient(to top,
      rgba(7,11,18,.50) 0%, rgba(7,11,18,.28) 26%, rgba(7,11,18,.09) 46%,
      transparent 62%)}
  /* même rapport sur téléphone. L'atelier n'est pas suivi d'une section mais
     du parcours de sourcing, dans la même section : c'est sa propre marge
     basse qu'il faut reprendre, pas celle du voisin. */
  .scene.named + .sec{padding-top:38px}
  .scene-inset.named{margin-bottom:20px}
}

/* ---------- grand écran : les cinq photographies en pleine largeur ----------
   Deux des quatre cadres sortaient de la grille — l'architecture par la
   droite, l'atelier et Hong Kong par la gauche — et laissaient en face une
   demi-page de papier vide. C'était voulu, et ça ne tenait pas : sur un
   écran de 1536 px, 553 px de vide à gauche d'une photographie ne se lisent
   pas comme une respiration, ils se lisent comme une image qui n'a pas
   chargé.

   La disposition du téléphone remonte donc sur l'ordinateur : une seule
   colonne, l'image pleine largeur, le cartouche posé dessus, la légende
   dessous. Les corps, les hauteurs et le rapport des blancs restent ceux du
   grand écran. Le téléphone et la tablette ne changent pas d'un pixel : ce
   bloc ne s'applique qu'au-dessus de 820 px.

   Les cinq photographies prennent aussi la même hauteur que les deux cartons
   de chapitre, clamp(420px, 74vh, 720px) : quatre cadres en 16/9 et deux
   bandeaux plus larges ne faisaient pas une série. */
@media (min-width:821px){
  .scene-quiet,.scene-quiet.flip,.scene-inset{
    grid-template-columns:minmax(0,1fr);gap:0;padding:0}
  .scene-quiet .pl-fig,.scene-inset .pl-fig{
    height:clamp(420px,74vh,720px);aspect-ratio:auto}
  /* image et cartouche dans la même case, comme sur téléphone */
  .scene.named .pl-fig,.scene.named .pl-over{grid-area:1/1}
  /* cartouche et légende calés sur la colonne de texte du site, exactement
     comme ceux des deux cartons : les cinq lieux s'alignent verticalement */
  .scene.named .pl-over{width:calc(100% - var(--gutter) * 2);
    max-width:var(--maxw);margin-inline:auto;
    padding:0 0 clamp(26px,3vw,52px)}
  .scene-quiet.named .pl-cap,
  .scene-quiet.flip.named .pl-cap,
  .scene-inset.named .pl-cap{grid-area:2/1;
    width:calc(100% - var(--gutter) * 2);max-width:var(--maxw);
    margin-inline:auto;padding:clamp(19px,1.7vw,29px) 0 0}
  /* l'atelier n'est pas suivi d'une section mais du parcours de sourcing :
     c'est sa marge basse qui tient le rapport, pas celle du voisin */
  .scene-inset{margin:clamp(34px,4.2vw,68px) 0 clamp(20px,1.6vw,30px)}
}

/* ===================== diagnostic =====================
   Rien de neuf : les jetons, les rayons, les transitions et le champagne des
   boutons sont ceux du reste du site. La page est claire, comme les pages
   d'article, et reprend la grammaire de la carte de direction du formulaire
   de contact — celle que le visiteur a déjà vue. */
.dg{max-width:56rem}
/* Le site réserve la hauteur de l'en-tête collant aux `section[id]` ; le
   résultat et le bloc sont des div, donc ils passaient dessous au défilement. */
.dg,.dg-out{scroll-margin-top:calc(var(--hd) + 16px)}
.dg-ask{font-size:clamp(19px,2.1vw,23px);line-height:1.35;margin:0 0 20px;
  font-family:var(--font-display);font-weight:400}
:lang(zh) .dg-ask{font-family:var(--font-zh-serif);font-weight:500}
.dg-dirs{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.dg-dir{display:flex;flex-direction:column;gap:7px;text-align:left;
  padding:20px 22px;border:1px solid var(--line-2);border-radius:var(--r-md);
  background:transparent;cursor:pointer;font:inherit;color:inherit;
  transition:border-color var(--t-fast),background var(--t-fast),transform .1s ease-out}
.dg-dir:hover{border-color:var(--warm-line-hi);background:var(--warm-veil)}
.dg-dir:active{transform:scale(.985)}
.dg-dir .mono{font-size:11px;color:var(--muted);letter-spacing:.06em}
:lang(zh) .dg-dir .mono{font-family:var(--font-zh);font-size:12.5px}
.dg-dir b{font-weight:560;font-size:17px;line-height:1.35}

.dg-q{border:0;padding:0;margin:0;min-width:0}
.dg-q legend{display:flex;align-items:baseline;gap:12px;padding:0;margin:0 0 20px;
  font-size:clamp(19px,2.1vw,23px);line-height:1.35;
  font-family:var(--font-display);font-weight:400}
:lang(zh) .dg-q legend{font-family:var(--font-zh-serif);font-weight:500}
.dg-n{flex:none;font-size:11px;color:var(--faint);letter-spacing:.08em}
.dg-opts{display:grid;gap:9px}
.dg-o{display:block;position:relative;padding:15px 18px 15px 46px;
  border:1px solid var(--line);border-radius:var(--r-sm);cursor:pointer;
  font-size:15.5px;line-height:1.45;
  transition:border-color var(--t-fast),background var(--t-fast)}
.dg-o::before{content:"";position:absolute;left:18px;top:50%;margin-top:-7px;
  width:14px;height:14px;border-radius:50%;border:1px solid var(--line-2);
  transition:border-color var(--t-fast),box-shadow var(--t-fast)}
.dg-o:hover{border-color:var(--warm-line-hi);background:var(--warm-veil)}
.dg-o input{position:absolute;opacity:0;pointer-events:none}
.dg-o:has(input:checked){border-color:var(--accent);background:rgba(42,86,198,.04)}
.dg-o:has(input:checked)::before{border-color:var(--accent);
  box-shadow:inset 0 0 0 3.5px var(--accent)}
.dg-o:has(input:focus-visible){outline:2px solid var(--accent);outline-offset:3px}

.dg-foot{margin-top:30px}
.dg-bar{height:2px;background:var(--line);border-radius:2px;overflow:hidden}
.dg-bar i{display:block;height:100%;width:0;border-radius:2px;
  background:linear-gradient(90deg,var(--accent),var(--cn));
  transition:width var(--t-med) var(--ease)}
.dg-nav{display:flex;align-items:center;justify-content:space-between;
  gap:16px;margin-top:14px}
.dg-back{background:none;border:0;padding:8px 0;font:inherit;font-size:14px;
  color:var(--accent-text);cursor:pointer;
  border-bottom:1px solid rgba(7,113,155,.35);transition:color var(--t-fast),opacity var(--t-fast)}
.dg-back:hover{color:var(--ink);border-color:var(--ink)}
.dg-back:disabled{opacity:.3;pointer-events:none}
.dg-step{font-size:12px;color:var(--faint);letter-spacing:.08em}

.dg-out-s{color:var(--muted);margin:10px 0 0;max-width:52ch}
.dg-lv{margin-top:40px}
.dg-lvh{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.09em;
  color:var(--muted);margin:0;font-family:var(--font-sans)}
:lang(zh) .dg-lvh{font-family:var(--font-zh);letter-spacing:.04em}
.dg-lvs{color:var(--faint);font-size:13.5px;margin:4px 0 18px}
.dg-lvb{display:grid;gap:12px}
.dg-all{display:none}

.dg-r{padding:20px 22px;border:1px solid var(--line);border-radius:var(--r-md);
  border-left-width:3px}
.dg-r h3{margin:0 0 8px;font-size:17.5px;line-height:1.35;font-weight:560}
.dg-r p{margin:0 0 12px;color:var(--muted);font-size:15px;line-height:1.62;max-width:62ch}
.dg-r .link{font-size:14px;padding-top:0}
.dg-l1{border-left-color:var(--seal)}
.dg-l2{border-left-color:var(--honey-ink)}
.dg-l3{border-left-color:var(--accent)}

.dg-end{margin-top:44px;padding-top:26px;border-top:1px solid var(--line)}
.dg-end > p{margin:0 0 20px;color:var(--muted);max-width:58ch;line-height:1.62}
.dg-note{margin:16px 0 0;font-size:13px;color:var(--faint)}

@media (max-width:640px){
  .dg-dirs{grid-template-columns:1fr}
  .dg-o{padding:14px 16px 14px 42px;font-size:15px}
  .dg-o::before{left:16px}
  .dg-r{padding:17px 18px}
}
/* Sans :has(), la sélection ne se verrait pas : on la montre au contour. */
@supports not selector(:has(*)){
  .dg-o input:checked ~ span{font-weight:600}
  .dg-o input:focus-visible ~ span{outline:2px solid var(--accent);outline-offset:3px}
}
/* Le drapeau « js » est posé sur <html> avant le premier rendu (voir build.py) :
   la mention ne s'affiche donc que si JavaScript est réellement absent, sans
   clignotement pour les autres. */
.dg-nojs{display:none}
html:not(.js) .dg-nojs{display:block;margin:0 0 28px;padding:16px 18px;
  border:1px solid var(--line-2);border-left:3px solid var(--honey-ink);
  border-radius:var(--r-sm);color:var(--muted);font-size:15px;line-height:1.6}
html:not(.js) .dg-start,html:not(.js) .dg-foot{display:none}

/* ---------- les positions qui étaient en attribut style= ----------
   Treize attributs style= vivaient dans le balisage, répétés sur chaque
   page : 2 895 occurrences pour onze valeurs distinctes. Tant qu'ils
   existaient, la politique de sécurité du contenu devait autoriser
   style-src 'unsafe-inline' — c'est-à-dire n'importe quel style posé dans
   la page, y compris par un script injecté. Les valeurs sont reprises ici
   à l'identique, au pixel près ; le balisage n'en porte plus aucune, et la
   politique peut refuser tout style en ligne. */
.route-ticks .route-tick:nth-child(1){left:0}
.route-ticks .route-tick:nth-child(2){left:25%}
.route-ticks .route-tick:nth-child(3){left:50%}
.route-ticks .route-tick:nth-child(4){left:75%}
.route-ticks .route-tick:nth-child(5){left:100%}
.sprite{position:absolute}
.wrap-rel{position:relative}
#tOff{color:var(--muted)}
.mt-24{margin-top:24px}
.mt-18{margin-top:18px}

/* Fonds d'ambiance : une variante pour les petits écrans.
   ------------------------------------------------------------------
   Les trois images .band-img, .amb-sz et .amb-eu sont des couches de
   fond, posées sous le contenu — deux d'entre elles à .42 et .22
   d'opacité. Mesuré sur l'accueil à 390 px de large : 453 Ko d'images
   partaient dès le chargement, en 1800 px, pour être affichées sur un
   écran quatre fois plus étroit.

   On ne retouche pas les fichiers d'origine et on ne change rien au
   desktop, qui continue de recevoir les mêmes 1800 px. Sous 760 px, la
   même image en 900 px prend sa place : à cette largeur l'œil ne peut
   pas faire la différence, et la page passe de 453 Ko à 193 Ko sur ces
   trois fonds. */
@media (max-width:760px){
  .band-img{background-image:url(img/amb-port-m.039e5efc4d.webp)}
  .amb-sz{background-image:url(img/amb-shenzhen-m.86cdb7c28c.webp)}
  .amb-eu{background-image:url(img/amb-europe-m.6e5414854d.webp)}
}

/* La suite du diagnostic — deux liens, selon la direction choisie.
   ------------------------------------------------------------------
   Posée entre les constats et la demande d'écrire. Elle reprend la
   matière des liens de résultat : même filet, même retrait, aucune
   couleur d'appel. Ce n'est pas un encart commercial, c'est la porte
   d'à côté pour celui qui veut lire avant de parler. */
.dg-next{margin:26px 0 0;padding-top:18px;border-top:1px solid var(--line)}
.dg-next-h{margin:0 0 10px;font-size:13px;color:var(--muted);
  letter-spacing:.02em}
.dg-next-l{display:flex;flex-wrap:wrap;gap:14px 26px}
@media (max-width:560px){.dg-next-l{gap:12px 0;flex-direction:column}}
