/ brutalist / br-onboard.jsx
br-onboard.jsx
  1  // Brutalist PILOT ONBOARDING — a ship-yard "induction packet" form.
  2  // Six-step stepper, left-rail identity, main form, rubber stamps, signatories.
  3  
  4  function BROnboard({ onNav }) {
  5    const [step, setStep] = React.useState(2); // show some progress
  6  
  7    const steps = [
  8      "IDENTITY",
  9      "BIOMETRICS",
 10      "LICENSE",
 11      "SAFETY DRILL",
 12      "HULL FAMILIARIZATION",
 13      "OATH",
 14    ];
 15  
 16    return (
 17      <BRPage minHeight={2000}>
 18        <BRNav active="onboard" cartCount={0} onNav={onNav}/>
 19  
 20        {/* HEADER */}
 21        <div style={{padding:"30px 40px 22px", borderBottom:`4px double ${BR.ink}`, display:"flex", justifyContent:"space-between", alignItems:"flex-end"}}>
 22          <div>
 23            <div style={{fontFamily:"IBM Plex Mono", fontSize:11, letterSpacing:3, color:BR.mute, textTransform:"uppercase"}}>FORM 31 · INDUCTION PACKET · PILOT OF RECORD</div>
 24            <div style={{fontFamily:"Oswald", fontWeight:900, fontSize:92, lineHeight:0.9, letterSpacing:-2, textTransform:"uppercase", marginTop:6}}>Induction</div>
 25          </div>
 26          <BRStamp rotate={4}>PROVISIONAL</BRStamp>
 27        </div>
 28  
 29        {/* STEPPER */}
 30        <div style={{padding:"18px 40px", borderBottom:`2px solid ${BR.ink}`, display:"flex", alignItems:"center", gap:8, background:BR.paper2}}>
 31          {steps.map((s, i) => (
 32            <React.Fragment key={s}>
 33              <div onClick={()=>setStep(i)} style={{flex:1, cursor:"pointer", textAlign:"center", padding:"10px 8px", border:`2px solid ${BR.ink}`, background: i<step ? BR.ink : i===step ? BR.rust : BR.paper, color: i<=step ? BR.paper : BR.ink}}>
 34                <div style={{fontFamily:"IBM Plex Mono", fontSize:9, letterSpacing:2, opacity:0.8}}>STEP {String(i+1).padStart(2,"0")}</div>
 35                <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:12, letterSpacing:2, marginTop:2}}>{s}</div>
 36              </div>
 37              {i<steps.length-1 && <div style={{width:12, height:2, background:BR.ink}}/>}
 38            </React.Fragment>
 39          ))}
 40        </div>
 41  
 42        {/* BODY */}
 43        <div style={{display:"grid", gridTemplateColumns:"280px 1fr 320px", minHeight:1300}}>
 44  
 45          {/* IDENTITY CARD */}
 46          <div style={{borderRight:`1px solid ${BR.ink}`, padding:"24px 24px"}}>
 47            <div style={{border:`2px solid ${BR.ink}`, padding:"18px 16px", background:BR.paper2}}>
 48              <div style={{fontFamily:"IBM Plex Mono", fontSize:9, letterSpacing:2, color:BR.mute, textTransform:"uppercase"}}>PILOT № PL-2396-04481</div>
 49              <div style={{width:"100%", aspectRatio:"1/1.2", background:BR.paper, border:`2px solid ${BR.ink}`, marginTop:10, position:"relative", overflow:"hidden"}}>
 50                {/* placeholder portrait — cross-hatched silhouette */}
 51                <svg viewBox="0 0 100 120" style={{width:"100%", height:"100%"}}>
 52                  <defs>
 53                    <pattern id="hatch" width="4" height="4" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
 54                      <line x1="0" y1="0" x2="0" y2="4" stroke={BR.ink} strokeOpacity="0.25" strokeWidth="1"/>
 55                    </pattern>
 56                  </defs>
 57                  <rect width="100" height="120" fill="url(#hatch)"/>
 58                  <circle cx="50" cy="48" r="22" fill={BR.paper} stroke={BR.ink} strokeWidth="2"/>
 59                  <path d="M18,120 C 18,84 32,72 50,72 C 68,72 82,84 82,120 Z" fill={BR.paper} stroke={BR.ink} strokeWidth="2"/>
 60                  <text x="50" y="115" fontFamily="IBM Plex Mono" fontSize="5" fill={BR.mute} textAnchor="middle" letterSpacing="1">PORTRAIT PENDING</text>
 61                </svg>
 62                <div style={{position:"absolute", top:6, right:6, background:BR.rust, color:BR.paper, padding:"2px 6px", fontFamily:"Oswald", fontWeight:700, fontSize:9, letterSpacing:1.5}}>PROV.</div>
 63              </div>
 64              <div style={{marginTop:12, fontFamily:"IBM Plex Mono", fontSize:10, lineHeight:1.7, color:BR.ink2, letterSpacing:0.5}}>
 65                <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:16, letterSpacing:1, color:BR.ink, textTransform:"uppercase"}}>Orlan Veyr</div>
 66                <div><span style={{color:BR.mute}}>TIER</span> 3 · COMMODORE</div>
 67                <div><span style={{color:BR.mute}}>UNIT</span> 7TH EXPEDITIONARY</div>
 68                <div><span style={{color:BR.mute}}>SECTOR</span> G-4 · CERES</div>
 69                <div><span style={{color:BR.mute}}>BLOOD</span> B+ / COAG 11.2</div>
 70                <div><span style={{color:BR.mute}}>EMERG</span> S. VEYR · +14 774</div>
 71              </div>
 72              <div style={{marginTop:14, height:80, border:`2px dashed ${BR.ink}`, display:"flex", alignItems:"center", justifyContent:"center", fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute, letterSpacing:1.5, textTransform:"uppercase"}}>
 73                THUMB PRINT HERE
 74              </div>
 75            </div>
 76  
 77            <div style={{marginTop:18}}>
 78              <BRH n="00">Progress</BRH>
 79              <div style={{fontFamily:"IBM Plex Mono", fontSize:11, color:BR.ink2, letterSpacing:1}}>
 80                {steps.map((s, i) => (
 81                  <div key={s} style={{padding:"6px 0", display:"flex", justifyContent:"space-between", borderBottom:`1px dotted ${BR.ink}33`}}>
 82                    <span style={{color: i<=step ? BR.ink : BR.mute}}>{String(i+1).padStart(2,"0")} · {s}</span>
 83                    <span style={{color: i<step ? BR.green : i===step ? BR.rust : BR.mute, fontWeight:700}}>
 84                      {i<step ? "✓" : i===step ? "●" : "—"}
 85                    </span>
 86                  </div>
 87                ))}
 88              </div>
 89            </div>
 90          </div>
 91  
 92          {/* CENTER FORM — current step */}
 93          <div style={{padding:"26px 36px"}}>
 94            <BRH n={String(step+1).padStart(2,"0")}>{steps[step]}</BRH>
 95  
 96            {step === 0 && <IdentityForm/>}
 97            {step === 1 && <BiometricsForm/>}
 98            {step === 2 && <LicenseForm/>}
 99            {step === 3 && <DrillForm/>}
100            {step === 4 && <FamiliarForm/>}
101            {step === 5 && <OathForm/>}
102  
103            <div style={{marginTop:28, display:"flex", gap:10}}>
104              <button onClick={()=>setStep(Math.max(0,step-1))} style={{padding:"14px 28px", background:"transparent", color:BR.ink, border:`2px solid ${BR.ink}`, fontFamily:"Oswald", fontWeight:700, fontSize:12, letterSpacing:3, cursor:"pointer"}}>◂ PRIOR STEP</button>
105              <button onClick={()=>setStep(Math.min(steps.length-1,step+1))} style={{padding:"14px 28px", background:BR.ink, color:BR.paper, border:"none", fontFamily:"Oswald", fontWeight:900, fontSize:12, letterSpacing:3, cursor:"pointer", flex:1}}>AFFIRM & ADVANCE ▸</button>
106            </div>
107          </div>
108  
109          {/* RIGHT RAIL — hull assigned + signatories */}
110          <div style={{borderLeft:`1px solid ${BR.ink}`, padding:"24px 24px", background:BR.paper2}}>
111            <BRH n="0α">Hull Assigned</BRH>
112            <div style={{border:`2px solid ${BR.ink}`, background:BR.paper, padding:"12px 14px"}}>
113              <div style={{width:"100%", height:80, border:`1px solid ${BR.ink}33`, position:"relative"}}>
114                <div style={{position:"absolute", inset:"10% 6%"}}>
115                  <ShipSilhouette ship={makeShip("KRV-IC-2849-AX")} stroke={BR.ink} glow={false} strokeWidth={1.2} detail="mid"/>
116                </div>
117              </div>
118              <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:16, letterSpacing:1, textTransform:"uppercase", marginTop:10}}>KRV IC-2849 Axiom</div>
119              <div style={{fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute, letterSpacing:1, marginTop:2}}>KRV-IC-2849-AX · INTERCEPTOR · TIER-3</div>
120            </div>
121  
122            <div style={{height:20}}/>
123            <BRH n="0β">Signatories</BRH>
124            {[
125              ["O. VEYR",       "Pilot of record",     true],
126              ["J. HOLLISTER",  "Coalition notary",    true],
127              ["S. ORIN",       "Yard master, Ceres",  false],
128              ["—",             "Flight surgeon",      false],
129            ].map(([n, role, done], i) => (
130              <div key={i} style={{padding:"10px 0", borderBottom:`1px dotted ${BR.ink}33`}}>
131                <div style={{display:"flex", justifyContent:"space-between"}}>
132                  <span style={{fontFamily:"Oswald", fontWeight:700, fontSize:13, letterSpacing:1.5, textTransform:"uppercase"}}>{n}</span>
133                  <span style={{fontFamily:"Oswald", fontWeight:700, fontSize:11, letterSpacing:2, color: done ? BR.green : BR.mute}}>{done ? "SIGNED" : "PENDING"}</span>
134                </div>
135                <div style={{fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute, letterSpacing:1, marginTop:2}}>{role}</div>
136                {done && <div style={{fontFamily:"'Caveat','Space Grotesk',cursive", fontSize:20, color:BR.ink, marginTop:4}}>{n.split(" ").map(x=>x[0]).join(".")}. {n.split(" ").slice(-1)[0]}</div>}
137              </div>
138            ))}
139  
140            <div style={{height:20}}/>
141            <div style={{border:`2px solid ${BR.rust}`, padding:"12px 14px", background:"rgba(194,66,26,0.05)", fontFamily:"IBM Plex Mono", fontSize:11, color:BR.rust, letterSpacing:1, lineHeight:1.5, textTransform:"uppercase"}}>
142              Provisional certification lapses in 30 cycles unless Oath (§06) is sworn at dry-dock C.
143            </div>
144          </div>
145        </div>
146  
147        <BRFooter/>
148      </BRPage>
149    );
150  }
151  
152  function PLField({ label, value, hand }) {
153    return (
154      <div style={{padding:"10px 0", borderBottom:`2px solid ${BR.ink}`}}>
155        <div style={{fontFamily:"IBM Plex Mono", fontSize:9, letterSpacing:2, color:BR.mute, textTransform:"uppercase"}}>{label}</div>
156        <div style={{fontFamily: hand ? "'Caveat','Space Grotesk',cursive" : "IBM Plex Mono", fontSize: hand ? 22 : 14, fontWeight: 500, color:BR.ink, marginTop: hand ? 0 : 4}}>{value}</div>
157      </div>
158    );
159  }
160  
161  function IdentityForm() {
162    return (
163      <div>
164        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:18}}>
165          <PLField label="Surname" value="VEYR" hand/>
166          <PLField label="Given names" value="Orlan Aenor" hand/>
167          <PLField label="Birth cycle" value="2354.088"/>
168          <PLField label="Birth sector" value="Tharsis · M-11"/>
169          <PLField label="Citizenship" value="Coalition · A-class"/>
170          <PLField label="Service record" value="CTR-8812 · Redacted §6"/>
171        </div>
172      </div>
173    );
174  }
175  
176  function BiometricsForm() {
177    const metrics = [
178      ["Retina pattern",    "CAPTURED", 1.0],
179      ["Palm vein scan",    "CAPTURED", 1.0],
180      ["Gait signature",    "CAPTURED", 0.94],
181      ["Voice print",       "CAPTURED", 0.88],
182      ["G-load tolerance",  "9.2 G sustained", 0.76],
183      ["Cognitive reflex",  "212 ms median",   0.82],
184    ];
185    return (
186      <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:12}}>
187        {metrics.map(([k,v,p], i) => (
188          <div key={i} style={{padding:"14px 16px", border:`2px solid ${BR.ink}`, background:BR.paper}}>
189            <div style={{display:"flex", justifyContent:"space-between"}}>
190              <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:13, letterSpacing:2, textTransform:"uppercase"}}>{k}</div>
191              <BRChip ok>OK</BRChip>
192            </div>
193            <div style={{fontFamily:"IBM Plex Mono", fontSize:11, color:BR.ink2, marginTop:4}}>{v}</div>
194            <div style={{height:6, background:BR.ink + "22", marginTop:10, border:`1px solid ${BR.ink}55`}}>
195              <div style={{height:"100%", width:`${p*100}%`, background:BR.ink}}/>
196            </div>
197          </div>
198        ))}
199      </div>
200    );
201  }
202  
203  function LicenseForm() {
204    return (
205      <>
206        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:18}}>
207          <PLField label="License №" value="LIC-7-44-8812-C"/>
208          <PLField label="Tier" value="Tier-3 · Commodore"/>
209          <PLField label="Expires" value="2399.088"/>
210          <PLField label="Ratings" value="IC · CH · DR · CR"/>
211          <PLField label="Endorsements" value="Night-jump · Q-drive"/>
212          <PLField label="Last medical" value="2396.060 · Cleared"/>
213        </div>
214        <div style={{height:20}}/>
215        <div style={{padding:"14px 16px", border:`2px solid ${BR.ink}`, background:BR.paper2}}>
216          <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:13, letterSpacing:2, textTransform:"uppercase", marginBottom:8}}>Endorsement Stamps</div>
217          <div style={{display:"flex", gap:18, flexWrap:"wrap"}}>
218            <BRStamp rotate={-8}>IC · CLASS</BRStamp>
219            <BRStamp rotate={5} color={BR.green}>CLEARED MED</BRStamp>
220            <BRStamp rotate={-3}>Q-DRIVE</BRStamp>
221            <BRStamp rotate={10} color={BR.ink}>NIGHT JUMP</BRStamp>
222          </div>
223        </div>
224      </>
225    );
226  }
227  
228  function DrillForm() {
229    const drills = [
230      ["Decompression · bay loss",   "PASS", "02:12"],
231      ["Drive flame-out · cold restart","PASS", "06:40"],
232      ["Transponder loss · silent run", "PASS", "04:05"],
233      ["Boarded vessel · hail procedure","PASS","03:18"],
234      ["Flare drill · close formation", "RETEST","—"],
235    ];
236    return (
237      <>
238        <div style={{fontFamily:"IBM Plex Sans", fontSize:13, color:BR.ink2, marginBottom:12, lineHeight:1.5}}>
239          Five mandatory drills under the §7 Safety Annex. Flare drill must be re-taken in-yard within 14 cycles.
240        </div>
241        <table style={{width:"100%", borderCollapse:"collapse", fontFamily:"IBM Plex Mono", fontSize:12}}>
242          <thead>
243            <tr style={{background:BR.paper2, borderBottom:`2px solid ${BR.ink}`}}>
244              {["#","Drill","Result","Elapsed","Invigilator"].map(h => (
245                <th key={h} style={{padding:"10px 12px", textAlign:"left", fontSize:10, letterSpacing:2, color:BR.mute, textTransform:"uppercase", fontWeight:500}}>{h}</th>
246              ))}
247            </tr>
248          </thead>
249          <tbody>
250            {drills.map(([t,r,e], i) => (
251              <tr key={i} style={{borderBottom:`1px dotted ${BR.ink}44`}}>
252                <td style={{padding:"10px 12px", fontWeight:700}}>{String(i+1).padStart(2,"0")}</td>
253                <td style={{padding:"10px 12px"}}>{t}</td>
254                <td style={{padding:"10px 12px"}}>
255                  <span style={{fontFamily:"Oswald", fontWeight:700, fontSize:11, letterSpacing:2, color: r==="PASS" ? BR.green : BR.rust}}>{r}</span>
256                </td>
257                <td style={{padding:"10px 12px"}}>{e}</td>
258                <td style={{padding:"10px 12px"}}>LT S. TOREN</td>
259              </tr>
260            ))}
261          </tbody>
262        </table>
263      </>
264    );
265  }
266  
267  function FamiliarForm() {
268    return (
269      <>
270        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:16}}>
271          {[
272            ["Drive subsystems",   true, "K-7 Q-drive · cold & warm start procedures"],
273            ["Weapon hardpoints",  true, "Tier-3 rails, lockout, disengagement"],
274            ["Life support",       true, "O₂ · CO₂ · coolant loops · emergency vent"],
275            ["Sensor suite",       false,"LIDAR · gravimeter · EM passive · decoys"],
276            ["Damage control",     false,"Fire suppression · hull patch kit · triage"],
277            ["Emergency egress",   false,"Pod launch · suit seals · beacon protocols"],
278          ].map(([t, done, d], i) => (
279            <div key={i} style={{padding:"12px 14px", border:`2px solid ${BR.ink}`, background: done ? BR.paper2 : BR.paper}}>
280              <div style={{display:"flex", justifyContent:"space-between", alignItems:"baseline"}}>
281                <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:13, letterSpacing:2, textTransform:"uppercase"}}>{t}</div>
282                <BRChip ok={done}>{done ? "COMPLETE" : "SCHEDULED"}</BRChip>
283              </div>
284              <div style={{fontFamily:"IBM Plex Sans", fontSize:12, color:BR.ink2, marginTop:4}}>{d}</div>
285            </div>
286          ))}
287        </div>
288      </>
289    );
290  }
291  
292  function OathForm() {
293    return (
294      <div style={{border:`3px double ${BR.ink}`, padding:"24px 28px", background:BR.paper2}}>
295        <div style={{fontFamily:"Oswald", fontWeight:700, fontSize:14, letterSpacing:3, textTransform:"uppercase", borderBottom:`1px solid ${BR.ink}`, paddingBottom:6, marginBottom:14}}>Pilot's Oath — §6 Coalition Articles</div>
296        <div style={{fontFamily:"IBM Plex Sans", fontSize:15, lineHeight:1.7, color:BR.ink, maxWidth:720}}>
297          I, the undersigned, accept custody of the hull registered to my name. I will fly it with the judgement of a master and the restraint of a citizen. I will answer to the ledger before I answer to pride; to the Coalition before I answer to profit. Should I fail this standard, I submit my wings to the yard.
298        </div>
299        <div style={{marginTop:24, display:"grid", gridTemplateColumns:"1fr 1fr", gap:30}}>
300          <div>
301            <div style={{fontFamily:"'Caveat','Space Grotesk',cursive", fontSize:36, color:BR.ink, borderBottom:`2px solid ${BR.ink}`, paddingBottom:6}}>O. Veyr</div>
302            <div style={{fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute, letterSpacing:2, marginTop:4, textTransform:"uppercase"}}>Pilot signatory · 2396.116</div>
303          </div>
304          <div>
305            <div style={{fontFamily:"'Caveat','Space Grotesk',cursive", fontSize:36, color:BR.ink, borderBottom:`2px solid ${BR.ink}`, paddingBottom:6}}>J. Hollister</div>
306            <div style={{fontFamily:"IBM Plex Mono", fontSize:10, color:BR.mute, letterSpacing:2, marginTop:4, textTransform:"uppercase"}}>Coalition notary · seal 4481</div>
307          </div>
308        </div>
309      </div>
310    );
311  }
312  
313  Object.assign(window, { BROnboard });