{"id":15690,"date":"2026-10-16T20:34:53","date_gmt":"2026-10-16T18:34:53","guid":{"rendered":"https:\/\/www.ok3d.fr\/?page_id=15690"},"modified":"2026-10-16T20:45:00","modified_gmt":"2026-10-16T18:45:00","slug":"oeil-designer","status":"publish","type":"page","link":"https:\/\/www.ok3d.fr\/en\/oeil-designer\/","title":{"rendered":"Designer&#039;s eye"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"15690\" class=\"elementor elementor-15690\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-bbe5d3c e-con e-atomic-element e-flexbox-base e-ba436fd \" data-id=\"bbe5d3c\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"bbe5d3c\" data-e-type=\"e-flexbox\" data-id=\"bbe5d3c\">\n    \t\t<div class=\"elementor-element elementor-element-c1d60fd elementor-widget elementor-widget-html\" data-id=\"c1d60fd\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- \u0152il de designer by OK3D \u2014 contenu centr\u00e9, sans en-t\u00eate int\u00e9gr\u00e9.\n  Coller TOUT ce fichier dans un widget HTML (sans balises html\/head\/body).\n  Page : Elementor pleine largeur pour conserver l'en-t\u00eate du site.\n  Conteneur : pleine largeur, marges\/padding 0,\n  d\u00e9bordement visible. Le contenu conserve son d\u00e9filement vertical.\n  Cadre maximal : 1600px, marges comprises, comme le configurateur OK3D.\n  Header : 94px + 54px d'espace sur ordinateur ; 78px + 43px sur mobile.\n  R\u00e9glages : --okk-content-max, --okk-header-offset et --okk-top-gap.\n-->\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n<link rel=\"stylesheet\" href=\"https:\/\/fonts.googleapis.com\/css2?family=Unbounded:wght@500;700;800&family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@500;600&display=swap\">\n<style>\n.okk-scope[data-okk=\"oeil\"]{display:block;position:relative;left:auto;width:100vw!important;max-width:100vw!important;margin-left:calc(50% - 50vw)!important;margin-right:calc(50% - 50vw)!important;font-family:var(--f-body);font-size:15px;line-height:1.5;color:var(--ink);background:var(--bg);text-align:left;-webkit-font-smoothing:antialiased}\n.okk-scope[data-okk=\"oeil\"] *,.okk-scope[data-okk=\"oeil\"] *::before,.okk-scope[data-okk=\"oeil\"] *::after{box-sizing:border-box}\n.okk-scope[data-okk=\"oeil\"] [hidden]{display:none!important}\n.okk-scope[data-okk=\"oeil\"] > .okk{min-height:100vh;min-height:100svh}\n.okk-scope[data-okk=\"oeil\"] img{max-width:100%;height:auto}\n.okk-scope[data-okk=\"oeil\"] button,.okk-scope[data-okk=\"oeil\"] input{font-family:inherit;text-transform:none;letter-spacing:normal;min-height:0;box-shadow:none;margin:0}\n.okk-scope[data-okk=\"oeil\"] h1,.okk-scope[data-okk=\"oeil\"] h2,.okk-scope[data-okk=\"oeil\"] h3{color:inherit;text-transform:none;padding:0}\n.okk-scope[data-okk=\"oeil\"] p{margin:0}\n.okk-scope[data-okk=\"oeil\"] a{text-decoration:none}\n.okk-scope[data-okk=\"oeil\"]{\n  color-scheme:dark;\n  --bg:#06080C; --ink:#EEF2F8; --muted:#8792A6; --faint:#566074; --line:#1C2433; --line2:#2A3549;\n  --brand:#2E6BFF; --brand-hi:#4F86FF; --brand2:#7CC4FF; --glow:rgba(46,107,255,.45); --good:#35E0A1; --bad:#FF5A6E; --gold:#FFCB6B;\n  --paper:#F3F5F8; --pink:#0E1116; --pline:#D5DBE4; --pshape:#DCE3EE; --pgood:#0FA968; --pbad:#F0533A; --puser:#2E6BFF;\n  --f-display:\"Unbounded\",\"Arial Black\",system-ui,sans-serif; --f-body:\"Geist\",system-ui,-apple-system,\"Segoe UI\",sans-serif; --f-mono:\"Geist Mono\",ui-monospace,Menlo,Consolas,monospace;\n}\n.okk-scope[data-okk=\"oeil\"]{background:var(--bg);color:var(--ink)}\n.okk-scope[data-okk=\"oeil\"]{font-family:var(--f-body);font-size:15px;line-height:1.5;-webkit-font-smoothing:antialiased}\n.okk-scope[data-okk=\"oeil\"] *{box-sizing:border-box}\n.okk-scope[data-okk=\"oeil\"] button{font:inherit;color:inherit;cursor:pointer;background:none;border:0;padding:0}\n.okk-scope[data-okk=\"oeil\"] a{color:inherit}\n.okk-scope[data-okk=\"oeil\"] a.btn{text-decoration:none}\n.okk-scope[data-okk=\"oeil\"] :focus-visible{outline:2px solid var(--brand2);outline-offset:3px;border-radius:6px}\n.okk-scope[data-okk=\"oeil\"] .okk{position:relative;overflow:hidden;min-height:100%;padding-left:max(16px,env(safe-area-inset-left,0px));padding-right:max(16px,env(safe-area-inset-right,0px));padding-block:0 28px;\n  background:radial-gradient(1000px 520px at 50% -10%,rgba(46,107,255,.2),transparent 62%),var(--bg)}\n.okk-scope[data-okk=\"oeil\"] .wrap{position:relative;max-width:1040px;margin:0 auto}\n.okk-scope[data-okk=\"oeil\"] .bar{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding-block:18px 0;flex-wrap:wrap}\n.okk-scope[data-okk=\"oeil\"] .brand{display:flex;align-items:center;gap:12px;text-decoration:none}\n.okk-scope[data-okk=\"oeil\"] .brand .mk{font:700 17px var(--f-display);letter-spacing:-.02em;display:flex;align-items:center;gap:8px}\n.okk-scope[data-okk=\"oeil\"] .brand .mk i{width:26px;height:26px;border-radius:9px;display:grid;place-items:center;font-style:normal;font-size:9px;background:linear-gradient(150deg,var(--brand-hi),#1C47C9);box-shadow:0 0 0 1px rgba(255,255,255,.12) inset,0 6px 20px -6px var(--glow)}\n.okk-scope[data-okk=\"oeil\"] .brand img{height:26px;width:auto;display:block}\n.okk-scope[data-okk=\"oeil\"] .brand .pr{font:500 13px var(--f-body);color:var(--muted);padding-left:12px;border-left:1px solid var(--line2)}\n.okk-scope[data-okk=\"oeil\"] .best{font:500 12px var(--f-mono);color:var(--muted);padding:6px 12px;border:1px solid var(--line);border-radius:999px}\n.okk-scope[data-okk=\"oeil\"] .best b{color:var(--ink)}\n.okk-scope[data-okk=\"oeil\"] .eyebrow{font:600 11px var(--f-mono);letter-spacing:.16em;text-transform:uppercase;color:var(--brand2);display:inline-flex;align-items:center;gap:8px}\n.okk-scope[data-okk=\"oeil\"] .eyebrow::before{content:\"\";width:6px;height:6px;border-radius:2px;background:var(--brand2);box-shadow:0 0 10px var(--brand2)}\n.okk-scope[data-okk=\"oeil\"] h1,.okk-scope[data-okk=\"oeil\"] h2,.okk-scope[data-okk=\"oeil\"] h3{margin:0;font-family:var(--f-display);letter-spacing:-.035em;text-wrap:balance}\n.okk-scope[data-okk=\"oeil\"] .grad{background:linear-gradient(100deg,var(--brand-hi),var(--brand2) 60%,#E9F5FF);-webkit-background-clip:text;background-clip:text;color:transparent}\n.okk-scope[data-okk=\"oeil\"] .btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;height:52px;padding:0 28px;border-radius:999px;font-weight:600;font-size:15.5px;transition:transform .15s,box-shadow .2s,border-color .2s,opacity .2s;white-space:nowrap}\n.okk-scope[data-okk=\"oeil\"] .btn.pri{background:linear-gradient(180deg,var(--brand-hi),#2152E0);color:#fff;box-shadow:inset 0 1px 0 rgba(255,255,255,.28),0 12px 34px -10px var(--glow),0 0 0 1px rgba(46,107,255,.6)}\n.okk-scope[data-okk=\"oeil\"] .btn.pri:hover{transform:translateY(-1px)}\n.okk-scope[data-okk=\"oeil\"] .btn.pri:disabled{opacity:.35;transform:none;cursor:default}\n.okk-scope[data-okk=\"oeil\"] .btn.gho{border:1px solid var(--line2);background:rgba(255,255,255,.02)}\n.okk-scope[data-okk=\"oeil\"] .btn.gho:hover{border-color:var(--muted)}\n.okk-scope[data-okk=\"oeil\"] .btn kbd{font:600 10.5px var(--f-mono);padding:2px 6px;border-radius:5px;border:1px solid rgba(255,255,255,.3)}\n.okk-scope[data-okk=\"oeil\"] .glass{background:linear-gradient(180deg,rgba(255,255,255,.04),rgba(255,255,255,.012));border:1px solid var(--line);border-radius:26px;box-shadow:inset 0 1px 0 rgba(255,255,255,.045)}\n@media (hover:none){.okk-scope[data-okk=\"oeil\"] .kbonly{display:none!important}}\n.okk-scope[data-okk=\"oeil\"] .intro{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:40px;align-items:center;padding-block:30px 46px}\n.okk-scope[data-okk=\"oeil\"] .intro h1{font-size:clamp(38px,6vw,76px);line-height:.98;font-weight:800;margin-top:18px}\n.okk-scope[data-okk=\"oeil\"] .intro p{color:var(--muted);font-size:clamp(15px,1.4vw,17px);max-width:44ch;margin:20px 0 0}\n.okk-scope[data-okk=\"oeil\"] .intro .meta{display:flex;gap:10px;flex-wrap:wrap;margin-top:22px}\n.okk-scope[data-okk=\"oeil\"] .intro .meta span{font:500 12px var(--f-mono);color:var(--ink);padding:6px 12px;border-radius:999px;background:rgba(255,255,255,.05)}\n.okk-scope[data-okk=\"oeil\"] .intro .gorow{margin-top:28px;display:flex;gap:10px;flex-wrap:wrap}\n.okk-scope[data-okk=\"oeil\"] .teaser{position:relative;border-radius:28px;overflow:hidden;background:var(--paper);aspect-ratio:4\/3.2;box-shadow:0 40px 80px -30px rgba(46,107,255,.45),0 0 0 1px rgba(255,255,255,.08)}\n.okk-scope[data-okk=\"oeil\"] .teaser svg{position:absolute;inset:0;width:100%;height:100%}\n.okk-scope[data-okk=\"oeil\"] .tscore{position:absolute;right:16px;top:14px;font:800 30px var(--f-display);color:var(--pink);letter-spacing:-.04em}\n.okk-scope[data-okk=\"oeil\"] .tscore small{font:600 10px var(--f-mono);letter-spacing:.14em;color:#7A8597;display:block;text-align:right}\n.okk-scope[data-okk=\"oeil\"] .icons{display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:8px;margin-top:6px}\n.okk-scope[data-okk=\"oeil\"] .ico{display:flex;flex-direction:column;align-items:center;gap:8px;padding:14px 4px;border-radius:18px;border:1px solid var(--line);text-align:center}\n.okk-scope[data-okk=\"oeil\"] .ico svg{width:30px;height:30px}\n.okk-scope[data-okk=\"oeil\"] .ico span{font:500 10.5px var(--f-mono);color:var(--muted);letter-spacing:.02em}\n.okk-scope[data-okk=\"oeil\"] .game{max-width:680px;margin:6px auto 0;display:flex;flex-direction:column;gap:16px}\n.okk-scope[data-okk=\"oeil\"] .steps{display:grid;grid-template-columns:repeat(8,1fr);gap:5px}\n.okk-scope[data-okk=\"oeil\"] .steps i{height:5px;border-radius:5px;background:var(--line2);transition:background .3s}\n.okk-scope[data-okk=\"oeil\"] .steps i.done{background:var(--brand2)}\n.okk-scope[data-okk=\"oeil\"] .steps i.now{background:var(--ink)}\n.okk-scope[data-okk=\"oeil\"] .ghead{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;flex-wrap:wrap}\n.okk-scope[data-okk=\"oeil\"] .ghead .n{font:600 11px var(--f-mono);letter-spacing:.16em;color:var(--brand2);text-transform:uppercase}\n.okk-scope[data-okk=\"oeil\"] .ghead h2{font-size:clamp(24px,3.4vw,36px);font-weight:800;margin-top:6px}\n.okk-scope[data-okk=\"oeil\"] .ghead p{margin:6px 0 0;color:var(--muted);max-width:52ch}\n.okk-scope[data-okk=\"oeil\"] .total{text-align:right}\n.okk-scope[data-okk=\"oeil\"] .total b{font:800 26px var(--f-display);letter-spacing:-.04em;font-variant-numeric:tabular-nums}\n.okk-scope[data-okk=\"oeil\"] .total span{display:block;font:500 10px var(--f-mono);letter-spacing:.14em;color:var(--muted);text-transform:uppercase}\n.okk-scope[data-okk=\"oeil\"] .boardwrap{position:relative;border-radius:24px;overflow:hidden;background:var(--paper);box-shadow:0 30px 70px -30px rgba(0,0,0,.7),0 0 0 1px rgba(255,255,255,.08)}\n.okk-scope[data-okk=\"oeil\"] .board{display:block;width:100%;height:auto;aspect-ratio:4\/3;touch-action:none;user-select:none;-webkit-user-select:none;cursor:crosshair}\n.okk-scope[data-okk=\"oeil\"] .hint{position:absolute;left:50%;bottom:14px;transform:translateX(-50%);font:600 12px var(--f-body);color:#4A5466;background:rgba(255,255,255,.85);padding:6px 12px;border-radius:999px;pointer-events:none;white-space:nowrap;transition:opacity .3s}\n.okk-scope[data-okk=\"oeil\"] .timer{position:absolute;left:0;top:0;height:4px;background:var(--puser);width:0}\n.okk-scope[data-okk=\"oeil\"] .gfoot{display:flex;justify-content:space-between;align-items:center;gap:14px;min-height:56px;flex-wrap:wrap}\n.okk-scope[data-okk=\"oeil\"] .fb{display:flex;align-items:center;gap:16px;min-width:0}\n.okk-scope[data-okk=\"oeil\"] .fb .sc{font:800 40px var(--f-display);letter-spacing:-.05em;font-variant-numeric:tabular-nums;line-height:1}\n.okk-scope[data-okk=\"oeil\"] .fb .sc small{font-size:.4em;color:var(--muted)}\n.okk-scope[data-okk=\"oeil\"] .fb .tx b{display:block;font-weight:700;font-size:16px}\n.okk-scope[data-okk=\"oeil\"] .fb .tx span{color:var(--muted);font:500 12.5px var(--f-mono)}\n.okk-scope[data-okk=\"oeil\"] .pop{animation:pop .5s cubic-bezier(.3,1.6,.5,1)}\n@keyframes pop{from{transform:scale(.6);opacity:0}}\n.okk-scope[data-okk=\"oeil\"] .p-grid line{stroke:#E3E8EF;stroke-width:1}\n.okk-scope[data-okk=\"oeil\"] .p-shape{fill:var(--pshape);stroke:#A9B4C4;stroke-width:1.5}\n.okk-scope[data-okk=\"oeil\"] .p-ink{fill:none;stroke:var(--pink);stroke-width:3;stroke-linecap:round;stroke-linejoin:round}\n.okk-scope[data-okk=\"oeil\"] .p-ink-f{fill:var(--pink)}\n.okk-scope[data-okk=\"oeil\"] .p-user{fill:var(--puser)}\n.okk-scope[data-okk=\"oeil\"] .p-user-s{fill:none;stroke:var(--puser);stroke-width:3;stroke-linecap:round;stroke-linejoin:round}\n.okk-scope[data-okk=\"oeil\"] .p-good{fill:var(--pgood)}\n.okk-scope[data-okk=\"oeil\"] .p-good-s{fill:none;stroke:var(--pgood);stroke-width:2;stroke-dasharray:6 5;stroke-linecap:round}\n.okk-scope[data-okk=\"oeil\"] .p-err{stroke:var(--pbad);stroke-width:1.6;stroke-dasharray:3 3}\n.okk-scope[data-okk=\"oeil\"] .p-axis{stroke:#8C97A8;stroke-width:1.5;stroke-dasharray:8 6}\n.okk-scope[data-okk=\"oeil\"] .p-lab{font:600 12px var(--f-mono);fill:#4A5466}\n.okk-scope[data-okk=\"oeil\"] .p-lab.good{fill:var(--pgood)}\n.okk-scope[data-okk=\"oeil\"] .reveal{animation:rv .6s ease both}\n@keyframes rv{from{opacity:0}}\n.okk-scope[data-okk=\"oeil\"] .drawg{animation:dash 1s ease both}\n@keyframes dash{from{stroke-dashoffset:600}}\n.okk-scope[data-okk=\"oeil\"] .tile{cursor:pointer;transition:transform .15s}\n.okk-scope[data-okk=\"oeil\"] .tile:hover{opacity:.92}\n.okk-scope[data-okk=\"oeil\"] .res{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);gap:18px;align-items:start;margin-top:6px}\n.okk-scope[data-okk=\"oeil\"] .rcard{padding:clamp(22px,4vw,36px);display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px}\n.okk-scope[data-okk=\"oeil\"] .ring{position:relative;width:min(240px,62vw);aspect-ratio:1}\n.okk-scope[data-okk=\"oeil\"] .ring svg{width:100%;height:100%;transform:rotate(-90deg)}\n.okk-scope[data-okk=\"oeil\"] .ring .v{position:absolute;inset:0;display:grid;place-items:center;align-content:center}\n.okk-scope[data-okk=\"oeil\"] .ring .v b{font:800 clamp(54px,12vw,76px) var(--f-display);letter-spacing:-.05em;line-height:1;font-variant-numeric:tabular-nums}\n.okk-scope[data-okk=\"oeil\"] .ring .v span{font:500 11px var(--f-mono);letter-spacing:.14em;color:var(--muted)}\n.okk-scope[data-okk=\"oeil\"] .rank{font:800 clamp(22px,3vw,30px) var(--f-display);letter-spacing:-.03em}\n.okk-scope[data-okk=\"oeil\"] .rcard p{margin:0;color:var(--muted);max-width:40ch}\n.okk-scope[data-okk=\"oeil\"] .ract{display:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-top:6px}\n.okk-scope[data-okk=\"oeil\"] .bd{padding:22px;display:flex;flex-direction:column;gap:6px}\n.okk-scope[data-okk=\"oeil\"] .bd h3{font-size:17px;font-weight:700;margin-bottom:8px}\n.okk-scope[data-okk=\"oeil\"] .brow{display:grid;grid-template-columns:30px minmax(0,1fr) 44px;gap:12px;align-items:center;padding:8px 0;border-top:1px solid var(--line)}\n.okk-scope[data-okk=\"oeil\"] .brow svg{width:26px;height:26px}\n.okk-scope[data-okk=\"oeil\"] .brow .nm{font-size:14px;font-weight:500}\n.okk-scope[data-okk=\"oeil\"] .brow .br{height:6px;border-radius:6px;background:var(--line2);overflow:hidden;margin-top:6px}\n.okk-scope[data-okk=\"oeil\"] .brow .br i{display:block;height:100%;border-radius:6px;background:linear-gradient(90deg,var(--brand),var(--brand2));width:0;transition:width 1s cubic-bezier(.2,.8,.2,1)}\n.okk-scope[data-okk=\"oeil\"] .brow .pt{font:700 16px var(--f-body);text-align:right;font-variant-numeric:tabular-nums}\n.okk-scope[data-okk=\"oeil\"] .cta{margin-top:18px;padding:clamp(22px,4vw,34px);display:grid;grid-template-columns:minmax(0,1fr) auto;gap:20px;align-items:center;background:linear-gradient(140deg,rgba(46,107,255,.16),rgba(46,107,255,.02))}\n.okk-scope[data-okk=\"oeil\"] .cta h3{font-size:clamp(19px,2.4vw,26px);font-weight:700}\n.okk-scope[data-okk=\"oeil\"] .cta p{margin:8px 0 0;color:var(--muted);max-width:56ch}\n.okk-scope[data-okk=\"oeil\"] .foot{margin-top:28px;display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;font:500 12px var(--f-mono);color:var(--faint)}\n.okk-scope[data-okk=\"oeil\"] .toast{position:absolute;left:50%;bottom:22px;transform:translateX(-50%);padding:10px 16px;border-radius:999px;background:var(--ink);color:var(--bg);font-weight:600;font-size:13px;z-index:60;max-width:90%;text-align:center}\n@media (max-width:860px){.okk-scope[data-okk=\"oeil\"] .intro{grid-template-columns:1fr;gap:28px;padding-top:14px}\n.okk-scope[data-okk=\"oeil\"] .res{grid-template-columns:1fr}\n.okk-scope[data-okk=\"oeil\"] .cta{grid-template-columns:1fr}}\n@media (max-width:520px){.okk-scope[data-okk=\"oeil\"] .bar .pr{display:none}\n.okk-scope[data-okk=\"oeil\"] .fb .sc{font-size:32px}\n.okk-scope[data-okk=\"oeil\"] .btn{height:48px;padding:0 22px}\n.okk-scope[data-okk=\"oeil\"] .gfoot .btn{width:100%}}\n@media (prefers-reduced-motion:reduce){.okk-scope[data-okk=\"oeil\"] *{animation:none!important;transition:none!important}}\n\n\/* OK3D \u2014 fond pleine largeur, contenu centr\u00e9 et largeur ma\u00eetris\u00e9e.\n   M\u00eame cadre de 1600px et m\u00eames marges que le configurateur OK3D.\n   --okk-content-max r\u00e8gle la largeur maximale du cadre, marges comprises.\n   Le fond seul suit la largeur de l'\u00e9cran, m\u00eame sur un \u00e9cran tr\u00e8s large. *\/\n.okk-scope[data-okk=\"oeil\"] {\n  --okk-content-max: 1600px;\n  --okk-gutter: clamp(22px, 5vw, 88px);\n  --okk-header-offset: 94px;\n  --okk-top-gap: 54px;\n  position: relative;\n  left: var(--okk-shift, 0px);\n  width: var(--okk-width, 100%) !important;\n  max-width: none !important;\n  min-width: 0;\n  min-height: 100vh;\n  min-height: 100dvh;\n  margin: 0 !important;\n  padding: 0 !important;\n}\n.okk-scope[data-okk=\"oeil\"] .okk {\n  width: 100%;\n  overflow: clip;\n  min-height: 100vh;\n  min-height: 100dvh;\n  padding-left: 0;\n  padding-right: 0;\n  padding-top: calc(var(--okk-header-offset) + var(--okk-top-gap));\n  padding-bottom: max(28px, env(safe-area-inset-bottom, 0px));\n  scroll-margin-top: calc(var(--okk-header-offset) + var(--okk-admin-height, 0px) + 24px);\n}\n.okk-scope[data-okk=\"oeil\"] .wrap {\n  width: 100%;\n  max-width: var(--okk-content-max);\n  margin: 0 auto;\n  padding-left: max(var(--okk-gutter), env(safe-area-inset-left, 0px));\n  padding-right: max(var(--okk-gutter), env(safe-area-inset-right, 0px));\n}\n.okk-scope[data-okk=\"oeil\"] .intro { padding-top: 0; }\n.okk-scope[data-okk=\"oeil\"] .lab { grid-template-columns: minmax(0, 1fr) clamp(360px, 28%, 400px); }\n.okk-scope[data-okk=\"oeil\"] .lab > *, .okk-scope[data-okk=\"oeil\"] .intro > *, .okk-scope[data-okk=\"oeil\"] .studio > *,\n.okk-scope[data-okk=\"oeil\"] .explain > *, .okk-scope[data-okk=\"oeil\"] .rowc > *, .okk-scope[data-okk=\"oeil\"] .res > *,\n.okk-scope[data-okk=\"oeil\"] .cta > *, .okk-scope[data-okk=\"oeil\"] .ghead > *, .okk-scope[data-okk=\"oeil\"] .fb > * { min-width: 0; }\n.okk-scope[data-okk=\"oeil\"] .viewcol { top: calc(var(--okk-header-offset) + var(--okk-admin-height, 0px) + 12px); }\n.okk-scope[data-okk=\"oeil\"] .intro h1, .okk-scope[data-okk=\"oeil\"] .studio h3,\n.okk-scope[data-okk=\"oeil\"] .drop span, .okk-scope[data-okk=\"oeil\"] .planfoot { overflow-wrap: anywhere; }\n.okk-scope[data-okk=\"oeil\"] .rowc > .chips { flex: 1 1 0; }\n.okk-scope[data-okk=\"oeil\"] input[type=color] { flex: 0 0 34px; }\n.okk-scope[data-okk=\"oeil\"] .btn { max-width: 100%; height: auto; min-height: 44px; padding-block: 10px; white-space: normal; text-align: center; }\n.okk-scope[data-okk=\"oeil\"] .btn.sm { min-height: 34px; padding-block: 6px; }\n.okk-scope[data-okk=\"oeil\"] .explain .btn { white-space: normal; }\n.okk-scope[data-okk=\"oeil\"] .bar .screen-btn { margin-left: auto; }\n.okk-scope[data-okk=\"oeil\"] .game { width: 100%; max-width: 680px; margin-inline: auto; }\n.okk-scope[data-okk=\"oeil\"] .icons .ico span { overflow-wrap: anywhere; }\n.okk-scope[data-okk=\"oeil\"] .hint {\n  width: max-content;\n  max-width: calc(100% - 24px);\n  white-space: normal;\n  text-align: center;\n}\n.okk-scope[data-okk=\"oeil\"] .fb .tx { overflow-wrap: anywhere; }\n.okk-scope[data-okk=\"oeil\"] .shot { overflow-y: auto; }\n.okk-scope[data-okk=\"oeil\"] .shot img, .okk-scope[data-okk=\"oeil\"] .shot video { max-height: 60vh; max-height: 60dvh; object-fit: contain; }\n.okk-scope[data-okk=\"oeil\"]:fullscreen {\n  left: 0 !important;\n  width: 100% !important;\n  height: 100vh;\n  height: 100dvh;\n  overflow: auto;\n  overscroll-behavior: contain;\n  --okk-admin-height: 0px !important;\n  --okk-header-offset: 0px !important;\n  --okk-top-gap: 32px !important;\n}\n.okk-scope[data-okk=\"oeil\"]:-webkit-full-screen {\n  left: 0 !important;\n  width: 100% !important;\n  height: 100vh;\n  overflow: auto;\n  --okk-admin-height: 0px !important;\n  --okk-header-offset: 0px !important;\n  --okk-top-gap: 32px !important;\n}\n\/* Ces ajustements ne concernent que le widget contenant ce laboratoire. *\/\n.elementor-widget-html:has(.okk-scope[data-okk=\"oeil\"]) { width: 100%; min-width: 0; padding: 0; }\n.elementor-widget-container:has(> .okk-scope[data-okk=\"oeil\"]) { padding: 0; margin: 0; }\n@media (max-width: 1020px) {\n  .okk-scope[data-okk=\"oeil\"] .lab { grid-template-columns: minmax(0, 1fr); }\n  .okk-scope[data-okk=\"oeil\"] .viewcol { position: relative; top: 0; }\n}\n@media (max-width: 700px) {\n  .okk-scope[data-okk=\"oeil\"] { --okk-header-offset: 78px; --okk-top-gap: 43px; }\n}\n@media (max-width: 560px) {\n  .okk-scope[data-okk=\"oeil\"] .bar { gap: 10px; }\n  .okk-scope[data-okk=\"oeil\"] .bar > a.btn { flex-basis: 100%; }\n  .okk-scope[data-okk=\"oeil\"] .intro { padding-block: 0 22px; }\n  .okk-scope[data-okk=\"oeil\"] .intro h1 { font-size: clamp(26px, 7.6vw, 40px); }\n  .okk-scope[data-okk=\"oeil\"] .desk { padding: 12px; }\n  .okk-scope[data-okk=\"oeil\"] .planfoot { flex-wrap: wrap; }\n  .okk-scope[data-okk=\"oeil\"] .explain { padding: 16px; }\n  .okk-scope[data-okk=\"oeil\"] .stats { flex-wrap: wrap; }\n  .okk-scope[data-okk=\"oeil\"] .hud.tr { right: 12px; }\n  .okk-scope[data-okk=\"oeil\"] .hud.tl { left: 14px; top: auto; bottom: 54px; }\n}\n\n\/* Affichage app lorsque le plein \u00e9cran natif n'est pas disponible. *\/\n.okk-scope[data-okk=\"oeil\"].is-app-screen {\n  position: fixed !important; inset: 0 !important; left: 0 !important;\n  width: 100% !important; max-width: none !important; margin: 0 !important;\n  height: 100vh; height: 100dvh; overflow: auto; overscroll-behavior: contain;\n  z-index: 2147483646;\n}\n.okk-scope[data-okk=\"oeil\"]:fullscreen, .okk-scope[data-okk=\"oeil\"].is-app-screen {\n  --okk-header-offset: 0px !important; --okk-top-gap: 32px !important;\n  --okk-admin-height: 0px !important;\n  left: 0 !important; width: 100% !important; max-width: none !important; margin: 0 !important;\n  height: 100vh; height: 100dvh; overflow: auto; overscroll-behavior: contain;\n  \n}\n.okk-scope[data-okk=\"oeil\"]:-webkit-full-screen {\n  --okk-header-offset: 0px !important; --okk-top-gap: 32px !important; --okk-admin-height: 0px !important;\n  left: 0 !important; width: 100% !important; max-width: none !important; margin: 0 !important;\n  height: 100vh; overflow: auto;\n  \n}\n.okk-scope[data-okk=\"oeil\"] .appbar {\n  display: flex; justify-content: flex-end; align-items: center; gap: 12px;\n  flex-wrap: wrap; padding: 0 0 18px;\n}\n.okk-scope[data-okk=\"oeil\"] .screen-btn { margin-left: auto; flex: 0 0 auto; }\n.okk-scope[data-okk=\"oeil\"]:fullscreen .appbar, .okk-scope[data-okk=\"oeil\"].is-app-screen .appbar,\n.okk-scope[data-okk=\"oeil\"]:fullscreen .bar, .okk-scope[data-okk=\"oeil\"].is-app-screen .bar {\n  position: sticky; top: 0; z-index: 40; background: var(--bg, #06080c);\n  padding-block: 10px; margin-bottom: 12px;\n}\n\n\n\/* Conserver la taille des boutons du jeu et un titre \u00e0 l'\u00e9chelle du contenu. *\/\n.okk-scope[data-okk=\"oeil\"] .btn { min-height: 52px; }\n.okk-scope[data-okk=\"oeil\"] .btn.sm { min-height: 34px; font-size: 12.5px; padding-inline: 13px; }\n.okk-scope[data-okk=\"oeil\"] .intro h1 { font-size: clamp(34px, 5vw, 64px); line-height: 1.04; }\n.okk-scope[data-okk=\"oeil\"] .bar { justify-content: flex-end; gap: 12px; padding-block: 0 18px; }\n.okk-scope[data-okk=\"oeil\"] .bar .best { flex: 0 1 auto; max-width: 100%; }\n@media (max-width: 520px) { .okk-scope[data-okk=\"oeil\"] .btn:not(.sm) { min-height: 48px; } }\n\n<\/style>\n<div class=\"okk-scope\" data-okk=\"oeil\">\n<div class=\"okk\" id=\"okk\">\n  <div class=\"wrap\">\n    <div class=\"bar\"><button type=\"button\" class=\"btn gho sm screen-btn\" data-okk-fullscreen aria-pressed=\"false\" hidden>\u26f6 Plein \u00e9cran<\/button><span class=\"best\" id=\"best\">Ton record : <b>\u2014<\/b><\/span><\/div>\n    <main id=\"view\"><\/main>\n  <\/div>\n<\/div>\n<\/div>\n\n<script>\n(function(){\nconst OKK=document.querySelector('[data-okk=\"oeil\"]:not([data-okk-ready])');\nif(!OKK) return;\nOKK.setAttribute('data-okk-ready', 'true');\n\n\n\/* =====================================================================\n   CONFIG\n   ===================================================================== *\/\nconst CONFIG={ brand:\"OK3D\", logoUrl:\"\", siteUrl:\"https:\/\/www.ok3d.fr\", contactUrl:\"https:\/\/www.ok3d.fr\/#contact\", shareUrl:\"https:\/\/www.ok3d.fr\/oeil-de-designer\", storageKey:\"okk-oeil-v1\" };\n\nconst $=s=>OKK.querySelector(s);\nconst NS=\"http:\/\/www.w3.org\/2000\/svg\";\nconst RM=matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\nfunction el(t,a={},p){ const e=document.createElementNS(NS,t); for(const k in a) e.setAttribute(k,a[k]); if(p) p.appendChild(e); return e }\nconst rnd=(a,b)=>a+Math.random()*(b-a), clamp=(v,a,b)=>Math.max(a,Math.min(b,v)), dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);\nconst angDiff=(a,b)=>{ let d=((a-b)%360+540)%360-180; return Math.abs(d) };\nconst fmt1=n=>n.toFixed(1).replace(\".\",\",\");\nlet SAVE={best:0,plays:0}; try{ SAVE=Object.assign(SAVE,JSON.parse(localStorage.getItem(CONFIG.storageKey))||{}) }catch(e){}\nconst persist=()=>{ try{ localStorage.setItem(CONFIG.storageKey,JSON.stringify(SAVE)) }catch(e){} };\nfunction toast(t){ const d=document.createElement(\"div\"); d.className=\"toast\"; d.textContent=t; $(\"#okk\").appendChild(d); setTimeout(()=>d.remove(),2600) }\n\n\/* icons for each test *\/\nconst ICONS={\n  parallele:'<svg viewBox=\"0 0 30 30\"><path d=\"M4 20 L22 6\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"\/><path d=\"M8 25 L26 11\" stroke=\"#7CC4FF\" stroke-width=\"1.8\" stroke-linecap=\"round\"\/><\/svg>',\n  horizon:'<svg viewBox=\"0 0 30 30\"><rect x=\"3\" y=\"5\" width=\"24\" height=\"20\" rx=\"3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\"\/><path d=\"M3 17 L27 14\" stroke=\"#7CC4FF\" stroke-width=\"1.8\"\/><circle cx=\"20\" cy=\"11\" r=\"2.4\" fill=\"currentColor\"\/><\/svg>',\n  milieu:'<svg viewBox=\"0 0 30 30\"><path d=\"M4 22 L26 8\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"\/><circle cx=\"15\" cy=\"15\" r=\"2.6\" fill=\"#7CC4FF\"\/><\/svg>',\n  carre:'<svg viewBox=\"0 0 30 30\"><rect x=\"6\" y=\"6\" width=\"18\" height=\"18\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"\/><path d=\"M6 6 H24\" stroke=\"#7CC4FF\" stroke-width=\"2.4\"\/><\/svg>',\n  complementaire:'<svg viewBox=\"0 0 30 30\"><circle cx=\"10\" cy=\"15\" r=\"7\" fill=\"#3D7BFF\"\/><circle cx=\"20\" cy=\"15\" r=\"7\" fill=\"#FFB23D\"\/><\/svg>',\n  gris:'<svg viewBox=\"0 0 30 30\"><rect x=\"3\" y=\"6\" width=\"12\" height=\"18\" rx=\"2\" fill=\"#20242C\"\/><rect x=\"6\" y=\"11\" width=\"6\" height=\"8\" fill=\"#808080\"\/><rect x=\"15\" y=\"6\" width=\"12\" height=\"18\" rx=\"2\" fill=\"#E8EAEE\"\/><rect x=\"18\" y=\"11\" width=\"6\" height=\"8\" fill=\"#7A7A7A\"\/><\/svg>',\n  doree:'<svg viewBox=\"0 0 30 30\"><rect x=\"3\" y=\"9\" width=\"24\" height=\"12\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\"\/><path d=\"M17.8 9 V21\" stroke=\"#7CC4FF\" stroke-width=\"2\"\/><\/svg>',\n  arc:'<svg viewBox=\"0 0 30 30\"><path d=\"M5 20 A11 11 0 0 1 22 7\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"\/><circle cx=\"16\" cy=\"18\" r=\"2.4\" fill=\"#7CC4FF\"\/><\/svg>',\n  surface:'<svg viewBox=\"0 0 30 30\"><rect x=\"3\" y=\"15\" width=\"9\" height=\"9\" rx=\"1\" fill=\"currentColor\"\/><rect x=\"14\" y=\"10\" width=\"13\" height=\"14\" rx=\"1\" fill=\"none\" stroke=\"#7CC4FF\" stroke-width=\"1.8\"\/><\/svg>',\n  centre:'<svg viewBox=\"0 0 30 30\"><path d=\"M5 25 L15 4 L26 23 Z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"\/><circle cx=\"15.3\" cy=\"17.3\" r=\"2.4\" fill=\"#7CC4FF\"\/><\/svg>',\n  cercle:'<svg viewBox=\"0 0 30 30\"><path d=\"M15 4 C 23 4, 26.5 10, 26 16 C 25.5 23, 20 26.5, 14 26 C 7 25.5, 4 20, 4.2 14.5 C 4.5 9, 9 4.4, 16 4.6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"\/><circle cx=\"15\" cy=\"15\" r=\"1.6\" fill=\"#7CC4FF\"\/><\/svg>',\n  couleur:'<svg viewBox=\"0 0 30 30\"><rect x=\"3\" y=\"7\" width=\"11\" height=\"16\" rx=\"3\" fill=\"#3D7BFF\"\/><rect x=\"16\" y=\"7\" width=\"11\" height=\"16\" rx=\"3\" fill=\"#5B8CFF\"\/><\/svg>',\n  angle:'<svg viewBox=\"0 0 30 30\"><path d=\"M6 6 V24 H24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"\/><path d=\"M6 19 H11 V24\" fill=\"none\" stroke=\"#7CC4FF\" stroke-width=\"1.6\"\/><\/svg>',\n  espace:'<svg viewBox=\"0 0 30 30\"><rect x=\"3\" y=\"9\" width=\"5\" height=\"12\" rx=\"1\" fill=\"currentColor\"\/><rect x=\"12.5\" y=\"6\" width=\"5\" height=\"18\" rx=\"1\" fill=\"#7CC4FF\"\/><rect x=\"22\" y=\"11\" width=\"5\" height=\"8\" rx=\"1\" fill=\"currentColor\"\/><\/svg>',\n  symetrie:'<svg viewBox=\"0 0 30 30\"><path d=\"M15 3 V27\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-dasharray=\"3 3\"\/><circle cx=\"8\" cy=\"12\" r=\"3\" fill=\"currentColor\"\/><circle cx=\"22\" cy=\"12\" r=\"3\" fill=\"#7CC4FF\"\/><\/svg>',\n  fuite:'<svg viewBox=\"0 0 30 30\"><path d=\"M3 27 L15 9 M27 27 L15 9\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"\/><circle cx=\"15\" cy=\"9\" r=\"2.4\" fill=\"#7CC4FF\"\/><\/svg>',\n  nuance:'<svg viewBox=\"0 0 30 30\"><g fill=\"#3D7BFF\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"\/><rect x=\"11.5\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"\/><rect x=\"20\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"\/><rect x=\"3\" y=\"11.5\" width=\"7\" height=\"7\" rx=\"1.5\"\/><rect x=\"20\" y=\"11.5\" width=\"7\" height=\"7\" rx=\"1.5\"\/><rect x=\"3\" y=\"20\" width=\"7\" height=\"7\" rx=\"1.5\"\/><rect x=\"11.5\" y=\"20\" width=\"7\" height=\"7\" rx=\"1.5\"\/><rect x=\"20\" y=\"20\" width=\"7\" height=\"7\" rx=\"1.5\"\/><\/g><rect x=\"11.5\" y=\"11.5\" width=\"7\" height=\"7\" rx=\"1.5\" fill=\"#7CA2FF\"\/><\/svg>'\n};\n\n\/* =====================================================================\n   LES 8 \u00c9PREUVES \u2014 chacune : setup(board) \u2192 {ready, score(), reveal(), auto?}\n   ===================================================================== *\/\nfunction grid(s){ const g=el(\"g\",{class:\"p-grid\"},s); for(let x=0;x<=400;x+=25) el(\"line\",{x1:x,y1:0,x2:x,y2:300},g); for(let y=0;y<=300;y+=25) el(\"line\",{x1:0,y1:y,x2:400,y2:y},g) }\nfunction marker(s,p,cls=\"p-user\",r=7){ const g=el(\"g\",{},s); el(\"circle\",{cx:p.x,cy:p.y,r:r+5,fill:\"none\",stroke:cls===\"p-user\"?\"#2E6BFF\":\"#0FA968\",\"stroke-opacity\":.35,\"stroke-width\":1.5},g); el(\"circle\",{cx:p.x,cy:p.y,r,class:cls},g); return g }\nfunction label(s,x,y,t,cls=\"p-lab\",anchor=\"middle\"){ const e=el(\"text\",{x,y,\"text-anchor\":anchor,class:cls},s); e.textContent=t; return e }\nfunction onPoint(s,cb){ \/\/ pointer down + move while pressed\n  let down=false; const P=e=>{ const m=s.getScreenCTM().inverse(), p=s.createSVGPoint(); p.x=e.clientX; p.y=e.clientY; const q=p.matrixTransform(m); return {x:clamp(q.x,0,400),y:clamp(q.y,0,300)} };\n  s.onpointerdown=e=>{ down=true; s.setPointerCapture(e.pointerId); cb(P(e),\"down\") };\n  s.onpointermove=e=>{ if(down) cb(P(e),\"move\") };\n  s.onpointerup=s.onpointercancel=e=>{ if(down){ down=false; cb(P(e),\"up\") } };\n}\nconst TESTS=[\n {key:\"centre\",title:\"Le centre exact\",instr:\"Touche le centre de gravit\u00e9 de ce triangle.\",hint:\"Touche ou glisse pour placer le point\",\n  setup(s,api){ grid(s); const c0={x:200+rnd(-25,25),y:150+rnd(-15,15)}; let a=rnd(0,360); const V=[]; for(let i=0;i<3;i++){ const r=rnd(95,125); V.push({x:c0.x+r*Math.cos(a*Math.PI\/180),y:c0.y+r*Math.sin(a*Math.PI\/180)*.85}); a+=rnd(95,145) }\n    el(\"polygon\",{points:V.map(p=>p.x+\",\"+p.y).join(\" \"),class:\"p-shape\"},s); const G={x:(V[0].x+V[1].x+V[2].x)\/3,y:(V[0].y+V[1].y+V[2].y)\/3}; let u=null, mk=null;\n    onPoint(s,p=>{ u=p; if(mk) mk.remove(); mk=marker(s,p); api.ready() });\n    return { score:()=>Math.round(100*Math.exp(-dist(u,G)\/16)), detail:()=>`\u00c9cart ${Math.round(dist(u,G))} px`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); V.forEach((v,i)=>{ const o1=V[(i+1)%3], o2=V[(i+2)%3]; el(\"line\",{x1:v.x,y1:v.y,x2:(o1.x+o2.x)\/2,y2:(o1.y+o2.y)\/2,class:\"p-good-s\"},g) });\n        el(\"line\",{x1:u.x,y1:u.y,x2:G.x,y2:G.y,class:\"p-err\"},g); marker(g,G,\"p-good\",6); s.appendChild(mk) } } }},\n {key:\"cercle\",title:\"Le cercle parfait\",instr:\"Dessine un cercle, le plus rond possible, d'un seul geste.\",hint:\"Dessine sans lever le doigt\",auto:true,\n  setup(s,api){ grid(s); el(\"circle\",{cx:200,cy:150,r:3,fill:\"#A9B4C4\"},s); let pts=[], path=null, res=null;\n    onPoint(s,(p,t)=>{ if(res) return; if(t===\"down\"){ pts=[p]; if(path) path.remove(); path=el(\"path\",{class:\"p-user-s\"},s) } else pts.push(p);\n      path.setAttribute(\"d\",\"M\"+pts.map(q=>q.x.toFixed(1)+\" \"+q.y.toFixed(1)).join(\" L\"));\n      if(t===\"up\"){ if(pts.length<12){ api.hint(\"Dessine un cercle complet\"); return } const c={x:0,y:0}; pts.forEach(q=>{ c.x+=q.x; c.y+=q.y }); c.x\/=pts.length; c.y\/=pts.length;\n        const r=pts.map(q=>dist(q,c)), R=r.reduce((a,b)=>a+b,0)\/r.length, cv=Math.sqrt(r.reduce((a,b)=>a+(b-R)**2,0)\/r.length)\/R;\n        let sweep=0; for(let i=1;i<pts.length;i++){ let d=Math.atan2(pts[i].y-c.y,pts[i].x-c.x)-Math.atan2(pts[i-1].y-c.y,pts[i-1].x-c.x); if(d>Math.PI) d-=2*Math.PI; if(d<-Math.PI) d+=2*Math.PI; sweep+=d }\n        const cov=Math.min(1,Math.abs(sweep)\/(2*Math.PI)), gap=dist(pts[0],pts[pts.length-1])\/R;\n        if(R<28){ api.hint(\"Plus grand !\"); path.remove(); return } if(cov<.85){ api.hint(\"Ferme ton cercle\"); path.remove(); return }\n        res={c,R,cv,score:Math.round(100*Math.exp(-cv\/.075)*(gap>.35?.85:1))}; api.ready(); api.submit() } });\n    return { score:()=>res.score, detail:()=>`Rondeur ${fmt1(Math.max(0,100-res.cv*100))} %`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); el(\"circle\",{cx:res.c.x,cy:res.c.y,r:res.R,class:\"p-good-s\"},g); el(\"circle\",{cx:res.c.x,cy:res.c.y,r:3,class:\"p-good\"},g) } } }},\n {key:\"couleur\",title:\"La teinte exacte\",instr:\"Fais glisser le curseur pour que le carr\u00e9 de droite ait exactement la couleur de gauche.\",hint:\"Glisse sur la barre de couleurs\",\n  setup(s,api){ const H=Math.round(rnd(0,360)), S=rnd(60,78), L=rnd(48,58); let u=(H+rnd(110,250))%360; const id=\"hg\"+Math.random().toString(36).slice(2,7);\n    const lg=el(\"linearGradient\",{id},el(\"defs\",{},s)); for(let i=0;i<=12;i++) el(\"stop\",{offset:i\/12,\"stop-color\":`hsl(${i*30} ${S}% ${L}%)`},lg);\n    el(\"rect\",{x:30,y:30,width:160,height:160,rx:16,fill:`hsl(${H} ${S}% ${L}%)`},s); const me=el(\"rect\",{x:210,y:30,width:160,height:160,rx:16},s);\n    label(s,110,212,\"Cible\"); label(s,290,212,\"Toi\"); el(\"rect\",{x:30,y:236,width:340,height:34,rx:17,fill:`url(#${id})`},s);\n    const knob=el(\"g\",{},s); el(\"circle\",{cx:0,cy:253,r:15,fill:\"#fff\",stroke:\"#0E1116\",\"stroke-width\":2},knob); const kc=el(\"circle\",{cx:0,cy:253,r:9},knob);\n    const draw=()=>{ const col=`hsl(${u} ${S}% ${L}%)`; me.setAttribute(\"fill\",col); kc.setAttribute(\"fill\",col); knob.setAttribute(\"transform\",`translate(${30+u\/360*340} 0)`) }; draw();\n    let moved=false; onPoint(s,p=>{ u=clamp((p.x-30)\/340,0,1)*360; draw(); if(!moved){ moved=true; api.ready() } });\n    return { score:()=>Math.round(100*Math.exp(-angDiff(u,H)\/12)), detail:()=>`\u00c9cart ${Math.round(angDiff(u,H))}\u00b0`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); const x=30+H\/360*340; el(\"path\",{d:`M${x-8} 226 L${x+8} 226 L${x} 236 Z`,class:\"p-good\"},g); label(g,x,221,`${H}\u00b0`,\"p-lab good\") } } }},\n {key:\"angle\",title:\"L'angle droit\",instr:\"Fais pivoter la ligne bleue pour former un angle parfaitement droit.\",hint:\"Glisse pour faire tourner\",\n  setup(s,api){ grid(s); const J={x:150,y:215}, a0=rnd(-18,18), tgt=a0-90; let u=tgt+(Math.random()<.5?-1:1)*rnd(22,40); const L=165;\n    el(\"line\",{x1:J.x,y1:J.y,x2:J.x+210*Math.cos(a0*Math.PI\/180),y2:J.y+210*Math.sin(a0*Math.PI\/180),class:\"p-ink\"},s);\n    const ln=el(\"line\",{x1:J.x,y1:J.y,class:\"p-user-s\"},s); const hd=el(\"circle\",{r:9,class:\"p-user\"},s); el(\"circle\",{cx:J.x,cy:J.y,r:6,class:\"p-ink-f\"},s);\n    const draw=()=>{ const x=J.x+L*Math.cos(u*Math.PI\/180), y=J.y+L*Math.sin(u*Math.PI\/180); ln.setAttribute(\"x2\",x); ln.setAttribute(\"y2\",y); hd.setAttribute(\"cx\",x); hd.setAttribute(\"cy\",y) }; draw();\n    let moved=false; onPoint(s,p=>{ u=Math.atan2(p.y-J.y,p.x-J.x)*180\/Math.PI; draw(); if(!moved){ moved=true; api.ready() } });\n    return { score:()=>Math.round(100*Math.exp(-angDiff(u,tgt)\/3.5)), detail:()=>`\u00c9cart ${fmt1(angDiff(u,tgt))}\u00b0`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); const r=tgt*Math.PI\/180, b=a0*Math.PI\/180; el(\"line\",{x1:J.x,y1:J.y,x2:J.x+L*Math.cos(r),y2:J.y+L*Math.sin(r),class:\"p-good-s\"},g);\n        const q=22, p1={x:J.x+q*Math.cos(b),y:J.y+q*Math.sin(b)}, p2={x:J.x+q*Math.cos(r),y:J.y+q*Math.sin(r)}; el(\"path\",{d:`M${p1.x} ${p1.y} L${p1.x+p2.x-J.x} ${p1.y+p2.y-J.y} L${p2.x} ${p2.y}`,fill:\"none\",stroke:\"#0FA968\",\"stroke-width\":2},g) } } }},\n {key:\"espace\",title:\"L'espacement \u00e9gal\",instr:\"Place le bloc du milieu pour que les deux espaces soient identiques.\",hint:\"Glisse le bloc bleu\",\n  setup(s,api){ grid(s); const by=220; const w1=rnd(40,80), w2=rnd(30,70), w3=rnd(40,90), h1=rnd(70,130), h2=rnd(60,150), h3=rnd(50,120);\n    const x1=40, x3=360-w3, ideal=(x1+w1+x3-w2)\/2; let x=ideal+(Math.random()<.5?-1:1)*rnd(28,60);\n    el(\"rect\",{x:x1,y:by-h1,width:w1,height:h1,rx:4,class:\"p-ink-f\"},s); el(\"rect\",{x:x3,y:by-h3,width:w3,height:h3,rx:4,class:\"p-ink-f\"},s); el(\"line\",{x1:20,y1:by,x2:380,y2:by,stroke:\"#A9B4C4\",\"stroke-width\":1.5},s);\n    const m=el(\"rect\",{y:by-h2,width:w2,height:h2,rx:4,class:\"p-user\"},s); const draw=()=>m.setAttribute(\"x\",x); draw();\n    let off=null, moved=false; onPoint(s,(p,t)=>{ if(t===\"down\") off=(p.x>=x&&p.x<=x+w2)?p.x-x:w2\/2; x=clamp(p.x-off,x1+w1+2,x3-w2-2); draw(); if(!moved){ moved=true; api.ready() } });\n    return { score:()=>Math.round(100*Math.exp(-Math.abs(x-ideal)\/6)), detail:()=>`\u00c9cart ${fmt1(Math.abs(x-ideal))} px`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); el(\"rect\",{x:ideal,y:by-h2,width:w2,height:h2,rx:4,class:\"p-good-s\"},g); const gp=(ideal-(x1+w1));\n        [[x1+w1,ideal],[ideal+w2,x3]].forEach(([a,b])=>{ el(\"line\",{x1:a,y1:by+18,x2:b,y2:by+18,stroke:\"#0FA968\",\"stroke-width\":2},g); label(g,(a+b)\/2,by+38,`${Math.round(gp)} px`,\"p-lab good\") }) } } }},\n {key:\"symetrie\",title:\"Le miroir\",instr:\"Place le point bleu \u00e0 l'exact sym\u00e9trique du point noir, de l'autre c\u00f4t\u00e9 de l'axe.\",hint:\"Touche de l'autre c\u00f4t\u00e9 de l'axe\",\n  setup(s,api){ grid(s); const ang=[90,90,70,110,60,120][Math.random()*6|0]*Math.PI\/180, C={x:200,y:150}, d={x:Math.cos(ang),y:Math.sin(ang)};\n    el(\"line\",{x1:C.x-d.x*240,y1:C.y-d.y*240,x2:C.x+d.x*240,y2:C.y+d.y*240,class:\"p-axis\"},s);\n    const n={x:-d.y,y:d.x}, side=Math.random()<.5?1:-1, along=rnd(-80,80), off=rnd(55,110)*side; const P={x:C.x+d.x*along+n.x*off,y:C.y+d.y*along+n.y*off};\n    const Q={x:C.x+d.x*along-n.x*off,y:C.y+d.y*along-n.y*off}; el(\"circle\",{cx:P.x,cy:P.y,r:9,class:\"p-ink-f\"},s); let u=null, mk=null;\n    onPoint(s,p=>{ u=p; if(mk) mk.remove(); mk=marker(s,p); api.ready() });\n    return { score:()=>Math.round(100*Math.exp(-dist(u,Q)\/12)), detail:()=>`\u00c9cart ${Math.round(dist(u,Q))} px`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); el(\"line\",{x1:P.x,y1:P.y,x2:Q.x,y2:Q.y,class:\"p-good-s\"},g); el(\"line\",{x1:u.x,y1:u.y,x2:Q.x,y2:Q.y,class:\"p-err\"},g); marker(g,Q,\"p-good\",7); s.appendChild(mk) } } }},\n {key:\"fuite\",title:\"Le point de fuite\",instr:\"Ces lignes partent vers un m\u00eame point. Touche l'endroit o\u00f9 elles se rejoignent.\",hint:\"Touche le point de fuite\",\n  setup(s,api){ const V={x:rnd(120,280),y:rnd(55,120)}; el(\"rect\",{x:0,y:V.y,width:400,height:300-V.y,fill:\"#E6EBF2\"},s); el(\"line\",{x1:0,y1:V.y,x2:400,y2:V.y,stroke:\"#C3CCD8\",\"stroke-width\":1},s);\n    const starts=[{x:rnd(-40,40),y:300},{x:rnd(360,440),y:300},{x:rnd(-10,30),y:rnd(170,240)},{x:rnd(370,410),y:rnd(180,240)}];\n    const segs=starts.map(p=>{ const f=rnd(.38,.52); return [p,{x:p.x+(V.x-p.x)*f,y:p.y+(V.y-p.y)*f}] });\n    segs.forEach(([a,b],i)=>el(\"line\",{x1:a.x,y1:a.y,x2:b.x,y2:b.y,class:\"p-ink\",\"stroke-width\":i<2?4:2.5},s));\n    let u=null, mk=null; onPoint(s,p=>{ u=p; if(mk) mk.remove(); mk=marker(s,p); api.ready() });\n    return { score:()=>Math.round(100*Math.exp(-dist(u,V)\/15)), detail:()=>`\u00c9cart ${Math.round(dist(u,V))} px`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); segs.forEach(([a,b])=>el(\"line\",{x1:b.x,y1:b.y,x2:V.x,y2:V.y,class:\"p-good-s\"},g)); el(\"line\",{x1:u.x,y1:u.y,x2:V.x,y2:V.y,class:\"p-err\"},g); marker(g,V,\"p-good\",6); s.appendChild(mk) } } }},\n {key:\"parallele\",title:\"Les parall\u00e8les\",instr:\"Fais pivoter la ligne bleue pour qu'elle soit parfaitement parall\u00e8le \u00e0 la ligne noire.\",hint:\"Glisse pour faire tourner\",\n  setup(s,api){ const a0=rnd(-55,55), J={x:200,y:205}, L=140; let u=a0+(Math.random()<.5?-1:1)*rnd(14,30);\n    const c={x:200+rnd(-40,40),y:85}; el(\"line\",{x1:c.x-150*Math.cos(a0*Math.PI\/180),y1:c.y-150*Math.sin(a0*Math.PI\/180),x2:c.x+150*Math.cos(a0*Math.PI\/180),y2:c.y+150*Math.sin(a0*Math.PI\/180),class:\"p-ink\"},s);\n    const ln=el(\"line\",{class:\"p-user-s\"},s), h1=el(\"circle\",{r:8,class:\"p-user\"},s); el(\"circle\",{cx:J.x,cy:J.y,r:5,class:\"p-ink-f\"},s);\n    const draw=()=>{ const dx=L*Math.cos(u*Math.PI\/180), dy=L*Math.sin(u*Math.PI\/180); ln.setAttribute(\"x1\",J.x-dx); ln.setAttribute(\"y1\",J.y-dy); ln.setAttribute(\"x2\",J.x+dx); ln.setAttribute(\"y2\",J.y+dy); h1.setAttribute(\"cx\",J.x+dx); h1.setAttribute(\"cy\",J.y+dy) }; draw();\n    let moved=false; onPoint(s,p=>{ let a=Math.atan2(p.y-J.y,p.x-J.x)*180\/Math.PI; if(angDiff(a,u)>90) a+=180; u=a; draw(); if(!moved){ moved=true; api.ready() } });\n    const err=()=>Math.min(angDiff(u,a0),angDiff(u,a0+180));\n    return { score:()=>Math.round(100*Math.exp(-err()\/2.6)), detail:()=>`\u00c9cart ${fmt1(err())}\u00b0`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); const dx=L*Math.cos(a0*Math.PI\/180), dy=L*Math.sin(a0*Math.PI\/180); el(\"line\",{x1:J.x-dx,y1:J.y-dy,x2:J.x+dx,y2:J.y+dy,class:\"p-good-s\"},g) } } }},\n {key:\"horizon\",title:\"L'horizon droit\",instr:\"La photo penche. Fais-la tourner jusqu'\u00e0 ce que l'horizon soit parfaitement droit.\",hint:\"Glisse \u00e0 gauche ou \u00e0 droite\",\n  setup(s,api){ const id=\"hc\"+Math.random().toString(36).slice(2,7); el(\"rect\",{x:0,y:0,width:400,height:300},el(\"clipPath\",{id},el(\"defs\",{},s)));\n    const wrap=el(\"g\",{\"clip-path\":`url(#${id})`},s), g=el(\"g\",{},wrap); const hy=rnd(130,175);\n    el(\"rect\",{x:-300,y:-300,width:1000,height:300+hy,fill:\"#9CC9EA\"},g); el(\"rect\",{x:-300,y:hy,width:1000,height:700,fill:\"#2F7FB0\"},g);\n    el(\"circle\",{cx:rnd(90,310),cy:hy-rnd(40,80),r:22,fill:\"#FFD27A\"},g); for(let i=0;i<5;i++) el(\"rect\",{x:rnd(-100,450),y:hy+rnd(15,120),width:rnd(30,90),height:3,rx:1.5,fill:\"#ffffff\",\"fill-opacity\":.45},g);\n    el(\"path\",{d:`M-300 ${hy} L${rnd(20,90)} ${hy-rnd(25,45)} L${rnd(120,170)} ${hy} Z`,fill:\"#2B4A3A\"},g);\n    let ang=(Math.random()<.5?-1:1)*rnd(4,11); const draw=()=>g.setAttribute(\"transform\",`rotate(${ang} 200 150)`); draw();\n    let x0=0,a0=0,moved=false; onPoint(s,(p,t)=>{ if(t===\"down\"){ x0=p.x; a0=ang; return } ang=clamp(a0+(p.x-x0)*.08,-25,25); draw(); if(!moved){ moved=true; api.ready() } });\n    return { score:()=>Math.round(100*Math.exp(-Math.abs(ang)\/1.1)), detail:()=>`Inclinaison ${fmt1(Math.abs(ang))}\u00b0`,\n      reveal(){ const r=el(\"g\",{class:\"reveal\"},s); const yy=150+(hy-150)\/Math.cos(ang*Math.PI\/180); el(\"line\",{x1:0,y1:yy,x2:400,y2:yy,class:\"p-good-s\"},r); label(r,200,yy-10,\"niveau\",\"p-lab good\") } } }},\n {key:\"milieu\",title:\"Le milieu\",instr:\"Touche le milieu exact de ce segment.\",hint:\"Touche le milieu\",\n  setup(s,api){ const a={x:rnd(30,110),y:rnd(40,260)}, b={x:rnd(290,370),y:rnd(40,260)}; el(\"line\",{x1:a.x,y1:a.y,x2:b.x,y2:b.y,class:\"p-ink\"},s); [a,b].forEach(p=>el(\"circle\",{cx:p.x,cy:p.y,r:5,class:\"p-ink-f\"},s));\n    const M={x:(a.x+b.x)\/2,y:(a.y+b.y)\/2}; let u=null, mk=null; onPoint(s,p=>{ u=p; if(mk) mk.remove(); mk=marker(s,p); api.ready() });\n    return { score:()=>Math.round(100*Math.exp(-dist(u,M)\/6)), detail:()=>`\u00c9cart ${Math.round(dist(u,M))} px`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); const n={x:-(b.y-a.y),y:b.x-a.x}, l=Math.hypot(n.x,n.y); el(\"line\",{x1:M.x-n.x\/l*16,y1:M.y-n.y\/l*16,x2:M.x+n.x\/l*16,y2:M.y+n.y\/l*16,stroke:\"#0FA968\",\"stroke-width\":3},g); marker(g,M,\"p-good\",5); s.appendChild(mk) } } }},\n {key:\"carre\",title:\"Le carr\u00e9 parfait\",instr:\"Tire le bord du haut pour transformer ce rectangle en carr\u00e9 parfait.\",hint:\"Glisse le bord bleu\",\n  setup(s,api){ const W=rnd(110,190), x=200-W\/2, base=270; let h=W+(Math.random()<.5?-1:1)*rnd(30,60); h=clamp(h,40,250);\n    const r=el(\"rect\",{x,width:W,class:\"p-shape\"},s); const top=el(\"line\",{x1:x,x2:x+W,class:\"p-user-s\",\"stroke-width\":5},s);\n    const draw=()=>{ r.setAttribute(\"y\",base-h); r.setAttribute(\"height\",h); top.setAttribute(\"y1\",base-h); top.setAttribute(\"y2\",base-h) }; draw();\n    let moved=false; onPoint(s,p=>{ h=clamp(base-p.y,30,260); draw(); if(!moved){ moved=true; api.ready() } });\n    return { score:()=>Math.round(100*Math.exp(-Math.abs(h-W)\/4.5)), detail:()=>`\u00c9cart ${fmt1(Math.abs(h-W))} px`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); el(\"rect\",{x,y:base-W,width:W,height:W,class:\"p-good-s\"},g); el(\"line\",{x1:x,y1:base,x2:x+W,y2:base-W,class:\"p-good-s\"},g) } } }},\n {key:\"complementaire\",title:\"La compl\u00e9mentaire\",instr:\"Trouve la couleur oppos\u00e9e \u00e0 celle de gauche sur le cercle chromatique.\",hint:\"Glisse sur la barre de couleurs\",\n  setup(s,api){ const H=Math.round(rnd(0,360)), S=72, L=55, T=(H+180)%360; let u=(H+rnd(40,100))%360; const id=\"hc\"+Math.random().toString(36).slice(2,7);\n    const lg=el(\"linearGradient\",{id},el(\"defs\",{},s)); for(let i=0;i<=12;i++) el(\"stop\",{offset:i\/12,\"stop-color\":`hsl(${i*30} ${S}% ${L}%)`},lg);\n    el(\"rect\",{x:30,y:22,width:160,height:140,rx:16,fill:`hsl(${H} ${S}% ${L}%)`},s); const me=el(\"rect\",{x:210,y:22,width:160,height:140,rx:16},s);\n    label(s,110,184,\"Couleur\"); label(s,290,184,\"Son oppos\u00e9e\"); el(\"rect\",{x:30,y:236,width:340,height:34,rx:17,fill:`url(#${id})`},s);\n    const knob=el(\"g\",{},s); el(\"circle\",{cx:0,cy:253,r:15,fill:\"#fff\",stroke:\"#0E1116\",\"stroke-width\":2},knob); const kc=el(\"circle\",{cx:0,cy:253,r:9},knob);\n    const draw=()=>{ const col=`hsl(${u} ${S}% ${L}%)`; me.setAttribute(\"fill\",col); kc.setAttribute(\"fill\",col); knob.setAttribute(\"transform\",`translate(${30+u\/360*340} 0)`) }; draw();\n    let moved=false; onPoint(s,p=>{ u=clamp((p.x-30)\/340,0,1)*360; draw(); if(!moved){ moved=true; api.ready() } });\n    return { score:()=>Math.round(100*Math.exp(-angDiff(u,T)\/14)), detail:()=>`\u00c9cart ${Math.round(angDiff(u,T))}\u00b0`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); const x=30+T\/360*340; el(\"path\",{d:`M${x-8} 226 L${x+8} 226 L${x} 236 Z`,class:\"p-good\"},g); label(g,x,221,`${Math.round(T)}\u00b0`,\"p-lab good\") } } }},\n {key:\"gris\",title:\"Le gris trompeur\",instr:\"R\u00e8gle le gris de droite pour qu'il soit identique \u00e0 celui de gauche. Attention, le fond trompe l'\u0153il.\",hint:\"Glisse sur la barre\",\n  setup(s,api){ const T=Math.round(rnd(30,70)); const dark=Math.random()<.5; let u=clamp(T+(Math.random()<.5?-1:1)*rnd(18,30),5,95); const id=\"gg\"+Math.random().toString(36).slice(2,7);\n    el(\"rect\",{x:20,y:20,width:175,height:190,rx:14,fill:dark?\"#14171D\":\"#F4F6F9\",stroke:\"#C9D1DC\"},s); el(\"rect\",{x:205,y:20,width:175,height:190,rx:14,fill:dark?\"#F4F6F9\":\"#14171D\",stroke:\"#C9D1DC\"},s);\n    el(\"rect\",{x:62,y:70,width:90,height:90,rx:8,fill:`hsl(0 0% ${T}%)`},s); const me=el(\"rect\",{x:247,y:70,width:90,height:90,rx:8},s);\n    const lg=el(\"linearGradient\",{id},el(\"defs\",{},s)); el(\"stop\",{offset:0,\"stop-color\":\"#000\"},lg); el(\"stop\",{offset:1,\"stop-color\":\"#fff\"},lg); el(\"rect\",{x:30,y:236,width:340,height:34,rx:17,fill:`url(#${id})`,stroke:\"#C9D1DC\"},s);\n    const knob=el(\"circle\",{cy:253,r:14,fill:\"none\",stroke:\"#2E6BFF\",\"stroke-width\":4},s);\n    const draw=()=>{ me.setAttribute(\"fill\",`hsl(0 0% ${u}%)`); knob.setAttribute(\"cx\",30+u\/100*340) }; draw();\n    let moved=false; onPoint(s,p=>{ u=clamp((p.x-30)\/340,0,1)*100; draw(); if(!moved){ moved=true; api.ready() } });\n    return { score:()=>Math.round(100*Math.exp(-Math.abs(u-T)\/3.5)), detail:()=>`\u00c9cart ${fmt1(Math.abs(u-T))} %`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); el(\"rect\",{x:150,y:100,width:100,height:30,rx:6,fill:`hsl(0 0% ${T}%)`,stroke:\"#0FA968\",\"stroke-width\":2},g); const x=30+T\/100*340; el(\"path\",{d:`M${x-8} 226 L${x+8} 226 L${x} 236 Z`,class:\"p-good\"},g) } } }},\n {key:\"doree\",title:\"Le nombre d'or\",instr:\"Coupe le rectangle pour que la grande partie fasse 1,618 fois la petite. La grande partie est \u00e0 gauche.\",hint:\"Glisse la ligne bleue\",\n  setup(s,api){ const x0=rnd(20,50), W=rnd(300,340), y=rnd(80,120), H=rnd(90,130); const T=x0+W\/1.618; let u=x0+W*rnd(.35,.5);\n    el(\"rect\",{x:x0,y,width:W,height:H,class:\"p-shape\"},s); const ln=el(\"line\",{y1:y-14,y2:y+H+14,class:\"p-user-s\",\"stroke-width\":4},s); const draw=()=>{ ln.setAttribute(\"x1\",u); ln.setAttribute(\"x2\",u) }; draw();\n    let moved=false; onPoint(s,p=>{ u=clamp(p.x,x0+10,x0+W-10); draw(); if(!moved){ moved=true; api.ready() } });\n    return { score:()=>Math.round(100*Math.exp(-Math.abs(u-T)\/5)), detail:()=>`Ratio ${fmt1((u-x0)\/Math.max(1,x0+W-u))} au lieu de 1,6`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); el(\"line\",{x1:T,y1:y-14,x2:T,y2:y+H+14,class:\"p-good-s\"},g); label(g,(x0+T)\/2,y+H+36,\"1,618\",\"p-lab good\"); label(g,(T+x0+W)\/2,y+H+36,\"1\",\"p-lab good\") } } }},\n {key:\"arc\",title:\"Le centre de l'arc\",instr:\"Cet arc appartient \u00e0 un cercle. Touche le centre de ce cercle.\",hint:\"Touche le centre\",\n  setup(s,api){ grid(s); const R=rnd(95,130), C={x:rnd(150,250),y:rnd(140,175)}, a1=rnd(0,360), span=rnd(70,120); const p=a=>({x:C.x+R*Math.cos(a*Math.PI\/180),y:C.y+R*Math.sin(a*Math.PI\/180)});\n    const A=p(a1), B=p(a1+span); el(\"path\",{d:`M${A.x} ${A.y} A${R} ${R} 0 0 1 ${B.x} ${B.y}`,class:\"p-ink\"},s);\n    let u=null, mk=null; onPoint(s,q=>{ u=q; if(mk) mk.remove(); mk=marker(s,q); api.ready() });\n    return { score:()=>Math.round(100*Math.exp(-dist(u,C)\/14)), detail:()=>`\u00c9cart ${Math.round(dist(u,C))} px`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); el(\"circle\",{cx:C.x,cy:C.y,r:R,class:\"p-good-s\"},g); el(\"line\",{x1:u.x,y1:u.y,x2:C.x,y2:C.y,class:\"p-err\"},g); marker(g,C,\"p-good\",6); s.appendChild(mk) } } }},\n {key:\"surface\",title:\"Double surface\",instr:\"Agrandis le carr\u00e9 bleu pour que sa surface soit exactement le double de celle du carr\u00e9 noir.\",hint:\"Glisse le coin bleu\",\n  setup(s,api){ const a=rnd(60,85), base=250, T=a*Math.SQRT2; let u=a*rnd(1.05,1.2);\n    el(\"rect\",{x:40,y:base-a,width:a,height:a,class:\"p-ink-f\"},s); label(s,40+a\/2,base+22,\"1\u00d7\"); const r=el(\"rect\",{x:170,class:\"p-user\"},s); const hd=el(\"circle\",{r:9,fill:\"#fff\",stroke:\"#2E6BFF\",\"stroke-width\":3},s); label(s,250,base+22,\"2\u00d7 ?\");\n    const draw=()=>{ r.setAttribute(\"y\",base-u); r.setAttribute(\"width\",u); r.setAttribute(\"height\",u); hd.setAttribute(\"cx\",170+u); hd.setAttribute(\"cy\",base-u) }; draw();\n    let moved=false; onPoint(s,p=>{ u=clamp(Math.max(p.x-170,base-p.y),30,215); draw(); if(!moved){ moved=true; api.ready() } });\n    return { score:()=>Math.round(100*Math.exp(-Math.abs(u-T)\/T\/.035)), detail:()=>`Surface ${fmt1((u*u)\/(a*a))}\u00d7 au lieu de 2\u00d7`,\n      reveal(){ const g=el(\"g\",{class:\"reveal\"},s); el(\"rect\",{x:170,y:base-T,width:T,height:T,class:\"p-good-s\"},g); label(g,170+T\/2,base-T-10,\"\u00d7\u221a2 de c\u00f4t\u00e9\",\"p-lab good\") } } }},\n {key:\"nuance\",title:\"La nuance cach\u00e9e\",instr:\"Une case est tr\u00e8s l\u00e9g\u00e8rement plus claire. Trouve-la, trois fois de suite, avant la fin du temps.\",hint:\"Touche la case diff\u00e9rente\",auto:true,timed:true,\n  setup(s,api){ const H=rnd(0,360), S=rnd(45,70), L=rnd(40,52); const levels=[[5,9],[6,5.5],[7,3.2]]; let lv=0, got=0, found=0, res=null; const pts=[30,35,35];\n    const play=()=>{ while(s.lastChild) s.removeChild(s.lastChild); el(\"rect\",{width:400,height:300,fill:\"#F3F5F8\"},s);\n      const [n,dl]=levels[lv]; const size=250, gap=6, cs=(size-gap*(n-1))\/n, ox=75, oy=25, odd=Math.random()*n*n|0;\n      for(let i=0;i<n*n;i++){ const r=el(\"rect\",{x:ox+(i%n)*(cs+gap),y:oy+Math.floor(i\/n)*(cs+gap),width:cs,height:cs,rx:5,class:\"tile\",fill:`hsl(${H} ${S}% ${i===odd?L+dl:L}%)`},s);\n        r.onpointerdown=e=>{ e.stopPropagation(); if(res) return; if(i===odd){ got+=pts[lv]; found++; r.setAttribute(\"stroke\",\"#0FA968\"); r.setAttribute(\"stroke-width\",4) } else { r.setAttribute(\"stroke\",\"#F0533A\"); r.setAttribute(\"stroke-width\",4); const ok=s.querySelectorAll(\".tile\")[odd]; ok.setAttribute(\"stroke\",\"#0FA968\"); ok.setAttribute(\"stroke-width\",4) }\n          setTimeout(()=>{ lv++; if(lv<levels.length) play(); else finish() },i===odd?350:900) } }\n      label(s,200,293,`Grille ${lv+1} \/ 3`) };\n    const finish=()=>{ if(res) return; res={score:got}; api.stopTimer(); api.ready(); api.submit() };\n    play(); api.startTimer(14,()=>{ lv=levels.length; finish() });\n    return { score:()=>res.score, detail:()=>`${found} grille${found>1?\"s\":\"\"} trouv\u00e9e${found>1?\"s\":\"\"} sur 3`, reveal(){} } }},\n];\n\n\/* =====================================================================\n   FLOW\n   ===================================================================== *\/\nconst view=$(\"#view\");\nlet G=null;\nconst verdict=sc=>sc>=95?\"Pixel perfect.\":sc>=85?\"\u0152il aff\u00fbt\u00e9.\":sc>=70?\"Tr\u00e8s bien vu.\":sc>=50?\"Pas mal du tout.\":sc>=25?\"Presque.\":\"Rat\u00e9, mais tu progresses.\";\nconst rank=sc=>sc>=90?[\"Directeur\u00b7rice artistique\",\"Ton \u0153il rep\u00e8re ce que la plupart des gens ne voient pas.\"]:sc>=80?[\"Designer confirm\u00e9\u00b7e\",\"Pr\u00e9cis, rapide, s\u00fbr de toi. Le m\u00e9tier est dans le regard.\"]\n  :sc>=65?[\"\u0152il prometteur\",\"Tu sens les d\u00e9s\u00e9quilibres. Encore quelques parties et tu les corriges les yeux ferm\u00e9s.\"]:sc>=50?[\"Amateur \u00e9clair\u00e9\",\"Bonne intuition. Les d\u00e9tails feront la diff\u00e9rence.\"]:[\"\u0152il en rodage\",\"Rejoue : chaque \u00e9preuve montre la bonne r\u00e9ponse, l'\u0153il s'entra\u00eene vite.\"];\nfunction updateBest(){ $(\"#best\").innerHTML=`Ton record : <b>${SAVE.best?SAVE.best+\" \/ 100\":\"\u2014\"}<\/b>`;  }\nfunction toTop(){ const r=$(\"#okk\").getBoundingClientRect(); if(r.top<0) $(\"#okk\").scrollIntoView({block:\"start\"}) }\n\nfunction intro(){\n  G=null;\n  view.innerHTML=`<section class=\"intro\">\n    <div><span class=\"eyebrow\">Un jeu OK3D<\/span><h1>As-tu l'\u0153il<br>d'un <span class=\"grad\">designer<\/span> ?<\/h1>\n      <p>Huit \u00e9preuves tir\u00e9es au hasard parmi 17 : centre, cercle parfait, couleur, angle droit, nombre d'or, horizon, gris trompeur\u2026 Chaque partie est diff\u00e9rente. Aucune connaissance requise, juste tes yeux.<\/p>\n      <div class=\"meta\"><span>8 \u00e9preuves sur 17<\/span><span>2 minutes<\/span><span>Jamais la m\u00eame partie<\/span><\/div>\n      <div class=\"gorow\"><button type=\"button\" class=\"btn pri\" id=\"go\">Commencer le test <kbd class=\"kbonly\">Entr\u00e9e<\/kbd><\/button><\/div><\/div>\n    <div class=\"teaser\" aria-hidden=\"true\"><svg viewBox=\"0 0 400 320\" id=\"tsv\"><\/svg><div class=\"tscore\"><span id=\"tsc\">0<\/span><small>RONDEUR<\/small><\/div><\/div>\n  <\/section>\n  <div class=\"icons\">${TESTS.map(t=>`<div class=\"ico\">${ICONS[t.key]}<span>${t.title.replace(\/^(Le |La |L')\/,\"\")}<\/span><\/div>`).join(\"\")}<\/div>`;\n  $(\"#go\").onclick=start; teaser();\n}\nlet teaseGen=0;\nasync function teaser(){ const my=++teaseGen, s=$(\"#tsv\"); if(!s) return; const g=el(\"g\",{class:\"p-grid\"},s); for(let x=0;x<=400;x+=25) el(\"line\",{x1:x,y1:0,x2:x,y2:320},g); for(let y=0;y<=320;y+=25) el(\"line\",{x1:0,y1:y,x2:400,y2:y},g);\n  const path=el(\"path\",{class:\"p-user-s\",\"stroke-width\":4},s), ghost=el(\"circle\",{cx:200,cy:165,r:105,class:\"p-good-s\",opacity:0},s); const tsc=$(\"#tsc\"); const wait=ms=>new Promise(r=>setTimeout(r,ms));\n  while(my===teaseGen&&document.body.contains(s)){\n    const k=[rnd(-.04,.04),rnd(-.04,.04),rnd(2,4)]; const pts=[]; ghost.setAttribute(\"opacity\",0); tsc.textContent=\"0\";\n    for(let i=0;i<=72;i++){ if(my!==teaseGen) return; const a=-Math.PI\/2+i\/72*2*Math.PI*1.01, r=105*(1+k[0]*Math.sin(a*k[2])+k[1]*Math.cos(a*2)); pts.push([200+r*Math.cos(a),165+r*Math.sin(a)]);\n      path.setAttribute(\"d\",\"M\"+pts.map(p=>p[0].toFixed(1)+\" \"+p[1].toFixed(1)).join(\" L\")); if(!RM) await wait(16) }\n    const sc=Math.round(rnd(88,98)); for(let v=0;v<=sc;v+=3){ tsc.textContent=Math.min(v,sc); await wait(14) } tsc.textContent=sc; ghost.setAttribute(\"opacity\",1); ghost.classList.add(\"drawg\"); await wait(2200) }\n}\nfunction pickTests(){ const last=(()=>{ try{ return JSON.parse(localStorage.getItem(CONFIG.storageKey+\"-last\"))||[] }catch(e){ return [] } })();\n  const w=TESTS.map(t=>({t,r:Math.random()+(last.includes(t.key)?.55:0)})).sort((a,b)=>a.r-b.r).slice(0,8).map(o=>o.t);\n  for(let i=w.length-1;i>0;i--){ const j=Math.random()*(i+1)|0; [w[i],w[j]]=[w[j],w[i]] } try{ localStorage.setItem(CONFIG.storageKey+\"-last\",JSON.stringify(w.map(t=>t.key))) }catch(e){} return w }\nfunction start(){ teaseGen++; G={order:pickTests(),i:0,scores:[],details:[]}; round(); toTop() }\nfunction round(){\n  const t=G.order[G.i], total=G.scores.reduce((a,b)=>a+b,0);\n  view.innerHTML=`<section class=\"game\">\n    <div class=\"steps\">${G.order.map((_,i)=>`<i class=\"${i<G.i?\"done\":i===G.i?\"now\":\"\"}\"><\/i>`).join(\"\")}<\/div>\n    <div class=\"ghead\"><div><span class=\"n\">\u00c9preuve ${String(G.i+1).padStart(2,\"0\")} \/ 08<\/span><h2>${t.title}<\/h2><p>${t.instr}<\/p><\/div><div class=\"total\"><b id=\"tot\">${total}<\/b><span>points<\/span><\/div><\/div>\n    <div class=\"boardwrap\"><svg class=\"board\" id=\"board\" viewBox=\"0 0 400 300\" aria-label=\"${t.title}\"><\/svg><div class=\"timer\" id=\"timer\"><\/div><div class=\"hint\" id=\"hint\">${t.hint}<\/div><\/div>\n    <div class=\"gfoot\"><div class=\"fb\" id=\"fb\"><\/div><button type=\"button\" class=\"btn pri\" id=\"ok\" disabled>Valider <kbd class=\"kbonly\">Entr\u00e9e<\/kbd><\/button><\/div><\/section>`;\n  const s=$(\"#board\"); let ready=false, done=false, tm=null;\n  const api={ ready(){ ready=true; $(\"#ok\").disabled=false; $(\"#hint\").style.opacity=0 }, hint(m){ const h=$(\"#hint\"); h.textContent=m; h.style.opacity=1 },\n    submit(){ submit() }, startTimer(sec,cb){ const b=$(\"#timer\"); b.style.transition=\"none\"; b.style.width=\"100%\"; void b.offsetWidth; b.style.transition=`width ${sec}s linear`; b.style.width=\"0%\"; tm=setTimeout(cb,sec*1000) },\n    stopTimer(){ clearTimeout(tm); const b=$(\"#timer\"); b.style.transition=\"none\"; b.style.width=\"0\" } };\n  const T=t.setup(s,api);\n  if(t.auto) $(\"#ok\").hidden=true;\n  const submit=()=>{ if(done||!ready) return; done=true; clearTimeout(tm); s.onpointerdown=s.onpointermove=s.onpointerup=null; s.style.cursor=\"default\";\n    const sc=clamp(T.score(),0,100); G.scores.push(sc); G.details.push(T.detail()); T.reveal();\n    const fb=$(\"#fb\"); fb.innerHTML=`<div class=\"sc pop\"><span id=\"sc\">0<\/span><small>\/100<\/small><\/div><div class=\"tx\"><b>${verdict(sc)}<\/b><span>${T.detail()}<\/span><\/div>`;\n    let v=0; const st=performance.now(); const step=n=>{ const k=Math.min(1,(n-st)\/600); v=Math.round(sc*(1-Math.pow(1-k,3))); const e=$(\"#sc\"); if(e) e.textContent=v; if(k<1) requestAnimationFrame(step) }; requestAnimationFrame(step);\n    $(\"#tot\").textContent=G.scores.reduce((a,b)=>a+b,0);\n    const ok=$(\"#ok\"); ok.hidden=false; ok.disabled=false; ok.innerHTML=G.i<7?`\u00c9preuve suivante <kbd class=\"kbonly\">Entr\u00e9e<\/kbd>`:`Voir mon score <kbd class=\"kbonly\">Entr\u00e9e<\/kbd>`; ok.onclick=next };\n  $(\"#ok\").onclick=submit; G.submit=submit; G.next=null;\n  function next(){ G.i++; if(G.i<G.order.length){ round(); toTop() } else results() }\n  G.key=()=>{ if(!done) submit(); else next() };\n}\nfunction results(){\n  const sc=Math.round(G.scores.reduce((a,b)=>a+b,0)\/G.scores.length), [title,txt]=rank(sc); const prev=SAVE.best; SAVE.plays++; if(sc>SAVE.best) SAVE.best=sc; persist(); updateBest();\n  const C=2*Math.PI*88;\n  view.innerHTML=`<section class=\"res\">\n    <div class=\"glass rcard\"><span class=\"eyebrow\">Ton r\u00e9sultat<\/span>\n      <div class=\"ring\"><svg viewBox=\"0 0 200 200\"><defs><linearGradient id=\"rg\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#4F86FF\"\/><stop offset=\"1\" stop-color=\"#7CC4FF\"\/><\/linearGradient><\/defs>\n        <circle cx=\"100\" cy=\"100\" r=\"88\" fill=\"none\" stroke=\"#1C2433\" stroke-width=\"12\"\/><circle id=\"arc\" cx=\"100\" cy=\"100\" r=\"88\" fill=\"none\" stroke=\"url(#rg)\" stroke-width=\"12\" stroke-linecap=\"round\" stroke-dasharray=\"${C}\" stroke-dashoffset=\"${C}\" style=\"transition:stroke-dashoffset 1.4s cubic-bezier(.2,.8,.2,1)\"\/><\/svg>\n        <div class=\"v\"><b id=\"fs\">0<\/b><span>SUR 100<\/span><\/div><\/div>\n      <div class=\"rank\">${title}<\/div><p>${txt}${sc>prev&&prev?` Nouveau record (avant : ${prev}).`:\"\"}<\/p>\n      <div class=\"ract\"><button type=\"button\" class=\"btn pri\" id=\"again\">Rejouer<\/button><button type=\"button\" class=\"btn gho\" id=\"share\">D\u00e9fier un ami<\/button><\/div><\/div>\n    <div class=\"glass bd\"><h3>Le d\u00e9tail<\/h3>${G.order.map((t,i)=>`<div class=\"brow\"><span style=\"color:#EEF2F8\">${ICONS[t.key]}<\/span><div><div class=\"nm\">${t.title} <span style=\"color:var(--muted);font:500 12px var(--f-mono)\">\u00b7 ${G.details[i]}<\/span><\/div><div class=\"br\"><i data-w=\"${G.scores[i]}\"><\/i><\/div><\/div><div class=\"pt\">${G.scores[i]}<\/div><\/div>`).join(\"\")}<\/div>\n  <\/section>\n  <section class=\"glass cta\"><div><span class=\"eyebrow\">Studio ${CONFIG.brand}<\/span><h3 style=\"margin-top:12px\">Un \u0153il comme le tien m\u00e9rite de beaux projets.<\/h3><p>${CONFIG.brand} con\u00e7oit des visuels 3D, des animations et des identit\u00e9s o\u00f9 chaque pixel est \u00e0 sa place. Un projet en t\u00eate ?<\/p><\/div>\n    <a class=\"btn pri\" href=\"${CONFIG.contactUrl}\" target=\"_blank\" rel=\"noopener\">Parlons-en \u2192<\/a><\/section>`;\n  requestAnimationFrame(()=>{ $(\"#arc\").style.strokeDashoffset=C*(1-sc\/100); OKK.querySelectorAll(\".br i\").forEach(i=>i.style.width=i.dataset.w+\"%\") });\n  let v=0; const st=performance.now(); const step=n=>{ const k=Math.min(1,(n-st)\/1400); $(\"#fs\").textContent=Math.round(sc*(1-Math.pow(1-k,3))); if(k<1) requestAnimationFrame(step) }; requestAnimationFrame(step);\n  $(\"#again\").onclick=start;\n  $(\"#share\").onclick=async()=>{ const t=`J'ai eu ${sc}\/100 au test \u00ab As-tu l'\u0153il d'un designer ? \u00bb (${title}). Tu fais mieux ? ${CONFIG.shareUrl}`; let ok=false; try{ await navigator.clipboard.writeText(t); ok=true }catch(e){} toast(ok?\"Message copi\u00e9 : colle-le \u00e0 un ami\":\"Copie impossible ici : \"+t) };\n  G.key=start; toTop();\n}\ndocument.addEventListener(\"keydown\",e=>{ if(e.key!==\"Enter\") return; if(e.target instanceof Element && e.target.closest('button,a,input,textarea,select,[contenteditable=\"true\"]')) return; const okk=$(\"#okk\"); if(!okk) return; if(!G){ if($(\"#go\")){ e.preventDefault(); start() } return } if(G.key){ e.preventDefault(); G.key() } });\n\n\/* iframe auto-height *\/\n(function(){ if(window.parent===window) return; let last=0; const send=()=>{ const h=Math.ceil($(\"#okk\").getBoundingClientRect().height); if(Math.abs(h-last)>2){ last=h; try{ parent.postMessage({type:\"ok3d-oeil:height\",height:h},\"*\") }catch(e){} } }; try{ new ResizeObserver(send).observe($(\"#okk\")) }catch(e){ setInterval(send,800) } })();\n\n\/* Pleine largeur, m\u00eame dans une colonne Elementor limit\u00e9e.\n   Aucune modification globale du th\u00e8me ou du d\u00e9filement de la page. *\/\n(function setupElementorScreen() {\n  let pending = 0;\n  const isFull = () => (document.fullscreenElement || document.webkitFullscreenElement) === OKK || OKK.classList.contains('is-app-screen');\n  const setVar = (name, value) => {\n    if (OKK.style.getPropertyValue(name) !== value) OKK.style.setProperty(name, value);\n  };\n  const measure = () => {\n    pending = 0;\n    if (!OKK.isConnected) { if (observer) observer.disconnect(); return; }\n    const full = isFull();\n    if (!full) {\n      const width = document.documentElement.clientWidth;\n      const shift = parseFloat(OKK.style.getPropertyValue('--okk-shift')) || 0;\n      const left = OKK.getBoundingClientRect().left;\n      setVar('--okk-width', width + 'px');\n      setVar('--okk-shift', (Math.round((shift - left) * 100) \/ 100) + 'px');\n    }\n    const admin = document.getElementById('wpadminbar');\n    const adminVisible = admin && getComputedStyle(admin).position === 'fixed';\n    setVar('--okk-admin-height', (!full && adminVisible ? admin.getBoundingClientRect().height : 0) + 'px');\n  };\n  const schedule = () => { if (!pending) pending = requestAnimationFrame(measure); };\n  const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(schedule) : null;\n  if (observer) {\n    observer.observe(document.documentElement);\n    if (OKK.parentElement) observer.observe(OKK.parentElement);\n  }\n  window.addEventListener('resize', schedule);\n  window.addEventListener('orientationchange', schedule);\n  window.addEventListener('load', schedule, { once: true });\n  if (window.visualViewport) window.visualViewport.addEventListener('resize', schedule);\n  if (document.fonts && document.fonts.ready) document.fonts.ready.then(schedule);\n  (function setupOK3DFullscreen(root) {\n  const doc = root.ownerDocument;\n  const win = doc.defaultView;\n  let appState = null;\n  const nativeFull = () => (doc.fullscreenElement || doc.webkitFullscreenElement) === root;\n  const active = () => nativeFull() || root.classList.contains('is-app-screen');\n  const attribute = (node, name, value) => {\n    if (node.getAttribute(name) !== value) node.setAttribute(name, value);\n  };\n  const sync = () => {\n    const full = active();\n    root.querySelectorAll('[data-okk-fullscreen]').forEach(button => {\n      const label = full ? '\u2922 Quitter le plein \u00e9cran' : '\u26f6 Plein \u00e9cran';\n      if (button.textContent !== label) button.textContent = label;\n      button.hidden = false;\n      attribute(button, 'aria-pressed', String(full));\n      attribute(button, 'aria-label', full ? 'Quitter le plein \u00e9cran' : 'Afficher en plein \u00e9cran');\n      attribute(button, 'title', full ? 'Quitter le plein \u00e9cran (\u00c9chap)' : 'Afficher en plein \u00e9cran');\n    });\n  };\n  const changed = () => {\n    sync();\n    win.dispatchEvent(new Event('resize'));\n  };\n  const saveOverflow = node => [node.style.getPropertyValue('overflow'), node.style.getPropertyPriority('overflow')];\n  const restoreOverflow = (node, value) => {\n    if (value[0]) node.style.setProperty('overflow', value[0], value[1]);\n    else node.style.removeProperty('overflow');\n  };\n  const focusButton = () => {\n    const button = root.querySelector('[data-okk-fullscreen]');\n    if (button) button.focus({ preventScroll: true });\n  };\n  const enterApp = () => {\n    if (appState) return;\n    const marker = doc.createComment('OK3D \u2014 retour du mode app');\n    appState = {\n      marker, parent: root.parentNode, next: root.nextSibling,\n      bodyOverflow: saveOverflow(doc.body), htmlOverflow: saveOverflow(doc.documentElement),\n      x: win.scrollX, y: win.scrollY\n    };\n    root.before(marker);\n    \/\/ Sortir des conteneurs transform\u00e9s d'Elementor pour couvrir toute la fen\u00eatre.\n    doc.body.appendChild(root);\n    root.classList.add('is-app-screen');\n    doc.body.style.setProperty('overflow', 'hidden', 'important');\n    doc.documentElement.style.setProperty('overflow', 'hidden', 'important');\n    root.scrollTop = 0;\n    changed();\n    focusButton();\n  };\n  const leaveApp = () => {\n    if (!appState) return;\n    const saved = appState;\n    appState = null;\n    root.classList.remove('is-app-screen');\n    if (saved.marker.isConnected) saved.marker.replaceWith(root);\n    else if (saved.parent && saved.parent.isConnected) {\n      saved.parent.insertBefore(root, saved.next && saved.next.parentNode === saved.parent ? saved.next : null);\n    }\n    restoreOverflow(doc.body, saved.bodyOverflow);\n    restoreOverflow(doc.documentElement, saved.htmlOverflow);\n    changed();\n    win.scrollTo(saved.x, saved.y);\n    focusButton();\n  };\n  root.addEventListener('click', async event => {\n    const button = event.target.closest && event.target.closest('[data-okk-fullscreen]');\n    if (!button || !root.contains(button)) return;\n    event.preventDefault();\n    if (appState) { leaveApp(); return; }\n    if (nativeFull()) {\n      const exit = doc.exitFullscreen || doc.webkitExitFullscreen;\n      if (exit) {\n        try { await exit.call(doc); } catch (_) { attribute(button, 'title', 'Appuie sur \u00c9chap pour quitter le plein \u00e9cran'); }\n      }\n      return;\n    }\n    const request = root.requestFullscreen || root.webkitRequestFullscreen;\n    if (request) {\n      try { await request.call(root); sync(); return; } catch (_) { \/* Aper\u00e7u ou appareil sans API : mode app. *\/ }\n    }\n    enterApp();\n  });\n  doc.addEventListener('keydown', event => {\n    if (event.key === 'Escape' && appState) {\n      event.preventDefault();\n      event.stopPropagation();\n      leaveApp();\n      return;\n    }\n    if (event.key !== 'Tab' || !active()) return;\n    const items = Array.from(root.querySelectorAll('button:not(:disabled),a[href],input:not(:disabled),select:not(:disabled),textarea:not(:disabled),[tabindex=\"0\"]'))\n      .filter(node => !node.hidden && node.getClientRects().length);\n    if (!items.length) return;\n    const first = items[0], last = items[items.length - 1];\n    if (event.shiftKey && (doc.activeElement === first || !root.contains(doc.activeElement))) {\n      event.preventDefault(); last.focus();\n    } else if (!event.shiftKey && (doc.activeElement === last || !root.contains(doc.activeElement))) {\n      event.preventDefault(); first.focus();\n    }\n  }, true);\n  doc.addEventListener('fullscreenchange', changed);\n  doc.addEventListener('webkitfullscreenchange', changed);\n  \/\/ Les barres de jeu (Keystroke notamment) sont reconstruites apr\u00e8s chaque s\u00e9rie.\n  if (typeof MutationObserver !== 'undefined') new MutationObserver(sync).observe(root, { childList: true, subtree: true });\n  sync();\n  return { active };\n})(OKK);\n  measure();\n})();\n\nupdateBest(); intro();\n\n})();\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>\u26f6 Plein \u00e9cranTon record : \u2014<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-15690","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/pages\/15690","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/comments?post=15690"}],"version-history":[{"count":4,"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/pages\/15690\/revisions"}],"predecessor-version":[{"id":15700,"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/pages\/15690\/revisions\/15700"}],"wp:attachment":[{"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/media?parent=15690"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}