/ brutalist / br-home.jsx
br-home.jsx
  1  // Brutalist HOMEPAGE — newspaper-style broadsheet masthead for the fleet catalog.
  2  // Above the fold: giant feature lot. Below: featured classes, market indices,
  3  // dispatches, and manufacturer index. Typographic, dense, editorial.
  4  
  5  function BRHome({ onNav, onShip }) {
  6    const feature = makeShip("FEAT-DR-0001-AA");
  7    const feature2 = makeShip("FEAT-IC-0022-BK");
  8    const lots = Array.from({length:6}).map((_,i) => makeShip("HOME-LOT-"+i));
  9    const dispatches = [
 10      ["2396.114", "COALITION", "Export tariffs on Tier-2 interceptors formally repealed. Effective immediately."],
 11      ["2396.113", "MARKET",    "Pleasure-yacht index holds at 112.4 despite Kepler Ring volatility."],
 12      ["2396.112", "YARD",      "Krovas Yards announce 18% throughput expansion at Ceres-II."],
 13      ["2396.111", "RECALL",    "Obsidian Forge issues voluntary recall on MN-class drive bolts, serials 44xx-46xx."],
 14      ["2396.110", "SYNDICATE", "Fleet liquidation releases 41 lots to open market via bonded brokerage."],
 15    ];
 16  
 17    return (
 18      <BRPage minHeight={2200}>
 19        <BRNav active="home" cartCount={2} onNav={onNav}/>
 20  
 21        {/* MASTHEAD SUBLINE */}
 22        <div style={{padding:"8px 40px", borderBottom:`1px solid ${BR.ink}`, fontFamily:"IBM Plex Mono", fontSize:10, letterSpacing:2, color:BR.mute, textTransform:"uppercase", display:"flex", justifyContent:"space-between"}}>
 23          <span>14,208 BONDED UNITS · 148 MANUFACTURERS · 92 SECTORS</span>
 24          <span style={{color:BR.rust, fontWeight:700}}>◆ DRY-DOCK MOVEMENT UPDATED · 14 MIN AGO</span>
 25          <span>TODAY: DR-CLASS +1.4% · IC-CLASS −0.6% · YT-CLASS HOLD</span>
 26        </div>
 27  
 28        {/* HERO — broadsheet headline */}
 29        <div style={{padding:"28px 40px 24px", borderBottom:`4px double ${BR.ink}`}}>
 30          <div style={{display:"flex", alignItems:"baseline", gap:16, fontFamily:"IBM Plex Mono", fontSize:10, letterSpacing:3, color:BR.mute, textTransform:"uppercase", marginBottom:12}}>
 31            <span style={{background:BR.ink, color:BR.paper, padding:"3px 10px"}}>EDITORIAL No. 412</span>
 32            <span>FEATURE LOT / WEEK 46</span>
 33            <span>— PRESENTED BY THE DESK AT SECTOR G-4 —</span>
 34          </div>
 35          <div style={{fontFamily:"Oswald", fontWeight:900, fontSize:120, lineHeight:0.88, letterSpacing:-3, color:BR.ink, textTransform:"uppercase", maxWidth:1100}}>
 36            Fourteen thousand hulls. One unbroken ledger.
 37          </div>
 38          <div style={{marginTop:18, display:"grid", gridTemplateColumns:"2fr 1.3fr", gap:40}}>
 39            <div style={{fontFamily:"IBM Plex Sans", fontSize:16, lineHeight:1.55, color:BR.ink2, columnCount:2, columnGap:28, textAlign:"justify"}}>
 40              Strata⁄Fleet is the Coalition's bonded registrar for civilian, commercial, and auxiliary vessels. Every hull listed here has passed three-stage hull inspection, transponder re-coding, and escrow bonding through Stellar Bank. We do not list speculative units. We do not list reconstructed hulls without the stamp of a certified yard. What you see is what you fly — or what your fleet flies home. Browse the index below, or ask the desk for a brokered introduction.
 41            </div>
 42            <div>
 43              <div style={{border:`2px solid ${BR.ink}`, padding:"14px 16px", background:BR.paper2}}>
 44                <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:12, letterSpacing:3, borderBottom:`1px solid ${BR.ink}`, paddingBottom:4}}>DESK QUICK-FIND</div>
 45                <div style={{display:"flex", gap:8, marginTop:10, border:`2px solid ${BR.ink}`, background:BR.paper}}>
 46                  <div style={{padding:"10px 12px", borderRight:`2px solid ${BR.ink}`, fontFamily:"IBM Plex Mono", fontSize:11, color:BR.mute}}>SN ▸</div>
 47                  <input placeholder="KRV-IC-2849-AX" style={{flex:1, border:"none", background:"transparent", fontFamily:"IBM Plex Mono", fontSize:13, outline:"none"}}/>
 48                  <div style={{background:BR.ink, color:BR.paper, padding:"10px 16px", fontFamily:"Oswald", fontSize:12, letterSpacing:3, cursor:"pointer"}} onClick={() => onNav && onNav("catalog")}>LOOKUP</div>
 49                </div>
 50                <div style={{marginTop:10, fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute, letterSpacing:1, lineHeight:1.7}}>
 51                  OR BROWSE BY — <a onClick={() => onNav("catalog")} style={{color:BR.ink, borderBottom:`1px solid ${BR.ink}`, cursor:"pointer"}}>CLASS</a>
 52                  · <a onClick={() => onNav("catalog")} style={{color:BR.ink, borderBottom:`1px solid ${BR.ink}`, cursor:"pointer"}}>FORGE</a>
 53                  · <a onClick={() => onNav("catalog")} style={{color:BR.ink, borderBottom:`1px solid ${BR.ink}`, cursor:"pointer"}}>DRIVE</a>
 54                  · <a onClick={() => onNav("catalog")} style={{color:BR.ink, borderBottom:`1px solid ${BR.ink}`, cursor:"pointer"}}>SECTOR</a>
 55                </div>
 56              </div>
 57              <div style={{marginTop:12, border:`2px solid ${BR.rust}`, padding:"10px 14px", fontFamily:"IBM Plex Mono", fontSize:11, color:BR.rust, letterSpacing:1.5, textTransform:"uppercase"}}>
 58                ⚑ Commodore tier required for hulls ≥ 2000 t
 59              </div>
 60            </div>
 61          </div>
 62        </div>
 63  
 64        {/* FEATURE LOT — full-bleed */}
 65        <div style={{padding:"28px 40px", borderBottom:`1px solid ${BR.ink}`}}>
 66          <div style={{display:"grid", gridTemplateColumns:"1.4fr 1fr", gap:32, alignItems:"stretch"}}>
 67            <div style={{position:"relative", border:`2px solid ${BR.ink}`, background:BR.paper2, padding:20}}>
 68              <div style={{display:"flex", justifyContent:"space-between", fontFamily:"IBM Plex Mono", fontSize:10, letterSpacing:2, color:BR.mute, textTransform:"uppercase"}}>
 69                <span>Plate I · Feature Lot</span>
 70                <span>Plate 1:{Math.round(feature.length_m/10)}</span>
 71              </div>
 72              <div style={{position:"relative", height:380, marginTop:10, background:`repeating-linear-gradient(0deg, transparent 0 23px, ${BR.ink}11 23px 24px), repeating-linear-gradient(90deg, transparent 0 23px, ${BR.ink}11 23px 24px)`, border:`1px solid ${BR.ink}`}}>
 73                <div style={{position:"absolute", inset:"10% 8%"}}>
 74                  <ShipSilhouette ship={feature} stroke={BR.ink} glow={false} strokeWidth={1.6}/>
 75                </div>
 76                <svg style={{position:"absolute", inset:0, pointerEvents:"none"}} width="100%" height="100%">
 77                  <line x1="8%" y1="94%" x2="92%" y2="94%" stroke={BR.ink}/>
 78                  <line x1="8%" y1="91%" x2="8%" y2="97%" stroke={BR.ink}/>
 79                  <line x1="92%" y1="91%" x2="92%" y2="97%" stroke={BR.ink}/>
 80                </svg>
 81                <div style={{position:"absolute", bottom:6, left:"50%", transform:"translateX(-50%)", fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute}}>{feature.length_m} m overall</div>
 82              </div>
 83              <BRStamp style={{position:"absolute", top:20, right:20}}>LOT OF THE WEEK</BRStamp>
 84            </div>
 85            <div style={{display:"flex", flexDirection:"column"}}>
 86              <div style={{fontFamily:"IBM Plex Mono", fontSize:11, letterSpacing:3, color:BR.mute, textTransform:"uppercase"}}>{feature.cls.code} · {feature.cls.name}</div>
 87              <div style={{fontFamily:"Oswald", fontWeight:900, fontSize:68, lineHeight:0.92, letterSpacing:-1, marginTop:4, textTransform:"uppercase"}}>{feature.model}</div>
 88              <div style={{fontFamily:"IBM Plex Mono", fontSize:12, letterSpacing:2, color:BR.ink, marginTop:6}}>{feature.serial} · {feature.mfg.name}</div>
 89              <div style={{fontFamily:"IBM Plex Sans", fontSize:14, lineHeight:1.55, color:BR.ink2, marginTop:14, maxWidth:440}}>
 90                Flagship of the {feature.mfg.name.split(" ")[0]} line. Triple-redundant {feature.drive.toLowerCase()} propulsion, {feature.crew}-crew accommodation, certified for deep-sector expedition. Hull is {feature.cond.toLowerCase().replace("_"," ")}; transponder re-coded at intake. Bonded by Stellar Bank.
 91              </div>
 92              <div style={{display:"flex", gap:8, marginTop:14, flexWrap:"wrap"}}>
 93                <BRChip rust>{feature.avail}</BRChip>
 94                <BRChip>{feature.cond}</BRChip>
 95                <BRChip>{feature.faction}</BRChip>
 96                <BRChip>{feature.drive}</BRChip>
 97              </div>
 98  
 99              <div style={{marginTop:18, display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:0, border:`2px solid ${BR.ink}`}}>
100                {[
101                  ["LENGTH", `${feature.length_m} m`],
102                  ["MASS", `${feature.mass.toLocaleString()} t`],
103                  ["JUMP", `${feature.jumpRange} ly`],
104                  ["CREW", feature.crew],
105                  ["ARMOR", `${feature.armor.toLocaleString()} mm`],
106                  ["DELIV.", `${feature.delivery} cyc`],
107                ].map(([k,v], i) => (
108                  <div key={k} style={{padding:"10px 12px", borderRight:(i%3!==2)?`1px solid ${BR.ink}`:"none", borderBottom: i<3 ? `1px solid ${BR.ink}` : "none"}}>
109                    <div style={{fontFamily:"IBM Plex Mono", fontSize:9, letterSpacing:2, color:BR.mute, textTransform:"uppercase"}}>{k}</div>
110                    <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:18, color:BR.ink, marginTop:2}}>{v}</div>
111                  </div>
112                ))}
113              </div>
114  
115              <div style={{marginTop:"auto", paddingTop:20, display:"flex", alignItems:"flex-end", justifyContent:"space-between", gap:14}}>
116                <div>
117                  <div style={{fontFamily:"IBM Plex Mono", fontSize:10, letterSpacing:2, color:BR.mute, textTransform:"uppercase"}}>UNIT PRICE</div>
118                  <div style={{fontFamily:"Oswald", fontWeight:900, fontSize:44, color:BR.ink, lineHeight:1}}>₵{formatCred(feature.price)}</div>
119                </div>
120                <button onClick={() => onShip && onShip(feature.seed)} style={{
121                  padding:"16px 24px", background:BR.ink, color:BR.paper, border:`2px solid ${BR.ink}`,
122                  fontFamily:"Oswald", fontWeight:700, fontSize:14, letterSpacing:3, cursor:"pointer",
123                }}>▸ OPEN DOSSIER</button>
124              </div>
125            </div>
126          </div>
127        </div>
128  
129        {/* BROWSE BY CLASS — giant typographic tiles */}
130        <div style={{padding:"28px 40px", borderBottom:`1px solid ${BR.ink}`}}>
131          <BRH n="02" size={16}>Browse by Class</BRH>
132          <div style={{display:"grid", gridTemplateColumns:"repeat(5, 1fr)", gap:0, border:`2px solid ${BR.ink}`}}>
133            {[
134              ["IC","Interceptor","2,104","Light combat"],
135              ["CH","Hauler","3,772","Logistics"],
136              ["YT","Yacht","941","Civilian"],
137              ["CR","Cruiser","612","Heavy combat"],
138              ["DR","Dreadnought","88","Capital"],
139            ].map(([code,name,count,role], i) => (
140              <div key={code} onClick={() => onNav && onNav("catalog")} style={{
141                padding:"22px 18px", borderRight: i<4 ? `1px solid ${BR.ink}` : "none",
142                cursor:"pointer", position:"relative", minHeight:180,
143                background: i===0 ? BR.ink : BR.paper, color: i===0 ? BR.paper : BR.ink,
144              }}>
145                <div style={{fontFamily:"Oswald", fontWeight:900, fontSize:92, lineHeight:0.85, letterSpacing:-2}}>{code}</div>
146                <div style={{fontFamily:"IBM Plex Mono", fontSize:10, letterSpacing:2, marginTop:8, opacity:0.75, textTransform:"uppercase"}}>{role}</div>
147                <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:18, marginTop:2, letterSpacing:1}}>{name}</div>
148                <div style={{position:"absolute", bottom:16, right:18, fontFamily:"IBM Plex Mono", fontSize:11, letterSpacing:1.5}}>{count} lots ▸</div>
149              </div>
150            ))}
151          </div>
152        </div>
153  
154        {/* DUAL PANEL — market indices + dispatches */}
155        <div style={{padding:"28px 40px", borderBottom:`1px solid ${BR.ink}`, display:"grid", gridTemplateColumns:"1.4fr 1fr", gap:40}}>
156          <div>
157            <BRH n="03" size={16}>Market Indices · 30 Cycles</BRH>
158            <div style={{border:`2px solid ${BR.ink}`}}>
159              <div style={{display:"grid", gridTemplateColumns:"80px 1fr 100px 80px 140px", padding:"6px 12px", borderBottom:`1px solid ${BR.ink}`, fontFamily:"IBM Plex Mono", fontSize:10, letterSpacing:2, color:BR.mute, textTransform:"uppercase", background:BR.paper2}}>
160                <span>Idx</span><span>Class</span><span style={{textAlign:"right"}}>Last</span><span style={{textAlign:"right"}}>Δ</span><span>Trend</span>
161              </div>
162              {[
163                ["IC-IDX", "Interceptor Composite", "112.4", "+1.2%", true],
164                ["CH-IDX", "Hauler Composite",      "087.8", "−0.4%", false],
165                ["YT-IDX", "Yacht Composite",       "142.9", "+0.1%", true],
166                ["CR-IDX", "Cruiser Composite",     "204.1", "+2.8%", true],
167                ["MN-IDX", "Mining Rig Composite",  "061.2", "−1.6%", false],
168                ["DR-IDX", "Dreadnought Composite", "388.4", "+1.4%", true],
169              ].map((row, i) => (
170                <div key={i} style={{display:"grid", gridTemplateColumns:"80px 1fr 100px 80px 140px", padding:"8px 12px", borderBottom: i<5 ? `1px dotted ${BR.ink}33` : "none", alignItems:"center", fontFamily:"IBM Plex Mono", fontSize:12}}>
171                  <span style={{letterSpacing:1.5, fontWeight:700}}>{row[0]}</span>
172                  <span>{row[1]}</span>
173                  <span style={{textAlign:"right", fontWeight:600}}>{row[2]}</span>
174                  <span style={{textAlign:"right", color: row[4] ? BR.green : BR.rust, fontWeight:700}}>{row[3]}</span>
175                  <span><MiniTrend up={row[4]} seed={row[0]}/></span>
176                </div>
177              ))}
178            </div>
179          </div>
180          <div>
181            <BRH n="04" size={16}>Desk Dispatches</BRH>
182            <div>
183              {dispatches.map((d, i) => (
184                <div key={i} style={{padding:"10px 0", borderBottom: i<dispatches.length-1 ? `1px solid ${BR.ink}33` : "none"}}>
185                  <div style={{display:"flex", gap:10, fontFamily:"IBM Plex Mono", fontSize:10, letterSpacing:2, color:BR.mute, textTransform:"uppercase"}}>
186                    <span>{d[0]}</span>
187                    <span style={{color: d[1]==="RECALL" ? BR.rust : BR.ink, fontWeight:700}}>◆ {d[1]}</span>
188                  </div>
189                  <div style={{fontFamily:"IBM Plex Sans", fontSize:13, color:BR.ink2, marginTop:4, lineHeight:1.4}}>{d[2]}</div>
190                </div>
191              ))}
192            </div>
193          </div>
194        </div>
195  
196        {/* RECENT LOTS — grid */}
197        <div style={{padding:"28px 40px", borderBottom:`1px solid ${BR.ink}`}}>
198          <div style={{display:"flex", justifyContent:"space-between", alignItems:"baseline"}}>
199            <BRH n="05" size={16}>Recent Lots</BRH>
200            <div style={{fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute, letterSpacing:2, marginTop:-20, textTransform:"uppercase"}}>SHOWING 6 OF 14,208</div>
201          </div>
202          <div style={{display:"grid", gridTemplateColumns:"repeat(3, 1fr)", gap:0, border:`2px solid ${BR.ink}`}}>
203            {lots.map((s, i) => (
204              <LotCard key={i} ship={s} borderR={i%3!==2} borderB={i<3} onOpen={() => onShip && onShip(s.seed)}/>
205            ))}
206          </div>
207          <div style={{display:"flex", justifyContent:"center", marginTop:18}}>
208            <button onClick={() => onNav && onNav("catalog")} style={{padding:"14px 36px", background:"transparent", border:`2px solid ${BR.ink}`, fontFamily:"Oswald", fontWeight:700, fontSize:13, letterSpacing:4, cursor:"pointer"}}>VIEW FULL CATALOG ▸</button>
209          </div>
210        </div>
211  
212        {/* MANUFACTURER INDEX */}
213        <div style={{padding:"28px 40px", borderBottom:`1px solid ${BR.ink}`}}>
214          <BRH n="06" size={16}>Manufacturer Index</BRH>
215          <div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:0, border:`2px solid ${BR.ink}`}}>
216            {[
217              ["KRV", "Krovas Yards", "Ceres-II", "2,104"],
218              ["AUR", "Aurelia Dynamics", "High Solis", "1,821"],
219              ["OBS", "Obsidian Forge", "Erebus Belt", "996"],
220              ["HEL", "Helion Shipworks", "Tau-Ceti", "1,412"],
221              ["VNT", "Vantari Collective", "Kepler Ring", "2,660"],
222              ["NXS", "Nexus Propulsion", "Luna-4", "881"],
223              ["KNT", "Kontos & Sons", "Mars Orb.", "740"],
224              ["IOTA","Iota Stellar", "Proxima", "1,203"],
225            ].map(([code,name,loc,count],i)=>(
226              <div key={code} style={{padding:"14px 16px", borderRight: (i%4!==3) ? `1px solid ${BR.ink}` : "none", borderBottom: i<4 ? `1px solid ${BR.ink}` : "none"}}>
227                <div style={{display:"flex", justifyContent:"space-between", alignItems:"baseline"}}>
228                  <div style={{fontFamily:"Oswald", fontWeight:900, fontSize:32, letterSpacing:-0.5}}>{code}</div>
229                  <div style={{fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute, letterSpacing:1.5}}>{count}</div>
230                </div>
231                <div style={{fontFamily:"IBM Plex Sans", fontSize:13, fontWeight:600}}>{name}</div>
232                <div style={{fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute, letterSpacing:1, marginTop:2}}>{loc.toUpperCase()}</div>
233              </div>
234            ))}
235          </div>
236        </div>
237  
238        <BRFooter/>
239      </BRPage>
240    );
241  }
242  
243  function MiniTrend({ up, seed }) {
244    const rng = mulberry32(hashStr(seed));
245    const pts = [];
246    let y = 0.5;
247    for (let i = 0; i < 20; i++) {
248      y += (rng() - (up ? 0.4 : 0.6)) * 0.12;
249      y = Math.max(0.1, Math.min(0.9, y));
250      pts.push([i/19*100, 20-y*20]);
251    }
252    return (
253      <svg width={100} height={20} style={{display:"block"}}>
254        <polyline points={pts.map(p=>p.join(",")).join(" ")} fill="none" stroke={up ? BR.green : BR.rust} strokeWidth={1.2}/>
255      </svg>
256    );
257  }
258  
259  function LotCard({ ship, borderR, borderB, onOpen }) {
260    return (
261      <div onClick={onOpen} style={{
262        padding:"14px 16px", cursor:"pointer",
263        borderRight: borderR ? `1px solid ${BR.ink}` : "none",
264        borderBottom: borderB ? `1px solid ${BR.ink}` : "none",
265        background:BR.paper,
266      }}>
267        <div style={{display:"flex", justifyContent:"space-between", fontFamily:"IBM Plex Mono", fontSize:10, letterSpacing:2, color:BR.mute, textTransform:"uppercase"}}>
268          <span>{ship.serial}</span>
269          <span>{ship.cls.code}</span>
270        </div>
271        <div style={{height:110, marginTop:8, border:`1px solid ${BR.ink}33`, position:"relative", background:`repeating-linear-gradient(0deg, transparent 0 15px, ${BR.ink}0d 15px 16px)`}}>
272          <div style={{position:"absolute", inset:"10% 8%"}}>
273            <ShipSilhouette ship={ship} stroke={BR.ink} glow={false} strokeWidth={1.2}/>
274          </div>
275        </div>
276        <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:22, letterSpacing:0, marginTop:10, lineHeight:1.1, textTransform:"uppercase"}}>{ship.model}</div>
277        <div style={{fontFamily:"IBM Plex Mono", fontSize:11, color:BR.ink2, marginTop:4, letterSpacing:1}}>{ship.mfg.name} · {ship.year}</div>
278        <div style={{display:"flex", justifyContent:"space-between", alignItems:"baseline", marginTop:10, paddingTop:8, borderTop:`1px dotted ${BR.ink}33`}}>
279          <div style={{fontFamily:"Oswald", fontWeight:900, fontSize:24}}>₵{formatCred(ship.price)}</div>
280          <BRChip rust>{ship.avail}</BRChip>
281        </div>
282      </div>
283    );
284  }
285  
286  Object.assign(window, { BRHome });