Design-Update 4: Liquid-Metal-Buttons, Glas-Refraktion, neuer Create-Screen mit Fake-Tastatur

- Like/Plus/Nachmachen/Generieren als Liquid-Metal: WebGL-Shader (esm.sh
  @paper-design/shaders, liquidMetalFragmentShader) ueber Metall-Gradient-
  Fallback, schwarzer Kern, graues Icon mit Schattenwurf
- Nav-Glas: bl-glass-refract (feImage-Displacement-Map mit RGB-Split, aus
  Nav-Groesse dynamisch generiert) + blur(3px) saturate(1.15), Weiss .12,
  Ring .4/.3; Layer als echte divs statt Pseudo-Elemente
- Create komplett neu (aus BrandLoop.dc.html): Vorschau-Stack mit Ketten-
  Faecher + Dots + Video-Badge, Optionen-Accordion (Typ/Format/Kette),
  Prompt-Box mit Border-Beam (border-beam.css), Liquid-Metal-Generate,
  Fake-iOS-Tastatur mit @Modell-Tokens (inputmode=none gegen echte Tastatur),
  interner Scroll + body.noscroll auch fuer Create

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
root
2026-07-23 14:36:15 +00:00
parent 5786bcdcf6
commit c1d278b6e5
2 changed files with 464 additions and 82 deletions

178
border-beam.css Normal file
View File

@@ -0,0 +1,178 @@
@property --beam-angle-bb {
syntax: "<angle>";
initial-value: 0deg;
inherits: true;
}
@property --beam-opacity-bb {
syntax: "<number>";
initial-value: 0;
inherits: true;
}
[data-beam="bb"] {
position: relative;
border-radius: 24px;
overflow: hidden;
}
[data-beam="bb"][data-active] {
animation:
beam-spin-bb 1.96s linear infinite,
beam-fade-in-bb 0.6s ease forwards;
}
[data-beam="bb"][data-active]::after {
content: "";
position: absolute;
inset: 0;
border-radius: 23px;
padding: 1px;
clip-path: inset(0 round 24px);
background: conic-gradient(
from var(--beam-angle-bb),
transparent 0%, transparent 54%,
rgba(255, 255, 255, 0.1) 57%,
rgba(255, 255, 255, 0.3) 60%,
rgba(255, 255, 255, 0.6) 63%,
rgba(255, 255, 255, 0.75) 66%,
rgba(255, 255, 255, 0.6) 69%,
rgba(255, 255, 255, 0.3) 72%,
rgba(255, 255, 255, 0.1) 75%,
transparent 78%, transparent 100%
),radial-gradient(ellipse 70px 40px at 33% -7.4%, rgb(255, 50, 100), transparent),
radial-gradient(ellipse 60px 35px at 12% -5%, rgb(40, 140, 255), transparent),
radial-gradient(ellipse 40px 70px at 2.1% 68.3%, rgb(50, 200, 80), transparent),
radial-gradient(ellipse 20px 35px at 2.1% 68.3%, rgb(30, 185, 170), transparent),
radial-gradient(ellipse 180px 32px at 74.4% 100%, rgb(100, 70, 255), transparent),
radial-gradient(ellipse 85px 26px at 55% 100%, rgb(40, 140, 255), transparent),
radial-gradient(ellipse 74px 32px at 93.9% 0%, rgb(255, 120, 40), transparent),
radial-gradient(ellipse 26px 42px at 100% 27.1%, rgb(240, 50, 180), transparent),
radial-gradient(ellipse 52px 48px at 100% 27.1%, rgb(180, 40, 240), transparent);
-webkit-mask:
conic-gradient(
from var(--beam-angle-bb),
transparent 0%, transparent 30%,
rgba(255, 255, 255, 0.1) 36%, rgba(255, 255, 255, 0.35) 44%,
white 52%, white 80%,
rgba(255, 255, 255, 0.35) 86%, rgba(255, 255, 255, 0.1) 92%,
transparent 95%, transparent 100%
),
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: source-in, xor;
mask:
conic-gradient(
from var(--beam-angle-bb),
transparent 0%, transparent 30%,
rgba(255, 255, 255, 0.1) 36%, rgba(255, 255, 255, 0.35) 44%,
white 52%, white 80%,
rgba(255, 255, 255, 0.35) 86%, rgba(255, 255, 255, 0.1) 92%,
transparent 95%, transparent 100%
),
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask-composite: intersect, exclude;
pointer-events: none;
z-index: 2;
opacity: calc(var(--beam-opacity-bb) * 0.26 * var(--beam-stroke-opacity, 1) * var(--beam-strength, 1));
animation: beam-hue-shift-bb 12s ease-in-out infinite;
}
[data-beam="bb"][data-active]::before {
content: "";
position: absolute;
inset: 0;
border-radius: 24px;
background: radial-gradient(ellipse 63px 36px at 33% -7.4%, rgba(255, 50, 100, 0.45), transparent),
radial-gradient(ellipse 54px 32px at 12% -5%, rgba(40, 140, 255, 0.45), transparent),
radial-gradient(ellipse 36px 63px at 2.1% 68.3%, rgba(50, 200, 80, 0.45), transparent),
radial-gradient(ellipse 18px 32px at 2.1% 68.3%, rgba(30, 185, 170, 0.45), transparent),
radial-gradient(ellipse 162px 29px at 74.4% 100%, rgba(100, 70, 255, 0.45), transparent),
radial-gradient(ellipse 77px 23px at 55% 100%, rgba(40, 140, 255, 0.45), transparent),
radial-gradient(ellipse 67px 29px at 93.9% 0%, rgba(255, 120, 40, 0.45), transparent),
radial-gradient(ellipse 23px 38px at 100% 27.1%, rgba(240, 50, 180, 0.45), transparent),
radial-gradient(ellipse 47px 43px at 100% 27.1%, rgba(180, 40, 240, 0.45), transparent);
box-shadow: inset 0 0 9px 1px rgba(255, 255, 255, 0.27);
-webkit-mask-image:
conic-gradient(
from var(--beam-angle-bb),
transparent 0%, transparent 30%,
rgba(255, 255, 255, 0.1) 36%, rgba(255, 255, 255, 0.35) 44%,
white 52%, white 80%,
rgba(255, 255, 255, 0.35) 86%, rgba(255, 255, 255, 0.1) 92%,
transparent 95%, transparent 100%
),
linear-gradient(white, transparent 28px, transparent calc(100% - 28px), white),
linear-gradient(to right, white, transparent 28px, transparent calc(100% - 28px), white);
-webkit-mask-composite: source-in, source-over;
mask-image:
conic-gradient(
from var(--beam-angle-bb),
transparent 0%, transparent 30%,
rgba(255, 255, 255, 0.1) 36%, rgba(255, 255, 255, 0.35) 44%,
white 52%, white 80%,
rgba(255, 255, 255, 0.35) 86%, rgba(255, 255, 255, 0.1) 92%,
transparent 95%, transparent 100%
),
linear-gradient(white, transparent 28px, transparent calc(100% - 28px), white),
linear-gradient(to right, white, transparent 28px, transparent calc(100% - 28px), white);
mask-composite: intersect, add;
pointer-events: none;
z-index: 1;
opacity: calc(var(--beam-opacity-bb) * 0.42 * var(--beam-inner-opacity, 1) * var(--beam-strength, 1));
clip-path: inset(0 round 24px);
animation: beam-hue-shift-bb 12s ease-in-out infinite;
}
[data-beam="bb"] [data-beam-bloom] {
display: none;
position: absolute;
inset: 0;
border-radius: 23px;
clip-path: inset(0 round 24px);
background: conic-gradient(
from var(--beam-angle-bb),
transparent 0%, transparent 58%,
rgba(255, 255, 255, 0.03) 62%,
rgba(255, 255, 255, 0.08) 65%,
rgba(255, 255, 255, 0.2) 67%,
rgba(255, 255, 255, 0.45) 69%,
rgba(255, 255, 255, 0.85) 70%,
rgba(255, 255, 255, 0.85) 70.5%,
rgba(255, 255, 255, 0.45) 71.5%,
rgba(255, 255, 255, 0.2) 73%,
rgba(255, 255, 255, 0.08) 75%,
rgba(255, 255, 255, 0.03) 78%,
transparent 82%
);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
padding: 1px;
filter: blur(8px) brightness(1.30) saturate(1.20);
pointer-events: none;
z-index: 3;
opacity: 0;
}
[data-beam="bb"][data-active] [data-beam-bloom] {
display: block;
opacity: calc(var(--beam-opacity-bb) * 0.24 * var(--beam-bloom-opacity, 1) * var(--beam-strength, 1));
}
@keyframes beam-spin-bb {
to { --beam-angle-bb: 360deg; }
}
@keyframes beam-fade-in-bb {
to { --beam-opacity-bb: 1; }
}
@keyframes beam-hue-shift-bb {
0% { filter: hue-rotate(calc(var(--beam-hue-base, 0deg) - 30deg)) brightness(1.30) saturate(1.20); }
50% { filter: hue-rotate(calc(var(--beam-hue-base, 0deg) + 30deg)) brightness(1.30) saturate(1.20); }
100% { filter: hue-rotate(calc(var(--beam-hue-base, 0deg) - 30deg)) brightness(1.30) saturate(1.20); }
}

View File

@@ -5,6 +5,22 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#0a0a0a">
<title>BrandLoop Prototyp</title>
<link rel="stylesheet" href="border-beam.css">
<script type="module">import('https://esm.sh/@paper-design/shaders').then(function(m){window.__paperShaders=m;var u={u_repetition:4,u_softness:.5,u_shiftRed:.3,u_shiftBlue:.3,u_distortion:0,u_contour:0,u_angle:45,u_scale:8,u_shape:1,u_offsetX:.1,u_offsetY:-.1};setInterval(function(){document.querySelectorAll('.lm-shader:not([data-lm])').forEach(function(el){el.dataset.lm='1';try{new m.ShaderMount(el,m.liquidMetalFragmentShader,u,undefined,0.6)}catch(e){}})},500)}).catch(function(){})</script>
<style>
/* Liquid-Metal-Buttons (aus BrandLoop.dc.html): Shader-Canvas ueber
Metall-Gradient (Fallback ohne WebGL/offline), schwarzer Kern, graues Icon */
.lm-shader canvas{width:100%!important;height:100%!important;display:block!important;position:absolute!important;top:0!important;left:0!important;border-radius:100px!important;}
.lm-bg{position:absolute; inset:0; border-radius:100px; overflow:hidden;
background:linear-gradient(135deg,#e8e8e8,#6f6f6f 35%,#f2f2f2 50%,#5c5c5c 68%,#cfcfcf);}
.lm-bg.deg120{background:linear-gradient(120deg,#e8e8e8,#6f6f6f 35%,#f2f2f2 50%,#5c5c5c 68%,#cfcfcf);}
.lm-core{position:absolute; inset:2px; border-radius:100px; background:linear-gradient(180deg,#202020,#000);}
.lm-ic{position:absolute; inset:0; display:grid; place-items:center;}
.lm-btn{position:relative; height:52px; border-radius:100px; cursor:pointer;
box-shadow:0 2px 5px rgba(0,0,0,.35), 0 9px 9px rgba(0,0,0,.12);}
.lm-label{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; gap:8px;
font-size:14.5px; font-weight:700; color:#9a9a9a; text-shadow:0 1px 2px rgba(0,0,0,.5);}
</style>
<style>
:root{
--bg:#0a0a0a;
@@ -226,11 +242,33 @@
display:grid; place-items:center; cursor:pointer; font-size:20px; color:rgba(255,255,255,.8); transition:transform .12s;}
.x-act:active{transform:scale(.92);}
.x-act.sm{width:44px; height:44px;}
.x-act.like{background:var(--accent); border:none; box-shadow:0 0 22px rgba(255,92,57,.27);}
.x-act.like{position:relative; background:none; border:none;
box-shadow:0 2px 5px rgba(0,0,0,.4), 0 9px 9px rgba(0,0,0,.15);}
/* ================= DETAIL & CREATE ================= */
#detail.active, #create.active{min-height:100dvh;
background:radial-gradient(125% 125% at 50% 150%, rgba(241,99,99,.21) 40%, transparent 100%);}
/* Create = fixer Vollbild-Screen mit internem Scroll + Fake-Tastatur */
#create.active{display:flex; flex-direction:column; height:100dvh; overflow:hidden; position:relative;}
.c-scroll{flex:1; overflow-y:auto; min-height:0; margin:0 -20px; padding:8px 18px 110px; scrollbar-width:none;
transition:padding-bottom .3s ease;}
.c-scroll::-webkit-scrollbar{display:none;}
.c-scroll.kb-open{padding-bottom:380px;}
.c-optlabel{font-size:11px; font-weight:700; letter-spacing:.12em; color:rgba(255,255,255,.4); margin-bottom:9px;}
.c-behind{position:absolute; top:0; left:50%; border-radius:22px; background:#1b1b1b;
border:1px solid rgba(255,255,255,.09); transform-origin:top center; z-index:0;}
.c-preview{position:relative; border-radius:22px; overflow:hidden; z-index:1;
box-shadow:0 18px 44px rgba(0,0,0,.5); transition:width .25s ease, height .25s ease;}
.kb{position:fixed; left:0; right:0; bottom:0; z-index:45; max-width:430px; margin:0 auto;
transform:translateY(112%); transition:transform .34s cubic-bezier(.22,1,.36,1);}
.kb.open{transform:none;}
.kb-row{display:flex; gap:5px; margin-bottom:8px;}
.kb-key{flex:1; height:38px; border-radius:6px; background:rgba(255,255,255,.16); display:grid; place-items:center;
font-size:16px; font-weight:500; color:#fff; cursor:pointer; user-select:none; -webkit-user-select:none; min-width:0;}
.kb-key.mod{background:rgba(255,255,255,.28); font-size:15px;}
.kb-chip{flex:none; display:flex; align-items:center; gap:7px; padding:8px 13px; border-radius:100px;
background:rgba(255,255,255,.12); font-size:12.5px; font-weight:600; cursor:pointer; white-space:nowrap; color:#fff;}
.kb-chip .dot{width:8px; height:8px; border-radius:100px; flex:none;}
.d-strip{display:flex; gap:10px; overflow-x:auto; margin:0 -18px; padding:0 18px; scrollbar-width:none; scroll-snap-type:x mandatory;}
.d-strip::-webkit-scrollbar{display:none;}
.d-img{flex:none; width:272px; height:330px; border-radius:20px; position:relative; display:grid; place-items:center; scroll-snap-align:center;}
@@ -256,28 +294,28 @@
.p-hero p{color:var(--mut); font-size:13px; margin-top:3px;}
/* ================= Bottom-Navigation ================= */
/* Liquid-Glass-Nav (aus BrandLoop.dc.html): verzerrter Glas-Backdrop
(bl-glass-distortion mit Specular-Highlight) + heller Weiss-Layer + Kanten-Ring.
bottom:14px OHNE Safe-Area-Aufschlag = exakt die Hoehe aus dem Design. */
/* Liquid-Glass-Nav (aus BrandLoop.dc.html): echte Refraktion via dynamischer
Displacement-Map (bl-glass-refract, RGB-Split) + Blur/Saturate-Backdrop +
Weiss-Layer + Kanten-Ring. bottom:14px = exakt die Hoehe aus dem Design. */
.nav{position:fixed; bottom:14px; left:50%; transform:translateX(-50%); z-index:30;
display:flex; align-items:center; gap:6px; padding:8px 10px; border-radius:100px;
background:rgba(255,255,255,.25);
box-shadow:0 6px 6px rgba(0,0,0,.2), 0 0 20px rgba(0,0,0,.1);
transition:all .7s cubic-bezier(.175,.885,.32,2.2);}
.nav::before{content:''; position:absolute; inset:0; z-index:-1; border-radius:100px; overflow:hidden;
-webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
filter:url(#bl-glass-distortion); isolation:isolate;}
.nav::after{content:''; position:absolute; inset:0; z-index:2; border-radius:100px; pointer-events:none;
box-shadow:inset 2px 2px 1px 0 rgba(255,255,255,.5), inset -1px -1px 1px 1px rgba(255,255,255,.5);}
.nav button{display:flex; align-items:center; gap:8px; background:none; border:none; font-family:inherit;
.nav-glass{position:absolute; inset:0; border-radius:100px;}
.nav-glass.blur{overflow:hidden; -webkit-backdrop-filter:blur(3px) saturate(1.15); backdrop-filter:blur(3px) saturate(1.15);}
.nav-glass.refract{overflow:hidden; -webkit-backdrop-filter:url(#bl-glass-refract); backdrop-filter:url(#bl-glass-refract);}
.nav-glass.white{background:rgba(255,255,255,.12); z-index:1;}
.nav-glass.ring{z-index:2; pointer-events:none;
box-shadow:inset 2px 2px 1px 0 rgba(255,255,255,.4), inset -1px -1px 1px 1px rgba(255,255,255,.3);}
.nav button{position:relative; z-index:3; display:flex; align-items:center; gap:8px; background:none; border:none; font-family:inherit;
width:40px; height:40px; justify-content:center; border-radius:100px; color:rgba(255,255,255,.6);
font-size:13.5px; font-weight:600; cursor:pointer; transition:background .2s, color .2s, width .2s;}
.nav button svg{width:20px; height:20px; flex:none;}
.nav button span{display:none;}
.nav button.on{width:auto; padding:9px 16px 9px 12px; background:rgba(255,255,255,.14); color:#fff;}
.nav button.on span{display:inline;}
.nav button.plus{background:var(--accent); color:#0a0a0a;}
.nav button.plus svg{stroke-width:2.4; width:18px; height:18px;}
/* Plus-Button = Liquid-Metal (Shader + schwarzer Kern) */
.nav button.plus{background:none; box-shadow:0 2px 5px rgba(0,0,0,.35);}
.nav button.plus.on{width:40px; padding:0;}
.nav button.plus.on span{display:none;}
@@ -302,19 +340,17 @@
<!-- Lucide-Icon-Sprite + Liquid-Glass-Filter -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<defs>
<filter id="bl-glass-distortion" x="0%" y="0%" width="100%" height="100%" filterUnits="objectBoundingBox">
<feTurbulence type="fractalNoise" baseFrequency="0.001 0.005" numOctaves="1" seed="17" result="turbulence"/>
<feComponentTransfer in="turbulence" result="mapped">
<feFuncR type="gamma" amplitude="1" exponent="10" offset="0.5"/>
<feFuncG type="gamma" amplitude="0" exponent="1" offset="0"/>
<feFuncB type="gamma" amplitude="0" exponent="1" offset="0.5"/>
</feComponentTransfer>
<feGaussianBlur in="turbulence" stdDeviation="3" result="softMap"/>
<feSpecularLighting in="softMap" surfaceScale="5" specularConstant="1" specularExponent="100" lighting-color="white" result="specLight">
<fePointLight x="-200" y="-200" z="300"/>
</feSpecularLighting>
<feComposite in="specLight" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="litImage"/>
<feDisplacementMap in="SourceGraphic" in2="softMap" scale="200" xChannelSelector="R" yChannelSelector="G"/>
<filter id="bl-glass-refract" color-interpolation-filters="sRGB" x="0%" y="0%" width="100%" height="100%">
<feImage id="glassMapImage" x="0" y="0" width="100%" height="100%" preserveAspectRatio="none" href="" result="map"/>
<feDisplacementMap in="SourceGraphic" in2="map" scale="-180" xChannelSelector="R" yChannelSelector="G" result="dispRed"/>
<feColorMatrix in="dispRed" type="matrix" values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" result="red"/>
<feDisplacementMap in="SourceGraphic" in2="map" scale="-170" xChannelSelector="R" yChannelSelector="G" result="dispGreen"/>
<feColorMatrix in="dispGreen" type="matrix" values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0" result="green"/>
<feDisplacementMap in="SourceGraphic" in2="map" scale="-160" xChannelSelector="R" yChannelSelector="G" result="dispBlue"/>
<feColorMatrix in="dispBlue" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0" result="blue"/>
<feBlend in="red" in2="green" mode="screen" result="rg"/>
<feBlend in="rg" in2="blue" mode="screen" result="out"/>
<feGaussianBlur in="out" stdDeviation="0.7"/>
</filter>
<symbol id="i-back" viewBox="0 0 24 24"><path d="m15 18-6-6 6-6"/></symbol>
<symbol id="i-chev" viewBox="0 0 24 24"><path d="m9 18 6-6-6-6"/></symbol>
@@ -359,7 +395,11 @@
<div class="x-actions">
<div class="x-act" id="btnSkip" role="button" aria-label="Skip"></div>
<div class="x-act sm" id="btnDetail" role="button" aria-label="Details / Nachmachen"><svg class="ic" style="width:18px;height:18px;color:rgba(255,255,255,.8)"><use href="#i-refresh"/></svg></div>
<div class="x-act like" id="btnLike" role="button" aria-label="Like"><svg width="24" height="24" viewBox="0 0 24 24" fill="#0a0a0a"><path d="M12 21 C5 15.5 2.5 12 2.5 8.5 A4.7 4.7 0 0 1 12 6.2 A4.7 4.7 0 0 1 21.5 8.5 C21.5 12 19 15.5 12 21 Z"/></svg></div>
<div class="x-act like" id="btnLike" role="button" aria-label="Like">
<div class="lm-shader lm-bg"></div>
<div class="lm-core"></div>
<div class="lm-ic"><svg width="24" height="24" viewBox="0 0 24 24" fill="#9a9a9a" style="filter:drop-shadow(0 1px 2px rgba(0,0,0,.5))"><path d="M12 21 C5 15.5 2.5 12 2.5 8.5 A4.7 4.7 0 0 1 12 6.2 A4.7 4.7 0 0 1 21.5 8.5 C21.5 12 19 15.5 12 21 Z"/></svg></div>
</div>
</div>
</div>
@@ -384,41 +424,50 @@
</div>
<div style="font-size:15px; font-weight:800; margin-bottom:10px;">Mit deinem Modell nachmachen</div>
<div class="chiprow" id="detailModels"></div>
<button class="btn" id="remakeBtn" style="margin-top:16px;"></button>
<div class="lm-btn" id="remakeBtn" role="button" style="margin-top:16px;">
<div class="lm-shader lm-bg deg120"></div>
<div class="lm-core"></div>
<div class="lm-label"><svg width="15" height="15" viewBox="0 0 24 24" fill="#9a9a9a" style="filter:drop-shadow(0 1px 2px rgba(0,0,0,.5))"><path d="M12 2 L14.4 9.6 L22 12 L14.4 14.4 L12 22 L9.6 14.4 L2 12 L9.6 9.6 Z"/></svg><span id="remakeLabel"></span></div>
</div>
<div style="text-align:center; font-size:11.5px; color:rgba(255,255,255,.4); margin-top:10px;">Prompt + Einstellungen werden übernommen, dein Modell wird eingesetzt</div>
</div>
<!-- ============ POST ERSTELLEN (neu) ============ -->
<!-- ============ POST ERSTELLEN (neu, aus BrandLoop.dc.html v4) ============ -->
<div class="screen" id="create">
<div class="topbar">
<button class="backpill" onclick="go('explore')"><span class="ar"></span> Zurück</button>
<h1 style="flex:1; text-align:center; margin-right:80px;">Neuer KI-Post</h1>
</div>
<label style="margin:6px 0 2px;">Prompt</label>
<textarea id="createPrompt" placeholder="Beschreibe deinen Post … z.B. Sneaker auf Betonboden, goldenes Abendlicht"></textarea>
<label>Dein Modell</label>
<div class="chiprow" id="createModels" style="margin-top:8px;"></div>
<div style="display:flex; gap:24px; margin-top:18px;">
<div style="flex:1;">
<label style="margin-bottom:8px;">Kette</label>
<div class="pill-row" style="margin-top:0;" id="countRow">
<div class="pill" data-count="1">1 Bild</div>
<div class="pill on" data-count="3">3er</div>
<div class="pill" data-count="5">5er</div>
<div class="c-scroll" id="cScroll">
<div style="display:flex; flex-direction:column; align-items:center; padding:10px 0 4px;">
<div id="cStack" onclick="toggleOpts()" style="position:relative; cursor:pointer;"></div>
<div onclick="toggleOpts()" style="display:flex; align-items:center; gap:8px; margin-top:14px; padding:9px 10px 9px 16px; border-radius:100px; background:rgba(255,255,255,.08); cursor:pointer;">
<span style="font-size:13px; font-weight:600;" id="cSelLabel"></span>
<svg id="cChevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,.6)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="transition:transform .2s;"><path d="M6 9 l6 6 l6 -6"/></svg>
</div>
<div id="cOpts" style="display:none; width:100%; margin-top:14px; border-radius:20px; background:#161616; border:1px solid rgba(255,255,255,.09); padding:16px; box-sizing:border-box;">
<div class="c-optlabel">TYP</div>
<div class="pill-row" style="margin:0 0 16px;" id="typeRow"></div>
<div class="c-optlabel">FORMAT</div>
<div class="pill-row" style="margin:0 0 16px;" id="formatRow"></div>
<div class="c-optlabel">KETTE</div>
<div class="pill-row" style="margin:0;" id="countRow"></div>
</div>
</div>
<div style="flex:1;">
<label style="margin-bottom:8px;">Format</label>
<div class="pill-row" style="margin-top:0;" id="formatRow">
<div class="pill" data-format="1:1">1:1</div>
<div class="pill on" data-format="4:5">4:5</div>
<div class="pill" data-format="9:16">9:16</div>
<label style="margin:22px 0 8px;">Dein Modell</label>
<div class="chiprow" id="createModels"></div>
<label style="margin:20px 0 8px;">Prompt</label>
<div data-beam="bb" data-active="true" style="border-radius:24px;">
<div style="border-radius:24px; background:#171717; border:1px solid rgba(255,255,255,.05); box-shadow:0 12px 34px rgba(0,0,0,.4);">
<textarea id="createPrompt" inputmode="none" placeholder="Beschreibe deinen Post … z.B. Sneaker auf Betonboden, goldenes Abendlicht" style="display:block; width:100%; box-sizing:border-box; height:104px; resize:none; border:none; border-radius:23px; background:transparent; color:#fff; font-size:13.5px; line-height:1.55; padding:16px 18px; outline:none;"></textarea>
</div>
<div data-beam-bloom="true"></div>
</div>
<div class="lm-btn" id="generateBtn" role="button" style="margin-top:22px;">
<div class="lm-shader lm-bg deg120"></div>
<div class="lm-core"></div>
<div class="lm-label"><svg width="15" height="15" viewBox="0 0 24 24" fill="#9a9a9a" style="filter:drop-shadow(0 1px 2px rgba(0,0,0,.5))"><path d="M12 2 L14.4 9.6 L22 12 L14.4 14.4 L12 22 L9.6 14.4 L2 12 L9.6 9.6 Z"/></svg><span id="generateLabel">Post generieren</span></div>
</div>
<label style="margin:20px 0 8px;">Vorschau der Kette</label>
<div class="frames" id="previewFrames"></div>
<button class="btn" id="generateBtn" style="margin-top:22px;">Post generieren</button>
<h3 style="margin-top:28px;">Mehr erstellen</h3>
<div class="card click" onclick="go('tut1')">
@@ -435,6 +484,16 @@
<svg class="ic chev"><use href="#i-chev"/></svg>
</div>
</div>
</div><!-- /c-scroll -->
<!-- Fake-Tastatur (Prototyp): erscheint beim Fokus aufs Prompt-Feld -->
<div class="kb" id="kbOverlay">
<div style="display:flex; align-items:center; gap:8px; padding:10px 12px; background:rgba(24,24,24,.96); border-top:1px solid rgba(255,255,255,.08);">
<div style="display:flex; gap:8px; overflow-x:auto; flex:1; scrollbar-width:none;" id="kbModels"></div>
<div id="kbDone" style="flex:none; padding:8px 14px; border-radius:100px; background:rgba(255,255,255,.14); font-size:12px; font-weight:700; cursor:pointer;">Fertig</div>
</div>
<div style="padding:8px 4px 24px; background:rgba(30,30,32,.98); backdrop-filter:blur(20px); -webkit-backdrop-filter:blur(20px);" id="kbKeys"></div>
</div>
</div>
<!-- ============ PROFIL / MENSCHLEIN (neu, enthält Knowledge Base) ============ -->
@@ -841,10 +900,18 @@ TEXT-OVERLAY: keins (Brand-Stil: clean)</textarea>
</div>
<!-- ============ NAVIGATION (neu: Feed · Profil · Plus · Dev) ============ -->
<nav class="nav">
<nav class="nav" id="navBar">
<div class="nav-glass blur"></div>
<div class="nav-glass refract"></div>
<div class="nav-glass white"></div>
<div class="nav-glass ring"></div>
<button id="n-feed" class="on" onclick="go('explore')"><svg class="ic"><use href="#i-home"/></svg><span>Feed</span></button>
<button id="n-profile" onclick="go('profile')"><svg class="ic"><use href="#i-user"/></svg><span>Profil</span></button>
<button id="n-create" class="plus" onclick="go('create')" aria-label="Neuer KI-Post"><svg class="ic"><use href="#i-plus"/></svg></button>
<button id="n-create" class="plus" onclick="go('create')" aria-label="Neuer KI-Post">
<div class="lm-shader lm-bg"></div>
<div class="lm-core"></div>
<div class="lm-ic"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#9a9a9a" stroke-width="2.4" stroke-linecap="round" style="filter:drop-shadow(0 1px 2px rgba(0,0,0,.5))"><path d="M12 5 V19 M5 12 H19"/></svg></div>
</button>
<button id="n-dev" onclick="go('devmode')" style="opacity:.55;" title="Nur intern sichtbar"><svg class="ic"><use href="#i-gear"/></svg><span>Dev</span></button>
</nav>
@@ -880,7 +947,7 @@ function go(id){
};
document.querySelectorAll('.nav button').forEach(b=>b.classList.remove('on'));
if(navMap[id]) document.getElementById(navMap[id]).classList.add('on');
document.body.classList.toggle('noscroll', id==='explore');
document.body.classList.toggle('noscroll', id==='explore'||id==='create');
window.scrollTo(0,0);
}
function pick(el){
@@ -924,7 +991,7 @@ function preloadPics(){
POSTS.forEach(function(p){ (p.pics||[]).forEach(function(u){ all[u]=1; }); if(p.avatar) all[p.avatar]=1; });
Object.keys(all).forEach(function(u){
var im=new Image();
im.onload=function(){ PICS_OK[u]=true; renderDeck(); renderDetailStrip(); };
im.onload=function(){ PICS_OK[u]=true; renderDeck(); renderDetailStrip(); renderCreateStack(); };
im.src=u;
});
}
@@ -1148,7 +1215,7 @@ document.querySelectorAll('.x-tab').forEach(function(el){
});
});
window.addEventListener('resize',function(){
measureTabs(); setHood(centerOf(X.tab), halfOf(X.tab));
measureTabs(); setHood(centerOf(X.tab), halfOf(X.tab)); updateGlassMap();
});
document.getElementById('btnSkip').onclick=function(){ fling(-1); };
document.getElementById('btnLike').onclick=function(){ fling(1); };
@@ -1184,9 +1251,8 @@ function renderDetailModels(){
host.querySelectorAll('.chip').forEach(function(c){
c.onclick=function(){ D.model=+c.dataset.i; D.remade=false; renderDetailModels(); };
});
var btn=document.getElementById('remakeBtn');
btn.textContent=D.remade ? '✓ Übernommen — im Editor geöffnet' : 'Nachmachen mit „'+MODELS[D.model].split(' ')[1]+'“';
btn.classList.toggle('sec', D.remade);
document.getElementById('remakeLabel').textContent=
D.remade ? '✓ Übernommen — im Editor geöffnet' : 'Nachmachen mit „'+MODELS[D.model].split(' ')[1]+'“';
}
document.getElementById('remakeBtn').onclick=function(){
if(D.remade) return;
@@ -1199,49 +1265,187 @@ document.getElementById('remakeBtn').onclick=function(){
},900);
};
/* ---------- Create: Modell / Kette / Format / Vorschau ---------- */
var C={model:0, count:3, format:'4:5', generating:0};
var RATIOS={'1:1':[130,130],'4:5':[124,155],'9:16':[96,170]};
/* ---------- Create: Vorschau-Stack / Optionen / Prompt-Beam / Fake-Tastatur ---------- */
var C={model:0, count:1, format:'4:5', mediaType:'image', optOpen:false, kbOpen:false, generating:0};
var C_MODELS=[
{name:'Ich (Avatar)', token:'@Ich', hue:260},
{name:'Kerze Studio', token:'@Kerze', hue:40},
{name:'Showroom', token:'@Showroom', hue:170}
];
function renderCreateStack(){
var host=document.getElementById('cStack');
if(!host) return;
var isVideo=C.mediaType==='video';
var isChain=!isVideo&&C.count>1;
var pr={'1:1':1,'4:5':.8,'9:16':.5625}[C.format];
var ph=258, pw=Math.round(ph*pr);
var cp=POSTS[1], u=pic(cp,0);
var bg=u?'#141414 url(&quot;'+u+'&quot;) center / cover no-repeat':dummy(150);
var html='';
var nBehind=isChain?Math.min(C.count-1,2):0;
for(var k=nBehind;k>=1;k--){
html+='<div class="c-behind" style="width:'+pw+'px; height:'+ph+'px; transform:translateX(-50%) rotate('+(k===1?-6:6)+'deg) translateY('+(4*k)+'px) scale('+(1-.04*k)+');"></div>';
}
html+='<div class="c-preview" style="width:'+pw+'px; height:'+ph+'px; background:'+bg+';">';
if(isVideo){
html+='<div style="position:absolute; inset:0; display:grid; place-items:center; pointer-events:none;"><div style="width:62px; height:62px; border-radius:100px; background:rgba(10,10,10,.5); backdrop-filter:blur(8px); display:grid; place-items:center;"><svg width="20" height="20" viewBox="0 0 24 24" fill="#fff"><path d="M8 5 L19 12 L8 19 Z"/></svg></div></div>';
html+='<div style="position:absolute; top:12px; left:12px; padding:5px 10px; border-radius:100px; background:rgba(10,10,10,.55); font-size:11px; font-weight:700; letter-spacing:.06em;">VIDEO · 0:08</div>';
}
if(isChain){
html+='<div style="position:absolute; top:12px; right:12px; padding:5px 10px; border-radius:100px; background:rgba(10,10,10,.55); font-size:11px; font-weight:700;">1/'+C.count+'</div>';
html+='<div style="position:absolute; left:0; right:0; bottom:14px; display:flex; justify-content:center; align-items:center; gap:5px;">';
for(var i=0;i<C.count;i++){
html+='<div style="width:'+(i===0?16:6)+'px; height:6px; border-radius:100px; background:'+(i===0?'#fff':'rgba(255,255,255,.45)')+'; transition:width .2s;"></div>';
}
html+='</div>';
}
html+='</div>';
host.innerHTML=html;
document.getElementById('cSelLabel').textContent = isVideo ? ('Video · '+C.format) : ((C.count===1?'Bild':C.count+'er-Kette')+' · '+C.format);
document.getElementById('cChevron').style.transform=C.optOpen?'rotate(180deg)':'none';
document.getElementById('cOpts').style.display=C.optOpen?'block':'none';
}
function pills(hostId, items, activeVal, onPick){
var host=document.getElementById(hostId);
host.innerHTML=items.map(function(it){
return '<div class="pill'+(it.v===activeVal?' on':'')+'" data-v="'+it.v+'">'+it.label+'</div>';
}).join('');
host.querySelectorAll('.pill').forEach(function(p){
p.onclick=function(){ onPick(p.dataset.v); };
});
}
function renderCreateOpts(){
pills('typeRow',[{v:'image',label:'Bild'},{v:'video',label:'Video'}],C.mediaType,function(v){C.mediaType=v; renderCreateOpts(); renderCreateStack();});
pills('formatRow',[{v:'1:1',label:'1:1'},{v:'4:5',label:'4:5'},{v:'9:16',label:'9:16'}],C.format,function(v){C.format=v; renderCreateOpts(); renderCreateStack();});
pills('countRow',[{v:'1',label:'1 Bild'},{v:'3',label:'3er'},{v:'5',label:'5er'}],String(C.count),function(v){C.count=+v; renderCreateOpts(); renderCreateStack();});
}
function toggleOpts(){ C.optOpen=!C.optOpen; renderCreateStack(); }
function renderCreateModels(){
var host=document.getElementById('createModels');
host.innerHTML=MODELS.map(function(m,i){
return '<div class="chip'+(i===C.model?' on':'')+'" data-i="'+i+'"><div class="dot"></div>'+m+'</div>';
host.innerHTML=C_MODELS.map(function(m,i){
return '<div class="chip'+(i===C.model?' on':'')+'" data-i="'+i+'"><div class="dot"></div>'+m.name+'</div>';
}).join('');
host.querySelectorAll('.chip').forEach(function(c){
c.onclick=function(){ C.model=+c.dataset.i; renderCreateModels(); };
});
}
function renderFrames(){
var r=RATIOS[C.format], host=document.getElementById('previewFrames'), html='';
for(var i=0;i<C.count;i++){
html+='<div class="frame" style="width:'+r[0]+'px; height:'+r[1]+'px;"><span>Bild '+(i+1)+'</span></div>';
}
host.innerHTML=html;
/* Prompt-Editing über die Fake-Tastatur (Caret-genau wie im Design) */
function editPrompt(mutate){
var ta=document.getElementById('createPrompt');
var p=ta.value, s=ta.selectionStart, e=ta.selectionEnd;
var r=mutate(p,s,e);
ta.value=r.val;
ta.focus(); ta.setSelectionRange(r.caret,r.caret);
}
document.querySelectorAll('#countRow .pill').forEach(function(p){
p.onclick=function(){ pick(p); C.count=+p.dataset.count; renderFrames(); };
});
document.querySelectorAll('#formatRow .pill').forEach(function(p){
p.onclick=function(){ pick(p); C.format=p.dataset.format; renderFrames(); };
});
function applyKey(code){
editPrompt(function(p,s,e){
if(code==='del'){
if(s!==e) return {val:p.slice(0,s)+p.slice(e), caret:s};
return {val:p.slice(0,Math.max(0,s-1))+p.slice(e), caret:Math.max(0,s-1)};
}
var ins=code==='space'?' ':code==='ret'?'\n':code;
return {val:p.slice(0,s)+ins+p.slice(e), caret:s+ins.length};
});
}
function insertToken(tok){
editPrompt(function(p,s,e){
var before=p.slice(0,s);
var lead=before&&!/\s$/.test(before)?' ':'';
var ins=lead+tok+' ';
return {val:before+ins+p.slice(e), caret:s+ins.length};
});
}
function setKb(open){
C.kbOpen=open;
document.getElementById('kbOverlay').classList.toggle('open',open);
document.getElementById('cScroll').classList.toggle('kb-open',open);
if(open){
C.optOpen=false; renderCreateStack();
requestAnimationFrame(function(){requestAnimationFrame(function(){
var el=document.getElementById('cScroll'); el.scrollTop=el.scrollHeight;
});});
}
}
function renderKb(){
var mh=document.getElementById('kbModels');
mh.innerHTML=C_MODELS.map(function(m,i){
return '<div class="kb-chip" data-i="'+i+'"><div class="dot" style="background:oklch(.7 .13 '+m.hue+');"></div>'+m.name+'</div>';
}).join('');
mh.querySelectorAll('.kb-chip').forEach(function(c){
function ins(e){ e.preventDefault(); insertToken(C_MODELS[+c.dataset.i].token); }
c.addEventListener('mousedown',ins);
c.addEventListener('touchstart',ins,{passive:false});
});
var rows=[
{pad:0, keys:'qwertyuiop'.split('').map(function(l){return {label:l, code:l};})},
{pad:16, keys:'asdfghjkl'.split('').map(function(l){return {label:l, code:l};})},
{pad:0, keys:[{label:'⇧', code:null, cls:'mod', flex:1.5}].concat('zxcvbnm'.split('').map(function(l){return {label:l, code:l};})).concat([{label:'⌫', code:'del', cls:'mod', flex:1.5}])},
{pad:0, keys:[{label:'123', code:null, cls:'mod', flex:1.6, fs:13},{label:'', code:'space', flex:5},{label:'return', code:'ret', flex:1.9, fs:13, bg:'rgba(122,162,255,.9)'}]}
];
var kh=document.getElementById('kbKeys'); var html='';
rows.forEach(function(row,ri){
html+='<div class="kb-row" style="padding:0 '+row.pad+'px;">';
row.keys.forEach(function(k,ki){
var st='';
if(k.flex) st+='flex:'+k.flex+';';
if(k.fs) st+='font-size:'+k.fs+'px;';
if(k.bg) st+='background:'+k.bg+';';
html+='<div class="kb-key'+(k.cls?' '+k.cls:'')+'" data-r="'+ri+'" data-k="'+ki+'" style="'+st+'">'+k.label+'</div>';
});
html+='</div>';
});
kh.innerHTML=html;
kh.querySelectorAll('.kb-key').forEach(function(el){
var k=rows[+el.dataset.r].keys[+el.dataset.k];
function press(e){ e.preventDefault(); if(k.code) applyKey(k.code); }
el.addEventListener('mousedown',press);
el.addEventListener('touchstart',press,{passive:false});
});
function done(e){ e.preventDefault(); document.getElementById('createPrompt').blur(); setKb(false); }
document.getElementById('kbDone').addEventListener('mousedown',done);
document.getElementById('kbDone').addEventListener('touchstart',done,{passive:false});
}
(function(){
var ta=document.getElementById('createPrompt');
ta.addEventListener('focus',function(){ setKb(true); });
ta.addEventListener('blur',function(){ setKb(false); });
})();
document.getElementById('generateBtn').onclick=function(){
if(C.generating) return;
var btn=this;
C.generating=1; btn.classList.add('sec'); btn.textContent='Generiere '+C.count+' Bilder …';
setTimeout(function(){ C.generating=2; btn.textContent='✓ In der Warteschlange'; },1400);
setTimeout(function(){ C.generating=0; btn.classList.remove('sec'); btn.textContent='Post generieren'; },3200);
var lbl=document.getElementById('generateLabel');
C.generating=1; lbl.textContent='Generiere '+C.count+' Bilder …';
setTimeout(function(){ C.generating=2; lbl.textContent='✓ In der Warteschlange'; },1400);
setTimeout(function(){ C.generating=0; lbl.textContent='Post generieren'; },3200);
};
/* ---------- Nav-Glas: dynamische Displacement-Map (aus dem Design) ---------- */
function updateGlassMap(){
var nav=document.getElementById('navBar'), img=document.getElementById('glassMapImage');
if(!nav||!img) return;
var w=nav.offsetWidth||286, h=nav.offsetHeight||56;
var r=Math.min(100,h/2), e=Math.min(w,h)*0.035;
var svg='<svg viewBox="0 0 '+w+' '+h+'" xmlns="http://www.w3.org/2000/svg">'+
'<defs><linearGradient id="rg" x1="100%" y1="0%" x2="0%" y2="0%"><stop offset="0%" stop-color="#0000"/><stop offset="100%" stop-color="red"/></linearGradient>'+
'<linearGradient id="bg" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stop-color="#0000"/><stop offset="100%" stop-color="blue"/></linearGradient></defs>'+
'<rect width="'+w+'" height="'+h+'" fill="black"/>'+
'<rect width="'+w+'" height="'+h+'" rx="'+r+'" fill="url(#rg)"/>'+
'<rect width="'+w+'" height="'+h+'" rx="'+r+'" fill="url(#bg)" style="mix-blend-mode:difference"/>'+
'<rect x="'+e+'" y="'+e+'" width="'+(w-e*2)+'" height="'+(h-e*2)+'" rx="'+r+'" fill="hsl(0 0% 50% / 0.93)" style="filter:blur(11px)"/></svg>';
img.setAttribute('href','data:image/svg+xml,'+encodeURIComponent(svg));
}
/* ---------- Init ---------- */
setHood(centerOf(X.tab), halfOf(X.tab));
requestAnimationFrame(function(){ measureTabs(); setHood(centerOf(X.tab), halfOf(X.tab)); });
requestAnimationFrame(function(){ measureTabs(); setHood(centerOf(X.tab), halfOf(X.tab)); updateGlassMap(); });
renderTabs();
renderDeck();
renderDetailStrip();
preloadPics();
renderDetailModels();
renderCreateModels();
renderFrames();
renderCreateOpts();
renderCreateStack();
renderKb();
</script>
</body>
</html>