{"id":15733,"date":"2026-10-16T23:14:55","date_gmt":"2026-10-16T21:14:55","guid":{"rendered":"https:\/\/www.ok3d.fr\/?page_id=15733"},"modified":"2026-10-21T16:56:55","modified_gmt":"2026-10-21T14:56:55","slug":"crash-test-logo","status":"publish","type":"page","link":"https:\/\/www.ok3d.fr\/en\/crash-test-logo\/","title":{"rendered":"Crash Test Logo"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"15733\" class=\"elementor elementor-15733\" 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-default-div 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<!-- Crash Test Logo by OK3D \u2014 \u00e0 coller EN ENTIER dans un widget HTML Elementor.\n  Page : Elementor pleine largeur. Conteneur : marges\/padding 0, d\u00e9bordement visible.\n  Le header du site est conserv\u00e9. Aucun menu ni en-t\u00eate de marque suppl\u00e9mentaire.\n  Cadre : 1600px, marges comprises. Espace haut : 94+54px ; mobile : 78+43px.\n  R\u00e9glages : --okk-content-max, --okk-header-offset, --okk-top-gap.\n  Traitement des logos local, sans biblioth\u00e8que JavaScript distante ni inscription.\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;600;700&family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap\">\n<style>.okct[data-okct=\"logo\"]{--bg:#06080c;--ink:#eef2f8;--muted:#98a4b8;--line:#243047;--blue:#2e6bff;--ice:#a4c9ff;--good:#86e2bc;--warm:#f0c78d;--okk-content-max:1600px;--okk-header-offset:94px;--okk-top-gap:54px;--gutter:clamp(22px,5vw,88px);--body:\"Geist\",system-ui,-apple-system,\"Segoe UI\",sans-serif;--display:\"Unbounded\",system-ui,sans-serif;--mono:\"Geist Mono\",ui-monospace,monospace;display:block;position:relative;left:var(--ct-shift,0px);width:var(--ct-width,100%)!important;max-width:none!important;min-width:0;margin:0!important;padding:0!important;color:var(--ink);background:var(--bg);font:400 15px\/1.5 var(--body);text-align:left;color-scheme:dark;-webkit-font-smoothing:antialiased;isolation:isolate}\n.okct[data-okct=\"logo\"] *,.okct[data-okct=\"logo\"] *::before,.okct[data-okct=\"logo\"] *::after{box-sizing:border-box}\n.okct[data-okct=\"logo\"] [hidden]{display:none!important}\n.okct[data-okct=\"logo\"] :focus-visible{outline:2px solid var(--ice);outline-offset:4px}\n.okct[data-okct=\"logo\"] button,.okct[data-okct=\"logo\"] input{font-family:var(--body);font-size:13px;line-height:1.4;letter-spacing:normal;text-transform:none;margin:0;box-shadow:none}\n.okct[data-okct=\"logo\"] button{padding:0;border:0;border-radius:0;background:none;color:inherit;cursor:pointer;touch-action:manipulation;transition:background .18s,border-color .18s,color .18s,transform .18s}\n.okct[data-okct=\"logo\"] button:enabled:active{transform:translateY(1px)}\n.okct[data-okct=\"logo\"] button:disabled{cursor:wait;opacity:.5}\n.okct[data-okct=\"logo\"] h1,.okct[data-okct=\"logo\"] h2,.okct[data-okct=\"logo\"] h3,.okct[data-okct=\"logo\"] p{margin:0;padding:0;color:inherit;letter-spacing:normal;text-transform:none}\n.okct[data-okct=\"logo\"] h1,.okct[data-okct=\"logo\"] h2,.okct[data-okct=\"logo\"] h3{font-family:var(--display)}\n.okct[data-okct=\"logo\"] a{color:inherit;text-decoration:none}\n.okct[data-okct=\"logo\"] canvas{display:block;max-width:100%}\n.okct[data-okct=\"logo\"] .ct-page{min-height:100vh;overflow:clip;padding-top:calc(var(--okk-header-offset) + var(--okk-top-gap) + var(--okk-admin-height,0px));padding-bottom:max(28px,env(safe-area-inset-bottom,0px));background:radial-gradient(ellipse 1000px 700px at 12% 0,rgba(46,107,255,.09),transparent 70%),var(--bg)}\n.okct[data-okct=\"logo\"] .ct-wrap{width:100%;max-width:var(--okk-content-max);padding-inline:max(var(--gutter),env(safe-area-inset-left,0px)) max(var(--gutter),env(safe-area-inset-right,0px));margin-inline:auto}\n.okct[data-okct=\"logo\"] .appbar{display:flex;align-items:center;justify-content:flex-end;gap:12px;flex-wrap:wrap;padding:0 0 20px}\n.okct[data-okct=\"logo\"] .ct-progress{display:flex;align-items:center;gap:9px;font:400 11px var(--mono);color:var(--muted);white-space:nowrap}\n.okct[data-okct=\"logo\"] .ct-progress strong{color:var(--ink);font-weight:500;border:1px solid var(--line);border-radius:999px;padding:6px 10px}\n.okct[data-okct=\"logo\"] .ct-btn{display:inline-flex;align-items:center;justify-content:center;gap:14px;min-height:46px;padding:12px 19px;border:1px solid #35445e;border-radius:999px;background:#101722;color:var(--ink);font-size:13px;font-weight:600;line-height:1.4;text-align:center}\n.okct[data-okct=\"logo\"] .ct-btn:enabled:hover,.okct[data-okct=\"logo\"] a.ct-btn:hover{border-color:#83a1cf;background:#1b2b45}\n.okct[data-okct=\"logo\"] .ct-btn>span{font-size:18px;font-weight:400}\n.okct[data-okct=\"logo\"] .ct-small{min-height:34px;font-size:11px;padding:7px 12px}\n.okct[data-okct=\"logo\"] .screen-btn{margin-left:0}\n.okct[data-okct=\"logo\"] .ct-primary{border-color:#5a88f8;background:linear-gradient(#427cff,#245be8);color:#fff;box-shadow:inset 0 1px 0 #ffffff30,0 8px 24px #2e6bff25}\n.okct[data-okct=\"logo\"] .ct-primary:enabled:hover{background:#467fff;border-color:#b0cdff}\n.okct[data-okct=\"logo\"] .ct-textbtn{font-size:11px;color:#b4c7e5;text-decoration:underline;text-decoration-color:#586e91;text-underline-offset:4px;padding:6px 0}\n.okct[data-okct=\"logo\"] .ct-textbtn:enabled:hover{color:#fff}\n.okct[data-okct=\"logo\"] .ct-intro{display:flex;justify-content:space-between;align-items:flex-end;gap:30px;padding-bottom:32px}\n.okct[data-okct=\"logo\"] .ct-eyebrow{display:flex;gap:8px;align-items:center;font:500 9px\/1.7 var(--mono);letter-spacing:.15em;color:var(--ice)}\n.okct[data-okct=\"logo\"] .ct-eyebrow i{width:5px;height:5px;border-radius:50%;background:var(--ice)}\n.okct[data-okct=\"logo\"] h1{font-size:clamp(32px,4.7vw,64px);line-height:1.13;font-weight:600;letter-spacing:-.055em;margin-top:14px;text-wrap:balance}\n.okct[data-okct=\"logo\"] h1>span{background:linear-gradient(100deg,#638eff,#b7d8ff);background-clip:text;-webkit-background-clip:text;color:transparent}\n.okct[data-okct=\"logo\"] .ct-intro-copy{max-width:370px;padding-bottom:6px}\n.okct[data-okct=\"logo\"] .ct-intro-copy>p{font-size:17px;letter-spacing:-.03em;margin-bottom:10px}\n.okct[data-okct=\"logo\"] .ct-intro-copy>span{color:var(--muted);font-size:14px;line-height:1.65}\n.okct[data-okct=\"logo\"] .ct-promises{display:flex;gap:7px;flex-wrap:wrap;margin-top:18px}\n.okct[data-okct=\"logo\"] .ct-promises>span{font:400 9px var(--mono);padding:6px 8px;border:1px solid var(--line);border-radius:6px;color:#b2c0d5}\n.okct[data-okct=\"logo\"] .ct-upload{position:relative;display:flex;gap:20px;align-items:center;padding:25px 28px;border:1px dashed #4c6590;border-radius:20px;background:linear-gradient(100deg,#14213a80,#0c121e);overflow:hidden}\n.okct[data-okct=\"logo\"] .ct-upload.over{background:#1b3356;border-color:#bed8ff;box-shadow:0 0 0 5px #2e6bff20}\n.okct[data-okct=\"logo\"] .ct-upload-icon{width:48px;height:48px;border-radius:14px;background:#2e6bff22;border:1px solid #6e9bff40;display:grid;place-items:center;flex-shrink:0}\n.okct[data-okct=\"logo\"] .ct-upload-icon svg{width:24px;height:24px;stroke:#bdd6ff;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}\n.okct[data-okct=\"logo\"] .ct-upload-copy{min-width:0;flex:1}\n.okct[data-okct=\"logo\"] .ct-upload-copy>strong{display:block;font-size:17px;letter-spacing:-.03em;font-weight:500;overflow-wrap:anywhere}\n.okct[data-okct=\"logo\"] .ct-upload-copy>span{display:block;color:#b1bfd4;font-size:11px;margin-top:4px}\n.okct[data-okct=\"logo\"] .ct-upload-copy>small{display:block;font-size:10px;color:var(--muted);margin-top:6px}\n.okct[data-okct=\"logo\"] .ct-upload-actions{display:flex;flex-direction:column;align-items:center;gap:6px;flex-shrink:0}\n.okct[data-okct=\"logo\"] .ct-load{position:absolute;inset:0;background:#0b1428ed;z-index:2;display:flex;gap:12px;align-items:center;justify-content:center;color:#c2d9ff;font-size:13px}\n.okct[data-okct=\"logo\"] .ct-spinner{width:22px;height:22px;border:2px solid #accfff22;border-top-color:#b6d2ff;border-radius:50%;animation:ct-spin .8s linear infinite}\n.okct[data-okct=\"logo\"] .ct-error{margin-top:12px;padding:14px 18px;border:1px solid #d7888260;border-radius:12px;background:#351b2240;color:#ffd3c9;font-size:12px;line-height:1.6}\n.okct[data-okct=\"logo\"] .ct-filebar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:15px 2px 22px}\n.okct[data-okct=\"logo\"] .ct-demo-pill{color:#d4c0ff;background:#baa0ff14;border:1px solid #b398ff40;font:500 8px var(--mono);letter-spacing:.08em;border-radius:5px;padding:5px 7px}\n.okct[data-okct=\"logo\"] .ct-filemeta{font:400 10px\/1.5 var(--mono);color:var(--muted);overflow-wrap:anywhere;min-width:0}\n.okct[data-okct=\"logo\"] .ct-trim{display:flex;align-items:center;gap:8px;margin-left:auto;font-size:10px;color:#c1cde0;cursor:pointer}\n.okct[data-okct=\"logo\"] input[type=\"checkbox\"]{width:14px;height:14px;accent-color:#79a4ff;padding:0;flex-shrink:0}\n.okct[data-okct=\"logo\"] .ct-tabs{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;padding:6px;background:#0c111b;border:1px solid var(--line);border-radius:17px;margin-bottom:16px}\n.okct[data-okct=\"logo\"] .ct-tabs>button{position:relative;display:flex;align-items:center;gap:12px;text-align:left;padding:14px 13px;border:1px solid transparent;border-radius:12px;min-width:0;color:#b2c0d6;font-size:12px}\n.okct[data-okct=\"logo\"] .ct-tabs>button:hover{background:#ffffff05}\n.okct[data-okct=\"logo\"] .ct-tabs>button[aria-selected=\"true\"]{background:#1c2c46;border-color:#46638d;color:#f2f6ff}\n.okct[data-okct=\"logo\"] .ct-tabnum{font:400 10px var(--mono);color:#a0b8dd}\n.okct[data-okct=\"logo\"] .ct-tabs small{display:block;font-size:9px;line-height:1.5;color:#95a6bf;margin-top:3px}\n.okct[data-okct=\"logo\"] .ct-tabs i{width:6px;height:6px;background:#41516c;border-radius:50%;margin-left:auto;flex-shrink:0}\n.okct[data-okct=\"logo\"] .ct-tabs>button[data-state=\"ok\"] i{background:var(--good)}\n.okct[data-okct=\"logo\"] .ct-tabs>button[data-state=\"review\"] i{background:var(--warm)}\n.okct[data-okct=\"logo\"] .ct-tabs>button[data-state=\"unsure\"] i{background:#b4a4e7}\n.okct[data-okct=\"logo\"] .ct-workspace{display:grid;grid-template-columns:minmax(0,1fr) 332px;gap:18px;align-items:start}\n.okct[data-okct=\"logo\"] .ct-workspace>*{min-width:0}\n.okct[data-okct=\"logo\"] .ct-test{border:1px solid var(--line);border-radius:22px;padding:25px;background:linear-gradient(150deg,#141d2d,#0b1019 65%);scroll-margin-top:calc(var(--okk-header-offset) + var(--okk-admin-height,0px) + 20px)}\n.okct[data-okct=\"logo\"] .ct-test-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:20px}\n.okct[data-okct=\"logo\"] .ct-test h2{font-size:clamp(18px,2vw,25px);line-height:1.4;letter-spacing:-.045em;font-weight:500;margin-top:7px}\n.okct[data-okct=\"logo\"] .ct-test-index{font:400 9px var(--mono);color:#8c9db7;white-space:nowrap;padding-top:5px}\n.okct[data-okct=\"logo\"] .ct-test-description{font-size:12px;line-height:1.7;color:#a9b8cd;margin:9px 0 22px;max-width:68ch}\n.okct[data-okct=\"logo\"] .ct-reference{position:relative;min-height:170px;border-radius:14px;overflow:hidden;display:grid;place-items:center;background:#f3f2ed;margin-bottom:14px;color:#20304c}\n.okct[data-okct=\"logo\"] .ct-reference canvas{width:100%;height:172px}\n.okct[data-okct=\"logo\"] .ct-reference>span{position:absolute;left:16px;bottom:12px;font:400 8px var(--mono);letter-spacing:.08em;color:#607089}\n.okct[data-okct=\"logo\"] .ct-size-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}\n.okct[data-okct=\"logo\"] .ct-size-tile{min-height:115px;position:relative;display:flex;align-items:center;justify-content:center;border:1px solid #d7dce3;border-radius:11px;overflow:auto;padding:27px 10px 22px;background:var(--tile-bg,#fff)}\n.okct[data-okct=\"logo\"] .ct-size-tile>span{position:absolute;bottom:9px;left:12px;color:var(--tile-ink,#53627c);font:400 8px var(--mono)}\n.okct[data-okct=\"logo\"] .ct-actual{max-width:none;flex-shrink:0;image-rendering:auto}\n.okct[data-okct=\"logo\"] .ct-custom-size{margin-top:11px;border:1px solid var(--line);border-radius:11px;padding:12px 16px;display:flex;justify-content:space-between;align-items:center;gap:14px;min-height:91px;overflow-x:auto;background:var(--tile-bg,#fff)}\n.okct[data-okct=\"logo\"] .ct-custom-size>span{font:400 9px var(--mono);color:var(--tile-ink,#53627c);flex-shrink:0}\n.okct[data-okct=\"logo\"] .ct-stage-note{font:400 9px\/1.7 var(--mono);color:#98aac5;margin-top:12px!important}\n.okct[data-okct=\"logo\"] .ct-back-grid,.okct[data-okct=\"logo\"] .ct-mono-grid,.okct[data-okct=\"logo\"] .ct-scene-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}\n.okct[data-okct=\"logo\"] .ct-preview-card{min-width:0;overflow:hidden;border:1px solid #334158;border-radius:13px;background:#101725}\n.okct[data-okct=\"logo\"] .ct-preview-card canvas{width:100%;height:auto;aspect-ratio:1.6}\n.okct[data-okct=\"logo\"] .ct-preview-card .ct-card-caption{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:10px 12px;font-size:11px;color:#dae5f8;line-height:1.5}\n.okct[data-okct=\"logo\"] .ct-card-caption small{color:#98b0d0;font:400 9px var(--mono);text-align:right}\n.okct[data-okct=\"logo\"] .ct-back-custom{margin-top:12px;border:1px solid #445a7d}\n.okct[data-okct=\"logo\"] .ct-back-custom canvas{aspect-ratio:2.8}\n.okct[data-okct=\"logo\"] .ct-side{border:1px solid var(--line);border-radius:22px;background:linear-gradient(155deg,#121a28,#0b1019);padding:25px 23px}\n.okct[data-okct=\"logo\"] .ct-side-heading h2{font-size:16px;line-height:1.6;font-weight:500;letter-spacing:-.035em;margin-top:8px}\n.okct[data-okct=\"logo\"] .ct-controls{margin-top:23px;padding-top:18px;border-top:1px solid var(--line)}\n.okct[data-okct=\"logo\"] .ct-mini-label{font:500 9px var(--mono);letter-spacing:.12em;color:#b2c8e9;display:block;margin-bottom:10px}\n.okct[data-okct=\"logo\"] .ct-control-label{font-size:12px;color:#d9e5f8;display:flex;justify-content:space-between;gap:10px;margin-bottom:10px}\n.okct[data-okct=\"logo\"] .ct-control-label>output{color:var(--ice);font:400 10px var(--mono)}\n.okct[data-okct=\"logo\"] .ct-chips{display:flex;gap:6px;flex-wrap:wrap;margin:0 0 18px}\n.okct[data-okct=\"logo\"] .ct-chips>button{font-size:10px;min-height:33px;padding:7px 10px;border:1px solid #344561;border-radius:8px;color:#bccbe1}\n.okct[data-okct=\"logo\"] .ct-chips>button[aria-pressed=\"true\"]{border-color:#729cda;background:#243d63;color:#fff}\n.okct[data-okct=\"logo\"] input[type=\"range\"]{appearance:none;-webkit-appearance:none;width:100%;height:24px;background:none;cursor:pointer;border:0;padding:0;min-height:0}\n.okct[data-okct=\"logo\"] input[type=\"range\"]::-webkit-slider-runnable-track{height:3px;border-radius:3px;background:linear-gradient(90deg,#a4c9ff var(--v,50%),#34435b var(--v,50%))}\n.okct[data-okct=\"logo\"] input[type=\"range\"]::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:#e5f0ff;border:0;margin-top:-5.5px;box-shadow:0 0 0 4px #bbd9ff10}\n.okct[data-okct=\"logo\"] input[type=\"range\"]::-moz-range-track{height:3px;background:#34435b}\n.okct[data-okct=\"logo\"] input[type=\"range\"]::-moz-range-progress{height:3px;background:#a4c9ff}\n.okct[data-okct=\"logo\"] input[type=\"range\"]::-moz-range-thumb{width:14px;height:14px;background:#e5f0ff;border:0;border-radius:50%}\n.okct[data-okct=\"logo\"] .ct-color-label{display:flex;align-items:center;justify-content:space-between;gap:12px;color:#d9e5f8;font-size:12px}\n.okct[data-okct=\"logo\"] input[type=\"color\"]{width:39px;height:34px;border:1px solid #547099;border-radius:8px;padding:4px;background:#101a2b;cursor:pointer}\n.okct[data-okct=\"logo\"] .ct-help{color:#a7b7ce;font-size:11px;line-height:1.7;margin-top:10px}\n.okct[data-okct=\"logo\"] .ct-observe{padding-top:18px;margin-top:19px;border-top:1px solid var(--line)}\n.okct[data-okct=\"logo\"] .ct-observe ul{padding-left:16px;margin:0;color:#c2cee0;font-size:12px;line-height:1.8}\n.okct[data-okct=\"logo\"] .ct-observe li+li{margin-top:7px}\n.okct[data-okct=\"logo\"] .ct-lesson{background:#14243d80;border:1px solid #365078;border-radius:12px;padding:14px;margin-top:20px}\n.okct[data-okct=\"logo\"] .ct-lesson .ct-eyebrow{font-size:8px}\n.okct[data-okct=\"logo\"] .ct-lesson p{color:#c3d4ed;font-size:11px;line-height:1.8;margin-top:7px}\n.okct[data-okct=\"logo\"] .ct-verdict{border-top:1px solid var(--line);padding-top:20px;margin-top:21px}\n.okct[data-okct=\"logo\"] .ct-verdict>p{color:var(--muted);font-size:10px;margin-bottom:12px}\n.okct[data-okct=\"logo\"] .ct-verdict-buttons{display:flex;flex-direction:column;gap:7px}\n.okct[data-okct=\"logo\"] .ct-verdict-buttons button{display:flex;align-items:center;gap:10px;border:1px solid #344560;border-radius:9px;padding:11px 12px;min-height:40px;text-align:left;font-size:11px;color:#bac9df}\n.okct[data-okct=\"logo\"] .ct-verdict-buttons button>span{width:15px;text-align:center}\n.okct[data-okct=\"logo\"] .ct-verdict-buttons [data-verdict=\"ok\"][aria-pressed=\"true\"]{border-color:#70c5a0;color:#b7f2d8;background:#1c473530}\n.okct[data-okct=\"logo\"] .ct-verdict-buttons [data-verdict=\"review\"][aria-pressed=\"true\"]{border-color:#d5aa70;color:#ffe0b1;background:#70492025}\n.okct[data-okct=\"logo\"] .ct-verdict-buttons [data-verdict=\"unsure\"][aria-pressed=\"true\"]{border-color:#9e88d7;color:#dfd0ff;background:#49317630}\n.okct[data-okct=\"logo\"] .ct-next{width:100%;justify-content:space-between;border-radius:10px;margin-top:15px;font-size:11px}\n.okct[data-okct=\"logo\"] .ct-diagnostic{padding-top:33px;margin-top:38px;border-top:1px solid var(--line)}\n.okct[data-okct=\"logo\"] .ct-summary-top{display:flex;justify-content:space-between;align-items:flex-end;gap:24px;margin-bottom:24px}\n.okct[data-okct=\"logo\"] .ct-summary-top h2,.okct[data-okct=\"logo\"] .ct-export h2{font-size:clamp(22px,2.5vw,33px);line-height:1.45;letter-spacing:-.045em;font-weight:500;margin-top:9px}\n.okct[data-okct=\"logo\"] .ct-summary-top h2>span,.okct[data-okct=\"logo\"] .ct-export h2>span{color:var(--muted)}\n.okct[data-okct=\"logo\"] .ct-summary-top>p{font-size:12px;color:var(--muted);line-height:1.8}\n.okct[data-okct=\"logo\"] .ct-summary-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}\n.okct[data-okct=\"logo\"] .ct-stat{padding:18px;border:1px solid var(--line);border-radius:13px;background:#101723;min-width:0}\n.okct[data-okct=\"logo\"] .ct-stat>span{display:block;font:400 8px var(--mono);letter-spacing:.08em;color:#a0b6d6;margin-bottom:10px}\n.okct[data-okct=\"logo\"] .ct-stat>strong{display:block;font-size:16px;font-weight:500;letter-spacing:-.02em;overflow-wrap:anywhere}\n.okct[data-okct=\"logo\"] .ct-stat>small{display:block;color:var(--muted);font-size:10px;line-height:1.7;margin-top:6px}\n.okct[data-okct=\"logo\"] .ct-findings{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:16px}\n.okct[data-okct=\"logo\"] .ct-finding{display:flex;align-items:flex-start;gap:12px;border-left:1px solid #46638d;padding:8px 14px}\n.okct[data-okct=\"logo\"] .ct-finding>span{font:400 10px var(--mono);color:var(--ice);padding-top:3px}\n.okct[data-okct=\"logo\"] .ct-finding strong{font-size:12px;font-weight:500}\n.okct[data-okct=\"logo\"] .ct-finding p{font-size:11px;line-height:1.8;color:#a9bbd4;margin-top:5px}\n.okct[data-okct=\"logo\"] .ct-method{border:1px solid var(--line);border-radius:10px;padding:13px 16px;margin-top:22px;font-size:11px;color:#b8c9e2}\n.okct[data-okct=\"logo\"] .ct-method summary{cursor:pointer}\n.okct[data-okct=\"logo\"] .ct-method p{margin-top:12px;line-height:1.9;color:#9fb2cf;max-width:100ch}\n.okct[data-okct=\"logo\"] .ct-export{display:grid;grid-template-columns:minmax(0,1fr) 350px;gap:35px;align-items:center;margin-top:35px;padding:34px 0;border-block:1px solid var(--line)}\n.okct[data-okct=\"logo\"] .ct-export>div>p{font-size:12px;color:var(--muted);line-height:1.8;max-width:55ch;margin-top:13px}\n.okct[data-okct=\"logo\"] .ct-export-actions{display:flex;flex-direction:column;gap:10px;min-width:0}\n.okct[data-okct=\"logo\"] .ct-name{font-size:10px;color:#b6c7e0;display:flex;flex-direction:column;gap:7px}\n.okct[data-okct=\"logo\"] input[type=\"text\"]{width:100%;border:1px solid #3a4f70;border-radius:9px;color:#e6effd;background:#101a2a;padding:10px 12px;min-height:42px;font-size:13px}\n.okct[data-okct=\"logo\"] .ct-export-note{text-align:center;font:400 9px var(--mono);color:var(--muted)}\n.okct[data-okct=\"logo\"] .ct-studio-link{display:flex;justify-content:space-between;align-items:center;gap:25px;padding:32px;margin-top:30px;border:1px solid #314d78;border-radius:19px;background:linear-gradient(120deg,#18336866,#14203b20)}\n.okct[data-okct=\"logo\"] .ct-studio-link h2{font-size:clamp(17px,2vw,23px);font-weight:500;line-height:1.5;letter-spacing:-.035em;margin-top:10px}\n.okct[data-okct=\"logo\"] .ct-studio-link p{font-size:12px;line-height:1.8;color:#a6bcdc;max-width:60ch;margin-top:10px}\n.okct[data-okct=\"logo\"] .ct-studio-link .ct-btn{flex-shrink:0;font-size:12px}\n.okct[data-okct=\"logo\"] .ct-endnote{display:flex;justify-content:space-between;flex-wrap:wrap;gap:14px;margin-top:28px;font:400 8px var(--mono);letter-spacing:.08em;color:#8a9cb7}\n.okct[data-okct=\"logo\"] .ct-toast{position:fixed;left:50%;bottom:max(25px,env(safe-area-inset-bottom,0px));transform:translateX(-50%);padding:14px 20px;background:#e7efff;color:#13213a;border-radius:12px;box-shadow:0 12px 55px #0007;z-index:2147483647;font-size:12px;text-align:center;width:max-content;max-width:calc(100% - 36px);pointer-events:none}\n.elementor-widget-html:has(.okct[data-okct=\"logo\"]){width:100%;min-width:0;padding:0}\n.elementor-widget-container:has(>.okct[data-okct=\"logo\"]){padding:0;margin:0}\n@keyframes ct-spin{to{transform:rotate(360deg)}}\n@media(max-width:1100px){.okct[data-okct=\"logo\"] .ct-workspace{grid-template-columns:minmax(0,1fr) 300px;gap:13px}.okct[data-okct=\"logo\"] .ct-test{padding:20px}.okct[data-okct=\"logo\"] .ct-side{padding:22px 19px}.okct[data-okct=\"logo\"] .ct-tabs small{font-size:8px}.okct[data-okct=\"logo\"] .ct-tabs>button{gap:8px;padding:12px 10px}.okct[data-okct=\"logo\"] .ct-intro-copy{max-width:320px}.okct[data-okct=\"logo\"] .ct-tabs .ct-tabnum{display:none}}\n@media(max-width:900px){.okct[data-okct=\"logo\"] .ct-workspace{grid-template-columns:minmax(0,1fr)}.okct[data-okct=\"logo\"] .ct-side{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 26px}.okct[data-okct=\"logo\"] .ct-side-heading{grid-column:1\/-1}.okct[data-okct=\"logo\"] .ct-controls{margin:0}.okct[data-okct=\"logo\"] .ct-observe{margin:0}.okct[data-okct=\"logo\"] .ct-lesson{margin:0}.okct[data-okct=\"logo\"] .ct-verdict{grid-column:2;grid-row:3\/5;margin:0;padding-top:0;border:0}.okct[data-okct=\"logo\"] .ct-next{margin:0;align-self:end}.okct[data-okct=\"logo\"] .ct-upload{gap:15px;padding:22px}.okct[data-okct=\"logo\"] .ct-upload-icon{display:none}.okct[data-okct=\"logo\"] .ct-summary-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.okct[data-okct=\"logo\"] .ct-export{grid-template-columns:minmax(0,1fr) 310px}.okct[data-okct=\"logo\"] .ct-studio-link{padding:25px}}\n@media(max-width:700px){.okct[data-okct=\"logo\"]{--okk-header-offset:78px;--okk-top-gap:43px}.okct[data-okct=\"logo\"] .ct-intro{flex-direction:column;align-items:flex-start;gap:21px;padding-bottom:25px}.okct[data-okct=\"logo\"] .ct-intro-copy{max-width:100%;padding:0}.okct[data-okct=\"logo\"] .ct-intro-copy>p{font-size:15px}.okct[data-okct=\"logo\"] .ct-intro-copy>span{font-size:13px}.okct[data-okct=\"logo\"] .ct-promises{margin-top:14px}.okct[data-okct=\"logo\"] .ct-upload{align-items:stretch;flex-direction:column;gap:18px}.okct[data-okct=\"logo\"] .ct-upload-actions{align-items:stretch;gap:10px}.okct[data-okct=\"logo\"] .ct-upload-copy>strong{font-size:16px}.okct[data-okct=\"logo\"] .ct-filebar{padding-bottom:19px}.okct[data-okct=\"logo\"] .ct-trim{margin-left:0;width:100%;margin-top:4px}.okct[data-okct=\"logo\"] .ct-tabs{grid-template-columns:repeat(2,minmax(0,1fr))}.okct[data-okct=\"logo\"] .ct-tabs>button{padding:12px}.okct[data-okct=\"logo\"] .ct-tabs small{font-size:8px}.okct[data-okct=\"logo\"] .ct-summary-top{align-items:flex-start;flex-direction:column;gap:12px}.okct[data-okct=\"logo\"] .ct-findings{grid-template-columns:minmax(0,1fr)}.okct[data-okct=\"logo\"] .ct-export{grid-template-columns:minmax(0,1fr);gap:22px}.okct[data-okct=\"logo\"] .ct-studio-link{flex-direction:column;align-items:flex-start}.okct[data-okct=\"logo\"] .ct-studio-link .ct-btn{width:100%}.okct[data-okct=\"logo\"] .ct-size-grid{gap:8px}.okct[data-okct=\"logo\"] .ct-test-index{display:none}}\n@media(max-width:480px){.okct[data-okct=\"logo\"] .appbar{gap:9px}.okct[data-okct=\"logo\"] .ct-progress{font-size:9px;gap:6px}.okct[data-okct=\"logo\"] .ct-small{font-size:10px}.okct[data-okct=\"logo\"] .ct-test{padding:18px 15px;border-radius:17px}.okct[data-okct=\"logo\"] .ct-side{display:flex;flex-direction:column;padding:22px 19px}.okct[data-okct=\"logo\"] .ct-controls,.okct[data-okct=\"logo\"] .ct-observe{padding-top:16px}.okct[data-okct=\"logo\"] .ct-verdict{padding-top:18px;border-top:1px solid var(--line)}.okct[data-okct=\"logo\"] .ct-next{align-self:stretch}.okct[data-okct=\"logo\"] .ct-back-grid,.okct[data-okct=\"logo\"] .ct-mono-grid,.okct[data-okct=\"logo\"] .ct-scene-grid{grid-template-columns:minmax(0,1fr)}.okct[data-okct=\"logo\"] .ct-test h2{font-size:18px}.okct[data-okct=\"logo\"] .ct-reference canvas{height:140px}.okct[data-okct=\"logo\"] .ct-size-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.okct[data-okct=\"logo\"] .ct-size-tile{min-height:100px;padding-inline:6px}.okct[data-okct=\"logo\"] .ct-custom-size{flex-direction:column;align-items:flex-start;justify-content:center;min-height:120px;gap:18px}.okct[data-okct=\"logo\"] .ct-stat{padding:15px}.okct[data-okct=\"logo\"] .ct-stat>strong{font-size:14px}.okct[data-okct=\"logo\"] .ct-tabs>button{font-size:11px}.okct[data-okct=\"logo\"] .ct-summary-grid{gap:8px}.okct[data-okct=\"logo\"] .ct-test-description{font-size:11px}.okct[data-okct=\"logo\"] .ct-endnote{font-size:7px}.okct[data-okct=\"logo\"] .ct-back-custom canvas{aspect-ratio:1.6}}\n@media(prefers-reduced-motion:reduce){.okct[data-okct=\"logo\"] button{transition:none}.okct[data-okct=\"logo\"] .ct-spinner{animation:none}}\n\/* Les r\u00e9glages restent \u00e0 c\u00f4t\u00e9 de l'aper\u00e7u qu'ils modifient, sur tous les \u00e9crans. *\/\n.okct[data-okct=\"logo\"] .ct-test>.ct-controls{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:8px 24px;margin:0 0 18px;padding:16px;border:1px solid #31425e;border-radius:12px;background:#0f1b2e}\n.okct[data-okct=\"logo\"] .ct-test>.ct-controls>.ct-help{grid-column:1\/-1;margin:0}\n.okct[data-okct=\"logo\"] .ct-test>.ct-controls .ct-chips{margin:0}\n.okct[data-okct=\"logo\"] .ct-test>.ct-controls>label:has(input[type=\"range\"]){grid-column:2;grid-row:1\/3;align-self:center}\n.okct[data-okct=\"logo\"] .ct-test>.ct-controls>.ct-mini-label{margin:0}\n@media(max-width:480px){.okct[data-okct=\"logo\"] .ct-test>.ct-controls{display:flex;flex-direction:column;gap:12px;padding:14px}.okct[data-okct=\"logo\"] .ct-test>.ct-controls>label{width:100%}}\n.okct[data-okct=\"logo\"]:fullscreen .ct-test,.okct[data-okct=\"logo\"].is-app-screen .ct-test{scroll-margin-top:76px}\n.okct[data-okct=\"logo\"]:-webkit-full-screen .ct-test{scroll-margin-top:76px}\n\n\n\/* Affichage app lorsque le plein \u00e9cran natif n'est pas disponible. *\/\n.okct[data-okct=\"logo\"].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.okct[data-okct=\"logo\"]:fullscreen, .okct[data-okct=\"logo\"].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.okct[data-okct=\"logo\"]:-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.okct[data-okct=\"logo\"] .appbar {\n  display: flex; justify-content: flex-end; align-items: center; gap: 12px;\n  flex-wrap: wrap; padding: 0 0 18px;\n}\n.okct[data-okct=\"logo\"] .screen-btn { margin-left: auto; flex: 0 0 auto; }\n.okct[data-okct=\"logo\"]:fullscreen .appbar, .okct[data-okct=\"logo\"].is-app-screen .appbar,\n.okct[data-okct=\"logo\"]:fullscreen .bar, .okct[data-okct=\"logo\"].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.okct[data-okct=\"logo\"] .screen-btn{margin-left:0}<\/style>\n<div class=\"okct\" data-okct=\"logo\">\n <div class=\"ct-page\"><div class=\"ct-wrap\">\n  <div class=\"appbar\"><button type=\"button\" class=\"ct-btn ct-small screen-btn\" data-okk-fullscreen aria-pressed=\"false\">\u26f6 Plein \u00e9cran<\/button><span class=\"ct-progress\">Ton diagnostic <strong data-ui=\"progress\">0 \/ 4<\/strong><\/span><\/div>\n  <div class=\"ct-intro\"><div><span class=\"ct-eyebrow\"><i aria-hidden=\"true\"><\/i> CRASH TEST LOGO<\/span><h1>Ton logo tient-il<br><span>la route ?<\/span><\/h1><\/div><div class=\"ct-intro-copy\"><p>Un beau logo. Partout, vraiment ?<\/p><span>Importe le tien. Observe ce qui r\u00e9siste.<br>Repars avec les bonnes questions et ta fiche de v\u00e9rification.<\/span><div class=\"ct-promises\"><span>4 \u00e9preuves<\/span><span>Sans compte<\/span><span>Sur ton appareil<\/span><\/div><\/div><\/div>\n  <div class=\"ct-upload\" data-ui=\"drop\"><div class=\"ct-upload-icon\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M12 16V4m-5 5 5-5 5 5M4 16v4h16v-4\"\/><\/svg><\/div><div class=\"ct-upload-copy\"><strong data-ui=\"upload-title\">D\u00e9pose ton logo. On regarde \u00e7a.<\/strong><span data-ui=\"upload-detail\">SVG, PNG, JPG ou WebP \u00b7 15 Mo maximum<\/span><small data-ui=\"file-note\">Un exemple fictif est d\u00e9j\u00e0 pr\u00eat pour d\u00e9couvrir les tests.<\/small><\/div><div class=\"ct-upload-actions\"><button type=\"button\" class=\"ct-btn ct-primary\" data-action=\"upload\">Importer mon logo <span aria-hidden=\"true\">\u21a5<\/span><\/button><button type=\"button\" class=\"ct-textbtn\" data-action=\"demo\">Essayer avec l\u2019exemple<\/button><\/div><input type=\"file\" accept=\"image\/svg+xml,image\/png,image\/jpeg,image\/webp,.svg,.png,.jpg,.jpeg,.webp\" data-ui=\"file\" hidden><div class=\"ct-load\" data-ui=\"load\" hidden><span class=\"ct-spinner\"><\/span> Pr\u00e9paration du logo\u2026<\/div><\/div>\n  <div class=\"ct-error\" data-ui=\"error\" role=\"alert\" hidden><\/div>\n  <div class=\"ct-filebar\"><span class=\"ct-demo-pill\" data-ui=\"demo-badge\">EXEMPLE FICTIF<\/span><span class=\"ct-filemeta\" data-ui=\"filemeta\">NOVA \u00b7 logo de d\u00e9monstration<\/span><button type=\"button\" class=\"ct-textbtn\" data-action=\"reset\" hidden>Retirer mon logo<\/button><label class=\"ct-trim\"><input type=\"checkbox\" data-ui=\"trim\" checked> Recadrer les marges transparentes<\/label><\/div>\n  <div class=\"ct-tabs\" role=\"tablist\" aria-label=\"Les quatre \u00e9preuves\"><button type=\"button\" role=\"tab\" data-step=\"0\" aria-selected=\"true\" tabindex=\"0\"><span class=\"ct-tabnum\">01<\/span><span>Petit format<small>Ce qu\u2019il reste \u00e0 16 px<\/small><\/span><i aria-hidden=\"true\"><\/i><\/button><button type=\"button\" role=\"tab\" data-step=\"1\" aria-selected=\"false\" tabindex=\"-1\"><span class=\"ct-tabnum\">02<\/span><span>Les fonds<small>Clair, sombre, color\u00e9<\/small><\/span><i aria-hidden=\"true\"><\/i><\/button><button type=\"button\" role=\"tab\" data-step=\"2\" aria-selected=\"false\" tabindex=\"-1\"><span class=\"ct-tabnum\">03<\/span><span>Sans couleur<small>La forme fait le travail<\/small><\/span><i aria-hidden=\"true\"><\/i><\/button><button type=\"button\" role=\"tab\" data-step=\"3\" aria-selected=\"false\" tabindex=\"-1\"><span class=\"ct-tabnum\">04<\/span><span>Dans la vraie vie<small>Quatre mises en situation<\/small><\/span><i aria-hidden=\"true\"><\/i><\/button><\/div>\n  <div class=\"ct-workspace\" data-ui=\"workspace\">\n   <div class=\"ct-test\" role=\"tabpanel\" tabindex=\"0\" data-ui=\"test\"><div class=\"ct-test-heading\"><div><span class=\"ct-eyebrow\" data-ui=\"test-kicker\">01 \/ PETIT FORMAT<\/span><h2 data-ui=\"test-title\">La taille change tout.<\/h2><\/div><span class=\"ct-test-index\" data-ui=\"test-index\">01 \u2014 04<\/span><\/div><p class=\"ct-test-description\" data-ui=\"test-description\"><\/p><div class=\"ct-controls\" data-ui=\"controls\"><\/div><div class=\"ct-stage\" data-ui=\"stage\"><\/div><\/div>\n   <aside class=\"ct-side\" aria-label=\"Observations et conseils\"><div class=\"ct-side-heading\"><span class=\"ct-eyebrow\">REGARDE DE PLUS PR\u00c8S<\/span><h2 data-ui=\"side-title\">L\u2019essentiel reste-t-il ?<\/h2><\/div><div class=\"ct-observe\"><span class=\"ct-mini-label\">\u00c0 OBSERVER<\/span><ul data-ui=\"questions\"><\/ul><\/div><div class=\"ct-lesson\"><span class=\"ct-eyebrow\">LE PETIT D\u00c9CLIC<\/span><p data-ui=\"lesson\"><\/p><\/div><div class=\"ct-verdict\"><span class=\"ct-mini-label\">TON OBSERVATION<\/span><p>Note ce que tu vois pour ta fiche.<\/p><div class=\"ct-verdict-buttons\" role=\"group\" aria-label=\"Ton observation pour cette \u00e9preuve\"><button type=\"button\" data-verdict=\"ok\" aria-pressed=\"false\"><span aria-hidden=\"true\">\u2713<\/span> \u00c7a fonctionne<\/button><button type=\"button\" data-verdict=\"review\" aria-pressed=\"false\"><span aria-hidden=\"true\">\u2197<\/span> \u00c0 retravailler<\/button><button type=\"button\" data-verdict=\"unsure\" aria-pressed=\"false\"><span aria-hidden=\"true\">?<\/span> \u00c0 v\u00e9rifier<\/button><\/div><\/div><button type=\"button\" class=\"ct-btn ct-next\" data-action=\"next\">\u00c9preuve suivante <span aria-hidden=\"true\">\u2192<\/span><\/button><\/aside>\n  <\/div>\n  <div class=\"ct-diagnostic\"><div class=\"ct-summary-top\"><div><span class=\"ct-eyebrow\">DU TEST \u00c0 L\u2019ACTION<\/span><h2>Ce que ton logo<br><span>t\u2019a montr\u00e9.<\/span><\/h2><\/div><p>Des mesures pour comprendre le fichier.<br>Tes observations pour d\u00e9cider de la suite.<\/p><\/div><div class=\"ct-summary-grid\" data-ui=\"summary\"><\/div><div class=\"ct-findings\" data-ui=\"findings\"><\/div><details class=\"ct-method\"><summary>Comment lire ces r\u00e9sultats ?<\/summary><p>Les tailles du premier test sont exprim\u00e9es en pixels CSS, \u00e0 regarder au zoom 100 % du navigateur. Le contraste m\u00e9dian compare les pixels visibles du logo au fond choisi, apr\u00e8s prise en compte de leur transparence. Une m\u00e9diane ne d\u00e9crit pas chaque d\u00e9tail : observe aussi les petits textes et les traits fins. Les transformations monochromes sont des simulations de ce fichier. Le test ne d\u00e9cide pas \u00e0 ta place si un logo est beau ou lisible.<\/p><\/details><\/div>\n  <div class=\"ct-export\"><div><span class=\"ct-eyebrow\">GARDE UNE TRACE UTILE<\/span><h2>Ta fiche.<br><span>Les prochaines bonnes d\u00e9cisions.<\/span><\/h2><p>Les aper\u00e7us, les mesures et tes observations, r\u00e9unis dans un PDF de trois pages.<\/p><\/div><div class=\"ct-export-actions\"><label class=\"ct-name\">Nom de la marque<input type=\"text\" maxlength=\"36\" value=\"NOVA Studio\" data-ui=\"brand\" autocomplete=\"off\"><\/label><button type=\"button\" class=\"ct-btn ct-primary\" data-action=\"pdf\">T\u00e9l\u00e9charger ma fiche PDF <span aria-hidden=\"true\">\u2193<\/span><\/button><button type=\"button\" class=\"ct-textbtn\" data-action=\"png\">Exporter une image r\u00e9capitulative \u2197<\/button><span class=\"ct-export-note\">Gratuit \u00b7 pas d\u2019adresse email demand\u00e9e<\/span><\/div><\/div>\n  <div class=\"ct-studio-link\"><div><span class=\"ct-eyebrow\">ET APR\u00c8S ?<\/span><h2>Une identit\u00e9 qui fonctionne<br>sur tous tes supports.<\/h2><p>Logo, d\u00e9clinaisons, animation, site : transforme tes observations en un projet concret avec OK3D.<\/p><\/div><a class=\"ct-btn\" href=\"https:\/\/www.ok3d.fr\/configurateur-projet\/\">Construire mon projet <span aria-hidden=\"true\">\u2197<\/span><\/a><\/div>\n  <div class=\"ct-endnote\"><span>CRASH TEST LOGO \u2014 UNE EXP\u00c9RIENCE OK3D<\/span><span>Ton fichier reste sur cet appareil.<\/span><\/div>\n <\/div><\/div><div class=\"ct-toast\" data-ui=\"toast\" role=\"status\" aria-live=\"polite\" hidden><\/div><noscript><p style=\"padding:28px\">Active JavaScript pour importer ton logo et lancer les tests.<\/p><\/noscript>\n<\/div>\n<script>(() => {\n 'use strict';\n const root=document.querySelector('[data-okct=\"logo\"]:not([data-okct-ready])');if(!root)return;root.setAttribute('data-okct-ready','true');\n const CT = (() => {\n const clamp=(v,a,b)=>Math.max(a,Math.min(b,v));\n const rgb=hex=>[1,3,5].map(i=>parseInt(hex.slice(i,i+2),16));\n const linear=v=>{v\/=255;return v<=.04045?v\/12.92:Math.pow((v+.055)\/1.055,2.4);};\n const luminance=c=>.2126*linear(c[0])+.7152*linear(c[1])+.0722*linear(c[2]);\n const contrast=(a,b)=>{const x=luminance(a),y=luminance(b);return (Math.max(x,y)+.05)\/(Math.min(x,y)+.05);};\n const srgb=v=>Math.round(255*(v<=.0031308?v*12.92:1.055*Math.pow(v,1\/2.4)-.055));\n const verdicts={ok:'\u00c7a fonctionne',review:'\u00c0 retravailler',unsure:'\u00c0 v\u00e9rifier'};\n const backgrounds=[{name:'Blanc',color:'#ffffff'},{name:'Nuit',color:'#101725'},{name:'Bleu',color:'#2e6bff'},{name:'Papier',color:'#eee7d8'}];\n function inspect(data,width,height){\n  let x0=width,y0=height,x1=-1,y1=-1,transparent=0,visible=0;\n  const sample=[],stride=Math.max(1,Math.ceil(Math.sqrt(width*height\/35000)));\n  for(let y=0;y<height;y++)for(let x=0;x<width;x++){\n   const i=(y*width+x)*4,a=data[i+3];\n   if(a<250)transparent++;\n   if(a>8){visible++;x0=Math.min(x0,x);y0=Math.min(y0,y);x1=Math.max(x1,x);y1=Math.max(y1,y);}\n   if(a>=128&&x%stride===0&&y%stride===0)sample.push([data[i],data[i+1],data[i+2],a\/255]);\n  }\n  if(!visible)throw Error('Ce fichier est enti\u00e8rement transparent. Choisis une version visible de ton logo.');\n  if(!sample.length){const i=(y0*width+x0)*4;sample.push([data[i],data[i+1],data[i+2],data[i+3]\/255]);}\n  const bounds={x:x0,y:y0,w:x1-x0+1,h:y1-y0+1};\n  return {bounds,transparent:transparent\/(width*height),margins:1-bounds.w*bounds.h\/(width*height),sample};\n }\n function against(samples,background){\n  const bg=rgb(background),ratios=samples.map(p=>contrast(p.slice(0,3).map((v,i)=>v*p[3]+bg[i]*(1-p[3])),bg)).sort((a,b)=>a-b);\n  const n=ratios.length,median=n%2?ratios[(n-1)\/2]:(ratios[n\/2-1]+ratios[n\/2])\/2;\n  return {median,min:ratios[0],count:n};\n }\n function sniff(bytes){\n  if(bytes[0]===137&&bytes[1]===80&&bytes[2]===78&&bytes[3]===71)return 'png';\n  if(bytes[0]===255&&bytes[1]===216&&bytes[2]===255)return 'jpg';\n  const text=new TextDecoder().decode(bytes.subarray(0,100));\n  if(text.startsWith('RIFF')&&text.slice(8,12)==='WEBP')return 'webp';\n  return null;\n }\n function rasterSize(bytes,type){\n  const d=new DataView(bytes.buffer,bytes.byteOffset,bytes.byteLength);\n  if(type==='png'&&bytes.length>=24)return [d.getUint32(16),d.getUint32(20)];\n  if(type==='webp'&&bytes.length>=30){\n   const chunk=new TextDecoder().decode(bytes.subarray(12,16));\n   if(chunk==='VP8X')return [1+bytes[24]+bytes[25]*256+bytes[26]*65536,1+bytes[27]+bytes[28]*256+bytes[29]*65536];\n   if(chunk==='VP8 ')return [d.getUint16(26,true)&16383,d.getUint16(28,true)&16383];\n   if(chunk==='VP8L')return [1+bytes[21]+((bytes[22]&63)<<8),1+(bytes[22]>>6)+(bytes[23]<<2)+((bytes[24]&15)<<10)];\n  }\n  if(type==='jpg'){\n   let p=2;\n   while(p+9<bytes.length){\n    if(bytes[p]!==255){p++;continue;}\n    const marker=bytes[p+1];if(marker===216||marker===217){p+=2;continue;}\n    const len=d.getUint16(p+2);if(len<2||p+2+len>bytes.length)break;\n    if([192,193,194,195,197,198,199,201,202,203,205,206,207].includes(marker))return [d.getUint16(p+7),d.getUint16(p+5)];\n    p+=len+2;\n   }\n  }\n  return null;\n }\n function formatBytes(n){return n<1048576?(Math.round(n\/1024)||1)+' Ko':(n\/1048576).toFixed(1).replace('.',',')+' Mo';}\n function pdf(images){\n  const encoder=new TextEncoder(),parts=[],offsets=[0];let length=0;\n  const push=value=>{const b=typeof value==='string'?encoder.encode(value):value;parts.push(b);length+=b.length;};\n  const object=(id,body)=>{offsets[id]=length;push(id+' 0 obj\\n');if(Array.isArray(body))body.forEach(push);else push(body);push('\\nendobj\\n');};\n  push('%PDF-1.4\\n%\\xE2\\xE3\\xCF\\xD3\\n');\n  object(1,'<< \/Type \/Catalog \/Pages 2 0 R >>');\n  object(2,'<< \/Type \/Pages \/Count '+images.length+' \/Kids ['+images.map((_,i)=>(3+i*3)+' 0 R').join(' ')+'] >>');\n  images.forEach((image,i)=>{\n   const p=3+i*3,im=p+1,content=p+2;\n   object(p,'<< \/Type \/Page \/Parent 2 0 R \/MediaBox [0 0 595.28 841.89] \/Resources << \/XObject << \/Im0 '+im+' 0 R >> >> \/Contents '+content+' 0 R >>');\n   object(im,['<< \/Type \/XObject \/Subtype \/Image \/Width '+image.width+' \/Height '+image.height+' \/ColorSpace \/DeviceRGB \/BitsPerComponent 8 \/Filter \/DCTDecode \/Length '+image.data.length+' >>\\nstream\\n',image.data,'\\nendstream']);\n   const commands='q 595.28 0 0 841.89 0 0 cm \/Im0 Do Q\\n';object(content,'<< \/Length '+encoder.encode(commands).length+' >>\\nstream\\n'+commands+'endstream');\n  });\n  const xref=length,count=images.length*3+3;push('xref\\n0 '+count+'\\n0000000000 65535 f \\n');\n  for(let i=1;i<count;i++)push(String(offsets[i]).padStart(10,'0')+' 00000 n \\n');\n  push('trailer\\n<< \/Size '+count+' \/Root 1 0 R >>\\nstartxref\\n'+xref+'\\n%%EOF');\n  const out=new Uint8Array(length);let pos=0;for(const part of parts){out.set(part,pos);pos+=part.length;}return out;\n }\n return {clamp,rgb,luminance,contrast,linear,srgb,inspect,against,sniff,rasterSize,formatBytes,pdf,verdicts,backgrounds};\n})();\n\n const DEMO=\"<svg xmlns=\\\"http:\/\/www.w3.org\/2000\/svg\\\" width=\\\"560\\\" height=\\\"160\\\" viewBox=\\\"0 0 560 160\\\">\\n <g fill=\\\"#2e6bff\\\"><path d=\\\"m42 76 41-48h26L68 76Z\\\"\/><path d=\\\"m71 76 41-48h26L97 76Z\\\"\/><path d=\\\"m42 84 41 48h26L68 84Z\\\"\/><path d=\\\"m71 84 41 48h26L97 84Z\\\"\/><\/g>\\n <g fill=\\\"#142135\\\"><path d=\\\"M179 116V44h14l36 47V44h15v72h-14l-36-47v47Z\\\"\/><path fill-rule=\\\"evenodd\\\" d=\\\"M293 41c23 0 38 16 38 39s-15 39-38 39-38-16-38-39 15-39 38-39Zm0 15c-14 0-22 10-22 24s8 24 22 24 22-10 22-24-8-24-22-24Z\\\"\/><path d=\\\"m338 44 26 72h17l26-72h-17l-17 52-17-52Z\\\"\/><path fill-rule=\\\"evenodd\\\" d=\\\"m407 116 27-72h18l27 72h-17l-5-15h-28l-5 15Zm27-29h18l-9-27Z\\\"\/><\/g>\\n<\/svg>\\n\";\n const $=selector=>root.querySelector(selector),all=selector=>Array.from(root.querySelectorAll(selector)),ui=name=>$('[data-ui=\"'+name+'\"]');\n const reduced=window.matchMedia('(prefers-reduced-motion: reduce)');\n const state={step:0,source:null,trim:true,custom:'#173857',size:128,sizeBG:'#ffffff',mono:'all',statuses:[null,null,null,null],brand:'NOVA Studio',busy:false,exporting:false};\n const instance='okct-'+Math.random().toString(36).slice(2,10);ui('test').id=instance+'-panel';\n for(const tab of all('[data-step]')){tab.id=instance+'-tab-'+tab.dataset.step;tab.setAttribute('aria-controls',ui('test').id);}\n const STEPS=[\n  {name:'Petit format',title:'La taille change tout.',side:'L\u2019essentiel reste-t-il ?',description:'Un logo doit aussi vivre dans un onglet, un avatar ou un petit coin d\u2019\u00e9cran. Observe-le \u00e0 ses dimensions r\u00e9elles.',questions:['Peux-tu encore reconna\u00eetre le symbole \u00e0 16 ou 24 px ?','Le nom et les petits d\u00e9tails restent-ils distincts ?'],lesson:'On ne reconna\u00eet pas un logo miniature gr\u00e2ce \u00e0 ses d\u00e9tails, mais gr\u00e2ce \u00e0 sa silhouette. Une marque peut avoir une version simplifi\u00e9e pour les petits formats.'},\n  {name:'Les fonds',title:'M\u00eame logo. Autre lecture.',side:'Le fond change la donne.',description:'Les m\u00eames couleurs ne se d\u00e9tachent pas de la m\u00eame fa\u00e7on sur tous les fonds. Compare aussi les petits textes, pas seulement le symbole.',questions:['Des lettres ou des traits disparaissent-ils sur certains fonds ?','Le fichier contient-il un rectangle de fond qui g\u00eane son utilisation ?'],lesson:'Le contraste est une relation entre deux couleurs. Une version claire et une version sombre du logo permettent souvent de couvrir davantage de supports.'},\n  {name:'Sans couleur',title:'La forme fait-elle le travail ?',side:'Une seule encre, et alors ?',description:'Compare la version couleur, ses valeurs de gris, puis les simulations en une seule encre. Les transparences du fichier sont conserv\u00e9es.',questions:['Les diff\u00e9rentes parties restent-elles distinctes sans leurs couleurs ?','La silhouette pourrait-elle fonctionner sur un tampon ou une gravure ?'],lesson:'Les niveaux de gris conservent les diff\u00e9rences de luminosit\u00e9. Une seule encre les supprime : la reconnaissance repose alors sur la forme et les espaces vides.'},\n  {name:'Dans la vraie vie',title:'Une marque, plusieurs vies.',side:'Le contexte compte aussi.',description:'Quatre simulations pour changer de point de vue : profil social, carte, enseigne et packaging. Le logo garde ses proportions.',questions:['Le cadrage fonctionne-t-il dans un avatar carr\u00e9 ou circulaire ?','Le logo a-t-il assez de place pour respirer sur ces supports ?'],lesson:'Le support impose son cadrage, sa distance de lecture et son environnement. Tester plusieurs usages aide \u00e0 pr\u00e9voir les bonnes d\u00e9clinaisons.'}\n ];\n let timer=0,layoutFrame=0,drawFrame=0,loadToken=0,disposed=false;\n function notify(text){clearTimeout(timer);ui('toast').textContent=text;ui('toast').hidden=false;timer=setTimeout(()=>{ui('toast').hidden=true;},4000);}\n function error(text){ui('error').textContent=text||'';ui('error').hidden=!text;}\n const makeCanvas=(w,h)=>{const c=document.createElement('canvas');c.width=Math.max(1,Math.round(w));c.height=Math.max(1,Math.round(h));return c;};\n function cleanSVG(text){\n  if(\/<!DOCTYPE|<!ENTITY\/i.test(text))throw Error('Ce SVG contient une d\u00e9claration externe. Exporte un SVG autonome depuis ton logiciel.');\n  const doc=new DOMParser().parseFromString(text,'image\/svg+xml'),el=doc.documentElement;\n  if(doc.querySelector('parsererror')||!el||el.localName!=='svg'||el.namespaceURI!=='http:\/\/www.w3.org\/2000\/svg')throw Error('Le fichier SVG est invalide.');\n  for(const node of Array.from(doc.querySelectorAll('*'))){\n   if(['script','foreignObject','iframe','object','embed','audio','video','animate','animateMotion','animateTransform','set'].includes(node.localName)){node.remove();continue;}\n   if(node.namespaceURI!=='http:\/\/www.w3.org\/2000\/svg'){node.remove();continue;}\n   for(const attr of Array.from(node.attributes)){\n    if(\/^on\/i.test(attr.name)||attr.name==='xml:base'){node.removeAttribute(attr.name);continue;}\n    if(attr.localName==='href'&&!\/^#\/.test(attr.value)&&!\/^data:image\\\/(?:png|jpeg|webp);base64,[a-z0-9+\/=\\s]+$\/i.test(attr.value))throw Error('Ce SVG r\u00e9f\u00e9rence un fichier externe. Int\u00e8gre les images et vectorise les polices avant de l\u2019importer.');\n    for(const match of attr.value.matchAll(\/url\\(\\s*['\"]?([^)'\"\\s]+)\/gi))if(!match[1].startsWith('#'))throw Error('Ce SVG utilise une ressource externe. Choisis un SVG autonome.');\n   }\n   if(node.localName==='style'){\n    if(\/@import|@font-face|@namespace\/i.test(node.textContent))throw Error('Ce SVG charge des ressources externes. Vectorise les polices et int\u00e8gre ses images.');\n    for(const match of node.textContent.matchAll(\/url\\(\\s*['\"]?([^)'\"\\s]+)\/gi))if(!match[1].startsWith('#'))throw Error('Ce SVG utilise un style externe. Choisis un SVG autonome.');\n   }\n  }\n  const length=value=>{const m=\/^\\s*(\\d*\\.?\\d+)\\s*(px|pt|mm|cm|in)?\\s*$\/i.exec(value||'');return m?Number(m[1])*({px:1,pt:96\/72,mm:96\/25.4,cm:96\/2.54,in:96}[m[2]||'px']):0;};\n  let w=length(el.getAttribute('width')),h=length(el.getAttribute('height'));\n  const vb=(el.getAttribute('viewBox')||'').trim().split(\/[\\s,]+\/).map(Number);\n  if(vb.length===4&&vb.every(Number.isFinite)&&vb[2]>0&&vb[3]>0){if(!w&&!h){w=vb[2];h=vb[3];}else if(!w)w=h*vb[2]\/vb[3];else if(!h)h=w*vb[3]\/vb[2];}\n  if(!w||!h||!Number.isFinite(w*h))throw Error('Les dimensions du SVG ne sont pas d\u00e9finies. Exporte-le avec une zone de travail ou un viewBox.');\n  if(!el.hasAttribute('viewBox'))el.setAttribute('viewBox','0 0 '+w+' '+h);\n  const scale=Math.min(2048\/w,2048\/h);el.setAttribute('width',String(Math.max(1,Math.round(w*scale))));el.setAttribute('height',String(Math.max(1,Math.round(h*scale))));\n  return {blob:new Blob([new XMLSerializer().serializeToString(el)],{type:'image\/svg+xml'}),width:w,height:h,liveText:!!doc.querySelector('text,tspan,textPath'),hasRaster:!!doc.querySelector('image')};\n }\n async function decode(file,demo=false){\n  if(file.size>15*1048576)throw Error('Ce logo d\u00e9passe 15 Mo. Exporte une version SVG autonome ou une image plus l\u00e9g\u00e8re.');\n  if(!file.size)throw Error('Ce fichier est vide.');\n  const bytes=new Uint8Array(await file.arrayBuffer());let type=CT.sniff(bytes),blob=file,info=null;\n  if(!type){\n   const text=new TextDecoder().decode(bytes);\n   if(\/<svg[\\s>]\/i.test(text)||\/\\.svg$\/i.test(file.name)){info=cleanSVG(text);blob=info.blob;type='svg';}\n   else throw Error('Choisis un logo SVG, PNG, JPG ou WebP. Ce format n\u2019est pas pris en charge.');\n  }\n  const size=type==='svg'?[info.width,info.height]:CT.rasterSize(bytes,type);\n  if(type!=='svg'&&size&&(!size[0]||!size[1]||size[0]*size[1]>24000000||Math.max(...size)>16384))throw Error('Cette image est trop grande pour le test local : maximum 24 m\u00e9gapixels et 16 384 px sur un c\u00f4t\u00e9. R\u00e9duis ses dimensions ou utilise un SVG.');\n  const url=URL.createObjectURL(blob);\n  try{\n   const img=await new Promise((resolve,reject)=>{const im=new Image();im.onload=()=>resolve(im);im.onerror=()=>reject(Error('Le navigateur ne parvient pas \u00e0 lire ce fichier. Essaie un nouvel export PNG ou SVG.'));im.src=url;});\n   const ow=type==='svg'?info.width:img.naturalWidth,oh=type==='svg'?info.height:img.naturalHeight;\n   if(type!=='svg'&&(!ow||!oh||ow*oh>24000000||Math.max(ow,oh)>16384))throw Error('Cette image d\u00e9passe les dimensions prises en charge. R\u00e9duis-la ou importe un SVG.');\n   const k=Math.min(1,2048\/img.naturalWidth,2048\/img.naturalHeight),canvas=makeCanvas(img.naturalWidth*k,img.naturalHeight*k),ctx=canvas.getContext('2d',{willReadFrequently:true});\n   if(!ctx)throw Error('Le navigateur ne peut pas pr\u00e9parer cet aper\u00e7u.');\n   ctx.drawImage(img,0,0,canvas.width,canvas.height);\n   const pixels=ctx.getImageData(0,0,canvas.width,canvas.height),analysis=CT.inspect(pixels.data,canvas.width,canvas.height);\n   return {canvas,pixels,analysis,variants:{original:canvas},contrastCache:new Map(),type,width:ow,height:oh,name:file.name,bytes:file.size,demo,liveText:!!(info&&info.liveText),hasRaster:!!(info&&info.hasRaster)};\n  }finally{URL.revokeObjectURL(url);}\n }\n async function load(file,demo=false){\n  if(state.exporting){notify('La fiche est en pr\u00e9paration. Attends la fin de l\u2019export pour changer de logo.');return;}\n  const token=++loadToken;state.busy=true;ui('load').hidden=false;error('');setDisabled();\n  try{\n   const source=await decode(file,demo);if(token!==loadToken||disposed)return;\n   releaseSource(state.source);state.source=source;state.statuses=[null,null,null,null];state.trim=true;ui('trim').checked=true;\n   state.brand=demo?'NOVA Studio':file.name.replace(\/\\.[^.]+$\/,'').replace(\/[_-]+\/g,' ').slice(0,36)||'Ma marque';ui('brand').value=state.brand;\n   ui('upload-title').textContent=demo?'D\u00e9pose ton logo. On regarde \u00e7a.':file.name;\n   ui('upload-detail').textContent=demo?'SVG, PNG, JPG ou WebP \u00b7 15 Mo maximum':'Logo pr\u00eat \u00e0 tester \u00b7 tu peux en importer un autre';\n   ui('file-note').textContent=demo?'Un exemple fictif est d\u00e9j\u00e0 pr\u00eat pour d\u00e9couvrir les tests.':'Ton fichier est trait\u00e9 dans ce navigateur, sans envoi vers le studio.';\n   ui('demo-badge').hidden=!demo;$('[data-action=\"reset\"]').hidden=demo;\n   ui('filemeta').textContent=source.type.toUpperCase()+' \u00b7 '+CT.formatBytes(source.bytes)+' \u00b7 '+(source.type==='svg'?(source.hasRaster?'SVG avec image int\u00e9gr\u00e9e':'Original vectoriel'):Math.round(source.width)+' \u00d7 '+Math.round(source.height)+' px');\n   renderAll();if(!demo)notify('Ton logo est pr\u00eat. \u00c0 toi de passer les quatre \u00e9preuves.');\n  }catch(e){if(token===loadToken)error(e.message||'Impossible de lire ce logo.');}\n  finally{if(token===loadToken){state.busy=false;ui('load').hidden=true;setDisabled();}}\n }\n function releaseSource(source){if(!source)return;for(const canvas of Object.values(source.variants)){canvas.width=1;canvas.height=1;}source.pixels=null;}\n function variant(mode){\n  const source=state.source;if(source.variants[mode])return source.variants[mode];\n  const c=makeCanvas(source.canvas.width,source.canvas.height),ctx=c.getContext('2d'),out=ctx.createImageData(c.width,c.height),input=source.pixels.data;\n  for(let i=0;i<input.length;i+=4){const v=mode==='white'?255:mode==='ink'?0:CT.srgb(CT.luminance([input[i],input[i+1],input[i+2]]));out.data[i]=out.data[i+1]=out.data[i+2]=v;out.data[i+3]=input[i+3];}\n  ctx.putImageData(out,0,0);source.variants[mode]=c;return c;\n }\n function drawLogo(ctx,x,y,w,h,mode='original'){\n  if(!state.source)return;\n  const source=state.source,box=state.trim?source.analysis.bounds:{x:0,y:0,w:source.canvas.width,h:source.canvas.height};\n  const k=Math.min(w\/box.w,h\/box.h),dw=box.w*k,dh=box.h*k;\n  ctx.save();ctx.imageSmoothingEnabled=true;ctx.imageSmoothingQuality='high';ctx.drawImage(variant(mode),box.x,box.y,box.w,box.h,x+(w-dw)\/2,y+(h-dh)\/2,dw,dh);ctx.restore();\n }\n function rounded(ctx,x,y,w,h,r,fill,stroke){ctx.beginPath();ctx.roundRect(x,y,w,h,r);if(fill){ctx.fillStyle=fill;ctx.fill();}if(stroke){ctx.strokeStyle=stroke;ctx.lineWidth=1;ctx.stroke();}}\n function canvasText(ctx,text,x,y,font,color,align='left'){ctx.font=font;ctx.fillStyle=color;ctx.textAlign=align;ctx.fillText(text,x,y);ctx.textAlign='left';}\n function scene(ctx,w,h,type){\n  const gradient=ctx.createLinearGradient(0,0,w,h);gradient.addColorStop(0,'#dce3eb');gradient.addColorStop(1,'#b8c3d0');ctx.fillStyle=gradient;ctx.fillRect(0,0,w,h);\n  ctx.save();ctx.scale(w\/640,h\/400);\n  if(type==='social'){\n   ctx.fillStyle='#111d30';ctx.fillRect(0,0,640,400);rounded(ctx,118,48,404,310,18,'#f5f7fa');\n   rounded(ctx,118,48,404,80,18,'#dbe6f6');ctx.fillStyle='#dbe6f6';ctx.fillRect(118,95,404,33);\n   ctx.save();ctx.beginPath();ctx.arc(320,152,60,0,Math.PI*2);ctx.fillStyle='#fff';ctx.fill();ctx.clip();drawLogo(ctx,270,115,100,76);ctx.restore();\n   ctx.strokeStyle='#fff';ctx.lineWidth=5;ctx.beginPath();ctx.arc(320,152,60,0,Math.PI*2);ctx.stroke();\n   canvasText(ctx,state.brand||'Ta marque',320,248,'600 24px \"Geist\", sans-serif','#1a2b46','center');\n   canvasText(ctx,'Une identit\u00e9, m\u00eame en petit.',320,277,'14px \"Geist\", sans-serif','#73839a','center');rounded(ctx,253,300,134,29,15,'#2e6bff');canvasText(ctx,'D\u00e9couvrir',320,320,'500 12px \"Geist\", sans-serif','#fff','center');\n  }else if(type==='card'){\n   ctx.save();ctx.translate(320,198);ctx.rotate(-.09);ctx.shadowColor='#20324c35';ctx.shadowBlur=28;ctx.shadowOffsetY=22;rounded(ctx,-219,-127,438,254,7,'#f8f5ee');ctx.restore();\n   ctx.save();ctx.translate(320,198);ctx.rotate(-.09);drawLogo(ctx,-160,-72,320,118);ctx.fillStyle='#dad8d1';ctx.fillRect(-159,83,178,1);canvasText(ctx,state.brand||'Ta marque',-158,105,'11px \"Geist Mono\", monospace','#7b8290');ctx.restore();\n  }else if(type==='sign'){\n   ctx.fillStyle='#b9c1c8';ctx.fillRect(0,0,640,400);\n   for(let i=0;i<8;i++){ctx.fillStyle=i%2?'#bec6cd':'#b3bdc6';ctx.fillRect(0,i*52,640,1);}\n   ctx.fillStyle='#243347';ctx.fillRect(90,155,460,245);ctx.fillStyle='#172336';ctx.fillRect(102,178,207,222);ctx.fillRect(323,178,215,222);\n   const glass=ctx.createLinearGradient(100,190,540,400);glass.addColorStop(0,'#6f859b60');glass.addColorStop(1,'#d9e0e80a');ctx.fillStyle=glass;ctx.fillRect(106,181,199,219);ctx.fillRect(328,181,206,219);\n   ctx.save();ctx.shadowColor='#111c3955';ctx.shadowBlur=12;ctx.shadowOffsetY=9;rounded(ctx,76,63,488,111,3,'#f0f0e9');ctx.restore();drawLogo(ctx,142,88,356,61);ctx.fillStyle='#8190a1';ctx.fillRect(310,187,8,213);\n  }else{\n   const bg=ctx.createLinearGradient(0,0,640,400);bg.addColorStop(0,'#e8e4dc');bg.addColorStop(1,'#cbc7c0');ctx.fillStyle=bg;ctx.fillRect(0,0,640,400);\n   ctx.save();ctx.translate(322,337);ctx.scale(1,.18);ctx.shadowColor='#1e263b60';ctx.shadowBlur=30;ctx.beginPath();ctx.ellipse(0,0,150,40,0,0,Math.PI*2);ctx.fillStyle='#8b8d93';ctx.fill();ctx.restore();\n   ctx.beginPath();ctx.moveTo(184,99);ctx.lineTo(376,70);ctx.lineTo(459,108);ctx.lineTo(264,139);ctx.closePath();ctx.fillStyle='#f6f2e8';ctx.fill();\n   ctx.beginPath();ctx.moveTo(376,70);ctx.lineTo(459,108);ctx.lineTo(459,316);ctx.lineTo(376,285);ctx.closePath();ctx.fillStyle='#b9c0c9';ctx.fill();\n   ctx.beginPath();ctx.moveTo(184,99);ctx.lineTo(376,70);ctx.lineTo(376,285);ctx.lineTo(184,322);ctx.closePath();ctx.fillStyle='#eeeade';ctx.fill();\n   ctx.save();ctx.transform(1,-.17,0,1,184,99);drawLogo(ctx,22,57,147,85);canvasText(ctx,state.brand||'Ta marque',24,184,'9px \"Geist Mono\", monospace','#7c8490');ctx.restore();\n  }\n  ctx.restore();\n }\n function prepareCanvas(canvas){\n  const rect=canvas.getBoundingClientRect();if(!rect.width||!rect.height)return null;\n  const dpr=Math.min(devicePixelRatio||1,2);const width=Math.max(1,Math.round(rect.width*dpr)),height=Math.max(1,Math.round(rect.height*dpr));\n  if(canvas.width!==width)canvas.width=width;if(canvas.height!==height)canvas.height=height;\n  const ctx=canvas.getContext('2d');ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,rect.width,rect.height);return {ctx,w:rect.width,h:rect.height};\n }\n function paintStage(){\n  drawFrame=0;if(!state.source||!root.isConnected)return;\n  for(const canvas of all('[data-draw]')){\n   const box=prepareCanvas(canvas);if(!box)continue;const {ctx,w,h}=box,kind=canvas.dataset.draw,bg=canvas.dataset.bg||state.sizeBG;\n   if(kind==='scene'){scene(ctx,w,h,canvas.dataset.scene);continue;}\n   ctx.fillStyle=bg;ctx.fillRect(0,0,w,h);\n   if(kind==='actual'){drawLogo(ctx,0,0,w,h);continue;}\n   drawLogo(ctx,w*.12,h*.22,w*.76,h*.56,canvas.dataset.mode||'original');\n  }\n }\n function schedulePaint(){if(!drawFrame)drawFrame=requestAnimationFrame(paintStage);}\n const tag=(name,attrs={},text)=>{const el=document.createElement(name);for(const[key,value]of Object.entries(attrs))el.setAttribute(key,value);if(text!==undefined)el.textContent=text;return el;};\n function canvasNode(kind,label,attrs={}){return tag('canvas',{'data-draw':kind,'role':'img','aria-label':label,...attrs});}\n function previewCard(label,background,mode,metric){\n  const card=tag('div',{class:'ct-preview-card'}),canvas=canvasNode('logo',label,{'data-bg':background,'data-mode':mode||'original'}),cap=tag('div',{class:'ct-card-caption'});\n  cap.append(tag('span',{},label));if(metric)cap.append(tag('small',{},metric));card.append(canvas,cap);return card;\n }\n function against(bg){const cache=state.source.contrastCache;if(!cache.has(bg))cache.set(bg,CT.against(state.source.analysis.sample,bg));return cache.get(bg);}\n function ratioFor(bg){return against(bg).median.toFixed(1).replace('.',',')+' : 1';}\n function renderStage(){\n  const stage=ui('stage');stage.replaceChildren();if(!state.source)return;\n  if(state.step===0){\n   const ref=tag('div',{class:'ct-reference'});ref.append(canvasNode('logo','Ton logo \u00e0 une taille confortable',{'data-bg':'#f3f2ed'}),tag('span',{},'UNE VUE CONFORTABLE, AVANT DE R\u00c9DUIRE.'));stage.append(ref);\n   const grid=tag('div',{class:'ct-size-grid'});\n   for(const size of [16,24,32,48,64,96]){\n    const card=tag('div',{class:'ct-size-tile'});card.style.setProperty('--tile-bg',state.sizeBG);card.style.setProperty('--tile-ink',CT.luminance(CT.rgb(state.sizeBG))>.3?'#53627c':'#becde3');\n    const canvas=canvasNode('actual','Logo dans un cadre de '+size+' pixels CSS',{class:'ct-actual'});canvas.style.width=size+'px';canvas.style.height=size+'px';card.append(canvas,tag('span',{},size+' \u00d7 '+size+' PX'));grid.append(card);\n   }stage.append(grid);\n   const custom=tag('div',{class:'ct-custom-size'});custom.style.setProperty('--tile-bg',state.sizeBG);custom.style.setProperty('--tile-ink',CT.luminance(CT.rgb(state.sizeBG))>.3?'#53627c':'#becde3');\n   custom.append(tag('span',{},'TON CADRE \u00b7 '+state.size+' \u00d7 '+state.size+' PX'));\n   const canvas=canvasNode('actual','Logo dans le cadre personnalis\u00e9 de '+state.size+' pixels CSS',{class:'ct-actual'});canvas.style.width=state.size+'px';canvas.style.height=state.size+'px';custom.append(canvas);stage.append(custom,tag('p',{class:'ct-stage-note'},'Tailles r\u00e9elles en pixels CSS. Regarde au zoom 100 % du navigateur.'));\n  }else if(state.step===1){\n   const grid=tag('div',{class:'ct-back-grid'});for(const bg of CT.backgrounds)grid.append(previewCard(bg.name,bg.color,'original','M\u00e9diane '+ratioFor(bg.color)));stage.append(grid);\n   const custom=previewCard('Ton fond',state.custom,'original','M\u00e9diane '+ratioFor(state.custom));custom.classList.add('ct-back-custom');stage.append(custom,tag('p',{class:'ct-stage-note'},'Le contraste m\u00e9dian est un rep\u00e8re mesur\u00e9 sur les pixels visibles, pas un verdict de lisibilit\u00e9.'));\n  }else if(state.step===2){\n   const grid=tag('div',{class:'ct-mono-grid'}),modes=state.mono==='all'?['original','gray','ink','white']:[state.mono];\n   for(const mode of modes)grid.append(previewCard({original:'La version couleur',gray:'Niveaux de gris',ink:'Une seule encre',white:'R\u00e9serve blanche'}[mode],mode==='white'?'#101725':'#f5f3ed',mode));stage.append(grid);\n   if(state.source.analysis.transparent<.01)stage.append(tag('p',{class:'ct-stage-note'},'Ton fichier est opaque : son fond est transform\u00e9 lui aussi. Une version transparente permettrait d\u2019isoler le logo.'));\n  }else{\n   const grid=tag('div',{class:'ct-scene-grid'});for(const[type,label]of [['social','Profil social'],['card','Carte de visite'],['sign','Enseigne'],['box','Packaging']]){\n    const card=tag('div',{class:'ct-preview-card'});card.append(canvasNode('scene','Simulation : '+label,{'data-scene':type}),tag('div',{class:'ct-card-caption'},label));grid.append(card);\n   }stage.append(grid,tag('p',{class:'ct-stage-note'},'Simulations visuelles : le cadrage et les proportions comptent. La production r\u00e9elle demande ses propres v\u00e9rifications.'));\n  }\n  schedulePaint();\n }\n function chips(values,key,current){const box=tag('div',{class:'ct-chips',role:'group','aria-label':key==='bg'?'Fond des petits formats':'Affichage monochrome'});for(const[value,label]of values)box.append(tag('button',{type:'button','data-setting':key,'data-value':value,'aria-pressed':String(value===current)},label));return box;}\n function renderControls(){\n  const box=ui('controls');box.replaceChildren();\n  if(state.step===0){\n   box.append(tag('span',{class:'ct-mini-label'},'LE FOND DU TEST'),chips(CT.backgrounds.slice(0,3).map(bg=>[bg.color,bg.name]),'bg',state.sizeBG));\n   const label=tag('label',{}),line=tag('span',{class:'ct-control-label'},'Cadre personnalis\u00e9'),out=tag('output',{'data-ui':'size-value'},state.size+' px');line.append(out);\n   const input=tag('input',{type:'range',min:'12',max:'256',value:String(state.size),'data-setting':'size','aria-label':'Taille du cadre personnalis\u00e9 en pixels CSS'});input.style.setProperty('--v',(state.size-12)\/244*100+'%');label.append(line,input);box.append(label,tag('p',{class:'ct-help'},'La taille d\u00e9signe le cadre carr\u00e9. Un logo tr\u00e8s horizontal occupera moins de hauteur.'));\n  }else if(state.step===1){\n   const label=tag('label',{class:'ct-color-label'},'Choisis ton fond');label.append(tag('input',{type:'color',value:state.custom,'data-setting':'custom','aria-label':'Couleur du fond personnalis\u00e9'}));box.append(label,tag('p',{class:'ct-help'},'Compare les fonds, puis essaie la couleur r\u00e9ellement utilis\u00e9e sur ton site ou tes supports.'));\n  }else if(state.step===2){box.append(tag('span',{class:'ct-mini-label'},'COMPARER LES VERSIONS'),chips([['all','Les quatre'],['gray','Gris'],['ink','Encre noire'],['white','Blanc']],'mono',state.mono));}\n  else box.append(tag('p',{class:'ct-help'},'Le nom de marque saisi en bas de page appara\u00eet dans les simulations et dans ta fiche. Les marges autour du logo restent importantes.'));\n }\n function findings(){\n  const s=state.source;if(!s)return[];const out=[];\n  out.push(s.type==='svg'?{title:s.hasRaster?'Un SVG avec une image int\u00e9gr\u00e9e':s.liveText?'Du texte encore \u00e9ditable':'Un original vectoriel',text:s.hasRaster?'Ce SVG contient une image bitmap. Le conteneur SVG ne la rend pas vectorielle : v\u00e9rifie sa d\u00e9finition en cas d\u2019agrandissement.':s.liveText?'Ce SVG contient du texte non vectoris\u00e9. La typographie peut changer si la police n\u2019est pas disponible sur l\u2019appareil. Vectorise-la pour une diffusion fid\u00e8le.':'Les contours peuvent \u00eatre redimensionn\u00e9s sans la pixellisation propre aux images bitmap. Garde aussi cet original pour tes d\u00e9clinaisons.'}:{title:'Un fichier en pixels',text:'L\u2019original mesure '+Math.round(s.width)+' \u00d7 '+Math.round(s.height)+' px. Un agrandissement peut rendre ses contours flous : conserve une source vectorielle si tu en disposes.'});\n  out.push(s.analysis.transparent<.01?{title:'Le fond fait partie du fichier',text:'Ce fichier est presque enti\u00e8rement opaque. Son rectangle de fond appara\u00eet dans les tests et sera aussi transform\u00e9 en monochrome. Pr\u00e9vois une version transparente pour les usages qui le demandent.'}:{title:'De la transparence pour les supports',text:'Le fichier contient des pixels transparents. V\u00e9rifie les contours sur fond clair et sombre : un l\u00e9ger halo peut \u00eatre peu visible sur le fond d\u2019origine.'});\n  if(s.analysis.margins>.2)out.push({title:'Un cadre plus large que le dessin',text:'Environ '+Math.round(s.analysis.margins*100)+' % de la surface se situe autour du cadre visible du logo. Le recadrage du test permet de comparer le dessin lui-m\u00eame ; il ne modifie pas ton fichier.'});\n  const weak=CT.backgrounds.filter(bg=>against(bg.color).median<2);\n  if(weak.length)out.push({title:'Des fonds \u00e0 regarder de pr\u00e8s',text:'Contraste m\u00e9dian inf\u00e9rieur \u00e0 2:1 sur '+weak.map(bg=>bg.name.toLowerCase()).join(', ')+'. Ce rep\u00e8re invite \u00e0 regarder les d\u00e9tails et \u00e0 envisager une autre d\u00e9clinaison si n\u00e9cessaire.'});\n  const marked=state.statuses.filter(Boolean).length,review=state.statuses.filter(v=>v==='review').length;\n  out.push({title:marked+' \u00e9preuve'+(marked>1?'s':'')+' observ\u00e9e'+(marked>1?'s':''),text:!marked?'Renseigne tes observations dans les quatre \u00e9preuves. Elles seront reprises telles quelles dans la fiche.':review?review+' point'+(review>1?'s':'')+' \u00e0 retravailler selon tes observations. Garde les exemples concrets pour pr\u00e9parer la prochaine version.':'Tes observations figurent dans la fiche. Pense aussi \u00e0 tester les supports r\u00e9els et les \u00e9ventuelles petites d\u00e9clinaisons.'});\n  return out;\n }\n function renderSummary(){\n  const box=ui('summary');box.replaceChildren();if(!state.source)return;const s=state.source;\n  const data=[['LE FICHIER',s.type.toUpperCase(),CT.formatBytes(s.bytes)+' \u00b7 '+(s.demo?'exemple fictif':'ton original')],['LA SOURCE',s.type==='svg'?(s.hasRaster?'SVG mixte':'Vectorielle'):Math.round(s.width)+' \u00d7 '+Math.round(s.height),'Les aper\u00e7us gardent les proportions.'],['LE FOND',s.analysis.transparent<.01?'Opaque':'Transparence','Le fond du fichier compte aussi.'],['TES OBSERVATIONS',state.statuses.filter(Boolean).length+' \/ 4','Aucun r\u00e9sultat esth\u00e9tique automatique.']];\n  for(const[label,value,note]of data){const el=tag('div',{class:'ct-stat'});el.append(tag('span',{},label),tag('strong',{},value),tag('small',{},note));box.append(el);}\n  ui('findings').replaceChildren(...findings().map((finding,index)=>{const el=tag('div',{class:'ct-finding'}),text=tag('div');text.append(tag('strong',{},finding.title),tag('p',{},finding.text));el.append(tag('span',{},String(index+1).padStart(2,'0')),text);return el;}));\n }\n function updateProgress(){\n  ui('progress').textContent=state.statuses.filter(Boolean).length+' \/ 4';\n   ui('test').setAttribute('aria-labelledby',instance+'-tab-'+state.step);\n  for(const tab of all('[data-step]')){const i=Number(tab.dataset.step);tab.setAttribute('aria-selected',String(i===state.step));tab.setAttribute('tabindex',i===state.step?'0':'-1');if(state.statuses[i])tab.dataset.state=state.statuses[i];else delete tab.dataset.state;}\n  for(const button of all('[data-verdict]'))button.setAttribute('aria-pressed',String(button.dataset.verdict===state.statuses[state.step]));\n  $('[data-action=\"next\"]').innerHTML=state.step===3?'Voir ma fiche <span aria-hidden=\"true\">\u2193<\/span>':'\u00c9preuve suivante <span aria-hidden=\"true\">\u2192<\/span>';\n }\n function renderAll(){\n  const step=STEPS[state.step];ui('test-kicker').textContent=String(state.step+1).padStart(2,'0')+' \/ '+step.name.toUpperCase();ui('test-title').textContent=step.title;ui('test-index').textContent=String(state.step+1).padStart(2,'0')+' \u2014 04';ui('test-description').textContent=step.description;ui('side-title').textContent=step.side;ui('lesson').textContent=step.lesson;\n  ui('questions').replaceChildren(...step.questions.map(q=>tag('li',{},q)));renderControls();renderStage();updateProgress();renderSummary();setDisabled();\n }\n function setDisabled(){for(const el of all('input,button'))if(!el.hasAttribute('data-okk-fullscreen'))el.disabled=state.busy||state.exporting||(!state.source&&el.dataset.action!=='upload'&&el.dataset.action!=='demo');}\n function changeStep(step,scroll=false){state.step=CT.clamp(step,0,3);renderAll();if(scroll)ui('test').scrollIntoView({behavior:reduced.matches?'instant':'smooth',block:'start'});}\n function demoFile(){return new File([DEMO],'NOVA-exemple.svg',{type:'image\/svg+xml'});}\n function download(blob,name){const url=URL.createObjectURL(blob),link=tag('a',{href:url,download:name});root.append(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),60000);}\n function reportPages(){\n const W=1440,H=2036,pages=[];\n const ink='#14243c',muted='#657995',blue='#2e6bff',line='#d9e2ef';\n const text=(ctx,value,x,y,size=24,color=ink,font='Geist',weight=400)=>canvasText(ctx,value,x,y,weight+' '+size+'px \"'+font+'\", sans-serif',color);\n const wrap=(ctx,value,x,y,width,size=24,color=muted,leading=36,limit=10)=>{\n  ctx.font='400 '+size+'px \"Geist\", sans-serif';ctx.fillStyle=color;let row='',count=0;\n  for(const word of String(value).split(\/\\s+\/)){\n   if(ctx.measureText((row?row+' ':'')+word).width>width&&row){ctx.fillText(row,x,y);y+=leading;count++;row=word;if(count>=limit-1)break;}else row+=(row?' ':'')+word;\n  }\n  if(count>=limit-1&&ctx.measureText(row).width>width){while(ctx.measureText(row+'\u2026').width>width)row=row.slice(0,-1);row+='\u2026';}\n  if(row)ctx.fillText(row,x,y);return y+leading;\n };\n const fit=(ctx,value,x,y,size,width,font='Unbounded')=>{ctx.font='500 '+size+'px \"'+font+'\", sans-serif';while(ctx.measureText(value).width>width&&size>25){size--;ctx.font='500 '+size+'px \"'+font+'\", sans-serif';}ctx.fillStyle=ink;ctx.fillText(value,x,y);};\n const base=(number,title,sub)=>{\n  const c=makeCanvas(W,H),ctx=c.getContext('2d');ctx.fillStyle='#f6f8fc';ctx.fillRect(0,0,W,H);\n  text(ctx,'CRASH TEST LOGO \/ OK3D',72,83,20,blue,'Geist Mono',500);\n  canvasText(ctx,String(number).padStart(2,'0')+' \/ 03',1368,83,'400 20px \"Geist Mono\", monospace',muted,'right');\n  fit(ctx,title,72,180,56,1296);wrap(ctx,sub,72,234,1280,23,muted,34,3);\n  ctx.strokeStyle=line;ctx.beginPath();ctx.moveTo(72,1959);ctx.lineTo(1368,1959);ctx.stroke();\n  text(ctx,'OK3D.FR \/ CR\u00c9ATION VISUELLE, BRANDING ET 3D',72,1995,17,muted,'Geist Mono');\n  canvasText(ctx,new Date().toLocaleDateString('fr-FR'),1368,1995,'400 17px \"Geist Mono\", monospace',muted,'right');\n  pages.push(c);return ctx;\n };\n const card=(ctx,x,y,w,h,bg='#fff')=>rounded(ctx,x,y,w,h,18,bg,line);\n const logoPanel=(ctx,x,y,w,h,bg,mode='original')=>{card(ctx,x,y,w,h,bg);drawLogo(ctx,x+w*.12,y+h*.22,w*.76,h*.56,mode);};\n const source=state.source,a=source.analysis;\n const fileLabel=source.name.length>85?source.name.slice(0,82)+'\u2026':source.name;\n const one=base(1,state.brand||'Ma marque',(source.demo?'EXEMPLE FICTIF \u00b7 ':'')+fileLabel+' \u00b7 '+source.type.toUpperCase()+' \u00b7 '+CT.formatBytes(source.bytes));\n logoPanel(one,72,290,1296,260,'#edf1f6');\n const stats=[['SOURCE',source.type==='svg'?(source.hasRaster?'SVG mixte':'Vectorielle'):Math.round(source.width)+' \u00d7 '+Math.round(source.height)+' px'],['FORMAT',source.type.toUpperCase()],['FOND',a.transparent<.01?'Opaque':'Transparent'],['OBSERVATIONS',state.statuses.filter(Boolean).length+' \/ 4']];\n stats.forEach(([label,value],i)=>{const x=72+i*330;card(one,x,582,306,133);text(one,label,x+22,620,17,muted,'Geist Mono');fit(one,value,x+22,672,25,260,'Geist');});\n text(one,'Tes quatre observations',72,792,31,ink,'Unbounded',500);\n state.statuses.forEach((value,i)=>{\n  const y=828+i*91;card(one,72,y,1296,76);text(one,String(i+1).padStart(2,'0'),94,y+47,21,blue,'Geist Mono');text(one,STEPS[i].name,158,y+47,25,ink,'Geist',500);\n  canvasText(one,value?CT.verdicts[value]:'Non renseign\u00e9e',1340,y+47,'500 22px \"Geist\", sans-serif',value==='review'?'#966118':value==='ok'?'#24735b':muted,'right');\n });\n text(one,'La r\u00e9duction, en un regard',72,1264,31,ink,'Unbounded',500);\n [16,24,32,48,64,96].forEach((size,i)=>{\n  const x=72+(i%3)*440,y=1305+Math.floor(i\/3)*220;card(one,x,y,416,196,'#fff');\n  const s=size*1.65;drawLogo(one,x+(416-s)\/2,y+25+(115-s)\/2,s,s);text(one,size+' \u00d7 '+size+' PX',x+20,y+171,18,muted,'Geist Mono');\n });\n wrap(one,'Comparaison proportionnelle. Les tailles r\u00e9elles se v\u00e9rifient dans le test en ligne, au zoom 100 % du navigateur. Les observations sont les tiennes ; elles ne constituent pas une note esth\u00e9tique automatique.',72,1805,1275,23,muted,36,3);\n const two=base(2,'Ce qui change avec le fond.','Le contraste est mesur\u00e9 sur les pixels visibles du fichier. La m\u00e9diane donne un rep\u00e8re global ; v\u00e9rifie les petits d\u00e9tails s\u00e9par\u00e9ment.');\n CT.backgrounds.forEach((bg,i)=>{\n  const x=72+(i%2)*660,y=310+Math.floor(i\/2)*320;logoPanel(two,x,y,636,260,bg.color);\n  text(two,bg.name,x,y+294,23,ink,'Geist',500);canvasText(two,'M\u00e9diane '+ratioFor(bg.color),x+636,y+294,'400 20px \"Geist Mono\", monospace',muted,'right');\n });\n logoPanel(two,72,970,1296,175,state.custom);text(two,'Ton fond '+state.custom.toUpperCase(),72,1184,22,ink,'Geist',500);canvasText(two,'M\u00e9diane '+ratioFor(state.custom),1368,1184,'400 20px \"Geist Mono\", monospace',muted,'right');\n text(two,'La couleur s\u2019efface. La forme reste ?',72,1270,29,ink,'Unbounded',500);\n [['original','Couleur'],['gray','Niveaux de gris'],['ink','Une seule encre'],['white','R\u00e9serve blanche']].forEach(([mode,label],i)=>{\n  const x=72+(i%2)*660,y=1313+Math.floor(i\/2)*244;logoPanel(two,x,y,636,190,mode==='white'?'#101725':'#ffffff',mode);text(two,label,x,y+225,22,ink,'Geist',500);\n });\n wrap(two,a.transparent<.01?'Ton fichier est opaque. Son fond est lui aussi transform\u00e9 en monochrome : une version transparente permettrait d\u2019isoler la forme du logo.':'Ces simulations conservent la transparence du fichier. Une version une encre pr\u00e9par\u00e9e par un designer peut diff\u00e9rer de cette transformation automatique.',72,1859,1290,21,muted,32,2);\n const three=base(3,'D\u2019un support \u00e0 l\u2019autre.','Quatre simulations pour discuter du cadrage, des proportions et des d\u00e9clinaisons. Elles ne remplacent pas une validation sur les supports de production.');\n [['social','Profil social'],['card','Carte de visite'],['sign','Enseigne'],['box','Packaging']].forEach(([type,label],i)=>{\n  const x=72+(i%2)*660,y=310+Math.floor(i\/2)*435;three.save();rounded(three,x,y,636,357,16);three.clip();three.translate(x,y);scene(three,636,357,type);three.restore();text(three,label,x,y+395,24,ink,'Geist',500);\n });\n text(three,'Les prochains bons gestes',72,1260,31,ink,'Unbounded',500);\n const items=findings().slice(0,3);\n items.forEach((finding,i)=>{const y=1295+i*170;card(three,72,y,1296,150);text(three,String(i+1).padStart(2,'0'),96,y+40,19,blue,'Geist Mono');text(three,finding.title,148,y+40,24,ink,'Geist',500);wrap(three,finding.text,148,y+80,1160,21,muted,31,3);});\n rounded(three,72,1831,1296,94,15,'#152b4d');text(three,'Pour faire \u00e9voluer ton identit\u00e9 avec OK3D',96,1867,23,'#e8f1ff','Geist',500);text(three,'www.ok3d.fr\/configurateur-projet\/',96,1901,19,'#b5cff7','Geist Mono');\n return pages;\n}\nfunction sharePage(){\n const page=makeCanvas(1600,1800),ctx=page.getContext('2d');ctx.fillStyle='#09101d';ctx.fillRect(0,0,1600,1800);\n canvasText(ctx,'CRASH TEST LOGO \/ OK3D',72,87,'500 21px \"Geist Mono\", monospace','#a8caff');\n let size=63;ctx.font='500 '+size+'px \"Unbounded\", sans-serif';while(ctx.measureText(state.brand||'Ma marque').width>1450&&size>28){size--;ctx.font='500 '+size+'px \"Unbounded\", sans-serif';}ctx.fillStyle='#edf3fd';ctx.fillText(state.brand||'Ma marque',72,188);\n canvasText(ctx,state.source.demo?'EXEMPLE FICTIF \u00b7 '+state.statuses.filter(Boolean).length+' \/ 4 observations renseign\u00e9es':state.statuses.filter(Boolean).length+' \/ 4 observations renseign\u00e9es',72,240,'400 20px \"Geist Mono\", monospace','#9fb3d1');\n rounded(ctx,72,288,1456,300,20,'#f0f3f7');drawLogo(ctx,210,350,1180,175);\n const card=(i,title)=>{const x=72+(i%2)*744,y=625+Math.floor(i\/2)*472;rounded(ctx,x,y,712,440,18,'#132139','#2d4263');canvasText(ctx,String(i+1).padStart(2,'0')+' \/ '+title,x+24,y+38,'400 18px \"Geist Mono\", monospace','#adcaef');return {x,y};};\n let b=card(0,'PETIT FORMAT');ctx.fillStyle='#fff';ctx.fillRect(b.x+24,b.y+65,664,285);[24,48,96].forEach((n,i)=>{drawLogo(ctx,b.x+70+i*205,b.y+148-n\/2,n,n);canvasText(ctx,n+' PX',b.x+70+i*205+n\/2,b.y+291,'400 17px \"Geist Mono\", monospace','#6f8098','center');});\n b=card(1,'LES FONDS');ctx.fillStyle='#fff';ctx.fillRect(b.x+24,b.y+65,332,285);ctx.fillStyle='#101725';ctx.fillRect(b.x+356,b.y+65,332,285);drawLogo(ctx,b.x+52,b.y+140,277,122);drawLogo(ctx,b.x+384,b.y+140,277,122);\n b=card(2,'SANS COULEUR');ctx.fillStyle='#fff';ctx.fillRect(b.x+24,b.y+65,332,285);ctx.fillStyle='#101725';ctx.fillRect(b.x+356,b.y+65,332,285);drawLogo(ctx,b.x+52,b.y+140,277,122,'ink');drawLogo(ctx,b.x+384,b.y+140,277,122,'white');\n b=card(3,'EN SITUATION');ctx.save();ctx.beginPath();ctx.rect(b.x+24,b.y+65,664,285);ctx.clip();ctx.translate(b.x+24,b.y+65);scene(ctx,664,285,'card');ctx.restore();\n state.statuses.forEach((value,i)=>{const x=72+(i%2)*744,y=625+Math.floor(i\/2)*472;canvasText(ctx,value?CT.verdicts[value]:'Observation \u00e0 compl\u00e9ter',x+24,y+401,'500 21px \"Geist\", sans-serif',value==='ok'?'#9ae7c4':value==='review'?'#f3cb94':'#b0c1d9');});\n canvasText(ctx,'Les observations sont personnelles. Les aper\u00e7us sont des simulations.',72,1658,'400 20px \"Geist\", sans-serif','#9cb0cd');\n ctx.strokeStyle='#34445e';ctx.beginPath();ctx.moveTo(72,1700);ctx.lineTo(1528,1700);ctx.stroke();\n canvasText(ctx,'TESTE TON LOGO SUR OK3D.FR',72,1753,'500 21px \"Geist Mono\", monospace','#d0e2ff');return page;\n}\n\n async function exportFile(kind){\n  if(!state.source||state.busy||state.exporting)return;\n  state.exporting=true;setDisabled();notify(kind==='pdf'?'Pr\u00e9paration des trois pages de ta fiche\u2026':'Pr\u00e9paration de ton image r\u00e9capitulative\u2026');\n  let pages=[];\n  try{\n   await new Promise(resolve=>requestAnimationFrame(resolve));\n   if(document.fonts){\n    const specimen='CRASH TEST LOGO OK3D NOVA Studio Cr\u00e9ation visuelle \u00c0 v\u00e9rifier \u00e9\u00e8\u00e0\u00e7\u0153 0123456789';\n    await Promise.race([Promise.allSettled(['400 24px \"Geist\"','500 24px \"Geist\"','600 24px \"Geist\"','400 20px \"Geist Mono\"','500 20px \"Geist Mono\"','500 56px \"Unbounded\"'].map(font=>document.fonts.load(font,specimen))),new Promise(resolve=>setTimeout(resolve,3000))]);\n   }\n   const slug=(state.brand||'ma-marque').normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').replace(\/[^a-z0-9]+\/gi,'-').replace(\/^-|-$\/g,'').slice(0,45)||'logo';\n   if(kind==='pdf'){\n    pages=reportPages();const images=[];\n    for(const page of pages){const b64=page.toDataURL('image\/jpeg',.94).split(',')[1],binary=atob(b64);images.push({width:page.width,height:page.height,data:Uint8Array.from(binary,c=>c.charCodeAt(0))});await new Promise(resolve=>setTimeout(resolve,0));}\n    download(new Blob([CT.pdf(images)],{type:'application\/pdf'}),'Crash-Test-Logo-'+slug+'.pdf');\n   }else{\n    const page=sharePage();pages=[page];const blob=await new Promise((resolve,reject)=>page.toBlob(b=>b?resolve(b):reject(Error('L\u2019image n\u2019a pas pu \u00eatre pr\u00e9par\u00e9e.')),'image\/png'));download(blob,'Crash-Test-Logo-'+slug+'.png');\n   }\n   notify('Ta fiche est pr\u00eate. Le t\u00e9l\u00e9chargement a \u00e9t\u00e9 lanc\u00e9.');\n  }catch(e){error('L\u2019export n\u2019a pas abouti : '+(e.message||'r\u00e9essaie avec un logo plus l\u00e9ger.'));}\n  finally{for(const page of pages){page.width=1;page.height=1;}state.exporting=false;setDisabled();}\n }\n root.addEventListener('click',event=>{\n  const button=event.target.closest('button');if(!button||button.disabled||!root.contains(button))return;\n  if(button.hasAttribute('data-step')){changeStep(Number(button.dataset.step));return;}\n  if(button.dataset.verdict){state.statuses[state.step]=button.dataset.verdict;updateProgress();renderSummary();return;}\n  if(button.dataset.setting){const key=button.dataset.setting;if(key==='bg')state.sizeBG=button.dataset.value;else if(key==='mono')state.mono=button.dataset.value;state.statuses[state.step]=null;renderAll();return;}\n  const action=button.dataset.action;\n  if(action==='upload')ui('file').click();\n  else if(action==='demo'||action==='reset'){state.step=0;load(demoFile(),true);}\n  else if(action==='next'){if(state.step<3)changeStep(state.step+1,true);else $('.ct-export').scrollIntoView({behavior:reduced.matches?'instant':'smooth',block:'center'});}\n  else if(action==='pdf'||action==='png')exportFile(action);\n });\n root.addEventListener('input',event=>{\n  const input=event.target;\n  if(input===ui('brand')){state.brand=input.value.slice(0,36);if(state.step===3)schedulePaint();return;}\n  if(input.dataset.setting==='size'){state.size=CT.clamp(Number(input.value),12,256);input.style.setProperty('--v',(state.size-12)\/244*100+'%');const out=ui('size-value');if(out)out.textContent=state.size+' px';state.statuses[0]=null;renderStage();updateProgress();renderSummary();}\n  if(input.dataset.setting==='custom'){state.custom=input.value;state.statuses[1]=null;renderStage();updateProgress();renderSummary();}\n });\n ui('file').addEventListener('change',event=>{const file=event.target.files[0];event.target.value='';if(file)load(file);});\n ui('trim').addEventListener('change',event=>{state.trim=event.target.checked;state.statuses=[null,null,null,null];renderAll();});\n $('.ct-tabs').addEventListener('keydown',event=>{\n  const button=event.target.closest('[data-step]');if(!button)return;let next=Number(button.dataset.step);\n  if(event.key==='ArrowRight')next=(next+1)%4;else if(event.key==='ArrowLeft')next=(next+3)%4;else if(event.key==='Home')next=0;else if(event.key==='End')next=3;else return;\n  event.preventDefault();changeStep(next);$('[data-step=\"'+next+'\"]').focus();\n });\n let dragDepth=0;\n root.addEventListener('dragenter',event=>{if(Array.from(event.dataTransfer?.types||[]).includes('Files')){event.preventDefault();dragDepth++;ui('drop').classList.add('over');}});\n root.addEventListener('dragover',event=>{if(Array.from(event.dataTransfer?.types||[]).includes('Files'))event.preventDefault();});\n root.addEventListener('dragleave',()=>{dragDepth=Math.max(0,dragDepth-1);if(!dragDepth)ui('drop').classList.remove('over');});\n root.addEventListener('drop',event=>{const files=Array.from(event.dataTransfer?.files||[]);if(!files.length)return;event.preventDefault();dragDepth=0;ui('drop').classList.remove('over');if(files.length>1)notify('Un seul logo \u00e0 la fois : le premier fichier sera utilis\u00e9.');load(files[0]);});\n root.addEventListener('paste',event=>{if(event.target.closest('input,textarea,[contenteditable=\"true\"]'))return;const file=event.clipboardData?.files?.[0];if(file){event.preventDefault();load(file);}});\n function fitLayout(){\n  layoutFrame=0;if(!root.isConnected){cleanup();return;}\n  const full=(document.fullscreenElement||document.webkitFullscreenElement)===root||root.classList.contains('is-app-screen');\n  const put=(name,value)=>{if(root.style.getPropertyValue(name)!==value)root.style.setProperty(name,value);};\n  if(!full){const shift=parseFloat(root.style.getPropertyValue('--ct-shift'))||0;put('--ct-shift',(Math.round((shift-root.getBoundingClientRect().left)*100)\/100)+'px');put('--ct-width',document.documentElement.clientWidth+'px');}\n  const admin=document.getElementById('wpadminbar');put('--okk-admin-height',(!full&&admin&&getComputedStyle(admin).position==='fixed'?admin.getBoundingClientRect().height:0)+'px');schedulePaint();\n }\n function scheduleLayout(){if(!layoutFrame)layoutFrame=requestAnimationFrame(fitLayout);}\n const observer=typeof ResizeObserver==='function'?new ResizeObserver(scheduleLayout):null;\n if(observer){observer.observe(document.documentElement);observer.observe(ui('workspace'));if(root.parentElement)observer.observe(root.parentElement);}\n window.addEventListener('resize',scheduleLayout);window.addEventListener('orientationchange',scheduleLayout);if(window.visualViewport)window.visualViewport.addEventListener('resize',scheduleLayout);\n if(document.fonts)document.fonts.ready.then(()=>{if(root.isConnected)scheduleLayout();});\n function cleanup(){if(disposed)return;disposed=true;loadToken++;if(observer)observer.disconnect();if(layoutFrame)cancelAnimationFrame(layoutFrame);if(drawFrame)cancelAnimationFrame(drawFrame);clearTimeout(timer);releaseSource(state.source);window.removeEventListener('resize',scheduleLayout);window.removeEventListener('orientationchange',scheduleLayout);if(window.visualViewport)window.visualViewport.removeEventListener('resize',scheduleLayout);}\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})(root);\n load(demoFile(),true);fitLayout();\n})();\n<\/script>\n\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 diagnostic 0 \/ 4 CRASH TEST LOGO Ton logo tient-illa route ? Un beau logo. Partout, vraiment ? Importe le tien. Observe ce qui r\u00e9siste.Repars avec les bonnes questions et ta fiche de v\u00e9rification.4 \u00e9preuvesSans compteSur ton appareil D\u00e9pose ton logo. On regarde \u00e7a.SVG, PNG, JPG ou WebP \u00b7 15 Mo maximumUn [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-15733","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/pages\/15733","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=15733"}],"version-history":[{"count":5,"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/pages\/15733\/revisions"}],"predecessor-version":[{"id":15768,"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/pages\/15733\/revisions\/15768"}],"wp:attachment":[{"href":"https:\/\/www.ok3d.fr\/en\/wp-json\/wp\/v2\/media?parent=15733"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}