concept3-holowire.jsx
1 // Concept 3 — HOLO WIREFRAME TURNTABLE 2 // Pure holographic. Glassmorphic panels floating over deep purple-black. 3 // Ship centered, shown as translucent wireframe with overlapping 4 // orthographic planes. Feminine-industrial; think Destiny/Horizon holo. 5 6 function Concept3_HoloWire({ seed = "C3-default" }) { 7 const ship = makeShip(seed); 8 const bg1 = "#070415"; 9 const bg2 = "#120a2e"; 10 const violet = "#b88cff"; 11 const mint = "#7affd0"; 12 const pink = "#ff7adc"; 13 const dim = "rgba(184,140,255,0.55)"; 14 const glass = "rgba(184,140,255,0.06)"; 15 16 const Panel = ({ children, style, flush }) => ( 17 <div style={{ 18 background: glass, backdropFilter:"blur(6px)", border:`1px solid ${violet}33`, 19 padding: flush ? 0 : 18, borderRadius: 2, ...style, 20 }}>{children}</div> 21 ); 22 23 const Stat = ({ k, v, unit, accent = violet }) => ( 24 <div> 25 <div style={{fontFamily:"JetBrains Mono", fontSize:9, letterSpacing:2, color:dim, textTransform:"uppercase"}}>{k}</div> 26 <div style={{fontFamily:"Orbitron", fontWeight:500, fontSize:22, color:accent, letterSpacing:1, lineHeight:1.1, marginTop:2}}> 27 {v}<span style={{fontSize:11, color:dim, marginLeft:4, fontFamily:"JetBrains Mono", fontWeight:400}}>{unit}</span> 28 </div> 29 </div> 30 ); 31 32 return ( 33 <div style={{ 34 width:1280, height:880, position:"relative", overflow:"hidden", 35 background: `radial-gradient(ellipse at 30% 20%, ${bg2} 0%, ${bg1} 60%), 36 radial-gradient(ellipse at 70% 80%, rgba(255,122,220,0.08) 0%, transparent 60%)`, 37 color: violet, fontFamily:"'Space Grotesk', sans-serif", 38 }}> 39 {/* Ambient particles */} 40 <svg style={{position:"absolute", inset:0, pointerEvents:"none", opacity:0.6}} width="100%" height="100%"> 41 {Array.from({length: 70}).map((_,i) => { 42 const rng = mulberry32(hashStr(seed+"p"+i)); 43 return <circle key={i} cx={rng()*1280} cy={rng()*880} r={rng()*1.4} fill={violet} opacity={0.3+rng()*0.5}/>; 44 })} 45 </svg> 46 47 {/* Top nav — floating island */} 48 <div style={{position:"absolute", top:20, left:"50%", transform:"translateX(-50%)", display:"flex", gap:2, background:"rgba(7,4,21,0.6)", border:`1px solid ${violet}33`, borderRadius:100, padding:4, backdropFilter:"blur(10px)"}}> 49 {["Catalog","Fleet","Brokers","Orders","Account"].map((t,i) => ( 50 <div key={t} style={{ 51 padding:"8px 18px", fontFamily:"JetBrains Mono", fontSize:10, letterSpacing:2, textTransform:"uppercase", 52 borderRadius:100, background: i===0 ? `${violet}22` : "transparent", color: i===0 ? "#fff" : dim, cursor:"pointer", 53 }}>{t}</div> 54 ))} 55 </div> 56 57 {/* Left floating specs cluster */} 58 <div style={{position:"absolute", top:90, left:32, width:300, display:"flex", flexDirection:"column", gap:14}}> 59 <Panel> 60 <div style={{fontFamily:"JetBrains Mono", fontSize:9, letterSpacing:2, color:dim}}>▸ {ship.cls.name.toUpperCase()} · {ship.mfg.code}</div> 61 <div style={{fontFamily:"Orbitron", fontWeight:700, fontSize:28, color:"#fff", letterSpacing:1, marginTop:6, lineHeight:1.05}}>{ship.model}</div> 62 <div style={{fontFamily:"JetBrains Mono", fontSize:10, color:dim, marginTop:8, letterSpacing:1}}>SN · {ship.serial}</div> 63 <div style={{display:"flex", gap:6, marginTop:12, flexWrap:"wrap"}}> 64 <Pill color={mint}>◉ {ship.avail}</Pill> 65 <Pill color={violet}>{ship.faction}</Pill> 66 <Pill color={pink}>{ship.cond}</Pill> 67 </div> 68 </Panel> 69 70 <Panel> 71 <div style={{fontFamily:"JetBrains Mono", fontSize:9, letterSpacing:2, color:dim, marginBottom:10}}>▸ CORE SPECS</div> 72 <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:14}}> 73 <Stat k="Length" v={ship.length_m} unit="m"/> 74 <Stat k="Mass" v={ship.mass.toLocaleString()} unit="t" accent={mint}/> 75 <Stat k="Jump" v={ship.jumpRange} unit="ly" accent={pink}/> 76 <Stat k="Top spd" v={(ship.topSpeed/1000).toFixed(1)} unit="k m/s"/> 77 </div> 78 </Panel> 79 80 <Panel> 81 <div style={{fontFamily:"JetBrains Mono", fontSize:9, letterSpacing:2, color:dim, marginBottom:10}}>▸ LINEAGE</div> 82 <div style={{fontFamily:"JetBrains Mono", fontSize:11, color:"rgba(255,255,255,0.85)", lineHeight:1.8}}> 83 <div style={{display:"flex", justifyContent:"space-between"}}><span style={{color:dim}}>Forge</span><span>{ship.mfg.name}</span></div> 84 <div style={{display:"flex", justifyContent:"space-between"}}><span style={{color:dim}}>Yards</span><span>{ship.mfg.loc}</span></div> 85 <div style={{display:"flex", justifyContent:"space-between"}}><span style={{color:dim}}>Era</span><span>{ship.year} cmn</span></div> 86 <div style={{display:"flex", justifyContent:"space-between"}}><span style={{color:dim}}>Drive</span><span>{ship.drive}</span></div> 87 </div> 88 </Panel> 89 </div> 90 91 {/* Right floating cluster */} 92 <div style={{position:"absolute", top:90, right:32, width:320, display:"flex", flexDirection:"column", gap:14}}> 93 <Panel> 94 <div style={{fontFamily:"JetBrains Mono", fontSize:9, letterSpacing:2, color:dim}}>▸ PROCUREMENT VALUE</div> 95 <div style={{fontFamily:"Orbitron", fontWeight:900, fontSize:40, color:"#fff", letterSpacing:1, marginTop:4, lineHeight:1, 96 background:`linear-gradient(90deg, ${violet}, ${pink})`, WebkitBackgroundClip:"text", WebkitTextFillColor:"transparent", 97 }}>₵{formatCred(ship.price)}</div> 98 <div style={{fontFamily:"JetBrains Mono", fontSize:10, color:dim, marginTop:6, letterSpacing:1}}>or lease ₵{formatCred(Math.round(ship.price/120))}/cycle</div> 99 </Panel> 100 101 <Panel> 102 <div style={{fontFamily:"JetBrains Mono", fontSize:9, letterSpacing:2, color:dim, marginBottom:12}}>▸ PERFORMANCE ENVELOPE</div> 103 <RadarChart ship={ship} violet={violet} mint={mint} pink={pink}/> 104 </Panel> 105 106 <Panel> 107 <div style={{fontFamily:"JetBrains Mono", fontSize:9, letterSpacing:2, color:dim, marginBottom:10}}>▸ RECENT OBSERVATIONS</div> 108 <div style={{fontFamily:"JetBrains Mono", fontSize:11, color:"rgba(255,255,255,0.8)", lineHeight:1.7}}> 109 {[["14h", "HANGAR SCAN · HULL 98.4%"], 110 ["2d", "FLIGHT LOG · 412 JUMPS"], 111 ["9d", "OVERHAUL · DRIVE CORE"], 112 ["31d", "LISTED · BROKER VNT"], 113 ].map(([t, txt]) => ( 114 <div key={t} style={{display:"flex", gap:10, padding:"3px 0"}}> 115 <span style={{color:mint, width:28}}>{t}</span> 116 <span>{txt}</span> 117 </div> 118 ))} 119 </div> 120 </Panel> 121 </div> 122 123 {/* CENTER — holo stage */} 124 <div style={{position:"absolute", left:360, right:368, top:110, bottom:120, display:"flex", flexDirection:"column", alignItems:"center"}}> 125 {/* Stage ring */} 126 <div style={{position:"relative", width:"100%", height:"70%"}}> 127 {/* concentric circles */} 128 <svg style={{position:"absolute", inset:0}} width="100%" height="100%" viewBox="0 0 520 440" preserveAspectRatio="xMidYMid meet"> 129 <defs> 130 <radialGradient id="disk" cx="50%" cy="70%" r="60%"> 131 <stop offset="0%" stopColor={violet} stopOpacity="0.25"/> 132 <stop offset="100%" stopColor={violet} stopOpacity="0"/> 133 </radialGradient> 134 </defs> 135 <ellipse cx="260" cy="340" rx="240" ry="40" fill="url(#disk)"/> 136 <ellipse cx="260" cy="340" rx="240" ry="40" fill="none" stroke={violet} strokeOpacity="0.4" strokeWidth="1"/> 137 <ellipse cx="260" cy="340" rx="180" ry="30" fill="none" stroke={violet} strokeOpacity="0.3" strokeWidth="1" strokeDasharray="4,4"/> 138 <ellipse cx="260" cy="340" rx="120" ry="20" fill="none" stroke={violet} strokeOpacity="0.25" strokeWidth="1"/> 139 {/* tick marks around the ring */} 140 {Array.from({length: 24}).map((_,i) => { 141 const a = (i/24)*2*Math.PI; 142 const rx = 240, ry = 40, cx = 260, cy = 340; 143 const x = cx + Math.cos(a)*rx; 144 const y = cy + Math.sin(a)*ry; 145 return <circle key={i} cx={x} cy={y} r={1.5} fill={violet} opacity={0.6}/>; 146 })} 147 </svg> 148 149 {/* layered orthographic wireframes */} 150 <div style={{position:"absolute", inset:"5% 10% 30%", filter:`drop-shadow(0 0 20px ${violet}88)`}}> 151 <ShipSilhouette ship={ship} stroke={violet} fill="rgba(184,140,255,0.08)" glow={false} strokeWidth={1.2}/> 152 </div> 153 {/* secondary plane (top view, smaller, offset) */} 154 <div style={{position:"absolute", top:"6%", left:"6%", width:180, opacity:0.55, transform:"rotate(-6deg)"}}> 155 <ShipSilhouette ship={ship} stroke={mint} strokeWidth={1} glow detail="min"/> 156 </div> 157 <div style={{position:"absolute", top:"14%", right:"4%", width:140, opacity:0.45, transform:"rotate(90deg)"}}> 158 <ShipSilhouette ship={ship} stroke={pink} strokeWidth={1} glow detail="min"/> 159 </div> 160 161 {/* ring labels */} 162 <div style={{position:"absolute", bottom:"10%", left:"8%", fontFamily:"JetBrains Mono", fontSize:10, color:dim, letterSpacing:2}}>AZ 000°</div> 163 <div style={{position:"absolute", bottom:"10%", right:"8%", fontFamily:"JetBrains Mono", fontSize:10, color:dim, letterSpacing:2}}>AZ 180°</div> 164 165 {/* Floating callouts */} 166 <FloatCallout top="22%" left="18%" color={mint} label="HARDPOINTS" value={ship.hardpoints}/> 167 <FloatCallout top="30%" right="14%" color={pink} label="SHIELD" value={`${ship.shields} GJ`}/> 168 <FloatCallout top="58%" left="22%" color={violet} label="CREW" value={ship.crew}/> 169 </div> 170 171 {/* view selector */} 172 <div style={{display:"flex", gap:8, marginTop:12}}> 173 {["ORTHO","TOP","SIDE","INTERIOR","SECTION"].map((v,i) => ( 174 <div key={v} style={{ 175 padding:"6px 14px", fontFamily:"JetBrains Mono", fontSize:10, letterSpacing:2, 176 border:`1px solid ${violet}44`, color: i===0 ? "#fff" : dim, 177 background: i===0 ? `${violet}22` : "transparent", borderRadius:2, 178 }}>{v}</div> 179 ))} 180 </div> 181 </div> 182 183 {/* BOTTOM floating action bar */} 184 <div style={{position:"absolute", left:"50%", bottom:24, transform:"translateX(-50%)", display:"flex", gap:10, alignItems:"center", padding:8, background:"rgba(7,4,21,0.7)", border:`1px solid ${violet}44`, borderRadius:100, backdropFilter:"blur(12px)"}}> 185 <div style={{padding:"10px 20px", fontFamily:"JetBrains Mono", fontSize:10, color:dim, letterSpacing:2}}>DELIVERY · {ship.delivery} CYC</div> 186 <div style={{padding:"10px 20px", fontFamily:"JetBrains Mono", fontSize:10, color:dim, letterSpacing:2, borderLeft:`1px solid ${violet}33`}}>WARRANTY · {ship.warranty}</div> 187 <button style={{ 188 padding:"12px 28px", background:"transparent", color:"#fff", border:`1px solid ${violet}`, 189 fontFamily:"Orbitron", fontWeight:700, fontSize:11, letterSpacing:3, cursor:"pointer", borderRadius:100, 190 }}>SAVE</button> 191 <button style={{ 192 padding:"12px 36px", background:`linear-gradient(90deg, ${violet}, ${pink})`, color:"#0a0520", border:"none", 193 fontFamily:"Orbitron", fontWeight:900, fontSize:12, letterSpacing:3, cursor:"pointer", borderRadius:100, 194 boxShadow:`0 0 24px ${violet}aa`, 195 }}>◈ PROCURE · ₵{formatCred(ship.price)}</button> 196 </div> 197 </div> 198 ); 199 } 200 201 function Pill({ color, children }) { 202 return <span style={{ 203 padding:"3px 8px", fontFamily:"JetBrains Mono", fontSize:9, letterSpacing:1.5, 204 border:`1px solid ${color}`, color, borderRadius:2, 205 }}>{children}</span>; 206 } 207 208 function FloatCallout({ top, left, right, color, label, value }) { 209 return ( 210 <div style={{position:"absolute", top, left, right, fontFamily:"JetBrains Mono", letterSpacing:1.5}}> 211 <div style={{width:50, height:1, background:color, opacity:0.6, marginBottom:5}}/> 212 <div style={{fontSize:9, color:`${color}aa`}}>{label}</div> 213 <div style={{fontSize:14, color:"#fff", fontWeight:500, marginTop:2, fontFamily:"Orbitron"}}>{value}</div> 214 </div> 215 ); 216 } 217 218 function RadarChart({ ship, violet, mint, pink }) { 219 const axes = [ 220 ["THRUST", Math.min(1, ship.thrust/100)], 221 ["SPEED", Math.min(1, ship.topSpeed/3000)], 222 ["MANEUV", Math.min(1, ship.maneuver/100)], 223 ["SHIELD", Math.min(1, ship.shields/2000)], 224 ["ARMOR", Math.min(1, ship.armor/2000)], 225 ["JUMP", Math.min(1, ship.jumpRange/40)], 226 ]; 227 const cx = 140, cy = 100, r = 80; 228 const pts = axes.map((ax,i) => { 229 const a = (i/axes.length)*2*Math.PI - Math.PI/2; 230 const rr = r*ax[1]; 231 return [cx + Math.cos(a)*rr, cy + Math.sin(a)*rr]; 232 }); 233 return ( 234 <svg width={280} height={200} style={{display:"block", margin:"0 auto"}}> 235 {/* rings */} 236 {[0.33, 0.66, 1].map(f => { 237 const rpts = axes.map((_,i) => { 238 const a = (i/axes.length)*2*Math.PI - Math.PI/2; 239 return [cx + Math.cos(a)*r*f, cy + Math.sin(a)*r*f]; 240 }); 241 return <polygon key={f} points={rpts.map(p=>p.join(",")).join(" ")} fill="none" stroke={violet} strokeOpacity={0.3} strokeWidth={1}/>; 242 })} 243 {/* axes */} 244 {axes.map((ax,i) => { 245 const a = (i/axes.length)*2*Math.PI - Math.PI/2; 246 return <line key={i} x1={cx} y1={cy} x2={cx+Math.cos(a)*r} y2={cy+Math.sin(a)*r} stroke={violet} strokeOpacity={0.2}/>; 247 })} 248 {/* polygon */} 249 <polygon points={pts.map(p=>p.join(",")).join(" ")} fill={`${mint}33`} stroke={mint} strokeWidth={1.5} style={{filter:`drop-shadow(0 0 8px ${mint})`}}/> 250 {pts.map((p,i) => <circle key={i} cx={p[0]} cy={p[1]} r={2.5} fill={mint}/>)} 251 {/* labels */} 252 {axes.map((ax,i) => { 253 const a = (i/axes.length)*2*Math.PI - Math.PI/2; 254 const lx = cx + Math.cos(a)*(r+14); 255 const ly = cy + Math.sin(a)*(r+14); 256 return <text key={i} x={lx} y={ly} textAnchor="middle" dominantBaseline="middle" fontFamily="JetBrains Mono" fontSize={9} fill="rgba(184,140,255,0.7)" letterSpacing={1.5}>{ax[0]}</text>; 257 })} 258 </svg> 259 ); 260 } 261 262 Object.assign(window, { Concept3_HoloWire });