{"id":27509,"date":"2026-05-21T14:53:14","date_gmt":"2026-05-21T18:53:14","guid":{"rendered":"https:\/\/www.itcloud.ca\/?page_id=27509"},"modified":"2026-05-21T15:42:07","modified_gmt":"2026-05-21T19:42:07","slug":"altitude-gallery","status":"publish","type":"page","link":"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/","title":{"rendered":"Altitude Gallery"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"27509\" class=\"elementor elementor-27509\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-40f2158 e-flex e-con-boxed e-con e-parent\" data-id=\"40f2158\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-e0cf3c2 e-con-full e-flex e-con e-child\" data-id=\"e0cf3c2\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t<a class=\"elementor-element elementor-element-abf375c e-con-full e-flex e-con e-child\" data-id=\"abf375c\" data-element_type=\"container\" data-e-type=\"container\" href=\"https:\/\/www.itcloud.ca\/en\/altitude-roadshow-2026\/\">\n\t\t\t\t<div class=\"elementor-element elementor-element-79a2313 elementor-widget__width-initial elementor-widget elementor-widget-image\" data-id=\"79a2313\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1\" height=\"1\" src=\"https:\/\/www.itcloud.ca\/wp-content\/uploads\/LogoAltitude-Reverse-1.svg\" class=\"attachment-large size-large wp-image-27536\" alt=\"\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/a>\n\t\t<div class=\"elementor-element elementor-element-fd244d6 e-con-full e-flex e-con e-child\" data-id=\"fd244d6\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-fd4076f elementor-widget elementor-widget-heading\" data-id=\"fd4076f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">GALLERY<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-c5da9de e-con-full e-flex e-con e-child\" data-id=\"c5da9de\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2f848c5 elementor-widget elementor-widget-html\" data-id=\"2f848c5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- ============================================================\n     ALTITUDE GALERIE \u2014 VERSION ELEMENTOR\n     \u00c0 coller dans un widget HTML dans Elementor\n\n     AVANT de coller :\n     1. Cr\u00e9er une section Elementor full-width\n     2. Background de la section : #0e0b1e\n     3. Min-height de la section : 100vh\n     4. Ajouter un widget HTML \u2192 coller ce code\n============================================================ -->\n\n<style>\n  \/* Scoped au widget \u2014 n'affecte pas le reste de la page *\/\n  #alt-wrap, #alt-wrap *,\n  #alt-wrap *::before,\n  #alt-wrap *::after {\n    box-sizing: border-box;\n  }\n\n  \/* Wrapper principal *\/\n  #alt-wrap {\n    position: relative;\n    z-index: 1;\n    min-height: 100vh;\n    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;\n    color: #fff;\n  }\n\n  #alt-app {\n    position: relative;\n    z-index: 1;\n    padding: 0 1.5rem 5rem;\n    max-width: 1400px;\n    margin: 0 auto;\n  }\n\n  \/* \u2500\u2500 Header \u2500\u2500 *\/\n  .alt-header {\n    text-align: center;\n    padding: 3.5rem 0 2.5rem;\n  }\n  .alt-logo {\n    display: inline-block;\n    font-size: 0.7rem;\n    letter-spacing: 0.28em;\n    text-transform: uppercase;\n    color: rgba(255,255,255,0.4);\n    margin-bottom: 1rem;\n  }\n  .alt-header h1 {\n    font-size: clamp(2.2rem, 5vw, 3.8rem);\n    font-weight: 800;\n    letter-spacing: -0.03em;\n    line-height: 1.05;\n    margin: 0;\n    background: linear-gradient(135deg, #fff 0%, #4CCFDB 45%, #ea4299 100%);\n    -webkit-background-clip: text;\n    -webkit-text-fill-color: transparent;\n    background-clip: text;\n  }\n  .alt-header p {\n    margin-top: 0.8rem;\n    color: rgba(255,255,255,0.5);\n    font-size: 1rem;\n  }\n\n  \/* \u2500\u2500 Tabs \u2500\u2500 *\/\n  .alt-tabs {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 0.6rem;\n    justify-content: center;\n    margin-bottom: 2.5rem;\n    padding: 0 0.5rem;\n  }\n  #alt-wrap .alt-tab {\n    padding: 0.75rem 1.8rem;\n    border-radius: 999px;\n    border: 1px solid rgba(255,255,255,0.25) !important;\n    background: #0035A9 !important;\n    color: rgba(255,255,255,0.9) !important;\n    font-size: 1.05rem;\n    font-weight: 500;\n    cursor: pointer;\n    transition: all 0.22s ease;\n    font-family: inherit;\n    white-space: nowrap;\n  }\n  #alt-wrap .alt-tab:hover:not(.disabled) {\n    background: #FFEC9F !important;\n    color: #1a1200 !important;\n    border-color: transparent !important;\n    box-shadow: 0 4px 24px rgba(255,236,159,0.4);\n  }\n  #alt-wrap .alt-tab.active {\n    background: #FFEC9F !important;\n    border-color: transparent !important;\n    color: #1a1200 !important;\n    font-weight: 700;\n    box-shadow: 0 4px 24px rgba(255,236,159,0.4);\n  }\n  #alt-wrap .alt-tab.disabled {\n    opacity: 0.4;\n    cursor: default;\n    border-style: dashed !important;\n  }\n  .alt-tab .count {\n    opacity: 0.7;\n    font-size: 0.78em;\n    margin-left: 0.35em;\n  }\n\n  \/* \u2500\u2500 Panels \u2500\u2500 *\/\n  .alt-panel { display: none; }\n  .alt-panel.active {\n    display: block;\n    animation: altFadeIn 0.3s ease;\n  }\n  @keyframes altFadeIn {\n    from { opacity: 0; transform: translateY(10px); }\n    to   { opacity: 1; transform: translateY(0); }\n  }\n\n  \/* \u2500\u2500 City label \u2500\u2500 *\/\n  .alt-city-label {\n    text-align: center;\n    font-size: 0.85rem;\n    letter-spacing: 0.2em;\n    text-transform: uppercase;\n    color: rgba(255,255,255,0.4);\n    margin-bottom: 1.5rem;\n  }\n\n  \/* \u2500\u2500 Photo grid \u2500\u2500 *\/\n  .alt-grid {\n    display: grid;\n    grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));\n    gap: 1rem;\n  }\n\n  \/* \u2500\u2500 Photo card \u2500\u2500 *\/\n  .alt-card {\n    border-radius: 14px;\n    overflow: hidden;\n    cursor: pointer;\n    position: relative;\n    background: rgba(255,255,255,0.04);\n    border: 1px solid rgba(255,255,255,0.08);\n    backdrop-filter: blur(10px);\n    -webkit-backdrop-filter: blur(10px);\n    transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s;\n    aspect-ratio: 4\/3;\n  }\n  .alt-card:hover {\n    transform: translateY(-5px) scale(1.01);\n    box-shadow: 0 20px 50px rgba(0,0,0,0.5);\n    border-color: rgba(76,207,219,0.35);\n  }\n  .alt-card img {\n    width: 100%; height: 100%;\n    object-fit: cover;\n    display: block;\n    transition: transform 0.4s ease, opacity 0.3s;\n  }\n  .alt-card img.loading { opacity: 0; }\n  .alt-card:hover img { transform: scale(1.05); }\n\n  .alt-card-overlay {\n    position: absolute;\n    inset: 0;\n    background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 55%);\n    opacity: 0;\n    transition: opacity 0.22s;\n    display: flex;\n    align-items: flex-end;\n    padding: 0.9rem 1rem;\n  }\n  .alt-card:hover .alt-card-overlay { opacity: 1; }\n  .alt-card-caption {\n    font-size: 0.78rem;\n    color: rgba(255,255,255,0.8);\n  }\n\n  \/* \u2500\u2500 Loading & states \u2500\u2500 *\/\n  .alt-loading, .alt-empty, .alt-error {\n    text-align: center;\n    padding: 5rem 2rem;\n    color: rgba(255,255,255,0.4);\n  }\n  .alt-spinner {\n    width: 2rem; height: 2rem;\n    border: 2px solid rgba(76,207,219,0.2);\n    border-top-color: #4CCFDB;\n    border-radius: 50%;\n    animation: spin 0.8s linear infinite;\n    margin: 0 auto 1rem;\n  }\n  @keyframes spin { to { transform: rotate(360deg); } }\n  .alt-error { color: rgba(234,66,153,0.7); }\n\n  \/* \u2500\u2500 Lightbox \u2500\u2500 *\/\n  .alt-lightbox {\n    display: none;\n    position: fixed;\n    inset: 0;\n    z-index: 99999;\n    background: rgba(5,3,18,0.95);\n    backdrop-filter: blur(24px);\n    -webkit-backdrop-filter: blur(24px);\n    align-items: center;\n    justify-content: center;\n  }\n  .alt-lightbox.open { display: flex; }\n\n  .alt-lb-wrap {\n    position: relative;\n    max-width: 90vw;\n    max-height: 88vh;\n    cursor: default;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 0.75rem;\n  }\n  .alt-lightbox img {\n    max-width: 90vw;\n    max-height: 82vh;\n    border-radius: 10px;\n    box-shadow: 0 0 100px rgba(0,0,0,0.8);\n    display: block;\n  }\n  .alt-lb-caption-bar {\n    font-size: 0.78rem;\n    color: rgba(255,255,255,0.4);\n    letter-spacing: 0.04em;\n    text-align: center;\n  }\n  .alt-lb-close {\n    position: absolute;\n    top: 1.25rem; right: 1.25rem;\n    width: 2.4rem; height: 2.4rem;\n    border-radius: 50% !important;\n    background: #FFEC9F !important;\n    border: none !important;\n    color: #0035A9 !important; font-size: 1rem; font-weight: 700;\n    cursor: pointer;\n    display: flex; align-items: center; justify-content: center;\n    transition: background 0.2s, transform 0.2s;\n    font-family: inherit;\n  }\n  .alt-lb-close:hover { background: #ffe066 !important; transform: scale(1.1); }\n  .alt-lb-nav {\n    position: absolute;\n    top: 50%; transform: translateY(-50%);\n    background: #FFEC9F !important;\n    border: none !important;\n    color: #0035A9 !important; font-size: 1.5rem; font-weight: 700;\n    width: 3rem; height: 3rem;\n    border-radius: 50% !important;\n    cursor: pointer;\n    display: flex; align-items: center; justify-content: center;\n    transition: background 0.2s, transform 0.2s;\n    font-family: inherit;\n  }\n  .alt-lb-nav:hover { background: #ffe066 !important; transform: translateY(-50%) scale(1.1); }\n  .alt-lb-prev { left: 1rem; }\n  .alt-lb-next { right: 1rem; }\n\n  @media (max-width: 640px) {\n    .alt-grid { grid-template-columns: repeat(2, 1fr); gap: 0.6rem; }\n    .alt-tab  { padding: 0.5rem 0.9rem; font-size: 0.82rem; }\n    .alt-lb-nav { display: none; }\n  }\n<\/style>\n\n<!-- Galerie \u2014 onglets + grille (le titre est g\u00e9r\u00e9 dans Elementor) -->\n<div id=\"alt-wrap\">\n  <div id=\"alt-app\">\n    <nav class=\"alt-tabs\" id=\"altTabs\" role=\"tablist\"><\/nav>\n    <main id=\"altPanels\"><\/main>\n  <\/div>\n<\/div>\n\n<!-- Lightbox -->\n<div class=\"alt-lightbox\" id=\"altLightbox\">\n  <button class=\"alt-lb-close\" id=\"altLbClose\">\u2715<\/button>\n  <button class=\"alt-lb-nav alt-lb-prev\" id=\"altLbPrev\">&#8249;<\/button>\n  <div class=\"alt-lb-wrap\" id=\"altLbWrap\">\n    <img decoding=\"async\" id=\"altLbImg\" src=\"\" alt=\"\">\n    <div class=\"alt-lb-caption-bar\" id=\"altLbCaptBar\"><\/div>\n  <\/div>\n  <button class=\"alt-lb-nav alt-lb-next\" id=\"altLbNext\">&#8250;<\/button>\n<\/div>\n\n<script>\n\/* \u2500\u2500 CONFIG \u2500\u2500 *\/\nconst SHEET_CSV_URL = 'https:\/\/docs.google.com\/spreadsheets\/d\/e\/2PACX-1vR5-84BuCsnz-wD6XNc7i6zd3tk1ALWAnIJrexBDR3y1_kUy0d0fTYQI38kdkLhsE0NGWJOGTJGp0WL\/pub?gid=0&single=true&output=csv';\n\nconst CITY_META = {\n  'Laval':       { year: '2024' },\n  'Toronto':     { year: '2023' },\n  'Chicoutimi':  { year: '2023' },\n  'Qu\u00e9bec':      { year: '2022' },\n  'Sherbrooke':  { year: '2022' },\n  'Halifax':     { year: '2021' },\n  'Calgary':     { year: '2021' },\n};\n\nconst CITIES_ORDER = Object.keys(CITY_META);\nconst tabsEl   = document.getElementById('altTabs');\nconst panelsEl = document.getElementById('altPanels');\nlet currentCity = 0, currentPhotoIdx = 0, galleryData = {}, lbKeys = [];\n\nfunction parseCSV(text) {\n  const rows = [];\n  for (const line of text.trim().split(\/\\r?\\n\/)) {\n    const cols = []; let cur = '', inQ = false;\n    for (let i = 0; i < line.length; i++) {\n      const c = line[i];\n      if (c === '\"') { inQ = !inQ; continue; }\n      if (c === ',' && !inQ) { cols.push(cur.trim()); cur = ''; continue; }\n      cur += c;\n    }\n    cols.push(cur.trim()); rows.push(cols);\n  }\n  return rows;\n}\n\nfunction buildGalleryData(rows) {\n  const data = {};\n  const start = (rows[0] && rows[0][0].toLowerCase().includes('ville')) ? 1 : 0;\n  for (let i = start; i < rows.length; i++) {\n    const [ville, url, caption = ''] = rows[i];\n    if (!ville || !url) continue;\n    const key = ville.trim();\n    if (!data[key]) data[key] = [];\n    data[key].push({ url: url.trim(), caption: caption.trim() });\n  }\n  return data;\n}\n\nfunction renderGallery(data) {\n  tabsEl.innerHTML = ''; panelsEl.innerHTML = '';\n  galleryData = data;\n  \/\/ Toujours afficher toutes les villes \u2014 avec photos ou non\n  const keys = [...CITIES_ORDER,\n                 ...Object.keys(data).filter(c => !CITIES_ORDER.includes(c))];\n  const firstActive = keys.findIndex(c => (data[c] || []).length > 0);\n\n  keys.forEach((city, i) => {\n    const photos  = data[city] || [];\n    const hasPhotos = photos.length > 0;\n    const meta    = CITY_META[city] || {};\n    const isFirst = i === (firstActive >= 0 ? firstActive : 0);\n    const tab = document.createElement('button');\n    tab.className = 'alt-tab' + (isFirst ? ' active' : '') + (!hasPhotos ? ' disabled' : '');\n    tab.innerHTML = city + (hasPhotos ? `<span class=\"count\">${photos.length}<\/span>` : '');\n    if (hasPhotos) tab.onclick = () => switchCity(i, keys);\n    tabsEl.appendChild(tab);\n\n    const panel = document.createElement('div');\n    panel.className = 'alt-panel' + (isFirst ? ' active' : '');\n    const label = document.createElement('div');\n    label.className = 'alt-city-label';\n    label.textContent = city + (meta.year ? ' \u00b7 Congr\u00e8s ' + meta.year : '');\n    panel.appendChild(label);\n\n    if (!photos.length) {\n      panel.innerHTML += '<div class=\"alt-empty\">\ud83d\udcf7 Photos \u00e0 venir\u2026<\/div>';\n    } else {\n      const grid = document.createElement('div');\n      grid.className = 'alt-grid';\n      photos.forEach((p, j) => {\n        const card = document.createElement('div');\n        card.className = 'alt-card';\n        const img = document.createElement('img');\n        img.className = 'loading';\n        img.alt = p.caption || city + ' \u00b7 Photo ' + (j + 1);\n        img.loading = 'lazy';\n        img.onload = () => img.classList.remove('loading');\n        img.src = p.url;\n        const overlay = document.createElement('div');\n        overlay.className = 'alt-card-overlay';\n        if (p.caption) { const cap = document.createElement('div'); cap.className = 'alt-card-caption'; cap.textContent = p.caption; overlay.appendChild(cap); }\n        card.appendChild(img); card.appendChild(overlay);\n        card.addEventListener('click', () => openLightbox(keys.indexOf(city), j, keys));\n        grid.appendChild(card);\n      });\n      panel.appendChild(grid);\n    }\n    panelsEl.appendChild(panel);\n  });\n}\n\nfunction switchCity(idx, keys) {\n  currentCity = idx;\n  document.querySelectorAll('.alt-tab').forEach((t, i) => t.classList.toggle('active', i === idx));\n  document.querySelectorAll('.alt-panel').forEach((p, i) => p.classList.toggle('active', i === idx));\n}\n\nconst lb = document.getElementById('altLightbox');\nconst lbImg = document.getElementById('altLbImg');\nconst lbCapt = document.getElementById('altLbCaptBar');\n\nfunction openLightbox(cityIdx, photoIdx, keys) {\n  lbKeys = keys; currentCity = cityIdx; currentPhotoIdx = photoIdx;\n  updateLb(); lb.classList.add('open'); document.body.style.overflow = 'hidden';\n}\nfunction closeLightbox() { lb.classList.remove('open'); document.body.style.overflow = ''; }\nfunction updateLb() {\n  const city = lbKeys[currentCity], photos = galleryData[city] || [], p = photos[currentPhotoIdx];\n  if (!p) return;\n  lbImg.src = p.url; lbImg.alt = p.caption || city + ' \u00b7 Photo ' + (currentPhotoIdx + 1);\n  lbCapt.textContent = (p.caption ? p.caption + ' \u00b7 ' : '') + city + '  ' + (currentPhotoIdx + 1) + ' \/ ' + photos.length;\n}\nfunction lbNext() { const len = (galleryData[lbKeys[currentCity]] || []).length; currentPhotoIdx = (currentPhotoIdx + 1) % len; updateLb(); }\nfunction lbPrev() { const len = (galleryData[lbKeys[currentCity]] || []).length; currentPhotoIdx = (currentPhotoIdx - 1 + len) % len; updateLb(); }\n\ndocument.getElementById('altLbClose').addEventListener('click', closeLightbox);\ndocument.getElementById('altLbNext').addEventListener('click', e => { e.stopPropagation(); lbNext(); });\ndocument.getElementById('altLbPrev').addEventListener('click', e => { e.stopPropagation(); lbPrev(); });\nlb.addEventListener('click', closeLightbox);\ndocument.getElementById('altLbWrap').addEventListener('click', e => e.stopPropagation());\ndocument.addEventListener('keydown', e => {\n  if (!lb.classList.contains('open')) return;\n  if (e.key === 'Escape') closeLightbox();\n  if (e.key === 'ArrowRight') lbNext();\n  if (e.key === 'ArrowLeft') lbPrev();\n});\n\n\/* \u2500\u2500 Swipe mobile \u2500\u2500 *\/\nlet swipeStartX = 0;\nlb.addEventListener('touchstart', e => { swipeStartX = e.touches[0].clientX; }, { passive: true });\nlb.addEventListener('touchend', e => {\n  const diff = swipeStartX - e.changedTouches[0].clientX;\n  if (Math.abs(diff) > 50) diff > 0 ? lbNext() : lbPrev();\n}, { passive: true });\n\nasync function loadSheet() {\n  panelsEl.innerHTML = '<div class=\"alt-loading\"><div class=\"alt-spinner\"><\/div>Chargement des photos\u2026<\/div>';\n  try {\n    const resp = await fetch(SHEET_CSV_URL, { cache: 'no-store' });\n    if (!resp.ok) throw new Error('HTTP ' + resp.status);\n    renderGallery(buildGalleryData(parseCSV(await resp.text())));\n  } catch (err) {\n    panelsEl.innerHTML = '<div class=\"alt-error\">Impossible de charger les photos.<br><small>' + err.message + '<\/small><\/div>';\n  }\n}\nloadSheet();\n\n\/* \u2500\u2500 AURORA WebGL \u2014 fixed sur le body (viewport uniquement, peu importe la hauteur de page) \u2500\u2500 *\/\n(function () {\n  'use strict';\n\n  \/* Fond sombre sur le body uniquement *\/\n  document.body.style.background = '#0e0b1e';\n\n  \/* Cr\u00e9er le canvas fixe couvrant le viewport *\/\n  const canvas = document.createElement('canvas');\n  canvas.id = 'alt-aurora';\n  Object.assign(canvas.style, {\n    position:      'fixed',\n    top:           '0',\n    left:          '0',\n    width:         '100%',\n    height:        '100%',\n    zIndex:        '-1',\n    pointerEvents: 'none',\n    display:       'block',\n  });\n  document.body.appendChild(canvas);\n  const FRAG = `\nprecision highp float;\nvarying vec2 v_uv;\nuniform vec2 u_res;\nuniform float u_t;\nuniform vec2 u_mouse;\nuniform float u_pulse;\n\nfloat hash(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }\nfloat vnoise(vec2 p){\n  vec2 i = floor(p), f = fract(p);\n  vec2 u = f*f*(3.0-2.0*f);\n  return mix(mix(hash(i), hash(i+vec2(1.0,0.0)), u.x),\n             mix(hash(i+vec2(0.0,1.0)), hash(i+vec2(1.0,1.0)), u.x), u.y);\n}\nfloat fbm(vec2 p){\n  float v = 0.0, a = 0.5;\n  for (int i = 0; i < 5; i++) { v += a * vnoise(p); p *= 2.02; a *= 0.5; }\n  return v;\n}\nvoid main(){\n  vec2 uv = v_uv;\n  float aspect = u_res.x \/ u_res.y;\n  vec2 m = u_mouse - 0.5;\n  vec2 q = vec2(uv.x * aspect, uv.y) * 1.8;\n  float t = u_t * 0.07;\n  vec2 w = vec2(fbm(q + t + m*1.5), fbm(q - t * 0.7 + 7.3 - m*1.5));\n  float n = fbm(q + w * 1.4 + t);\n  float band = sin(uv.y * 5.5 + w.x * 4.0 + u_t * 0.6 + m.x * 3.0) * 0.5 + 0.5;\n  band = pow(band, 1.6);\n  vec3 midnight = vec3(0.055, 0.020, 0.165);  \/* violet tr\u00e8s sombre *\/\n  vec3 deep     = vec3(0.000, 0.100, 0.420);  \/* bleu nuit profond  *\/\n  vec3 royal    = vec3(0.000, 0.208, 0.663);  \/* bleu Altitude #0035A9 *\/\n  vec3 azure    = vec3(0.000, 0.480, 1.000);  \/* bleu \u00e9lectrique    *\/\n  vec3 bright   = vec3(0.200, 0.700, 1.000);  \/* bleu ciel lumineux *\/\n  vec3 col = mix(midnight, deep, 1.0 - uv.y + uv.x * 0.5);\n  col = mix(col, royal,  smoothstep(0.25, 0.70, n) * band * 0.90);\n  col = mix(col, azure,  smoothstep(0.45, 0.85, n) * pow(band, 1.6) * 0.85);\n  col = mix(col, bright, smoothstep(0.68, 1.00, n) * band * 0.55);\n  vec2 mp = u_mouse;\n  vec2 d = (uv - mp) * vec2(aspect, 1.0);\n  float r = length(d);\n  col += azure  * 0.25 * exp(-r * 5.0);\n  col += bright * 0.15 * exp(-r * 2.8);\n  float ring = exp(-pow(r, 2.0) * 18.0) * u_pulse;\n  col += vec3(0.95, 0.85, 1.0) * ring * 0.45;\n  vec2 c = uv - 0.5;\n  col *= 1.0 - dot(c, c) * 0.45;\n  gl_FragColor = vec4(col, 1.0);\n}`;\n  const VS = `attribute vec2 a_pos; varying vec2 v_uv;\nvoid main(){ v_uv = a_pos * 0.5 + 0.5; gl_Position = vec4(a_pos, 0.0, 1.0); }`;\n\n  \/* canvas d\u00e9j\u00e0 cr\u00e9\u00e9 et attach\u00e9 au body ci-dessus *\/\n  const gl = canvas.getContext('webgl', { antialias: false, premultipliedAlpha: false }) || canvas.getContext('experimental-webgl');\n  if (!gl) { canvas.style.background = 'radial-gradient(circle at 30% 20%, #2a2547 0%, #0c0a1a 70%)'; return; }\n\n  const compile = (src, type) => { const s = gl.createShader(type); gl.shaderSource(s, src); gl.compileShader(s); return gl.getShaderParameter(s, gl.COMPILE_STATUS) ? s : null; };\n  const program = gl.createProgram();\n  gl.attachShader(program, compile(VS, gl.VERTEX_SHADER));\n  gl.attachShader(program, compile(FRAG, gl.FRAGMENT_SHADER));\n  gl.linkProgram(program); gl.useProgram(program);\n\n  const buf = gl.createBuffer();\n  gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);\n  const aPos = gl.getAttribLocation(program, 'a_pos');\n  gl.enableVertexAttribArray(aPos); gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);\n\n  const u_res = gl.getUniformLocation(program, 'u_res');\n  const u_t   = gl.getUniformLocation(program, 'u_t');\n  const u_mouse = gl.getUniformLocation(program, 'u_mouse');\n  const u_pulse = gl.getUniformLocation(program, 'u_pulse');\n\n  const ptr = { tx: 0.5, ty: 0.5, x: 0.5, y: 0.5, pulse: 0 };\n  window.addEventListener('pointermove', e => {\n    ptr.tx = e.clientX \/ window.innerWidth;\n    ptr.ty = 1.0 - (e.clientY \/ window.innerHeight);\n  });\n  window.addEventListener('pointerdown', () => { ptr.pulse = 1; });\n\n  const resize = () => {\n    const dpr = Math.min(window.devicePixelRatio || 1, 1.5);\n    canvas.width  = Math.round(window.innerWidth  * dpr);\n    canvas.height = Math.round(window.innerHeight * dpr);\n  };\n  window.addEventListener('resize', resize);\n  resize();\n\n  let last = performance.now(); const start = last;\n  const frame = () => {\n    const now = performance.now(), dt = Math.min(0.05, (now - last) \/ 1000); last = now;\n    const k = 1 - Math.exp(-dt * 6);\n    ptr.x += (ptr.tx - ptr.x) * k; ptr.y += (ptr.ty - ptr.y) * k;\n    ptr.pulse *= Math.exp(-dt * 2.2);\n    gl.viewport(0, 0, canvas.width, canvas.height);\n    gl.uniform2f(u_res, canvas.width, canvas.height);\n    gl.uniform1f(u_t, (now - start) \/ 1000);\n    gl.uniform2f(u_mouse, ptr.x, ptr.y);\n    gl.uniform1f(u_pulse, ptr.pulse);\n    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n    requestAnimationFrame(frame);\n  };\n  requestAnimationFrame(frame);\n})();\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-d5a75da glass-dark square e-flex e-con-boxed e-con e-child\" data-id=\"d5a75da\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;video&quot;,&quot;position&quot;:&quot;fixed&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b847d7d elementor-hidden-mobile_extra elementor-hidden-mobile elementor-widget elementor-widget-image\" data-id=\"b847d7d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<a href=\"https:\/\/www.itcloud.ca\/\">\n\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1\" height=\"1\" src=\"https:\/\/www.itcloud.ca\/wp-content\/uploads\/2024\/08\/Logo-ITCloud-En-Blanc.svg\" class=\"attachment-large size-large wp-image-17980\" alt=\"\" \/>\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-fdfd81c e-con-full e-flex e-con e-child\" data-id=\"fdfd81c\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-d023230 elementor-widget elementor-widget-heading\" data-id=\"d023230\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\"><a href=\"https:\/\/www.itcloud.ca\/en\/altitude-roadshow-2026-conferences\/\">Conferences<\/a><\/h3>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d155e29 elementor-widget elementor-widget-button\" data-id=\"d155e29\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"button.default\">\n\t\t\t\t\t\t\t\t\t\t<a class=\"elementor-button elementor-button-link elementor-size-sm\" href=\"https:\/\/www.itcloud.ca\/en\/altitude-roadshow-2026\/\">\n\t\t\t\t\t\t<span class=\"elementor-button-content-wrapper\">\n\t\t\t\t\t\t\t\t\t<span class=\"elementor-button-text\">Altitude Roadshow<\/span>\n\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"GALLERY \u2715 &#8249; &#8250; Conferences Altitude Roadshow","protected":false},"author":14,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"class_list":["post-27509","page","type-page","status-publish","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Altitude Gallery - ITCloud.ca<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Altitude Gallery - ITCloud.ca\" \/>\n<meta property=\"og:description\" content=\"GALLERY \u2715 &#8249; &#8250; Conferences Altitude Roadshow\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/\" \/>\n<meta property=\"og:site_name\" content=\"ITCloud.ca\" \/>\n<meta property=\"article:modified_time\" content=\"2026-05-21T19:42:07+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"41 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/altitude-gallery\\\/\",\"url\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/altitude-gallery\\\/\",\"name\":\"Altitude Gallery - ITCloud.ca\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/altitude-gallery\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/altitude-gallery\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.itcloud.ca\\\/wp-content\\\/uploads\\\/LogoAltitude-Reverse-1.svg\",\"datePublished\":\"2026-05-21T18:53:14+00:00\",\"dateModified\":\"2026-05-21T19:42:07+00:00\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/altitude-gallery\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/altitude-gallery\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.itcloud.ca\\\/wp-content\\\/uploads\\\/LogoAltitude-Reverse-1.svg\",\"contentUrl\":\"https:\\\/\\\/www.itcloud.ca\\\/wp-content\\\/uploads\\\/LogoAltitude-Reverse-1.svg\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/\",\"name\":\"ITCloud.ca\",\"description\":\"Continuit\u00e9 et Productivit\u00e9\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/#organization\",\"name\":\"ITCloud.ca\",\"url\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.itcloud.ca\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/logo-itcloud.svg\",\"contentUrl\":\"https:\\\/\\\/www.itcloud.ca\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/logo-itcloud.svg\",\"width\":1,\"height\":1,\"caption\":\"ITCloud.ca\"},\"image\":{\"@id\":\"https:\\\/\\\/www.itcloud.ca\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Altitude Gallery - ITCloud.ca","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/","og_locale":"en_US","og_type":"article","og_title":"Altitude Gallery - ITCloud.ca","og_description":"GALLERY \u2715 &#8249; &#8250; Conferences Altitude Roadshow","og_url":"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/","og_site_name":"ITCloud.ca","article_modified_time":"2026-05-21T19:42:07+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"41 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/","url":"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/","name":"Altitude Gallery - ITCloud.ca","isPartOf":{"@id":"https:\/\/www.itcloud.ca\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/#primaryimage"},"image":{"@id":"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/#primaryimage"},"thumbnailUrl":"https:\/\/www.itcloud.ca\/wp-content\/uploads\/LogoAltitude-Reverse-1.svg","datePublished":"2026-05-21T18:53:14+00:00","dateModified":"2026-05-21T19:42:07+00:00","inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.itcloud.ca\/en\/altitude-gallery\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.itcloud.ca\/en\/altitude-gallery\/#primaryimage","url":"https:\/\/www.itcloud.ca\/wp-content\/uploads\/LogoAltitude-Reverse-1.svg","contentUrl":"https:\/\/www.itcloud.ca\/wp-content\/uploads\/LogoAltitude-Reverse-1.svg"},{"@type":"WebSite","@id":"https:\/\/www.itcloud.ca\/en\/#website","url":"https:\/\/www.itcloud.ca\/en\/","name":"ITCloud.ca","description":"Continuit\u00e9 et Productivit\u00e9","publisher":{"@id":"https:\/\/www.itcloud.ca\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.itcloud.ca\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.itcloud.ca\/en\/#organization","name":"ITCloud.ca","url":"https:\/\/www.itcloud.ca\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.itcloud.ca\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.itcloud.ca\/wp-content\/uploads\/2021\/01\/logo-itcloud.svg","contentUrl":"https:\/\/www.itcloud.ca\/wp-content\/uploads\/2021\/01\/logo-itcloud.svg","width":1,"height":1,"caption":"ITCloud.ca"},"image":{"@id":"https:\/\/www.itcloud.ca\/en\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/www.itcloud.ca\/en\/wp-json\/wp\/v2\/pages\/27509","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.itcloud.ca\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.itcloud.ca\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.itcloud.ca\/en\/wp-json\/wp\/v2\/users\/14"}],"replies":[{"embeddable":true,"href":"https:\/\/www.itcloud.ca\/en\/wp-json\/wp\/v2\/comments?post=27509"}],"version-history":[{"count":13,"href":"https:\/\/www.itcloud.ca\/en\/wp-json\/wp\/v2\/pages\/27509\/revisions"}],"predecessor-version":[{"id":27563,"href":"https:\/\/www.itcloud.ca\/en\/wp-json\/wp\/v2\/pages\/27509\/revisions\/27563"}],"wp:attachment":[{"href":"https:\/\/www.itcloud.ca\/en\/wp-json\/wp\/v2\/media?parent=27509"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}