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,279
Folders
408
Scanned Size
3.84 GB
PHP Files
891
Editable Text Files
12,540
File viewer
guarded to /htdocs
/auth/login.php
<?php declare(strict_types=1); /** * /login.php * Guardian Access — SIMON Intelligence * * Updated for: * - larger full-screen visualizer * - auto-hiding login panel * - expandable header drop panel * - reduced side clutter * - extended redirect allowlist * - cleaner holographic Guardian login */ $https = ( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (isset($_SERVER['SERVER_PORT']) && (int)$_SERVER['SERVER_PORT'] === 443) ); if (session_status() !== PHP_SESSION_ACTIVE) { session_set_cookie_params([ 'lifetime' => 0, 'path' => '/', 'domain' => '', 'secure' => $https, 'httponly' => true, 'samesite' => 'Lax', ]); session_name('GUARDIANSESSID'); session_start(); } if (!isset($_SESSION['csrf_token']) || !is_string($_SESSION['csrf_token']) || $_SESSION['csrf_token'] === '') { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } $allowedRedirects = [ '/guardian/', '/guardian/index.php', '/guardian/dashboard.php', '/guardian/MainjView.php', '/_guardian/', '/_guardian/index.php', '/_guardian/dashboard.php', '/_guardian/events_viewer.php', '/_guardian/tracker.php', '/simon/', '/simon/index.php', '/simon/analytics.php', '/simon/simon_console.php', '/simon/console.php', '/simon/site_audit_daily.php', '/simon/site_map.php', '/simon/site_map_summary.php', '/simon/health.php', '/simon/security.php', '/simon/providers.php', '/simon/reports.php', '/app/', '/app/index.php', '/app/dashboard.php', '/app/profile.php', '/app/settings.php', '/app/admin.php', '/connlink/', '/connlink/index.php', '/connlink/links.php', '/connlink/templates.php', '/connlink/health.php', '/connlink/logs.php', '/connlink/routes.php', '/connlink/security.php', '/admin/', '/admin/index.php', '/admin/dashboard.php', '/admin/simon_master_dashboard.php', '/tools/', '/tools/index.php', '/tools/security.php', '/tools/traffic.php', '/tools/summary.php', '/tools/debug.php', '/tools/console.php', '/tools/diagnostics.php', // SiteCheck-style tools visible in your screenshot '/tools/SiteCheck/', '/tools/SiteCheck/index.php', '/tools/SiteCheck/_debug_gate.php', '/tools/SiteCheck/ip.php', '/tools/SiteCheck/simon_console.php', '/tools/SiteCheck/site_audit_daily.php', '/tools/SiteCheck/site_map.php', '/tools/SiteCheck/site_map_summary.php', '/tools/SiteCheck/site_review_scanner.php', '/tools/SiteCheck/tools_htaccess_watch.php', '/tools/SiteCheck/website_checker.php', ]; $redirect = '/_guardian/dashboard.php'; if (!empty($_GET['redirect'])) { $candidate = (string) $_GET['redirect']; if (in_array($candidate, $allowedRedirects, true)) { $redirect = $candidate; } } $_SESSION['post_auth_redirect'] = $redirect; $errorMap = [ 'oauth_failed' => 'Authentication could not be completed. Please try again.', 'session_expired' => 'Your session expired. Please sign in again.', 'provider_unavailable' => 'The selected sign-in provider is temporarily unavailable.', 'email_required' => 'Please enter a valid email address.', 'magic_link_sent' => 'A secure sign-in link was sent to your email.', 'passkey_failed' => 'Passkey verification failed. Try again or use another method.', 'access_denied' => 'Access denied. Your account is not approved for this environment.', 'invalid_code' => 'The access code was invalid or expired.', ]; $status = ''; $statusType = 'info'; if (!empty($_GET['error'])) { $key = (string) $_GET['error']; $status = $errorMap[$key] ?? 'An unexpected authentication error occurred.'; $statusType = 'error'; } elseif (!empty($_GET['status'])) { $key = (string) $_GET['status']; $status = $errorMap[$key] ?? 'Status updated.'; $statusType = 'success'; } function e(string $value): string { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); } $quickTargets = [ ['label' => 'Guardian', 'url' => '/_guardian/dashboard.php'], ['label' => 'Events', 'url' => '/_guardian/events_viewer.php'], ['label' => 'Tracker', 'url' => '/_guardian/tracker.php'], ['label' => 'SIMON Console', 'url' => '/simon/simon_console.php'], ['label' => 'Traffic', 'url' => '/simon/console.php'], ['label' => 'Site Audit', 'url' => '/tools/SiteCheck/site_audit_daily.php'], ['label' => 'Site Map', 'url' => '/tools/SiteCheck/site_map.php'], ['label' => 'Summary', 'url' => '/tools/SiteCheck/site_map_summary.php'], ['label' => 'Review Scan', 'url' => '/tools/SiteCheck/site_review_scanner.php'], ['label' => 'Htaccess Watch', 'url' => '/tools/SiteCheck/tools_htaccess_watch.php'], ['label' => 'Website Checker', 'url' => '/tools/SiteCheck/website_checker.php'], ['label' => 'Connlink', 'url' => '/connlink/index.php'], ['label' => 'Admin', 'url' => '/admin/dashboard.php'], ]; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Guardian Access — SIMON Intelligence</title> <meta name="theme-color" content="#050816"> <meta name="description" content="Guardian secure login for SIMON Intelligence. Sign in with trusted identity providers, passkeys, or secure email link."> <style> :root{ --bg:#020611; --bg2:#071224; --bg3:#0a1731; --bg4:#0d1d3d; --cyan:#97f5ff; --cyan2:#43ddff; --cyan3:#18bfff; --blue:#5f87ff; --violet:#b184ff; --green:#7fffd4; --amber:#ffcc74; --red:#ff8a9a; --white:#eefcff; --text:#ebfbff; --muted:rgba(214,239,255,.72); --muted2:rgba(214,239,255,.52); --edge:rgba(146,246,255,.16); --edgeStrong:rgba(146,246,255,.32); --shadow:rgba(0,0,0,.55); --header-h:78px; --panelW:min(520px, calc(100vw - 36px)); } *{box-sizing:border-box;margin:0;padding:0} html,body{ width:100%; min-height:100%; overflow:hidden; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Helvetica,Arial,sans-serif; color:var(--text); background: radial-gradient(circle at 50% 50%, rgba(80,150,255,.10), transparent 18%), radial-gradient(circle at 50% 56%, rgba(0,234,255,.12), transparent 32%), radial-gradient(circle at 18% 18%, rgba(90,120,255,.08), transparent 24%), linear-gradient(180deg, #06101c 0%, #040811 48%, #010306 100%); } body::before{ content:""; position:fixed; inset:0; pointer-events:none; background: linear-gradient(rgba(255,255,255,.018) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.018) 1px, transparent 1px); background-size:58px 58px; opacity:.14; mix-blend-mode:screen; } body::after{ content:""; position:fixed; inset:0; pointer-events:none; background: repeating-linear-gradient( to bottom, rgba(255,255,255,.016) 0px, rgba(255,255,255,.016) 1px, transparent 2px, transparent 5px ); opacity:.09; mix-blend-mode:screen; } a{color:inherit;text-decoration:none} button,input{font:inherit} #matrixCanvas,#orbCanvas,#fxCanvas{ position:fixed; inset:0; width:100%; height:100%; display:block; pointer-events:none; } #matrixCanvas{z-index:1;opacity:.28} #orbCanvas{z-index:2} #fxCanvas{z-index:3} .vignette{ position:fixed; inset:0; z-index:4; pointer-events:none; background: radial-gradient(circle at 50% 50%, transparent 34%, rgba(0,0,0,.10) 60%, rgba(0,0,0,.46) 100%); } .app-shell{ position:relative; z-index:10; width:100%; min-height:100vh; } /* HEADER */ .topbar{ position:fixed; top:0; left:0; right:0; height:var(--header-h); z-index:30; display:flex; align-items:center; justify-content:space-between; gap:20px; padding:14px 22px; background:linear-gradient(180deg, rgba(5,12,23,.84), rgba(5,12,23,.42)); border-bottom:1px solid rgba(146,246,255,.08); backdrop-filter:blur(18px); box-shadow:0 8px 24px rgba(0,0,0,.18); } .brand-wrap{ display:flex; align-items:center; gap:14px; min-width:0; } .brand-mark{ width:44px; height:44px; border-radius:14px; display:grid; place-items:center; border:1px solid rgba(146,246,255,.22); background: radial-gradient(circle at 50% 45%, rgba(146,246,255,.18), rgba(0,0,0,0) 62%), linear-gradient(180deg, rgba(11,24,47,.94), rgba(7,12,24,.95)); box-shadow: inset 0 0 16px rgba(146,246,255,.05), 0 0 24px rgba(56,222,255,.08); } .brand-title{ font-size:27px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; color:#f0fbff; line-height:1; } .brand-sub{ margin-top:6px; font-size:11px; letter-spacing:.20em; text-transform:uppercase; color:var(--muted); } .topbar-actions{ display:flex; align-items:center; gap:10px; } .metric-chip,.drop-trigger{ padding:10px 14px; border-radius:999px; border:1px solid rgba(146,246,255,.14); background:rgba(146,246,255,.04); color:var(--muted); font-size:12px; letter-spacing:.08em; text-transform:uppercase; transition:transform .22s ease, box-shadow .22s ease, border-color .22s ease, background .22s ease; } .metric-chip strong{color:#f0fbff;margin-left:6px;letter-spacing:.02em} .drop-trigger{ cursor:pointer; color:#eefcff; font-weight:700; } .metric-chip:hover,.drop-trigger:hover{ transform:translateY(-2px); border-color:rgba(146,246,255,.28); box-shadow:0 0 22px rgba(56,222,255,.12); background:rgba(146,246,255,.07); } /* HEADER DROP PANEL */ .header-drop{ position:fixed; top:calc(var(--header-h) - 2px); left:18px; right:18px; z-index:29; border:1px solid rgba(146,246,255,.16); border-top:none; border-radius:0 0 26px 26px; background:linear-gradient(180deg, rgba(8,18,35,.94), rgba(6,12,24,.92)); box-shadow:0 24px 70px rgba(0,0,0,.38), 0 0 30px rgba(56,222,255,.08); backdrop-filter:blur(22px); overflow:hidden; max-height:0; opacity:0; transform:translateY(-8px); pointer-events:none; transition:max-height .34s ease, opacity .24s ease, transform .24s ease; } .header-drop.open{ max-height:420px; opacity:1; transform:translateY(0); pointer-events:auto; } .header-drop-inner{ padding:18px 18px 20px; } .drop-title{ font-size:13px; letter-spacing:.18em; text-transform:uppercase; color:var(--cyan); margin-bottom:14px; font-weight:800; } .drop-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:10px; } .drop-link{ min-height:52px; padding:12px 14px; border-radius:16px; border:1px solid rgba(146,246,255,.12); background:rgba(146,246,255,.04); color:#ecfbff; display:flex; align-items:center; justify-content:space-between; gap:10px; transition:transform .18s ease, box-shadow .18s ease, border-color .18s ease, background .18s ease; } .drop-link:hover{ transform:translateY(-2px); border-color:rgba(146,246,255,.30); background:rgba(146,246,255,.08); box-shadow:0 14px 24px rgba(0,0,0,.22), 0 0 20px rgba(56,222,255,.10); } .drop-link small{ color:var(--muted2); display:block; font-size:11px; margin-top:2px; } .drop-link-icon{ width:34px; height:34px; border-radius:12px; display:grid; place-items:center; border:1px solid rgba(146,246,255,.14); background:linear-gradient(180deg, rgba(18,32,56,.98), rgba(8,14,28,.96)); flex:0 0 34px; } /* FULLSCREEN VISUALIZER */ .hero{ position:fixed; inset:var(--header-h) 0 0 0; z-index:11; } .full-visual{ position:absolute; inset:0; perspective:2200px; overflow:hidden; } .visual-center{ position:absolute; inset:0; } .ring{ position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); border-radius:50%; border:1px solid rgba(146,246,255,.11); box-shadow: 0 0 30px rgba(56,222,255,.09), 0 0 90px rgba(56,222,255,.04), inset 0 0 40px rgba(56,222,255,.05); } .ring.r1{ width:440px; height:440px; animation:spinSlow 22s linear infinite; } .ring.r2{ width:720px; height:720px; animation:spinReverse 34s linear infinite; opacity:.40; } .ring.r3{ width:1040px; height:1040px; animation:spinSlow 56s linear infinite; opacity:.14; } .arc{ position:absolute; left:50%; top:50%; width:1040px; height:1040px; transform:translate(-50%,-50%); border-radius:50%; border-top:1px solid rgba(146,246,255,.12); border-bottom:1px solid rgba(146,246,255,.06); } .arc.a1{ transform:translate(-50%,-50%) rotateX(74deg); opacity:.28; } .arc.a2{ transform:translate(-50%,-50%) rotateY(74deg); opacity:.18; } .arc.a3{ transform:translate(-50%,-50%) rotateZ(28deg); opacity:.14; } .core-shadow{ position:absolute; left:50%; bottom:7%; transform:translateX(-50%); width:980px; height:260px; border-radius:50%; background:radial-gradient(circle, rgba(37,187,255,.32), rgba(27,74,140,.14), transparent 72%); filter:blur(42px); opacity:.9; } .status-pod{ position:absolute; left:40px; bottom:36px; min-width:300px; padding:18px 20px; border-radius:22px; background:linear-gradient(180deg, rgba(10,20,38,.78), rgba(6,12,24,.56)); border:1px solid rgba(146,246,255,.18); box-shadow:0 12px 40px rgba(0,0,0,.38), inset 0 0 24px rgba(146,246,255,.05), 0 0 30px rgba(56,222,255,.05); backdrop-filter:blur(20px); } .status-pod .label{ font-size:11px; letter-spacing:.22em; text-transform:uppercase; color:var(--muted); margin-bottom:10px; } .status-pod .value{ display:flex; align-items:center; gap:10px; font-size:20px; font-weight:700; color:var(--cyan); letter-spacing:.06em; } .status-pod .mini{ margin-top:8px; font-size:12px; color:var(--muted); } .pulse-dot{ width:10px; height:10px; border-radius:50%; background:var(--green); box-shadow:0 0 16px var(--green), 0 0 30px rgba(127,255,212,.4); animation:pulseDot 1.6s infinite ease-in-out; } /* SINGLE LOGIN PANEL */ .login-shell{ position:fixed; right:28px; top:52%; transform:translateY(-50%); z-index:21; width:var(--panelW); transition:transform .28s ease, opacity .28s ease, right .28s ease; } .login-shell.hidden{ transform:translateY(-50%) translateX(calc(100% - 72px)); opacity:.92; } .login-handle{ position:absolute; top:50%; left:-22px; transform:translateY(-50%); width:44px; height:120px; border-radius:18px 0 0 18px; border:1px solid rgba(146,246,255,.18); border-right:none; background:linear-gradient(180deg, rgba(8,18,35,.90), rgba(6,12,24,.92)); box-shadow:0 12px 30px rgba(0,0,0,.32), 0 0 20px rgba(56,222,255,.08); display:flex; align-items:center; justify-content:center; cursor:pointer; color:var(--cyan); letter-spacing:.14em; text-transform:uppercase; font-size:11px; writing-mode:vertical-rl; text-orientation:mixed; user-select:none; } .login-panel{ --rx:0deg; --ry:0deg; --mx:50%; --my:50%; position:relative; width:100%; padding:28px 28px 24px; border-radius:30px; background: radial-gradient(circle at var(--mx) var(--my), rgba(151,245,255,.16), transparent 22%), linear-gradient(180deg, rgba(10,20,38,.88), rgba(7,12,24,.80)); border:1px solid var(--edge); box-shadow: 0 30px 80px rgba(0,0,0,.52), 0 0 60px rgba(56,222,255,.08), inset 0 0 30px rgba(146,246,255,.05); backdrop-filter:blur(28px); overflow:hidden; transform-style:preserve-3d; transform: rotateX(var(--rx)) rotateY(var(--ry)) translateZ(0); transition: transform .18s ease-out, box-shadow .18s ease-out, border-color .18s ease-out, background .18s ease-out; } .login-panel:hover{ border-color:rgba(146,246,255,.34); box-shadow: 0 42px 100px rgba(0,0,0,.58), 0 0 85px rgba(56,222,255,.16), inset 0 0 44px rgba(146,246,255,.10); } .login-panel::before{ content:""; position:absolute; inset:0; background:linear-gradient(135deg, rgba(146,246,255,.10), transparent 25%, transparent 70%, rgba(95,135,255,.08)); pointer-events:none; } .login-panel::after{ content:""; position:absolute; left:-20%; top:-40%; width:140%; height:60%; background:linear-gradient(90deg, transparent, rgba(255,255,255,.10), transparent); transform:rotate(12deg); animation:panelSweep 7s linear infinite; pointer-events:none; } .login-glow{ position:absolute; inset:-20%; background: radial-gradient(circle at var(--mx) var(--my), rgba(80,220,255,.12), transparent 18%), radial-gradient(circle at 50% 110%, rgba(95,135,255,.12), transparent 28%); filter:blur(30px); pointer-events:none; z-index:0; } .panel-top,.description,.auth-stack,.email-form,.secondary-links,.meta-grid,.footnote,.banner,.guardian-kicker{ position:relative; z-index:2; } .panel-top{ display:flex; align-items:flex-start; justify-content:space-between; gap:14px; } .guardian-kicker{ display:inline-flex; align-items:center; gap:10px; padding:9px 14px; border-radius:999px; border:1px solid rgba(146,246,255,.18); background:rgba(146,246,255,.05); color:var(--cyan); font-size:11px; font-weight:700; letter-spacing:.16em; text-transform:uppercase; } .guardian-kicker .mini-dot{ width:8px; height:8px; border-radius:50%; background:var(--cyan); box-shadow:0 0 12px var(--cyan); } .panel-badge{ width:48px; height:48px; border-radius:16px; display:grid; place-items:center; border:1px solid rgba(146,246,255,.20); background:linear-gradient(180deg, rgba(11,22,43,.96), rgba(6,12,24,.94)); box-shadow:0 10px 26px rgba(0,0,0,.26), 0 0 24px rgba(56,222,255,.06); color:#eaf9ff; } .login-panel h1{ margin-top:18px; font-size:44px; line-height:1; letter-spacing:.02em; font-weight:800; text-shadow:0 0 24px rgba(151,245,255,.08); } .login-panel h1 span{ display:block; margin-top:10px; font-size:16px; font-weight:600; letter-spacing:.26em; text-transform:uppercase; color:var(--cyan); } .description{ margin-top:18px; font-size:15px; line-height:1.65; color:rgba(226,245,255,.78); max-width:41ch; } .banner{ margin-top:18px; padding:14px 16px; border-radius:18px; font-size:14px; line-height:1.55; border:1px solid transparent; } .banner.error{ color:#ffd7de; background:rgba(255,90,120,.10); border-color:rgba(255,120,140,.20); } .banner.success{ color:#d9fff2; background:rgba(105,255,206,.08); border-color:rgba(127,255,212,.18); } .banner.info{ color:#dff8ff; background:rgba(146,246,255,.06); border-color:rgba(146,246,255,.16); } .auth-stack{ margin-top:24px; display:grid; gap:14px; } .auth-btn{ position:relative; display:flex; align-items:center; justify-content:center; gap:12px; width:100%; min-height:58px; border-radius:18px; border:1px solid rgba(255,255,255,.14); text-decoration:none; font-size:15px; font-weight:700; letter-spacing:.02em; transition:transform .24s ease, box-shadow .24s ease, border-color .24s ease, background .24s ease; overflow:hidden; will-change:transform; } .auth-btn::before{ content:""; position:absolute; inset:0; background:linear-gradient(90deg, transparent, rgba(255,255,255,.12), transparent); transform:translateX(-100%); transition:transform .55s ease; } .auth-btn:hover{ transform:translateY(-2px) scale(1.01); } .auth-btn:hover::before{ transform:translateX(100%); } .auth-google{ color:#09111f; background:linear-gradient(180deg, rgba(255,255,255,.98), rgba(240,247,255,.92)); box-shadow:0 10px 24px rgba(255,255,255,.12); } .auth-google:hover{ box-shadow:0 16px 36px rgba(255,255,255,.18), 0 0 30px rgba(146,246,255,.12); } .auth-apple{ color:#eef7ff; background:linear-gradient(180deg, rgba(20,28,42,.96), rgba(8,12,22,.95)); border-color:rgba(146,246,255,.18); box-shadow:0 10px 26px rgba(0,0,0,.26), 0 0 24px rgba(56,222,255,.06); } .auth-apple:hover{ border-color:rgba(146,246,255,.34); box-shadow:0 16px 36px rgba(0,0,0,.32), 0 0 30px rgba(56,222,255,.12); } .auth-passkey{ color:#ecfbff; background:linear-gradient(180deg, rgba(12,32,44,.95), rgba(7,16,25,.96)); border-color:rgba(127,255,212,.18); box-shadow:0 10px 26px rgba(0,0,0,.22), 0 0 20px rgba(127,255,212,.05); } .icon{ width:20px; height:20px; display:inline-flex; align-items:center; justify-content:center; flex:0 0 20px; } .email-form{ margin-top:14px; display:grid; grid-template-columns:1fr auto; gap:10px; } .input-wrap{position:relative} .input-wrap input{ width:100%; min-height:54px; padding:0 16px 0 46px; border-radius:16px; border:1px solid rgba(146,246,255,.14); background:rgba(146,246,255,.04); color:#f1fbff; outline:none; box-shadow:inset 0 0 16px rgba(146,246,255,.03); transition:border-color .22s ease, box-shadow .22s ease, background .22s ease; } .input-wrap input:focus{ border-color:rgba(146,246,255,.34); background:rgba(146,246,255,.07); box-shadow:0 0 0 4px rgba(56,222,255,.08), inset 0 0 18px rgba(146,246,255,.06); } .input-wrap input::placeholder{color:rgba(214,239,255,.44)} .input-wrap svg{ position:absolute; left:15px; top:50%; transform:translateY(-50%); width:18px; height:18px; color:var(--muted); } .send-link-btn{ min-height:54px; padding:0 18px; border-radius:16px; border:1px solid rgba(146,246,255,.18); background:linear-gradient(180deg, rgba(146,246,255,.18), rgba(32,122,255,.12)); color:#ecfbff; cursor:pointer; font-weight:700; letter-spacing:.02em; transition:transform .22s ease, box-shadow .22s ease; } .send-link-btn:hover{ transform:translateY(-2px); box-shadow:0 14px 30px rgba(0,0,0,.26), 0 0 24px rgba(56,222,255,.12); } .secondary-links{ margin-top:16px; display:flex; align-items:center; gap:16px; flex-wrap:wrap; } .secondary-links a{ color:var(--cyan); text-decoration:none; font-size:13px; font-weight:600; } .secondary-links a:hover{text-decoration:underline} .meta-grid{ margin-top:24px; display:grid; grid-template-columns:repeat(3, 1fr); gap:12px; } .meta-card{ padding:14px 12px; border-radius:18px; background:rgba(146,246,255,.04); border:1px solid rgba(146,246,255,.10); box-shadow:inset 0 0 18px rgba(146,246,255,.02); transition:transform .22s ease, box-shadow .22s ease, border-color .22s ease; } .meta-card:hover{ transform:translateY(-2px); border-color:rgba(146,246,255,.24); box-shadow:0 10px 28px rgba(0,0,0,.22), 0 0 20px rgba(56,222,255,.08); } .meta-card .k{ font-size:10px; color:rgba(215,241,255,.55); text-transform:uppercase; letter-spacing:.16em; } .meta-card .v{ margin-top:8px; font-size:15px; font-weight:700; color:#f0fbff; } .footnote{ margin-top:18px; font-size:12px; line-height:1.7; color:rgba(206,233,249,.60); } .footnote strong{color:rgba(230,250,255,.85)} .sr-only{ position:absolute !important; width:1px !important; height:1px !important; padding:0 !important; margin:-1px !important; overflow:hidden !important; clip:rect(0,0,0,0) !important; white-space:nowrap !important; border:0 !important; } .auth-btn:focus-visible, .send-link-btn:focus-visible, .input-wrap input:focus-visible, .secondary-links a:focus-visible, .drop-trigger:focus-visible, .drop-link:focus-visible, .login-handle:focus-visible{ outline:2px solid var(--cyan); outline-offset:3px; box-shadow:0 0 0 6px rgba(56,222,255,.18); } @keyframes spinSlow{ from{transform:translate(-50%,-50%) rotate(0deg)} to{transform:translate(-50%,-50%) rotate(360deg)} } @keyframes spinReverse{ from{transform:translate(-50%,-50%) rotate(360deg)} to{transform:translate(-50%,-50%) rotate(0deg)} } @keyframes pulseDot{ 0%,100%{transform:scale(1);opacity:.9} 50%{transform:scale(1.35);opacity:1} } @keyframes panelSweep{ from{transform:translateX(-120%) rotate(12deg)} to{transform:translateX(120%) rotate(12deg)} } @keyframes holoSweep{ from{transform:translateY(0)} to{transform:translateY(150px)} } @media (max-width: 1100px){ .login-shell{ right:18px; } .ring.r1{ width:320px; height:320px; } .ring.r2{ width:560px; height:560px; } .ring.r3{ width:840px; height:840px; } .arc{ width:840px; height:840px; } } @media (max-width: 820px){ html,body{overflow:auto} .topbar{ height:auto; padding:14px 14px; flex-direction:column; align-items:flex-start; } .header-drop{ top:96px; left:10px; right:10px; } .hero{ top:112px; } .login-shell{ top:auto; right:12px; left:12px; bottom:110px; transform:none; width:auto; } .login-shell.hidden{ transform:translateY(calc(100% - 74px)); right:12px; } .login-handle{ top:-18px; left:50%; transform:translateX(-50%); width:140px; height:36px; border-radius:18px 18px 0 0; border-right:1px solid rgba(146,246,255,.18); border-bottom:none; writing-mode:horizontal-tb; } .login-panel{ border-radius:24px; padding:22px 18px 18px; } .login-panel h1{font-size:34px} .login-panel h1 span{font-size:13px} .email-form{grid-template-columns:1fr} .meta-grid{grid-template-columns:1fr} .status-pod{ left:12px; right:12px; min-width:0; width:auto; bottom:12px; } } @media (prefers-reduced-motion: reduce){ *, *::before, *::after{ animation:none !important; transition:none !important; scroll-behavior:auto !important; } #matrixCanvas,#orbCanvas,#fxCanvas{ display:none !important; } } </style> </head> <body> <noscript> <div style="position:fixed;top:12px;left:12px;right:12px;z-index:9999;padding:12px 14px;border-radius:12px;background:#1a2333;color:#eefcff;border:1px solid rgba(146,246,255,.2);font:14px/1.4 sans-serif;"> JavaScript is disabled. Secure sign-in still works, but live Guardian visuals are unavailable. </div> </noscript> <canvas id="matrixCanvas" aria-hidden="true"></canvas> <canvas id="orbCanvas" aria-hidden="true"></canvas> <canvas id="fxCanvas" aria-hidden="true"></canvas> <div class="vignette" aria-hidden="true"></div> <div class="app-shell"> <header class="topbar"> <div class="brand-wrap"> <div class="brand-mark" aria-hidden="true"> <svg viewBox="0 0 64 64" width="24" height="24" fill="none"> <circle cx="32" cy="32" r="20" stroke="#97F5FF" stroke-width="2.4"/> <path d="M18 32h28M32 18c5 5.5 8 12 8 14s-3 8.5-8 14c-5-5.5-8-12-8-14s3-8.5 8-14Z" stroke="#97F5FF" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> <div> <div class="brand-title">SIMON Intelligence</div> <div class="brand-sub">Guardian Security Access Layer</div> </div> </div> <div class="topbar-actions"> <div class="metric-chip">Protection <strong>Adaptive</strong></div> <div class="metric-chip">Identity <strong>OAuth + Passkey</strong></div> <button type="button" class="drop-trigger" id="dropTrigger">Menu ▾</button> </div> </header> <div class="header-drop" id="headerDrop"> <div class="header-drop-inner"> <div class="drop-title">Quick Access Routes</div> <div class="drop-grid"> <?php foreach ($quickTargets as $item): ?> <a class="drop-link sound-trigger" data-tone="mid" href="<?= e($item['url']) ?>"> <div> <strong><?= e($item['label']) ?></strong> <small><?= e($item['url']) ?></small> </div> <span class="drop-link-icon">↗</span> </a> <?php endforeach; ?> </div> </div> </div> <section class="hero"> <div class="full-visual" aria-hidden="true"> <div class="visual-center"> <div class="ring r1"></div> <div class="ring r2"></div> <div class="ring r3"></div> <div class="arc a1"></div> <div class="arc a2"></div> <div class="arc a3"></div> <div class="core-shadow"></div> <div class="status-pod"> <div class="label">Guardian Status</div> <div class="value"> <span class="pulse-dot"></span> <span>System Online</span> </div> <div class="mini">Identity, route, session, and policy checks ready for post-auth evaluation.</div> </div> </div> </div> </section> <div class="login-shell" id="loginShell"> <button type="button" class="login-handle sound-trigger" data-tone="low" id="loginToggle">Guardian</button> <div class="login-panel" id="loginPanel"> <div class="login-glow"></div> <div class="panel-top"> <div class="guardian-kicker"> <span class="mini-dot"></span> <span>Guardian Powered • SIMON Core</span> </div> <div class="panel-badge" aria-hidden="true"> <svg viewBox="0 0 24 24" width="20" height="20" fill="none"> <path d="M12 3L19 6V11C19 15.5 16.1 19.6 12 21C7.9 19.6 5 15.5 5 11V6L12 3Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/> <path d="M9.4 12.2L11.2 14L14.8 10.4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/> </svg> </div> </div> <h1> Guardian <span>Secure Access Intelligence</span> </h1> <p class="description"> Enter through the protected identity layer. Guardian verifies trusted access and routes your session into the SIMON command environment with device-aware security posture. </p> <?php if ($status !== ''): ?> <div class="banner <?= e($statusType) ?>" role="alert"> <?= e($status) ?> </div> <?php endif; ?> <div class="auth-stack"> <a class="auth-btn auth-google sound-trigger" href="/api/auth/google_start.php" data-tone="high"> <span class="icon" aria-hidden="true"> <svg viewBox="0 0 24 24" width="20" height="20"> <path fill="#EA4335" d="M12 10.2v3.9h5.5c-.2 1.3-1.5 3.9-5.5 3.9-3.3 0-6-2.7-6-6s2.7-6 6-6c1.9 0 3.2.8 4 1.5l2.7-2.6C17.1 3.4 14.8 2.4 12 2.4 6.7 2.4 2.4 6.7 2.4 12s4.3 9.6 9.6 9.6c5.5 0 9.1-3.9 9.1-9.3 0-.6-.1-1.1-.2-1.6H12z"/> </svg> </span> <span>Continue with Google</span> </a> <a class="auth-btn auth-apple sound-trigger" href="/api/auth/apple_start.php" data-tone="mid"> <span class="icon" aria-hidden="true"> <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"> <path d="M16.7 12.9c0-2.5 2-3.8 2.1-3.9-1.1-1.7-2.9-1.9-3.5-1.9-1.5-.2-2.9.9-3.6.9-.7 0-1.8-.9-3-.8-1.5 0-3 .9-3.8 2.2-1.6 2.8-.4 7 1.2 9.2.8 1.1 1.8 2.3 3 2.2 1.2-.1 1.7-.8 3.2-.8s1.9.8 3.2.8c1.3 0 2.1-1.1 2.9-2.2.9-1.3 1.3-2.6 1.3-2.6 0 0-2.5-1-2.5-3.1zm-2.3-7.3c.6-.8 1-1.8.8-2.9-.9 0-2 .6-2.6 1.4-.6.7-1 1.8-.9 2.8 1 .1 2-.5 2.7-1.3z"/> </svg> </span> <span>Continue with Apple</span> </a> <a class="auth-btn auth-passkey sound-trigger" href="/api/auth/passkey_start.php" data-tone="low"> <span class="icon" aria-hidden="true"> <svg viewBox="0 0 24 24" width="20" height="20" fill="none"> <path d="M6 10a6 6 0 1 1 10.8 3.6L15 15.4V18h-2v2h-2v-2.5l2.8-2.8A3.5 3.5 0 1 0 10 13" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/> </svg> </span> <span>Use Passkey / Biometric</span> </a> </div> <form class="email-form" method="post" action="/api/auth/email_magic_start.php" novalidate> <input type="hidden" name="csrf_token" value="<?= e($_SESSION['csrf_token']) ?>"> <input type="hidden" name="redirect" value="<?= e($redirect) ?>"> <label class="input-wrap" for="magic_email"> <span class="sr-only">Email address</span> <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"> <path d="M4 6h16v12H4z" stroke="currentColor" stroke-width="1.8"/> <path d="M4.5 7l7.5 6 7.5-6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/> </svg> <input id="magic_email" name="email" type="email" autocomplete="email" inputmode="email" placeholder="Enter email for secure sign-in link" required > </label> <button type="submit" class="send-link-btn sound-trigger" data-tone="mid">Send Email Link</button> </form> <div class="secondary-links"> <a class="sound-trigger" data-tone="low" href="/api/auth/access_code.php">Use access code</a> <a class="sound-trigger" data-tone="low" href="/api/auth/request_access.php">Request access</a> </div> <div class="meta-grid"> <div class="meta-card"> <div class="k">Protection</div> <div class="v">Adaptive</div> </div> <div class="meta-card"> <div class="k">Identity</div> <div class="v">Trusted Providers</div> </div> <div class="meta-card"> <div class="k">Redirect</div> <div class="v"><?= e($redirect) ?></div> </div> </div> <p class="footnote"> <strong>Guardian</strong> is the secure front gate for the platform. Sessions can be extended with device fingerprinting, allowlist control, risk scoring, anomaly alerts, protected routing, and multi-factor step-up verification. </p> </div> </div> </div> <script> (() => { const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; const loginPanel = document.getElementById('loginPanel'); const loginShell = document.getElementById('loginShell'); const loginToggle = document.getElementById('loginToggle'); const dropTrigger = document.getElementById('dropTrigger'); const headerDrop = document.getElementById('headerDrop'); /* ---------- Header drop ---------- */ const toggleDrop = () => { headerDrop.classList.toggle('open'); }; dropTrigger?.addEventListener('click', toggleDrop); document.addEventListener('click', (e) => { if (!headerDrop.contains(e.target) && !dropTrigger.contains(e.target)) { headerDrop.classList.remove('open'); } }); /* ---------- Login auto hide ---------- */ let hideTimer = null; function clearHideTimer() { if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; } } function scheduleHide() { clearHideTimer(); hideTimer = setTimeout(() => { if (window.innerWidth > 820) { loginShell.classList.add('hidden'); } }, 3200); } function showLogin() { loginShell.classList.remove('hidden'); scheduleHide(); } loginToggle?.addEventListener('click', () => { loginShell.classList.toggle('hidden'); if (!loginShell.classList.contains('hidden')) { scheduleHide(); } else { clearHideTimer(); } }); loginShell?.addEventListener('mouseenter', () => { loginShell.classList.remove('hidden'); clearHideTimer(); }); loginShell?.addEventListener('mouseleave', () => { scheduleHide(); }); window.addEventListener('load', () => { if (window.innerWidth > 820) { scheduleHide(); } }); window.addEventListener('resize', () => { if (window.innerWidth <= 820) { clearHideTimer(); loginShell.classList.remove('hidden'); } else { scheduleHide(); } }); /* ---------- Panel 3D hover ---------- */ if (loginPanel && !reduceMotion) { const resetTilt = () => { loginPanel.style.setProperty('--rx', '0deg'); loginPanel.style.setProperty('--ry', '0deg'); loginPanel.style.setProperty('--mx', '50%'); loginPanel.style.setProperty('--my', '50%'); }; loginPanel.addEventListener('mousemove', (e) => { const rect = loginPanel.getBoundingClientRect(); const px = (e.clientX - rect.left) / rect.width; const py = (e.clientY - rect.top) / rect.height; const ry = (px - 0.5) * 14; const rx = (0.5 - py) * 14; loginPanel.style.setProperty('--rx', `${rx}deg`); loginPanel.style.setProperty('--ry', `${ry}deg`); loginPanel.style.setProperty('--mx', `${px * 100}%`); loginPanel.style.setProperty('--my', `${py * 100}%`); }); loginPanel.addEventListener('mouseleave', resetTilt); } /* ---------- Sound engine ---------- */ let audioCtx = null; let soundEnabled = !reduceMotion; function getAudio() { if (!soundEnabled) return null; if (!audioCtx) { const AudioContextClass = window.AudioContext || window.webkitAudioContext; if (!AudioContextClass) return null; audioCtx = new AudioContextClass(); } return audioCtx; } function playTone(type = 'mid') { const ctx = getAudio(); if (!ctx) return; const now = ctx.currentTime; const osc = ctx.createOscillator(); const gain = ctx.createGain(); const filter = ctx.createBiquadFilter(); osc.type = type === 'high' ? 'triangle' : (type === 'low' ? 'sine' : 'sawtooth'); osc.frequency.value = type === 'high' ? 880 : (type === 'low' ? 320 : 540); filter.type = 'lowpass'; filter.frequency.value = type === 'high' ? 2400 : 1600; gain.gain.setValueAtTime(0.0001, now); gain.gain.exponentialRampToValueAtTime(0.03, now + 0.01); gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.16); osc.connect(filter); filter.connect(gain); gain.connect(ctx.destination); osc.start(now); osc.stop(now + 0.18); } document.querySelectorAll('.sound-trigger').forEach(el => { el.addEventListener('mouseenter', () => playTone(el.dataset.tone || 'mid')); el.addEventListener('focus', () => playTone(el.dataset.tone || 'mid')); }); if (reduceMotion) return; /* ---------- FX canvases ---------- */ const matrixCanvas = document.getElementById('matrixCanvas'); const orbCanvas = document.getElementById('orbCanvas'); const fxCanvas = document.getElementById('fxCanvas'); if (!matrixCanvas || !orbCanvas || !fxCanvas) return; const mtx = matrixCanvas.getContext('2d'); const ctx = orbCanvas.getContext('2d'); const fx = fxCanvas.getContext('2d'); let w = 0; let h = 0; let dpr = 1; let cx = 0; let cy = 0; let isMobile = false; const mouse = { x: 0, y: 0, tx: 0, ty: 0 }; const chars = 'アカサタナハマヤラワ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ<>/=+*{}[]$#@'; let columns = []; let particles = []; let stars = []; let matrixFrameCounter = 0; const orbState = { radius: 220, pointCount: 1450, cameraDepth: 1180, points: [] }; function rand(min, max) { return min + Math.random() * (max - min); } function randomChar() { return chars[(Math.random() * chars.length) | 0]; } function resize() { dpr = Math.min(window.devicePixelRatio || 1, 2); w = window.innerWidth; h = window.innerHeight; isMobile = w < 768; cx = w * 0.50; cy = h * 0.53; orbState.radius = isMobile ? 120 : (w < 1100 ? 170 : 220); orbState.pointCount = isMobile ? 600 : (w < 1100 ? 980 : 1450); [matrixCanvas, orbCanvas, fxCanvas].forEach(canvas => { canvas.width = Math.floor(w * dpr); canvas.height = Math.floor(h * dpr); canvas.style.width = w + 'px'; canvas.style.height = h + 'px'; }); [mtx, ctx, fx].forEach(context => { context.setTransform(dpr, 0, 0, dpr, 0, 0); context.imageSmoothingEnabled = true; }); initMatrix(); initParticles(); initStars(); generateSpherePoints(); } function initMatrix() { const fontSize = isMobile ? 14 : 16; const count = Math.ceil(w / fontSize); columns = []; for (let i = 0; i < count; i++) { columns.push({ x: i * fontSize, y: rand(-h, h), speed: rand(1.8, isMobile ? 4.2 : 6.6), length: Math.floor(rand(8, isMobile ? 18 : 26)), alpha: rand(0.06, 0.26), drift: rand(-0.1, 0.1) }); } } function initParticles() { const particleCount = isMobile ? 42 : (w < 1100 ? 86 : 140); particles = []; for (let i = 0; i < particleCount; i++) { particles.push({ angle: rand(0, Math.PI * 2), dist: rand(orbState.radius * 0.9, orbState.radius * 2.4), size: rand(1.0, isMobile ? 2.6 : 3.8), speed: rand(0.0008, 0.0032), alpha: rand(0.08, 0.42), tilt: rand(-0.4, 0.4) }); } } function initStars() { const starCount = isMobile ? 72 : (w < 1100 ? 120 : 180); stars = []; for (let i = 0; i < starCount; i++) { stars.push({ x: rand(0, w), y: rand(0, h), size: rand(0.5, 1.8), alpha: rand(0.18, 0.82), pulse: rand(0, Math.PI * 2) }); } } function generateSpherePoints() { orbState.points = []; const total = orbState.pointCount; for (let i = 0; i < total; i++) { const t = i / total; const inc = Math.acos(1 - 2 * t); const azi = Math.PI * (1 + Math.sqrt(5)) * i; orbState.points.push({ x: Math.sin(inc) * Math.cos(azi), y: Math.cos(inc), z: Math.sin(inc) * Math.sin(azi) }); } } function rotateX(p, a) { const c = Math.cos(a), s = Math.sin(a); return { x: p.x, y: p.y * c - p.z * s, z: p.y * s + p.z * c }; } function rotateY(p, a) { const c = Math.cos(a), s = Math.sin(a); return { x: p.x * c + p.z * s, y: p.y, z: -p.x * s + p.z * c }; } function rotateZ(p, a) { const c = Math.cos(a), s = Math.sin(a); return { x: p.x * c - p.y * s, y: p.x * s + p.y * c, z: p.z }; } function drawMatrix(time) { mtx.clearRect(0, 0, w, h); mtx.fillStyle = 'rgba(1, 6, 12, 0.10)'; mtx.fillRect(0, 0, w, h); const fontSize = isMobile ? 14 : 16; mtx.font = fontSize + 'px monospace'; mtx.textAlign = 'left'; mtx.textBaseline = 'top'; matrixFrameCounter++; for (const col of columns) { for (let i = 0; i < col.length; i++) { const y = col.y - i * fontSize * 1.05; if (y < -fontSize || y > h + fontSize) continue; const isHead = i === 0; const alpha = Math.max(0, col.alpha - i * 0.018); mtx.fillStyle = isHead ? `rgba(210,255,255,${Math.min(0.88, alpha + 0.26)})` : `rgba(95,255,220,${alpha})`; if (isHead) { mtx.shadowColor = 'rgba(140,255,240,0.55)'; mtx.shadowBlur = 9; } else { mtx.shadowBlur = 0; } const char = (matrixFrameCounter % 3 === 0 || isHead) ? randomChar() : '.'; mtx.fillText(char, col.x + Math.sin((time * 0.001) + y * 0.01) * col.drift * 8, y); } col.y += col.speed; if (col.y - col.length * fontSize > h + 40) { col.y = rand(-120, 0); col.speed = rand(1.8, isMobile ? 4.2 : 6.6); col.length = Math.floor(rand(8, isMobile ? 18 : 26)); col.alpha = rand(0.06, 0.26); } } mtx.shadowBlur = 0; } function drawOrb(time) { ctx.clearRect(0, 0, w, h); mouse.x += (mouse.tx - mouse.x) * 0.035; mouse.y += (mouse.ty - mouse.y) * 0.035; const rotY = time * 0.00026 + (mouse.x - w * 0.5) / w * 0.7; const rotX = time * 0.00016 + (mouse.y - h * 0.5) / h * 0.4; const rotZ = Math.sin(time * 0.00022) * 0.12; const coreGradient = ctx.createRadialGradient(cx, cy, 8, cx, cy, orbState.radius * 2.4); coreGradient.addColorStop(0, 'rgba(255,255,255,0.95)'); coreGradient.addColorStop(0.06, 'rgba(176,247,255,0.98)'); coreGradient.addColorStop(0.18, 'rgba(71,212,255,0.40)'); coreGradient.addColorStop(0.36, 'rgba(32,122,255,0.18)'); coreGradient.addColorStop(1, 'rgba(0,0,0,0)'); ctx.fillStyle = coreGradient; ctx.beginPath(); ctx.arc(cx, cy, orbState.radius * 2.4, 0, Math.PI * 2); ctx.fill(); const rendered = []; for (const point of orbState.points) { let p = { x: point.x * orbState.radius, y: point.y * orbState.radius, z: point.z * orbState.radius }; p = rotateY(p, rotY); p = rotateX(p, rotX); p = rotateZ(p, rotZ); const perspective = orbState.cameraDepth / (orbState.cameraDepth + p.z + orbState.radius * 0.25); rendered.push({ sx: cx + p.x * perspective, sy: cy + p.y * perspective, size: Math.max(0.35, perspective * (isMobile ? 1.7 : 2.35)), brightness: Math.max(0.1, (p.z + orbState.radius) / (orbState.radius * 2)), z: p.z }); } rendered.sort((a, b) => a.z - b.z); ctx.strokeStyle = 'rgba(120,236,255,0.08)'; ctx.lineWidth = 1; const step = isMobile ? 20 : 12; for (let i = 0; i < rendered.length; i += step) { const a = rendered[i]; const b = rendered[(i + Math.floor(step / 2)) % rendered.length]; const dist = Math.hypot(a.sx - b.sx, a.sy - b.sy); if (dist < (isMobile ? 90 : 150)) { ctx.beginPath(); ctx.moveTo(a.sx, a.sy); ctx.lineTo(b.sx, b.sy); ctx.stroke(); } } for (const p of rendered) { const alpha = 0.16 + p.brightness * 0.78; ctx.fillStyle = `rgba(155,246,255,${alpha})`; ctx.shadowColor = 'rgba(111,237,255,0.68)'; ctx.shadowBlur = 12; ctx.beginPath(); ctx.arc(p.sx, p.sy, p.size, 0, Math.PI * 2); ctx.fill(); } ctx.shadowBlur = 0; ctx.strokeStyle = 'rgba(165,248,255,0.24)'; ctx.lineWidth = 1.2; for (let i = 0; i < 4; i++) { ctx.save(); ctx.translate(cx, cy); ctx.rotate((time * 0.00011) + i * Math.PI / 4); ctx.scale(1, 0.34 + i * 0.05); ctx.beginPath(); ctx.arc(0, 0, orbState.radius + i * 18, 0, Math.PI * 2); ctx.stroke(); ctx.restore(); } for (const p of particles) { p.angle += p.speed; const px = cx + Math.cos(p.angle) * p.dist; const py = cy + Math.sin(p.angle + p.tilt) * p.dist * 0.28; ctx.fillStyle = `rgba(144,246,255,${p.alpha})`; ctx.shadowColor = 'rgba(92,223,255,0.55)'; ctx.shadowBlur = 10; ctx.beginPath(); ctx.arc(px, py, p.size, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = `rgba(100,226,255,${p.alpha * 0.30})`; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(px, py); ctx.stroke(); } ctx.shadowBlur = 0; const flare = ctx.createRadialGradient(cx, cy, 0, cx, cy, isMobile ? 54 : 90); flare.addColorStop(0, 'rgba(255,255,255,0.96)'); flare.addColorStop(0.15, 'rgba(170,248,255,0.9)'); flare.addColorStop(0.40, 'rgba(58,214,255,0.22)'); flare.addColorStop(1, 'rgba(0,0,0,0)'); ctx.fillStyle = flare; ctx.beginPath(); ctx.arc(cx, cy, isMobile ? 54 : 90, 0, Math.PI * 2); ctx.fill(); } function drawFx(time) { fx.clearRect(0, 0, w, h); for (const s of stars) { const a = s.alpha * (0.65 + Math.sin(time * 0.0014 + s.pulse) * 0.35); fx.fillStyle = `rgba(180,235,255,${a})`; fx.shadowColor = 'rgba(120,224,255,0.55)'; fx.shadowBlur = 8; fx.beginPath(); fx.arc(s.x, s.y, s.size, 0, Math.PI * 2); fx.fill(); } fx.shadowBlur = 0; const sweepX = (time * 0.18) % (w + 400) - 200; const scanGrad = fx.createLinearGradient(sweepX - 120, 0, sweepX + 120, 0); scanGrad.addColorStop(0, 'rgba(0,0,0,0)'); scanGrad.addColorStop(0.5, 'rgba(110,245,255,0.05)'); scanGrad.addColorStop(1, 'rgba(0,0,0,0)'); fx.fillStyle = scanGrad; fx.fillRect(0, 0, w, h); } function animate(time) { drawMatrix(time); drawOrb(time); drawFx(time); requestAnimationFrame(animate); } window.addEventListener('mousemove', (e) => { mouse.tx = e.clientX; mouse.ty = e.clientY; }); window.addEventListener('touchmove', (e) => { if (!e.touches || !e.touches[0]) return; mouse.tx = e.touches[0].clientX; mouse.ty = e.touches[0].clientY; }, { passive: true }); window.addEventListener('resize', resize); mouse.tx = window.innerWidth * 0.5; mouse.ty = window.innerHeight * 0.5; resize(); requestAnimationFrame(animate); })(); </script> </body> </html>
Save file
Quick jump
open a path
Open