Design-Sync: 3D-Ordner-System im Profil

prototyp-app.html aus dem Design-Projekt uebernommen: Ordnerbaum im Profil
mit 3D-Ordner-Optik, Ordner-Detail-Screen, Erstellen/Umbenennen/Loeschen/
Farbwahl-Dialoge, Bild-Kacheln im Ordner-Raster; alte Asset-/Attribut-
Karten des Profils dafuer entfernt. Bildpfade wieder auf .jpg gedreht.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
root
2026-07-24 20:16:45 +00:00
parent 48839527f4
commit e6ed459947

View File

@@ -197,6 +197,129 @@
.asset svg{width:16px; height:16px; color:var(--mut); flex-shrink:0;} .asset svg{width:16px; height:16px; color:var(--mut); flex-shrink:0;}
.asset .score{margin-left:auto;} .asset .score{margin-left:auto;}
/* ---- 3D-Ordner (Profil) ---- */
.folders{display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-bottom:4px;}
.fold{position:relative; background:var(--card); border:1px solid rgba(255,255,255,.07); border-radius:18px;
padding:6px 10px 22px; cursor:pointer;
transition:transform .7s cubic-bezier(.16,1,.3,1), border-color .4s, box-shadow .4s;}
.fold:hover{border-color:var(--border-strong);}
.fold.open{border-color:var(--border-strong); box-shadow:0 16px 36px rgba(0,0,0,.45); transform:scale(1.03) rotate(-1deg);}
.fold .glow{position:absolute; inset:0; border-radius:18px; opacity:0; transition:opacity .6s; pointer-events:none;}
.fold.open .glow{opacity:.16;}
.f-stage{position:relative; height:112px; perspective:900px;}
.f-piece{position:absolute; left:50%; transition:transform .7s cubic-bezier(.16,1,.3,1);}
.f-back{width:92px; height:66px; margin-left:-46px; top:calc(50% - 33px); border-radius:9px;
border:1px solid rgba(255,255,255,.1); filter:brightness(.82); transform-origin:bottom center; z-index:10;}
.fold.open .f-back{transform:rotateX(-20deg) scaleY(1.05);}
.f-tab{width:32px; height:11px; margin-left:-34px; top:calc(50% - 42px); border-radius:5px 5px 0 0;
border:1px solid rgba(255,255,255,.1); border-bottom:none; filter:brightness(.7); transform-origin:bottom center; z-index:10;}
.fold.open .f-tab{transform:rotateX(-30deg) translateY(-3px);}
.f-front{width:92px; height:66px; margin-left:-46px; top:calc(50% - 30px); border-radius:9px;
border:1px solid rgba(255,255,255,.18); transform-origin:bottom center; z-index:30;
box-shadow:0 10px 20px rgba(0,0,0,.45);}
.f-front::after{content:''; position:absolute; inset:0; border-radius:9px;
background:linear-gradient(135deg,rgba(255,255,255,.32),transparent 62%);}
.fold.open .f-front{transform:rotateX(35deg) translateY(11px);}
.f-stack{position:absolute; left:50%; top:50%; z-index:20;}
.f-card{position:absolute; width:50px; height:66px; left:-25px; top:-33px; border-radius:8px; overflow:hidden;
background:#1c1c1c; border:1px solid rgba(255,255,255,.08); opacity:0; cursor:pointer;
box-shadow:0 8px 18px rgba(0,0,0,.5); transform:translate(0,0) rotate(0deg) scale(.4);
transition:transform .7s cubic-bezier(.16,1,.3,1), opacity .45s ease, box-shadow .4s;
transition-delay:calc(var(--i) * 45ms);}
.fold.open .f-card{opacity:1; transform:translate(var(--tx), calc(-46px + var(--ty))) rotate(var(--rot)) scale(1);}
.fold.open .f-card:active{transform:translate(var(--tx), calc(-58px + var(--ty))) rotate(var(--rot)) scale(1.16);
box-shadow:0 16px 28px rgba(0,0,0,.6);}
.f-card img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;}
.f-card .shade{position:absolute; inset:0; background:linear-gradient(to top,rgba(0,0,0,.92),rgba(0,0,0,.12) 58%,transparent);}
.f-card b{position:absolute; left:4px; right:4px; bottom:3px; font-size:8px; line-height:1.15; font-weight:800;
letter-spacing:-.01em; text-transform:uppercase; color:#fff; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.f-meta{text-align:center; margin-top:2px; position:relative; z-index:2;}
.f-meta h4{font-size:13px; font-weight:700; letter-spacing:-.01em;}
.f-meta p{font-size:11.5px; color:var(--faint); margin-top:1px;}
.f-hint{position:absolute; left:0; right:0; bottom:7px; text-align:center; font-size:9px; font-weight:700;
letter-spacing:.14em; text-transform:uppercase; color:rgba(255,255,255,.26); transition:opacity .4s;}
.fold.open .f-hint{opacity:0;}
.f-tile{position:relative; height:168px; border-radius:18px; overflow:hidden; cursor:pointer;
background:#1a1a1a; border:1px solid rgba(255,255,255,.08); transition:transform .12s, border-color .3s;}
.f-tile:hover{border-color:var(--border-strong);}
.f-tile:active{transform:scale(.97);}
.f-tile img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;}
.f-tile .shade{position:absolute; inset:0; background:linear-gradient(to top,rgba(0,0,0,.88),rgba(0,0,0,.1) 62%,transparent);}
.f-tile .cap{position:absolute; left:11px; right:11px; bottom:9px;}
.f-tile .cap h4{font-size:12.5px; font-weight:800; letter-spacing:-.01em;}
.f-tile .cap p{font-size:10.5px; color:rgba(255,255,255,.6); margin-top:1px; line-height:1.35;}
.f-sub{position:absolute; top:9px; left:9px; font-size:9px; font-weight:800; letter-spacing:.1em;
text-transform:uppercase; color:rgba(255,255,255,.55);}
/* ---- Ordner: Ghost, Menü, Dialog ---- */
.fold.ghost{border-style:dashed; border-color:rgba(255,255,255,.16); background:rgba(255,255,255,.02);}
.fold.ghost .f-piece{background:rgba(255,255,255,.05) !important; border:1px dashed rgba(255,255,255,.2); filter:none !important; box-shadow:none;}
.fold.ghost .f-front::after{display:none;}
.fold.ghost .f-plus{position:absolute; left:50%; top:calc(50% - 4px); transform:translate(-50%,-50%); z-index:40;
color:rgba(255,255,255,.5); transition:color .3s, transform .5s cubic-bezier(.16,1,.3,1);}
.fold.ghost.open .f-plus{color:#fff; transform:translate(-50%,-50%) scale(1.12);}
.fold.ghost .f-meta h4{color:rgba(255,255,255,.66);}
.f-menu{position:absolute; top:7px; right:7px; z-index:45; width:26px; height:26px; border-radius:100px;
display:grid; place-items:center; color:rgba(255,255,255,.55); opacity:0; cursor:pointer;
transition:opacity .25s, background .2s, color .2s;}
.f-menu:hover{background:rgba(255,255,255,.12); color:#fff;}
.fold:hover .f-menu, .fold.open .f-menu, .f-menu.on{opacity:1;}
@media (hover:none){.f-menu{opacity:.85;}}
.fmenu{position:fixed; z-index:70; width:208px; padding:7px; border-radius:19px; display:none; flex-direction:column; gap:1px;
background:rgba(255,255,255,.12); -webkit-backdrop-filter:blur(3px) saturate(1.15); backdrop-filter:blur(3px) saturate(1.15);
box-shadow:inset 2px 2px 1px 0 rgba(255,255,255,.4), inset -1px -1px 1px 1px rgba(255,255,255,.3), 0 16px 40px rgba(0,0,0,.5);}
.fmenu.open{display:flex;}
.fmenu .mi{display:flex; align-items:center; gap:11px; padding:10px 12px; border-radius:14px;
font-size:13.5px; font-weight:600; color:#fff; cursor:pointer; transition:background .15s;}
.fmenu .mi:hover{background:rgba(255,255,255,.16);}
.fmenu .mi svg{width:16px; height:16px;}
.fmenu .mi.del{color:#ff8b74;}
.fmenu .hd{font-size:9.5px; font-weight:800; letter-spacing:.12em; text-transform:uppercase;
color:rgba(255,255,255,.55); padding:9px 12px 3px;}
.fmenu .sw{display:flex; gap:7px; padding:2px 12px 9px;}
.fmenu .sw i{width:23px; height:23px; border-radius:8px; cursor:pointer; border:1px solid rgba(255,255,255,.28);
transition:transform .15s;}
.fmenu .sw i:hover{transform:scale(1.12);}
.fmenu .sw i.on{box-shadow:0 0 0 2px rgba(255,255,255,.85);}
.fdlg{position:fixed; inset:0; z-index:75; display:none; align-items:center; justify-content:center; padding:20px;}
.fdlg.open{display:flex;}
.fdlg .bd{position:absolute; inset:0; background:rgba(10,10,10,.72); -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);}
.fdlg .pane2{position:relative; width:100%; max-width:326px; border-radius:22px; padding:16px 16px 14px;
background:rgba(255,255,255,.12); -webkit-backdrop-filter:blur(3px) saturate(1.15); backdrop-filter:blur(3px) saturate(1.15);
box-shadow:inset 2px 2px 1px 0 rgba(255,255,255,.4), inset -1px -1px 1px 1px rgba(255,255,255,.3), 0 20px 46px rgba(0,0,0,.55);}
.fdlg h4{font-size:15px; font-weight:800; letter-spacing:-.01em; margin-bottom:10px;}
.fdlg input[type=text]{margin:0 0 10px; background:rgba(0,0,0,.35); border-color:rgba(255,255,255,.16);}
/* ---- Ordner-Lightbox ---- */
.fbox{position:fixed; inset:0; z-index:60; display:none; align-items:center; justify-content:center; padding:16px;}
.fbox.open{display:flex;}
.fbox .bd{position:absolute; inset:0; background:rgba(10,10,10,.92); -webkit-backdrop-filter:blur(18px); backdrop-filter:blur(18px);}
.fbox .pane{position:relative; width:100%; max-width:394px; border-radius:24px; overflow:hidden;
background:var(--card); border:1px solid rgba(255,255,255,.1); box-shadow:0 35px 60px -15px rgba(0,0,0,.65);
animation:fpop .5s cubic-bezier(.16,1,.3,1);}
@keyframes fpop{from{opacity:0; transform:scale(.88) translateY(16px);} to{opacity:1; transform:none;}}
.fbox .shot{position:relative; width:100%; height:min(52dvh,420px); background:#111;}
.fbox .shot img{width:100%; height:100%; object-fit:cover; display:block;}
.fbox .shot .veil{position:absolute; inset:0; background:linear-gradient(to bottom,rgba(0,0,0,.2),transparent 40%,rgba(0,0,0,.45));}
.fbox .info{padding:15px 17px 17px; border-top:1px solid rgba(255,255,255,.06);}
.fbox .info h4{font-size:17px; font-weight:800; letter-spacing:-.015em;}
.fbox .dots{display:flex; align-items:center; gap:7px; margin-top:11px;}
.fbox .dots i{width:6px; height:6px; border-radius:100px; background:rgba(255,255,255,.25); cursor:pointer; transition:transform .3s, background .3s;}
.fbox .dots i.on{background:#fff; transform:scale(1.4);}
.fbox .count{margin-left:auto; font-size:10.5px; font-weight:700; letter-spacing:.1em; color:var(--faint);}
.fbox .navb{position:absolute; top:calc(min(52dvh,420px) / 2 - 20px); width:40px; height:40px; border-radius:100px; display:grid; place-items:center;
background:rgba(18,18,18,.66); -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
border:1px solid rgba(255,255,255,.14); color:#fff; cursor:pointer; z-index:3; transition:opacity .25s, transform .12s;}
.fbox .navb:active{transform:scale(.9);}
.fbox .navb.off{opacity:0; pointer-events:none;}
.fbox .navb.prev{left:10px;} .fbox .navb.next{right:10px;}
.fbox .xb{position:absolute; top:10px; right:10px; z-index:3; width:36px; height:36px; border-radius:100px; display:grid; place-items:center;
background:rgba(18,18,18,.66); -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
border:1px solid rgba(255,255,255,.14); color:#fff; cursor:pointer;}
/* ---- Log & Code ---- */ /* ---- Log & Code ---- */
.log{font-size:12.5px; color:var(--mut); border-left:2px solid var(--border-strong); padding:5px 0 5px 12px; margin:8px 0; line-height:1.5;} .log{font-size:12.5px; color:var(--mut); border-left:2px solid var(--border-strong); padding:5px 0 5px 12px; margin:8px 0; line-height:1.5;}
.code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace; font-size:11px; line-height:1.7; color:var(--mut); .code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace; font-size:11px; line-height:1.7; color:var(--mut);
@@ -548,20 +671,9 @@
<div class="card metric"><p>Follower</p><h4 class="g">+218</h4></div> <div class="card metric"><p>Follower</p><h4 class="g">+218</h4></div>
</div> </div>
<div class="card click" onclick="go('improve-select')"> <h3>Ordner</h3>
<div class="cardrow"> <div class="folders" id="folders"></div>
<div class="ico amber"><svg class="ic"><use href="#i-spark"/></svg></div> <p class="note" style="margin:2px 0 4px;">Tippen öffnet den Ordner, tippen auf eine Karte zeigt sie groß.</p>
<div class="body"><h4 class="t-sm">Asset verbessern</h4><p>Gesicht, Produkt oder Szene weiterentwickeln jederzeit.</p></div>
<svg class="ic chev"><use href="#i-chev"/></svg>
</div>
</div>
<div class="card click" onclick="go('scene-results')">
<div class="cardrow">
<div class="ico zinc"><svg class="ic"><use href="#i-film"/></svg></div>
<div class="body"><h4 class="t-sm">Zuletzt: „Roter Lippenstift Close-up"</h4><p>3 Videos generiert · Vote offen</p></div>
<svg class="ic chev"><use href="#i-chev"/></svg>
</div>
</div>
<h3>Knowledge Base · Feste Regeln <span style="text-transform:none; letter-spacing:0; color:var(--faint); font-weight:500;">(kein Score, gelten immer)</span></h3> <h3>Knowledge Base · Feste Regeln <span style="text-transform:none; letter-spacing:0; color:var(--faint); font-weight:500;">(kein Score, gelten immer)</span></h3>
<div class="card"> <div class="card">
@@ -570,26 +682,7 @@
<div class="rule"><svg class="ic"><use href="#i-check"/></svg><p style="color:var(--txt);">Logo nie verzerren</p></div> <div class="rule"><svg class="ic"><use href="#i-check"/></svg><p style="color:var(--txt);">Logo nie verzerren</p></div>
</div> </div>
<h3>Assets (freigegebene Versionen)</h3>
<div class="card">
<div class="asset"><svg class="ic"><use href="#i-user"/></svg><span>Mara <span class="badge b-ok">v4</span></span><span class="score">6120</span></div>
<div class="asset"><svg class="ic"><use href="#i-box"/></svg><span>Rouge No. 5 <span class="badge b-ok">v2</span></span><span class="score">6480</span></div>
</div>
<h3>Attribute (Elo 010.000)</h3> <h3>Attribute (Elo 010.000)</h3>
<div class="card click" onclick="go('kb-detail')">
<div class="cardrow">
<div class="ico rose"><svg class="ic"><use href="#i-pin"/></svg></div>
<div class="body"><h4 class="t-sm">Location: Kaiserslautern <span class="score">5240 ▲</span></h4><p>12 Videos · 8 Siege · Unter-Attribut: Stadion <span class="score">5560</span></p></div>
<svg class="ic chev"><use href="#i-chev"/></svg>
</div>
</div>
<div class="card">
<div class="cardrow">
<div class="ico zinc"><svg class="ic"><use href="#i-pin"/></svg></div>
<div class="body"><h4 class="t-sm">Location: Köln <span class="score" style="color:var(--faint);">4980 ▼</span> <span class="badge b-arch">ARCHIVIERT</span></h4><p>Historie bleibt erhalten ein schlechter Score ist auch Wissen.</p></div>
</div>
</div>
<div class="card"> <div class="card">
<div class="asset"><svg class="ic"><use href="#i-bulb"/></svg><span>Licht: warmes Abendlicht</span><span class="score">6350 ▲</span></div> <div class="asset"><svg class="ic"><use href="#i-bulb"/></svg><span>Licht: warmes Abendlicht</span><span class="score">6350 ▲</span></div>
<div class="asset"><svg class="ic"><use href="#i-mic"/></svg><span>Voice: warm-weiblich</span><span class="score">5890</span></div> <div class="asset"><svg class="ic"><use href="#i-mic"/></svg><span>Voice: warm-weiblich</span><span class="score">5890</span></div>
@@ -602,6 +695,17 @@
<div class="log">17.07. · LLM-Vergleich K'lautern vs. Köln: „Stadion war der Unterschied" → neues Unter-Attribut Stadion (Start: Ø 5480)</div> <div class="log">17.07. · LLM-Vergleich K'lautern vs. Köln: „Stadion war der Unterschied" → neues Unter-Attribut Stadion (Start: Ø 5480)</div>
</div> </div>
<!-- ============ ORDNER-DETAIL ============ -->
<div class="screen" id="folder">
<div class="topbar">
<button class="backpill" id="fdBack"><span class="ar"></span> Zurück</button>
<h1 id="fdTitle">Ordner</h1>
</div>
<p class="sub" id="fdCrumb" style="margin:2px 0 16px;"></p>
<div class="folders" id="fdGrid"></div>
<div class="note" id="fdEmpty" style="display:none;">Noch leer lege hier einen Unterordner an oder öffne die Bilder.</div>
</div>
<!-- ============ KB DETAIL ============ --> <!-- ============ KB DETAIL ============ -->
<div class="screen" id="kb-detail"> <div class="screen" id="kb-detail">
<div class="topbar"><button class="backpill" onclick="go('profile')"><span class="ar"></span> Zurück</button><h1>Kaiserslautern</h1></div> <div class="topbar"><button class="backpill" onclick="go('profile')"><span class="ar"></span> Zurück</button><h1>Kaiserslautern</h1></div>
@@ -988,6 +1092,37 @@ TEXT-OVERLAY: keins (Brand-Stil: clean)</textarea></div><div data-beam-bloom="tr
<p style="margin-bottom:0;"><b>Klickpfad zum Präsentieren:</b><br>Feed (swipen) → ↺ Detail → Nachmachen → Editor → Profil (Knowledge) → Verbessern → Dev-Modus</p> <p style="margin-bottom:0;"><b>Klickpfad zum Präsentieren:</b><br>Feed (swipen) → ↺ Detail → Nachmachen → Editor → Profil (Knowledge) → Verbessern → Dev-Modus</p>
</div> </div>
<!-- ============ ORDNER-MENÜ + DIALOG ============ -->
<div class="fmenu" id="fmenu"></div>
<div class="fdlg" id="fdlg">
<div class="bd" onclick="fnClose()"></div>
<div class="pane2">
<h4 id="fnTitle">Neuer Ordner</h4>
<input type="text" id="fnName" placeholder="Ordnername" autocomplete="off">
<div class="pill-row" style="margin:0;">
<div class="pill on" onclick="fnSave()">Speichern</div>
<div class="pill" onclick="fnClose()">Abbrechen</div>
</div>
</div>
</div>
<!-- ============ ORDNER-LIGHTBOX ============ -->
<div class="fbox" id="fbox">
<div class="bd" onclick="fbClose()"></div>
<div class="pane">
<div class="navb prev" id="fbPrev" onclick="fbNav(-1)"><svg class="ic" style="width:18px;height:18px;transform:rotate(180deg);"><use href="#i-chev"/></svg></div>
<div class="navb next" id="fbNext" onclick="fbNav(1)"><svg class="ic" style="width:18px;height:18px;"><use href="#i-chev"/></svg></div>
<div class="xb" onclick="fbClose()"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></div>
<div class="shot"><img id="fbImg" alt=""><div class="veil"></div></div>
<div class="info">
<h4 id="fbTitle"></h4>
<p class="sub" id="fbSub" style="margin-top:3px;"></p>
<div class="dots" id="fbDots"></div>
<button class="btn" id="fbOpen" style="margin-top:14px;">Im Ordner öffnen</button>
</div>
</div>
</div>
<script> <script>
/* ---------- Navigation ---------- */ /* ---------- Navigation ---------- */
function go(id){ function go(id){
@@ -995,7 +1130,7 @@ function go(id){
document.getElementById(id).classList.add('active'); document.getElementById(id).classList.add('active');
var navMap={ var navMap={
explore:'n-feed', explore:'n-feed',
profile:'n-profile','kb-detail':'n-profile','improve-select':'n-profile','improve-input':'n-profile','improve-results':'n-profile','improve-vote':'n-profile', profile:'n-profile',folder:'n-profile','kb-detail':'n-profile','improve-select':'n-profile','improve-input':'n-profile','improve-results':'n-profile','improve-vote':'n-profile',
create:'n-create',detail:'n-create',tut1:'n-create',tut2:'n-create',onboarding1:'n-create',onboarding2:'n-create', create:'n-create',detail:'n-create',tut1:'n-create',tut2:'n-create',onboarding1:'n-create',onboarding2:'n-create',
'scene-create':'n-create','script-confirm':'n-create','scene-results':'n-create','scene-vote':'n-create',publish:'n-create', 'scene-create':'n-create','script-confirm':'n-create','scene-results':'n-create','scene-vote':'n-create',publish:'n-create',
'model-create':'n-create','model-result':'n-create',drafts:'n-create', 'model-create':'n-create','model-result':'n-create',drafts:'n-create',
@@ -1780,6 +1915,340 @@ renderCreateModels();
renderCreateOpts(); renderCreateOpts();
renderCreateStack(); renderCreateStack();
renderKb(); renderKb();
/* ---------- 3D-Ordner (Profil + Ordner-Detail) ---------- */
var U='uploads/';
var FGRADS=[
{grad:'linear-gradient(135deg,#ff5c39,#b8320f)', tint:'#ff5c39'},
{grad:'linear-gradient(135deg,#fb923c,#a3480a)', tint:'#fb923c'},
{grad:'linear-gradient(135deg,#fbbf24,#8a5a06)', tint:'#fbbf24'},
{grad:'linear-gradient(135deg,#6b7280,#26292e)', tint:'#94a3b8'},
{grad:'linear-gradient(135deg,#34d399,#0b6152)', tint:'#34d399'},
{grad:'linear-gradient(135deg,#e8e8e8,#6f6f6f)', tint:'#d4d4d4'}
];
var TREE=[
{t:'Posts', target:'detail', label:'Post öffnen', g:0,
kids:[
{t:'Veröffentlicht', g:0, kids:[], items:[
{t:'Velvet Drop', s:'Karussell · 3 Bilder · 12.07.', p:U+'pasted-1784643111353-0.png', h:20},
{t:'Bogenfenster', s:'Toner im Tageslicht · 04.07.', p:U+'pasted-1784643165286-0.png', h:60},
{t:'Poster-Ad', s:'Statement-Motiv · 21.06.', p:U+'pasted-1784625407187-0.png', h:340}
]},
{t:'Geplant', g:2, kids:[], items:[
{t:'Glow Serum', s:'3er-Kette · Slot 08.08.', p:U+'pasted-1784643149542-0.png', h:40},
{t:'Age Decoder', s:'Produkt-Still · Slot 14.08.', p:U+'pasted-1784663383522-0.png', h:10}
]}
],
items:[
{t:'Velvet Drop', s:'Karussell · 3 Bilder · 12.07.', p:U+'pasted-1784643111353-0.png', h:20},
{t:'Glow Serum', s:'3er-Kette · 08.07.', p:U+'pasted-1784643149542-0.png', h:40},
{t:'Bogenfenster', s:'Toner im Tageslicht · 04.07.', p:U+'pasted-1784643165286-0.png', h:60},
{t:'Age Decoder', s:'Produkt-Still · 29.06.', p:U+'pasted-1784663383522-0.png', h:10},
{t:'Poster-Ad', s:'Statement-Motiv · 21.06.', p:U+'pasted-1784625407187-0.png', h:340}
]},
{t:'Videos', target:'scene-results', label:'Videos ansehen', g:1,
kids:[
{t:'Vote offen', g:1, kids:[], items:[
{t:'Roter Lippenstift', s:'Close-up · 3 Varianten', p:U+'pasted-1784663207742-0.png', h:30}
]},
{t:'Ads', g:4, kids:[], items:[
{t:'Abendlicht #31', s:'ROAS 3,1× · CTR 2,4 %', p:U+'pasted-1784663233931-0.png', h:70},
{t:'Stadion-Spot', s:'Kaiserslautern · 12 Videos', p:U+'pasted-1784663261701-0.png', h:255}
]}
],
items:[
{t:'Roter Lippenstift', s:'Close-up · Vote offen', p:U+'pasted-1784663207742-0.png', h:30},
{t:'Velvet Teaser', s:'9:16 · 8 s · veröffentlicht', p:U+'pasted-1784663200459-0.png', h:50},
{t:'Abendlicht #31', s:'Ad-Performance ROAS 3,1×', p:U+'pasted-1784663233931-0.png', h:70},
{t:'Stadion-Spot', s:'Kaiserslautern · 12 Videos', p:U+'pasted-1784663261701-0.png', h:255}
]},
{t:'Modelle', target:'model-result', label:'Modell öffnen', g:2,
kids:[
{t:'Archiv', g:3, kids:[], items:[
{t:'Mara v3', s:'Archiviert · Elo 5310', p:U+'pasted-1784663207742-0.png', h:210}
]}
],
items:[
{t:'Mara v4', s:'Freigegeben · Elo 6120', p:U+'pasted-1784663200459-0.png', h:260},
{t:'Noor v2', s:'In Prüfung · Elo 5480', p:U+'pasted-1784663261701-0.png', h:290},
{t:'Mara v3', s:'Archiviert · Elo 5310', p:U+'pasted-1784663207742-0.png', h:210}
]},
{t:'Locations', target:'kb-detail', label:'Location öffnen', g:3,
kids:[
{t:'Kaiserslautern', g:3, kids:[], items:[
{t:'Stadion', s:'Unter-Attribut · Elo 5560', p:null, h:200},
{t:'Innenstadt', s:'Elo 5120 · 4 Videos', p:null, h:180}
]}
],
items:[
{t:'Kaiserslautern', s:'Elo 5240 ▲ · 12 Videos · 8 Siege', p:null, h:170},
{t:'Stadion', s:'Unter-Attribut · Elo 5560', p:null, h:200},
{t:'Köln', s:'Archiviert · Elo 4980 ▼', p:null, h:230}
]},
{t:'Assets', target:'improve-select', label:'Asset öffnen', g:4,
kids:[
{t:'Gesichter', g:2, kids:[], items:[
{t:'Mara v4', s:'Freigegeben · 6120', p:U+'pasted-1784663200459-0.png', h:150}
]},
{t:'Produkte', g:0, kids:[], items:[
{t:'Rouge No. 5', s:'Freigegeben · 6480', p:U+'pasted-1784643111353-0.png', h:130},
{t:'Logo Lockup', s:'v1 · nie verzerren', p:U+'pasted-1784643165286-0.png', h:110}
]}
],
items:[
{t:'Mara v4', s:'Gesicht · freigegeben · 6120', p:U+'pasted-1784663200459-0.png', h:150},
{t:'Rouge No. 5', s:'Produkt · freigegeben · 6480', p:U+'pasted-1784643111353-0.png', h:130},
{t:'Logo Lockup', s:'Brand · v1 · nie verzerren', p:U+'pasted-1784643165286-0.png', h:110}
]},
{t:'Verbessern', target:'improve-select', label:'Verbessern starten', g:5,
kids:[], items:[
{t:'Gesicht: Mara', s:'Offen · Referenz fehlt', p:U+'pasted-1784663200459-0.png', h:0},
{t:'Produkt: Rouge', s:'Offen · 2 Varianten', p:U+'pasted-1784643149542-0.png', h:15},
{t:'Szene: Abendlicht', s:'Läuft · Vote offen', p:U+'pasted-1784663233931-0.png', h:45}
]}
];
var FPATH=[]; /* aktueller Pfad im Ordnerbaum */
function nodeAt(path){ var n={kids:TREE, t:'Ordner'}; path.forEach(function(i){ n=n.kids[i]; }); return n; }
function grad(n){ return FGRADS[n.g||0]; }
function previewItems(n){
var out=(n.items||[]).slice(0,5);
if(!out.length) (n.kids||[]).forEach(function(k){ (k.items||[]).forEach(function(it){ if(out.length<5) out.push(it); }); });
return out;
}
function countLabel(n){
var k=(n.kids||[]).length, i=(n.items||[]).length, parts=[];
if(k) parts.push(k+' Ordner');
parts.push(i+(i===1?' Bild':' Bilder'));
return parts.join(' · ');
}
/* ---- Ordner-Kachel (3D) ---- */
function foldHtml(n, path){
var g=grad(n), prev=previewItems(n), c=prev.length, mid=(c-1)/2;
var cards=prev.map(function(it,i){
var fac = c>1 ? (i-mid)/mid : 0;
var st='--i:'+i+'; --tx:'+(fac*41).toFixed(1)+'px; --ty:'+(Math.abs(fac)*10).toFixed(1)+'px; --rot:'+(fac*20).toFixed(1)+'deg;';
if(!it.p) st+=' background:'+dummy(it.h||40)+';';
var img = it.p ? '<img src="'+it.p+'" alt="" onerror="this.remove()">' : '';
return '<div class="f-card" data-i="'+i+'" style="'+st+'">'+img+'<div class="shade"></div><b>'+it.t+'</b></div>';
}).join('');
return '<div class="fold" data-path="'+path.join('-')+'">'+
'<div class="glow" style="background:radial-gradient(circle at 50% 72%,'+g.tint+' 0%,transparent 70%)"></div>'+
'<div class="f-menu" data-menu="'+path.join('-')+'" title="Optionen">'+
'<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.9"/><circle cx="12" cy="12" r="1.9"/><circle cx="12" cy="19" r="1.9"/></svg>'+
'</div>'+
'<div class="f-stage">'+
'<div class="f-piece f-back" style="background:'+g.grad+'"></div>'+
'<div class="f-piece f-tab" style="background:'+g.grad+'"></div>'+
'<div class="f-stack">'+cards+'</div>'+
'<div class="f-piece f-front" style="background:'+g.grad+'"></div>'+
'</div>'+
'<div class="f-meta"><h4>'+n.t+'</h4><p>'+countLabel(n)+'</p></div>'+
'<div class="f-hint">Tippen</div>'+
'</div>';
}
/* ---- Ghost-Ordner: gleiches Element, transparente Textur ---- */
function ghostHtml(path){
return '<div class="fold ghost" data-new="'+path.join('-')+'">'+
'<div class="f-stage">'+
'<div class="f-piece f-back"></div>'+
'<div class="f-piece f-tab"></div>'+
'<div class="f-plus"><svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg></div>'+
'<div class="f-piece f-front"></div>'+
'</div>'+
'<div class="f-meta"><h4>Ordner erstellen</h4><p>neu anlegen</p></div>'+
'</div>';
}
/* ---- Bild-Kachel (gleiches Raster wie die Ordner) ---- */
function tileHtml(it, i){
var bg = it.p ? '' : ' background:'+dummy(it.h||40)+';';
var img = it.p ? '<img src="'+it.p+'" alt="" onerror="this.remove()">' : '';
return '<div class="f-tile" data-item="'+i+'" style="'+bg+'">'+img+
'<div class="shade"></div><div class="cap"><h4>'+it.t+'</h4><p>'+it.s+'</p></div></div>';
}
function pathOf(str){ return str ? str.split('-').map(Number) : []; }
function wireFolds(wrap, onEnter){
wrap.querySelectorAll('.fold').forEach(function(el){
el.addEventListener('mouseenter',function(){ el.classList.add('open'); });
el.addEventListener('mouseleave',function(){ el.classList.remove('open'); });
el.addEventListener('click',function(e){
if(el.dataset.new!==undefined){ fnOpen('create', pathOf(el.dataset.new)); return; }
var path=pathOf(el.dataset.path);
if(e.target.closest('.f-menu')){ fmOpen(e.target.closest('.f-menu'), path); return; }
var c=e.target.closest('.f-card');
if(c && el.classList.contains('open')){ fbOpen(path, +c.dataset.i, true); return; }
if(el.classList.contains('open')){ onEnter(path); return; }
wrap.querySelectorAll('.fold').forEach(function(o){ if(o!==el) o.classList.remove('open'); });
el.classList.add('open');
var hint=el.querySelector('.f-hint'); if(hint) hint.textContent='Öffnen';
});
});
}
/* ---- Profil-Raster ---- */
function renderFolders(){
var wrap=document.getElementById('folders');
if(!wrap) return;
wrap.innerHTML = TREE.map(function(n,i){ return foldHtml(n,[i]); }).join('') + ghostHtml([]);
wireFolds(wrap, openFolder);
}
/* ---- Ordner-Detail ---- */
function openFolder(path){
FPATH=path.slice();
renderFolderScreen();
go('folder');
}
function renderFolderScreen(){
var n=nodeAt(FPATH);
document.getElementById('fdTitle').textContent=n.t;
var crumb=['Profil'], p=[];
FPATH.forEach(function(i){ p.push(i); crumb.push(nodeAt(p).t); });
document.getElementById('fdCrumb').textContent=crumb.join(' ')+' · '+countLabel(n);
document.getElementById('fdBack').onclick=function(){
if(FPATH.length>1){ FPATH.pop(); renderFolderScreen(); window.scrollTo(0,0); } else go('profile');
};
var grid=document.getElementById('fdGrid');
grid.innerHTML = (n.kids||[]).map(function(k,i){ return foldHtml(k, FPATH.concat(i)); }).join('')
+ (n.items||[]).map(function(it,i){ return tileHtml(it,i); }).join('')
+ ghostHtml(FPATH);
wireFolds(grid, openFolder);
grid.querySelectorAll('.f-tile').forEach(function(el){
el.addEventListener('click',function(){ fbOpen(FPATH, +el.dataset.item, false); });
});
document.getElementById('fdEmpty').style.display = ((n.kids||[]).length+(n.items||[]).length) ? 'none' : 'block';
}
function refreshFolders(){
renderFolders();
if(document.getElementById('folder').classList.contains('active')) renderFolderScreen();
}
/* ---- Kontextmenü (Liquid Glass) ---- */
var FM={path:null};
function fmOpen(btn, path){
FM.path=path.slice();
var n=nodeAt(path), m=document.getElementById('fmenu');
m.innerHTML =
'<div class="mi" onclick="fnOpen(\'rename\', FM.path)">'+
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20h4l10-10-4-4L4 16z"/><path d="M14 6l4 4"/></svg>Name ändern</div>'+
'<div class="hd">Farbe</div>'+
'<div class="sw">'+FGRADS.map(function(g,i){
return '<i class="'+(i===(n.g||0)?'on':'')+'" style="background:'+g.grad+'" onclick="fmColor('+i+')"></i>';
}).join('')+'</div>'+
'<div class="mi del" onclick="fmDelete()">'+
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13"/></svg>Ordner löschen</div>';
m.classList.add('open');
var r=btn.getBoundingClientRect(), w=208;
var left=Math.min(Math.max(10, r.right-w), window.innerWidth-w-10);
var top=r.bottom+8;
if(top+m.offsetHeight>window.innerHeight-10) top=r.top-m.offsetHeight-8;
top=Math.min(Math.max(10, top), Math.max(10, window.innerHeight-m.offsetHeight-10));
m.style.left=left+'px'; m.style.top=top+'px';
document.querySelectorAll('.f-menu').forEach(function(b){ b.classList.remove('on'); });
btn.classList.add('on');
}
function fmClose(){
document.getElementById('fmenu').classList.remove('open');
document.querySelectorAll('.f-menu').forEach(function(b){ b.classList.remove('on'); });
}
function fmColor(i){
var n=nodeAt(FM.path); n.g=i; fmClose(); refreshFolders();
}
function fmDelete(){
var parent=nodeAt(FM.path.slice(0,-1)), idx=FM.path[FM.path.length-1];
parent.kids.splice(idx,1);
fmClose();
if(document.getElementById('folder').classList.contains('active') && FM.path.length<=FPATH.length){
var same=FPATH.slice(0,FM.path.length).join('-')===FM.path.join('-');
if(same){ FPATH=FM.path.slice(0,-1); if(!FPATH.length){ renderFolders(); go('profile'); return; } }
}
refreshFolders();
}
document.addEventListener('click',function(e){
if(!e.target.closest('.fmenu') && !e.target.closest('.f-menu')) fmClose();
},true);
/* ---- Dialog: erstellen / umbenennen ---- */
var FN={mode:'create', path:[]};
function fnOpen(mode, path){
fmClose();
FN.mode=mode; FN.path=path.slice();
var dlg=document.getElementById('fdlg'), inp=document.getElementById('fnName');
document.getElementById('fnTitle').textContent = mode==='create' ? 'Neuer Ordner' : 'Name ändern';
inp.value = mode==='rename' ? nodeAt(path).t : '';
inp.placeholder = mode==='create' ? 'z. B. Kampagne Herbst' : 'Ordnername';
dlg.classList.add('open');
setTimeout(function(){ inp.focus(); },60);
}
function fnClose(){ document.getElementById('fdlg').classList.remove('open'); }
function fnSave(){
var inp=document.getElementById('fnName'), name=(inp.value||'').trim();
if(!name){ inp.focus(); return; }
if(FN.mode==='create'){
var n=nodeAt(FN.path);
if(!n.kids) n.kids=[];
n.kids.push({t:name, g:(n.kids.length+2)%6, kids:[], items:[]});
} else {
nodeAt(FN.path).t=name;
}
fnClose(); refreshFolders();
}
document.getElementById('fnName').addEventListener('keydown',function(e){ if(e.key==='Enter') fnSave(); });
/* ---- Lightbox ---- */
var FB={path:[], i:0, fromPreview:false};
function fbOpen(path,i,fromPreview){
FB.path=path.slice(); FB.i=i; FB.fromPreview=!!fromPreview;
fbPaint();
document.getElementById('fbox').classList.add('open');
document.body.style.overflow='hidden';
}
function fbClose(){
document.getElementById('fbox').classList.remove('open');
document.body.style.overflow = document.getElementById('explore').classList.contains('active') ? 'hidden' : '';
}
function fbList(){ var n=nodeAt(FB.path); return (n.items||[]).length ? n.items : previewItems(n); }
function fbNav(d){
var l=fbList(), n=FB.i+d;
if(n<0||n>=l.length) return;
FB.i=n; fbPaint();
}
function fbPaint(){
var node=nodeAt(FB.path), l=fbList(), it=l[FB.i];
if(!it) return;
var img=document.getElementById('fbImg'), shot=img.parentElement;
if(it.p){ img.style.display='block'; img.src=it.p; shot.style.background='#111'; }
else { img.style.display='none'; shot.style.background=dummy(it.h||40); }
document.getElementById('fbTitle').textContent=it.t;
document.getElementById('fbSub').textContent=node.t+' · '+it.s;
document.getElementById('fbDots').innerHTML=l.map(function(_,k){
return '<i class="'+(k===FB.i?'on':'')+'" onclick="FB.i='+k+';fbPaint()"></i>';
}).join('')+'<span class="count">'+(FB.i+1)+' / '+l.length+'</span>';
var b=document.getElementById('fbOpen');
if(FB.fromPreview){
b.textContent='Ordner „'+node.t+'" öffnen';
b.onclick=function(){ fbClose(); openFolder(FB.path); };
} else {
b.textContent=node.label||'Im Editor öffnen';
var tgt=node.target||'detail';
b.onclick=function(){ fbClose(); go(tgt); };
}
document.getElementById('fbPrev').classList.toggle('off', FB.i===0);
document.getElementById('fbNext').classList.toggle('off', FB.i===l.length-1);
}
document.addEventListener('keydown',function(e){
if(document.getElementById('fdlg').classList.contains('open')){ if(e.key==='Escape') fnClose(); return; }
if(!document.getElementById('fbox').classList.contains('open')) return;
if(e.key==='Escape') fbClose();
if(e.key==='ArrowRight') fbNav(1);
if(e.key==='ArrowLeft') fbNav(-1);
});
renderFolders();
</script> </script>
</body> </body>
</html> </html>