{"id":141,"date":"2026-09-21T12:19:47","date_gmt":"2026-09-21T12:19:47","guid":{"rendered":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/veille\/"},"modified":"2026-10-02T12:31:21","modified_gmt":"2026-10-02T12:31:21","slug":"veille","status":"publish","type":"page","link":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/veille\/","title":{"rendered":"Veille technologique"},"content":{"rendered":"\n<div class=\"wp-block-group hero-navy is-layout-flow wp-block-group-is-layout-flow\">\n<h1 class=\"wp-block-heading\">Veille <em>technologique<\/em><\/h1>\n\n\n<p class=\"wp-block-paragraph\">Suivi de l&rsquo;actualit\u00e9 des syst\u00e8mes, des r\u00e9seaux et de la cybers\u00e9curit\u00e9.<\/p>\n\n<\/div>\n\n\n<div class=\"wp-block-group is-layout-flow wp-block-group-is-layout-flow\">\n<h2 class=\"wp-block-heading\">M\u00e9thode<\/h2>\n\n\n<ul class=\"legal-list\"><li><span>Th\u00e8me principal<\/span><b>R\u00e9seau et Wi-Fi d&rsquo;entreprise<\/b><\/li><li><span>Th\u00e8mes secondaires<\/span><b>S\u00e9curit\u00e9 des \u00e9quipements r\u00e9seau \u00b7 T\u00e9l\u00e9phonie IP<\/b><\/li><li><span>Rythme<\/span><b>Hebdomadaire<\/b><\/li><li><span>Outils<\/span><b>Flux RSS \u00b7 Bulletins de s\u00e9curit\u00e9 constructeurs<\/b><\/li><li><span>P\u00e9riode couverte<\/span><b>Depuis septembre 2025<\/b><\/li><\/ul>\n\n\n<p class=\"wp-block-paragraph\">Pour chaque information retenue, je note la <strong>date<\/strong>, la <strong>source<\/strong> et l&rsquo;<strong>impact m\u00e9tier<\/strong> : c&rsquo;est le format attendu \u00e0 l&rsquo;oral, qui permet de retrouver et de justifier une information devant le jury.<\/p>\n\n<\/div>\n\n\n<div class=\"wp-block-group is-layout-flow wp-block-group-is-layout-flow\">\n<h2 class=\"wp-block-heading\">Sources suivies<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Sources que je consulte pour suivre l&rsquo;actualit\u00e9 des \u00e9quipements et des architectures r\u00e9seau.<\/p>\n\n\n<h3 class=\"wp-block-heading\">R\u00e9seau et Wi-Fi<\/h3>\n\n\n<ul class=\"badges\"><li>LeMagIT \u2014 rubrique R\u00e9seau<\/li><li>IT-Connect<\/li><li>Bulletins de s\u00e9curit\u00e9 Ubiquiti<\/li><li>Notes de version TP-Link<\/li><li>Documentation Teltonika<\/li><\/ul>\n\n\n<h3 class=\"wp-block-heading\">S\u00e9curit\u00e9 des \u00e9quipements<\/h3>\n\n\n<ul class=\"badges\"><li>CERT-FR (ANSSI)<\/li><li>Bulletins CVE<\/li><li>IT-Connect \u2014 rubrique S\u00e9curit\u00e9<\/li><\/ul>\n\n\n<h3 class=\"wp-block-heading\">T\u00e9l\u00e9phonie IP<\/h3>\n\n\n<ul class=\"badges\"><li>Notes de version Snom<\/li><li>Notes de version Yealink<\/li><\/ul>\n\n<\/div>\n\n\n<div class=\"wp-block-group is-layout-flow wp-block-group-is-layout-flow\">\n<h2 class=\"wp-block-heading\">Journal de veille<\/h2>\n\n\n<div class=\"tbl-wrap\"><table class=\"tbl\"><thead><tr><th>Date<\/th><th>Source<\/th><th>Information<\/th><th>Impact m\u00e9tier<\/th><\/tr><\/thead><tbody><tr><td class=\"mono\">26\/08\/2026<\/td><td><a href=\"https:\/\/www.it-connect.tech\/22-vulnerabilities-fixed-in-the-unifi-ecosystem-including-3-rated-cvss-10-10\/\" target=\"_blank\" rel=\"noopener\">IT-Connect<\/a><br><span class=\"dim\">Bulletin Ubiquiti<\/span><\/td><td>Ubiquiti corrige 22 vuln\u00e9rabilit\u00e9s dans l&rsquo;\u00e9cosyst\u00e8me UniFi, dont trois not\u00e9es <strong>CVSS 10\/10<\/strong> (CVE-2026-77537 sur UniFi Protect, CVE-2026-77550 sur UniFi OS, CVE-2026-77554 sur UniFi Talk). Elles permettent l&rsquo;ex\u00e9cution de commandes \u00e0 distance ou le contournement d&rsquo;authentification, sans interaction de l&rsquo;utilisateur.<\/td><td>Nous d\u00e9ployons des bornes Wi-Fi UniFi chez nos clients. J&rsquo;ai v\u00e9rifi\u00e9 les versions en service et identifi\u00e9 celles \u00e0 mettre \u00e0 jour (UniFi Network 10.5.67 ou sup\u00e9rieur). Des failles comparables de mai 2026 ont \u00e9t\u00e9 exploit\u00e9es en moins d&rsquo;un mois : la mise \u00e0 jour ne peut pas attendre.<\/td><\/tr><tr><td class=\"mono\">03\/02\/2026<\/td><td><a href=\"https:\/\/www.lemagit.fr\/conseil\/Reseau-Comment-bien-commencer-le-deploiement-du-Wifi-7\" target=\"_blank\" rel=\"noopener\">LeMagIT<\/a><br><span class=\"dim\">Dossier r\u00e9seau<\/span><\/td><td>Le d\u00e9ploiement du Wi-Fi 7 impose des pr\u00e9requis d&rsquo;infrastructure : liens multi-gigabit vers les bornes, alimentation <strong>PoE++<\/strong>, c\u00e2blage parfois \u00e0 reprendre, contr\u00f4leurs WLAN \u00e0 mettre \u00e0 jour et <strong>WPA3 obligatoire<\/strong>.<\/td><td>Nous installons des switchs TP-Link PoE et des bornes Wi-Fi. Avant de proposer du Wi-Fi 7 \u00e0 un client, il faut v\u00e9rifier le budget PoE des switchs et l&rsquo;\u00e9tat du c\u00e2blage : le point bloquant est souvent l\u00e0, pas sur la borne elle-m\u00eame.<\/td><\/tr><tr><td class=\"mono\">26\/03\/2026<\/td><td><a href=\"https:\/\/www.clubic.com\/actualite-606319-tp-link-alerte-sur-plusieurs-failles-de-securite-graves-ces-routeurs-doivent-etre-mis-a-jour.html\" target=\"_blank\" rel=\"noopener\">Clubic<\/a><br><span class=\"dim\">Alerte TP-Link<\/span><\/td><td>TP-Link signale quatre vuln\u00e9rabilit\u00e9s sur ses routeurs <strong>Archer NX200, NX210, NX500 et NX600<\/strong>. La plus grave (CVE-2025-15517, CVSS 8.6) permet de <strong>contourner l&rsquo;authentification<\/strong> du serveur web et de modifier la configuration ou pousser un firmware sans identifiants. Trois autres (CVE-2025-15518, 15519, 15605) autorisent l&rsquo;injection de commandes ou le d\u00e9chiffrement des fichiers de configuration.<\/td><td>TP-Link fait partie de nos \u00e9quipements courants. Ce type d&rsquo;alerte rappelle qu&rsquo;un routeur dont l&rsquo;interface d&rsquo;administration reste joignable est une porte d&rsquo;entr\u00e9e : il faut appliquer les firmwares correctifs et ne jamais exposer l&rsquo;administration c\u00f4t\u00e9 WAN.<\/td><\/tr><tr><td class=\"mono\">24\/01\/2022<br><span class=\"dim\">r\u00e9f\u00e9rence<\/span><\/td><td><a href=\"https:\/\/www.lemagit.fr\/conseil\/Securite-comment-mettre-en-uvre-la-segmentation-du-reseau\" target=\"_blank\" rel=\"noopener\">LeMagIT<\/a><br><span class=\"dim\">M\u00e9thode<\/span><\/td><td>Article de fond sur la segmentation r\u00e9seau : d\u00e9finir des r\u00e8gles par type de ressource, privil\u00e9gier les <strong>listes d&rsquo;autorisation<\/strong> plut\u00f4t que d&rsquo;interdiction, et s&rsquo;appuyer sur pare-feu, ACL et VLAN.<\/td><td>Base m\u00e9thodologique que j&rsquo;applique sur les installations clients : s\u00e9parer administration, t\u00e9l\u00e9phonie IP et Wi-Fi invit\u00e9 dans des VLAN distincts pour limiter la propagation en cas de compromission.<\/td><\/tr><\/tbody><\/table><\/div>\n\n\n<div class=\"fun-fact\"><strong>&#128161; Conseil pour l&rsquo;oral<\/strong>Le jury demande souvent de d\u00e9velopper une information pr\u00e9cise. Garde au moins trois entr\u00e9es que tu sais expliquer : ce que tu as lu, pourquoi \u00e7a compte, et ce que tu en as fait.<\/div>\n\n<\/div>\n\n\n\n<!-- =========================================================\n     CONSOLE INTERACTIVE \u2014 Portfolio Nicolas Jedrosik\n     \u00c0 coller dans un bloc \"HTML personnalis\u00e9\" de WordPress.\n     ========================================================= -->\n\n<div id=\"njc-overlay\">\n  <div id=\"njc-window\">\n    <div id=\"njc-bar\">\n      <span class=\"njc-dot njc-red\"><\/span>\n      <span class=\"njc-dot njc-yellow\"><\/span>\n      <span class=\"njc-dot njc-green\"><\/span>\n      <span id=\"njc-title\">bash \u2014 nicolas@portfolio<\/span>\n      <span id=\"njc-close\" title=\"Fermer (Echap)\">\u2715<\/span>\n    <\/div>\n    <div id=\"njc-body\">\n      <div id=\"njc-out\"><\/div>\n      <div id=\"njc-inputline\">\n        <span class=\"njc-prompt\">nicolas@portfolio<\/span><span class=\"njc-sep\">:<\/span><span class=\"njc-path\">~<\/span><span class=\"njc-sep\">$<\/span>\n        <input id=\"njc-input\" autocomplete=\"off\" spellcheck=\"false\" \/>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<div id=\"njc-hint\"><kbd>Ctrl<\/kbd> + <kbd>K<\/kbd> \u2014 ouvrir la console<\/div>\n\n<style>\n#njc-overlay{\n  position:fixed;inset:0;z-index:99999;display:none;\n  background:rgba(3,7,13,.82);backdrop-filter:blur(5px);\n  align-items:center;justify-content:center;padding:20px;\n}\n#njc-overlay.njc-on{display:flex;animation:njcFade .18s ease}\n@keyframes njcFade{from{opacity:0}to{opacity:1}}\n#njc-window{\n  width:min(760px,100%);background:#050a12;border:1px solid rgba(34,211,238,.28);\n  border-radius:12px;overflow:hidden;box-shadow:0 30px 80px rgba(0,0,0,.7);\n  font-family:'JetBrains Mono',ui-monospace,monospace;\n  animation:njcPop .22s cubic-bezier(.2,.9,.3,1.3);\n}\n@keyframes njcPop{from{transform:scale(.95) translateY(12px);opacity:0}to{transform:none;opacity:1}}\n#njc-bar{display:flex;align-items:center;gap:7px;padding:11px 14px;background:#0c1522;\n  border-bottom:1px solid rgba(34,211,238,.16)}\n.njc-dot{width:11px;height:11px;border-radius:50%}\n.njc-red{background:#ff5f57}.njc-yellow{background:#febc2e}.njc-green{background:#28c840}\n#njc-title{flex:1;text-align:center;font-size:.72rem;color:#64748b}\n#njc-close{cursor:pointer;color:#64748b;font-size:.8rem;padding:0 4px}\n#njc-close:hover{color:#ff5f57}\n#njc-body{padding:16px 18px 18px;max-height:62vh;overflow-y:auto;font-size:.84rem;line-height:1.7}\n#njc-out div{white-space:pre-wrap;word-break:break-word;color:#94a3b8}\n#njc-out .njc-c{color:#22d3ee}\n#njc-out .njc-v{color:#a78bfa}\n#njc-out .njc-g{color:#34d399}\n#njc-out .njc-w{color:#fbbf24}\n#njc-out .njc-echo{color:#e2e8f0}\n#njc-inputline{display:flex;align-items:center;gap:6px;margin-top:8px}\n.njc-prompt{color:#34d399}.njc-path{color:#22d3ee}.njc-sep{color:#64748b}\n#njc-input{flex:1;background:transparent;border:0;outline:0;color:#e2e8f0;\n  font-family:inherit;font-size:.84rem;caret-color:#22d3ee}\n#njc-hint{\n  position:fixed;right:16px;bottom:38px;z-index:9997;\n  font-family:'JetBrains Mono',monospace;font-size:.66rem;color:#475569;\n  background:rgba(10,18,32,.9);border:1px solid rgba(34,211,238,.18);\n  border-radius:7px;padding:6px 10px;cursor:pointer;transition:.2s\n}\n#njc-hint:hover{color:#22d3ee;border-color:rgba(34,211,238,.5)}\n#njc-hint kbd{background:#0c1522;border:1px solid rgba(34,211,238,.3);border-radius:4px;\n  padding:1px 5px;color:#22d3ee;font-family:inherit;font-size:.62rem}\n@media(max-width:600px){#njc-hint{display:none}}\n<\/style>\n\n<script>\n(function () {\n  if (window.__njConsole) return;\n  window.__njConsole = true;\n\n  var BASE = '\/index.php\/';\n  var PAGES = {\n    'accueil':   { path: '',            desc: 'page d\\u2019accueil' },\n    'a-propos':  { path: 'a-propos\/',   desc: 'qui je suis, mes comp\\u00e9tences' },\n    'parcours':  { path: 'parcours\/',   desc: 'formation et exp\\u00e9rience' },\n    'portfolio': { path: 'portfolio\/',  desc: 'mes projets' },\n    'contact':   { path: 'contact\/',    desc: 'me contacter' },\n    'entreprise':  { path: 'entreprise\/',       desc: 'mon entreprise d\\u2019accueil' },\n    'veille':      { path: 'veille\/',           desc: 'veille technologique' },\n    'competences': { path: 'competences\/',      desc: 'tableau de synth\\u00e8se' },\n    'legal':       { path: 'mentions-legales\/', desc: 'mentions l\\u00e9gales' }\n  };\n  var ALIAS = { 'projets':'portfolio','about':'a-propos','home':'accueil','cv':'parcours','me':'contact','boite':'entreprise','societe':'entreprise','synthese':'competences','skills':'competences','mentions':'legal','legales':'legal' };\n\n  var ov  = document.getElementById('njc-overlay');\n  var out = document.getElementById('njc-out');\n  var inp = document.getElementById('njc-input');\n  var hint= document.getElementById('njc-hint');\n  var history = [], hIdx = -1, busy = false;\n\n  function line(text, cls) {\n    var d = document.createElement('div');\n    if (cls) d.className = cls;\n    d.innerHTML = text;\n    out.appendChild(d);\n    document.getElementById('njc-body').scrollTop = 1e9;\n    return d;\n  }\n  function typeLine(text, cls, speed) {\n    return new Promise(function (res) {\n      var d = line('', cls), i = 0;\n      var t = setInterval(function () {\n        d.textContent = text.slice(0, ++i);\n        document.getElementById('njc-body').scrollTop = 1e9;\n        if (i >= text.length) { clearInterval(t); res(); }\n      }, speed || 22);\n    });\n  }\n  var wait = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };\n\n  function open(pre) {\n    ov.classList.add('njc-on');\n    if (!out.childElementCount) {\n      line('<span class=\"njc-c\">Console portfolio<\/span> \\u2014 Nicolas Jedrosik \\u00b7 BTS SIO SISR');\n      line('Tapez <span class=\"njc-g\">help<\/span> pour la liste des commandes.');\n      line('&nbsp;');\n    }\n    setTimeout(function () { inp.focus(); }, 60);\n    if (pre) { inp.value = pre; }\n  }\n  function close() { ov.classList.remove('njc-on'); }\n\n  function go(slug) { window.location.href = BASE + PAGES[slug].path; }\n\n  \/\/ ---- Commandes ----\n  var CMDS = {\n    help: function () {\n      line('<span class=\"njc-c\">Commandes disponibles<\/span>');\n      line('  <span class=\"njc-g\">ls<\/span>            liste les pages du site');\n      line('  <span class=\"njc-g\">cd<\/span> &lt;page&gt;     ouvre une page (ex: cd portfolio)');\n      line('  <span class=\"njc-g\">whoami<\/span>        qui je suis');\n      line('  <span class=\"njc-g\">skills<\/span>        mes comp\\u00e9tences techniques');\n      line('  <span class=\"njc-g\">neofetch<\/span>      fiche d\\u2019identit\\u00e9 compl\\u00e8te');\n      line('  <span class=\"njc-g\">contact<\/span>       comment me joindre');\n      line('  <span class=\"njc-g\">date<\/span>          date et heure');\n      line('  <span class=\"njc-g\">history<\/span>       historique des commandes');\n      line('  <span class=\"njc-g\">clear<\/span>         efface la console');\n      line('  <span class=\"njc-g\">exit<\/span>          ferme la console');\n      if (root) {\n        line('&nbsp;');\n        line('<span class=\"njc-w\">Mode superviseur actif<\/span>');\n        line('  <span class=\"njc-g\">nmap<\/span>          scan du portfolio');\n        line('  <span class=\"njc-g\">incident<\/span>      simule un incident reseau');\n        line('  <span class=\"njc-g\">uptime<\/span>        temps ecoule depuis la rentree');\n        line('  <span class=\"njc-g\">traceroute<\/span>    diagnostic reseau avance');\n        line('  <span class=\"njc-g\">reboot<\/span>        quitte le mode superviseur');\n        line('  <span class=\"njc-g\">mute<\/span>          coupe ou retablit le son');\n      }\n    },\n    ls: function () {\n      line('total ' + Object.keys(PAGES).length);\n      Object.keys(PAGES).forEach(function (k) {\n        line('drwxr-xr-x  nicolas  <span class=\"njc-c\">' + k.padEnd(11) + '<\/span>  ' + PAGES[k].desc);\n      });\n      line('&nbsp;');\n      line('<span class=\"njc-w\">astuce<\/span> : <span class=\"njc-g\">cd portfolio<\/span> pour ouvrir une page.');\n    },\n    whoami: function () {\n      line('Nicolas Jedrosik');\n      line('\\u00c9tudiant BTS SIO \\u00b7 option <span class=\"njc-c\">SISR<\/span> \\u00b7 FAB Academy, La Roche-sur-Yon');\n      line('Alternant chez <span class=\"njc-v\">Loire T\\u00e9l\\u00e9com<\/span> \\u2014 r\\u00e9seau, t\\u00e9l\\u00e9phonie, syst\\u00e8mes');\n    },\n    skills: function () {\n      line('<span class=\"njc-c\">Syst\\u00e8mes<\/span>       Windows Server \\u00b7 Linux');\n      line('<span class=\"njc-c\">Virtualisation<\/span> Proxmox');\n      line('<span class=\"njc-c\">R\\u00e9seau<\/span>         Teltonika \\u00b7 TP-Link \\u00b7 Unifi \\u00b7 VLAN \\u00b7 PoE');\n      line('<span class=\"njc-c\">T\\u00e9l\\u00e9phonie<\/span>     Snom \\u00b7 Yealink \\u00b7 Clearly Anywhere');\n      line('<span class=\"njc-c\">Langues<\/span>        Anglais \\u2014 niveau B2');\n    },\n    contact: function () {\n      line('Ouverture de la page contact...');\n      setTimeout(function () { go('contact'); }, 700);\n    },\n    date: function () { line(new Date().toLocaleString('fr-FR')); },\n    clear: function () { out.innerHTML = ''; },\n    exit: function () { line('D\\u00e9connexion...'); setTimeout(close, 450); },\n    neofetch: function () {\n      var rows = [\n        ['nicolas', '@portfolio'],\n        ['--------', ''],\n        ['Formation', 'BTS SIO \\u2014 option SISR'],\n        ['\\u00c9cole', 'FAB Academy, La Roche-sur-Yon'],\n        ['Alternance', 'Loire T\\u00e9l\\u00e9com'],\n        ['Sp\\u00e9cialit\\u00e9s', 'R\\u00e9seau \\u00b7 T\\u00e9l\\u00e9phonie \\u00b7 Syst\\u00e8mes'],\n        ['Langues', 'Fran\\u00e7ais \\u00b7 Anglais B2'],\n        ['Localisation', 'Challans, Vend\\u00e9e']\n      ];\n      var art = ['   .--.   ', '  |o_o |  ', '  |:_\/ |  ', ' \/\/   \\\\ \\\\ ', '(|     | )', \"\/'\\\\_   _\/`\\\\\", '\\\\___)=(___\/'];\n      for (var i = 0; i < Math.max(art.length, rows.length); i++) {\n        var a = (art[i] || '           ');\n        var r = rows[i] ? '<span class=\"njc-c\">' + rows[i][0] + '<\/span>' + (rows[i][1] ? ' : ' + rows[i][1] : '') : '';\n        line('<span class=\"njc-v\">' + a.replace(\/ \/g, '&nbsp;') + '<\/span>&nbsp;&nbsp;' + r);\n      }\n    },\n    sudo: function () { line('<span class=\"njc-w\">nicolas n\\u2019est pas dans le fichier sudoers. Cet incident sera signal\\u00e9.<\/span> \\uD83D\\uDE0F'); },\n    pwd: function () { line('\/home\/nicolas\/portfolio'); }\n  };\n\n  function run(raw) {\n    var input = raw.trim();\n    if (!input) return;\n    history.unshift(input); hIdx = -1;\n    line('<span class=\"njc-g\">nicolas@portfolio<\/span><span class=\"njc-sep\">:<\/span><span class=\"njc-path\">~<\/span><span class=\"njc-sep\">$<\/span> <span class=\"njc-echo\">' + input.replace(\/<\/g, '&lt;') + '<\/span>');\n\n    var parts = input.split(\/\\s+\/);\n    var cmd = parts[0].toLowerCase(), arg = (parts[1] || '').toLowerCase();\n\n    if (cmd === 'cd' || cmd === 'open') {\n      var slug = ALIAS[arg] || arg;\n      if (!arg) { line('usage : cd &lt;page&gt; \\u2014 tapez <span class=\"njc-g\">ls<\/span> pour la liste'); }\n      else if (PAGES[slug]) { line('Ouverture de <span class=\"njc-c\">' + slug + '<\/span>...'); setTimeout(function () { go(slug); }, 650); }\n      else { line('<span class=\"njc-w\">cd: ' + arg.replace(\/<\/g, '&lt;') + ': dossier introuvable<\/span>'); }\n    } else if (cmd === 'sudo') {\n      if (arg === 'ilovebideur') { unlock(); } else { CMDS.sudo(); }\n    } else if (CMDS[cmd]) {\n      CMDS[cmd]();\n    } else {\n      line('<span class=\"njc-w\">' + cmd.replace(\/<\/g, '&lt;') + ' : commande introuvable<\/span> \\u2014 tapez <span class=\"njc-g\">help<\/span>');\n    }\n    line('&nbsp;');\n  }\n\n  \/\/ ---- Saisie ----\n  inp.addEventListener('keydown', function (e) {\n    if (e.key === 'Enter') { run(inp.value); inp.value = ''; }\n    else if (e.key === 'ArrowUp') { e.preventDefault(); if (hIdx < history.length - 1) inp.value = history[++hIdx]; }\n    else if (e.key === 'ArrowDown') { e.preventDefault(); inp.value = hIdx > 0 ? history[--hIdx] : (hIdx = -1, ''); }\n    else if (e.key === 'Escape') close();\n  });\n\n  document.addEventListener('keydown', function (e) {\n    if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); open(); }\n    else if (e.key === 'Escape') close();\n  });\n  hint.addEventListener('click', function () { open(); });\n  document.getElementById('njc-close').addEventListener('click', close);\n  ov.addEventListener('click', function (e) { if (e.target === ov) close(); });\n\n  \/\/ ---- Boutons et menu : tape la commande puis navigue ----\n  var ROUTES = [\n    { match: \/portfolio\/i, slug: 'portfolio', cmd: '.\/ouvrir_portfolio.sh',\n      out: ['[ok] lecture de ~\/projets\/', '[ok] 1 projet index\\u00e9', '[ok] rendu de la page...'] },\n    { match: \/contact\/i, slug: 'contact', cmd: 'mail -s \"Bonjour Nicolas\"',\n      out: ['[ok] r\\u00e9solution du destinataire', '[ok] ouverture du formulaire...'] },\n    { match: \/propos\/i, slug: 'a-propos', cmd: 'cat ~\/profile\/about.md',\n      out: ['[ok] lecture du profil...'] },\n    { match: \/parcours\/i, slug: 'parcours', cmd: 'git log --oneline --graph parcours',\n      out: ['[ok] 2 entr\\u00e9es de formation', '[ok] 1 exp\\u00e9rience en cours...'] },\n    { match: \/accueil\/i, slug: 'accueil', cmd: 'cd ~\/ && .\/index.sh',\n      out: ['[ok] session ouverte...'] },\n    { match: \/entreprise\/i, slug: 'entreprise', cmd: 'cat \/etc\/entreprise.conf',\n      out: ['[ok] lecture de la fiche entreprise', '[ok] contexte professionnel charg\\u00e9...'] },\n    { match: \/veille\/i, slug: 'veille', cmd: 'tail -f \/var\/log\/veille.log',\n      out: ['[ok] 3 entr\\u00e9es index\\u00e9es', '[ok] sources \\u00e0 jour...'] },\n    { match: \/competences|comp\\u00e9tences\/i, slug: 'competences', cmd: 'grep -r \\\"competence\\\" .\/referentiel\/',\n      out: ['[ok] bloc 1 \\u00b7 bloc 2 \\u00b7 bloc 4', '[ok] tableau de synth\\u00e8se charg\\u00e9...'] },\n    { match: \/mentions|legales|legal\/i, slug: 'legal', cmd: 'cat .\/LEGAL.txt',\n      out: ['[ok] mentions l\\u00e9gales...'] }\n  ];\n\n  async function sequence(route) {\n    if (busy) return;\n    busy = true;\n    out.innerHTML = '';\n    open();\n    line('&nbsp;');\n    var d = line('<span class=\"njc-g\">nicolas@portfolio<\/span><span class=\"njc-sep\">:<\/span><span class=\"njc-path\">~<\/span><span class=\"njc-sep\">$<\/span> <span class=\"njc-echo\" id=\"njc-typing\"><\/span>');\n    var span = d.querySelector('#njc-typing');\n    for (var i = 0; i <= route.cmd.length; i++) {\n      span.textContent = route.cmd.slice(0, i);\n      await wait(26);\n    }\n    await wait(260);\n    for (var j = 0; j < route.out.length; j++) {\n      line('<span class=\"njc-c\">' + route.out[j] + '<\/span>');\n      await wait(230);\n    }\n    await wait(260);\n    line('<span class=\"njc-g\">[done]<\/span> redirection...');\n    await wait(420);\n    go(route.slug);\n  }\n\n  document.addEventListener('click', function (e) {\n    var a = e.target.closest('a');\n    if (!a) return;\n    if (a.closest('#wpadminbar') || a.closest('#njc-overlay')) return;\n    var href = a.getAttribute('href') || '';\n    if (!href || href.charAt(0) === '#' || a.target === '_blank') return;\n    if (a.hostname && a.hostname !== window.location.hostname) return;\n    if (\/wp-admin|wp-login\/.test(href)) return;\n\n    for (var i = 0; i < ROUTES.length; i++) {\n      var r = ROUTES[i];\n      if (r.match.test(href) || r.match.test(a.textContent)) {\n        e.preventDefault();\n        sequence(r);\n        return;\n      }\n    }\n  }, true);\n\n  \/* ================= EASTER EGG : KONAMI ================= *\/\n  var KONAMI = [\"ArrowUp\",\"ArrowUp\",\"ArrowDown\",\"ArrowDown\",\"ArrowLeft\",\"ArrowRight\",\"ArrowLeft\",\"ArrowRight\",\"b\",\"a\"];\n  var kpos = 0, root = false;\n\n  document.addEventListener(\"keydown\", function (e) {\n    var k = e.key;\n    if (typeof k === \"string\") { if (k.length === 1) { k = k.toLowerCase(); } }\n    if (k === KONAMI[kpos]) { kpos++; }\n    else { kpos = (k === KONAMI[0]) ? 1 : 0; }\n    if (kpos === KONAMI.length) { kpos = 0; unlock(); }\n  });\n\n  var HOSTS = [\n    [\"accueil\",     \"10.85.0.1\", \"80\/tcp\",  \"http\", \"Hero v2.0\"],\n    [\"a-propos\",    \"10.85.0.2\", \"80\/tcp\",  \"http\", \"Profil \\u00b7 comp\\u00e9tences\"],\n    [\"parcours\",    \"10.85.0.3\", \"80\/tcp\",  \"http\", \"Formation \\u00b7 alternance\"],\n    [\"entreprise\",  \"10.85.0.4\", \"80\/tcp\",  \"http\", \"LTC Technologies\"],\n    [\"veille\",      \"10.85.0.5\", \"80\/tcp\",  \"rss\",  \"4 entr\\u00e9es index\\u00e9es\"],\n    [\"portfolio\",   \"10.85.0.6\", \"80\/tcp\",  \"http\", \"R\\u00e9alisations\"],\n    [\"competences\", \"10.85.0.7\", \"80\/tcp\",  \"http\", \"R\\u00e9f\\u00e9rentiel BTS SIO\"],\n    [\"contact\",     \"10.85.0.8\", \"22\/tcp\",  \"ssh\",  \"LinkedIn uniquement\"]\n  ];\n\n  \/* une fois le mode superviseur actif, le systeme reste instable :\n     de breves salves de bruit surviennent a intervalles aleatoires *\/\n  \/* ---- Gresillement de statique (synthese Web Audio, aucun fichier) ---- *\/\n  var actx = null, bruitBuf = null, muet = false;\n  function audioInit() {\n    if (actx) { return; }\n    try {\n      var AC = window.AudioContext || window.webkitAudioContext;\n      if (!AC) { return; }\n      actx = new AC();\n      var n = Math.floor(actx.sampleRate * 2);\n      bruitBuf = actx.createBuffer(1, n, actx.sampleRate);\n      var d = bruitBuf.getChannelData(0);\n      var i = 0;\n      for (i = 0; i < n; i++) { d[i] = Math.random() * 2 - 1; }\n    } catch (e) { actx = null; }\n  }\n  function audioReprise() {\n    if (!actx) { return; }\n    if (actx.state === \"suspended\") { actx.resume(); }\n  }\n  \/* le son reste bloque tant que l utilisateur n a pas interagi :\n     on relance le contexte au premier clic ou a la premiere touche *\/\n  document.addEventListener(\"click\", audioReprise, true);\n  document.addEventListener(\"keydown\", audioReprise, true);\n\n  function gresillement(ms, force) {\n    if (muet) { return; }\n    audioInit();\n    if (!actx) { return; }\n    audioReprise();\n    try {\n      var src = actx.createBufferSource();\n      src.buffer = bruitBuf;\n      src.loop = true;\n      var filtre = actx.createBiquadFilter();\n      filtre.type = \"bandpass\";\n      filtre.frequency.value = 800 + Math.random() * 2800;\n      filtre.Q.value = 0.55;\n      var gain = actx.createGain();\n      var vol = 0.075 * force;\n      var t = actx.currentTime;\n      var duree = ms \/ 1000;\n      gain.gain.setValueAtTime(0.0001, t);\n      gain.gain.exponentialRampToValueAtTime(vol, t + 0.01);\n      \/* sautes de niveau : le signal decroche par a-coups *\/\n      var pas = duree \/ 12;\n      var k = 1;\n      for (k = 1; k < 12; k++) {\n        var v = 0.004 + Math.random() * vol;\n        gain.gain.setValueAtTime(v, t + pas * k);\n        filtre.frequency.setValueAtTime(600 + Math.random() * 3200, t + pas * k);\n      }\n      gain.gain.exponentialRampToValueAtTime(0.0001, t + duree);\n      src.connect(filtre);\n      filtre.connect(gain);\n      gain.connect(actx.destination);\n      src.start(t);\n      src.stop(t + duree + 0.05);\n    } catch (e) {}\n  }\n\n  var aleaActif = false;\n  function glitchAleatoire() {\n    if (aleaActif) { return; }\n    aleaActif = true;\n    function suivant() {\n      if (!aleaActif) { return; }\n      var attente = 2200 + Math.random() * 4300;\n      window.__njAleaT = setTimeout(function () {\n        if (!aleaActif) { return; }\n        var tirage = Math.random();\n        var duree = 110 + Math.random() * 260;\n        if (tirage > 0.65) { duree = 420 + Math.random() * 650; }\n        if (tirage > 0.93) { duree = 1100 + Math.random() * 900; }\n        glitch(duree);\n        suivant();\n      }, attente);\n    }\n    suivant();\n  }\n  function stopSuperviseur() {\n    aleaActif = false;\n    root = false;\n    clearTimeout(window.__njAleaT);\n    clearTimeout(window.__njGlitchStop);\n    try { sessionStorage.removeItem(\"njRoot\"); } catch (e) {}\n    document.body.classList.remove(\"nj-root\");\n    document.body.classList.remove(\"nj-glitch\");\n    document.body.classList.remove(\"nj-alert\");\n    var cv = document.getElementById(\"nj-glitch-canvas\");\n    if (cv) { cv.remove(); }\n  }\n\n  function glitch(ms) {\n    var fx = document.getElementById(\"nj-glitch-fx\");\n    if (!fx) {\n      fx = document.createElement(\"div\");\n      fx.id = \"nj-glitch-fx\";\n      document.body.appendChild(fx);\n    }\n    var cv = document.getElementById(\"nj-glitch-canvas\");\n    if (!cv) {\n      cv = document.createElement(\"canvas\");\n      cv.id = \"nj-glitch-canvas\";\n      document.body.appendChild(cv);\n    }\n    \/* la console doit sortir du conteneur transforme, sinon son position:fixed derive *\/\n    if (ov.parentNode !== document.body) { document.body.appendChild(ov); }\n    var hint = document.getElementById(\"njc-hint\");\n    if (hint) { if (hint.parentNode !== document.body) { document.body.appendChild(hint); } }\n\n    document.body.classList.add(\"nj-glitch\");\n    gresillement(ms, Math.min(1, 0.45 + ms \/ 2600));\n\n    var boite = cv.getBoundingClientRect();\n    var w = Math.max(320, Math.floor(boite.width));\n    var h = Math.max(240, Math.floor(boite.height));\n    cv.width = w;\n    cv.height = h;\n    var ctx = cv.getContext(\"2d\");\n    var COUL = [\"#ff2d95\", \"#39ff5b\", \"#00e5ff\", \"#1f6bff\", \"#ffffff\", \"#b6ff00\", \"#ff0033\", \"#7a00ff\", \"#00ffa3\", \"#ffe600\"];\n\n    function bruit() {\n      ctx.globalAlpha = 1;\n      ctx.fillStyle = \"#000000\";\n      ctx.fillRect(0, 0, w, h);\n      \/* couverture integrale, ligne par ligne *\/\n      var y = 0;\n      while (y < h) {\n        var lh = 2 + Math.floor(Math.random() * 5);\n        var x = 0;\n        while (x < w) {\n          var sw = 14 + Math.random() * 190;\n          ctx.globalAlpha = 0.85 + Math.random() * 0.15;\n          ctx.fillStyle = COUL[Math.floor(Math.random() * COUL.length)];\n          ctx.fillRect(x, y, sw, lh);\n          x = x + sw;\n        }\n        y = y + lh + Math.floor(Math.random() * 2);\n      }\n      \/* dechirures noires *\/\n      var d = 0;\n      for (d = 0; d < 16; d++) {\n        ctx.globalAlpha = 1;\n        ctx.fillStyle = \"#000000\";\n        ctx.fillRect(0, Math.random() * h, w, 1 + Math.random() * 8);\n      }\n      \/* bandes vives pleine largeur *\/\n      var v = 0;\n      for (v = 0; v < 5; v++) {\n        ctx.globalAlpha = 1;\n        ctx.fillStyle = COUL[Math.floor(Math.random() * COUL.length)];\n        ctx.fillRect(0, Math.random() * h, w, 2 + Math.random() * 7);\n      }\n      \/* breches laissant entrevoir la page *\/\n      var g = 0;\n      for (g = 0; g < 3; g++) {\n        ctx.clearRect(0, Math.random() * h, w, 6 + Math.random() * 26);\n      }\n      ctx.globalAlpha = 1;\n    }\n\n    bruit();\n    var fin = Date.now() + ms;\n    \/* filet de securite : requestAnimationFrame gele dans un onglet en arriere-plan,\n       ce minuteur garantit que le glitch se termine quoi qu il arrive *\/\n    clearTimeout(window.__njGlitchStop);\n    window.__njGlitchStop = setTimeout(function () {\n      document.body.classList.remove(\"nj-glitch\");\n      ctx.clearRect(0, 0, w, h);\n    }, ms + 80);\n    function frame() {\n      if (Date.now() > fin) {\n        document.body.classList.remove(\"nj-glitch\");\n        ctx.clearRect(0, 0, w, h);\n        return;\n      }\n      bruit();\n      requestAnimationFrame(frame);\n    }\n    requestAnimationFrame(frame);\n  }\n\n  async function unlock() {\n    if (root) { open(); line(\"<span class=\\\"njc-w\\\">mode superviseur d\\u00e9j\\u00e0 actif<\/span>\"); line(\"&nbsp;\"); return; }\n    root = true;\n    try { sessionStorage.setItem(\"njRoot\", \"1\"); } catch (e) {}\n    glitch(2200);\n    document.body.classList.add(\"nj-root\");\n    out.innerHTML = \"\";\n    open();\n    line(\"&nbsp;\");\n\n    var d = line(\"<span class=\\\"njc-g\\\">nicolas@portfolio<\/span><span class=\\\"njc-sep\\\">:<\/span><span class=\\\"njc-path\\\">~<\/span><span class=\\\"njc-sep\\\">$<\/span> <span class=\\\"njc-echo\\\" id=\\\"njc-k1\\\"><\/span>\");\n    var s1 = d.querySelector(\"#njc-k1\");\n    var cmd1 = \"sudo -i\";\n    for (var i = 0; i <= cmd1.length; i++) { s1.textContent = cmd1.slice(0, i); await wait(50); }\n    await wait(300);\n    line(\"[sudo] mot de passe pour nicolas : <span class=\\\"njc-v\\\">\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022<\/span>\");\n    await wait(500);\n    line(\"<span class=\\\"njc-g\\\">[ok]<\/span> \\u00e9l\\u00e9vation de privil\\u00e8ges accord\\u00e9e\");\n    await wait(400);\n    line(\"&nbsp;\");\n\n    var d2 = line(\"<span class=\\\"njc-w\\\">root@portfolio<\/span><span class=\\\"njc-sep\\\">:<\/span><span class=\\\"njc-path\\\">~<\/span><span class=\\\"njc-sep\\\">#<\/span> <span class=\\\"njc-echo\\\" id=\\\"njc-k2\\\"><\/span>\");\n    var s2 = d2.querySelector(\"#njc-k2\");\n    var cmd2 = \"nmap -sV 10.85.0.0\/24\";\n    for (var j = 0; j <= cmd2.length; j++) { s2.textContent = cmd2.slice(0, j); await wait(32); }\n    await wait(350);\n    line(\"Starting Nmap 7.95 \\u00b7 scan du portfolio\");\n    line(\"&nbsp;\");\n    for (var h = 0; h < HOSTS.length; h++) {\n      var x = HOSTS[h];\n      line(\"<span class=\\\"njc-c\\\">\" + x[0].padEnd(13) + \"<\/span>\" + x[1].padEnd(12) +\n           \"<span class=\\\"njc-g\\\">\" + x[2].padEnd(9) + \"open<\/span>  \" + x[3].padEnd(6) + x[4]);\n      await wait(170);\n    }\n    await wait(300);\n    line(\"&nbsp;\");\n    line(\"Nmap done: <span class=\\\"njc-g\\\">8 hosts up<\/span>, scanned in 2.04s\");\n    await wait(400);\n    line(\"&nbsp;\");\n    line(\"<span class=\\\"njc-w\\\">\\u2605 MODE SUPERVISEUR D\\u00c9BLOQU\\u00c9<\/span>\");\n    line(\"Nouvelles commandes : <span class=\\\"njc-g\\\">nmap<\/span> \\u00b7 <span class=\\\"njc-g\\\">incident<\/span> \\u00b7 <span class=\\\"njc-g\\\">uptime<\/span> \\u00b7 <span class=\\\"njc-g\\\">traceroute<\/span> \\u00b7 <span class=\\\"njc-g\\\">reboot<\/span> \\u00b7 <span class=\\\"njc-g\\\">mute<\/span>\");\n    line(\"&nbsp;\");\n    glitchAleatoire();\n  }\n\n  \/* commandes r\\u00e9serv\\u00e9es au mode superviseur *\/\n  CMDS.nmap = function () {\n    if (!root) { line(\"<span class=\\\"njc-w\\\">nmap : permission refus\\u00e9e<\/span> \\u2014 privil\\u00e8ges insuffisants\"); return; }\n    line(\"Starting Nmap 7.95 \\u00b7 scan du portfolio\");\n    HOSTS.forEach(function (x) {\n      line(\"<span class=\\\"njc-c\\\">\" + x[0].padEnd(13) + \"<\/span>\" + x[1].padEnd(12) +\n           \"<span class=\\\"njc-g\\\">\" + x[2].padEnd(9) + \"open<\/span>  \" + x[3].padEnd(6) + x[4]);\n    });\n    line(\"Nmap done: <span class=\\\"njc-g\\\">8 hosts up<\/span>\");\n  };\n\n  CMDS.uptime = function () {\n    var d0 = new Date(2025, 8, 1);\n    var jours = Math.floor((Date.now() - d0.getTime()) \/ 86400000);\n    line(\"up <span class=\\\"njc-c\\\">\" + jours + \" jours<\/span> \\u00b7 alternance active \\u00b7 charge moyenne : 0.42, 0.51, 0.47\");\n  };\n\n  \/* cinq scenarios, tire au sort a chaque appel *\/\n  var TRACES = [\n    { cible: \"cafetiere.loire-telecom.lan\", ip: \"10.85.42.1\",\n      sauts: [\n        [\" 1\", \"poste-nicolas.lan\", \"10.85.0.12\", \"0.412\"],\n        [\" 2\", \"switch-bureau-poe\", \"10.85.0.254\", \"1.203\"],\n        [\" 3\", \"vlan20-services\", \"10.85.20.1\", \"2.874\"],\n        [\" 4\", \"borne-wifi-salle-pause\", \"10.85.20.37\", \"9.551\"],\n        [\" 5\", \"cafetiere.loire-telecom.lan\", \"10.85.42.1\", \"14.002\"]\n      ],\n      fin: [\n        [\"w\", \"[warn] HTTP 418 \\u2014 I am a teapot\"],\n        [\"w\", \"[warn] la cible refuse de preparer du cafe : c est une theiere\"],\n        [\"g\", \"[info] incident remonte aux services generaux\"],\n        [\"v\", \"[info] RFC 2324, section 2.3.2 \\u2014 rien a signaler cote reseau\"]\n      ] },\n\n    { cible: \"imprimante-compta.lan\", ip: \"10.85.30.14\",\n      sauts: [\n        [\" 1\", \"poste-nicolas.lan\", \"10.85.0.12\", \"0.388\"],\n        [\" 2\", \"switch-etage-1\", \"10.85.0.253\", \"1.744\"],\n        [\" 3\", \"vlan30-bureautique\", \"10.85.30.1\", \"3.109\"],\n        [\" 4\", \"imprimante-compta.lan\", \"10.85.30.14\", \"5.021\"]\n      ],\n      fin: [\n        [\"w\", \"[warn] bourrage papier detecte \\u2014 couche 1 (physique)\"],\n        [\"w\", \"[warn] toner signale vide, toner reellement a 60 pour cent\"],\n        [\"g\", \"[info] diagnostic complet : couche 8 (entre la chaise et le clavier)\"],\n        [\"v\", \"[info] aucune action reseau requise\"]\n      ] },\n\n    { cible: \"direction.loire-telecom.lan\", ip: \"10.85.10.1\",\n      sauts: [\n        [\" 1\", \"poste-nicolas.lan\", \"10.85.0.12\", \"0.401\"],\n        [\" 2\", \"switch-bureau-poe\", \"10.85.0.254\", \"1.188\"],\n        [\" 3\", \"vlan10-direction\", \"10.85.10.1\", \"2.402\"],\n        [\" 4\", \"*\", \"*\", \"*\"],\n        [\" 5\", \"*\", \"*\", \"*\"],\n        [\"30\", \"*\", \"*\", \"*\"]\n      ],\n      fin: [\n        [\"w\", \"[warn] 30 sauts atteints, destination injoignable\"],\n        [\"w\", \"[warn] demande de budget perdue en cours de route\"],\n        [\"g\", \"[info] ticket #4471 toujours ouvert depuis 3 semaines\"],\n        [\"v\", \"[info] boucle de routage administrative detectee\"]\n      ] },\n\n    { cible: \"box-client.fibre.lan\", ip: \"192.168.1.1\",\n      sauts: [\n        [\" 1\", \"poste-nicolas.lan\", \"10.85.0.12\", \"0.395\"],\n        [\" 2\", \"routeur-teltonika\", \"10.85.0.1\", \"2.117\"],\n        [\" 3\", \"lien-fibre-client\", \"172.16.4.2\", \"18.740\"],\n        [\" 4\", \"*\", \"*\", \"*\"]\n      ],\n      fin: [\n        [\"w\", \"[warn] destination injoignable depuis 14h02\"],\n        [\"w\", \"[warn] la box a ete debranchee pour brancher un aspirateur\"],\n        [\"g\", \"[info] intervention sur site programmee\"],\n        [\"v\", \"[info] cause racine : prise electrique unique\"]\n      ] },\n\n    { cible: \"wifi-invite.loire-telecom.lan\", ip: \"10.85.50.1\",\n      sauts: [\n        [\" 1\", \"poste-nicolas.lan\", \"10.85.0.12\", \"0.377\"],\n        [\" 2\", \"switch-bureau-poe\", \"10.85.0.254\", \"1.260\"],\n        [\" 3\", \"vlan50-invites\", \"10.85.50.1\", \"3.318\"],\n        [\" 4\", \"borne-wifi-accueil\", \"10.85.50.22\", \"11.804\"]\n      ],\n      fin: [\n        [\"w\", \"[warn] authentification refusee : mot de passe expire\"],\n        [\"w\", \"[warn] le client affirme que ca marchait hier\"],\n        [\"g\", \"[info] aucune modification effectuee, selon le client\"],\n        [\"v\", \"[info] journaux consultes : 47 modifications en 24h\"]\n      ] }\n  ];\n\n  CMDS.traceroute = async function () {\n    if (!root) { line('<span class=\"njc-w\">traceroute : permission refusee<\/span>'); return; }\n    var t = TRACES[Math.floor(Math.random() * TRACES.length)];\n    line('traceroute vers <span class=\"njc-c\">' + t.cible + '<\/span> (' + t.ip + '), 30 sauts max');\n    line('&nbsp;');\n    var i = 0;\n    for (i = 0; i < t.sauts.length; i++) {\n      var s = t.sauts[i];\n      if (s[1] === \"*\") {\n        line('<span class=\"njc-c\">' + s[0] + '<\/span>  * * *' + \"\".padEnd(24) + '<span class=\"njc-w\">demande de timeout<\/span>');\n      } else {\n        line('<span class=\"njc-c\">' + s[0] + '<\/span>  ' + s[1].padEnd(30) + s[2].padEnd(15) +\n             '<span class=\"njc-g\">' + s[3] + ' ms<\/span>');\n      }\n      await wait(420);\n    }\n    await wait(600);\n    line('&nbsp;');\n    var k = 0;\n    for (k = 0; k < t.fin.length; k++) {\n      line('<span class=\"njc-' + t.fin[k][0] + '\">' + t.fin[k][1] + '<\/span>');\n      await wait(720);\n    }\n    line('&nbsp;');\n  };\n\n  CMDS.history = function () {\n    var h = [\n      [\"  1\", \"whoami\", \"\"],\n      [\"  2\", \"cat profile.txt\", \"\"],\n      [\"  3\", \"ip -br a\", \"\"],\n      [\"  4\", \"ping -c4 10.85.0.254\", \"\"],\n      [\"  5\", \"ssh nicolas@switch-bureau-poe\", \"\"],\n      [\"  6\", \"show vlan brief\", \"\"],\n      [\"  7\", \"exit\", \"\"],\n      [\"  8\", \"sudo ilovebideur\", \"secret\"],\n      [\"  9\", \"traceroute cafetiere.loire-telecom.lan\", \"\"],\n      [\" 10\", \"nmap -sV 10.85.0.0\/24\", \"\"],\n      [\" 11\", \"clear\", \"\"],\n      [\" 12\", \"history\", \"\"]\n    ];\n    var i = 0;\n    for (i = 0; i < h.length; i++) {\n      if (h[i][2] === \"secret\") {\n        line('<span class=\"njc-sep\">' + h[i][0] + '<\/span>  <span class=\"njc-echo\">' + h[i][1] + '<\/span>');\n      } else {\n        line('<span class=\"njc-sep\">' + h[i][0] + '<\/span>  ' + h[i][1]);\n      }\n    }\n  };\n\n  CMDS.mute = function () {\n    muet = !muet;\n    if (muet) { line('<span class=\"njc-w\">[ok] son coupe<\/span>'); }\n    else { line('<span class=\"njc-g\">[ok] son retabli<\/span>'); }\n  };\n\n  CMDS.reboot = function () {\n    if (!root) { line('<span class=\"njc-w\">reboot : permission refusee<\/span>'); return; }\n    stopSuperviseur();\n    line('<span class=\"njc-g\">[ok]<\/span> session superviseur fermee');\n    line('<span class=\"njc-g\">[ok]<\/span> systeme restaure');\n  };\n\n  CMDS.incident = async function () {\n    if (!root) { line(\"<span class=\\\"njc-w\\\">incident : permission refus\\u00e9e<\/span>\"); return; }\n    glitch(1000);\n    document.body.classList.add(\"nj-alert\");\n    line(\"<span class=\\\"njc-w\\\">\\u26a0 INCIDENT SIMUL\\u00c9<\/span> \\u2014 perte de lien sur le VLAN 20\");\n    await wait(700);\n    line(\"[..] bascule sur le lien de secours 4G\");\n    await wait(900);\n    line(\"[..] v\\u00e9rification des bornes Wi-Fi\");\n    await wait(900);\n    line(\"<span class=\\\"njc-g\\\">[ok]<\/span> service r\\u00e9tabli \\u00b7 interruption 2 min 38 s\");\n    document.body.classList.remove(\"nj-alert\");\n    line(\"&nbsp;\");\n  };\n  \/* le mode superviseur survit a la navigation, le temps de l onglet *\/\n  try {\n    if (sessionStorage.getItem(\"njRoot\") === \"1\") {\n      root = true;\n      document.body.classList.add(\"nj-root\");\n      glitchAleatoire();\n    }\n  } catch (e) {}\n})();\n<\/script>\n\n\n\n\n<script>\n(function(){\n  if (window.__njFx) return; window.__njFx = true;\n  var reduce = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\n  \/\/ 1. barre de progression de lecture\n  var bar = document.createElement(\"div\");\n  bar.id = \"nj-progress\";\n  document.body.appendChild(bar);\n  function prog(){\n    var h = document.documentElement.scrollHeight - window.innerHeight;\n    bar.style.width = (h > 0 ? (window.scrollY \/ h) * 100 : 0) + \"%\";\n  }\n  window.addEventListener(\"scroll\", prog, {passive:true});\n  window.addEventListener(\"resize\", prog);\n  prog();\n\n  \/\/ 2. apparition des cartes au defilement\n  var cards = document.querySelectorAll(\".entry-content > .wp-block-group:not(.hero-navy), .stats-row\");\n  if (!reduce && \"IntersectionObserver\" in window) {\n    cards.forEach(function(c){ c.classList.add(\"nj-reveal\"); });\n    var io = new IntersectionObserver(function(entries){\n      entries.forEach(function(e){\n        if (e.isIntersecting) { e.target.classList.add(\"nj-seen\"); io.unobserve(e.target); }\n      });\n    }, {threshold: 0.12, rootMargin: \"0px 0px -40px 0px\"});\n    cards.forEach(function(c){ io.observe(c); });\n  }\n\n  \/\/ 3. compteurs animes sur la ligne de statistiques\n  var nums = document.querySelectorAll(\".stats-row .stat-num\");\n  if (!reduce && nums.length && \"IntersectionObserver\" in window) {\n    var io2 = new IntersectionObserver(function(entries){\n      entries.forEach(function(e){\n        if (!e.isIntersecting) return;\n        io2.unobserve(e.target);\n        var el = e.target, final = el.textContent.trim();\n        if (!\/^[0-9]+$\/.test(final)) return;\n        var target = parseInt(final, 10), cur = 0;\n        var step = Math.max(1, Math.round(target \/ 18));\n        el.textContent = \"0\";\n        var t = setInterval(function(){\n          cur += step;\n          if (cur >= target) { cur = target; clearInterval(t); }\n          el.textContent = String(cur);\n        }, 45);\n      });\n    }, {threshold: 0.5});\n    nums.forEach(function(n){ io2.observe(n); });\n  }\n})();\n<\/script>\n\n\n\n<script>\n(function(){\n  if (window.__njLegal) return; window.__njLegal = true;\n  function add(){\n    var f = document.querySelector(\".site-footer\") || document.querySelector(\"footer\");\n    if (!f || document.querySelector(\".footer-legal-link\")) return;\n    var d = document.createElement(\"div\");\n    d.className = \"footer-legal-link\";\n    var a = document.createElement(\"a\");\n    a.href = \"\/index.php\/mentions-legales\/\";\n    a.textContent = \"Mentions legales\";\n    d.appendChild(a);\n    f.appendChild(d);\n  }\n  if (document.readyState === \"loading\") document.addEventListener(\"DOMContentLoaded\", add);\n  else add();\n})();\n<\/script>\n\n\n\n<script>\n(function () {\n  if (window.__njFab) { return; }\n  window.__njFab = true;\n  var b = document.createElement(\"button\");\n  b.id = \"nj-fab\";\n  b.type = \"button\";\n  b.setAttribute(\"aria-label\", \"Ouvrir la console\");\n  b.textContent = \">_\";\n  b.addEventListener(\"click\", function () {\n    document.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"k\", ctrlKey: true, bubbles: true }));\n    setTimeout(function () {\n      var i = document.getElementById(\"njc-input\");\n      if (i) { i.focus(); }\n    }, 160);\n  });\n  document.body.appendChild(b);\n  var o = document.getElementById(\"njc-overlay\");\n  if (o) {\n    var maj = function () {\n      b.style.visibility = (getComputedStyle(o).display !== \"none\") ? \"hidden\" : \"\";\n    };\n    var mo = new MutationObserver(maj);\n    mo.observe(o, { attributes: true, attributeFilter: [\"class\", \"style\"] });\n    mo.observe(document.body, { attributes: true, attributeFilter: [\"class\"] });\n    maj();\n  }\n})();\n<\/script>\n\n\n\n<script>\n(function () {\n  if (window.__njTurbo) { return; }\n  window.__njTurbo = true;\n\n  var CLE = \"nj-nav-directe\";\n  function actif() { try { return localStorage.getItem(CLE) === \"1\"; } catch (e) { return false; } }\n  function definir(v) { try { localStorage.setItem(CLE, v ? \"1\" : \"0\"); } catch (e) {} }\n\n  \/* 1. court-circuite l animation de chargement sur les liens internes *\/\n  \/* sur window : la phase de capture y passe avant tout ecouteur pose sur document *\/\n  window.addEventListener(\"click\", function (e) {\n    if (!actif()) { return; }\n    if (e.button !== 0) { return; }\n    if (e.metaKey) { return; }\n    if (e.ctrlKey) { return; }\n    if (e.shiftKey) { return; }\n    if (e.altKey) { return; }\n    var a = (e.target ? (e.target.closest ? e.target.closest(\"a[href]\") : null) : null);\n    if (!a) { return; }\n    if (a.target === \"_blank\") { return; }\n    if (a.hasAttribute(\"download\")) { return; }\n    if (a.hostname !== location.hostname) { return; }\n    var h = a.getAttribute(\"href\") || \"\";\n    if (h.charAt(0) === \"#\") { return; }\n    if (h.indexOf(\"mailto:\") === 0) { return; }\n    e.preventDefault();\n    e.stopImmediatePropagation();\n    location.href = a.href;\n  }, true);\n\n  \/* 2. la mention, presente sur chaque page *\/\n  var astuce = document.createElement(\"div\");\n  astuce.id = \"nj-astuce\";\n  function txt(s) { return document.createTextNode(s); }\n  function bal(nom, s) { var e = document.createElement(nom); e.textContent = s; return e; }\n  function majAstuce() {\n    while (astuce.firstChild) { astuce.removeChild(astuce.firstChild); }\n    if (actif()) {\n      astuce.className = \"nj-astuce-on\";\n      astuce.appendChild(txt(\"\/\/ navigation directe active \\u2014 les animations de chargement sont d\\u00e9sactiv\\u00e9es. Tapez \"));\n      astuce.appendChild(bal(\"b\", \"turbo\"));\n      astuce.appendChild(txt(\" dans la console pour les remettre.\"));\n    } else {\n      astuce.className = \"\";\n      astuce.appendChild(txt(\"\/\/ les chargements vous agacent ? Ouvrez la console avec \"));\n      astuce.appendChild(bal(\"kbd\", \"Ctrl\"));\n      astuce.appendChild(txt(\" + \"));\n      astuce.appendChild(bal(\"kbd\", \"K\"));\n      astuce.appendChild(txt(\" (ou le bouton >_ sur mobile) et tapez \"));\n      astuce.appendChild(bal(\"b\", \"turbo\"));\n      astuce.appendChild(txt(\" : la navigation devient instantan\\u00e9e.\"));\n    }\n  }\n  function placer() {\n    var pied = document.querySelector(\"footer.site-footer\");\n    if (pied) { pied.parentNode.insertBefore(astuce, pied); } else { document.body.appendChild(astuce); }\n    majAstuce();\n  }\n  if (document.readyState === \"loading\") { document.addEventListener(\"DOMContentLoaded\", placer); } else { placer(); }\n\n  \/* 3. commande turbo + completion au Tab *\/\n  function ecrire(s) {\n    var out = document.getElementById(\"njc-out\");\n    if (!out) { return; }\n    var d = document.createElement(\"div\");\n    d.textContent = s;\n    out.appendChild(d);\n    out.scrollTop = out.scrollHeight;\n  }\n  var CMD = [\"help\", \"ls\", \"cd\", \"whoami\", \"skills\", \"neofetch\", \"contact\", \"date\", \"history\", \"clear\", \"exit\", \"pwd\", \"turbo\", \"tortue\"];\n  var ROOT = [\"nmap\", \"incident\", \"uptime\", \"traceroute\", \"reboot\", \"mute\"];\n  var PG = [\"accueil\", \"a-propos\", \"parcours\", \"portfolio\", \"contact\", \"entreprise\", \"veille\", \"competences\", \"legal\"];\n\n  function surTouche(e) {\n    var el = e.target;\n    if (e.key === \"Tab\") {\n      e.preventDefault();\n      e.stopImmediatePropagation();\n      var v = el.value;\n      var m = v.match(\/^(\\s*(?:cd|open)\\s+)(\\S*)$\/i);\n      var liste, debut, prefixe;\n      if (m) { debut = m[1]; prefixe = m[2].toLowerCase(); liste = PG; }\n      else {\n        debut = \"\";\n        prefixe = v.trim().toLowerCase();\n        liste = CMD.slice();\n        if (document.body.classList.contains(\"nj-root\")) { liste = liste.concat(ROOT); }\n      }\n      if (!prefixe) { return; }\n      var c = liste.filter(function (x) { return x.indexOf(prefixe) === 0; });\n      if (c.length === 1) { el.value = debut + c[0] + (c[0] === \"cd\" ? \" \" : \"\"); }\n      else if (c.length > 1) {\n        var commun = c[0];\n        c.forEach(function (x) { while (x.indexOf(commun) !== 0) { commun = commun.slice(0, -1); } });\n        el.value = debut + commun;\n        ecrire(c.join(\"   \"));\n      }\n      return;\n    }\n    if (e.key !== \"Enter\") { return; }\n    var cmd = (el.value || \"\").trim().toLowerCase();\n    if (cmd === \"help\") {\n      setTimeout(function () {\n        ecrire(\"  turbo         navigation instantan\\u00e9e, sans animation\");\n        ecrire(\"  tortue        navigation \\u00e0 0,3 Ko\/s, pour les nostalgiques\");\n      }, 80);\n      return;\n    }\n    if (cmd !== \"turbo\") { return; }\n    e.preventDefault();\n    e.stopImmediatePropagation();\n    ecrire(\"nicolas@portfolio:~$ turbo\");\n    var on = !actif();\n    definir(on);\n    if (on) {\n      ecrire(\"[ok] navigation directe activ\\u00e9e\");\n      ecrire(\"les animations de chargement sont coup\\u00e9es sur tout le site.\");\n      ecrire(\"tapez turbo \\u00e0 nouveau pour les remettre.\");\n    } else {\n      ecrire(\"[ok] navigation directe d\\u00e9sactiv\\u00e9e\");\n      ecrire(\"les animations de chargement sont de retour.\");\n    }\n    majAstuce();\n    el.value = \"\";\n  }\n  \/* sur window egalement : la console capte Entree sur document avant nous *\/\n  window.addEventListener(\"keydown\", function (e) {\n    if (!e.target) { return; }\n    if (e.target.id !== \"njc-input\") { return; }\n    surTouche(e);\n  }, true);\n})();\n<\/script>\n\n\n\n<script>\n(function () {\n  if (window.__njReboot) { return; }\n  window.__njReboot = true;\n\n  \/* references d origine, capturees avant toute modification *\/\n  var T = window.setTimeout.bind(window);\n\n  \/* --- traque des minuteurs lances pendant le mode superviseur --- *\/\n  var traces = [], enregistre = false;\n  var vraiT = window.setTimeout, vraiI = window.setInterval;\n  window.setTimeout = function () {\n    var id = vraiT.apply(window, arguments);\n    if (enregistre) { traces.push(id); }\n    return id;\n  };\n  window.setInterval = function () {\n    var id = vraiI.apply(window, arguments);\n    if (enregistre) { traces.push(id); }\n    return id;\n  };\n  function purger() {\n    traces.forEach(function (id) { clearTimeout(id); clearInterval(id); });\n    traces = [];\n  }\n  new MutationObserver(function () {\n    if (document.body.classList.contains(\"nj-root\")) { enregistre = true; }\n  }).observe(document.body, { attributes: true, attributeFilter: [\"class\"] });\n\n  var enCours = false, relanceFaite = false;\n  var ecran = null, flux = null, minuteurs = [];\n  function plus(fn, ms) { minuteurs.push(T(fn, ms)); }\n\n  function ecrire(s) {\n    var out = document.getElementById(\"njc-out\");\n    if (!out) { return; }\n    var d = document.createElement(\"div\");\n    d.textContent = s;\n    out.appendChild(d);\n    out.scrollTop = out.scrollHeight;\n  }\n\n  function ligne(s) {\n    if (!flux) { return document.createElement(\"div\"); }\n    var d = document.createElement(\"div\");\n    if (s.indexOf(\"[  OK  ]\") === 0) {\n      var sp = document.createElement(\"span\");\n      sp.className = \"nj-ok\";\n      sp.textContent = \"[  OK  ]\";\n      d.appendChild(sp);\n      d.appendChild(document.createTextNode(s.slice(8)));\n    } else {\n      d.textContent = s;\n    }\n    flux.appendChild(d);\n    return d;\n  }\n\n  var ARRET = [\n    \"[  OK  ] Arr\\u00eat des sondes r\\u00e9seau\",\n    \"[  OK  ] Sortie du mode superviseur\",\n    \"[  OK  ] D\\u00e9montage de \/dev\/glitch\",\n    \"[  OK  ] Synchronisation du cache\"\n  ];\n  var NOYAU = [\n    \"[    0.000000] Linux version 6.8.0-nj (nicolas@portfolio)\",\n    \"[    0.412771] D\\u00e9tection du mat\\u00e9riel\",\n    \"[    1.034502] eth0: lien \\u00e9tabli \\u2014 1000 Mbit\/s full-duplex\",\n    \"[    1.288140] Montage du syst\\u00e8me de fichiers racine\",\n    \"[    2.004913] D\\u00e9marrage de portfolio.service\"\n  ];\n\n  function barre(p) {\n    var n = 22, pleins = Math.round(n * p), s = \"[\";\n    for (var i = 0; i < n; i++) { s += (i < pleins ? \"\\u2588\" : \"\\u2591\"); }\n    return s + \"] \" + Math.round(p * 100) + \"%\";\n  }\n  function anime(duree, rendu) {\n    var debut = Date.now();\n    var pas = function () {\n      var p = Math.min(1, (Date.now() - debut) \/ duree);\n      rendu(p);\n      if (p < 1) { plus(pas, 45); }\n    };\n    plus(pas, 45);\n  }\n\n  function surEchap(e) { if (e.key === \"Escape\") { nettoyer(); } }\n\n  function relancerConsole() {\n    var i = document.getElementById(\"njc-input\");\n    if (!i) { return; }\n    relanceFaite = true;\n    i.value = \"reboot\";\n    i.dispatchEvent(new KeyboardEvent(\"keydown\", { key: \"Enter\", bubbles: true, cancelable: true }));\n    i.value = \"\";\n  }\n\n  function nettoyer() {\n    minuteurs.forEach(clearTimeout);\n    minuteurs = [];\n    if (ecran) { ecran.remove(); }\n    ecran = null; flux = null;\n    document.removeEventListener(\"keydown\", surEchap, true);\n    if (!relanceFaite) { relancerConsole(); }\n    var out = document.getElementById(\"njc-out\");\n    if (out) {\n      while (out.firstChild) { out.removeChild(out.firstChild); }\n      ecrire(\"Console portfolio \\u2014 Nicolas Jedrosik \\u00b7 BTS SIO SISR\");\n      ecrire(\"Tapez help pour la liste des commandes.\");\n    }\n    relanceFaite = false;\n    enCours = false;\n    \/* des minuteurs du mode superviseur peuvent se relancer apres coup *\/\n    purger();\n    T(purger, 1200); T(purger, 3500); T(purger, 7000); T(purger, 11000);\n    T(function () { enregistre = false; traces = []; }, 13000);\n  }\n  window.__njPurge = purger;\n\n  function rallumer() {\n    ecran.className = \"nj-rallume\";\n    var t = 160;\n    plus(function () {\n      ligne(\"portfolio BIOS v2.6 \\u2014 power-on self test\");\n      var m = ligne(\"\");\n      anime(760, function (p) {\n        var v = Math.round(16384 * p);\n        m.textContent = \"m\\u00e9moire .................... \" + v + \" Mo\" + (p >= 1 ? \"     OK\" : \"\");\n      });\n    }, t);\n    t += 900;\n    plus(function () { ligne(\"contr\\u00f4leur r\\u00e9seau ........... eth0      OK\"); }, t); t += 180;\n    plus(function () { ligne(\"stockage ................... nvme0n1   OK\"); }, t); t += 280;\n    plus(function () { ligne(\"\"); }, t);\n    NOYAU.forEach(function (l) { t += 185; plus(function () { ligne(l); }, t); });\n    t += 280;\n    plus(function () {\n      ligne(\"\");\n      var b = ligne(barre(0));\n      anime(950, function (p) { b.textContent = barre(p); });\n    }, t);\n    t += 1150;\n    plus(function () {\n      ligne(\"\");\n      var f = ligne(\"[  OK  ] Interface pr\\u00eate.\");\n      var c = document.createElement(\"span\");\n      c.id = \"nj-curseur\";\n      f.appendChild(c);\n    }, t);\n    t += 820;\n    plus(function () { if (ecran) { ecran.className = \"nj-flash\"; } }, t);\n    plus(nettoyer, t + 280);\n  }\n\n  function eteindre() {\n    ecran.className = \"nj-eteint\";\n    plus(function () {\n      if (!flux) { return; }\n      while (flux.firstChild) { flux.removeChild(flux.firstChild); }\n      ecran.className = \"nj-noir\";\n      plus(relancerConsole, 140);\n      plus(rallumer, 640);\n    }, 470);\n  }\n\n  function ouvrir() {\n    ecran = document.createElement(\"div\");\n    ecran.id = \"nj-reboot\";\n    flux = document.createElement(\"div\");\n    flux.id = \"nj-reboot-flux\";\n    ecran.appendChild(flux);\n    var aide = document.createElement(\"div\");\n    aide.id = \"nj-reboot-aide\";\n    aide.textContent = \"\\u00c9chap pour passer\";\n    ecran.appendChild(aide);\n    document.body.appendChild(ecran);\n    document.addEventListener(\"keydown\", surEchap, true);\n    ecran.addEventListener(\"click\", function () { nettoyer(); });\n    ligne(\"Broadcast message from root@portfolio (pts\/0)\");\n    ligne(\"\");\n    ligne(\"The system is going down for reboot NOW!\");\n    plus(eteindre, 1150);\n  }\n\n  window.addEventListener(\"keydown\", function (e) {\n    if (e.key !== \"Enter\") { return; }\n    if (!e.target) { return; }\n    if (e.target.id !== \"njc-input\") { return; }\n    if (enCours) { return; }\n    if ((e.target.value || \"\").trim().toLowerCase() !== \"reboot\") { return; }\n    if (!document.body.classList.contains(\"nj-root\")) { return; }\n    e.preventDefault();\n    e.stopImmediatePropagation();\n    enCours = true;\n    e.target.value = \"\";\n    ecrire(\"nicolas@portfolio:~$ reboot\");\n    var t = 0;\n    ARRET.forEach(function (l) { t += 210; plus(function () { ecrire(l); }, t); });\n    plus(ouvrir, t + 380);\n  }, true);\n})();\n<\/script>\n\n\n\n<script>\n(function () {\n  if (window.__njTortue) { return; }\n  window.__njTortue = true;\n\n  var T = window.setTimeout.bind(window);\n  var CLE = \"nj-tortue\", TURBO = \"nj-nav-directe\";\n  function actif() { try { return localStorage.getItem(CLE) === \"1\"; } catch (e) { return false; } }\n  function definir(v) { try { localStorage.setItem(CLE, v ? \"1\" : \"0\"); } catch (e) {} }\n\n  function ecrire(s) {\n    var out = document.getElementById(\"njc-out\");\n    if (!out) { return; }\n    var d = document.createElement(\"div\");\n    d.textContent = s;\n    out.appendChild(d);\n    out.scrollTop = out.scrollHeight;\n  }\n\n  var ETAPES = [\n    { p: 0.04, ms: 1800, m: \"r\\u00e9solution du nom de domaine\" },\n    { p: 0.09, ms: 2200, m: \"ouverture de la connexion\" },\n    { p: 0.11, ms: 3400, m: \"n\\u00e9gociation du protocole\" },\n    { p: 0.23, ms: 2600, m: \"r\\u00e9ception des en-t\\u00eates\" },\n    { p: 0.27, ms: 4200, m: \"un paquet a pris la mauvaise sortie\" },\n    { p: 0.31, ms: 3100, m: \"reroutage par Clermont-Ferrand\" },\n    { p: 0.38, ms: 2800, m: \"le hamster reprend son souffle\" },\n    { p: 0.44, ms: 3600, m: \"n\\u00e9gociation du d\\u00e9bit avec le serveur\" },\n    { p: 0.47, ms: 5200, m: \"attente du jeton sur l\\u2019anneau\" },\n    { p: 0.55, ms: 2900, m: \"d\\u00e9compression (une archive dans une archive)\" },\n    { p: 0.61, ms: 3300, m: \"recomptage des octets\" },\n    { p: 0.68, ms: 2700, m: \"un octet s\\u2019est perdu, on le cherche\" },\n    { p: 0.74, ms: 3800, m: \"octet retrouv\\u00e9, remerciements d\\u2019usage\" },\n    { p: 0.82, ms: 3200, m: \"assemblage de la page\" },\n    { p: 0.88, ms: 2600, m: \"v\\u00e9rification de l\\u2019int\\u00e9grit\\u00e9\" },\n    { p: 0.94, ms: 4400, m: \"finalisation\" },\n    { p: 0.97, ms: 5400, m: \"finalisation (vraiment, cette fois)\" },\n    { p: 0.99, ms: 2600, m: \"dernier octet en approche\" },\n    { p: 1.00, ms: 800,  m: \"termin\\u00e9\" }\n  ];\n  var RESTES = [\"8 s\", \"15 s\", \"4 s\", \"1 min 30 s\", \"42 s\", \"3 min\", \"19 s\", \"2 min 10 s\", \"6 min 40 s\", \"55 s\", \"12 s\", \"4 min\", \"1 min 05 s\", \"27 s\", \"9 s\", \"2 s\", \"1 s\", \"1 s\", \"\\u2014\"];\n\n  var ecran = null, barreEl = null, etatEl = null, resteEl = null;\n  var minuteurs = [], destination = null;\n  function plus(fn, ms) { minuteurs.push(T(fn, ms)); }\n\n  function dessiner(p) {\n    var n = 20, pleins = Math.round(n * p), s = \"[\";\n    for (var i = 0; i < n; i++) { s += (i < pleins ? \"\\u2588\" : \"\\u2591\"); }\n    return s + \"] \" + Math.round(p * 100) + \"%\";\n  }\n\n\n  function fermer(aller) {\n    minuteurs.forEach(clearTimeout);\n    minuteurs = [];\n    if (ecran) { ecran.remove(); }\n    ecran = null; barreEl = null; etatEl = null; resteEl = null;\n    var u = destination;\n    destination = null;\n    if (aller) { if (u) { location.href = u; } }\n  }\n\n  function bloc(parent, id, texte) {\n    var d = document.createElement(\"div\");\n    if (id) { d.id = id; }\n    d.textContent = texte;\n    parent.appendChild(d);\n    return d;\n  }\n\n  function ouvrir(lien) {\n    ecran = document.createElement(\"div\");\n    ecran.id = \"nj-tortue\";\n    var boite = document.createElement(\"div\");\n    boite.id = \"nj-tortue-boite\";\n    ecran.appendChild(boite);\n    bloc(boite, \"nj-tortue-titre\", \"[ mode tortue ] \\ud83d\\udc22   liaison 0,3 Ko\/s\");\n    bloc(boite, \"nj-tortue-cafe\", \"\\u2615  Allez vous chercher un caf\\u00e9, vous avez le temps.\");\n    var chemin = (lien.pathname || \"\/\").replace(\/\\\/index\\.php\/, \"\").replace(\/\\\/$\/, \"\") || \"\/accueil\";\n    bloc(boite, \"nj-tortue-cmd\", \"$ wget --limit-rate=0.3k .\" + chemin);\n    barreEl = bloc(boite, \"nj-tortue-barre\", dessiner(0));\n    etatEl = bloc(boite, \"nj-tortue-etat\", \"initialisation\");\n    resteEl = bloc(boite, \"nj-tortue-reste\", \"temps restant estim\\u00e9 : 4 s\");\n    bloc(boite, \"nj-tortue-aide\", \"inutile de cliquer : ce chargement ne peut pas \\u00eatre interrompu. tapez tortue dans la console, une fois arriv\\u00e9, pour d\\u00e9sactiver le mode.\");\n    document.body.appendChild(ecran);\n    \/* aucune sortie anticipee : le chargement va a son terme *\/\n\n    var t = 0, pPrec = 0;\n    ETAPES.forEach(function (e, i) {\n      var depart = pPrec;\n      var debut = t;\n      pPrec = e.p;\n      t += e.ms;\n      plus(function () {\n        if (etatEl) { etatEl.textContent = e.m; }\n        if (resteEl) { resteEl.textContent = \"temps restant estim\\u00e9 : \" + RESTES[i]; }\n        var d0 = Date.now();\n        var pas = function () {\n          if (!barreEl) { return; }\n          var q = Math.min(1, (Date.now() - d0) \/ e.ms);\n          barreEl.textContent = dessiner(depart + (e.p - depart) * q);\n          if (q < 1) { plus(pas, 70); }\n        };\n        pas();\n      }, debut);\n    });\n    plus(function () { fermer(true); }, t + 450);\n  }\n\n  window.addEventListener(\"click\", function (e) {\n    if (!actif()) { return; }\n    if (e.button !== 0) { return; }\n    if (e.metaKey) { return; }\n    if (e.ctrlKey) { return; }\n    if (e.shiftKey) { return; }\n    if (e.altKey) { return; }\n    var a = (e.target ? (e.target.closest ? e.target.closest(\"a[href]\") : null) : null);\n    if (!a) { return; }\n    if (a.target === \"_blank\") { return; }\n    if (a.hasAttribute(\"download\")) { return; }\n    if (a.hostname !== location.hostname) { return; }\n    var h = a.getAttribute(\"href\") || \"\";\n    if (h.charAt(0) === \"#\") { return; }\n    if (h.indexOf(\"mailto:\") === 0) { return; }\n    if (ecran) { return; }\n    e.preventDefault();\n    e.stopImmediatePropagation();\n    destination = a.href;\n    ouvrir(a);\n  }, true);\n\n  window.addEventListener(\"keydown\", function (e) {\n    if (e.key !== \"Enter\") { return; }\n    if (!e.target) { return; }\n    if (e.target.id !== \"njc-input\") { return; }\n    \/* si turbo vient d etre active, tortue n a plus lieu d etre *\/\n    try { if (localStorage.getItem(TURBO) === \"1\") { definir(false); } } catch (err) {}\n    if ((e.target.value || \"\").trim().toLowerCase() !== \"tortue\") { return; }\n    e.preventDefault();\n    e.stopImmediatePropagation();\n    e.target.value = \"\";\n    ecrire(\"nicolas@portfolio:~$ tortue\");\n    var on = !actif();\n    definir(on);\n    if (on) {\n      try { localStorage.setItem(TURBO, \"0\"); } catch (err) {}\n      ecrire(\"[ok] mode tortue activ\\u00e9\");\n      ecrire(\"d\\u00e9bit ramen\\u00e9 \\u00e0 0,3 Ko\/s. prenez un caf\\u00e9.\");\n      ecrire(\"tapez tortue \\u00e0 nouveau, ou turbo, pour revenir \\u00e0 la normale.\");\n    } else {\n      ecrire(\"[ok] mode tortue d\\u00e9sactiv\\u00e9\");\n      ecrire(\"vitesse de navigation normale r\\u00e9tablie.\");\n    }\n  }, true);\n})();\n<\/script>\n\n","protected":false},"excerpt":{"rendered":"<p>Veille technologique Suivi de l&rsquo;actualit\u00e9 des syst\u00e8mes, des r\u00e9seaux et de la cybers\u00e9curit\u00e9. M\u00e9thode Th\u00e8me principalR\u00e9seau et Wi-Fi d&rsquo;entreprise Th\u00e8mes [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-141","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/wp-json\/wp\/v2\/pages\/141","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/wp-json\/wp\/v2\/comments?post=141"}],"version-history":[{"count":32,"href":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/wp-json\/wp\/v2\/pages\/141\/revisions"}],"predecessor-version":[{"id":462,"href":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/wp-json\/wp\/v2\/pages\/141\/revisions\/462"}],"wp:attachment":[{"href":"https:\/\/nicolas.jedrosik.monbtssio.fr\/index.php\/wp-json\/wp\/v2\/media?parent=141"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}