SIMON Master Console Rebuild
True command center for SIMON + WEB360
This rebuild gives you a cleaner production spine: interactive tree, live viewer, guarded editor, dynamic graphics, architecture map, and note storage. It is designed to sit beside your existing
console.php
and become the stronger replacement path.
Dashboard
Tree
Viewer
System Map
File Registry
Notes
Files
15,338
Folders
408
Scanned Size
3.84 GB
PHP Files
890
Editable Text Files
12,599
File viewer
guarded to /htdocs
/art/index.php
<!doctype html> <html lang="en" data-lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,viewport-fit=cover"> <meta name="theme-color" content="#070b16"> <title>Tilo Soto Art — Intergalactic Gallery • Gaylord Sinclair</title> <meta name="description" content="Explore Tilo Soto's futuristic artworks in an intergalactic neon-glass gallery with immersive motion and a cinematic lightbox viewer."> <link rel="canonical" href="https://www.gaylordsinclair.com/art/"> <meta property="og:type" content="website"> <meta property="og:title" content="Tilo Soto Art — Intergalactic Gallery • Gaylord Sinclair"> <meta property="og:description" content="Explore Tilo's futuristic art—floating panels, neon energy, and immersive motion galleries."> <meta property="og:url" content="https://www.gaylordsinclair.com/art/"> <meta property="og:image" content="https://www.gaylordsinclair.com/apple-touch-icon.png"> <meta name="twitter:card" content="summary_large_image"> <script type="application/ld+json"> {"@context":"https://schema.org","@type":"CollectionPage","name":"Tilo Soto — Art Gallery","url":"https://www.gaylordsinclair.com/art/","about":{"@type":"Person","name":"Tilo Soto"},"description":"Intergalactic neon-glass gallery with motion panels and a lightbox viewer."} </script> <!-- SIMON / GA4 centralized trackers --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-S6MEHB4BEY"></script> <script> window.SIMON_CONFIG = Object.assign({ GA4_ID:'G-S6MEHB4BEY', IMPACT_VERIFICATION:'e8ec6683-663c-421a-a30d-f8c0fe517345', WEBHOOK_BASE:'/connlink/test1/ui/webhook_inbox.php', VISIT_STREAM:'visits', PULSE_STREAM:'default', KEY:'', SOURCE:'art-gallery-tilo', TRACK_HEAT:true, TRACK_SCROLL:true, TRACK_LIGHTBOX:true }, window.SIMON_CONFIG || {}); window.dataLayer = window.dataLayer || []; function gtag(){ dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', window.SIMON_CONFIG.GA4_ID, { page_title: document.title, page_location: location.href, send_page_view: true }); </script> <!-- Animated favicon from MP4 --> <script> document.addEventListener("DOMContentLoaded",()=>{ try{ const link=document.createElement("link");link.rel="icon";document.head.appendChild(link); const video=document.createElement("video"); video.src="/favicon.mp4";video.muted=true;video.loop=true;video.playsInline=true; const canvas=document.createElement("canvas");canvas.width=64;canvas.height=64; const c=canvas.getContext("2d");let tick=0; function draw(){tick=(tick+1)%2;if(!video.paused&&!video.ended&&tick===0){c.clearRect(0,0,64,64);try{c.drawImage(video,0,0,64,64);link.href=canvas.toDataURL("image/png");}catch{}}requestAnimationFrame(draw);} video.addEventListener("play",draw);video.play().catch(()=>{}); }catch{} }); </script> <style> /* ───────────────────────────────────────────────────────── TOKENS ───────────────────────────────────────────────────────── */ :root{ --bg:#070b16; --ink:#eaf2ff; --muted:#a8b6d9; --edge:rgba(255,255,255,.14); --glass:rgba(255,255,255,.06); --line:conic-gradient(from 0deg,#00d0ff,#7b5bff,#ff2bd6,#00ffd0,#00d0ff); --floatDepth:22px; --hoverLift:14px; --speed:18s; --price:#cfe3ff; --cyan:#00d0ff; --violet:#7b5bff; --pink:#ff2bd6; } *{box-sizing:border-box} html,body{height:100%} body{ margin:0;color:var(--ink); font:15px/1.6 ui-sans-serif,system-ui,Inter,Segoe UI,Roboto,Arial; background: radial-gradient(1200px 800px at 85% -10%,rgba(106,224,255,.10),transparent 60%), radial-gradient(900px 700px at 10% -20%,rgba(123,91,255,.10),transparent 60%), radial-gradient(1200px 900px at 50% 120%,rgba(255,43,214,.06),transparent 60%), var(--bg); overflow-x:hidden; } a{color:inherit;text-decoration:none} /* ───────────────────────────────────────────────────────── CANVAS LAYERS ───────────────────────────────────────────────────────── */ #cv-bg,#orbs,#orbs2{ position:fixed;inset:0;z-index:-3; display:block;width:100vw;height:100vh;background:transparent; } #cv-bg{z-index:-3} #orbs{z-index:-2;filter:blur(.2px) saturate(1.1)} #orbs2{z-index:-1;filter:blur(.2px) saturate(1.1)} /* ───────────────────────────────────────────────────────── TOP BAR ───────────────────────────────────────────────────────── */ .top{ position:sticky;top:0;z-index:30; background:linear-gradient(180deg,rgba(7,11,22,.90),rgba(7,11,22,.52) 60%,rgba(7,11,22,0)); border-bottom:1px solid var(--edge); backdrop-filter:blur(14px); } .wrap{max-width:1280px;margin-inline:auto;padding:12px 16px} .row{display:flex;align-items:center;gap:14px;flex-wrap:wrap} .brand{display:flex;align-items:center;gap:10px;font-weight:950;letter-spacing:.08em} .orb{ width:14px;height:14px;border-radius:50%; background:var(--line); box-shadow:0 0 18px rgba(106,224,255,.75); animation:orbPulse 3s ease-in-out infinite; } @keyframes orbPulse{ 0%,100%{box-shadow:0 0 14px rgba(106,224,255,.65)} 50% {box-shadow:0 0 28px rgba(106,224,255,.95),0 0 48px rgba(123,91,255,.5)} } /* Energy line under header */ .top::after{ content:'';display:block; height:1px; background:linear-gradient(90deg, transparent 0%, var(--cyan) 20%, var(--violet) 50%, var(--pink) 80%, transparent 100%); opacity:.55; animation:scanH 4s ease-in-out infinite; } @keyframes scanH{ 0%,100%{opacity:.25;transform:scaleX(.6)} 50% {opacity:.70;transform:scaleX(1.02)} } /* Nav */ .topnav{display:flex;gap:10px;flex-wrap:wrap} .topnav .glow{ position:relative;display:inline-flex;align-items:center;justify-content:center; padding:9px 12px;border-radius:12px; font-weight:900;letter-spacing:.02em;font-size:13px; border:1px solid rgba(106,224,255,.28); background:linear-gradient(180deg,rgba(255,255,255,.10),rgba(255,255,255,.03)); box-shadow:0 0 12px rgba(106,224,255,.12),inset 0 0 0 1px rgba(255,255,255,.05); transition:transform .15s,box-shadow .2s,border-color .2s,filter .2s; -webkit-tap-highlight-color:transparent; } .topnav .glow:hover{ transform:translateY(-2px); border-color:rgba(106,224,255,.80); box-shadow:0 0 26px rgba(106,224,255,.50),0 0 50px rgba(123,91,255,.25); filter:saturate(1.1); } .topnav .glow.active{ border-color:rgba(106,224,255,.92); box-shadow:0 0 30px rgba(106,224,255,.60),0 0 56px rgba(123,91,255,.32); } .chip{ margin-left:auto;display:inline-flex;align-items:center;gap:10px; padding:10px 14px;border-radius:12px;cursor:pointer;user-select:none; background:linear-gradient(180deg,rgba(255,255,255,.10),rgba(255,255,255,.03)); border:1px solid var(--edge);color:var(--ink);font-weight:900; box-shadow:inset 0 1px 0 rgba(255,255,255,.10); transition:border-color .2s,box-shadow .2s; } .chip:hover{border-color:rgba(106,224,255,.45);box-shadow:0 0 18px rgba(106,224,255,.18),inset 0 1px 0 rgba(255,255,255,.12)} .controls{display:flex;gap:10px;flex-wrap:wrap;margin-left:auto} @media(max-width:920px){.controls{width:100%;justify-content:space-between}.chip{margin-left:0}} /* ───────────────────────────────────────────────────────── HERO ───────────────────────────────────────────────────────── */ .hero{padding:18px 16px 6px;margin-inline:auto;max-width:1280px;color:var(--muted);font-size:14px} h1{ margin:8px 0 6px; font-size:clamp(28px,4.8vw,54px);line-height:1.06; background:var(--line);-webkit-background-clip:text;background-clip:text;color:transparent; text-shadow:0 0 24px rgba(106,224,255,.18);letter-spacing:.02em; } .heroRow{display:flex;gap:12px;flex-wrap:wrap;align-items:center} .hint{ display:inline-flex;align-items:center;gap:10px; padding:10px 12px;border-radius:14px; border:1px solid rgba(255,255,255,.12); background:linear-gradient(180deg,rgba(255,255,255,.08),rgba(255,255,255,.02)); } .hint b{color:var(--ink)} .spacerGrow{flex:1} /* ───────────────────────────────────────────────────────── CARD WRAP (animated gradient border) ───────────────────────────────────────────────────────── */ .card-wrap{ position:relative; padding:2px; border-radius:22px; overflow:hidden; /* clips the spinning disc to the border area */ background:rgba(255,255,255,.06); /* dormant state */ transition:background .3s ease; cursor:pointer; /* 3D tilt applied by JS */ transform-style:preserve-3d; will-change:transform; } /* Spinning gradient disc — the border source */ .card-wrap::before{ content:''; position:absolute; inset:-80%; /* oversized so the disc always covers corners */ background:conic-gradient(#00d0ff,#7b5bff,#ff2bd6,#00ffd0,#00d0ff); border-radius:50%; /* disc shape */ opacity:0; animation:borderSpin 2.8s linear infinite; transition:opacity .35s ease; pointer-events:none; z-index:0; } .card-wrap:hover::before{opacity:1} @keyframes borderSpin{to{transform:rotate(360deg)}} /* Outer glow ring — appears on hover */ .card-wrap::after{ content:''; position:absolute; inset:-3px; border-radius:24px; background:transparent; box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent; opacity:0; transition:opacity .35s, box-shadow .35s; pointer-events:none; z-index:0; } .card-wrap:hover::after{ opacity:1; box-shadow: 0 0 22px rgba(0,208,255,.35), 0 0 55px rgba(123,91,255,.25), 0 0 90px rgba(255,43,214,.14); } /* ───────────────────────────────────────────────────────── CARD (sits inside wrap, clips the border-disc to 2px) ───────────────────────────────────────────────────────── */ .card{ position:relative;z-index:1; border-radius:20px;overflow:hidden; background:linear-gradient(180deg,rgba(255,255,255,.09),rgba(255,255,255,.02)); border:none; box-shadow:0 10px 40px rgba(0,0,0,.40); animation:float var(--speed) ease-in-out infinite; animation-delay:var(--delay,0s); will-change:transform,box-shadow; transition:box-shadow .3s ease; } .card-wrap:hover .card{ box-shadow: 0 24px 70px rgba(0,0,0,.60), inset 0 1px 0 rgba(255,255,255,.18); } /* Parallax shimmer inside card on hover */ .card::after{ content:''; position:absolute;inset:0;z-index:2; background:radial-gradient(circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,.12) 0%,transparent 55%); opacity:0; transition:opacity .25s; pointer-events:none; } .card-wrap:hover .card::after{opacity:1} /* Image */ .card img{ display:block;width:100%;height:100%; object-fit:cover; aspect-ratio:var(--ratio,4/5); filter:saturate(1.06) contrast(1.05); transform:scale(1.02); transition:transform .45s cubic-bezier(.2,.8,.2,1),filter .3s; } .card-wrap:hover .card img{ transform:scale(1.10); filter:saturate(1.14) contrast(1.08) brightness(1.04); } /* Meta overlay */ .meta{ position:absolute;inset:auto 0 0 0; padding:14px 12px 12px; background:linear-gradient(180deg,transparent,rgba(4,6,14,.82) 55%, rgba(4,6,14,.96)); display:flex;align-items:flex-end;justify-content:space-between;gap:10px; } .leftMeta{display:flex;flex-direction:column;gap:6px} .titleMini{font-weight:950;font-size:13px;letter-spacing:.02em} .mutedMini{color:var(--muted);font-size:12px;font-weight:800} .pillTag{ display:inline-flex;align-items:center;gap:8px; padding:5px 10px;border-radius:999px; border:1px solid rgba(255,255,255,.14); background:linear-gradient(180deg,rgba(255,255,255,.14),rgba(255,255,255,.04)); font-weight:900;font-size:11px; transition:border-color .25s, background .25s; } .card-wrap:hover .pillTag{ border-color:rgba(0,208,255,.45); background:linear-gradient(180deg,rgba(0,208,255,.16),rgba(0,208,255,.04)); } .price{ font-weight:950;font-size:14px;color:var(--price); text-shadow:0 0 18px rgba(106,224,255,.22);white-space:nowrap; } /* Float keyframe */ @keyframes float{ 0% {transform:translateY(0) rotateX(0) rotateY(0)} 50% {transform:translateY(calc(var(--amp,1)*var(--floatDepth))) rotateX(var(--tilt,3deg)) rotateY(calc(var(--tilt,3deg)*-1))} 100%{transform:translateY(0) rotateX(0) rotateY(0)} } /* Gallery grid */ .grid{ max-width:1280px;margin:14px auto 42px;padding:0 16px; display:grid;gap:18px; grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); } /* ───────────────────────────────────────────────────────── LIGHTBOX (new cinematic transition) ───────────────────────────────────────────────────────── */ .modal{ position:fixed;inset:0;display:grid;place-items:center;z-index:50; /* Visibility transition instead of display:none/grid swap */ opacity:0; visibility:hidden; pointer-events:none; transition:opacity .38s cubic-bezier(.25,.8,.25,1), visibility .38s; } .modal.open{ opacity:1; visibility:visible; pointer-events:auto; } /* Backdrop */ .backdrop{ position:absolute;inset:0; background:rgba(4,6,12,.72); backdrop-filter:blur(0px); transition:backdrop-filter .45s cubic-bezier(.25,.8,.25,1); } .modal.open .backdrop{backdrop-filter:blur(14px)} /* Sheet — slides up + scales in */ .sheet{ position:relative; width:min(1320px,94vw); max-height:90vh; border-radius:22px;overflow:hidden; border:1px solid var(--edge); background:linear-gradient(180deg,rgba(255,255,255,.12),rgba(255,255,255,.04)); box-shadow:0 30px 140px rgba(0,0,0,.70),inset 0 1px 0 rgba(255,255,255,.14); display:grid; grid-template-columns:1fr minmax(280px,34%); transform:scale(.88) translateY(32px); opacity:0; transition:transform .44s cubic-bezier(.2,.8,.2,1), opacity .38s ease, box-shadow .4s; } .modal.open .sheet{ transform:scale(1) translateY(0); opacity:1; box-shadow: 0 30px 140px rgba(0,0,0,.70), 0 0 60px rgba(0,208,255,.10), 0 0 120px rgba(123,91,255,.07), inset 0 1px 0 rgba(255,255,255,.14); } /* Sheet image */ .sheet > img{width:100%;height:100%;object-fit:contain;background:#000;display:block} /* Sheet panel */ .panel{padding:16px;overflow-y:auto} .panel h3{margin:6px 0 6px;font-size:18px; background:var(--line);-webkit-background-clip:text;background-clip:text;color:transparent; } .muted{color:var(--muted);font-size:13px;font-weight:700} .priceBig{ margin-top:8px;font-weight:950;font-size:20px;color:var(--price); text-shadow:0 0 22px rgba(106,224,255,.22); } .btn{ display:inline-flex;align-items:center;gap:10px; padding:10px 14px;border-radius:12px;cursor:pointer;user-select:none; border:1px solid var(--edge); background:linear-gradient(180deg,rgba(255,255,255,.14),rgba(255,255,255,.05)); font-weight:900;color:var(--ink); transition:border-color .2s,box-shadow .2s,transform .15s; } .btn:hover{ border-color:rgba(106,224,255,.50); box-shadow:0 0 20px rgba(106,224,255,.22); transform:translateY(-1px); } .closeX{position:absolute;top:10px;right:10px} .rowBtns{display:flex;gap:8px;flex-wrap:wrap} /* Nav arrows */ .navArrow{ position:absolute;top:50%;transform:translateY(-50%); border-radius:12px;padding:10px 14px; border:1px solid var(--edge); background:linear-gradient(180deg,rgba(255,255,255,.14),rgba(255,255,255,.04)); cursor:pointer;user-select:none;z-index:60; font-weight:950;color:var(--ink); transition:border-color .2s,box-shadow .2s,transform .2s; } .navArrow:hover{ border-color:rgba(106,224,255,.55); box-shadow:0 0 22px rgba(106,224,255,.28); transform:translateY(calc(-50% - 2px)); } .navArrow.l{left:10px}.navArrow.r{right:10px} /* ───────────────────────────────────────────────────────── CARD ENTRANCE (from IntersectionObserver) ───────────────────────────────────────────────────────── */ .card-wrap.hidden-card{ opacity:0; transform:translateY(28px) scale(.96); } .card-wrap{ transition:opacity .5s cubic-bezier(.2,.8,.2,1), transform .5s cubic-bezier(.2,.8,.2,1); } /* ───────────────────────────────────────────────────────── FOOTER ───────────────────────────────────────────────────────── */ footer{margin:36px 0 64px;padding:0 16px} .foot{ max-width:1280px;margin:0 auto;padding:16px 0; border-top:1px solid var(--edge); color:var(--muted); display:flex;align-items:center;justify-content:space-between; flex-wrap:wrap;gap:10px;font-weight:800; } .foot a{text-decoration:underline;text-underline-offset:3px;} /* ───────────────────────────────────────────────────────── MEDIA QUERIES ───────────────────────────────────────────────────────── */ @media(max-width:820px){ .sheet{grid-template-columns:1fr} .navArrow{top:auto;bottom:10px;transform:none} .navArrow:hover{transform:translateY(-2px)} .navArrow.l{left:10px}.navArrow.r{right:10px} } @media(prefers-reduced-motion:reduce){ .card{animation:none!important} .card-wrap::before{animation:none!important} .modal,.sheet,.backdrop{transition:none!important} } /* SIMON tracker HUD + deeper 3D dynamics */ .simon-hud{ position:fixed;right:14px;bottom:14px;z-index:45; width:min(330px,calc(100vw - 28px)); border:1px solid rgba(106,224,255,.24); border-radius:18px; background:linear-gradient(180deg,rgba(7,11,22,.78),rgba(7,11,22,.44)); box-shadow:0 18px 80px rgba(0,0,0,.45),0 0 34px rgba(0,208,255,.13),inset 0 1px 0 rgba(255,255,255,.10); backdrop-filter:blur(16px); overflow:hidden; transform:translateZ(0); } .simon-hud::before{ content:'';position:absolute;inset:0;pointer-events:none; background:linear-gradient(90deg,transparent,rgba(106,224,255,.12),transparent); transform:translateX(-120%);animation:hudScan 6s ease-in-out infinite; } @keyframes hudScan{50%{transform:translateX(120%)}100%{transform:translateX(120%)}} .simon-hud-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border-bottom:1px solid rgba(255,255,255,.10)} .simon-hud-title{font-size:11px;font-weight:950;letter-spacing:.18em;text-transform:uppercase;color:#eaf2ff} .simon-hud-dot{width:9px;height:9px;border-radius:50%;background:#00ffd0;box-shadow:0 0 18px rgba(0,255,208,.9)} .simon-hud-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:rgba(255,255,255,.08)} .simon-stat{padding:9px 8px;background:rgba(7,11,22,.68)} .simon-stat b{display:block;font-size:15px;line-height:1.1;color:#fff} .simon-stat span{display:block;margin-top:2px;font-size:9px;letter-spacing:.10em;color:var(--muted);text-transform:uppercase;font-weight:900} .simon-hud-log{min-height:28px;padding:8px 12px;color:var(--muted);font-size:11px;font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .depth-field{position:fixed;inset:0;z-index:-1;pointer-events:none;perspective:900px;overflow:hidden} .depth-ring{position:absolute;left:50%;top:50%;width:45vmax;height:45vmax;border-radius:50%;border:1px solid rgba(106,224,255,.08);box-shadow:0 0 70px rgba(123,91,255,.10);transform-style:preserve-3d;animation:depthSpin 28s linear infinite} .depth-ring.r2{width:62vmax;height:62vmax;border-color:rgba(255,43,214,.07);animation-duration:42s;animation-direction:reverse} .depth-ring.r3{width:80vmax;height:80vmax;border-color:rgba(0,255,208,.055);animation-duration:55s} @keyframes depthSpin{to{transform:translate(-50%,-50%) rotateX(68deg) rotateZ(360deg)}} @media(max-width:720px){.simon-hud{left:10px;right:10px;bottom:10px;width:auto}.simon-hud-grid{grid-template-columns:repeat(2,1fr)}} </style> </head> <body> <!-- Background canvas layers --> <canvas id="cv-bg" aria-hidden="true"></canvas> <div class="depth-field" aria-hidden="true"> <i class="depth-ring r1"></i> <i class="depth-ring r2"></i> <i class="depth-ring r3"></i> </div> <aside class="simon-hud" aria-label="SIMON telemetry status"> <div class="simon-hud-head"><div class="simon-hud-title">SIMON TRACKER</div><div class="simon-hud-dot" id="simonLiveDot"></div></div> <div class="simon-hud-grid"> <div class="simon-stat"><b id="trkViews">1</b><span>view</span></div> <div class="simon-stat"><b id="trkClicks">0</b><span>clicks</span></div> <div class="simon-stat"><b id="trkArt">0</b><span>art opens</span></div> <div class="simon-stat"><b id="trkDepth">0%</b><span>scroll</span></div> </div> <div class="simon-hud-log" id="trkLog">Telemetry ready.</div> </aside> <canvas id="orbs" aria-hidden="true"></canvas> <canvas id="orbs2" aria-hidden="true"></canvas> <header class="top" role="banner"> <div class="wrap"> <div class="row"> <div class="brand" aria-label="Gallery brand"> <span class="orb" aria-hidden="true"></span> <span id="tBrand">TILO • ART GALLERY</span> </div> <nav class="topnav" aria-label="Primary"> <a class="glow" href="/home/">Home</a> <a class="glow" href="/arcade/">Arcade</a> <a class="glow active" href="/art/">Art</a> <a class="glow" href="/books/">Books</a> <a class="glow" href="/comics/">Comics</a> <a class="glow" href="/pressbox/">Pressbox</a> <a class="glow" href="/simon_ai/">Simon AI</a> <a class="glow" href="/web360/">Web360</a> <a class="glow" href="https://simon.gaylordsinclair.com" target="_blank" rel="noopener">Store</a> </nav> <div class="controls"> <button id="langBtn" class="chip" type="button" title="Language">EN / ES</button> <button id="toggleMotion" class="chip" type="button" title="Pause floating">⏸️ Motion</button> <button id="goFullscreen" class="chip" type="button" title="Fullscreen">⤢ Fullscreen</button> </div> </div> </div> </header> <section class="hero"> <h1 id="tHero">Tilo's Universe.</h1> <div class="heroRow"> <div class="hint" id="tHint"> <b>Tap</b> any panel to zoom. Use <b>◀ ▶</b> (or swipe) to navigate. Motion can be paused. </div> <div class="spacerGrow"></div> <div class="hint"> <span id="tPriceLabel">Price</span>: <b>$500.00</b> </div> </div> </section> <main id="gallery" class="grid" aria-live="polite"></main> <section id="modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="mTitle" aria-hidden="true"> <div class="backdrop" id="backdrop"></div> <button class="navArrow l" id="prev" aria-label="Previous">◀</button> <article class="sheet" role="document"> <img id="mImg" alt="Artwork preview" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="> <div class="panel"> <button id="close" class="btn closeX" aria-label="Close">✕</button> <h3 id="mTitle">Tilo — Piece #1</h3> <div class="pillTag" id="mTag" style="display:inline-flex;margin-bottom:4px">Tilo • #1</div> <div class="priceBig">$500.00</div> <p class="muted" id="tLightboxNote" style="margin-top:10px"> Originals & prints available on request. </p> <div class="rowBtns" style="margin-top:12px"> <a class="btn" id="email" href="#">✉️ Message Tilo</a> <a class="btn" id="download" href="#" download>⬇️ Download (preview)</a> </div> <p class="muted" id="tCC" style="margin-top:10px">CC: admin@gaylordsinclair.com</p> </div> </article> <button class="navArrow r" id="next" aria-label="Next">▶</button> </section> <noscript> <style>.nsg{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px;max-width:1280px;margin:10px auto;padding:0 16px}.nsg img{width:100%;border-radius:16px;border:1px solid rgba(255,255,255,.14)}</style> <div class="nsg"> <img src="/art/tilo/photos/1.jpg" alt="Tilo — Piece #1"> <img src="/art/tilo/photos/2.jpg" alt="Tilo — Piece #2"> <img src="/art/tilo/photos/3.jpg" alt="Tilo — Piece #3"> </div> </noscript> <footer> <div class="foot"> <div>© <span id="yr"></span> <span id="tFootLeft">Tilo Soto • Intergalactic Gallery</span></div> <div> <span id="tFootRight">Contact</span>: <a href="mailto:Soto1280@yahoo.com?subject=Tilo%20Art%20Inquiry">Soto1280@yahoo.com</a> • <a href="tel:+19039002203">(903) 900-2203</a> </div> </div> </footer> <script> (function(){ 'use strict'; /* ══════════════════════════════════════════════════════ i18n ══════════════════════════════════════════════════════ */ /* ══════════════════════════════════════════════════════ SIMON TRACKERS — GA4 + webhook + local HUD ══════════════════════════════════════════════════════ */ const CFG = window.SIMON_CONFIG || {}; const TRACK = { session: (crypto && crypto.randomUUID) ? crypto.randomUUID() : String(Date.now())+'-'+Math.random().toString(16).slice(2), views: Number(sessionStorage.getItem('simon_views')||0)+1, clicks: 0, art: 0, scroll: 0, last: 'Telemetry ready.' }; sessionStorage.setItem('simon_views', String(TRACK.views)); const trkEls = { views:document.getElementById('trkViews'), clicks:document.getElementById('trkClicks'), art:document.getElementById('trkArt'), depth:document.getElementById('trkDepth'), log:document.getElementById('trkLog'), dot:document.getElementById('simonLiveDot') }; function updateHud(msg){ if(msg) TRACK.last=msg; if(trkEls.views) trkEls.views.textContent=String(TRACK.views); if(trkEls.clicks) trkEls.clicks.textContent=String(TRACK.clicks); if(trkEls.art) trkEls.art.textContent=String(TRACK.art); if(trkEls.depth) trkEls.depth.textContent=Math.max(0,Math.min(100,TRACK.scroll))+'%'; if(trkEls.log) trkEls.log.textContent=TRACK.last; if(trkEls.dot){ trkEls.dot.style.transform='scale(1.45)'; setTimeout(()=>{trkEls.dot.style.transform='';},180); } } function simonPayload(eventName, data){ return Object.assign({ event:eventName, title:document.title, url:location.href, path:location.pathname, source:CFG.SOURCE || 'art-gallery', session:TRACK.session, at:new Date().toISOString(), screen:innerWidth+'x'+innerHeight, tz:Intl.DateTimeFormat().resolvedOptions().timeZone || 'Local', lang:document.documentElement.lang || 'en', referrer:document.referrer || '' }, data || {}); } function track(eventName, data){ const payload=simonPayload(eventName,data); try{ if(typeof gtag==='function') gtag('event', eventName, payload); }catch{} try{ const key=CFG.KEY ? ('&key='+encodeURIComponent(CFG.KEY)) : ''; const stream=(eventName==='page_view'||eventName==='visit') ? (CFG.VISIT_STREAM||'visits') : (CFG.PULSE_STREAM||'default'); fetch((CFG.WEBHOOK_BASE||'/connlink/test1/ui/webhook_inbox.php')+'?stream='+encodeURIComponent(stream)+key,{ method:'POST',headers:{'Content-Type':'application/json'},keepalive:true,body:JSON.stringify(payload) }).catch(()=>{}); }catch{} updateHud((data && (data.label || data.title || data.href)) ? eventName+' · '+(data.label || data.title || data.href) : eventName); } updateHud('Visit captured.'); track('visit',{label:'Art gallery visit'}); const I18N = { en: { brand:'TILO • ART GALLERY', hero:"Tilo's Universe.", hint:'<b>Tap</b> any panel to zoom. Use <b>◀ ▶</b> (or swipe) to navigate. Motion can be paused.', priceLabel:'Price', lightboxNote:'Originals & prints available on request.', msgBtn:'✉️ Message Tilo', dlBtn:'⬇️ Download (preview)', cc:'CC: admin@gaylordsinclair.com', footLeft:'Tilo Soto • Intergalactic Gallery', footRight:'Contact', inquireSubject:'Tilo Art Inquiry', inquireBody:(title,price)=>`Hi Tilo,%0D%0A%0D%0AI'm interested in ${title} (${price}).%0D%0AIs it available as an original or print?%0D%0A%0D%0AThank you,%0D%0A` }, es: { brand:'TILO • GALERÍA DE ARTE', hero:'El Universo de Tilo.', hint:'<b>Toca</b> cualquier panel para ampliar. Usa <b>◀ ▶</b> (o desliza) para navegar. Puedes pausar el movimiento.', priceLabel:'Precio', lightboxNote:'Originales e impresiones disponibles a solicitud.', msgBtn:'✉️ Mensaje a Tilo', dlBtn:'⬇️ Descargar (vista previa)', cc:'CC: admin@gaylordsinclair.com', footLeft:'Tilo Soto • Galería Intergaláctica', footRight:'Contacto', inquireSubject:'Consulta de Arte de Tilo', inquireBody:(title,price)=>`Hola Tilo,%0D%0A%0D%0AEstoy interesado(a) en ${title} (${price}).%0D%0A¿Está disponible como original o impresión?%0D%0A%0D%0AGracias,%0D%0A` } }; function getLang(){ const s=localStorage.getItem('gs_lang'); return (s==='es'||s==='en') ? s : 'en'; } function setLang(lang){ localStorage.setItem('gs_lang',lang); document.documentElement.setAttribute('lang',lang); document.documentElement.setAttribute('data-lang',lang); const t=I18N[lang]; document.getElementById('tBrand').textContent=t.brand; document.getElementById('tHero').textContent=t.hero; document.getElementById('tHint').innerHTML=t.hint; document.getElementById('tPriceLabel').textContent=t.priceLabel; document.getElementById('tLightboxNote').textContent=t.lightboxNote; document.getElementById('tCC').textContent=t.cc; document.getElementById('tFootLeft').textContent=t.footLeft; document.getElementById('tFootRight').textContent=t.footRight; document.getElementById('email').textContent=t.msgBtn; document.getElementById('download').textContent=t.dlBtn; } document.getElementById('langBtn').addEventListener('click',()=>{ setLang(getLang()==='en'?'es':'en'); }); /* ══════════════════════════════════════════════════════ CANVAS — 3 layers: stars+nebula / orbs / storms ══════════════════════════════════════════════════════ */ const cvBg = document.getElementById('cv-bg'); const cvs = document.getElementById('orbs'); const cvs2 = document.getElementById('orbs2'); const ctxBg = cvBg.getContext('2d'); const ctx = cvs.getContext('2d'); const ctx2 = cvs2.getContext('2d'); let W=0,H=0,DPR=Math.min(2,window.devicePixelRatio||1); const TAU=Math.PI*2; const rand=(a,b)=>a+Math.random()*(b-a); const hsla=(h,s,l,a)=>`hsla(${h} ${s}% ${l}% / ${a})`; function resize(){ W=innerWidth; H=innerHeight; [cvBg,cvs,cvs2].forEach(c=>{ c.width=Math.floor(W*DPR); c.height=Math.floor(H*DPR); c.style.width=W+'px'; c.style.height=H+'px'; }); [ctxBg,ctx,ctx2].forEach(c=>c.setTransform(DPR,0,0,DPR,0,0)); } addEventListener('resize',resize,{passive:true}); resize(); // — Stars const stars=Array.from({length:280},()=>({ x:rand(0,W), y:rand(0,H), r:rand(.25,1.5), a:rand(.08,.38), flicker:rand(.4,1.2) })); // — Nebula drift state let nbDrift={x:0,y:0,tx:0,ty:0}; addEventListener('mousemove',e=>{ nbDrift.tx=(e.clientX-W*.5)*0.022; nbDrift.ty=(e.clientY-H*.5)*0.016; },{passive:true}); function drawBg(t){ ctxBg.clearRect(0,0,W,H); // stars for(const s of stars){ const a=s.a*(Math.sin(t*.0009*s.flicker+s.x*.008)*.3+.72); ctxBg.globalAlpha=a; ctxBg.beginPath(); ctxBg.fillStyle='#eaf2ff'; ctxBg.arc(s.x,s.y,s.r,0,TAU); ctxBg.fill(); } ctxBg.globalAlpha=1; // nebula nbDrift.x+=(nbDrift.tx-nbDrift.x)*.025; nbDrift.y+=(nbDrift.ty-nbDrift.y)*.025; const dx=Math.sin(t*.00015)*70+nbDrift.x; const dy=Math.cos(t*.00011)*45+nbDrift.y; const g1=ctxBg.createRadialGradient(W*.7+dx,H*.05+dy,30,W*.7+dx,H*.05+dy,W*.55); g1.addColorStop(0,'rgba(106,224,255,.12)'); g1.addColorStop(1,'transparent'); ctxBg.fillStyle=g1; ctxBg.fillRect(0,0,W,H); const g2=ctxBg.createRadialGradient(W*.12-dx*.5,H*.35-dy*.4,20,W*.12-dx*.5,H*.35-dy*.4,W*.45); g2.addColorStop(0,'rgba(123,91,255,.14)'); g2.addColorStop(1,'transparent'); ctxBg.fillStyle=g2; ctxBg.fillRect(0,0,W,H); const g3=ctxBg.createRadialGradient(W*.5+dx*.3,H*1.1,10,W*.5+dx*.3,H*1.1,W*.6); g3.addColorStop(0,'rgba(255,43,214,.08)'); g3.addColorStop(1,'transparent'); ctxBg.fillStyle=g3; ctxBg.fillRect(0,0,W,H); } // — Orbs const layerA=[], layerB=[]; const MAX_A=90, MAX_B=140; function makeOrb(px=1){ const r=rand(1.6,px>1?3.2:5.2); const hue=rand(180,320),sat=rand(70,100),light=rand(55,75),a=rand(.18,.45); return{x:rand(-W*.1,W*1.1),y:rand(-H*.1,H*1.1), vx:rand(-.25,.35)/px, vy:rand(-.2,.3)/px, r, glow:r*12, c:hsla(hue,sat,light,a), trail:[]}; } for(let i=0;i<MAX_A;i++) layerA.push(makeOrb(1)); for(let i=0;i<MAX_B;i++) layerB.push(makeOrb(1.6)); function drawLayer(context,list,hazeAt=[.8,.15]){ context.clearRect(0,0,W,H); const g=context.createRadialGradient(W*hazeAt[0],H*hazeAt[1],0,W*hazeAt[0],H*hazeAt[1],Math.max(W,H)*.9); g.addColorStop(0,'rgba(106,224,255,.08)'); g.addColorStop(1,'rgba(0,0,0,0)'); context.fillStyle=g; context.fillRect(0,0,W,H); for(const o of list){ o.trail.push({x:o.x,y:o.y,r:o.r}); if(o.trail.length>12) o.trail.shift(); for(let i=0;i<o.trail.length;i++){ const tr=o.trail[i],f=i/o.trail.length; context.beginPath(); context.fillStyle=o.c; context.globalAlpha=.07*f; context.arc(tr.x,tr.y,tr.r+f*o.glow*.08,0,TAU); context.fill(); } context.globalAlpha=1; context.beginPath(); context.shadowColor=o.c; context.shadowBlur=o.glow; context.fillStyle=o.c; context.arc(o.x,o.y,o.r,0,TAU); context.fill(); context.shadowBlur=0; o.x+=o.vx; o.y+=o.vy; const m=40; if(o.x<-m) o.x=W+m; if(o.x>W+m) o.x=-m; if(o.y<-m) o.y=H+m; if(o.y>H+m) o.y=-m; } } // — Storms const storms=[]; function spawnStorm(x,y){ x=x??rand(W*.2,W*.8); y=y??rand(H*.2,H*.7); const count=Math.floor(rand(90,160)), hueSeed=rand(180,320); const kids=Array.from({length:count},()=>({ x,y,vx:rand(-2.4,2.4),vy:rand(-2.0,2.0), r:rand(.8,2.2),life:rand(600,1200), c:hsla(hueSeed+rand(-10,10),rand(70,100),rand(55,75),rand(.25,.55)) })); storms.push({kids}); } setInterval(()=>spawnStorm(),5200); addEventListener('click',e=>{ // only spawn if not clicking a card or button if(!e.target.closest('.card-wrap,.btn,.chip,button,a')) spawnStorm(e.clientX,e.clientY); },{passive:true}); function drawStorms(){ ctx2.save(); for(let s=storms.length-1;s>=0;s--){ const S=storms[s]; for(const k of S.kids){ ctx2.beginPath();ctx2.fillStyle=k.c;ctx2.globalAlpha=.9; ctx2.shadowColor=k.c;ctx2.shadowBlur=12; ctx2.arc(k.x,k.y,k.r,0,TAU);ctx2.fill();ctx2.shadowBlur=0; k.x+=k.vx;k.y+=k.vy;k.vx*=.985;k.vy*=.985;k.life-=16; if(k.life<300) k.r*=.985; } S.kids=S.kids.filter(k=>k.life>60&&k.r>.2); if(!S.kids.length) storms.splice(s,1); } ctx2.restore(); } // Mouse-attract orbs let mx=null,my=null; addEventListener('mousemove',e=>{mx=e.clientX;my=e.clientY;},{passive:true}); setInterval(()=>{ if(mx==null) return; for(const o of layerA){ const dx=mx-o.x,dy=my-o.y,d2=dx*dx+dy*dy; if(d2<42000){o.vx+=dx/52000;o.vy+=dy/52000;} } },55); /* ══════════════════════════════════════════════════════ GALLERY — build cards ══════════════════════════════════════════════════════ */ const gallery=document.getElementById('gallery'); const PRICE='$500.00'; const TOTAL=62; const items=Array.from({length:TOTAL},(_,i)=>{ const n=i+1; return{id:n, src:`/art/tilo/photos/${n}.jpg`, title:`Tilo — Piece #${n}`, tag:`Tilo • #${n}`, price:PRICE}; }); const ratios=['1/1','4/5','3/4','16/12','4/3']; items.forEach((art,idx)=>{ // outer wrapper (holds spinning border) const wrap=document.createElement('div'); wrap.className='card-wrap hidden-card'; wrap.style.setProperty('--delay',(Math.random()*8).toFixed(2)+'s'); wrap.style.setProperty('--amp',(Math.random()*2+.7).toFixed(2)); wrap.style.setProperty('--tilt',(Math.random()*4+1).toFixed(1)+'deg'); // inner card const card=document.createElement('figure'); card.className='card'; card.style.setProperty('--ratio',ratios[idx%ratios.length]); card.innerHTML=` <img loading="lazy" src="${art.src}" alt="${art.title}"> <figcaption class="meta"> <div class="leftMeta"> <div class="titleMini">${art.title}</div> <div class="mutedMini">${art.tag}</div> <div class="pillTag">View in Lightbox</div> </div> <div class="price">${art.price}</div> </figcaption>`; wrap.appendChild(card); wrap.addEventListener('click',()=>{ TRACK.clicks++; TRACK.art++; track('art_open',{id:art.id,title:art.title,price:art.price}); openModal(art.id); }); gallery.appendChild(wrap); // 3D tilt on mouse addTilt(wrap, card); }); /* — Per-card 3D magnetic tilt ─────────────────────── */ function addTilt(wrap, card){ const MAX_TILT=22; // degrees let rafId=null, isHovering=false; let targetRX=0, targetRY=0, currentRX=0, currentRY=0; function animate(){ currentRX+=(targetRX-currentRX)*.12; currentRY+=(targetRY-currentRY)*.12; // pause float anim while tilting const dist=Math.abs(currentRX)+Math.abs(currentRY); if(isHovering || dist>.05){ card.style.animationPlayState='paused'; wrap.style.transform= `perspective(1200px) rotateX(${currentRX}deg) rotateY(${currentRY}deg) translateY(-${12+dist*.1}px) scale(1.03)`; // move shimmer centre const cx=50+currentRY/.14*.5; const cy=50-currentRX/.14*.5; card.style.setProperty('--mx',cx+'%'); card.style.setProperty('--my',cy+'%'); rafId=requestAnimationFrame(animate); } else { wrap.style.transform=''; if(motionOn) card.style.animationPlayState='running'; rafId=null; } } wrap.addEventListener('mousemove',e=>{ const rect=wrap.getBoundingClientRect(); const nx=(e.clientX-rect.left)/rect.width -.5; // -0.5..0.5 const ny=(e.clientY-rect.top) /rect.height-.5; targetRY= nx*MAX_TILT*2; targetRX=-ny*MAX_TILT*2; if(!rafId) rafId=requestAnimationFrame(animate); }); wrap.addEventListener('mouseenter',()=>{ isHovering=true; if(!rafId) rafId=requestAnimationFrame(animate); }); wrap.addEventListener('mouseleave',()=>{ isHovering=false; targetRX=0; targetRY=0; if(!rafId) rafId=requestAnimationFrame(animate); }); } /* ══════════════════════════════════════════════════════ MOTION TOGGLE ══════════════════════════════════════════════════════ */ const toggleBtn=document.getElementById('toggleMotion'); let motionOn=true; function setMotion(on){ motionOn=on; document.querySelectorAll('.card').forEach(c=>{c.style.animationPlayState=on?'running':'paused';}); document.querySelectorAll('.card-wrap::before').forEach(()=>{}); // CSS handles it toggleBtn.textContent=on?'⏸️ Motion':'▶️ Motion'; } toggleBtn.addEventListener('click',()=>setMotion(!motionOn)); toggleBtn.addEventListener('click',()=>track('motion_toggle',{enabled:motionOn,label:motionOn?'Motion on':'Motion paused'})); /* ══════════════════════════════════════════════════════ FULLSCREEN ══════════════════════════════════════════════════════ */ document.getElementById('goFullscreen').addEventListener('click',()=>{ TRACK.clicks++; track('fullscreen_toggle',{label:document.fullscreenElement?'Exit fullscreen':'Enter fullscreen'}); if(!document.fullscreenElement) document.documentElement.requestFullscreen?.(); else document.exitFullscreen?.(); }); /* ══════════════════════════════════════════════════════ LIGHTBOX ══════════════════════════════════════════════════════ */ const modal = document.getElementById('modal'); const backdrop = document.getElementById('backdrop'); const closeBtn = document.getElementById('close'); const prevBtn = document.getElementById('prev'); const nextBtn = document.getElementById('next'); const mImg = document.getElementById('mImg'); const mTitle = document.getElementById('mTitle'); const mTag = document.getElementById('mTag'); const emailBtn = document.getElementById('email'); const dlBtn = document.getElementById('download'); let current=1; function show(id){ const it=items.find(x=>x.id===id); if(!it) return; current=id; // image crossfade mImg.style.opacity='0'; mImg.src=it.src; mImg.onload=()=>{ mImg.style.transition='opacity .3s'; mImg.style.opacity='1'; }; mTitle.textContent=it.title; mTag.textContent=it.tag; const lang=getLang(), t=I18N[lang]; const subj=`${t.inquireSubject} — ${it.title}`; const body=t.inquireBody(it.title,it.price)+`%0D%0A(Cc: admin@gaylordsinclair.com)%0D%0A`; emailBtn.href=`mailto:Soto1280@yahoo.com?subject=${encodeURIComponent(subj)}&body=${body}&bcc=${encodeURIComponent('admin@gaylordsinclair.com')}`; emailBtn.textContent=t.msgBtn; dlBtn.textContent=t.dlBtn; dlBtn.href=it.src; } function openModal(id){ show(id); modal.classList.add('open'); modal.setAttribute('aria-hidden','false'); document.body.style.overflow='hidden'; } emailBtn.addEventListener('click',()=>{ TRACK.clicks++; track('art_email_click',{id:current,title:mTitle.textContent}); }); dlBtn.addEventListener('click',()=>{ TRACK.clicks++; track('art_download_click',{id:current,title:mTitle.textContent,href:dlBtn.href}); }); function closeModal(){ track('lightbox_close',{id:current}); modal.classList.remove('open'); modal.setAttribute('aria-hidden','true'); document.body.style.overflow=''; } backdrop.addEventListener('click',closeModal); closeBtn.addEventListener('click',closeModal); prevBtn.addEventListener('click',()=>{ TRACK.clicks++; track('lightbox_prev',{from:current}); show(((current+TOTAL-2)%TOTAL)+1); }); nextBtn.addEventListener('click',()=>{ TRACK.clicks++; track('lightbox_next',{from:current}); show((current%TOTAL)+1); }); addEventListener('keydown',e=>{ if(!modal.classList.contains('open')) return; if(e.key==='Escape') closeModal(); if(e.key==='ArrowLeft') prevBtn.click(); if(e.key==='ArrowRight') nextBtn.click(); }); // Swipe (function enableSwipe(el){ let sx=0,sy=0,dx=0,dy=0; el.addEventListener('touchstart',e=>{const t=e.touches[0];sx=t.clientX;sy=t.clientY;},{passive:true}); el.addEventListener('touchmove', e=>{const t=e.touches[0];dx=t.clientX-sx;dy=t.clientY-sy;},{passive:true}); el.addEventListener('touchend', ()=>{ if(Math.abs(dx)>60&&Math.abs(dy)<80){ (dx>0?prevBtn:nextBtn).click(); } sx=sy=dx=dy=0; },{passive:true}); })(modal); /* ══════════════════════════════════════════════════════ CARD ENTRANCE (IntersectionObserver) ══════════════════════════════════════════════════════ */ const io=new IntersectionObserver((entries)=>{ for(const e of entries){ if(e.isIntersecting){ // stagger based on index const wraps=Array.from(document.querySelectorAll('.card-wrap')); const i=wraps.indexOf(e.target); setTimeout(()=>{ e.target.classList.remove('hidden-card'); }, (i%8)*55); io.unobserve(e.target); } } },{rootMargin:'60px'}); document.querySelectorAll('.card-wrap').forEach(c=>io.observe(c)); /* ══════════════════════════════════════════════════════ MASTER ANIMATION LOOP ══════════════════════════════════════════════════════ */ (function loop(t){ drawBg(t); drawLayer(ctx, layerA, [.8,.15]); drawLayer(ctx2, layerB, [.2,.25]); drawStorms(); requestAnimationFrame(loop); })(); /* Global interaction + scroll depth tracking */ document.addEventListener('click',e=>{ const hit=e.target.closest('a,button,.chip,.glow'); if(!hit || hit.closest('.card-wrap')) return; TRACK.clicks++; track('ui_click',{label:(hit.textContent||hit.getAttribute('aria-label')||hit.href||'control').trim().slice(0,90),href:hit.href||''}); },{capture:true,passive:true}); let lastDepth=0; addEventListener('scroll',()=>{ const max=Math.max(1,document.documentElement.scrollHeight-innerHeight); const pct=Math.round((scrollY/max)*100); TRACK.scroll=Math.max(TRACK.scroll,pct); updateHud(); if(CFG.TRACK_SCROLL && TRACK.scroll>=lastDepth+25){ lastDepth=TRACK.scroll; track('scroll_depth',{depth:TRACK.scroll,label:'Scroll '+TRACK.scroll+'%'}); } },{passive:true}); addEventListener('visibilitychange',()=>{ if(document.visibilityState==='hidden') track('page_exit',{scroll:TRACK.scroll,clicks:TRACK.clicks,art:TRACK.art}); }); /* ══════════════════════════════════════════════════════ MISC ══════════════════════════════════════════════════════ */ document.getElementById('yr').textContent=new Date().getFullYear(); if(matchMedia('(prefers-reduced-motion:reduce)').matches) setMotion(false); // Active nav (function(){ const here=(location.pathname.replace(/\/+$/,'').toLowerCase()||'/'); document.querySelectorAll('.topnav .glow').forEach(a=>{ const href=(a.getAttribute('href')||'').replace(/^https?:\/\/[^/]+/,''); const norm=(href.replace(/\/+$/,'').toLowerCase()||'/'); if(norm&&(here===norm||(norm!=='/'&&here.startsWith(norm)))) a.classList.add('active'); }); })(); setLang(getLang()); })(); </script> </body> </html>
Save file
Quick jump
open a path
Open