/ concepts / concept4-reticle.jsx
concept4-reticle.jsx
  1  // Concept 4 — RADAR RETICLE DEALER
  2  // Circular radar-scope centerpiece. Amber-on-black arcade war-room.
  3  // Ship floats at the center of a sweeping radar; surrounding sectors
  4  // hold specs like quadrants of a targeting scope.
  5  
  6  function Concept4_Reticle({ seed = "C4-default" }) {
  7    const ship = makeShip(seed);
  8    const bg = "#080604";
  9    const amber = "#ffbe2e";
 10    const amberDim = "#8c6610";
 11    const red = "#ff4d4d";
 12    const green = "#8fff6a";
 13    const paper = "#fff3d0";
 14  
 15    const Tag = ({ children, danger, ok }) => (
 16      <span style={{
 17        border:`1px solid ${danger?red:ok?green:amber}`,
 18        color: danger?red:ok?green:amber,
 19        padding:"2px 8px", fontFamily:"'IBM Plex Mono',monospace", fontSize:10, letterSpacing:2,
 20        background: "#0d0907",
 21      }}>{children}</span>
 22    );
 23  
 24    const SpecLine = ({ k, v, u }) => (
 25      <div style={{display:"flex", justifyContent:"space-between", fontFamily:"'IBM Plex Mono',monospace", fontSize:11, color:paper, padding:"3px 0", borderBottom:`1px solid ${amberDim}44`}}>
 26        <span style={{color:amberDim, letterSpacing:1.5}}>{k}</span>
 27        <span>{v} <span style={{color:amberDim, fontSize:9}}>{u}</span></span>
 28      </div>
 29    );
 30  
 31    return (
 32      <div style={{
 33        width:1280, height:880, background: `radial-gradient(ellipse at center, #120c05 0%, ${bg} 70%)`,
 34        position:"relative", overflow:"hidden", color:amber, fontFamily:"'IBM Plex Mono', monospace",
 35      }}>
 36        {/* faint grid */}
 37        <div style={{position:"absolute", inset:0, backgroundImage:`linear-gradient(${amberDim}22 1px, transparent 1px), linear-gradient(90deg, ${amberDim}22 1px, transparent 1px)`, backgroundSize:"60px 60px"}}/>
 38  
 39        {/* TOP CHROME */}
 40        <div style={{display:"flex", alignItems:"center", justifyContent:"space-between", padding:"14px 28px", borderBottom:`1px solid ${amber}`, background:"rgba(0,0,0,0.5)", position:"relative", zIndex:2}}>
 41          <div style={{display:"flex", alignItems:"center", gap:20}}>
 42            <div style={{fontFamily:"Orbitron", fontWeight:900, fontSize:18, letterSpacing:5, color:amber}}>◉ WARROOM.VI</div>
 43            <div style={{fontSize:10, letterSpacing:2, color:amberDim}}>FLEET PROCUREMENT · TERMINAL 07-G</div>
 44          </div>
 45          <div style={{display:"flex", gap:18, fontSize:10, letterSpacing:2, color:amberDim}}>
 46            <span style={{color:green}}>◉ LINK 98%</span>
 47            <span>CHR 2396.114.08:41</span>
 48            <span>OBS RINGS · 8 CONTACTS</span>
 49            <span>★★★★☆ BROKER 4.6</span>
 50          </div>
 51        </div>
 52  
 53        {/* TOP TABS */}
 54        <div style={{display:"flex", borderBottom:`1px solid ${amber}`, background:"rgba(0,0,0,0.3)"}}>
 55          {["Fighters","Haulers","Yachts","Industrial","Capital","Science"].map((t,i) => (
 56            <div key={t} style={{
 57              padding:"8px 20px", fontSize:11, letterSpacing:3, textTransform:"uppercase",
 58              borderRight:`1px solid ${amberDim}44`, color: i===0 ? amber : amberDim,
 59              background: i===0 ? `${amber}15` : "transparent",
 60              borderBottom: i===0 ? `2px solid ${amber}` : "none",
 61            }}>{t}</div>
 62          ))}
 63        </div>
 64  
 65        {/* MAIN GRID */}
 66        <div style={{display:"grid", gridTemplateColumns:"1fr 560px 1fr", height:"calc(100% - 100px)"}}>
 67  
 68          {/* LEFT QUADRANTS */}
 69          <div style={{padding:"20px 24px", display:"flex", flexDirection:"column", gap:16, borderRight:`1px solid ${amberDim}66`}}>
 70            {/* Header */}
 71            <div>
 72              <div style={{fontSize:10, letterSpacing:3, color:amberDim}}>DESIGNATION / {ship.cls.code}</div>
 73              <div style={{fontFamily:"Orbitron", fontWeight:700, fontSize:28, color:paper, letterSpacing:1, marginTop:4, lineHeight:1.05}}>{ship.model}</div>
 74              <div style={{fontSize:11, letterSpacing:2, color:amber, marginTop:4}}>{ship.serial}</div>
 75              <div style={{display:"flex", gap:6, marginTop:10, flexWrap:"wrap"}}>
 76                <Tag>{ship.cls.name.toUpperCase()}</Tag>
 77                <Tag>{ship.mfg.code}</Tag>
 78                <Tag ok>{ship.avail}</Tag>
 79                <Tag danger>CLASS-3 EXPORT</Tag>
 80              </div>
 81            </div>
 82  
 83            {/* Q1 - Hull */}
 84            <div>
 85              <QuadHdr n="Q1" title="HULL"/>
 86              <SpecLine k="LENGTH"  v={ship.length_m} u="m"/>
 87              <SpecLine k="MASS"    v={ship.mass.toLocaleString()} u="t"/>
 88              <SpecLine k="ARMOR"   v={ship.armor.toLocaleString()} u="mm"/>
 89              <SpecLine k="SHIELD"  v={ship.shields.toLocaleString()} u="GJ"/>
 90              <SpecLine k="CREW"    v={ship.crew} u="pax"/>
 91            </div>
 92  
 93            {/* Q2 - Drive */}
 94            <div>
 95              <QuadHdr n="Q2" title="DRIVE"/>
 96              <SpecLine k="TYPE"    v={ship.drive} u=""/>
 97              <SpecLine k="TOP SPD" v={ship.topSpeed.toLocaleString()} u="m/s"/>
 98              <SpecLine k="THRUST" v={ship.thrust} u="g"/>
 99              <SpecLine k="JUMP"    v={ship.jumpRange} u="ly"/>
100            </div>
101  
102            {/* Q3 - Combat */}
103            <div>
104              <QuadHdr n="Q3" title="ARMAMENT"/>
105              <SpecLine k="HARDPT" v={ship.hardpoints} u="mt"/>
106              <SpecLine k="MANEUV" v={ship.maneuver} u="%"/>
107              <SpecLine k="SIG"    v={ship.signature} u="dB"/>
108            </div>
109          </div>
110  
111          {/* CENTER — RADAR SCOPE */}
112          <div style={{position:"relative", display:"flex", flexDirection:"column", alignItems:"center", padding:"16px"}}>
113            {/* scope */}
114            <div style={{position:"relative", width:520, height:520, marginTop:10}}>
115              <svg viewBox="0 0 520 520" width="520" height="520" style={{filter:`drop-shadow(0 0 30px ${amber}55)`}}>
116                <defs>
117                  <radialGradient id="scopeBg" cx="50%" cy="50%" r="50%">
118                    <stop offset="0%" stopColor={amber} stopOpacity="0.08"/>
119                    <stop offset="70%" stopColor={amber} stopOpacity="0.03"/>
120                    <stop offset="100%" stopColor={amber} stopOpacity="0"/>
121                  </radialGradient>
122                  <linearGradient id="sweep" x1="0" y1="0" x2="1" y2="0">
123                    <stop offset="0%" stopColor={amber} stopOpacity="0.3"/>
124                    <stop offset="100%" stopColor={amber} stopOpacity="0"/>
125                  </linearGradient>
126                </defs>
127                <circle cx="260" cy="260" r="250" fill="url(#scopeBg)"/>
128                {/* rings */}
129                {[60, 120, 180, 240].map(r => <circle key={r} cx="260" cy="260" r={r} fill="none" stroke={amber} strokeOpacity="0.35" strokeWidth="1"/>)}
130                {/* crosshairs */}
131                <line x1="10" y1="260" x2="510" y2="260" stroke={amber} strokeOpacity="0.3"/>
132                <line x1="260" y1="10" x2="260" y2="510" stroke={amber} strokeOpacity="0.3"/>
133                {/* cardinal ticks */}
134                {Array.from({length:36}).map((_,i) => {
135                  const a = (i*10)*Math.PI/180;
136                  const r1 = 240, r2 = i%9===0 ? 256 : 248;
137                  return <line key={i} x1={260+Math.cos(a)*r1} y1={260+Math.sin(a)*r1}
138                    x2={260+Math.cos(a)*r2} y2={260+Math.sin(a)*r2} stroke={amber} strokeWidth={i%9===0?1.5:0.8} opacity={i%9===0?0.9:0.4}/>;
139                })}
140                {/* bearing labels */}
141                {[[260,26,"000"],[494,260,"090"],[260,504,"180"],[26,260,"270"]].map(([x,y,t]) => (
142                  <text key={t} x={x} y={y} textAnchor="middle" dominantBaseline="middle" fontFamily="IBM Plex Mono" fontSize="11" fill={amber} letterSpacing="2">{t}</text>
143                ))}
144                {/* sweep arc */}
145                <path d="M 260 260 L 500 260 A 240 240 0 0 0 430 90 Z" fill="url(#sweep)"/>
146                {/* contacts / blips */}
147                {Array.from({length:6}).map((_,i) => {
148                  const rng = mulberry32(hashStr(seed+"blip"+i));
149                  const a = rng()*2*Math.PI;
150                  const r = 80+rng()*150;
151                  const x = 260+Math.cos(a)*r, y=260+Math.sin(a)*r;
152                  return <g key={i}>
153                    <circle cx={x} cy={y} r={3} fill={amber}/>
154                    <circle cx={x} cy={y} r={8} fill="none" stroke={amber} strokeOpacity={0.4}/>
155                  </g>;
156                })}
157              </svg>
158  
159              {/* Ship at center */}
160              <div style={{position:"absolute", left:"50%", top:"50%", width:260, height:140, transform:"translate(-50%, -50%)"}}>
161                <ShipSilhouette ship={ship} stroke={amber} fill="rgba(255,190,46,0.12)" glow strokeWidth={1.4}/>
162              </div>
163              {/* reticle */}
164              <div style={{position:"absolute", left:"50%", top:"50%", width:300, height:300, transform:"translate(-50%,-50%)", pointerEvents:"none"}}>
165                <div style={{position:"absolute", inset:0, border:`1px solid ${red}`, borderRadius:"50%", opacity:0.7}}/>
166                {["tl","tr","bl","br"].map(c => {
167                  const st = {position:"absolute", width:24, height:24, borderColor:red};
168                  if (c==="tl") Object.assign(st, {top:-4, left:-4, borderTop:`2px solid ${red}`, borderLeft:`2px solid ${red}`});
169                  if (c==="tr") Object.assign(st, {top:-4, right:-4, borderTop:`2px solid ${red}`, borderRight:`2px solid ${red}`});
170                  if (c==="bl") Object.assign(st, {bottom:-4, left:-4, borderBottom:`2px solid ${red}`, borderLeft:`2px solid ${red}`});
171                  if (c==="br") Object.assign(st, {bottom:-4, right:-4, borderBottom:`2px solid ${red}`, borderRight:`2px solid ${red}`});
172                  return <div key={c} style={st}/>;
173                })}
174              </div>
175            </div>
176  
177            {/* status line below scope */}
178            <div style={{marginTop:12, width:"100%", padding:"10px 16px", border:`1px solid ${amber}`, display:"flex", justifyContent:"space-between", fontSize:10, letterSpacing:2, background:"rgba(0,0,0,0.5)"}}>
179              <span>SCAN · 420 m</span>
180              <span style={{color:green}}>LOCK · TIER-2</span>
181              <span>TTL 00:02:14</span>
182              <span>RNG {ship.length_m}m</span>
183            </div>
184          </div>
185  
186          {/* RIGHT QUADRANTS */}
187          <div style={{padding:"20px 24px", display:"flex", flexDirection:"column", gap:16, borderLeft:`1px solid ${amberDim}66`}}>
188  
189            {/* price */}
190            <div>
191              <div style={{fontSize:10, letterSpacing:3, color:amberDim}}>ASSESSMENT / LOT VALUE</div>
192              <div style={{fontFamily:"Orbitron", fontWeight:900, fontSize:44, color:paper, letterSpacing:0, marginTop:2, lineHeight:1}}>
193                ₵{formatCred(ship.price)}
194              </div>
195              <div style={{fontSize:10, letterSpacing:2, color:amberDim, marginTop:4}}>↑ 2.3% vs 30-CYC AVG · {ship.mfg.loc.toUpperCase()} EXW</div>
196            </div>
197  
198            {/* Broker */}
199            <div>
200              <QuadHdr n="Q4" title="BROKER"/>
201              <SpecLine k="FIRM" v={ship.mfg.name} u=""/>
202              <SpecLine k="RATING" v="4.6 / 5.0" u="★"/>
203              <SpecLine k="CLOSINGS" v="1,240" u="units"/>
204              <SpecLine k="ESCROW" v="STELLAR BANK" u=""/>
205            </div>
206  
207            {/* Terms */}
208            <div>
209              <QuadHdr n="Q5" title="TERMS"/>
210              <SpecLine k="DELIVERY" v={`${ship.delivery} cyc`} u=""/>
211              <SpecLine k="LEASE" v={`₵${formatCred(Math.round(ship.price/120))}`} u="/mo"/>
212              <SpecLine k="WARRANTY" v={ship.warranty} u=""/>
213              <SpecLine k="HULL COND" v={ship.cond} u=""/>
214            </div>
215  
216            {/* Actions */}
217            <div style={{marginTop:"auto", display:"flex", flexDirection:"column", gap:8}}>
218              <button style={{
219                padding:"16px", background:amber, color:bg, border:"none",
220                fontFamily:"Orbitron", fontWeight:900, fontSize:13, letterSpacing:4, cursor:"pointer",
221                boxShadow:`0 0 20px ${amber}88`,
222              }}>◉ ACQUIRE UNIT</button>
223              <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:6}}>
224                <button style={{padding:"12px", background:"transparent", color:amber, border:`1px solid ${amber}`, fontFamily:"Orbitron", fontSize:10, letterSpacing:2, cursor:"pointer"}}>BID</button>
225                <button style={{padding:"12px", background:"transparent", color:amber, border:`1px solid ${amber}`, fontFamily:"Orbitron", fontSize:10, letterSpacing:2, cursor:"pointer"}}>WATCH</button>
226              </div>
227              <div style={{padding:8, border:`1px solid ${red}`, color:red, fontSize:9, letterSpacing:2, textAlign:"center", background:"rgba(255,77,77,0.08)"}}>
228                ⚠ 3 OTHER BIDDERS ACTIVE · AUTH COOLDOWN 14s
229              </div>
230            </div>
231          </div>
232        </div>
233  
234        {/* BOTTOM TICKER */}
235        <div style={{position:"absolute", bottom:0, left:0, right:0, padding:"6px 28px", borderTop:`1px solid ${amber}`, background:"rgba(0,0,0,0.6)", fontSize:10, letterSpacing:2, color:amberDim, display:"flex", gap:32, overflow:"hidden"}}>
236          <span style={{color:green}}>▲ KRV-IC +3.2%</span>
237          <span style={{color:red}}>▼ OBS-DR -1.1%</span>
238          <span>HEL-YT HOLD</span>
239          <span>VNT-CH +0.4%</span>
240          <span style={{color:green}}>▲ NXS-EX +5.6%</span>
241          <span>IOTA-SC HOLD</span>
242          <span>AUR-FR +0.9%</span>
243        </div>
244      </div>
245    );
246  }
247  
248  function QuadHdr({ n, title }) {
249    return (
250      <div style={{display:"flex", alignItems:"baseline", gap:10, borderBottom:`1px solid #ffbe2e`, paddingBottom:4, marginBottom:6}}>
251        <span style={{fontSize:9, letterSpacing:2, color:"#8c6610"}}>{n}</span>
252        <span style={{fontFamily:"Orbitron", fontWeight:700, fontSize:12, letterSpacing:3, color:"#ffbe2e"}}>{title}</span>
253      </div>
254    );
255  }
256  
257  Object.assign(window, { Concept4_Reticle });