Files
Website-Fa/src/components/Hero.astro
cemGr ac2c2be559 -
2026-05-18 22:26:32 +02:00

623 lines
38 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
---
<section class="hero" id="top">
<canvas id="hero-canvas"></canvas>
<canvas id="hero-canvas-gl"></canvas>
<div class="hero-content">
<div class="ai-badge">
<div class="ai-dot"></div>
KI-getriebene Technologieberatung
</div>
<div class="eyebrow">Regensburg · Bayern · International</div>
<h1 id="fa-headline">Komplexe Systeme.<br /><em class="underline">Klare Lösungen.</em><br />Echter Fortschritt.</h1>
<p class="hero-sub">Von intelligenten KI-Agenten bis zur regulierten Medizinsoftware wir verbinden modernste Algorithmen mit der Präzision, die kritische Systeme verlangen.</p>
<div class="hero-actions">
<a href="#kontakt" class="btn-primary">Beratungstermin vereinbaren</a>
<a href="#kontorion" class="btn-ghost">Kontorion ansehen ↗</a>
</div>
</div>
<div class="hero-visual" id="fa-hero-visual">
<div class="cap-card" id="fa-cap-card">
<div class="cap-head">
<span>Kern-Disziplinen</span>
<span class="cap-head-mark">Live</span>
</div>
<div class="cap-row">
<div class="cap-tag">AI</div>
<div class="cap-body">
<div class="cap-title">Generative KI &amp; Agents</div>
<div class="cap-keys">LLM · RAG · Computer Vision · MLOps</div>
</div>
</div>
<div class="cap-row">
<div class="cap-tag">MD</div>
<div class="cap-body">
<div class="cap-title">Regulierte Software</div>
<div class="cap-keys">MDR · IEC 62304 · ISO 13485 · FHIR / DICOM</div>
</div>
</div>
<div class="cap-row">
<div class="cap-tag">SW</div>
<div class="cap-body">
<div class="cap-title">Engineering &amp; Architektur</div>
<div class="cap-keys">Apps · Backend · APIs · Tech Consulting</div>
</div>
</div>
<div class="cap-foot">
<span class="cap-foot-dot">Regensburg · DE</span>
<span>Seit&nbsp;2020</span>
</div>
</div>
<div id="fa-stat-stack" style="display:none; flex-direction:column; gap:1.25rem; align-items:flex-end; width:100%;">
<div class="stat-card">
<div class="num">LLM</div>
<div class="label">Agents · RAG · Fine-Tuning<br />Generative KI für Ihr Unternehmen</div>
</div>
<div class="stat-card light">
<div class="num">MDR</div>
<div class="label">IEC 62304 · HL7 · FHIR · DICOM<br />Regulatorische Expertise</div>
</div>
<div class="hero-badge">Seit Regensburg</div>
</div>
</div>
</section>
<style>
.hero {
min-height: 88vh;
display: grid;
grid-template-columns: 1.15fr 0.85fr;
align-items: center;
padding: 5rem var(--content-pad) 4.5rem;
position: relative;
overflow: hidden;
background: #fff;
}
.hero.no-visual {
grid-template-columns: 1fr;
padding: 6rem var(--content-pad) 5.5rem;
}
.hero.no-visual .hero-content { padding-right: 0; max-width: 880px; }
.hero.no-visual h1 { font-size: clamp(2.8rem, 5.4vw, 5rem); }
.hero.no-visual .hero-sub { max-width: 540px; font-size: 17px; }
#hero-canvas, #hero-canvas-gl {
position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0;
}
#hero-canvas { opacity: 1; }
#hero-canvas-gl { opacity: 0; transition: opacity 0.35s ease; pointer-events: none; }
#hero-canvas-gl.active { opacity: 1; }
#hero-canvas-gl.active {
-webkit-mask-image: linear-gradient(to right, transparent 0%, transparent 18%, rgba(0,0,0,0.18) 30%, rgba(0,0,0,0.55) 45%, rgba(0,0,0,0.9) 60%, #000 75%);
mask-image: linear-gradient(to right, transparent 0%, transparent 18%, rgba(0,0,0,0.18) 30%, rgba(0,0,0,0.55) 45%, rgba(0,0,0,0.9) 60%, #000 75%);
}
.hero.gl-active::before {
content: ''; position: absolute; inset: 0;
background: linear-gradient(to right, rgba(255,255,255,0.88) 0%, rgba(255,255,255,0.78) 22%, rgba(255,255,255,0.45) 42%, rgba(255,255,255,0.1) 60%, rgba(255,255,255,0) 75%);
z-index: 1; pointer-events: none; transition: opacity 0.35s ease;
}
#hero-canvas.hidden { opacity: 0; }
.hero-content { position: relative; z-index: 2; padding-right: 3.5rem; max-width: 720px; }
.eyebrow {
font-size: 11px; font-weight: 500; letter-spacing: 3px; text-transform: uppercase;
color: var(--fa-blue); margin-bottom: 1.75rem;
display: flex; align-items: center; gap: 12px;
}
.eyebrow::before { content: ''; display: block; width: 36px; height: 1px; background: var(--fa-blue); }
.ai-badge {
display: inline-flex; align-items: center; gap: 8px;
background: var(--fa-navy); color: #fff;
font-size: 11px; font-weight: 500; letter-spacing: 1.5px; text-transform: uppercase;
padding: 0.45rem 1rem 0.45rem 0.7rem; margin-bottom: 1.5rem;
}
.ai-dot {
width: 7px; height: 7px; border-radius: 50%; background: var(--fa-blue);
animation: pulse 2s ease-in-out infinite;
}
h1 {
font-family: 'Syne', sans-serif;
font-size: clamp(2.6rem, 4.4vw, 4.2rem);
font-weight: 700; line-height: 0.98;
color: var(--fa-navy); letter-spacing: -2.5px; margin-bottom: 2.25rem;
text-wrap: balance;
}
h1 em {
font-style: normal; color: var(--fa-blue);
position: relative; display: inline-block;
color: color-mix(in oklch, var(--fa-blue), oklch(0.55 0.22 255) calc(var(--hero-progress, 0) * 100%));
transition: color 0.05s linear;
}
h1 em.underline::after {
content: ''; position: absolute; left: 0; right: 0; bottom: 0.06em;
height: 0.08em; background: var(--fa-blue); opacity: 0.35;
transform-origin: left center;
animation: faUnderlineDraw 1.2s cubic-bezier(0.22, 0.61, 0.36, 1) 0.6s both;
}
h1 em.underline::before {
content: ''; position: absolute; left: 0; bottom: 0.04em; height: 0.14em;
width: calc(var(--hero-progress, 0) * 100%);
background: linear-gradient(90deg, oklch(0.62 0.18 250) 0%, oklch(0.70 0.22 260) 100%);
opacity: 0.85; border-radius: 1px;
box-shadow: 0 0 12px oklch(0.65 0.22 255 / 0.4);
transition: width 0.05s linear; pointer-events: none;
}
h1.flat em { color: var(--fa-navy); }
h1.flat em::after, h1.flat em::before { display: none; }
.hero-sub {
font-size: 16px; font-weight: 300; line-height: 1.8;
color: var(--fa-mid); max-width: 460px; margin-bottom: 3rem;
}
.hero-actions { display: flex; align-items: center; gap: 1.75rem; }
.btn-primary {
font-family: 'DM Sans', sans-serif; font-size: 14px; font-weight: 500;
color: #fff; background: var(--fa-navy); border: none;
padding: 0.9rem 2.25rem; cursor: pointer; transition: background 0.2s;
text-decoration: none; display: inline-flex; align-items: center; gap: 0.5rem;
white-space: nowrap;
}
.btn-primary:hover { background: var(--fa-blue); }
.btn-ghost {
font-family: 'DM Sans', sans-serif; font-size: 14px; font-weight: 400;
color: var(--fa-navy); background: none; border: none; cursor: pointer;
opacity: 0.6; transition: opacity 0.2s;
text-decoration: none; display: inline-flex; align-items: center;
}
.btn-ghost:hover { opacity: 1; }
/* Hero visual */
.hero-visual {
position: relative; z-index: 2;
display: flex; flex-direction: column; align-items: stretch;
max-width: 380px; margin-left: auto;
}
.cap-card {
background: var(--fa-navy); color: #fff;
border: 0.5px solid rgba(107,143,212,0.25);
}
.cap-head {
padding: 1.1rem 1.5rem;
border-bottom: 0.5px solid rgba(107,143,212,0.18);
display: flex; align-items: center; justify-content: space-between;
font-family: 'Syne', sans-serif; font-size: 11px; font-weight: 600;
letter-spacing: 2.5px; text-transform: uppercase;
color: rgba(255,255,255,0.68);
}
.cap-head-mark {
display: flex; align-items: center; gap: 6px; color: var(--fa-blue-light);
}
.cap-head-mark::before {
content: ''; width: 5px; height: 5px; border-radius: 50%;
background: var(--fa-blue-light); animation: pulse 2.4s ease-in-out infinite;
}
.cap-row {
display: grid; grid-template-columns: 44px 1fr;
padding: 1.25rem 1.5rem;
border-bottom: 0.5px solid rgba(107,143,212,0.12);
gap: 1rem; align-items: center; transition: background 0.25s;
}
.cap-row:last-of-type { border-bottom: none; }
.cap-row:hover { background: rgba(107,143,212,0.06); }
.cap-tag {
font-family: 'Syne', sans-serif; font-size: 12px; font-weight: 700;
letter-spacing: 1.5px; color: var(--fa-blue-light);
border: 0.5px solid rgba(168,192,232,0.35); padding: 6px 0; text-align: center;
}
.cap-title {
font-family: 'Syne', sans-serif; font-size: 15px; font-weight: 600;
color: #fff; letter-spacing: -0.2px; margin-bottom: 4px;
}
.cap-keys {
font-size: 12px; font-weight: 300; line-height: 1.5;
color: rgba(255,255,255,0.68); letter-spacing: 0.2px;
}
.cap-foot {
padding: 0.85rem 1.5rem; background: rgba(0,0,0,0.18);
display: flex; align-items: center; justify-content: space-between;
font-size: 11px; letter-spacing: 1.8px; text-transform: uppercase;
color: rgba(255,255,255,0.65);
}
.cap-foot-dot { display: inline-flex; align-items: center; gap: 8px; color: rgba(255,255,255,0.7); }
.cap-foot-dot::before {
content: ''; width: 6px; height: 6px; border-radius: 50%;
background: #6FD49A; box-shadow: 0 0 0 3px rgba(111,212,154,0.18);
}
/* Stat cards */
.stat-card {
background: var(--fa-navy); color: #fff;
padding: 1.75rem 2.25rem; width: 290px;
}
.stat-card .num {
font-family: 'Syne', sans-serif; font-size: 2rem; font-weight: 800;
letter-spacing: -0.5px; color: var(--fa-blue-light); line-height: 1; margin-bottom: 0.4rem;
}
.stat-card .label { font-size: 13px; font-weight: 300; opacity: 0.65; line-height: 1.5; }
.stat-card.light {
background: rgba(238,243,251,0.9); color: var(--fa-navy);
border: 0.5px solid var(--fa-border); margin-right: 2rem;
backdrop-filter: blur(4px);
}
.stat-card.light .num { color: var(--fa-navy); }
.stat-card.light .label { opacity: 0.55; color: var(--fa-navy); }
.hero-badge {
background: var(--fa-blue); color: #fff;
font-size: 11px; font-weight: 500; letter-spacing: 2px; text-transform: uppercase;
padding: 0.55rem 1.1rem; align-self: flex-start; margin-left: 1rem;
}
/* Responsive */
@media (max-width: 900px) {
.hero { grid-template-columns: 1fr; min-height: auto; padding: 4rem var(--content-pad); }
.hero-content { padding-right: 0; }
.hero-visual { display: none; }
}
@media (max-width: 480px) {
h1 { font-size: clamp(1.85rem, 8.5vw, 2.6rem); letter-spacing: -1.2px; }
.eyebrow { font-size: 10px; letter-spacing: 2px; margin-bottom: 1.25rem; }
.hero-sub { font-size: 15px; margin-bottom: 2rem; }
.hero-actions { flex-wrap: wrap; gap: 1rem; }
.btn-primary { padding: 0.85rem 1.6rem; }
.hero { padding: 3rem var(--content-pad) 3.5rem; }
#hero-canvas, #hero-canvas-gl { opacity: 0.5; }
}
@media (hover: none) and (pointer: coarse) {
/* No hover states on touch */
}
</style>
<script is:inline>
/* ============================================================
HERO SCROLL PROGRESS
============================================================ */
(function() {
const hero = document.querySelector('.hero');
if (!hero) return;
const root = document.documentElement;
let ticking = false;
function update() {
const rect = hero.getBoundingClientRect();
const h = rect.height;
let p = -rect.top / h;
p = Math.max(0, Math.min(1, (p - 0.12) / 0.7));
root.style.setProperty('--hero-progress', p.toFixed(3));
ticking = false;
}
function onScroll() { if (!ticking) { requestAnimationFrame(update); ticking = true; } }
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll);
update();
})();
/* ============================================================
HERO CANVAS — 2D modes
============================================================ */
(function() {
const canvas = document.getElementById('hero-canvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const BLUE = '107,143,212', NAVY = '26,31,92';
let W = 0, H = 0, nodes = [], hubs = [], flowParticles = [];
let mode = 'constellation', rafId = null;
const mouse = { x: -9999, y: -9999 };
let running = true;
const dpr = Math.min(window.devicePixelRatio || 1, 2);
function resize() {
const rect = canvas.parentElement.getBoundingClientRect();
W = rect.width; H = rect.height;
canvas.width = W * dpr; canvas.height = H * dpr;
canvas.style.width = W + 'px'; canvas.style.height = H + 'px';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
function rand(a, b) { return a + Math.random() * (b - a); }
/* CONSTELLATION */
function initConstellation() {
hubs = [
{ ax: 0.18, ay: 0.30, label: 'LLM · RAG' },
{ ax: 0.62, ay: 0.18, label: 'COMPUTER VISION' },
{ ax: 0.78, ay: 0.62, label: 'MDR · 62304' },
{ ax: 0.32, ay: 0.82, label: 'FHIR · DICOM' },
].map((h, i) => ({ ...h, x: h.ax * W, y: h.ay * H, vx: 0, vy: 0, r: 4.5, phase: i * 1.7 }));
nodes = [];
const count = Math.max(28, Math.floor((W * H) / 22000));
for (let i = 0; i < count; i++) {
nodes.push({ x: rand(0, W), y: rand(0, H), vx: rand(-0.18, 0.18), vy: rand(-0.15, 0.15), r: rand(1.2, 2.4) });
}
}
function updateConstellation(t) {
for (const h of hubs) {
const targetX = h.ax * W + Math.sin(t * 0.0004 + h.phase) * 18;
const targetY = h.ay * H + Math.cos(t * 0.0005 + h.phase * 1.3) * 14;
h.vx += (targetX - h.x) * 0.008; h.vy += (targetY - h.y) * 0.008;
h.vx *= 0.92; h.vy *= 0.92; h.x += h.vx; h.y += h.vy;
}
for (const n of nodes) {
n.x += n.vx; n.y += n.vy;
if (n.x < -10) n.x = W + 10; if (n.x > W + 10) n.x = -10;
if (n.y < -10) n.y = H + 10; if (n.y > H + 10) n.y = -10;
}
}
function nearestK(node, others, k) {
const arr = [];
for (const o of others) {
if (o === node) continue;
const dx = node.x - o.x, dy = node.y - o.y;
arr.push({ o, d: Math.sqrt(dx*dx + dy*dy) });
}
arr.sort((a, b) => a.d - b.d);
return arr.slice(0, k);
}
function drawConstellation() {
ctx.clearRect(0, 0, W, H);
ctx.lineWidth = 0.9;
for (const n of nodes) {
const nn = nearestK(n, nodes, 2);
for (const { o, d } of nn) {
if (d > 160) continue;
ctx.strokeStyle = `rgba(${BLUE}, ${(1 - d / 160) * 0.25})`;
ctx.beginPath(); ctx.moveTo(n.x, n.y); ctx.lineTo(o.x, o.y); ctx.stroke();
}
}
ctx.lineWidth = 1.3;
for (const h of hubs) {
const nn = nearestK(h, nodes, 6);
for (const { o, d } of nn) {
if (d > 260) continue;
ctx.strokeStyle = `rgba(${BLUE}, ${(1 - d / 260) * 0.55})`;
ctx.beginPath(); ctx.moveTo(h.x, h.y); ctx.lineTo(o.x, o.y); ctx.stroke();
}
}
if (mouse.x > -9000) {
const closest = nearestK({ x: mouse.x, y: mouse.y }, nodes.concat(hubs), 4);
ctx.lineWidth = 1.4;
for (const { o, d } of closest) {
if (d > 220) continue;
ctx.strokeStyle = `rgba(${BLUE}, ${(1 - d / 220) * 0.7})`;
ctx.beginPath(); ctx.moveTo(mouse.x, mouse.y); ctx.lineTo(o.x, o.y); ctx.stroke();
}
}
for (const n of nodes) {
const g2 = ctx.createRadialGradient(n.x, n.y, 0, n.x, n.y, n.r * 3);
g2.addColorStop(0, `rgba(${BLUE}, 0.18)`); g2.addColorStop(1, `rgba(${BLUE}, 0)`);
ctx.beginPath(); ctx.arc(n.x, n.y, n.r * 3, 0, Math.PI * 2); ctx.fillStyle = g2; ctx.fill();
ctx.beginPath(); ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2); ctx.fillStyle = `rgba(${BLUE}, 0.45)`; ctx.fill();
}
const t = performance.now();
for (const h of hubs) {
const pulseT = ((t * 0.0007 + h.phase) % 1);
ctx.beginPath(); ctx.arc(h.x, h.y, 8 + pulseT * 52, 0, Math.PI * 2);
ctx.strokeStyle = `rgba(${BLUE}, ${(1 - pulseT) * 0.65})`; ctx.lineWidth = 1.2; ctx.stroke();
const pulseT2 = ((t * 0.0011 + h.phase + 0.5) % 1);
ctx.beginPath(); ctx.arc(h.x, h.y, 5 + pulseT2 * 28, 0, Math.PI * 2);
ctx.strokeStyle = `rgba(${BLUE}, ${(1 - pulseT2) * 0.45})`; ctx.lineWidth = 0.8; ctx.stroke();
const g = ctx.createRadialGradient(h.x, h.y, 0, h.x, h.y, 42);
g.addColorStop(0, `rgba(${BLUE}, 0.45)`); g.addColorStop(0.5, `rgba(${BLUE}, 0.12)`); g.addColorStop(1, `rgba(${BLUE}, 0)`);
ctx.beginPath(); ctx.arc(h.x, h.y, 42, 0, Math.PI * 2); ctx.fillStyle = g; ctx.fill();
ctx.beginPath(); ctx.arc(h.x, h.y, h.r, 0, Math.PI * 2); ctx.fillStyle = `rgba(${BLUE}, 1)`; ctx.fill();
ctx.beginPath(); ctx.arc(h.x, h.y, h.r + 3.5, 0, Math.PI * 2); ctx.strokeStyle = `rgba(${BLUE}, 0.75)`; ctx.lineWidth = 1.2; ctx.stroke();
ctx.beginPath(); ctx.arc(h.x, h.y, h.r + 8, 0, Math.PI * 2); ctx.strokeStyle = `rgba(${BLUE}, 0.3)`; ctx.lineWidth = 0.8; ctx.stroke();
ctx.font = '700 11px "Syne", sans-serif';
const tw = ctx.measureText(h.label).width;
const lx = h.x + h.r + 12, ly = h.y - 5;
ctx.fillStyle = `rgba(${BLUE}, 0.12)`; ctx.fillRect(lx - 4, ly - 2, tw + 8, 16);
ctx.strokeStyle = `rgba(${BLUE}, 0.4)`; ctx.lineWidth = 0.6; ctx.strokeRect(lx - 4, ly - 2, tw + 8, 16);
ctx.fillStyle = `rgba(${NAVY}, 0.95)`; ctx.fillText(h.label, lx, ly + 9);
}
}
/* NETWORK */
function initNetwork() {
nodes = []; hubs = [];
const count = Math.max(50, Math.floor((W * H) / 14000));
for (let i = 0; i < count; i++) {
nodes.push({ x: rand(0, W), y: rand(0, H), vx: rand(-0.25, 0.25), vy: rand(-0.2, 0.2), r: rand(2, 5), isKey: Math.random() < 0.12, label: ['LLM','RAG','CV','AI','ML','API','MDR'][Math.floor(Math.random()*7)] });
}
}
function updateNetwork() {
for (const n of nodes) { n.x += n.vx; n.y += n.vy; if (n.x < 0 || n.x > W) n.vx *= -1; if (n.y < 0 || n.y > H) n.vy *= -1; }
}
function drawNetwork() {
ctx.clearRect(0, 0, W, H);
for (let i = 0; i < nodes.length; i++) {
for (let j = i + 1; j < nodes.length; j++) {
const dx = nodes[i].x - nodes[j].x, dy = nodes[i].y - nodes[j].y;
const d = Math.sqrt(dx*dx + dy*dy);
if (d < 130) { ctx.strokeStyle = `rgba(${BLUE}, ${(1-d/130)*0.18})`; ctx.lineWidth = 0.6; ctx.beginPath(); ctx.moveTo(nodes[i].x,nodes[i].y); ctx.lineTo(nodes[j].x,nodes[j].y); ctx.stroke(); }
}
const mdx = nodes[i].x - mouse.x, mdy = nodes[i].y - mouse.y, md = Math.sqrt(mdx*mdx+mdy*mdy);
if (md < 160) { ctx.strokeStyle = `rgba(${BLUE}, ${(1-md/160)*0.35})`; ctx.lineWidth = 0.8; ctx.beginPath(); ctx.moveTo(nodes[i].x,nodes[i].y); ctx.lineTo(mouse.x,mouse.y); ctx.stroke(); }
}
for (const n of nodes) {
const g = ctx.createRadialGradient(n.x,n.y,0,n.x,n.y,n.r*4); g.addColorStop(0,`rgba(${BLUE},0.12)`); g.addColorStop(1,`rgba(${BLUE},0)`);
ctx.beginPath(); ctx.arc(n.x,n.y,n.r*4,0,Math.PI*2); ctx.fillStyle = g; ctx.fill();
ctx.beginPath(); ctx.arc(n.x,n.y,n.r,0,Math.PI*2); ctx.fillStyle = n.isKey ? `rgba(${BLUE},0.75)` : `rgba(${NAVY},0.35)`; ctx.fill();
if (n.isKey) { ctx.font = '600 9px "Syne",sans-serif'; ctx.fillStyle = `rgba(${BLUE},0.55)`; ctx.fillText(n.label, n.x+n.r+4, n.y+3); }
}
}
/* FLOW FIELD */
function initFlowfield() {
flowParticles = [];
const count = Math.max(160, Math.floor((W * H) / 4500));
for (let i = 0; i < count; i++) flowParticles.push({ x: rand(0,W), y: rand(0,H), px:0, py:0, life: rand(0,200), maxLife: 220+rand(0,280), speed: 0.35+rand(0,0.7) });
ctx.fillStyle = '#fff'; ctx.fillRect(0,0,W,H);
}
function flowAngle(x, y, t) {
return (Math.sin(x*0.0045+t*0.00025) + Math.cos(y*0.0042-t*0.00030) + Math.sin((x+y)*0.0023+t*0.00040)) * Math.PI;
}
function updateFlowfield(t) {
for (const p of flowParticles) {
p.px = p.x; p.py = p.y;
const ang = flowAngle(p.x, p.y, t);
p.x += Math.cos(ang)*p.speed; p.y += Math.sin(ang)*p.speed;
if (mouse.x > -9000) { const dx=p.x-mouse.x,dy=p.y-mouse.y,d=Math.sqrt(dx*dx+dy*dy); if (d<130&&d>0.1) { const f=(1-d/130)*1.6; p.x+=(dx/d)*f; p.y+=(dy/d)*f; } }
p.life++;
if (p.life>p.maxLife||p.x<-5||p.x>W+5||p.y<-5||p.y>H+5) { p.x=rand(0,W); p.y=rand(0,H); p.life=0; p.px=p.x; p.py=p.y; }
}
}
function drawFlowfield() {
ctx.fillStyle = 'rgba(255,255,255,0.018)'; ctx.fillRect(0,0,W,H);
ctx.lineWidth = 1.0; ctx.lineCap = 'round';
for (const p of flowParticles) {
const lr = p.life/p.maxLife, a = Math.sin(lr*Math.PI)*0.7;
ctx.strokeStyle = ((p.x+p.y)|0)%3!==0 ? `rgba(${BLUE},${a})` : `rgba(${NAVY},${a*0.7})`;
ctx.beginPath(); ctx.moveTo(p.px,p.py); ctx.lineTo(p.x,p.y); ctx.stroke();
}
}
/* TOPOGRAPHY */
const TOPO_RES = 26;
let topoField = null, topoCols = 0, topoRows = 0;
function initTopo() {
topoCols = Math.ceil(W/TOPO_RES)+2; topoRows = Math.ceil(H/TOPO_RES)+2;
topoField = new Float32Array(topoCols*topoRows);
}
function topoNoise(x,y,t) { return Math.sin(x*0.011+t*0.00028)+Math.cos(y*0.013-t*0.00022)+0.6*Math.sin((x+y)*0.0075+t*0.00036)+0.4*Math.cos((x-y)*0.0098-t*0.00042); }
function updateTopo(t) {
const mx=mouse.x,my=mouse.y,hasMouse=mx>-9000;
for (let j=0;j<topoRows;j++) { const y=j*TOPO_RES; for (let i=0;i<topoCols;i++) { const x=i*TOPO_RES; let v=topoNoise(x,y,t); if (hasMouse) { const dx=x-mx,dy=y-my,d2=dx*dx+dy*dy; if (d2<60000) v+=1.6*Math.exp(-d2/9000); } topoField[j*topoCols+i]=v; } }
}
function drawTopo() {
ctx.clearRect(0,0,W,H);
const thresholds=[-1.6,-1.0,-0.4,0.2,0.8,1.4,2.0];
for (let ti=0;ti<thresholds.length;ti++) {
const thr=thresholds[ti],mid=(thresholds.length-1)/2,closeness=1-Math.abs(ti-mid)/mid;
ctx.strokeStyle=`rgba(${BLUE},${0.13+closeness*0.18})`; ctx.lineWidth=closeness>0.7?0.95:0.55; ctx.beginPath();
for (let j=0;j<topoRows-1;j++) { for (let i=0;i<topoCols-1;i++) { const tl=topoField[j*topoCols+i],tr=topoField[j*topoCols+i+1],br=topoField[(j+1)*topoCols+i+1],bl=topoField[(j+1)*topoCols+i]; let idx=0; if(tl>thr)idx|=1;if(tr>thr)idx|=2;if(br>thr)idx|=4;if(bl>thr)idx|=8; if(idx===0||idx===15)continue; const x=i*TOPO_RES,y=j*TOPO_RES,s=TOPO_RES,tx=x+s*(thr-tl)/(tr-tl),rx=x+s,ry=y+s*(thr-tr)/(br-tr),bx=x+s*(thr-bl)/(br-bl),by=y+s,lx=x,ly=y+s*(thr-tl)/(bl-tl); switch(idx){case 1:case 14:ctx.moveTo(lx,ly);ctx.lineTo(tx,y);break;case 2:case 13:ctx.moveTo(tx,y);ctx.lineTo(rx,ry);break;case 3:case 12:ctx.moveTo(lx,ly);ctx.lineTo(rx,ry);break;case 4:case 11:ctx.moveTo(rx,ry);ctx.lineTo(bx,by);break;case 5:ctx.moveTo(lx,ly);ctx.lineTo(tx,y);ctx.moveTo(rx,ry);ctx.lineTo(bx,by);break;case 6:case 9:ctx.moveTo(tx,y);ctx.lineTo(bx,by);break;case 7:case 8:ctx.moveTo(lx,ly);ctx.lineTo(bx,by);break;case 10:ctx.moveTo(lx,ly);ctx.lineTo(bx,by);ctx.moveTo(tx,y);ctx.lineTo(rx,ry);break;} } } ctx.stroke();
}
ctx.fillStyle=`rgba(${BLUE},0.55)`;
for (let j=2;j<topoRows-2;j+=4) { for (let i=2;i<topoCols-2;i+=4) { if(topoField[j*topoCols+i]>1.8){ctx.beginPath();ctx.arc(i*TOPO_RES,j*TOPO_RES,2,0,Math.PI*2);ctx.fill();} } }
}
/* LOOP & API */
function loop() {
if (!running) return;
const t = performance.now();
if (mode==='constellation'){updateConstellation(t);drawConstellation();}
else if (mode==='network'){updateNetwork();drawNetwork();}
else if (mode==='flow'){updateFlowfield(t);drawFlowfield();}
else if (mode==='topo'){updateTopo(t);drawTopo();}
rafId = requestAnimationFrame(loop);
}
function setMode(m) {
mode = m;
const cnv2d=document.getElementById('hero-canvas'), cnvGL=document.getElementById('hero-canvas-gl'), hero=document.querySelector('.hero');
if (m==='plasma'||m==='mercury'||m==='crystal') { running=false; cancelAnimationFrame(rafId); rafId=null; ctx.clearRect(0,0,W,H); cnv2d.classList.add('hidden'); cnvGL.classList.add('active'); hero.classList.add('gl-active'); if(window.__faSetGLMode)window.__faSetGLMode(m); return; }
cnv2d.classList.remove('hidden'); cnvGL.classList.remove('active'); hero.classList.remove('gl-active'); if(window.__faSetGLMode)window.__faSetGLMode('off');
if (m==='off'){running=false;cancelAnimationFrame(rafId);rafId=null;ctx.clearRect(0,0,W,H);cnv2d.classList.add('hidden');return;}
if(m==='constellation')initConstellation(); else if(m==='network')initNetwork(); else if(m==='flow')initFlowfield(); else if(m==='topo')initTopo();
running=true; cancelAnimationFrame(rafId); rafId=null; loop();
}
window.__faSetCanvasMode = setMode;
window.addEventListener('resize', () => { resize(); if(mode==='constellation')initConstellation(); else if(mode==='network')initNetwork(); else if(mode==='flow')initFlowfield(); else if(mode==='topo')initTopo(); });
canvas.parentElement.addEventListener('mousemove', e => { const rect=canvas.getBoundingClientRect(); mouse.x=e.clientX-rect.left; mouse.y=e.clientY-rect.top; });
canvas.parentElement.addEventListener('mouseleave', () => { mouse.x=-9999; mouse.y=-9999; });
resize(); initConstellation(); loop();
})();
/* ============================================================
HERO WEBGL
============================================================ */
(function() {
const canvas = document.getElementById('hero-canvas-gl');
const gl = canvas.getContext('webgl', { antialias: false, alpha: true, premultipliedAlpha: false });
if (!gl) { window.__faSetGLMode = () => {}; return; }
const VS_SRC = `attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0.0, 1.0); }`;
const FRAG_HEADER = `
precision highp float;
uniform vec2 uRes; uniform float uTime; uniform vec2 uMouse;
float hash21(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}
float vnoise(vec2 p){vec2 i=floor(p),f=fract(p),u=f*f*(3.0-2.0*f);float a=hash21(i),b=hash21(i+vec2(1,0)),c=hash21(i+vec2(0,1)),d=hash21(i+vec2(1,1));return mix(mix(a,b,u.x),mix(c,d,u.x),u.y);}
float fbm(vec2 p){float v=0.0,a=0.5;mat2 r=mat2(0.8,-0.6,0.6,0.8);for(int i=0;i<4;i++){v+=a*vnoise(p);p=r*p*2.02;a*=0.5;}return v;}
`;
const FS_PLASMA = `void main(){vec2 frag=gl_FragCoord.xy;float scale=min(uRes.x,uRes.y);vec2 uv=(frag-0.5*uRes)/scale,m=(uMouse-0.5*uRes)/scale,center=m*0.55,p=uv-center;float r=length(p),a=atan(p.y,p.x),corona=0.0;for(int i=0;i<3;i++){float fi=float(i);corona+=fbm(vec2(a*(3.0+fi)+uTime*(0.18+fi*0.07),r*4.5-uTime*0.25))/(fi+1.0);}corona/=1.6;float core=exp(-pow(r*4.0,2.0)),outer=exp(-r*2.8)*(0.55+0.5*corona),fil=smoothstep(0.6,0.92,fbm(vec2(a*6.0,r*7.0+uTime*0.4)));fil*=smoothstep(0.08,0.3,r)*smoothstep(0.7,0.25,r);vec3 navy=vec3(0.102,0.122,0.361),blue=vec3(0.420,0.561,0.831),lblue=vec3(0.659,0.753,0.910),col=mix(blue,navy,smoothstep(0.0,0.25,r));col=mix(col,lblue,fil);float a1=clamp(core*0.65+outer*0.55+fil*0.45,0.0,0.85);gl_FragColor=vec4(col,a1);}`;
const FS_MERCURY = `float metaball(vec2 p,vec2 c,float r){return r/max(length(p-c),0.0001);}void main(){vec2 frag=gl_FragCoord.xy;float scale=min(uRes.x,uRes.y);vec2 uv=(frag-0.5*uRes)/scale,m=(uMouse-0.5*uRes)/scale,b0=vec2(sin(uTime*0.27)*0.42,cos(uTime*0.23)*0.28),b1=vec2(cos(uTime*0.35)*0.48,sin(uTime*0.21)*0.26),b2=vec2(sin(uTime*0.19+2.0)*0.5,cos(uTime*0.31+1.0)*0.36),b3=vec2(cos(uTime*0.25+4.0)*0.36,sin(uTime*0.17+3.0)*0.44),b4=m,b5=-m*0.4;float f=metaball(uv,b0,0.16)+metaball(uv,b1,0.18)+metaball(uv,b2,0.13)+metaball(uv,b3,0.15)+metaball(uv,b4,0.22)+metaball(uv,b5,0.10);float surface=smoothstep(2.3,2.85,f),halo=smoothstep(1.6,2.3,f)-surface;vec2 e=vec2(0.004,0.0);float fx=(metaball(uv+e.xy,b0,0.16)+metaball(uv+e.xy,b1,0.18)+metaball(uv+e.xy,b2,0.13)+metaball(uv+e.xy,b3,0.15)+metaball(uv+e.xy,b4,0.22)+metaball(uv+e.xy,b5,0.10))-f,fy=(metaball(uv+e.yx,b0,0.16)+metaball(uv+e.yx,b1,0.18)+metaball(uv+e.yx,b2,0.13)+metaball(uv+e.yx,b3,0.15)+metaball(uv+e.yx,b4,0.22)+metaball(uv+e.yx,b5,0.10))-f;vec2 n=normalize(vec2(fx,fy)+vec2(1e-5));vec3 navy=vec3(0.102,0.122,0.361),blue=vec3(0.420,0.561,0.831),lblue=vec3(0.659,0.753,0.910),pale=vec3(0.93,0.95,0.99);float ang=atan(n.y,n.x),t1=sin(ang*2.0+uTime*0.5)*0.5+0.5,t2=sin(ang*3.0-uTime*0.4)*0.5+0.5;vec3 surfCol=mix(navy,blue,t1);surfCol=mix(surfCol,lblue,t2*0.55);float fres=pow(1.0-abs(n.y),1.6);surfCol=mix(surfCol,pale,fres*0.5);float spec=pow(max(dot(n,normalize(vec2(0.5,0.8))),0.0),18.0);surfCol+=vec3(spec)*0.4;vec3 haloCol=mix(blue,lblue,0.6),col=mix(haloCol,surfCol,surface);float a1=clamp(surface*0.92+halo*0.35,0.0,0.95);gl_FragColor=vec4(col,a1);}`;
const FS_CRYSTAL = `vec2 hash22b(vec2 p){vec3 p3=fract(vec3(p.xyx)*vec3(0.1031,0.1030,0.0973));p3+=dot(p3,p3.yzx+33.33);return fract((p3.xx+p3.yz)*p3.zy);}vec3 voronoiB(vec2 p){vec2 i=floor(p),f=fract(p);float d1=1e9,d2=1e9;vec2 ic=vec2(0.0);for(int y=-1;y<=1;y++){for(int x=-1;x<=1;x++){vec2 g=vec2(float(x),float(y)),o=hash22b(i+g);o=0.5+0.5*sin(uTime*0.5+6.28*o);vec2 r=g+o-f;float d=dot(r,r);if(d<d1){d2=d1;d1=d;ic=i+g;}else if(d<d2)d2=d;}}return vec3(sqrt(d1),sqrt(d2),hash21(ic));}void main(){vec2 frag=gl_FragCoord.xy;float scale=min(uRes.x,uRes.y);vec2 uv=frag/scale,m=uMouse/scale,d=uv-m,uvw=uv+d*0.45*exp(-length(d)*2.2);vec3 vd=voronoiB(uvw*3.5);float d1=vd.x,d2=vd.y,cellId=vd.z,edge=d2-d1;vec3 navy=vec3(0.102,0.122,0.361),blue=vec3(0.420,0.561,0.831),lblue=vec3(0.659,0.753,0.910),pale=vec3(0.85,0.90,0.97),fill=mix(pale,lblue,cellId*0.7);float boundary=smoothstep(0.0,0.35,d1);fill=mix(fill,blue,boundary*0.55);float edgeMask=1.0-smoothstep(0.0,0.035,edge);vec3 edgeCol=mix(navy,blue,sin(uTime*0.3+cellId*8.0)*0.5+0.5);vec3 mvd=voronoiB(m*3.5);float hovered=step(abs(mvd.z-cellId),0.001),caustic=smoothstep(0.5,0.95,sin(d1*28.0+uTime+cellId*14.0))*(1.0-edgeMask)*0.3,col_=fill+edgeCol*edgeMask+lblue*caustic;vec3 col=mix(col_,mix(blue,lblue,0.4),hovered*(1.0-edgeMask)*0.45);float fillAlpha=0.18+boundary*0.18+hovered*0.12,a1=clamp(max(fillAlpha,edgeMask*0.85),0.0,0.85);gl_FragColor=vec4(col,a1);}`;
function compile(type,src){const s=gl.createShader(type);gl.shaderSource(s,src);gl.compileShader(s);if(!gl.getShaderParameter(s,gl.COMPILE_STATUS)){console.error('GL compile:',gl.getShaderInfoLog(s));return null;}return s;}
function makeProgram(fsSrc){const vs=compile(gl.VERTEX_SHADER,VS_SRC),fs=compile(gl.FRAGMENT_SHADER,FRAG_HEADER+'\n'+fsSrc);if(!vs||!fs)return null;const p=gl.createProgram();gl.attachShader(p,vs);gl.attachShader(p,fs);gl.linkProgram(p);if(!gl.getProgramParameter(p,gl.LINK_STATUS)){console.error('GL link:',gl.getProgramInfoLog(p));return null;}return p;}
const buf=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,buf);gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),gl.STATIC_DRAW);
const programs={};
function getProgram(name){if(programs[name])return programs[name];let src;if(name==='plasma')src=FS_PLASMA;else if(name==='mercury')src=FS_MERCURY;else if(name==='crystal')src=FS_CRYSTAL;else return null;const p=makeProgram(src);if(!p)return null;programs[name]={prog:p,uRes:gl.getUniformLocation(p,'uRes'),uTime:gl.getUniformLocation(p,'uTime'),uMouse:gl.getUniformLocation(p,'uMouse'),a_pos:gl.getAttribLocation(p,'a_pos')};return programs[name];}
gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);
let glMode='off',glRunning=false,glRafId=null;
const dpr=Math.min(window.devicePixelRatio||1,2),mouse={x:-9999,y:-9999},mouseSmooth={x:0,y:0};
function resize(){const rect=canvas.parentElement.getBoundingClientRect();const w=Math.floor(rect.width*dpr),h=Math.floor(rect.height*dpr);if(canvas.width!==w||canvas.height!==h){canvas.width=w;canvas.height=h;canvas.style.width=rect.width+'px';canvas.style.height=rect.height+'px';gl.viewport(0,0,w,h);}}
canvas.parentElement.addEventListener('mousemove',e=>{const rect=canvas.getBoundingClientRect();mouse.x=(e.clientX-rect.left)*dpr;mouse.y=(rect.height-(e.clientY-rect.top))*dpr;});
canvas.parentElement.addEventListener('mouseleave',()=>{mouse.x=-9999;mouse.y=-9999;});
function initCenterMouse(){const rect=canvas.parentElement.getBoundingClientRect();if(mouse.x<-9000){mouseSmooth.x=(rect.width/2)*dpr;mouseSmooth.y=(rect.height/2)*dpr;}}
const startMs=performance.now();let lastFrame=startMs;
function glLoop(){if(!glRunning)return;resize();const now=performance.now(),dt=(now-lastFrame)/1000;lastFrame=now;const t=(now-startMs)/1000;const target=mouse.x<-9000?{x:canvas.width/2,y:canvas.height/2}:mouse;mouseSmooth.x+=(target.x-mouseSmooth.x)*Math.min(1,dt*5);mouseSmooth.y+=(target.y-mouseSmooth.y)*Math.min(1,dt*5);const p=getProgram(glMode);if(!p){glRafId=requestAnimationFrame(glLoop);return;}gl.useProgram(p.prog);gl.bindBuffer(gl.ARRAY_BUFFER,buf);gl.enableVertexAttribArray(p.a_pos);gl.vertexAttribPointer(p.a_pos,2,gl.FLOAT,false,0,0);gl.uniform2f(p.uRes,canvas.width,canvas.height);gl.uniform1f(p.uTime,t);gl.uniform2f(p.uMouse,mouseSmooth.x,mouseSmooth.y);gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);gl.drawArrays(gl.TRIANGLES,0,6);glRafId=requestAnimationFrame(glLoop);}
function setGLMode(m){if(m===glMode)return;glMode=m;if(m==='off'){glRunning=false;cancelAnimationFrame(glRafId);glRafId=null;gl.clear(gl.COLOR_BUFFER_BIT);return;}if(m==='plasma'||m==='mercury'||m==='crystal'){getProgram(m);initCenterMouse();if(!glRunning){glRunning=true;lastFrame=performance.now();cancelAnimationFrame(glRafId);glLoop();}}}
window.__faSetGLMode=setGLMode;
window.addEventListener('resize',resize);resize();
})();
/* ============================================================
TWEAKS PANEL
============================================================ */
(function() {
const DEFAULTS = {"canvasMode":"mercury","visualMode":"none","emphasis":"underline"};
const state = { ...DEFAULTS };
const panel = document.createElement('div');
panel.id = 'fa-tweaks';
panel.style.cssText = 'position:fixed;bottom:24px;right:24px;z-index:9999;width:280px;background:#fff;color:#1A1F5C;border:0.5px solid rgba(26,31,92,0.15);box-shadow:0 20px 60px -20px rgba(26,31,92,0.35),0 4px 12px -4px rgba(26,31,92,0.1);font-family:"DM Sans",sans-serif;display:none;';
panel.innerHTML = `
<div style="display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:0.5px solid rgba(26,31,92,0.1);cursor:move;" id="fa-tweaks-head">
<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;color:#1A1F5C;">Tweaks</div>
<button id="fa-tweaks-close" style="background:none;border:none;cursor:pointer;font-size:18px;line-height:1;color:#6B7280;padding:0;">×</button>
</div>
<div style="padding:16px 18px;display:flex;flex-direction:column;gap:18px;">
<div><div style="font-size:10px;font-weight:500;letter-spacing:2px;text-transform:uppercase;color:#6B7280;margin-bottom:8px;">Hero Canvas</div>
<div class="fa-seg" data-key="canvasMode" style="display:grid;grid-template-columns:1fr 1fr;gap:1px;background:rgba(26,31,92,0.1);border:0.5px solid rgba(26,31,92,0.1);">
<button data-v="constellation">Constellation</button><button data-v="network">Network</button>
<button data-v="flow">Flow field</button><button data-v="topo">Topografie</button>
<button data-v="plasma">Plasma</button><button data-v="mercury">Mercury</button>
<button data-v="crystal" style="grid-column:span 2;">Crystal</button>
<button data-v="off" style="grid-column:span 2;">Off</button>
</div></div>
<div><div style="font-size:10px;font-weight:500;letter-spacing:2px;text-transform:uppercase;color:#6B7280;margin-bottom:8px;">Right Panel</div>
<div class="fa-seg" data-key="visualMode" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1px;background:rgba(26,31,92,0.1);border:0.5px solid rgba(26,31,92,0.1);">
<button data-v="stack">Stack</button><button data-v="stats">Cards</button><button data-v="none">Keine</button>
</div></div>
<div><div style="font-size:10px;font-weight:500;letter-spacing:2px;text-transform:uppercase;color:#6B7280;margin-bottom:8px;">Headline Emphasis</div>
<div class="fa-seg" data-key="emphasis" style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1px;background:rgba(26,31,92,0.1);border:0.5px solid rgba(26,31,92,0.1);">
<button data-v="underline">Blue+line</button><button data-v="blue">Blue</button><button data-v="flat">All navy</button>
</div></div>
</div>
<style>#fa-tweaks .fa-seg button{background:#fff;border:none;padding:9px 6px;font-family:"DM Sans",sans-serif;font-size:11px;font-weight:500;color:#6B7280;cursor:pointer;transition:all 0.15s;letter-spacing:0.3px;}#fa-tweaks .fa-seg button:hover{color:#1A1F5C;}#fa-tweaks .fa-seg button.active{background:#1A1F5C;color:#fff;}</style>
`;
document.body.appendChild(panel);
function apply() {
if(window.__faSetCanvasMode)window.__faSetCanvasMode(state.canvasMode);
const cap=document.getElementById('fa-cap-card'),stats=document.getElementById('fa-stat-stack'),visual=document.getElementById('fa-hero-visual'),hero=document.querySelector('.hero');
hero.classList.remove('no-visual');
if(state.visualMode==='stack'){cap.style.display='block';stats.style.display='none';visual.style.display='flex';visual.classList.remove('legacy');}
else if(state.visualMode==='stats'){cap.style.display='none';stats.style.display='flex';visual.style.display='flex';visual.classList.add('legacy');}
else{visual.style.display='none';hero.classList.add('no-visual');}
window.dispatchEvent(new Event('resize'));
const h1=document.getElementById('fa-headline'),em=h1.querySelector('em');
h1.classList.remove('flat');em.classList.remove('underline');
if(state.emphasis==='underline')em.classList.add('underline');else if(state.emphasis==='flat')h1.classList.add('flat');
panel.querySelectorAll('.fa-seg').forEach(seg=>{const key=seg.dataset.key;seg.querySelectorAll('button').forEach(b=>{b.classList.toggle('active',b.dataset.v===state[key]);});});
}
panel.querySelectorAll('.fa-seg button').forEach(btn=>{btn.addEventListener('click',()=>{const seg=btn.closest('.fa-seg');state[seg.dataset.key]=btn.dataset.v;apply();try{window.parent.postMessage({type:'__edit_mode_set_keys',edits:{[seg.dataset.key]:btn.dataset.v}},'*');}catch(e){}});});
document.getElementById('fa-tweaks-close').addEventListener('click',()=>{panel.style.display='none';try{window.parent.postMessage({type:'__edit_mode_dismissed'},'*');}catch(e){}});
(function(){const head=document.getElementById('fa-tweaks-head');let dragging=false,sx=0,sy=0,px=0,py=0;head.addEventListener('mousedown',e=>{if(e.target.id==='fa-tweaks-close')return;dragging=true;sx=e.clientX;sy=e.clientY;const r=panel.getBoundingClientRect();px=r.left;py=r.top;panel.style.right='auto';panel.style.bottom='auto';e.preventDefault();});window.addEventListener('mousemove',e=>{if(!dragging)return;panel.style.left=(px+e.clientX-sx)+'px';panel.style.top=(py+e.clientY-sy)+'px';});window.addEventListener('mouseup',()=>dragging=false);})();
window.addEventListener('message',e=>{if(!e.data)return;if(e.data.type==='__activate_edit_mode')panel.style.display='block';if(e.data.type==='__deactivate_edit_mode')panel.style.display='none';});
try{window.parent.postMessage({type:'__edit_mode_available'},'*');}catch(e){}
apply();
})();
</script>