Compare commits

..

8 Commits

Author SHA1 Message Date
root
65a19d7678 Design-Sync: Kapsel-Shape (8) fuer Dither-Chips, Liquid-Metal u_shape 0
Aus prototyp-app.html im Design-Projekt uebernommen: neue GLSL-Shape 8
(aspect-korrigierte Kapsel statt zentrierter Kugel - passt sich der
Pill-Breite an), Chips auf shape:8, Liquid-Metal-Uniform u_shape 1->0.
Bildpfade bleiben .jpg (Design-Kopie basierte auf aelterem .png-Stand).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 19:14:15 +00:00
root
2f2ec31466 Testeffekt: Dithering-Shader (Sphere/Random) auf den Modell-Chips im Create-Screen
WebGL2-Port von designali-in/dithering-shader als Vanilla-Modul (mountDither):
Shader laeuft als Canvas-Layer in den drei Chips, ausgewaehlt = weiss/silber
(#f2f2f2), nicht ausgewaehlt = dunkelgrau (#555); Auswahlwechsel aendert nur
die Front-Farbe (keine neuen GL-Kontexte). Mount lazy beim ersten Oeffnen des
Create-Screens; ohne WebGL2 bleibt der normale Chip-Look.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:02:28 +00:00
root
c1d278b6e5 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>
2026-07-23 14:38:29 +00:00
5786bcdcf6 Update preview config from ticket system 2026-07-22 19:53:57 +00:00
root
0291bd26b9 Fix: Explore-Screen scrollt nicht mehr (body.noscroll + overscroll-behavior)
Der globale .app-padding-bottom machte die Seite hoeher als den Viewport ->
auf dem Feed war vertikales Wippen moeglich. Explore sperrt jetzt body-Scroll
(overflow:hidden, height:100dvh, padding-bottom:0), scrollbare Screens
(Detail/Create/Profil) bleiben unveraendert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 19:52:42 +00:00
root
64b1c8d703 Design-Update 3: neue Liquid-Glass-Nav (bl-glass-distortion), Nav auf Design-Hoehe, Feed-Animationen
- Nav: Specular-Lighting-Glasfilter + Weiss-Layer rgba(255,255,255,.25) +
  Kanten-Ring + Aussenschatten; bottom:14px ohne Safe-Area-Aufschlag
  (= exakt die Hoehe aus BrandLoop.dc.html), Bounce-Transition
- Fling: Top-Karte fadet aus (.3s), Stack rueckt mit Settle-Animation nach
  (.45s cubic-bezier(.22,1,.36,1)), 4 Karten im Stack, Snap-back weicher
- Tab-Wechsel: Deck+Blur Crossfade (180ms), Hood animiert sofort
- Blur-Layer: background .7s ease + opacity .25s ease
- x-actions Abstand auf Design-Wert 84px

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 19:30:06 +00:00
root
8ebe0632bc 5 Demo-Bilder (Pinterest-Auswahl des Nutzers) in Feed/Detail verdrahtet
okolo->Mara2, celimax-Poster->Lena, Balenciaga-Subway->Jonas+Avatar,
MODAILY->Mara3, THE-ROW->Tom1; Bildpfade auf .jpg umgestellt.
Offen: Mara1 (Bogenfenster-Toner), Tom4/Feld1/Kai, Feld2/Nina - naechste Lieferung.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 18:27:12 +00:00
root
342504f481 QR-Code für Expo Go (exps://expo-brandloop.project.webklar.com)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:32:19 +00:00
8 changed files with 762 additions and 104 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); }
}

BIN
expo-qr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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:0,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;
@@ -23,6 +39,10 @@
}
*{box-sizing:border-box; margin:0; padding:0;}
html{-webkit-text-size-adjust:100%;}
html, body{overscroll-behavior-y:none;}
/* Explore ist ein fixer Vollbild-Screen: Seite darf nicht scrollen/wippen */
body.noscroll{overflow:hidden; height:100dvh;}
body.noscroll .app{padding-bottom:0;}
body{
background:var(--bg); color:var(--txt);
font-family:-apple-system,BlinkMacSystemFont,"Helvetica Neue","Inter",Roboto,system-ui,sans-serif;
@@ -178,7 +198,8 @@
#explore{padding:0; --xs:max(env(safe-area-inset-top, 0px), 64px);}
#explore.active{display:flex; flex-direction:column; height:100dvh; overflow:hidden;}
.x-blur{position:absolute; inset:-50px; pointer-events:none; z-index:0;
filter:blur(36px) brightness(.78) saturate(1.15); transform:scale(1.08); transition:background .3s;}
filter:blur(36px) brightness(.78) saturate(1.15); transform:scale(1.08);
transition:background .7s ease, opacity .25s ease;}
.x-hood{position:absolute; inset:0; pointer-events:none; z-index:2;}
.x-hood svg{position:absolute; top:calc(var(--xs) - 14px); left:0; display:block; width:100%; height:120px;}
.x-hood .solid{position:absolute; top:calc(var(--xs) + 105px); left:0; right:0; bottom:0; background:var(--bg);}
@@ -216,16 +237,45 @@
.x-empty{position:absolute; inset:0; border-radius:26px; background:#141414; border:1px dashed rgba(255,255,255,.15);
display:flex; flex-direction:column; align-items:center; justify-content:center; gap:14px;}
.x-actions{position:relative; z-index:3; display:flex; justify-content:center; align-items:center; gap:22px;
padding:6px 0 calc(96px + env(safe-area-inset-bottom));}
padding:6px 0 84px;}
.x-act{width:56px; height:56px; border-radius:100px; background:rgba(255,255,255,.08); border:1px solid rgba(255,255,255,.12);
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;}
/* Dithering-Shader-Chips (Testeffekt auf den Modell-Chips) */
.chip.dither{position:relative; overflow:hidden; background:#000; color:#fff;
border:1px solid rgba(255,255,255,.14);}
.chip.dither.on{background:#000; color:#fff; border-color:rgba(255,255,255,.55);}
.chip.dither canvas{position:absolute; inset:0; width:100%; height:100%;}
.chip.dither > *:not(canvas){position:relative; z-index:1;}
.chip.dither span{text-shadow:0 1px 2px rgba(0,0,0,.7);}
.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;}
@@ -251,30 +301,28 @@
.p-hero p{color:var(--mut); font-size:13px; margin-top:3px;}
/* ================= Bottom-Navigation ================= */
/* Liquid-Glass-Nav (aus BrandLoop.dc.html): Turbulenz-Filter als backdrop,
blur(20px) als Fallback falls der Browser url()-backdrop-filter nicht kann */
/* 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;
margin-bottom:env(safe-area-inset-bottom);}
.nav::before{content:''; position:absolute; inset:0; z-index:-1; border-radius:100px;
background:rgba(10,10,10,.18);
-webkit-backdrop-filter:blur(20px); backdrop-filter:blur(20px);
-webkit-backdrop-filter:url(#bl-liquid-glass); backdrop-filter:url(#bl-liquid-glass);}
.nav::after{content:''; position:absolute; inset:0; z-index:2; border-radius:100px; pointer-events:none;
box-shadow:0 0 8px rgba(0,0,0,.03), 0 2px 6px rgba(0,0,0,.08),
inset 3px 3px .5px -3.5px rgba(255,255,255,.09), inset -3px -3px .5px -3.5px rgba(255,255,255,.85),
inset 1px 1px 1px -.5px rgba(255,255,255,.6), inset -1px -1px 1px -.5px rgba(255,255,255,.6),
inset 0 0 6px 6px rgba(255,255,255,.12), inset 0 0 2px 2px rgba(255,255,255,.06),
0 0 12px rgba(0,0,0,.15);}
.nav button{display:flex; align-items:center; gap:8px; background:none; border:none; font-family:inherit;
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-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;}
@@ -294,17 +342,22 @@
.sheet li::before{content:"·"; position:absolute; left:2px; color:var(--accent); font-weight:700;}
</style>
</head>
<body>
<body class="noscroll">
<!-- Lucide-Icon-Sprite + Liquid-Glass-Filter -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<defs>
<filter id="bl-liquid-glass" x="0%" y="0%" width="100%" height="100%" color-interpolation-filters="sRGB">
<feTurbulence type="fractalNoise" baseFrequency="0.05 0.05" numOctaves="1" seed="1" result="turbulence"/>
<feGaussianBlur in="turbulence" stdDeviation="2" result="blurredNoise"/>
<feDisplacementMap in="SourceGraphic" in2="blurredNoise" scale="70" xChannelSelector="R" yChannelSelector="B" result="displaced"/>
<feGaussianBlur in="displaced" stdDeviation="4" result="finalBlur"/>
<feComposite in="finalBlur" in2="finalBlur" operator="over"/>
<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>
@@ -349,7 +402,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>
@@ -374,43 +431,52 @@
</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>
</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>
<h3 style="margin-top:28px;">Mehr erstellen</h3>
<div class="card click" onclick="go('tut1')">
<div class="cardrow">
<div class="ico rose"><svg class="ic"><use href="#i-film"/></svg></div>
@@ -425,6 +491,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) ============ -->
@@ -831,10 +907,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>
@@ -870,6 +954,8 @@ 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'||id==='create');
if(id==='create') requestAnimationFrame(initCreateDither);
window.scrollTo(0,0);
}
function pick(el){
@@ -886,20 +972,20 @@ var ACCENT='#ff5c39';
var HUES={Produkt:40, Mensch:260, Szene:170};
var POSTS=[
{id:1, creator:'Lena M.', cat:'Produkt', model:'Sneaker Studio v2', video:true, h:250, imgs:['[ki-video: sneaker spot]'],
pics:['uploads/pasted-1784663207742-0.png']},
pics:['uploads/pasted-1784663207742-0.jpg']},
{id:2, creator:'Mara S.', cat:'Produkt', model:'Glow Serum v3', video:false, h:150, imgs:['Toner im Bogenfenster','Age Decoder Essence','Poster-Ad'],
pics:['uploads/pasted-1784643149542-0.png','uploads/pasted-1784643165286-0.png','uploads/pasted-1784643111353-0.png'],
avatar:'uploads/pasted-1784663200459-0.png'},
pics:['uploads/pasted-1784643149542-0.jpg','uploads/pasted-1784643165286-0.jpg','uploads/pasted-1784643111353-0.jpg'],
avatar:'uploads/pasted-1784663200459-0.jpg'},
{id:3, creator:'Jonas K.', cat:'Mensch', model:'Coach Avatar', video:true, h:40, imgs:['[ki-video: gym coaching ad]'],
pics:['uploads/pasted-1784663200459-0.png']},
pics:['uploads/pasted-1784663200459-0.jpg']},
{id:4, creator:'Tom B.', cat:'Mensch', model:'Barber Ich v1', video:false, h:90, imgs:['[ki-bild 1/4: portrait]','[ki-bild 2/4: fade cut]','[ki-bild 3/4: shop szene]','[ki-bild 4/4: logo karte]'],
pics:['uploads/pasted-1784663261701-0.png','uploads/pasted-1784663207742-0.png','uploads/pasted-1784663200459-0.png','uploads/pasted-1784663233931-0.png']},
pics:['uploads/pasted-1784663261701-0.jpg','uploads/pasted-1784663207742-0.jpg','uploads/pasted-1784663200459-0.jpg','uploads/pasted-1784663233931-0.jpg']},
{id:5, creator:'Feld & Co',cat:'Szene', model:'Café Interieur', video:false, h:190, imgs:['[ki-bild 1/3: tresen]','[ki-bild 2/3: latte art]','[ki-bild 3/3: fassade]'],
pics:['uploads/pasted-1784663233931-0.png','uploads/pasted-1784663383522-0.png','uploads/pasted-1784663261701-0.png']},
pics:['uploads/pasted-1784663233931-0.jpg','uploads/pasted-1784663383522-0.jpg','uploads/pasted-1784663261701-0.jpg']},
{id:6, creator:'Nina R.', cat:'Produkt', model:'Schmuck Makro', video:false, h:310, imgs:['[ki-bild: ring makro]'],
pics:['uploads/pasted-1784663383522-0.png']},
pics:['uploads/pasted-1784663383522-0.jpg']},
{id:7, creator:'Kai W.', cat:'Szene', model:'Immo Walkthrough', video:true, h:210, imgs:['[ki-video: wohnungs-tour]'],
pics:['uploads/pasted-1784663233931-0.png']}
pics:['uploads/pasted-1784663233931-0.jpg']}
];
var X={tab:'posts', idx:0, dx:0, imgIdx:0, liked:[], anim:false, hoodCx:null, hoodHalf:null, hoodRaf:null, centers:null, halves:null};
var TAB_CENTERS={videos:40, folge:205, posts:366};
@@ -913,7 +999,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;
});
}
@@ -1002,7 +1088,12 @@ function renderBlur(){
}
function esc(s){ return s.replace(/&/g,'&amp;').replace(/</g,'&lt;'); }
function renderDeck(){
function cardTransform(depth){
return depth===0 ? 'translateX(0px) rotate(0deg)' : 'scale('+(1-depth*.06)+') translateY('+(-depth*34)+'px)';
}
/* settle=true: Karten starten eine Position tiefer und gleiten nach vorn
(Nachrueck-Animation aus dem Design nach Fling/Tab-Wechsel) */
function renderDeck(settle){
var host=document.getElementById('xDeck');
var d=deck();
renderBlur();
@@ -1017,16 +1108,17 @@ function renderDeck(){
return;
}
var html='';
var slice=d.slice(X.idx, X.idx+3);
var slice=d.slice(X.idx, X.idx+4);
for(var i=slice.length-1; i>=0; i--){
var p=slice[i], top=(i===0);
var imgI=top?Math.min(X.imgIdx,p.imgs.length-1):0;
var u=pic(p,imgI);
var hue=p.h+imgI*25;
var tf=top
var depth=settle?i+1:i;
var tf=(top&&!settle)
? 'translateX('+X.dx+'px) rotate('+(X.dx/22)+'deg)'
: 'scale('+(1-i*.06)+') translateY('+(-i*34)+'px)';
html+='<div class="x-card'+(top?' top':'')+'" data-i="'+i+'" style="z-index:'+(4-i)+'; transform:'+tf+'; transition:transform .28s ease; cursor:'+(top?'grab':'default')+';">';
: cardTransform(depth);
html+='<div class="x-card'+(top?' top':'')+'" data-i="'+i+'" style="z-index:'+(5-i)+'; transform:'+tf+'; transition:'+(settle?'none':'transform .45s cubic-bezier(.22,1,.36,1)')+'; cursor:'+(top?'grab':'default')+';">';
var bg=u ? '#141414 url(&quot;'+u+'&quot;) center / cover no-repeat' : dummy(hue);
html+='<div class="img" style="background:'+bg+';'+(top?'':' filter:brightness(.85);')+'"></div>';
if(top){
@@ -1054,6 +1146,14 @@ function renderDeck(){
host.innerHTML=html;
var topEl=host.querySelector('.x-card.top');
if(topEl) topEl.addEventListener('pointerdown', onDown);
if(settle){
requestAnimationFrame(function(){ requestAnimationFrame(function(){
host.querySelectorAll('.x-card').forEach(function(el){
el.style.transition='transform .45s cubic-bezier(.22,1,.36,1)';
el.style.transform=cardTransform(+el.dataset.i);
});
});});
}
}
function fling(dir){
@@ -1062,14 +1162,15 @@ function fling(dir){
X.anim=true;
var topEl=document.querySelector('#xDeck .x-card.top');
if(topEl){
topEl.style.transition='transform .28s ease';
topEl.style.transition='transform .3s cubic-bezier(.4,0,.7,1), opacity .3s ease';
topEl.style.transform='translateX('+(dir*640)+'px) rotate('+(dir*640/22)+'deg)';
topEl.style.opacity='0';
}
setTimeout(function(){
if(dir>0 && post) X.liked.push(post.id);
X.idx++; X.dx=0; X.imgIdx=0; X.anim=false;
renderDeck();
},280);
renderDeck(true);
},300);
}
function onDown(e){
@@ -1093,7 +1194,7 @@ function onDown(e){
var d=ev.clientX-sx;
if(Math.abs(d)>110){ fling(d>0?1:-1); return; }
X.dx=0;
topEl.style.transition='transform .28s ease';
topEl.style.transition='transform .45s cubic-bezier(.22,1,.36,1)';
topEl.style.transform='translateX(0) rotate(0)';
if(likeB) likeB.style.opacity=0;
if(skipB) skipB.style.opacity=0;
@@ -1108,12 +1209,21 @@ function onDown(e){
document.querySelectorAll('.x-tab').forEach(function(el){
el.addEventListener('click',function(){
X.tab=el.dataset.tab; X.idx=0; X.dx=0; X.imgIdx=0;
renderTabs(); renderDeck(); animateHood(centerOf(X.tab), halfOf(X.tab));
if(X.tab===el.dataset.tab) return;
var k=el.dataset.tab;
var deckEl=document.getElementById('xDeck'), blurEl=document.getElementById('xBlur');
animateHood(centerOf(k), halfOf(k));
deckEl.style.transition='opacity .18s ease';
deckEl.style.opacity='0'; blurEl.style.opacity='0';
setTimeout(function(){
X.tab=k; X.idx=0; X.dx=0; X.imgIdx=0;
renderTabs(); renderDeck(true);
deckEl.style.opacity='1'; blurEl.style.opacity='1';
},180);
});
});
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); };
@@ -1149,9 +1259,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;
@@ -1164,49 +1273,420 @@ 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]};
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>';
/* ---------- Dithering-Shader (WebGL2-Port von designali-in/dithering-shader) ----------
Testweise auf den Modell-Chips im Create-Screen: Sphere/Random,
ausgewaehlt = weiss/silber, nicht ausgewaehlt = dunkelgrau. */
var DITHER_VERT='#version 300 es\nprecision mediump float;\nlayout(location = 0) in vec4 a_position;\nvoid main(){ gl_Position = a_position; }';
var DITHER_FRAG=[
'#version 300 es',
'precision mediump float;',
'uniform float u_time;',
'uniform vec2 u_resolution;',
'uniform vec4 u_colorBack;',
'uniform vec4 u_colorFront;',
'uniform float u_shape;',
'uniform float u_type;',
'uniform float u_pxSize;',
'out vec4 fragColor;',
'vec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); }',
'float snoise(vec2 v) {',
' const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);',
' vec2 i = floor(v + dot(v, C.yy));',
' vec2 x0 = v - i + dot(i, C.xx);',
' vec2 i1;',
' i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);',
' vec4 x12 = x0.xyxy + C.xxzz;',
' x12.xy -= i1;',
' i = mod(i, 289.0);',
' vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));',
' vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);',
' m = m * m;',
' m = m * m;',
' vec3 x = 2.0 * fract(p * C.www) - 1.0;',
' vec3 h = abs(x) - 0.5;',
' vec3 ox = floor(x + 0.5);',
' vec3 a0 = x - ox;',
' m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);',
' vec3 g;',
' g.x = a0.x * x0.x + h.x * x0.y;',
' g.yz = a0.yz * x12.xz + h.yz * x12.yw;',
' return 130.0 * dot(m, g);',
'}',
'#define TWO_PI 6.28318530718',
'#define PI 3.14159265358979323846',
'float hash11(float p) {',
' p = fract(p * 0.3183099) + 0.1;',
' p *= p + 19.19;',
' return fract(p * p);',
'}',
'float hash21(vec2 p) {',
' p = fract(p * vec2(0.3183099, 0.3678794)) + 0.1;',
' p += dot(p, p + 19.19);',
' return fract(p.x * p.y);',
'}',
'float getSimplexNoise(vec2 uv, float t) {',
' float noise = .5 * snoise(uv - vec2(0., .3 * t));',
' noise += .5 * snoise(2. * uv + vec2(0., .32 * t));',
' return noise;',
'}',
'const int bayer2x2[4] = int[4](0, 2, 3, 1);',
'const int bayer4x4[16] = int[16](0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5);',
'const int bayer8x8[64] = int[64](0, 32, 8, 40, 2, 34, 10, 42, 48, 16, 56, 24, 50, 18, 58, 26, 12, 44, 4, 36, 14, 46, 6, 38, 60, 28, 52, 20, 62, 30, 54, 22, 3, 35, 11, 43, 1, 33, 9, 41, 51, 19, 59, 27, 49, 17, 57, 25, 15, 47, 7, 39, 13, 45, 5, 37, 63, 31, 55, 23, 61, 29, 53, 21);',
'float getBayerValue(vec2 uv, int size) {',
' ivec2 pos = ivec2(mod(uv, float(size)));',
' int index = pos.y * size + pos.x;',
' if (size == 2) { return float(bayer2x2[index]) / 4.0; }',
' else if (size == 4) { return float(bayer4x4[index]) / 16.0; }',
' else if (size == 8) { return float(bayer8x8[index]) / 64.0; }',
' return 0.0;',
'}',
'void main() {',
' float t = .5 * u_time;',
' vec2 uv = gl_FragCoord.xy / u_resolution.xy;',
' uv -= .5;',
' float pxSize = u_pxSize;',
' vec2 pxSizeUv = gl_FragCoord.xy;',
' pxSizeUv -= .5 * u_resolution;',
' pxSizeUv /= pxSize;',
' vec2 pixelizedUv = floor(pxSizeUv) * pxSize / u_resolution.xy;',
' pixelizedUv += .5;',
' pixelizedUv -= .5;',
' vec2 shape_uv = pixelizedUv;',
' vec2 dithering_uv = pxSizeUv;',
' vec2 ditheringNoise_uv = uv * u_resolution;',
' float shape = 0.;',
' if (u_shape < 1.5) {',
' shape_uv *= .001;',
' shape = 0.5 + 0.5 * getSimplexNoise(shape_uv, t);',
' shape = smoothstep(0.3, 0.9, shape);',
' } else if (u_shape < 2.5) {',
' shape_uv *= .003;',
' for (float i = 1.0; i < 6.0; i++) {',
' shape_uv.x += 0.6 / i * cos(i * 2.5 * shape_uv.y + t);',
' shape_uv.y += 0.6 / i * cos(i * 1.5 * shape_uv.x + t);',
' }',
' shape = .15 / abs(sin(t - shape_uv.y - shape_uv.x));',
' shape = smoothstep(0.02, 1., shape);',
' } else if (u_shape < 3.5) {',
' shape_uv *= .05;',
' float stripeIdx = floor(2. * shape_uv.x / TWO_PI);',
' float rand = hash11(stripeIdx * 10.);',
' rand = sign(rand - .5) * pow(.1 + abs(rand), .4);',
' shape = sin(shape_uv.x) * cos(shape_uv.y - 5. * rand * t);',
' shape = pow(abs(shape), 6.);',
' } else if (u_shape < 4.5) {',
' shape_uv *= 4.;',
' float wave = cos(.5 * shape_uv.x - 2. * t) * sin(1.5 * shape_uv.x + t) * (.75 + .25 * cos(3. * t));',
' shape = 1. - smoothstep(-1., 1., shape_uv.y + wave);',
' } else if (u_shape < 5.5) {',
' float dist = length(shape_uv);',
' float waves = sin(pow(dist, 1.7) * 7. - 3. * t) * .5 + .5;',
' shape = waves;',
' } else if (u_shape < 6.5) {',
' float l = length(shape_uv);',
' float angle = 6. * atan(shape_uv.y, shape_uv.x) + 4. * t;',
' float twist = 1.2;',
' float offset = pow(l, -twist) + angle / TWO_PI;',
' float mid = smoothstep(0., 1., pow(l, twist));',
' shape = mix(0., fract(offset), mid);',
' } else if (u_shape < 7.5) {',
' shape_uv *= 2.;',
' float d = 1. - pow(length(shape_uv), 2.);',
' vec3 pos = vec3(shape_uv, sqrt(d));',
' vec3 lightPos = normalize(vec3(cos(1.5 * t), .8, sin(1.25 * t)));',
' shape = .5 + .5 * dot(lightPos, pos);',
' shape *= step(0., d);',
' } else {',
' float ar = u_resolution.x / u_resolution.y;',
' vec2 p = shape_uv * vec2(ar, 1.) * 2.1;',
' vec2 q = vec2(sign(p.x) * max(abs(p.x) - (1.05 * ar - 1.), 0.), p.y);',
' float d = 1. - dot(q, q);',
' vec3 pos = vec3(q, sqrt(max(d, 0.)));',
' vec3 lightPos = normalize(vec3(cos(1.5 * t), .8, sin(1.25 * t)));',
' shape = .5 + .5 * dot(lightPos, pos);',
' shape *= step(0., d);',
' }',
' int type = int(floor(u_type));',
' float dithering = 0.0;',
' switch (type) {',
' case 1: {',
' dithering = step(hash21(ditheringNoise_uv), shape);',
' } break;',
' case 2:',
' dithering = getBayerValue(dithering_uv, 2);',
' break;',
' case 3:',
' dithering = getBayerValue(dithering_uv, 4);',
' break;',
' default:',
' dithering = getBayerValue(dithering_uv, 8);',
' break;',
' }',
' dithering -= .5;',
' float res = step(.5, shape + dithering);',
' vec3 fgColor = u_colorFront.rgb * u_colorFront.a;',
' float fgOpacity = u_colorFront.a;',
' vec3 bgColor = u_colorBack.rgb * u_colorBack.a;',
' float bgOpacity = u_colorBack.a;',
' vec3 color = fgColor * res;',
' float opacity = fgOpacity * res;',
' color += bgColor * (1. - opacity);',
' opacity += bgOpacity * (1. - opacity);',
' fragColor = vec4(color, opacity);',
'}'
].join('\n');
function ditherHexToRgba(hex){
var m=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if(!m) return [0,0,0,1];
return [parseInt(m[1],16)/255, parseInt(m[2],16)/255, parseInt(m[3],16)/255, 1];
}
function mountDither(canvas, opts){
var gl=canvas.getContext('webgl2');
if(!gl) return null;
function sh(type,src){
var s=gl.createShader(type);
gl.shaderSource(s,src); gl.compileShader(s);
if(!gl.getShaderParameter(s,gl.COMPILE_STATUS)){ console.error(gl.getShaderInfoLog(s)); return null; }
return s;
}
var vs=sh(gl.VERTEX_SHADER,DITHER_VERT), fs=sh(gl.FRAGMENT_SHADER,DITHER_FRAG);
if(!vs||!fs) return null;
var prog=gl.createProgram();
gl.attachShader(prog,vs); gl.attachShader(prog,fs); gl.linkProgram(prog);
if(!gl.getProgramParameter(prog,gl.LINK_STATUS)){ console.error(gl.getProgramInfoLog(prog)); return null; }
var L={};
['u_time','u_resolution','u_colorBack','u_colorFront','u_shape','u_type','u_pxSize'].forEach(function(n){ L[n]=gl.getUniformLocation(prog,n); });
var 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);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0,2,gl.FLOAT,false,0,0);
var dpr=window.devicePixelRatio||1;
var r=canvas.parentElement.getBoundingClientRect();
canvas.width=Math.max(2,Math.round((r.width||120)*dpr));
canvas.height=Math.max(2,Math.round((r.height||36)*dpr));
gl.viewport(0,0,canvas.width,canvas.height);
var front=ditherHexToRgba(opts.front||'#ffffff');
var back=ditherHexToRgba(opts.back||'#000000');
var t0=Date.now();
(function frame(){
var t=(Date.now()-t0)*.001*(opts.speed||1.5);
gl.useProgram(prog);
gl.uniform1f(L.u_time,t);
gl.uniform2f(L.u_resolution,canvas.width,canvas.height);
gl.uniform4fv(L.u_colorBack,back);
gl.uniform4fv(L.u_colorFront,front);
gl.uniform1f(L.u_shape,opts.shape||7);
gl.uniform1f(L.u_type,opts.type||1);
gl.uniform1f(L.u_pxSize,opts.pxSize||2);
gl.drawArrays(gl.TRIANGLES,0,6);
requestAnimationFrame(frame);
})();
return { setFront:function(hex){ front=ditherHexToRgba(hex); } };
}
/* ---------- 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('.chip').forEach(function(c){
c.onclick=function(){ C.model=+c.dataset.i; renderCreateModels(); };
host.querySelectorAll('.pill').forEach(function(p){
p.onclick=function(){ onPick(p.dataset.v); };
});
}
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;
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();});
}
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 toggleOpts(){ C.optOpen=!C.optOpen; renderCreateStack(); }
var DCHIPS={built:false, mounted:false, insts:[]};
var DITHER_ON='#f2f2f2', DITHER_OFF='#555555';
function renderCreateModels(){
var host=document.getElementById('createModels');
if(!DCHIPS.built){
host.innerHTML=C_MODELS.map(function(m,i){
return '<div class="chip dither" data-i="'+i+'"><canvas></canvas><div class="dot"></div><span>'+m.name+'</span></div>';
}).join('');
host.querySelectorAll('.chip').forEach(function(c){
c.onclick=function(){ C.model=+c.dataset.i; renderCreateModels(); };
});
DCHIPS.built=true;
}
host.querySelectorAll('.chip').forEach(function(c,i){
var on=(i===C.model);
c.classList.toggle('on',on);
c.querySelector('.dot').style.background=on?'var(--accent)':'rgba(255,255,255,.3)';
if(DCHIPS.insts[i]) DCHIPS.insts[i].setFront(on?DITHER_ON:DITHER_OFF);
});
}
/* Shader erst mounten, wenn der Create-Screen sichtbar ist (sonst Groesse 0) */
function initCreateDither(){
if(DCHIPS.mounted) return;
var chips=document.querySelectorAll('#createModels .chip.dither');
if(!chips.length) return;
DCHIPS.mounted=true;
chips.forEach(function(c,i){
DCHIPS.insts[i]=mountDither(c.querySelector('canvas'), {front:(i===C.model)?DITHER_ON:DITHER_OFF, shape:8, type:1, pxSize:2, speed:1.5});
});
}
/* 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);
}
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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB