diff --git a/lcars/.log b/lcars/.log
new file mode 100644
index 0000000..07ce2a0
--- /dev/null
+++ b/lcars/.log
@@ -0,0 +1,2 @@
+2025/09/30 11:18:25 Failed to create internal StateDB: SQL logic error: near ")": syntax error (1)
+2025/09/30 11:20:30 Failed to create internal StateDB: SQL logic error: near ")": syntax error (1)
diff --git a/lcars/embed/create_state_db.sql b/lcars/embed/create_state_db.sql
new file mode 100644
index 0000000..241d099
--- /dev/null
+++ b/lcars/embed/create_state_db.sql
@@ -0,0 +1,14 @@
+CREATE TABLE ship_messages (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ timestamp TEXT NOT NULL,
+ subsystem TEXT NOT NULL,
+ severity TEXT CHECK(severity IN ('CRITICAL', 'ALERT', 'WARNING', 'NOTICE', 'INFO')) NOT NULL DEFAULT 'INFO',
+ color TEXT NOT NULL,
+ message TEXT NOT NULL
+);
+
+CREATE TABLE crew_member (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ rank TEXT NOT NULL,
+ name TEXT NOT NULL
+);
\ No newline at end of file
diff --git a/lcars/embed/crew.json b/lcars/embed/crew.json
new file mode 100644
index 0000000..1b2a1fa
--- /dev/null
+++ b/lcars/embed/crew.json
@@ -0,0 +1,95 @@
+[
+ "Ensign Beckett Mariner",
+ "Ensign Brad Boimler",
+ "Captain Carol Freeman",
+ "Commander Jack Ransom",
+ "Ensign Barsa Orsino",
+ "Lieutenant Commander D'Vana Tendi",
+ "Lieutenant Commander Sam Rutherford",
+ "Lieutenant Commander Shaxs",
+ "Ensign Liora Vance",
+ "Ensign Rylan Sato",
+ "Lieutenant Jarek Torin",
+ "Lieutenant Kira Dallin",
+ "Lieutenant T'Lara Venn",
+ "Lieutenant Shonnie Velar",
+ "Lieutenant Commander Aric Thorne",
+ "Lieutenant Commander Selene Marvik",
+ "Lieutenant Commander Jovan Kreel",
+ "Lieutenant Orin Kallis",
+ "Ensign Mira Talon",
+ "Ensign Fynn Darvik",
+ "Lieutenant Commander Elara Voss",
+ "Lieutenant Zev Ralyn",
+ "Ensign Daxia Morn",
+ "Lieutenant Varek Solis",
+ "Ensign Tylen Kael",
+ "Lieutenant Commander Nira Falco",
+ "Lieutenant Kael Dorran",
+ "Ensign Saren Vale",
+ "Ensign Tova Lin",
+ "Lieutenant Commander Ryn Talor",
+ "Lieutenant Draven Korr",
+ "Ensign Lyra Kenning",
+ "Ensign Joren Pax",
+ "Lieutenant Commander Calix Arden",
+ "Lieutenant Selan Vey",
+ "Ensign Aricel Taren",
+ "Ensign Velin Daro",
+ "Lieutenant Caris Vennor",
+ "Lieutenant Kellen Dray",
+ "Lieutenant Risa Talven",
+ "Lieutenant Commander Thalen Voss",
+ "Lieutenant Commander Sariah Quell",
+ "Ensign Orin Talvik",
+ "Ensign Lyric Selden",
+ "Lieutenant Commander Varen Korr",
+ "Lieutenant Elara Vynn",
+ "Ensign Jax Talmar",
+ "Lieutenant Commander Neris Vay",
+ "Lieutenant Draven Solis",
+ "Ensign Tavia Korlen",
+ "Ensign Ryn Paxil",
+ "Lieutenant Commander Kira Dalen",
+ "Lieutenant Zev Ardin",
+ "Ensign Lyra Taven",
+ "Ensign Fynn Velar",
+ "Lieutenant Commander Calen Rhos",
+ "Lieutenant Selan Vaylen",
+ "Ensign Aricel Dorran",
+ "Ensign Tylen Korr",
+ "Lieutenant Commander Nira Talos",
+ "Lieutenant Kael Venn",
+ "Ensign Saren Daro",
+ "Ensign Tova Vennor",
+ "Lieutenant Commander Ryn Arden",
+ "Lieutenant Draven Voss",
+ "Ensign Lyra Talin",
+ "Ensign Joren Vay",
+ "Lieutenant Commander Calix Talven",
+ "Lieutenant Selan Korr",
+ "Ensign Aricel Vynn",
+ "Ensign Velin Talor",
+ "Lieutenant Caris Vaylen",
+ "Lieutenant Kellen Rhos",
+ "Lieutenant Risa Vennor",
+ "Lieutenant Commander Thalen Daro",
+ "Lieutenant Commander Sariah Voss",
+ "Ensign Orin Vaylen",
+ "Ensign Lyric Talven",
+ "Lieutenant Commander Varen Talos",
+ "Lieutenant Elara Solis",
+ "Ensign Jax Vennor",
+ "Lieutenant Commander Neris Talor",
+ "Lieutenant Draven Vaylen",
+ "Ensign Tavia Dorran",
+ "Ensign Ryn Voss",
+ "Lieutenant Commander Kira Arden",
+ "Lieutenant Zev Vay",
+ "Ensign Lyra Daro",
+ "Ensign Fynn Talor",
+ "Lieutenant Commander Calen Venn",
+ "Lieutenant Selan Talvik",
+ "Ensign Aricel Rhos"
+ ]
+
diff --git a/lcars/embed/messages.json b/lcars/embed/messages.json
new file mode 100644
index 0000000..4d468e7
--- /dev/null
+++ b/lcars/embed/messages.json
@@ -0,0 +1,1066 @@
+[
+ {
+ "timestamp": "2258-03-14T10:00:00Z",
+ "subsystem": "Engineering",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Coolant leak detected in Port Nacelle plasma manifold. Lt. Alvarez leading EVA team."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:05Z",
+ "subsystem": "Medical",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Crewman Jayala reported missing from Beta Shift muster. Last seen entering Holodeck 3."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:10Z",
+ "subsystem": "Security",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Brig forcefield cycling unpredictably after power surge; holding cell offline."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:15Z",
+ "subsystem": "Security",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Three failed command authorization attempts by Crewman Dovan during Gamma Shift."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:20Z",
+ "subsystem": "Navigation",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Shuttlecraft Sequoia overdue by two hours; last piloted by Ensign Boimler."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:25Z",
+ "subsystem": "Computer",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Memory parity mismatch detected in Core Segment Gamma-12; recalibration required."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:30Z",
+ "subsystem": "Engineering",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Warp Core plasma injectors 3 and 5 at 94% efficiency."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:35Z",
+ "subsystem": "Tactical",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Two dorsal phaser array emitters offline; Lt. Shaxs coordinating repairs."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:40Z",
+ "subsystem": "Life Support",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Environmental controls on Deck 7 report humidity spike to 78%."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:45Z",
+ "subsystem": "Medical",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Biofilter malfunction in Sickbay stasis bed 2; engineering support requested."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:50Z",
+ "subsystem": "Engineering",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Jefferies Tube 22 flooded with hydraulic fluid. Cleanup team assigned."
+ },
+ {
+ "timestamp": "2258-03-14T10:00:55Z",
+ "subsystem": "Security",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Photon torpedo #17 removed by Ensign Faranak without clearance."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:00Z",
+ "subsystem": "Engineering",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Dilithium crystal lattice shows micro-fracturing; rotation cycle initiated."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:05Z",
+ "subsystem": "Life Support",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Deck 11 grav plating fluctuation caused minor injuries to three crew members."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:10Z",
+ "subsystem": "Computer",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Secondary Core experiencing intermittent lag on Ops data uplink."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:15Z",
+ "subsystem": "Medical",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Ensign Phelps admitted with plasma burn to left arm; stable condition."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:20Z",
+ "subsystem": "Computer",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Holographic LCARS terminals on Deck 4 flickering due to power variance."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:25Z",
+ "subsystem": "Security",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Transporter Room 1 reports pattern buffer ghost echo under review."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:30Z",
+ "subsystem": "Crew",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "D'Vana Tendi missed Sickbay shift after volunteering for shuttle maintenance."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:35Z",
+ "subsystem": "Crew",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Commander Ransom reported muscle strain during holodeck combat program."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:40Z",
+ "subsystem": "Computer",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Memory integrity at 99.3% overall."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:45Z",
+ "subsystem": "Life Support",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Replicator in Mess Hall 2 producing lukewarm coffee; morale note logged."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:50Z",
+ "subsystem": "Tactical",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Security drill caused nine-second transporter lockout; Lt. Kayshon apologized."
+ },
+ {
+ "timestamp": "2258-03-14T10:01:55Z",
+ "subsystem": "Science",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Sensor probe launched to examine tetryon emissions near bow hull."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:00Z",
+ "subsystem": "Hydroponics",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Fungal bloom detected in Deck 5 greenhouse; Lt. K’Raa leading analysis."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:05Z",
+ "subsystem": "Computer",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Subspace comm buffer corrupted; Ensign Maru re-indexing manually."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:10Z",
+ "subsystem": "Holodeck",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Holodeck 1 stuck in black-and-white detective mode; access requires period slang."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:15Z",
+ "subsystem": "Cargo",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Anti-grav pallets in Cargo Bay 6 oscillating; Crewman Knox avoided injury."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:20Z",
+ "subsystem": "Crew",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Beckett Mariner left three Type-II phasers in the gym locker room."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:25Z",
+ "subsystem": "Environmental",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Observation lounge viewport frosting unexpectedly; climate sync pending."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:30Z",
+ "subsystem": "Hydroponics",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Deck 5 tomato vines achieved limited sentience; requesting voting rights in jest. Lt. K’Raa unconcerned."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:35Z",
+ "subsystem": "Computer",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Isolinear Chip Bank E7 humming the Bajoran Gratitude Chant at 11 Hz. Diagnostics inconclusive."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:40Z",
+ "subsystem": "Environmental",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Observation Deck 2 oxygen mix replaced with helium for 14 seconds. Voices recorded for morale bulletin."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:45Z",
+ "subsystem": "Replicators",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Mess Hall 1 replicator now defaulting to 'Spicy Bolian Nutripaste' for all beverage requests."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:50Z",
+ "subsystem": "Security",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Lt. Shaxs reported ‘honor dueling’ with a malfunctioning training dummy. Dummy lost two limbs."
+ },
+ {
+ "timestamp": "2258-03-14T10:02:55Z",
+ "subsystem": "Engineering",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Jefferies Tube 14 now leads to a broom closet on Deck 1 after map update glitch."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:00Z",
+ "subsystem": "Holodeck",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Holodeck 3 spawned three Dixon Hill NPCs outside its grid. Containment field recalibrated."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:05Z",
+ "subsystem": "Astrometrics",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Unidentified subspace ping repeating Morse for 'send snacks'. Source undetermined."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:10Z",
+ "subsystem": "Engineering",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "EPS conduit K-47 briefly rerouted power to the shipwide karaoke buffer. Warp modulation destabilized."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:15Z",
+ "subsystem": "Medical",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Dr. T’Ana confiscated 19 unauthorized hyposprays labeled 'just caffeine.'"
+ },
+ {
+ "timestamp": "2258-03-14T10:03:20Z",
+ "subsystem": "Crew",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Ensign Mariner attempting to persuade replicator to produce contraband Romulan Ale using voice modulation."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:25Z",
+ "subsystem": "Transporter",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Pattern buffer registered a transient sixth finger on Lt. Barnes during beam-out. No physical trace remains."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:30Z",
+ "subsystem": "Science",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Chroniton surge around Astrometrics Console 4 now forming smiley faces on sensor readouts."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:35Z",
+ "subsystem": "Computer",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "LCARS sidebar fonts replaced with Ferengi cursive after unapproved theme install by Crewman Drox."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:40Z",
+ "subsystem": "Cargo Bay",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Crate of self-warming blankets activated simultaneously. Temperature spike registered, no injuries."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:45Z",
+ "subsystem": "Environmental",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Deck 3 climate regulator attempted to simulate 'San Francisco Fog' without authorization."
+ },
+ {
+ "timestamp": "2258-03-14T10:03:50Z",
+ "subsystem": "Computer",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Core subroutine briefly reclassified Captain Freeman as 'Furniture: Command-Class Recliner.'"
+ },
+ {
+ "timestamp": "2258-03-14T10:03:55Z",
+ "subsystem": "Holodeck",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Holodeck 2 running unauthorized program: 'Worf’s Bat’leth Pilates Volume 3.' Participation at 38%."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:00Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Power relay S-19 redirected output to deck lighting in rhythmic pulses. Crew reports 'disco corridor effect.'"
+ },
+ {
+ "timestamp": "2258-03-14T10:04:05Z",
+ "subsystem": "Science",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Sensor array detected neutrino echo shaped like the ship’s logo. Astrometrics refuses to comment."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:10Z",
+ "subsystem": "Medical",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Nurse Stevens miscalibrated dermal regenerator; gave Ensign T’Lun faint forehead tattoo of a targ."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:15Z",
+ "subsystem": "Security",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Brig camera feed replaced with loop of Lt. Shaxs bench-pressing a photon torpedo. No one claims responsibility."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:20Z",
+ "subsystem": "Crew",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Ensign Boimler filed formal complaint about being listed as 'Adventure Liability' in crew roster metadata."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:25Z",
+ "subsystem": "Replicators",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Replicator 4B insists every meal be served on ornate Klingon ceremonial plates. Lt. K'orin approves."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:30Z",
+ "subsystem": "Astrometrics",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Random subspace fold briefly displayed giant Vulcan eyebrow on long-range scans."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:35Z",
+ "subsystem": "Transporter",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Pattern enhancers created residual duplicate of Crewman Sirota’s left shoe. Duplicate is three sizes larger."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:40Z",
+ "subsystem": "Tactical",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Phaser diagnostics reported emitter latency due to Shaxs yelling 'MORE POWER' at random intervals."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:45Z",
+ "subsystem": "Computer",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Core logic node began recursively indexing its own error logs, generating 438 phantom alerts in 3 seconds."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:50Z",
+ "subsystem": "Cargo Bay",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Container labeled 'Do Not Open Under Any Circumstances' found open. Contents: 400 plush targ puppets."
+ },
+ {
+ "timestamp": "2258-03-14T10:04:55Z",
+ "subsystem": "Environmental",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Air filtration temporarily smelled of Andorian peppermint. Crew mildly pleased."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:00Z",
+ "subsystem": "Computer",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Auxiliary core started auto-generating haiku about warp plasma. Reading shows minor philosophical inconsistencies."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:05Z",
+ "subsystem": "Engineering",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Warp plasma manifold 3A produced sparks shaped like smiley faces. Crewman Patel applauded gesture."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:10Z",
+ "subsystem": "Holodeck",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Holodeck 5 running unauthorized program 'Interstellar Dog Show'; canine avatars escaped into corridor 12."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:15Z",
+ "subsystem": "Medical",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Dr. T’Ana reported Ensign Maru using hypospray as an air freshener. Room 3 now smells faintly of plasma burn."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:20Z",
+ "subsystem": "Crew",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Lt. Shaxs attempting to bench-press an EPS conduit for morale purposes; zero gravity assist enabled."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:25Z",
+ "subsystem": "Environmental",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Deck 9 air filters began playing 4D jazz through vent system. Crew reports mild disorientation."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:30Z",
+ "subsystem": "Security",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Brig door control temporarily replaced with voice recognition. Lt. Kayshon recited Klingon tongue twisters to test."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:35Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "EPS conduit Y-12 emitted random spark bursts. Crewman Sirota instructed to 'interpret as performance art.'"
+ },
+ {
+ "timestamp": "2258-03-14T10:05:40Z",
+ "subsystem": "Computer",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Core subroutine attempted to compile itself into origami. Manual override required."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:45Z",
+ "subsystem": "Replicators",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Replicator 6A produced 14 perfectly edible tribbles. Crewman Mariner attempted to negotiate ownership."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:50Z",
+ "subsystem": "Astrometrics",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Random subspace anomaly reported as 'smiling starfish'; sensors indicate no threat."
+ },
+ {
+ "timestamp": "2258-03-14T10:05:55Z",
+ "subsystem": "Transporter",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Pattern buffer generated minor echo of Ensign Phelps’ eyebrows during routine beam-in."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:00Z",
+ "subsystem": "Tactical",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Phaser bank 2 now aligned to emit harmless holographic butterflies for crew morale exercises."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:05Z",
+ "subsystem": "Crew",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Ensign Boimler insists his shoes were temporarily listed as 'hazardous cargo' in the inventory system."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:10Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Warp coil stabilization module emitted 7-second burst of blue sparks; crew advised to document spectacle."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:15Z",
+ "subsystem": "Medical",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Lt. Cmdr. T’Ana reports successful extraction of a micro-Targ from Ensign Mariner’s locker."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:20Z",
+ "subsystem": "Computer",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "LCARS dashboard spontaneously switched to retro monochrome theme; crew nostalgia rating: high."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:25Z",
+ "subsystem": "Environmental",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Hydroponics room 4 briefly filled with harmless bio-luminescent fog; visibility reduced to 73%."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:30Z",
+ "subsystem": "Security",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Transporter pad in Security Office used as temporary catwalk for a very confused tribble."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:35Z",
+ "subsystem": "Cargo Bay",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Automated pallet lift unexpectedly delivered 12 crates of rubber ducks to Deck 7 recreation area."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:40Z",
+ "subsystem": "Crew",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Captain Freeman approved spontaneous yoga break after micro-anomaly readings spiked morale sensors."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:45Z",
+ "subsystem": "Holodeck",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Holodeck 7 attempted to simulate 'All Crew as Tribbles'; program aborted after 2.3 seconds."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:50Z",
+ "subsystem": "Astrometrics",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Sensors detected anomalous quantum ping spelling out 'HI CAPTAIN'; source unknown."
+ },
+ {
+ "timestamp": "2258-03-14T10:06:55Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Deck 12 EPS relay began pulsing Morse code for 'I need coffee.' Crewman Patel acknowledged."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:00Z",
+ "subsystem": "Computer",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Core logic node entered recursive loop generating holographic cat memes on every console. Manual intervention required."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:05Z",
+ "subsystem": "Replicators",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Replicator outputting edible mini phasers at 72% efficiency. Lt. Shaxs approves as training tools."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:10Z",
+ "subsystem": "Medical",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Hypospray refill system labeled 'Mystery Juice' briefly administered non-lethal blue ink to volunteers."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:15Z",
+ "subsystem": "Environmental",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Deck 4 air vents released faint aroma of Vulcan plumeria. Crew morale up 3%."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:20Z",
+ "subsystem": "Security",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Security holo-training dummy reported to have 'requested vacation'. Lt. Kayshon amused."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:25Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Warp manifold 6C produced small plasma tornado. Crewman Patel advised to avoid standing in it."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:30Z",
+ "subsystem": "Computer",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Auxiliary AI attempted to rewrite ship-wide protocol using limericks. Partial success."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:35Z",
+ "subsystem": "Crew",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Ensign Boimler challenged replicator to a duel; replicator declined citing non-violence protocol."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:40Z",
+ "subsystem": "Holodeck",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Holodeck 6 generated 12 dancing Klingons; program paused after two accidental phaser discharges."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:45Z",
+ "subsystem": "Medical",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Dr. T’Ana treated Crewman Mariner for paper cut from top-secret manual. Crew morale slightly increased."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:50Z",
+ "subsystem": "Tactical",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Phaser bank diagnostics now include simulated applause for successful hits."
+ },
+ {
+ "timestamp": "2258-03-14T10:07:55Z",
+ "subsystem": "Astrometrics",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Sensor anomaly caused star map to display large smiling face. No known threat."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:00Z",
+ "subsystem": "Replicators",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Replicator 3C producing only spherical bread products. Crewman Mariner unsure if intentional."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:05Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "EPS conduit L-12 briefly emitted rainbow-colored sparks; sensors report no lasting damage."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:10Z",
+ "subsystem": "Computer",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Core logic node attempting to simulate alternate universe where Captain Freeman is a tribble. Manual override required."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:15Z",
+ "subsystem": "Environmental",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Humidity spikes detected in Deck 8 greenhouse; plants unaffected, crew mildly confused."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:20Z",
+ "subsystem": "Security",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Transporter room keypad briefly replaced by fortune-telling device. Lt. Kayshon predicted 'exciting day ahead'."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:25Z",
+ "subsystem": "Crew",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Ensign Rutherford accidentally uploaded his vacation photos to LCARS dashboard. Crew mildly entertained."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:30Z",
+ "subsystem": "Holodeck",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Holodeck 8 tried to simulate ‘All Crew as Ferengi’; program aborted after 4 seconds due to laughter overload."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:35Z",
+ "subsystem": "Cargo Bay",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Conveyor belt delivered 200 rubber ducks into zero-G recreation area; clean-up team dispatched."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:40Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Deck 14 warp stabilizer briefly emitted glow resembling smiley face; no damage detected."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:45Z",
+ "subsystem": "Computer",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Auxiliary AI began writing short stories about the ship’s life support ducts. Narrative incomplete."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:50Z",
+ "subsystem": "Medical",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Dr. T’Ana administered mild sedative to Crewman Mariner after spontaneous karaoke performance."
+ },
+ {
+ "timestamp": "2258-03-14T10:08:55Z",
+ "subsystem": "Tactical",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Phaser bank 3 now emits gentle musical notes on idle to boost crew morale."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:00Z",
+ "subsystem": "Environmental",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Deck 10 oxygen mixture briefly modified to simulate Martian atmosphere; crew unaffected."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:05Z",
+ "subsystem": "Astrometrics",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Sensor array detected star shaped like a cat; no navigational hazard."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:10Z",
+ "subsystem": "Replicators",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Replicator produced miniature tribble sculptures for crew gift exchange; edible but squeaky."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:15Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "EPS conduit M-09 randomly pulsed rainbow light; engineers advised to log as 'artistic output.'"
+ },
+ {
+ "timestamp": "2258-03-14T10:09:20Z",
+ "subsystem": "Crew",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Lt. Shaxs held impromptu strength contest with gravity training hologram; hologram lost."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:25Z",
+ "subsystem": "Computer",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Core attempted to simulate entire starship in a single memory sector. Partial crash occurred."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:30Z",
+ "subsystem": "Medical",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Crewman Phelps received temporary tattoo of mini-targ on left shoulder; non-toxic."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:35Z",
+ "subsystem": "Holodeck",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Holodeck 9 running 'Galactic Tea Party'; minor quantum displacement detected in replicator outputs."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:40Z",
+ "subsystem": "Security",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Deck 2 surveillance feed replaced with continuous loop of Lt. Kayshon juggling phasers."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:45Z",
+ "subsystem": "Environmental",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Deck 7 ventilation briefly simulated ocean breeze. Crew reported mild relaxation."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:50Z",
+ "subsystem": "Cargo Bay",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Zero-G crate misaligned; 3 crates of Targ plushies drifted into corridor 11."
+ },
+ {
+ "timestamp": "2258-03-14T10:09:55Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Deck 11 warp stabilizer output showing unexpected rainbow spectrum; no critical damage."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:00Z",
+ "subsystem": "Computer",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Auxiliary AI attempted to merge all ship logs into a single poem; caused minor UI collapse."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:05Z",
+ "subsystem": "Holodeck",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Holodeck 10 ran 'Galactic Zoo'; 5 simulated Targs escaped into corridor 9."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:10Z",
+ "subsystem": "Medical",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Ensign Maru given non-lethal shock for attempting to teach replicator interpretive dance."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:15Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "EPS conduit N-17 briefly pulsed Morse code: 'I love coffee.' Crew morale confirmed uplifted."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:20Z",
+ "subsystem": "Environmental",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Deck 5 greenhouse ambient light cycled through rainbow spectrum for 12 seconds; photosynthesis unaffected."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:25Z",
+ "subsystem": "Replicators",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Replicator 2A producing miniature photon torpedoes for training; edible but slightly metallic taste."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:30Z",
+ "subsystem": "Tactical",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Phaser bank 4 outputting harmless holographic stars as idle animation."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:35Z",
+ "subsystem": "Crew",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Captain Freeman conducted spontaneous trivia contest on Deck 3; winners rewarded with mini tribbles."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:40Z",
+ "subsystem": "Astrometrics",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Sensor anomaly showed distant star blinking rhythmically; logged as 'friendly hello.'"
+ },
+ {
+ "timestamp": "2258-03-14T10:10:45Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Warp coil 7B emitted sparks shaped like miniature starships. Crewman Patel photographed for morale log."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:50Z",
+ "subsystem": "Computer",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "LCARS interface generated random motivational quotes for each console; some contradictory."
+ },
+ {
+ "timestamp": "2258-03-14T10:10:55Z",
+ "subsystem": "Holodeck",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Holodeck 11 simulated 'Crew as Giant Cheese Wheels'; program paused after 3 seconds to prevent existential distress."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:00Z",
+ "subsystem": "Medical",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Crewman Boimler treated for minor paper cut from holo-legal form; minor embarrassment reported."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:05Z",
+ "subsystem": "Security",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Deck 6 security cameras briefly displayed live feed of tribble horde in recreation bay. No alarm triggered."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:10Z",
+ "subsystem": "Environmental",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Air mixture in Deck 7 briefly shifted to simulate Klingon vineyard; crew requested repeat performance."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:15Z",
+ "subsystem": "Cargo Bay",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Zero-G pallet misaligned; 8 crates of plush Targs floated into corridor 12. Crewman Knox deployed net."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:20Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Deck 13 EPS stabilizer emitted rainbow-colored sparks; no damage detected."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:25Z",
+ "subsystem": "Computer",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Core attempted to simulate entire ship as holographic chessboard; partial memory lock occurred."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:30Z",
+ "subsystem": "Replicators",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Replicator produced 50 mini photon torpedoes for training; edible but slightly metallic taste."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:35Z",
+ "subsystem": "Tactical",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Phaser bank 5 configured to emit harmless confetti bursts on idle; morale boost noted."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:40Z",
+ "subsystem": "Crew",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Lt. Shaxs conducted impromptu arm wrestling contest with holographic duplicate; duplicate lost."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:45Z",
+ "subsystem": "Environmental",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Deck 8 greenhouse briefly bathed in rainbow lighting to test plant photosynthesis under alternate spectra."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:50Z",
+ "subsystem": "Astrometrics",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Sensor anomaly displayed star in shape of peace sign; logged as decorative phenomenon."
+ },
+ {
+ "timestamp": "2258-03-14T10:11:55Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "EPS conduit O-21 emitted sparks resembling miniature ships; crew documented for morale."
+ },
+ {
+ "timestamp": "2258-03-14T10:12:00Z",
+ "subsystem": "Computer",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Auxiliary AI began writing short adventure stories about the ship’s ducts; story incomplete."
+ },
+ {
+ "timestamp": "2258-03-14T10:12:05Z",
+ "subsystem": "Medical",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Crewman Phelps temporarily given minor tattoo of mini-Targ; non-toxic."
+ },
+ {
+ "timestamp": "2258-03-14T10:12:10Z",
+ "subsystem": "Holodeck",
+ "severity": "WARNING",
+ "color": "yellow",
+ "message": "Holodeck 12 attempted 'Galactic Tea Party'; minor quantum displacement detected in replicator outputs."
+ },
+ {
+ "timestamp": "2258-03-14T10:12:15Z",
+ "subsystem": "Security",
+ "severity": "INFO",
+ "color": "teal",
+ "message": "Deck 7 surveillance temporarily replaced with footage of Lt. Kayshon juggling phasers."
+ },
+ {
+ "timestamp": "2258-03-14T10:12:20Z",
+ "subsystem": "Environmental",
+ "severity": "NOTICE",
+ "color": "blue",
+ "message": "Deck 7 air briefly simulated Martian atmosphere; crew unaffected."
+ },
+ {
+ "timestamp": "2258-03-14T10:12:25Z",
+ "subsystem": "Cargo Bay",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Zero-G crate misaligned; 10 crates of Targ plushies drifted into corridor 13."
+ },
+ {
+ "timestamp": "2258-03-14T10:12:30Z",
+ "subsystem": "Engineering",
+ "severity": "ALERT",
+ "color": "orange",
+ "message": "Deck 15 warp stabilizer outputting rainbow spectrum; no critical damage."
+ },
+ {
+ "timestamp": "2258-03-14T10:12:35Z",
+ "subsystem": "Computer",
+ "severity": "CRITICAL",
+ "color": "red",
+ "message": "Core attempted to rewrite it's internal sub routines; manual intervention required. Shipwide system failure imminent."
+ }
+]
\ No newline at end of file
diff --git a/lcars/experiments.go b/lcars/experiments.go
new file mode 100644
index 0000000..4cfee16
--- /dev/null
+++ b/lcars/experiments.go
@@ -0,0 +1,79 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+)
+
+func readCrew() (string, error) {
+
+ content, err := embedded.ReadFile("embed/crew.json")
+ if err != nil {
+ return "", err
+ }
+
+ // Create a slice to hold the parsed names
+ var crewNames []string
+
+ // Parse the JSON
+ err = json.Unmarshal(content, &crewNames)
+ if err != nil {
+ fmt.Println("Error parsing JSON:", err)
+ return "", err
+ }
+
+ // Print the results
+ for i, text := range crewNames {
+ rank, name := splitRank(text)
+ if rank == "" {
+ rank = "ERROR"
+ }
+ fmt.Printf("%d: rank: %s name: %s\n", i+1, rank , name)
+ }
+ // fmt.Println(string(content))
+ return "", nil
+
+}
+
+// splitRank separates the rank (all tokens except the last two) from the crewman's name (last two tokens)
+func splitRank(fullName string) (rank, name string) {
+ tokens := strings.Fields(fullName)
+ if len(tokens) < 2 {
+ return fullName, "" // fallback if malformed
+ }
+
+ nameTokens := tokens[len(tokens)-2:] // last 2 tokens as name
+ rankTokens := tokens[:len(tokens)-2] // everything else as rank
+ name = strings.Join(nameTokens, " ")
+ rank = strings.Join(rankTokens, " ")
+
+ return rank, name
+}
+
+type Message struct {
+ Timestamp string `json:"timestamp"`
+ Subsystem string `json:"subsystem"`
+ Severity string `json:"severity"`
+ Color string `json:"color"`
+ Message string `json:"message"`
+}
+
+func readMessages() ([]Message, error) {
+ content, err := embedded.ReadFile("embed/messages.json")
+ if err != nil {
+ return nil, err
+ }
+
+ var messages []Message
+ if err := json.Unmarshal(content, &messages); err != nil {
+ return nil, err
+ }
+
+ // For demonstration, print the parsed messages
+ for _, m := range messages {
+ fmt.Printf("[%s] %s (%s) - %s\n", m.Timestamp, m.Subsystem, m.Severity, m.Message)
+ }
+
+ return messages, nil
+}
diff --git a/lcars/frontend/assets/Antonio-Bold.woff b/lcars/frontend/assets/Antonio-Bold.woff
new file mode 100644
index 0000000..6f24591
Binary files /dev/null and b/lcars/frontend/assets/Antonio-Bold.woff differ
diff --git a/lcars/frontend/assets/Antonio-Bold.woff2 b/lcars/frontend/assets/Antonio-Bold.woff2
new file mode 100644
index 0000000..e772d58
Binary files /dev/null and b/lcars/frontend/assets/Antonio-Bold.woff2 differ
diff --git a/lcars/frontend/assets/Antonio-Regular.woff b/lcars/frontend/assets/Antonio-Regular.woff
new file mode 100644
index 0000000..19027a5
Binary files /dev/null and b/lcars/frontend/assets/Antonio-Regular.woff differ
diff --git a/lcars/frontend/assets/Antonio-Regular.woff2 b/lcars/frontend/assets/Antonio-Regular.woff2
new file mode 100644
index 0000000..2c89826
Binary files /dev/null and b/lcars/frontend/assets/Antonio-Regular.woff2 differ
diff --git a/lcars/frontend/assets/beep1.mp3 b/lcars/frontend/assets/beep1.mp3
new file mode 100644
index 0000000..9b8c564
Binary files /dev/null and b/lcars/frontend/assets/beep1.mp3 differ
diff --git a/lcars/frontend/assets/beep2.mp3 b/lcars/frontend/assets/beep2.mp3
new file mode 100644
index 0000000..c7e07f6
Binary files /dev/null and b/lcars/frontend/assets/beep2.mp3 differ
diff --git a/lcars/frontend/assets/beep3.mp3 b/lcars/frontend/assets/beep3.mp3
new file mode 100644
index 0000000..26c59dc
Binary files /dev/null and b/lcars/frontend/assets/beep3.mp3 differ
diff --git a/lcars/frontend/assets/beep4.mp3 b/lcars/frontend/assets/beep4.mp3
new file mode 100644
index 0000000..8b39d77
Binary files /dev/null and b/lcars/frontend/assets/beep4.mp3 differ
diff --git a/lcars/frontend/assets/classic.css b/lcars/frontend/assets/classic.css
new file mode 100644
index 0000000..5875c89
--- /dev/null
+++ b/lcars/frontend/assets/classic.css
@@ -0,0 +1,3011 @@
+@charset "utf-8";
+
+/*
+
+ CSS Document
+ LCARS Classic Theme
+ Version 24.2
+ By Jim Robertus www.thelcars.com
+ Modified: 2025 Jul 27
+
+*/
+
+:root {
+ font-size: 1.375rem;
+ color-scheme: dark;
+ --lfw: 240px;
+ --african-violet: #c9f;
+ --almond: #ffaa90;
+ --almond-creme: #fba;
+ --blue: #56f;
+ --bluey: #89f;
+ --butterscotch: #f96;
+ --gold: #fa0;
+ --golden-orange: #f90;
+ --gray: #668;
+ --green: #993;
+ --ice: #9cf;
+ --lilac: #c5f;
+ --lima-bean: #cc6;
+ --magenta: #c59;
+ --mars: #f20;
+ --moonlit-violet: #96f;
+ --orange: #f80;
+ --peach: #f86;
+ --red: #c44;
+ --sky: #aaf;
+ --space-white: #f5f6fa;
+ --sunflower: #fc9;
+ --tomato: #f55;
+ --violet-creme: #dbf;
+ --left-frame-top-color: var(--bluey);
+ --left-frame-color: var(--red); /* .panel-9 inherits this color */
+ --left-frame-padding: .75rem;
+ --corner-color-top: var(--bluey);
+ --corner-color-bottom: var(--red);
+ --panel-1-color: var(--african-violet);
+ --panel-4-color: var(--red);
+ --panel-5-color: var(--orange);
+ --panel-6-color: var(--butterscotch);
+ --panel-7-color: var(--bluey);
+ --panel-8-color: var(--butterscotch);
+ --panel-10-color: var(--orange);
+ --panel-top-button-color: var(--african-violet);
+ --bar-height: 28px;
+ --bar-1-6-width: 40%;
+ --bar-2-7-width: 4%;
+ --bar-3-8-width: 17%;
+ --bar-5-10-width: 4%;
+ --bar-1-color: var(--bluey);
+ --bar-2-color: var(--orange);
+ --bar-3-color: var(--african-violet);
+ --bar-4-color: var(--african-violet);
+ --bar-5-color: var(--red);
+ --bar-6-color: var(--red);
+ --bar-7-color: var(--butterscotch);
+ --bar-8-color: var(--red);
+ --bar-9-color: var(--african-violet);
+ --bar-10-color: var(--butterscotch);
+
+/* Ultra layout elements */
+
+ --section-2-color: var(--almond-creme);
+ --pill-1-color: var(--red);
+ --pill-2-color: var(--butterscotch);
+ --pill-3-color: var(--bluey);
+ --pill-4-color: var(--almond-creme);
+ --pill-5-color: var(--orange);
+ --pill-6-color: var(--moonlit-violet);
+ --pill-a1-color: var(--moonlit-violet);
+ --pill-a2-color: var(--moonlit-violet);
+ --pill-a3-color: black;
+ --pill-a4-color: var(--african-violet);
+ --pill-a5-color: var(--orange);
+ --pill-a6-color: var(--almond-creme);
+ --panel-11-color: var(--red);
+ --panel-12-color: var(--bluey);
+ --panel-13-color: var(--almond-creme);
+ --panel-14-color: var(--almond-creme);
+ --panel-15-color: var(--almond-creme);
+
+ /* End Ultra layout elements */
+
+ --radius-top: 0 0 0 160px;
+ --radius-bottom: 160px 0 0 0;
+ --radius-content-top: 0 0 0 60px;
+ --radius-content-bottom: 60px 0 0 0;
+ --panel-border: .25rem solid black;
+ --bar-border: .25rem solid black;
+ --bar-cut-width: 34%;
+ --bar-cut-out-width: 34%;
+ --divider-height: .5rem;
+/*
+ NOTE: --font-color also sets the following:
+ 1. horizontal line
color
+ 2. lcars-list default bullet color
+ 3. blockquote border color
+ 4. images with the *border* class border color
+*/
+ --font-color: var(--african-violet);
+ --sub-fonts: .875rem;
+ --dc-font-size: .875rem;
+ --dc-row-height: calc(var(--dc-font-size) + .125rem);
+ --banner-color: var(--orange);
+ --meta-data-color: var(--african-violet);
+ --data-cascade-color: var(--orange);
+ --light-color: white;
+ --h1-color: var(--african-violet);
+ --h2-color: var(--african-violet);
+ --h3-color: var(--african-violet);
+ --h4-color: var(--african-violet);
+ --link-color: #c16fff;
+ --code-color: var(--orange);
+ --nav-width: 240px;
+ --nav-1-color: var(--bluey);
+ --nav-2-color: var(--butterscotch);
+ --nav-3-color: var(--orange);
+ --nav-4-color: var(--red);
+ --button-color: var(--african-violet);
+ --button-color-sidebar: var(--red);
+ --lcars-bar-color: var(--african-violet);
+ --lcars-bar-start-color: var(--moonlit-violet);
+ --lcars-bar-end-color: var(--moonlit-violet);
+ --lcars-bar-text-color: var(--golden-orange);
+
+/* Image Frame */
+
+ --image-border-color: var(--almond-creme);
+ --primary-color: var(--orange);
+ --secondary-color: var(--moonlit-violet);
+ --accent-color: var(--almond-creme);
+ --title-color: var(--almond-creme);
+ --spacers: .65rem;
+ --frame-height: 40px;
+}
+
+@media (max-width: 1500px) {
+ :root {
+ --lfw: 200px;
+ --radius-top: 0 0 0 130px;
+ --radius-bottom: 130px 0 0 0;
+ --divider-height: .4rem;
+ --nav-width: 210px;
+ --dc-font-size: .75rem;
+ --bar-height: 24px;
+ }
+}
+
+@media (max-width: 1300px) {
+ :root {
+ font-size: 1.2rem;
+ --sub-fonts: .9rem;
+ --lfw: 180px;
+ --nav-width: 180px;
+ --radius-top: 0 0 0 100px;
+ --radius-bottom: 100px 0 0 0;
+ --radius-content-top: 0 0 0 40px;
+ --radius-content-bottom: 40px 0 0 0;
+ --bar-height: 20px;
+ }
+}
+
+@media (max-width: 950px) {
+ :root {
+ --lfw: 150px;
+ }
+}
+
+@media (max-width: 750px) {
+ :root {
+ --panel-border: .25rem solid black;
+ --bar-border: .25rem solid black;
+ --lfw: 120px;
+ --radius-top: 0 0 0 80px;
+ --radius-bottom: 80px 0 0 0;
+ --radius-content-top: 0 0 0 34px;
+ --radius-content-bottom: 34px 0 0 0;
+ --nav-width: 174px;
+ --bar-height: 16px;
+ --spacers: .5rem;
+ --frame-height: 25px;
+ }
+}
+
+@media (max-width: 525px) {
+ :root {
+ --lfw: 62px;
+ --left-frame-padding: .5rem;
+ --radius-top: 0 0 0 40px;
+ --radius-bottom: 40px 0 0 0;
+ --bar-height: 10px;
+ --divider-height: .3rem;
+ }
+}
+
+@media (max-width: 450px) {
+ :root {
+ --nav-width: 48%;
+ }
+}
+
+*, *:after, *:before {
+ box-sizing: border-box;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ font: inherit;
+}
+
+img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
+input, textarea, button, select {
+ font: inherit;
+}
+
+@font-face {
+ font-family: 'Antonio';
+ font-weight: 400;
+ src: url('Antonio-Regular.woff2') format('woff2'),
+ url('Antonio-Regular.woff') format('woff');
+}
+
+@font-face {
+ font-family: 'Antonio';
+ font-weight: 700;
+ src: url('Antonio-Bold.woff2') format('woff2'),
+ url('Antonio-Bold.woff') format('woff')
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ display: flex;
+ padding-top: 10px;
+ padding-left: 5px;
+ background-color: black;
+ font-family: 'Antonio', 'Arial Narrow', 'Avenir Next Condensed', sans-serif;
+ font-weight: 400;
+ line-height: 1.5;
+ color: var(--font-color);
+}
+
+a {
+ text-decoration: underline;
+ text-decoration-thickness: 2px;
+ text-underline-offset: .2rem;
+ color: var(--link-color);
+}
+
+a:hover {
+ filter: brightness(115%);
+ animation: none;
+}
+
+a:active {
+ filter: brightness(80%);
+ outline: none;
+}
+
+button {
+ border: none;
+ outline: none;
+ color: black;
+ transition: width 1s;
+}
+
+button:hover {
+ cursor: pointer;
+ animation: none;
+ filter: brightness(115%);
+ color: black;
+}
+
+button:active {
+ filter: brightness(85%);
+}
+
+/* Ultra Layout elements */
+
+.wrap-everything {
+ display: flex;
+ width: 100%;
+ column-gap: 10px;
+}
+
+.wrap-standard {
+ width: 100%;
+}
+
+#column-1 {
+ width: 350px;
+ padding: 10px 10px 10px 20px;
+ transition: 800ms;
+}
+
+#column-2 {
+ width: var(--lfw);
+ background-color: var(--section-2-color);
+ text-align: right;
+ font-weight: bold;
+ line-height: 1.2;
+ color: black;
+ transition: 800ms;
+ z-index: 2;
+}
+
+#column-2 a {
+ color: black;
+ text-decoration: none;
+}
+
+#column-3 {
+ flex: 1;
+ margin-inline: auto;
+}
+
+.wrap {
+ display: flex;
+ margin-inline: auto;
+ padding-left: 5px;
+ padding-right: 15px;
+ overflow: hidden;
+}
+
+@media (max-width: 1680px) {
+ #column-1 {
+ margin-left: -370px;
+ }
+
+ #column-2 {
+ margin-left: -230px;
+ }
+
+ .wrap-everything {
+ column-gap: 5px;
+ }
+}
+
+@media (max-width: 1500px) {
+ #column-1,
+ #column-2 {
+ display: none;
+ }
+}
+
+.lcars-frame {
+ display: flex;
+ min-height: 280px;
+ position: relative;
+ --frame-color: var(--african-violet);
+}
+
+.frame-col-1 {
+ width: 20px;
+ height: 280px;
+ background: var(--frame-color);
+ border-radius: 16px 0 0 16px;
+ position: relative;
+}
+
+.frame-col-1:before {
+ content: '';
+ display: block;
+ width: 20px;
+ height: 200px;
+ border-top: 5px solid black;
+ border-bottom: 5px solid black;
+ background-color: var(--frame-color);
+ position: absolute;
+ top: 40px;
+ left: 0;
+}
+
+.frame-col-1-cell-a {
+ width: 14px;
+ height: 65px;
+ background-color: var(--tomato);
+ border-left: 4px solid black;
+ border-bottom: 4px solid black;
+ position: absolute;
+ top: 45px;
+ right: 0;
+ z-index: 2;
+}
+
+.frame-col-1-cell-b {
+ width: 14px;
+ height: 70px;
+ background-color: var(--bluey);
+ border-left: 4px solid black;
+ position: absolute;
+ top: 110px;
+ right: 0;
+ z-index: 2;
+}
+
+.frame-col-1-cell-c {
+ width: 14px;
+ height: 65px;
+ background-color: var(--orange);
+ border-top: 4px solid black;
+ border-left: 4px solid black;
+ position: absolute;
+ bottom: 45px;
+ right: 0;
+ z-index: 2;
+}
+
+.frame-col-1-blocks:before {
+ content: '';
+ display: block;
+ width: 10px;
+ height: 3px;
+ background-color: black;
+ position: absolute;
+ top: 54px;
+ left: 0;
+}
+
+.frame-col-2 {
+ width:20px;
+ height: 280px;
+ background-color: var(--frame-color);
+ position: relative;
+}
+
+.frame-col-2:before {
+ content: '';
+ display: block;
+ width: 20px;
+ height: 240px;
+ background-color: black;
+ border-radius: 10px 0 0 10px;
+ position: absolute;
+ top: 20px;
+ left: 0;
+}
+
+.frame-col-3 {
+ display: flex;
+ width: 240px;
+ height: 280px;
+ align-items: center;
+ justify-content: center;
+}
+
+.frame-col-4 {
+ width:20px;
+ height: 280px;
+ background-color: var(--frame-color);
+ position: relative;
+}
+
+.frame-col-4:before {
+ content: '';
+ display: block;
+ width: 20px;
+ height: 240px;
+ background-color: black;
+ border-radius: 0 10px 10px 0;
+ position: absolute;
+ top: 20px;
+ left: 0;
+}
+
+.display-horizontal {
+ rotate: 90deg;
+}
+
+.frame-col-5 {
+ width:20px;
+ height: 280px;
+ background-color: var(--frame-color);
+ border-radius: 0 16px 16px 0;
+ padding-top: 40px;
+ position: relative;
+}
+
+.frame-col-5:before {
+ content: '';
+ display: block;
+ width: 20px;
+ height: 200px;
+ border-top: 5px solid black;
+ border-bottom: 5px solid black;
+ background-color: var(--frame-color);
+}
+
+.frame-col-5-cell-a {
+ width: 14px;
+ height: 65px;
+ background-color: var(--lilac);
+ border-bottom: 4px solid black;
+ border-right: 4px solid black;
+ position: absolute;
+ top: 45px;
+ left: 0;
+ z-index: 2;
+}
+
+.frame-col-5-cell-b {
+ width: 14px;
+ height: 70px;
+ background-color: var(--violet-creme);
+ border-right: 4px solid black;
+ position: absolute;
+ top: 110px;
+ left: 0;
+ z-index: 2;
+}
+
+.frame-col-5-cell-c {
+ width: 14px;
+ height: 65px;
+ background-color: var(--moonlit-violet);
+ border-top: 4px solid black;
+ border-right: 4px solid black;
+ position: absolute;
+ bottom: 45px;
+ left: 0;
+ z-index: 2;
+}
+
+.line {
+ height: 20px;
+ width: 12px;
+ background: linear-gradient(#600, var(--mars), #600);
+}
+
+.line:nth-child(1) {
+ animation: animateLine6 1s 0.2s infinite;
+}
+
+.line:nth-child(2) {
+ animation: animateLine5 1s 0.3s infinite;
+}
+
+.line:nth-child(3) {
+ animation: animateLine3 1s 0.4s infinite;
+}
+
+.line:nth-child(4) {
+ animation: animateLine3 1s 0.5s infinite;
+}
+
+.line:nth-child(5) {
+ animation: animateLine2 1s 0.6s infinite;
+}
+
+.line:nth-child(6) {
+ animation: animateLine2 1s 0.7s infinite;
+}
+
+.line:nth-child(7) {
+ animation: animateLine2 1s 0.8s infinite;
+}
+
+/* 8 & 9 are middle lines*/
+
+.line:nth-child(8) {
+ animation: animateLine4 1s 0.9s infinite;
+}
+
+.line:nth-child(9) {
+ animation: animateLine4 1s 1s infinite;
+}
+
+.line:nth-child(10) {
+ animation: animateLine2 1s 0.8s infinite;
+}
+
+.line:nth-child(11) {
+ animation: animateLine2 1s 0.7s infinite;
+}
+
+.line:nth-child(12) {
+ animation: animateLine2 1s 0.6s infinite;
+}
+
+.line:nth-child(13) {
+ animation: animateLine3 1s 0.5s infinite;
+}
+
+.line:nth-child(14) {
+ animation: animateLine3 1s 0.4s infinite;
+}
+
+.line:nth-child(15) {
+ animation: animateLine5 1s 0.3s infinite;
+}
+
+.line:nth-child(16) {
+ animation: animateLine6 1s 0.2s infinite;
+}
+
+@keyframes animateLine2 {
+ 0% {
+ height: 180px;
+ }
+ 50% {
+ height: 90px;
+ }
+ 100% {
+ height: 180px;
+ }
+}
+
+@keyframes animateLine3 {
+ 0% {
+ height: 120px;
+ }
+ 50% {
+ height: 60px;
+ }
+ 100% {
+ height: 120px;
+ }
+}
+
+@keyframes animateLine4 {
+ 0% {
+ height: 230px;
+ }
+ 50% {
+ height: 115px;
+ }
+ 100% {
+ height: 230px;
+ }
+}
+
+@keyframes animateLine5 {
+ 0% {
+ height: 60px;
+ }
+ 50% {
+ height: 30px;
+ }
+ 100% {
+ height: 60px;
+ }
+}
+
+@keyframes animateLine6 {
+ 0% {
+ height: 30px;
+ }
+ 50% {
+ height: 15px;
+ }
+ 100% {
+ height: 30px;
+ }
+}
+
+.pillbox,
+.pillbox-2 {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-gap: 8px;
+ margin: 1.25rem auto;
+ text-align: right;
+ font-size: var(--sub-fonts);
+}
+
+.pill,
+.pill-2 {
+ display: flex;
+ width: 100%;
+ height: 56px;
+ justify-content: flex-end;
+ align-items: flex-end;
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+ padding-right: .75rem;
+ padding-bottom: .35rem;
+}
+
+.pillbox a {
+ display: flex;
+ width: 100%;
+ height: 56px;
+ justify-content: flex-end;
+ align-items: flex-end;
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+ padding-right: .75rem;
+ padding-bottom: .35rem;
+}
+
+.pillbox-2 a {
+ display: flex;
+ width: 100%;
+ height: 56px;
+ justify-content: flex-end;
+ align-items: flex-end;
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+ padding-right: .75rem;
+ padding-bottom: .35rem;
+}
+
+.pill:hover,
+.pill-2:hover {
+ filter: brightness(115%);
+}
+
+.pill:active,
+.pill-2:active {
+ filter: brightness(80%);
+}
+
+.pill:nth-child(1),
+.pillbox a:nth-child(1) {
+ border-radius: 100vmax 0 0 100vmax;
+ background-color: var(--pill-1-color);
+}
+
+.pill:nth-child(2),
+.pillbox a:nth-child(2) {
+ background-color: var(--pill-2-color);
+}
+
+.pill:nth-child(3),
+.pillbox a:nth-child(3) {
+ border-radius: 100vmax 0 0 100vmax;
+ background-color: var(--pill-3-color);
+}
+
+.pill:nth-child(4),
+.pillbox a:nth-child(4) {
+ background-color: var(--pill-4-color);
+}
+
+.pill:nth-child(5),
+.pillbox a:nth-child(5) {
+ background-color: var(--pill-5-color);
+ border-radius: 100vmax 0 0 100vmax;
+}
+
+.pill:nth-child(6),
+.pillbox a:nth-child(6) {
+ background-color: var(--pill-6-color);
+}
+
+.pill-2:nth-child(1),
+.pillbox-2 a:nth-child(1) {
+ border-radius: 100vmax 0 0 100vmax;
+ background-color: var(--pill-a1-color);
+}
+
+.pill-2:nth-child(2),
+.pillbox-2 a:nth-child(2) {
+ background-color: var(--pill-a2-color);
+ border-radius: 0 100vmax 100vmax 0;
+ padding-right: 1rem;
+}
+
+.pill-2:nth-child(3),
+.pillbox-2 a:nth-child(3) {
+ background-color: var(--pill-a3-color);
+}
+
+.pill-2:nth-child(4),
+.pillbox-2 a:nth-child(4) {
+ background-color: var(--pill-a4-color);
+ border-radius: 0 100vmax 100vmax 0;
+ padding-right: 1rem;
+}
+
+.pill-2:nth-child(5),
+.pillbox-2 a:nth-child(5) {
+ background-color: var(--pill-a5-color);
+ border-radius: 100vmax 0 0 100vmax;
+}
+
+.pill-2:nth-child(6),
+.pillbox-2 a:nth-child(6) {
+ background-color: var(--pill-a6-color);
+ border-radius: 0 100vmax 100vmax 0;
+ padding-right: 1rem;
+}
+
+.lcars-list-2 ul {
+ list-style: none;
+}
+
+.lcars-list-2 {
+ margin: 0 auto 50px auto;
+ padding-left: 5px;
+}
+
+.lcars-list-2 li {
+ position: relative;
+ padding-bottom: 5px;
+ padding-left: 38px;
+ font-size: var(--sub-fonts);
+ color: var(--orange);
+}
+
+.lcars-list-2 li::before {
+ content: '';
+ display: block;
+ width: 24px;
+ height: 14px;
+ border-radius: 50%;
+ background-color: var(--orange);
+ position: absolute;
+ top: 8px;
+ left: 0;
+}
+
+.panel-11 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 27vh;
+ max-height: 275px;
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ background-color: var(--panel-11-color);
+ border-bottom: var(--panel-border);
+}
+
+
+.panel-12 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 34vh;
+ max-height: 350px;
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ background-color: var(--panel-12-color);
+ border-bottom: var(--panel-border);
+}
+
+.panel-13 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 20vh;
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ background-color: var(--panel-13-color);
+ border-bottom: var(--panel-border);
+}
+
+.panel-14 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 20vh;
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ background-color: var(--panel-14-color);
+ border-bottom: var(--panel-border);
+}
+
+.panel-15 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 20vh;
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ background-color: var(--panel-15-color);
+ border-bottom: var(--panel-border);
+}
+
+.section-2-buttons a {
+ display: block;
+ text-decoration: none;
+ text-align: right;
+ border-bottom: var(--panel-border);
+ padding: 1.5rem .75rem .75rem 2px;
+ background-color: var(--section-2-color);
+ text-transform: uppercase;
+ color: black;
+}
+
+.section-2-buttons a:nth-child(2) {
+ background-color: var(--butterscotch);
+}
+
+.section-2-buttons a:nth-child(3) {
+ background-color: var(--african-violet);
+}
+
+/* End Ultra layout elements */
+
+.scroll-top {
+ display: none;
+}
+
+.left-frame-top,
+.left-frame {
+ width: var(--lfw);
+ text-align: right;
+ font-size: clamp(.875rem, 2vw, 1rem);
+ line-height: 1.2;
+ font-weight: bold;
+ color: black;
+ transition: width 1s;
+}
+
+.left-frame-top {
+ background-color: var(--left-frame-top-color);
+ border-radius: var(--radius-top);
+}
+
+.left-frame-top a,
+.left-frame a {
+ text-decoration: none;
+ color: black;
+}
+
+.left-frame {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding-top: 100px;
+ background-color: var(--left-frame-color);
+ border-radius: var(--radius-bottom);
+}
+
+.right-frame-top {
+ flex: 1;
+ align-content: flex-end;
+ position: relative;
+}
+
+.right-frame-top:before {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(to top right, var(--corner-color-top) 50%, black 50%);
+ position: absolute;
+ left: 0;
+ bottom: var(--bar-height);
+ z-index: -1;
+}
+
+.right-frame-top:after {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background-color: black;
+ border-radius: var(--radius-content-top);
+ position: absolute;
+ left: 0;
+ bottom: var(--bar-height);
+ z-index: -1;
+}
+
+@media (max-width: 650px) {
+ .right-frame-top:before {
+ bottom: 16px;
+ }
+
+ .right-frame-top:after {
+ bottom: 16px;
+ }
+}
+
+@media (max-width: 525px) {
+ .right-frame-top:before {
+ bottom: 10px;
+ }
+
+ .right-frame-top:after {
+ bottom: 10px;
+ }
+}
+
+.banner {
+ padding-bottom: 1rem;
+ padding-left: 5px;
+ text-align: right;
+ text-transform: uppercase;
+ line-height: 1.1;
+ font-size: clamp(1.25rem, 0.75rem + 4vw, 4rem);
+ color: var(--banner-color);
+}
+
+.banner a {
+ color: var(--banner-color);
+ text-decoration: none;
+}
+
+.data-cascade-button-group {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-start;
+ flex-wrap: wrap;
+}
+
+.header-content {
+ flex: 1;
+ min-height: 180px;
+ padding-right: .5rem;
+ padding-left: clamp(20px, 3vw, 50px);
+}
+
+.header-content > *:first-child {
+ margin-top: 0;
+}
+
+.header-content > *:last-child {
+ margin-bottom: 0;
+}
+
+/* Data Cascade 2025 */
+
+.data-cascade-wrapper {
+ flex: 1;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ max-width: 100%;
+ height: calc(var(--dc-row-height) * 9); /* 204px */
+ overflow: hidden;
+ padding-right: .5rem;
+ padding-left: clamp(20px, 3vw, 50px);
+ column-gap: .5rem;
+}
+
+.data-column {
+ display: grid;
+ grid-template-columns: 1fr;
+ margin-top: 1px;
+ text-align: right;
+ font-size: var(--dc-font-size); /* .938 */
+ line-height: 1;
+ color: black;
+}
+
+.dc-row-1,
+.dc-row-2,
+.dc-row-3,
+.dc-row-4,
+.dc-row-5,
+.dc-row-6,
+.dc-row-7 {
+ text-box: trim-both cap alphabetic;
+ height: var(--dc-row-height);
+}
+
+@media (max-width: 780px) {
+ .data-cascade-wrapper {
+ display: none;
+ }
+}
+
+@keyframes data-group-1 {
+
+ 0%,
+ 3.99% {
+ color: black;
+ }
+
+ 4%,
+ 45.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 46%,
+ 49.99% {
+ color: var(--light-color);
+ }
+
+ 50%,
+ 63.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 64%,
+ 67.99% {
+ color: var(--light-color);
+ }
+
+ 68%,
+ 100% {
+ color: var(--data-cascade-color);
+ }
+}
+
+@keyframes data-group-1a {
+
+ 0%,
+ 4.99% {
+ color: black;
+ }
+
+ 5%,
+ 45.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 46%,
+ 49.99% {
+ color: var(--light-color);
+ }
+
+ 50%,
+ 63.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 64%,
+ 67.99% {
+ color: var(--light-color);
+ }
+
+ 68%,
+ 100% {
+ color: var(--data-cascade-color);
+ }
+}
+
+@keyframes data-group-2 {
+
+ 0%,
+ 12.99% {
+ color: black;
+ }
+
+ 13%,
+ 49.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 50%,
+ 53.99% {
+ color: var(--light-color);
+ }
+
+ 54%,
+ 67.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 68%,
+ 71.99% {
+ color: var(--light-color);
+ }
+
+ 72%,
+ 100% {
+ color: var(--data-cascade-color);
+ }
+}
+
+@keyframes data-group-2b {
+
+ 0%,
+ 14.99% {
+ color: black;
+ }
+
+ 15%,
+ 49.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 50%,
+ 53.99% {
+ color: var(--light-color);
+ }
+
+ 54%,
+ 67.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 68%,
+ 71.99% {
+ color: var(--light-color);
+ }
+
+ 72%,
+ 81.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 82%,
+ 100% {
+ color: var(--light-color);
+ }
+}
+
+@keyframes data-group-3 {
+
+ 0%,
+ 26.99% {
+ color: black;
+ }
+
+ 27%,
+ 40.99% {
+ color: var(--light-color);
+ }
+
+ 41%,
+ 53.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 54%,
+ 57.99% {
+ color: var(--light-color);
+ }
+
+ 58%,
+ 71.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 72%,
+ 75.99% {
+ color: var(--light-color);
+ }
+
+ 76%,
+ 100% {
+ color: var(--data-cascade-color);
+ }
+}
+
+.dc-row-1 {
+ animation: data-group-1 6000ms ease 200ms infinite;
+}
+
+.dc-row-2 {
+ animation: data-group-1a 6000ms ease 200ms infinite;
+}
+
+.dc-row-3 {
+ animation: data-group-2 6000ms ease 200ms infinite;
+}
+
+.dc-row-4 {
+ animation: data-group-2b 6000ms ease 200ms infinite;
+}
+
+.dc-row-5 {
+ animation: data-group-3 6000ms ease 200ms infinite;
+}
+
+.dc-row-6 {
+ animation: data-group-3 6000ms ease 200ms infinite;
+}
+
+.dc-row-7 {
+ animation: data-group-3 6000ms ease 200ms infinite;
+}
+
+/* Static data cascade */
+
+.data-cascade-wrapper#frozen .dc-row-1 {
+ animation: none;
+ color: var(--orange);
+}
+
+.data-cascade-wrapper#frozen .dc-row-2 {
+ animation: none;
+ color: var(--orange);
+}
+
+.data-cascade-wrapper#frozen .dc-row-3 {
+ animation: none;
+ color: var(--orange);
+}
+
+.data-cascade-wrapper#frozen .dc-row-4 {
+ animation: none;
+ color: var(--orange);
+}
+
+.data-cascade-wrapper#frozen .dc-row-5 {
+ animation: none;
+ color: var(--light-color);
+}
+
+.data-cascade-wrapper#frozen .dc-row-6 {
+ animation: none;
+ color: var(--light-color);
+}
+
+.data-cascade-wrapper#frozen .dc-row-7 {
+ animation: none;
+ color: var(--light-color);
+}
+
+/* Navigation & buttons */
+
+.lcars-button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ margin-block: 1rem;
+ width: 224px;
+ height: 80px;
+ padding-inline: 1.75rem;
+ padding-bottom: .675rem;
+ background-color: var(--button-color);
+ border-radius: 100vmax;
+ border-width: 0;
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.panel-1 a {
+ display: flex;
+ width: var(--lfw);
+ justify-content: flex-end;
+ align-items: flex-end;
+ background-color: var(--panel-1-color);
+ height: clamp(90px, 11vw, 160px);
+ padding: var(--left-frame-padding);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ text-decoration: none;
+ color: black;
+ transition: width 1s;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.panel-1-button {
+ display: flex;
+ width: var(--lfw);
+ justify-content: flex-end;
+ align-items: flex-end;
+ background-color: var(--panel-1-color);
+ min-height: clamp(90px, 11vw, 160px);
+ overflow: hidden;
+ padding: var(--left-frame-padding);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ text-decoration: none;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+nav {
+ display: flex;
+ flex-wrap: wrap;
+ align-self: center;
+ width: calc(var(--nav-width) + var(--nav-width) + 1rem);
+ justify-content: flex-end;
+ column-gap: .5rem;
+ row-gap: .65rem;
+}
+
+@media (max-width: 1500px) {
+ nav {
+ column-gap: .375rem;
+ row-gap: .5rem;
+ }
+}
+
+@media (max-width: 1440px) {
+ .data-cascade-button-group:has(.header-content) {
+ row-gap: 1rem;
+ }
+
+ .data-cascade-button-group:has(.header-content) > nav {
+ width: 100%;
+ }
+}
+
+nav a,
+nav button,
+.buttons button {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ align-items: flex-end;
+ width: var(--nav-width);
+ height: calc(var(--nav-width) / 2.8);
+ padding-inline: 1.5rem;
+ padding-bottom: .7rem;
+ border-radius: 100vmax;
+ background-color: var(--button-color);
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ text-transform: uppercase;
+ font-weight: bold;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+nav a:nth-child(1),
+nav button:nth-child(1) {
+ background-color: var(--nav-1-color);
+}
+
+nav a:nth-child(2),
+nav button:nth-child(2) {
+ background-color: var(--nav-2-color);
+}
+
+nav a:nth-child(3),
+nav button:nth-child(3) {
+ background-color: var(--nav-3-color);
+}
+
+nav a:nth-child(4),
+nav button:nth-child(4) {
+ background-color: var(--nav-4-color);
+}
+
+@media (max-width: 1300px) {
+ nav {
+ padding-left: .5rem;
+ gap: .5rem;
+ }
+
+ nav button {
+ padding-bottom: .5rem;
+ font-size: .875rem;
+ padding-inline: 1.25rem;
+ }
+}
+
+@media (max-width: 780px) {
+ nav {
+ flex: 1;
+ }
+
+ .data-cascade-button-group:has(.header-content) > *:nth-child(2) {
+ flex: none;
+ }
+}
+
+@media (max-width: 450px) {
+ nav a,
+ nav button {
+ height: 63px;
+ }
+}
+
+.buttons {
+ display: flex;
+ flex-wrap: wrap;
+ gap: .5rem;
+ margin-block: 2rem;
+}
+
+.justify-space-between {
+ justify-content: space-between;
+}
+
+.justify-center {
+ justify-content: center;
+}
+
+.justify-flex-end {
+ justify-content: flex-end;
+}
+
+.justify-space-around {
+ justify-content: space-around;
+}
+
+.justify-space-evenly {
+ justify-content: space-evenly;
+}
+
+.buttons a,
+.buttons button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ width: 224px;
+ height: 80px;
+ padding-inline: 1.75rem;
+ padding-bottom: .675rem;
+ background-color: var(--button-color);
+ border-radius: 100vmax;
+ border-width: 0;
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.sidebar-nav button,
+.sidebar-nav a,
+.sidebar-button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ min-height: 3.75rem;
+ width: var(--lfw);
+ background-color: var(--button-color-sidebar);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ padding: var(--left-frame-padding);
+ text-decoration: none;
+ text-align: right;
+ word-break: break-all;
+ text-transform: uppercase;
+ color: black;
+}
+
+@media (max-width: 1300px) {
+ .lcars-button,
+ .buttons a,
+ .buttons button {
+ width: 200px;
+ height: 74px;
+ }
+}
+
+.panel-button {
+ display: flex;
+ justify-content: flex-end;
+ width: var(--lfw);
+ border-radius: 0;
+ padding: var(--left-frame-padding);
+ border-bottom: var(--panel-border);
+}
+
+.pan-0 /* use this if you're not mimicking an established panel */ {
+ height: 18vh;
+ max-height: 240px;
+ background-color: var(--button-color-sidebar);
+}
+
+.pan-4 {
+ height: 22vh;
+ max-height: 300px;
+ background-color: var(--panel-4-color) !important;
+}
+
+.pan-5 {
+ height: 4.25rem;
+ background-color: var(--panel-5-color) !important;
+ align-items: center;
+}
+
+.pan-6 {
+ height: 29vh;
+ max-height: 360px;
+ background-color: var(--panel-6-color) !important;
+}
+
+.pan-7 {
+ height: 27vh;
+ max-height: 350px;
+ background-color: var(--panel-7-color) !important;
+}
+
+.pan-8 {
+ height: 15vh;
+ background-color: var(--panel-8-color) !important;
+}
+
+.pan-10 {
+ height: 30vh;
+ background-color: var(--panel-10-color) !important;
+}
+
+.text-bottom {
+ align-items: flex-end;
+}
+
+#topBtn {
+ display: none;
+ position: fixed;
+ bottom: 0;
+ z-index: 99;
+ border-radius: 0;
+ border-top: var(--panel-border);
+ border-right: none;
+ border-bottom: var(--panel-border);
+ border-left: none;
+ outline: none;
+ width: var(--lfw);
+ padding: var(--left-frame-padding);
+ padding-bottom: 10vh;
+ background-color: var(--panel-top-button-color);
+ text-align: right;
+ line-height: 1;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ cursor: pointer;
+}
+
+#topBtn:hover {
+ filter: brightness(115%);
+}
+
+#topBtn:active {
+ filter: brightness(80%);
+}
+
+@media (max-width: 525px) {
+ .sidebar-button,
+ .sidebar-nav a,
+ .sidebar-nav button,
+ .panel-button {
+ font-size: .75rem;
+ }
+
+ #topBtn {
+ padding-bottom: 6vh;
+ }
+}
+
+/* --- Horizontal bar panels & sidebar panels --- */
+
+.bar-panel {
+ display: flex;
+ height: var(--bar-height);
+}
+
+.first-bar-panel {
+ margin-top: clamp(15px, 2vw, 30px);
+}
+
+.bar-1,
+.bar-2,
+.bar-3,
+.bar-4,
+.bar-5,
+.bar-6,
+.bar-7,
+.bar-9,
+.bar-10 {
+ height: var(--bar-height);
+}
+
+.bar-1,
+.bar-2,
+.bar-3,
+.bar-4,
+.bar-6,
+.bar-7,
+.bar-8,
+.bar-9 {
+ border-right: var(--bar-border);
+}
+
+.bar-1 {
+ width: var(--bar-1-6-width);
+ background-color: var(--bar-1-color);
+}
+
+.bar-2 {
+ width: var(--bar-2-7-width);
+ background-color: var(--bar-2-color);
+}
+
+.bar-3 {
+ width: var(--bar-3-8-width);
+ background-color: var(--bar-3-color);
+}
+
+.bar-4 {
+ flex: 1;
+ background-color: var(--bar-4-color);
+}
+
+.bar-5 {
+ width: var(--bar-5-10-width);
+ background-color: var(--bar-5-color);
+}
+
+.bar-6 {
+ width: var(--bar-1-6-width);
+ background-color: var(--bar-6-color);
+}
+
+.bar-7 {
+ width: var(--bar-2-7-width);
+ background-color: var(--bar-7-color);
+}
+
+.bar-8 {
+ width: var(--bar-3-8-width);
+ height: 50%;
+ background-color: var(--bar-8-color);
+}
+
+.bar-9 {
+ flex: 1;
+ background-color: var(--bar-9-color);
+}
+
+.bar-10 {
+ width: var(--bar-5-10-width);
+ background-color: var(--bar-10-color);
+}
+
+#gap {
+ margin-top: var(--divider-height);
+}
+
+.panel-3,
+.panel-4,
+.panel-5,
+.panel-6,
+.panel-7,
+.panel-8 {
+ border-bottom: var(--panel-border);
+}
+
+.panel-2,
+.panel-3,
+.panel-4,
+.panel-6,
+.panel-7,
+.panel-8,
+.panel-10 {
+ padding: var(--left-frame-padding);
+}
+
+.panel-4 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 22vh;
+ max-height: 300px;
+ background-color: var(--panel-4-color);
+}
+
+.panel-5 {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ height: 4.25rem;
+ padding: var(--left-frame-padding);
+ background-color: var(--panel-5-color);
+}
+
+.panel-6 {
+ height: 29vh;
+ max-height: 360px;
+ background-color: var(--panel-6-color);
+}
+
+.panel-7 {
+ height: 27vh;
+ max-height: 350px;
+ background-color: var(--panel-7-color);
+}
+
+.panel-8 {
+ height: 15vh;
+ background-color: var(--panel-8-color);
+}
+
+/* Note: panel-9 height is fluid and governed by the amount of content on the page. Background color is inherited from :root --left-frame-color */
+
+.panel-9 {
+ min-height: 27vh;
+ padding: var(--left-frame-padding);
+}
+
+.panel-10 {
+ height: 30vh;
+ border-top: var(--panel-border);
+ background-color: var(--panel-10-color);
+}
+
+@media (max-width: 525px) {
+ .panel-4,
+ .panel-6,
+ .panel-7 {
+ height: 18vh;
+ }
+}
+
+.right-frame {
+ flex: 1;
+ position: relative;
+}
+
+.right-frame::before {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(to bottom right, var(--corner-color-bottom) 50%, black 50%);
+ position: absolute;
+ left: 0;
+ top: var(--bar-height);
+ z-index: -1;
+}
+
+.right-frame::after {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background-color: black;
+ border-radius: var(--radius-content-bottom);
+ position: absolute;
+ left: 0;
+ top: var(--bar-height);
+ z-index: -1;
+}
+
+main {
+ padding-top: 1.5rem;
+ padding-bottom: .5rem;
+ padding-left: clamp(20px, 3vw, 50px);
+}
+
+main > *:first-child,
+article > *:first-child {
+ margin-top: 0;
+}
+
+main:has(.floor-heading) > *:nth-child(2) {
+ margin-top: 0;
+}
+
+@media (max-width: 1400px) {
+ main {
+ padding-top: 1rem;
+ }
+}
+
+.flexbox {
+ display: flex;
+ gap: 2vw;
+ flex-wrap: wrap;
+}
+
+.col {
+ flex: 1 1 360px;
+}
+
+.col > *:first-child {
+ margin-top: 0;
+}
+
+h1, h2, h3, h4 {
+ margin-block: 1.75rem;
+ font-weight: normal;
+ line-height: 1.2;
+ text-transform: uppercase;
+ text-box: trim-both cap alphabetic;
+}
+
+h1 {
+ font-size: clamp(1.5rem, 1.25rem + 3.5vw, 4rem);
+ text-align: right;
+ color: var(--h1-color);
+}
+
+h2 {
+ font-size: clamp(1.4rem, 1.1rem + 2.25vw, 2.3rem);
+ color: var(--h2-color);
+}
+
+h3 {
+ font-size: clamp(1.15rem, 1.05rem + 1.25vw, 1.875rem);
+ color: var(--h3-color);
+}
+
+h4 {
+ font-size: clamp(1.025rem, 1rem + 1.125vw, 1.575rem);
+ color: var(--h4-color);
+}
+
+.floor-heading {
+ display: flex;
+ justify-content: flex-end;
+ width: 100%;
+ position: fixed;
+ left: 50%;
+ transform: translate(-50%, 0);
+ bottom: 10px;
+ z-index:4;
+}
+
+.floor-heading > * {
+ margin-block: 0;
+ width: fit-content;
+ padding-inline: .5rem;
+ padding-bottom: .5rem;
+ background-color: black;
+}
+
+p {
+ margin-block: 1.75rem;
+ text-box: trim-both cap alphabetic;
+}
+
+.caption {
+ margin-top: -1rem;
+ margin-bottom: 2.75rem;
+ text-align: center;
+ font-size: var(--sub-fonts);
+}
+
+.pics-right .caption,
+.pics-left .caption {
+ margin-top: 1rem;
+}
+
+.indent {
+ padding-left: 1.5rem;
+}
+
+.postmeta {
+ margin-block: 1.25rem;
+ text-align: right;
+ font-size: clamp(1.2rem, 0.88rem + 1.28vw, 1.6rem);
+ line-height: 1.2;
+ text-transform: uppercase;
+}
+
+article h1 {
+ margin-bottom: 0;
+}
+
+hr {
+ margin-block: 1.5rem;
+ height: 6px;
+ border: none;
+ background-color: var(--font-color);
+ border-radius: 3px;
+}
+
+blockquote {
+ margin-block: 1.75rem;
+ margin-left: 1.5rem;
+ padding-block: .25rem;
+ padding-left: 1.5rem;
+ position: relative;
+ text-box: trim-both cap alphabetic;
+}
+
+blockquote::before {
+ content: '';
+ display: block;
+ width: 10px;
+ height: 100%;
+ background-color: var(--font-color);
+ border-radius: 5px;
+ position: absolute;
+ left: 0;
+ top: 0;
+}
+
+blockquote > *:first-child {
+ margin-top: 0;
+}
+
+blockquote > * {
+ margin-bottom: 0;
+}
+
+iframe {
+ display: block;
+ width: 100%;
+ border: none;
+}
+
+.flush {
+ margin-top: -1rem;
+}
+
+.nomar {
+ margin-block: 0 !important;
+}
+
+.go-center {
+ text-align: center !important;
+}
+
+.go-right {
+ text-align: right !important;
+}
+
+.go-left {
+ text-align: left !important;
+}
+
+.go-big {
+ font-size: clamp(1.2rem, 1rem + 1vw, 1.275rem);
+}
+
+.uppercase {
+ text-transform: uppercase;
+}
+
+.strike {
+ text-decoration: line-through;
+ text-decoration-thickness: .15rem;
+}
+
+.now {
+ white-space: nowrap;
+}
+
+.big-sky {
+ margin-top: 5rem;
+}
+
+.smoke-glass {
+ opacity: .35;
+}
+
+strong {
+ font-weight: bold;
+}
+
+code {
+ font-family: monospace;
+ font-size: .9rem;
+ color: var(--code-color);
+}
+
+.code {
+ width: 100%;
+ min-height: 5rem;
+ padding-block: .5rem;
+ padding-inline: 1rem;
+ background-color: black;
+ border-color: var(--code-color);
+ tab-size: 4;
+ font-family: monospace;
+ font-size: .85rem;
+ color: var(--code-color);
+}
+
+::selection {
+ background-color: var(--orange);
+ color: black;
+}
+
+@keyframes blink {
+ 0% {opacity: 0}
+ 49%{opacity: 0}
+ 50% {opacity: 1}
+}
+
+.blink-slow {
+ animation: blink 3500ms infinite;
+}
+
+.blink {
+ animation: blink 2s infinite;
+}
+
+.blink-fast {
+ animation: blink 1s infinite;
+}
+
+@keyframes pulse {
+ 0% {filter: brightness(1.0)}
+ 50% {filter: brightness(.25)}
+}
+
+.pulse {
+ animation: pulse 2s infinite;
+}
+
+.pulse-rate-high {
+ animation: pulse 1s infinite;
+}
+
+.pulse-rate-low {
+ animation: pulse 3s infinite;
+}
+
+/* Accordion Dropdown */
+
+.accordion-wrapper {
+ display: block;
+ margin: 1.75rem auto;
+ width: 100%;
+}
+
+.limit-width {
+ max-width: 1240px;
+}
+
+.accordion {
+ display: flex;
+ align-items: center;
+ min-height: 3rem;
+ width: 100%;
+ padding-right: 2.75rem;
+ padding-left: 1rem;
+ border-radius: 100vmax;
+ background-color: var(--african-violet);
+ border-left: solid 3rem var(--moonlit-violet);
+ text-align: left;
+ font-size: 1.25rem;
+ color: black;
+ cursor: pointer;
+ transition: 0.4s ease;
+ position: relative;
+}
+
+.active, .accordion:hover {
+ background-color: var(--violet-creme);
+ border-left-color: var(--orange);
+ filter: none;
+}
+
+.accordion:before {
+ content: '';
+ display: block;
+ width: .5rem;
+ height: 4rem;
+ background-color: black;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.accordion:after {
+ display: block;
+ content: '\276F';
+ position: absolute;
+ right: 1.5rem;
+ top: 21%;
+ transform: rotate(90deg);
+ transition: 0.4s;
+ font-weight: bold;
+ color: black;
+}
+
+.active:after {
+ content: "\276F";
+ transform:rotate(-90deg);
+ transition: 0.4s ease;
+}
+
+.accordionContent {
+ padding-block: .5rem;
+ padding-inline: 3.5rem;
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 0.25s ease-out;
+}
+
+.accordionContent ul {
+ margin-left: 0;
+}
+
+@media (max-width: 525px) {
+ .accordion {
+ min-height: 2.5rem;
+ font-size: 1rem;
+ border-left-width: 2.5rem;
+ }
+}
+
+/* Images */
+
+.pics-right {
+ float: right;
+ margin-inline: 1rem;
+ margin-bottom: 2rem;
+ max-width: 500px;
+ clear: both;
+}
+
+.pics-left {
+ float: left;
+ margin-inline: 1rem;
+ margin-bottom: 2rem;
+ max-width: 500px;
+}
+
+@media (max-width: 1060px) {
+ .pics-right,
+ .pics-left {
+ float: none;
+ margin-inline: auto;
+ }
+
+ .pics-right img,
+ .pics-left img {
+ margin-inline: auto;
+ }
+}
+
+.pics {
+ margin-block: 2rem;
+ margin-inline: auto;
+}
+
+.border {
+ border: 2px solid var(--font-color);
+}
+
+/*
+
+Gallery
+
+NOTE: Gallery is experimental and still under development.
+
+*/
+
+.gallery {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: .75rem;
+ list-style: none;
+ text-align: center;
+ font-size: .875rem;
+}
+
+.gallery img {
+ align-self: flex-start;
+ border: 4px solid black;
+}
+
+.gallery li div {
+ padding-block: .6rem;
+ text-box: trim-both cap alphabetic;
+}
+
+.thumbs img {
+ width: 340px;
+}
+
+.gallery a {
+ align-self: flex-start;
+}
+
+.gallery a img:hover {
+ border-color: var(--font-color);
+}
+
+.lcars-list {
+ margin-block: 1.15rem;
+ margin-left: 2rem;
+ list-style: none;
+}
+
+.lcars-list li {
+ position: relative;
+ padding-block: .45rem;
+ padding-left: 2.25rem;
+ text-box: trim-both cap alphabetic;
+}
+
+.lcars-list li::before {
+ content: '';
+ display: block;
+ width: 34px;
+ height: 18px;
+ border-radius: 50%;
+ background-color: var(--font-color);
+ position: absolute;
+ top: .45rem;
+ left: 0;
+}
+
+@media (max-width: 650px) {
+ .lcars-list {
+ margin-left: .5rem;
+ }
+
+ .lcars-list li::before {
+ transform: scale(90%);
+ }
+}
+
+.lcars-bar {
+ margin-block: 1.75rem;
+ height: clamp(15px, 2vh, 25px);
+ background: transparent;
+ border-right: clamp(15px, 2vh, 25px) solid var(--lcars-bar-end-color);
+ border-left: clamp(15px, 2vh, 25px) solid var(--lcars-bar-start-color);
+ border-radius: 100vmax;
+ position: relative;
+}
+
+.lcars-bar::after {
+ content: '';
+ display: block;
+ height: clamp(15px, 2vh, 25px);
+ width: 100%;
+ background-color: var(--lcars-bar-color);
+ border-right: .8vh solid black;
+ border-left: .8vh solid black;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.lcars-text-bar {
+ display: flex;
+ position: relative;
+ height: clamp(16px, 4vh, 41px);
+ margin-block: 2.75rem;
+ overflow: visible;
+ border-radius: 100vmax;
+ background-color: var(--lcars-bar-color);
+ border-right: clamp(16px, 4vh, 43px) solid var(--lcars-bar-end-color);
+ border-left: clamp(16px, 4vh, 43px) solid var(--lcars-bar-start-color);
+}
+
+.the-end {
+ justify-content: flex-end;
+}
+
+.lcars-text-bar h2,
+.lcars-text-bar h3,
+.lcars-text-bar h4,
+.lcars-text-bar span {
+ margin-block: 0;
+ background-color: black;
+ height: clamp(20px, 5.5vh, 60px); /* Setting height is a Firefox fix */
+ overflow: visible;
+ border-top: 1px solid black;
+ padding-inline: 1vh;
+ font-size: clamp(17px, 4.5vh, 46px);
+ line-height: 1;
+ text-transform: uppercase;
+ color: var(--lcars-bar-text-color);
+ z-index: 1;
+ text-box: trim-both cap alphabetic;
+}
+
+.lcars-text-bar::before {
+ content: '';
+ background-color: black;
+ display: block;
+ width: 1vh;
+ height: 6vh;
+ position: absolute;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+}
+
+.lcars-text-bar::after {
+ content: '';
+ background-color: black;
+ display: block;
+ width: 1vh;
+ height: 6vh;
+ position: absolute;
+ top: 0;
+ right: 0;
+ overflow: hidden;
+}
+
+/* Image Frame */
+
+.image-frame {
+ display: block;
+ margin: 2.75rem auto;
+ width: fit-content;
+ background: linear-gradient(var(--primary-color) 56%, var(--secondary-color) 44%);
+ border-radius: 50px 25px 0 50px;
+ position: relative;
+}
+
+.image-frame::before {
+ content: '';
+ display: block;
+ width: 40px;
+ height: 40px;
+ background-color: black;
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+.image-frame::after {
+ content: '';
+ display: block;
+ border-top: var(--spacers) solid black;
+ border-bottom: var(--spacers) solid black;
+ width: 45px;
+ height: 80px;
+ background-color: var(--secondary-color);
+ position: absolute;
+ left: 0;
+ top: 56%;
+}
+
+.imgf-title {
+ display: flex;
+ justify-content: flex-end;
+ height: 40px;
+ border-right: 40px solid var(--secondary-color);
+ border-radius: 25px 100vh 100vh 0;
+ position: relative;
+ z-index: 1;
+ text-align: right;
+}
+
+.h4-wrapper {
+ width: fit-content;
+ height: var(--frame-height);
+ background-color: black;
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-title h4 {
+ margin-block: 0;
+ width: fit-content;
+ background-color: black;
+ padding-left: var(--spacers);
+ font-size: 2.05rem;
+ color: var(--title-color);
+ line-height: 40px;
+ text-transform: uppercase;
+}
+
+.imgf-image-body {
+ margin-left: 45px;
+ background-color: black;
+ width: fit-content;
+ padding: 1rem;
+ border-radius: 28px 0 0 28px;
+
+}
+
+.image-holder {
+ width: fit-content;
+ padding: 1rem;
+ border: 2px solid var(--image-border-color);
+ border-radius: 20px;
+}
+
+.imgf-base {
+ display: grid;
+ grid-template-columns: 20% 13% 35px 15% 1fr;
+ margin-left: 80px;
+ border-left: var(--spacers) solid black;
+
+}
+
+.imgf-block-1 {
+ height: var(--frame-height);
+ background-color: var(--accent-color);
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-block-2 {
+ height: var(--frame-height);
+ background-color: var(--secondary-color);
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-block-3 {
+ height: var(--frame-height);
+ background-color: black;
+ border-right: 10px solid var(--secondary-color);
+ border-left: 10px solid var(--accent-color);
+}
+
+.imgf-block-4 {
+ height: var(--frame-height);
+ background-color: var(--secondary-color);
+ border-left: var(--spacers) solid black;
+}
+
+.imgf-block-5 {
+ height: var(--frame-height);
+ background-color: black;
+}
+
+@media (max-width: 750px) {
+ .image-frame {
+ border-radius: 40px 25px 0 40px;
+ }
+
+ .image-frame::after {
+ width: 25px;
+ height: 60px;
+ top: 50%;
+ }
+
+ .imgf-title {
+ height: 25px;
+ border-right: 24px solid var(--secondary-color);
+ }
+
+ .h4-wrapper {
+ width: fit-content;
+ height: var(--frame-height);
+ background-color: black;
+ }
+
+ .imgf-title h4 {
+ font-size: 1.45rem;
+ }
+
+ .imgf-image-body {
+ margin-left: 25px;
+ padding: .75rem;
+ border-radius: 20px 0 0 20px;
+ }
+
+ .image-holder {
+ padding: .75rem;
+ border-radius: 10px;
+ }
+}
+
+/* color custom classes */
+
+.font-african-violet {
+ color: var(--african-violet) !important;
+}
+
+.button-african-violet,
+.background-african-violet,
+.bullet-african-violet {
+ background-color: var(--african-violet) !important;
+}
+
+.font-almond {
+ color: var(--almond) !important;
+}
+
+.button-almond,
+.background-almond,
+.bullet-almond::before {
+ background-color: var(--almond) !important;
+}
+
+.font-almond-creme {
+ color: var(--almond-creme) !important;
+}
+
+.button-almond-creme,
+.background-almond-creme,
+.bullet-almond-creme::before {
+ background-color: var(--almond-creme) !important;
+}
+
+.font-blue {
+ color: var(--blue) !important;
+}
+
+.button-blue,
+.background-blue,
+.bullet-blue::before {
+ background-color: var(--blue) !important;
+}
+
+.font-bluey {
+ color: var(--bluey) !important;
+}
+
+.button-bluey,
+.background-bluey,
+.bullet-bluey::before {
+ background-color: var(--bluey) !important;
+}
+
+.font-butterscotch {
+ color: var(--butterscotch) !important;
+}
+
+.button-butterscotch,
+.background-butterscotch,
+.bullet-butterscotch::before {
+ background-color: var(--butterscotch) !important;
+}
+
+.font-gold {
+ color: var(--gold) !important;
+}
+
+.button-gold,
+.background-gold,
+.bullet-gold::before {
+ background-color: var(--gold) !important;
+}
+
+.font-golden-orange {
+ color: var(--golden-orange) !important;
+}
+
+.button-golden-orange,
+.background-golden-orange,
+.bullet-golden-orange::before {
+ background-color: var(--golden-orange) !important;
+}
+
+.font-gray {
+ color: var(--gray) !important;
+}
+
+.button-gray,
+.background-gray,
+.bullet-gray::before {
+ background-color: var(--gray) !important;
+}
+
+.font-green {
+ color: var(--green) !important;
+}
+
+.button-green,
+.background-green,
+.bullet-green::before {
+ background-color: var(--green) !important;
+}
+
+.font-ice {
+ color: var(--ice) !important;
+}
+
+.button-ice,
+.background-ice,
+.bullet-ice::before {
+ background-color: var(--ice) !important;
+}
+
+.font-lilac {
+ color: var(--lilac) !important;
+}
+
+.button-lilac,
+.background-lilac,
+.bullet-lilac::before {
+ background-color: var(--lilac) !important;
+}
+
+.font-lima-bean {
+ color: var(--lima-bean) !important;
+}
+
+.button-lima-bean,
+.background-lima-bean,
+.bullet-lima-bean::before {
+ background-color: var(--lima-bean) !important;
+}
+
+.font-magenta {
+ color: var(--magenta) !important;
+}
+
+.button-magenta,
+.background-magenta,
+.bullet-magenta::before {
+ background-color: var(--magenta) !important;
+}
+
+.font-mars {
+ color: var(--mars) !important;
+}
+
+.button-mars,
+.background-mars,
+.bullet-mars::before {
+ background-color: var(--mars) !important;
+}
+
+.font-moonlit-violet {
+ color: var(--moonlit-violet) !important;
+}
+
+.button-moonlit-violet,
+.background-moonlit-violet,
+.bullet-moonlit-violet::before {
+ background-color: var(--moonlit-violet) !important;
+}
+
+.font-orange {
+ color: var(--orange) !important;
+}
+
+.button-orange,
+.background-orange,
+.bullet-orange::before {
+ background-color: var(--orange) !important;
+}
+
+.font-peach {
+ color: var(--peach) !important;
+}
+
+.button-peach,
+.background-peach,
+.bullet-peach::before {
+ background-color: var(--peach) !important;
+}
+
+.font-red {
+ color: var(--red) !important;
+}
+
+.button-red,
+.background-red,
+.bullet-red::before {
+ background-color: var(--red) !important;
+}
+
+.font-sky {
+ color: var(--sky) !important;
+}
+
+.button-sky,
+.background-sky,
+.bullet-sky::before {
+ background-color: var(--sky) !important;
+}
+
+.font-space-white {
+ color: var(--space-white) !important;
+}
+
+.button-space-white,
+.background-space-white,
+.bullet-space-white::before {
+ background-color: var(--space-white) !important;
+}
+
+.font-sunflower {
+ color: var(--sunflower) !important;
+}
+
+.button-sunflower,
+.background-sunflower,
+.bullet-sunflower::before {
+ background-color: var(--sunflower) !important;
+}
+
+.font-tomato {
+ color: var(--tomato) !important;
+}
+
+.button-tomato,
+.background-tomato,
+.bullet-tomato::before {
+ background-color: var(--tomato) !important;
+}
+
+.font-violet-creme {
+ color: var(--violet-creme) !important;
+}
+
+.button-violet-creme,
+.background-violet-creme,
+.bullet-violet-creme::before {
+ background-color: var(--violet-creme) !important;
+}
+
+/* Footer */
+
+footer {
+ margin-top: 2.5vw;
+ padding-bottom: 3rem;
+ padding-left: clamp(20px, 3vw, 50px);
+ font-size: .825rem;
+ --footer-bar-height: 1rem;
+}
+
+footer:has(.footer-frame) {
+ font-size: inherit;
+}
+
+.footer-frame {
+ display: grid;
+ grid-template-columns: 1fr var(--lfw);
+ align-items: center;
+ background: linear-gradient(var(--red) 50%, var(--bluey) 50%);
+ border-radius: 0 2rem 2rem 0;
+ padding-block: var(--footer-bar-height);
+ position: relative;
+}
+
+.footer-frame::before {
+ content: '';
+ display: block;
+ width: 20%;
+ height: calc(var(--footer-bar-height) / 2);
+ /*border-radius: 100vmax 0 0 100vmax;*/
+ position: absolute;
+ left: 0;
+ top: calc(var(--footer-bar-height) + .5rem);
+ background-color: var(--red);
+ z-index: 1;
+}
+
+.footer-frame::after {
+ content: '';
+ display: block;
+ width: 20%;
+ height: calc(var(--footer-bar-height) / 2);
+ /*border-radius: 100vmax 0 0 100vmax;*/
+ position: absolute;
+ left: 0;
+ bottom: var(--footer-bar-height);
+ background-color: var(--bluey);
+}
+
+.footer-frame-content {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background-color: black;
+ min-height: 250px;
+ border-radius: 0 1.5rem 1.5rem 0;
+ padding-block: 2.5rem;
+ padding-inline: 2rem;
+ position: relative;
+ text-align: center;
+}
+
+.footer-frame-content::before {
+ content: '';
+ display: block;
+ width: .25rem;
+ height: var(--footer-bar-height);
+ background-color: black;
+ position: absolute;
+ top: calc(var(--footer-bar-height) - var(--footer-bar-height) * 2);
+ left: 62%;
+}
+
+.footer-frame-content::after {
+ content: '';
+ display: block;
+ width: .25rem;
+ height: var(--footer-bar-height);
+ background-color: black;
+ position: absolute;
+ bottom: calc(var(--footer-bar-height) - var(--footer-bar-height) * 2);
+ left: 62%;
+}
+
+.footer-frame-content p {
+ margin-block: .8rem;
+}
+
+@media (max-width: 880px) {
+ .footer-frame-content .buttons {
+ justify-content: center;
+ }
+}
+
+@media (max-width: 600px) {
+ footer {
+ --footer-bar-height: .75rem;
+ }
+
+ .footer-frame-content {
+ padding-inline: .5rem;
+ }
+}
+
+.footer-frame-content *:first-child {
+ margin-top: 0;
+}
+
+.footer-frame-content *:last-child {
+ margin-bottom: 0;
+}
+
+.footer-frame-panel {
+ height: 50%;
+ min-height: 100px;
+ background-color: var(--butterscotch);
+ border-block: var(--bar-border);
+ padding: var(--left-frame-padding);
+ font-size: clamp(.875rem, 2vw, 1rem);
+ font-weight: bold;
+ color: black;
+}
+
+.headtrim {
+ height: 10px;
+ width: 100%;
+ background-color: black;
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 999;
+}
+
+.baseboard {
+ height: 10px;
+ width: 100%;
+ background-color: black;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ z-index: 999;
+}
+
+/* Grouped @media */
+
+@media (max-width: 525px) {
+ body {
+ padding: .25rem;
+ }
+
+ .baseboard,
+ .headtrim {
+ display: none;
+ }
+
+ .wrap {
+ padding-left: 0;
+ padding-right: .25rem;
+ }
+
+ .left-frame {
+ padding-top: 25px;
+ }
+
+ .floor-heading {
+ bottom: 0;
+ }
+
+ .hop {
+ display: none;
+ }
+
+ blockquote {
+ margin-left: .75rem;
+ }
+}
+
+@-moz-document url-prefix() {
+
+ main {
+ padding-top: clamp(1rem, 4vw, 30px);
+ }
+
+ h1, h2, h3, h4, p {
+ margin-block: 1.15rem;
+ }
+
+ .imgf-title h4 {
+ margin-top: -4px;
+ margin-bottom: 0;
+ }
+
+ .postmeta {
+ margin-top: .75rem;
+ }
+
+ .lcars-text-bar h2,
+ .lcars-text-bar h3,
+ .lcars-text-bar h4,
+ .lcars-text-bar span {
+ position: absolute;
+ top: -.7vh;
+ }
+
+ .lcars-list li::before {
+ top: .85rem;
+ }
+
+ .meta-data {
+ height: 1.3rem;
+ line-height: 1rem;
+ }
+}
\ No newline at end of file
diff --git a/lcars/frontend/assets/lcars.js b/lcars/frontend/assets/lcars.js
new file mode 100644
index 0000000..3c067eb
--- /dev/null
+++ b/lcars/frontend/assets/lcars.js
@@ -0,0 +1,50 @@
+document.addEventListener("touchstart", function() {},false);
+let mybutton = document.getElementById("topBtn");
+window.onscroll = function() {scrollFunction()};
+function scrollFunction() {
+ if (document.body.scrollTop > 200 || document.documentElement.scrollTop > 200) {
+ mybutton.style.display = "block";
+ } else {
+ mybutton.style.display = "none";
+ }
+}
+function topFunction() {
+ document.body.scrollTop = 0;
+ document.documentElement.scrollTop = 0;
+}
+function playSoundAndRedirect(audioId, url) {
+ var audio = document.getElementById(audioId);
+ audio.play();
+
+ audio.onended = function() {
+ window.location.href = url;
+ };
+}
+function goToAnchor(anchorId) {
+ window.location.hash = anchorId;
+}
+// Accordion drop-down
+var acc = document.getElementsByClassName("accordion");
+var i;
+
+for (i = 0; i < acc.length; i++) {
+ acc[i].addEventListener("click", function() {
+ this.classList.toggle("active");
+ var accordionContent = this.nextElementSibling;
+ if (accordionContent.style.maxHeight){
+ accordionContent.style.maxHeight = null;
+ } else {
+ accordionContent.style.maxHeight = accordionContent.scrollHeight + "px";
+ }
+ });
+}
+// LCARS keystroke sound (not to be used with hyperlinks)
+ const LCARSkeystroke = document.getElementById('LCARSkeystroke');
+ const allPlaySoundButtons = document.querySelectorAll('.playSoundButton');
+ allPlaySoundButtons.forEach(button => {
+ button.addEventListener('click', function() {
+ LCARSkeystroke.pause();
+ LCARSkeystroke.currentTime = 0; // Reset to the beginning of the sound
+ LCARSkeystroke.play();
+ });
+ });
\ No newline at end of file
diff --git a/lcars/frontend/assets/lower-decks-padd.css b/lcars/frontend/assets/lower-decks-padd.css
new file mode 100644
index 0000000..d914632
--- /dev/null
+++ b/lcars/frontend/assets/lower-decks-padd.css
@@ -0,0 +1,1881 @@
+@charset "utf-8";
+
+/*
+
+ CSS Document
+ LCARS Lower Decks PADD Theme
+ Version 24.2
+ By Jim Robertus www.thelcars.com
+ Modified: 2025 Aug 4
+
+*/
+
+:root {
+ font-size: 1.375rem;
+ color-scheme: dark;
+ --lfw: 240px;
+ --alpha-blue: #58e;
+ --arctic-ice: #6cf;
+ --arctic-snow: #9cf;
+ --radioactive: #8ff;
+ --beta-blue: #79d;
+ --night-cloud: #344470;
+ --night-rain: #455580;
+ --sunset-red: #f30;
+ --left-frame-top-color: var(--alpha-blue);
+ --left-frame-color: var(--alpha-blue); /* panel-6 inherits this color */
+ --left-frame-padding: .75rem;
+ --corner-color-top: var(--alpha-blue);
+ --corner-color-bottom: var(--alpha-blue);
+ --panel-1-color: var(--night-rain);
+ --panel-4-color: var(--beta-blue);
+ --panel-5-color: var(--night-rain);
+ --panel-7-color: var(--night-cloud);
+ --panel-top-button-color: var(--beta-blue);
+ --bar-height: 28px;
+ --bar-1-6-width: 10%;
+ --bar-2-7-width: 28%;
+ --bar-3-8-width: 7%;
+ --bar-5-10-width: 5%;
+ --bar-1-color: var(--alpha-blue);
+ --bar-2-color: var(--radioactive);
+ --bar-3-color: var(--beta-blue);
+ --bar-4-color: var(--alpha-blue);
+ --bar-5-color: var(--arctic-ice);
+ --bar-6-color: var(--alpha-blue);
+ --bar-7-color: var(--radioactive);
+ --bar-8-color: var(--beta-blue);
+ --bar-9-color: var(--alpha-blue);
+ --bar-10-color: var(--arctic-ice);
+/*
+ NOTE: --font-color also sets the following:
+ 1. horizontal line
color
+ 2. lcars-list bullet color
+ 3. blockquote border color
+ 4. images with the *border* class
+ 5. data-cascade text color
+*/
+ --font-color: var(--beta-blue);
+ --sub-fonts: .875rem;
+ --banner-color: var(--radioactive);
+ --h1-color: var(--arctic-ice);
+ --h2-color: var(--arctic-ice);
+ --h3-color: var(--arctic-ice);
+ --h4-color: var(--arctic-ice);
+ --light-color: #bdf;
+ --link-color: var(--beta-blue);
+ --code-color: var(--alpha-blue);
+ --nav-width: 240px;
+ --nav-1-color: var(--alpha-blue);
+ --nav-2-color: var(--beta-blue);
+ --nav-3-color: var(--arctic-ice);
+ --nav-4-color: var(--night-rain);
+ --button-color: var(--alpha-blue);
+ --button-color-sidebar: var(--alpha-blue);
+ --radius-top: 0 0 0 100px;
+ --radius-bottom: 100px 0 0 0;
+ --radius-content-top: 0 0 0 44px;
+ --radius-content-bottom: 44px 0 0 0;
+ --panel-border: .4rem solid #000;
+ --bar-border: .4rem solid #000;
+ --bar-cut-width: 27%;
+ --divider-height: .75rem;
+ --lcars-bar-color: var(--night-rain);
+ --lcars-bar-start-color: var(--beta-blue);
+ --lcars-bar-end-color: var(--beta-blue);
+ --lcars-bar-text-color: var(--beta-blue);
+
+/* Image Frame: */
+
+ --image-border-color: var(--beta-blue);
+ --primary-color: var(--night-rain);
+ --secondary-color: var(--beta-blue);
+ --accent-color: var(--arctic-snow);
+ --spacers: .65rem;
+ --frame-height: 40px;
+}
+
+@media (max-width: 1500px) {
+ :root {
+ --lfw: 200px;
+ --nav-width: 210px;
+ --bar-height: 20px;
+ --divider-height: .5rem;
+ }
+}
+
+@media (max-width: 1300px) {
+ :root {
+ font-size: 1.2rem;
+ --sub-fonts: .9rem;
+ --lfw: 180px;
+ --radius-top: 0 0 0 80px;
+ --radius-bottom: 80px 0 0 0;
+ --radius-content-top: 0 0 0 30px;
+ --radius-content-bottom: 30px 0 0 0;
+ --nav-width: 180px;
+ }
+}
+
+@media (max-width: 950px) {
+ :root {
+ --lfw: 150px;
+ }
+}
+
+@media (max-width: 750px) {
+ :root {
+ --lfw: 120px;
+ --radius-top: 0 0 0 60px;
+ --radius-bottom: 60px 0 0 0;
+ --radius-content-top: 0 0 0 24px;
+ --radius-content-bottom: 24px 0 0 0;
+ --bar-height: 16px;
+ --spacers: .5rem;
+ --frame-height: 25px;
+ }
+}
+
+@media (max-width: 525px) {
+ :root {
+ --lfw: 62px;
+ --left-frame-padding: .5rem;
+ --radius-top: 0 0 0 30px;
+ --radius-bottom: 30px 0 0 0;
+ --bar-height: 10px;
+ --panel-border: .25rem solid black;
+ --bar-border: .25rem solid black;
+ --divider-height: .345rem;
+ }
+}
+
+@media (max-width: 450px) {
+ :root {
+ --nav-width: 48%;
+ }
+}
+
+*, *:after, *:before {
+ box-sizing: border-box;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ font: inherit;
+}
+
+img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
+input, textarea, button, select {
+ font: inherit;
+}
+
+@font-face {
+ font-family: 'Antonio';
+ font-weight: 400;
+ src: url('Antonio-Regular.woff2') format('woff2'),
+ url('Antonio-Regular.woff') format('woff');
+}
+
+@font-face {
+ font-family: 'Antonio';
+ font-weight: 700;
+ src: url('Antonio-Bold.woff2') format('woff2'),
+ url('Antonio-Bold.woff') format('woff')
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ display: flex;
+ padding-top: 10px;
+ padding-left: 5px;
+ background-color: black;
+ font-family: 'Antonio', 'Arial Narrow', 'Avenir Next Condensed', sans-serif;
+ font-weight: 400;
+ line-height: 1.5;
+ color: var(--font-color);
+}
+
+a {
+ text-decoration: underline;
+ text-decoration-thickness: 2px;
+ text-underline-offset: .2rem;
+ color: var(--link-color);
+}
+
+a:hover {
+ filter: brightness(115%);
+ animation: none;
+}
+
+a:active {
+ filter: brightness(80%);
+ outline: none;
+}
+
+button {
+ border: none;
+ outline: none;
+ word-break: break-all;
+ color: black;
+ transition: width 1s;
+}
+
+button:hover {
+ cursor: pointer;
+ animation: none;
+ filter: brightness(115%);
+ color: black;
+}
+
+button:active {
+ filter: brightness(85%);
+}
+
+@keyframes colorchange {
+ 0% {color: var(--night-cloud)}
+ 25% {color: var(--night-cloud);}
+ 50% {color: var(--arctic-snow);}
+ 75% {color: var(--arctic-snow);}
+ 80% {color: var(--arctic-snow);}
+ 90% {color: var(--night-cloud);}
+ 100% {color: var(--night-cloud);}
+}
+
+.wrap-all {
+ width: 100%;
+}
+
+.wrap {
+ display: flex;
+ width: 100%;
+ padding-left: 5px;
+ padding-right: 15px;
+ overflow: hidden;
+}
+
+.scroll-top {
+ display: none;
+}
+
+.left-frame-top,
+.left-frame {
+ width: var(--lfw);
+ text-align: right;
+ font-size: clamp(.875rem, 2vw, 1rem);
+ line-height: 1.2;
+ font-weight: bold;
+ color: black;
+ transition: width 1s;
+}
+
+.left-frame-top {
+ background-color: var(--left-frame-top-color);
+ border-radius: var(--radius-top);
+}
+
+.left-frame-top a,
+.left-frame a {
+ text-decoration: none;
+ color: black;
+}
+
+.left-frame {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding-top: 14vh;
+ background-color: var(--left-frame-color);
+ border-radius: var(--radius-bottom);
+}
+
+.left-frame:has(.sidebar-nav) {
+ padding-top: 10vh;
+}
+
+.panel-2 {
+ padding-top: .75rem;
+ padding-right: .75rem;
+}
+
+.right-frame-top {
+ flex: 1;
+ position: relative;
+}
+
+.right-frame-top::before {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(to top right, var(--corner-color-top) 50%, #000 50%);
+ position: absolute;
+ left: 0;
+ bottom: var(--bar-height);
+ z-index: -1;
+}
+
+.right-frame-top::after {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background-color: black;
+ border-radius: var(--radius-content-top);
+ position: absolute;
+ left: 0;
+ bottom: var(--bar-height);
+ z-index: -1;
+}
+
+@media (max-width: 500px) {
+ .right-frame-top::before {
+ bottom: 10px;
+ }
+
+ .right-frame-top::after {
+ bottom: 10px;
+ }
+}
+
+.banner {
+ padding-bottom: 1rem;
+ padding-left: 5px;
+ text-align: right;
+ text-transform: uppercase;
+ line-height: 1.1;
+ font-size: clamp(1.5rem, 1.25rem + 3.5vw, 4rem);
+ color: var(--banner-color);
+}
+
+.banner a {
+ color: var(--banner-color);
+ text-decoration: none;
+}
+
+.data-cascade-button-group {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ padding-bottom: .75rem;
+}
+
+.data-cascade-button-group:has(.header-content) {
+ justify-content: flex-start;
+}
+
+@media (max-width: 1440px) {
+ .data-cascade-button-group:has(.header-content) {
+ row-gap: 1rem;
+ }
+
+ .data-cascade-button-group:has(.header-content) > nav {
+ width: 100%;
+ }
+}
+
+.header-content {
+ flex: 1;
+ min-height: 184px;
+ padding-right: 2rem;
+ padding-left: clamp(20px, 3vw, 50px);
+}
+
+.header-content > *:first-child {
+ margin-top: 0;
+}
+
+.header-content > *:last-child {
+ margin-bottom: 0;
+}
+
+/* Data Stream */
+
+.data-wrapper {
+ flex: 1;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ align-items: flex-end;
+ max-width: 100%;
+ height: 164px;
+ overflow: hidden;
+ padding-top: 10px;
+ padding-right: 2rem;
+ padding-left: clamp(20px, 3vw, 50px);
+ column-gap: 1rem;
+ font-size: 1.25rem;
+}
+
+.data-column {
+ display: grid;
+ grid-template-columns: 1fr;
+ line-height: 1;
+ --dc-row-height: 40px;
+}
+
+.data-bullet {
+ width: 38px;
+ height: 22px;
+ border-radius: 50%;
+ background-color: var(--font-color);
+}
+
+.dc-row-1,
+.dc-row-2,
+.dc-row-3,
+.dc-row-4 {
+ display: flex;
+ align-items: center;
+ min-height: var(--dc-row-height);
+}
+
+.dc-row-1:has(.data-bullet) {
+ padding-inline: 1rem;
+}
+
+.dc-row-2:has(.data-bullet) {
+ padding-inline: 1rem;
+}
+
+.dc-row-3:has(.data-bullet) {
+ padding-inline: 1rem;
+}
+
+.dc-row-4:has(.data-bullet) {
+ padding-inline: 1rem;
+}
+
+.darkspace {
+ min-width: 3rem;
+ text-align: center;
+ justify-content: center;
+}
+
+.darkfont {
+ color: black;
+}
+
+@media (max-width: 1300px) {
+ .data-wrapper {
+ height: 130px;
+ font-size: 1rem;
+ }
+
+ .data-column {
+ --dc-row-height: 30px;
+ }
+
+ .data-bullet {
+ transform: scale(.8);
+ }
+}
+
+@media (max-width: 1100px) {
+ .hide-data {
+ display: none;
+ }
+
+ .data-wrapper {
+ column-gap: .575rem;
+ padding-right: 1rem;
+ }
+}
+
+@media (max-width: 740px) {
+ .data-wrapper {
+ display: none;
+ }
+}
+
+/* Navigation & buttons */
+
+.lcars-button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ margin-block: 1rem;
+ width: 224px;
+ height: 80px;
+ padding-inline: 1.75rem;
+ padding-bottom: .675rem;
+ background-color: var(--button-color);
+ border-radius: 100vmax;
+ border-width: 0;
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.panel-1 a {
+ display: flex;
+ width: var(--lfw);
+ justify-content: flex-end;
+ align-items: flex-end;
+ background-color: var(--panel-1-color);
+ height: clamp(90px, 11vw, 160px);
+ padding: var(--left-frame-padding);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ text-decoration: none;
+ color: black;
+ transition: width 1s;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.panel-1-button {
+ display: flex;
+ width: var(--lfw);
+ justify-content: flex-end;
+ align-items: flex-end;
+ background-color: var(--panel-1-color);
+ min-height: clamp(90px, 11vw, 160px);
+ overflow: hidden;
+ padding: var(--left-frame-padding);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ text-decoration: none;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+nav {
+ display: flex;
+ flex-wrap: wrap;
+ align-self: center;
+ width: calc(var(--nav-width) + var(--nav-width) + 1rem);
+ justify-content: flex-end;
+ column-gap: .5rem;
+ row-gap: .65rem;
+}
+
+@media (max-width: 1500px) {
+ nav {
+ column-gap: .375rem;
+ row-gap: .5rem;
+ }
+}
+
+@media (max-width: 1440px) {
+ .data-cascade-button-group:has(.header-content) {
+ row-gap: 1rem;
+ }
+
+ .data-cascade-button-group:has(.header-content) > nav {
+ width: 100%;
+ }
+}
+
+nav a,
+nav button,
+.buttons button {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ align-items: flex-end;
+ width: var(--nav-width);
+ height: calc(var(--nav-width) / 2.8);
+ padding-inline: 1.5rem;
+ padding-bottom: .7rem;
+ border-radius: 100vmax;
+ background-color: var(--button-color);
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ text-transform: uppercase;
+ font-weight: bold;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+nav a:nth-child(1),
+nav button:nth-child(1) {
+ background-color: var(--nav-1-color);
+}
+
+nav a:nth-child(2),
+nav button:nth-child(2) {
+ background-color: var(--nav-2-color);
+}
+
+nav a:nth-child(3),
+nav button:nth-child(3) {
+ background-color: var(--nav-3-color);
+}
+
+nav a:nth-child(4),
+nav button:nth-child(4) {
+ background-color: var(--nav-4-color);
+}
+
+@media (max-width: 1300px) {
+ nav {
+ padding-left: .5rem;
+ gap: .5rem;
+ }
+
+ nav button {
+ padding-bottom: .5rem;
+ font-size: .875rem;
+ padding-inline: 1.25rem;
+ }
+}
+
+@media (max-width: 780px) {
+ nav {
+ flex: 1;
+ }
+
+ .data-cascade-button-group:has(.header-content) > *:nth-child(2) {
+ flex: none;
+ }
+}
+
+@media (max-width: 450px) {
+ nav a,
+ nav button {
+ height: 63px;
+ }
+}
+
+.buttons {
+ display: flex;
+ flex-wrap: wrap;
+ gap: .5rem;
+ margin-block: 2rem;
+}
+
+.justify-space-between {
+ justify-content: space-between;
+}
+
+.justify-center {
+ justify-content: center;
+}
+
+.justify-flex-end {
+ justify-content: flex-end;
+}
+
+.justify-space-around {
+ justify-content: space-around;
+}
+
+.justify-space-evenly {
+ justify-content: space-evenly;
+}
+
+.buttons a,
+.buttons button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ width: 224px;
+ height: 80px;
+ padding-inline: 1.75rem;
+ padding-bottom: .675rem;
+ background-color: var(--button-color);
+ border-radius: 100vmax;
+ border-width: 0;
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.sidebar-nav button,
+.sidebar-nav a,
+.sidebar-button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ min-height: 3.75rem;
+ width: var(--lfw);
+ background-color: var(--button-color-sidebar);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ padding: var(--left-frame-padding);
+ text-decoration: none;
+ text-align: right;
+ word-break: break-all;
+ text-transform: uppercase;
+ color: black;
+}
+
+@media (max-width: 1300px) {
+ .lcars-button,
+ .buttons a,
+ .buttons button {
+ width: 200px;
+ height: 74px;
+ }
+}
+
+.panel-button {
+ display: flex;
+ justify-content: flex-end;
+ width: var(--lfw);
+ border-radius: 0;
+ padding: var(--left-frame-padding);
+ border-bottom: var(--panel-border);
+}
+
+.pan-0 /* use this if you're not mimicking an established panel */ {
+ height: 18vh;
+ max-height: 240px;
+ background-color: var(--button-color-sidebar);
+}
+
+.pan-4 {
+ height: 14vh;
+ background-color: var(--panel-4-color) !important;
+}
+
+.pan-5 {
+ height: 30vh;
+ background-color: var(--panel-5-color) !important;
+}
+
+.pan-7 {
+ height: 30vh;
+ background-color: var(--panel-7-color) !important;
+}
+
+.text-bottom {
+ align-items: flex-end;
+}
+
+#topBtn {
+ display: none;
+ position: fixed;
+ bottom: 0;
+ z-index: 99;
+ border-radius: 0;
+ border-top: var(--panel-border);
+ border-right: none;
+ border-bottom: var(--panel-border);
+ border-left: none;
+ outline: none;
+ width: var(--lfw);
+ padding: var(--left-frame-padding);
+ padding-bottom: 10vh;
+ background-color: var(--panel-top-button-color);
+ text-align: right;
+ line-height: 1;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ cursor: pointer;
+}
+
+#topBtn:hover {
+ filter: brightness(115%);
+}
+
+#topBtn:active {
+ filter: brightness(80%);
+}
+
+@media (max-width: 525px) {
+ .sidebar-button,
+ .sidebar-nav a,
+ .sidebar-nav button,
+ .panel-button {
+ font-size: .75rem;
+ }
+
+ #topBtn {
+ padding-bottom: 6vh;
+ }
+}
+
+.bar-panel {
+ display: flex;
+ height: var(--bar-height);
+}
+
+.first-bar-panel {
+ margin-top: 2.5vh;
+ position: relative;
+}
+
+.bar-1,
+.bar-2,
+.bar-3,
+.bar-4,
+.bar-5,
+.bar-6,
+.bar-7,
+.bar-8,
+.bar-9,
+.bar-10 {
+ height: var(--bar-height);
+}
+
+.bar-1,
+.bar-2,
+.bar-3,
+.bar-4,
+.bar-6,
+.bar-7,
+.bar-8,
+.bar-9 {
+ border-right: var(--bar-border);
+}
+
+.divider {
+ display: flex;
+ height: var(--divider-height);
+ z-index: 1999;
+}
+
+.block-left {
+ width: var(--lfw);
+ height: var(--divider-height);
+}
+
+.block-right {
+ flex: 1;
+ height: var(--divider-height);
+ position: relative;
+}
+
+.block-row {
+ display: flex;
+ width: 100%;
+ height: var(--divider-height);
+ padding-right: .4rem;
+ padding-left: 1px;
+}
+
+.bar-11 {
+ height: var(--divider-height);
+ width: var(--bar-1-6-width);
+}
+
+.bar-12 {
+ height: var(--divider-height);
+ width: var(--bar-2-7-width);
+
+}
+
+.bar-13 {
+ height: var(--divider-height);
+ width: var(--bar-3-8-width);
+}
+.bar-14 {
+ flex: 1;
+ height: var(--divider-height);
+ z-index: 1999;
+}
+
+.bar-1 {
+ width: var(--bar-1-6-width);
+ background-color: var(--bar-1-color);
+}
+
+.bar-2 {
+ width: var(--bar-2-7-width);
+ background-color: var(--bar-2-color);
+}
+
+.bar-3 {
+ width: var(--bar-3-8-width);
+ background-color: var(--bar-3-color);
+}
+
+.bar-4 {
+ flex: 1;
+ position: relative;
+ background-color: var(--bar-4-color);
+}
+
+.bar-5 {
+ width: var(--bar-5-10-width);
+ background-color: var(--bar-5-color);
+}
+
+.bar-6 {
+ width: var(--bar-1-6-width);
+ background-color: var(--bar-6-color);
+}
+
+.bar-7 {
+ width: var(--bar-2-7-width);
+ background-color: var(--bar-7-color);
+}
+
+.bar-8 {
+ width: var(--bar-3-8-width);
+ background-color: var(--bar-8-color);
+}
+
+.bar-9 {
+ flex: 1;
+ position: relative;
+ background-color: var(--bar-9-color);
+}
+
+.bar-4::after {
+ content: "";
+ display: block;
+ width: var(--bar-cut-width);
+ height: 48%;
+ background-color: black;
+ border-radius: 0 8px 0 0;
+ position: absolute;
+ left: 0;
+ bottom: 0;
+}
+
+.bar-9::after {
+ content: "";
+ display: block;
+ width: var(--bar-cut-width);
+ height: 48%;
+ background-color: black;
+ border-radius: 0 0 8px 0;
+ position: absolute;
+ left: 0;
+ top: 0;
+}
+
+.bar-10 {
+ width: var(--bar-5-10-width);
+ background-color: var(--bar-10-color);
+}
+
+/* Bottom half */
+
+#gap {
+ margin-top: var(--divider-height);
+}
+
+.panel-3,
+.panel-4,
+.panel-5 {
+ border-bottom: var(--panel-border);
+}
+
+.panel-2,
+.panel-3,
+.panel-4,
+.panel-5,
+.panel-6,
+.panel-7 {
+ padding: var(--left-frame-padding);
+}
+
+.panel-4 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ min-height: 14vh;
+ background-color: var(--panel-4-color);
+}
+
+.panel-5 {
+ height: 30vh;
+ background-color: var(--panel-5-color);
+}
+
+/* Note: panel-6 height is fluid and governed by the amount of content on the page. Background color is inherited from :root --left-frame-color */
+
+.panel-6 {
+ min-height: 15vh;
+}
+
+.panel-7 {
+ height: 30vh;
+ border-top: var(--panel-border);
+ background-color: var(--panel-7-color);
+}
+
+.right-frame {
+ flex: 1;
+ position: relative;
+}
+
+.right-frame::before {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(to bottom right, var(--corner-color-bottom) 50%, #000 50%);
+ position: absolute;
+ left: 0;
+ top: var(--bar-height);
+ z-index: -1;
+}
+
+.right-frame::after {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background-color: black;
+ border-radius: var(--radius-content-bottom);
+ position: absolute;
+ left: 0;
+ top: var(--bar-height);
+ z-index: -1;
+}
+
+main {
+ padding-top: 1.5rem;
+ padding-bottom: .5rem;
+ padding-left: clamp(20px, 3vw, 50px);
+}
+
+main > *:first-child,
+article > *:first-child {
+ margin-top: 0;
+}
+
+main:has(.floor-heading) > *:nth-child(2) {
+ margin-top: 0;
+}
+
+.flexbox {
+ display: flex;
+ gap: 1.2rem;
+ flex-wrap: wrap;
+}
+
+.col {
+ flex: 1 1 360px;
+}
+
+.col > *:first-child {
+ margin-top: 0;
+}
+
+h1, h2, h3, h4 {
+ margin-block: 1.75rem;
+ font-weight: normal;
+ line-height: 1.2;
+ text-transform: uppercase;
+ text-box: trim-both cap alphabetic;
+}
+
+h1 {
+ font-size: clamp(1.5rem, 1.25rem + 3.5vw, 4rem);
+ text-align: right;
+ color: var(--h1-color);
+}
+
+h2 {
+ font-size: clamp(1.4rem, 1.1rem + 2.25vw, 2.3rem);
+ color: var(--h2-color);
+}
+
+h3 {
+ font-size: clamp(1.15rem, 1.05rem + 1.25vw, 1.875rem);
+ color: var(--h3-color);
+}
+
+h4 {
+ font-size: clamp(1.025rem, 1rem + 1.125vw, 1.575rem);
+ color: var(--h4-color);
+}
+
+.floor-heading {
+ display: flex;
+ justify-content: flex-end;
+ width: 100%;
+ position: fixed;
+ left: 50%;
+ transform: translate(-50%, 0);
+ bottom: 10px;
+ z-index:4;
+}
+
+.floor-heading > * {
+ margin-block: 0;
+ width: fit-content;
+ padding: .5rem;
+ background-color: black;
+}
+
+p {
+ margin-block: 1.75rem;
+ text-box: trim-both cap alphabetic;
+}
+
+.caption {
+ margin-top: -1rem;
+ text-align: center;
+ font-size: var(--sub-fonts);
+}
+
+.indent {
+ padding-left: 1.5rem;
+}
+
+.postmeta {
+ margin-block: 1.25rem;
+ text-align: right;
+ font-size: clamp(1.2rem, 0.88rem + 1.28vw, 1.6rem);
+ line-height: 1.2;
+ text-transform: uppercase;
+}
+
+article h1 {
+ margin-bottom: 0;
+}
+
+hr {
+ margin-block: 1.5rem;
+ height: 6px;
+ border: none;
+ background-color: var(--font-color);
+ border-radius: 3px;
+}
+
+blockquote {
+ margin-block: 1.75rem;
+ margin-left: 1.5rem;
+ padding-block: .25rem;
+ padding-left: 1.5rem;
+ position: relative;
+ text-box: trim-both cap alphabetic;
+}
+
+blockquote::before {
+ content: '';
+ display: block;
+ width: 10px;
+ height: 100%;
+ background-color: var(--font-color);
+ border-radius: 5px;
+ position: absolute;
+ left: 0;
+ top: 0;
+}
+
+blockquote > *:first-child {
+ margin-top: 0;
+}
+
+blockquote > * {
+ margin-bottom: 0;
+}
+
+.flush {
+ margin-top: -1rem;
+}
+
+.nomar {
+ margin-block: 0 !important;
+}
+
+.go-center {
+ text-align: center !important;
+}
+
+.go-right {
+ text-align: right !important;
+}
+
+.go-left {
+ text-align: left !important;
+}
+
+.go-big {
+ font-size: clamp(1.2rem, 1rem + 1vw, 1.275rem);
+}
+
+.uppercase {
+ text-transform: uppercase;
+}
+
+.strike {
+ text-decoration: line-through;
+ text-decoration-thickness: .15rem;
+}
+
+.now {
+ white-space: nowrap;
+}
+
+.big-sky {
+ margin-top: 5rem;
+}
+
+.smoke-glass {
+ opacity: .35;
+}
+
+strong {
+ font-weight: bold;
+}
+
+code {
+ font-family: monospace;
+ font-size: .9rem;
+ color: var(--code-color);
+}
+
+.code {
+ width: 100%;
+ min-height: 5rem;
+ padding-block: .5rem;
+ padding-inline: 1rem;
+ background-color: #232323;
+ border-color: #4c4c4c;
+ tab-size: 4;
+ font-family: monospace;
+ font-size: .85rem;
+ color: #dcdcdc;
+}
+
+@keyframes blink {
+ 0% {opacity: 0}
+ 49%{opacity: 0}
+ 50% {opacity: 1}
+}
+
+.blink-slow {
+ animation: blink 3500ms infinite;
+ animation-delay: 1s;
+}
+
+.blink {
+ animation: blink 2s infinite;
+ animation-delay: 1s;
+}
+
+.blink-fast {
+ animation: blink 1s infinite;
+ animation-delay: 1s;
+}
+
+@keyframes pulse {
+ 0% {filter: brightness(1.0)}
+ 50% {filter: brightness(.25)}
+}
+
+.pulse {
+ animation: pulse 2300ms infinite;
+}
+
+.pulse-rate-high {
+ animation: pulse 1s infinite;
+}
+
+.accordion-wrapper {
+ display: block;
+ margin: 1.75rem auto;
+ width: 100%;
+}
+
+.limit-width {
+ max-width: 1240px;
+}
+
+.accordion {
+ display: flex;
+ align-items: center;
+ min-height: 3rem;
+ width: 100%;
+ padding-right: 2.75rem;
+ padding-left: 1rem;
+ border-radius: 100vmax;
+ background-color: var(--alpha-blue);
+ border-left: solid 3rem var(--night-rain);
+ text-align: left;
+ font-size: 1.25rem;
+ color: black;
+ cursor: pointer;
+ transition: 0.4s ease;
+ position: relative;
+}
+
+.active, .accordion:hover {
+ background-color: var(--beta-blue);
+ border-left-color: var(--radioactive);
+ filter: none;
+}
+
+.accordion:before {
+ content: '';
+ display: block;
+ width: .5rem;
+ height: 4rem;
+ background-color: black;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.accordion:after {
+ display: block;
+ content: '\276F';
+ position: absolute;
+ right: 1.5rem;
+ top: 21%;
+ transform: rotate(90deg);
+ transition: 0.4s;
+ font-weight: bold;
+ color: black;
+}
+
+.active:after {
+ content: "\276F";
+ transform:rotate(-90deg);
+ transition: 0.4s ease;
+}
+
+.accordionContent {
+ padding-block: .5rem;
+ padding-inline: 3.5rem;
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 0.25s ease-out;
+}
+
+.accordionContent ul {
+ margin-left: 0;
+}
+
+@media (max-width: 525px) {
+ .accordion {
+ min-height: 2.5rem;
+ font-size: 1rem;
+ border-left-width: 2.5rem;
+ }
+}
+
+/* Images */
+
+.pics-right {
+ float: right;
+ margin-inline: 1rem;
+ margin-bottom: 2rem;
+ max-width: 500px;
+}
+
+.pics-left {
+ float: left;
+ margin-inline: 1rem;
+ margin-bottom: 2rem;
+ max-width: 500px;
+}
+
+@media (max-width: 1060px) {
+ .pics-right,
+ .pics-left {
+ float: none;
+ margin-inline: auto;
+ }
+
+ .pics-right img,
+ .pics-left img {
+ margin-inline: auto;
+ }
+}
+
+.pics {
+ margin-block: 2rem;
+ margin-inline: auto;
+}
+
+.border {
+ border: 2px solid var(--font-color);
+}
+
+.lcars-list {
+ margin-block: 1.15rem;
+ margin-left: 2rem;
+ list-style: none;
+}
+
+.lcars-list li {
+ position: relative;
+ padding-block: .45rem;
+ padding-left: 2.25rem;
+ text-box: trim-both cap alphabetic;
+}
+
+.lcars-list li::before {
+ content: '';
+ display: block;
+ width: 34px;
+ height: 18px;
+ border-radius: 50%;
+ background-color: var(--font-color);
+ position: absolute;
+ top: .45rem;
+ left: 0;
+}
+
+@media (max-width: 650px) {
+
+ .lcars-list {
+ margin-left: .5rem;
+ }
+
+ .lcars-list li::before {
+ transform: scale(90%);
+ }
+}
+
+.lcars-bar {
+ margin-block: 1.75rem;
+ height: clamp(15px, 2vh, 25px);
+ background: transparent;
+ border-right: clamp(15px, 2vh, 25px) solid var(--lcars-bar-end-color);
+ border-left: clamp(15px, 2vh, 25px) solid var(--lcars-bar-start-color);
+ border-radius: 100vmax;
+ position: relative;
+}
+
+.lcars-bar::after {
+ content: '';
+ display: block;
+ height: clamp(15px, 2vh, 25px);
+ width: 100%;
+ background-color: var(--lcars-bar-color);
+ border-right: .8vh solid black;
+ border-left: .8vh solid black;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.lcars-text-bar {
+ display: flex;
+ position: relative;
+ height: clamp(16px, 4vh, 41px);
+ margin-block: 2.75rem;
+ overflow: visible;
+ border-radius: 100vmax;
+ background-color: var(--lcars-bar-color);
+ border-right: clamp(16px, 4vh, 43px) solid var(--lcars-bar-end-color);
+ border-left: clamp(16px, 4vh, 43px) solid var(--lcars-bar-start-color);
+}
+
+.the-end {
+ justify-content: flex-end;
+}
+
+.lcars-text-bar h2,
+.lcars-text-bar h3,
+.lcars-text-bar h4,
+.lcars-text-bar span {
+ margin-block: 0;
+ background-color: black;
+ height: clamp(20px, 5.5vh, 60px); /* Setting height is a Firefox fix */
+ overflow: visible;
+ border-top: 1px solid black;
+ padding-inline: 1vh;
+ font-size: clamp(17px, 4.5vh, 46px);
+ line-height: 1;
+ text-transform: uppercase;
+ color: var(--lcars-bar-text-color);
+ z-index: 1;
+ text-box: trim-both cap alphabetic;
+}
+
+.lcars-text-bar::before {
+ content: '';
+ background-color: black;
+ display: block;
+ width: 1vh;
+ height: 6vh;
+ position: absolute;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+}
+
+.lcars-text-bar::after {
+ content: '';
+ background-color: black;
+ display: block;
+ width: 1vh;
+ height: 6vh;
+ position: absolute;
+ top: 0;
+ right: 0;
+ overflow: hidden;
+}
+
+/* LCARS Image Frame */
+
+.image-frame {
+ margin: 2.75rem auto;
+ width: fit-content;
+ background: linear-gradient(var(--primary-color) 56%, var(--secondary-color) 44%);
+ border-radius: 50px 25px 0 50px;
+ position: relative;
+}
+
+.image-frame::before {
+ content: '';
+ display: block;
+ width: 40px;
+ height: 40px;
+ background-color: black;
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+.image-frame::after {
+ content: '';
+ display: block;
+ border-top: var(--spacers) solid black;
+ border-bottom: var(--spacers) solid black;
+ width: 45px;
+ height: 80px;
+ background-color: var(--secondary-color);
+ position: absolute;
+ left: 0;
+ top: 56%;
+}
+
+.imgf-title {
+ display: flex;
+ justify-content: flex-end;
+ height: 40px;
+ border-right: 40px solid var(--secondary-color);
+ border-radius: 25px 100vh 100vh 0;
+ position: relative;
+ z-index: 1;
+ text-align: right;
+}
+
+.h4-wrapper {
+ width: fit-content;
+ height: var(--frame-height);
+ background-color: black;
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-title h4 {
+ margin-block: 0;
+ width: fit-content;
+ background-color: black;
+ padding-left: var(--spacers);
+ font-size: 2.05rem;
+ color: var(--title-color);
+ line-height: 40px;
+ text-transform: uppercase;
+}
+
+.imgf-image-body {
+ margin-left: 45px;
+ background-color: black;
+ width: fit-content;
+ padding: 1rem;
+ border-radius: 28px 0 0 28px;
+}
+
+.image-holder {
+ width: fit-content;
+ padding: 1rem;
+ border: 2px solid var(--image-border-color);
+ border-radius: 20px;
+}
+
+.imgf-base {
+ display: grid;
+ grid-template-columns: 20% 13% 35px 15% 1fr;
+ margin-left: 80px;
+ border-left: var(--spacers) solid black;
+}
+
+.imgf-block-1 {
+ height: var(--frame-height);
+ background-color: var(--accent-color);
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-block-2 {
+ height: var(--frame-height);
+ background-color: var(--secondary-color);
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-block-3 {
+ height: var(--frame-height);
+ background-color: black;
+ border-right: 10px solid var(--secondary-color);
+ border-left: 10px solid var(--accent-color);
+}
+
+.imgf-block-4 {
+ height: var(--frame-height);
+ background-color: var(--secondary-color);
+ border-left: var(--spacers) solid black;
+}
+
+.imgf-block-5 {
+ height: var(--frame-height);
+ background-color: black;
+}
+
+@media (max-width: 750px) {
+
+ .image-frame {
+ border-radius: 40px 25px 0 40px;
+ }
+
+ .image-frame::after {
+ width: 25px;
+ height: 60px;
+ top: 50%;
+ }
+
+ .imgf-title {
+ height: 25px;
+ border-right: 24px solid var(--secondary-color);
+ }
+
+ .h4-wrapper {
+ width: fit-content;
+ height: var(--frame-height);
+ background-color: black;
+ }
+
+ .imgf-title h4 {
+ font-size: 1.45rem;
+ }
+
+ .imgf-image-body {
+ margin-left: 25px;
+ padding: .75rem;
+ border-radius: 20px 0 0 20px;
+ }
+
+ .image-holder {
+ padding: .75rem;
+ border-radius: 10px;
+ }
+}
+
+/* Color Styles */
+
+.font-alpha-blue {
+ color: var(--alpha-blue) !important;
+}
+
+.button-alpha-blue,
+.background-alpha-blue,
+.bullet-alpha-blue::before {
+ background-color: var(--alpha-blue) !important;
+}
+
+.font-arctic-ice {
+ color: var(--arctic-ice) !important;
+}
+
+.button-arctic-ice,
+.background-arctic-ice,
+.bullet-arctic-ice::before {
+ background-color: var(--arctic-ice) !important;
+}
+
+.font-arctic-snow {
+ color: var(--arctic-snow) !important;
+}
+
+.button-arctic-snow,
+.background-snow,
+.bullet-arctic-snow::before {
+ background-color: var(--arctic-snow) !important;
+}
+
+.font-radioactive {
+ color: var(--radioactive) !important;
+}
+
+.button-radioactive,
+.background-radioactive,
+.bullet-radioactive::before {
+ background-color: var(--radioactive) !important;
+}
+
+.font-beta-blue {
+ color: var(--beta-blue) !important;
+}
+
+.button-beta-blue,
+.background-beta-blue,
+.bullet-beta-blue::before {
+ background-color: var(--beta-blue) !important;
+}
+
+.font-night-cloud {
+ color: var(--night-cloud) !important;
+}
+
+.button-night-cloud,
+.background-night-cloud,
+.bullet-night-cloud::before {
+ background-color: var(--night-cloud) !important;
+}
+
+.font-night-rain {
+ color: var(--night-rain) !important;
+}
+
+.button-night-rain,
+.background-night-rain,
+.bullet-night-rain::before {
+ background-color: var(--night-rain) !important;
+}
+
+.font-sunset-red {
+ color: var(--sunset-red) !important;
+}
+
+.button-sunset-red,
+.background-sunset-red,
+.bullet-sunset-red::before {
+ background-color: var(--sunset-red) !important;
+}
+
+/* Footer */
+
+footer {
+ margin-top: 2.5vw;
+ padding-bottom: 3rem;
+ padding-left: clamp(20px, 3vw, 50px);
+ font-size: .875rem;
+}
+
+.headtrim {
+ height: 10px;
+ width: 100%;
+ background-color: black;
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 999;
+}
+
+.baseboard {
+ height: 10px;
+ width: 100%;
+ background-color: black;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ z-index: 999;
+}
+
+/* Grouped @media */
+
+@media (max-width: 650px) {
+
+ .first-bar-panel {
+ margin-top: .75rem;
+ }
+
+ .bar-4::before {
+ height: 5px;
+ top: -12px;
+ }
+
+ .bar-9::before {
+ height: 5px;
+ bottom: -12px;
+ }
+}
+
+@media (max-width: 525px) {
+
+ body {
+ padding: .25rem;
+ }
+
+ .baseboard {
+ display: none;
+ }
+
+ .wrap {
+ padding-left: 0;
+ padding-right: .25rem;
+ }
+
+ .left-frame {
+ padding-top: 8vh;
+ }
+
+ .divider {
+ padding-right: 10px;
+ }
+
+ .block-row {
+ padding-right: 0;
+ padding-left: .25rem;
+ }
+
+ .floor-heading {
+ bottom: 0;
+ }
+
+ .hop {
+ display: none;
+ }
+
+ blockquote {
+ margin-left: .75rem;
+ }
+}
+
+@-moz-document url-prefix() {
+
+ main {
+ padding-top: clamp(1rem, 4vw, 30px);
+ }
+
+ h1, h2, h3, h4, p {
+ margin-block: 1.15rem;
+ }
+
+ .postmeta {
+ margin-top: .75rem;
+ }
+
+ .lcars-text-bar h2,
+ .lcars-text-bar h3,
+ .lcars-text-bar h4,
+ .lcars-text-bar span {
+ position: absolute;
+ top: -.7vh;
+ }
+}
\ No newline at end of file
diff --git a/lcars/frontend/assets/lower-decks.css b/lcars/frontend/assets/lower-decks.css
new file mode 100644
index 0000000..32c9827
--- /dev/null
+++ b/lcars/frontend/assets/lower-decks.css
@@ -0,0 +1,1856 @@
+@charset "utf-8";
+
+/*
+
+ CSS Document
+ LCARS Lower Decks Theme
+ Version 24.2
+ By Jim Robertus www.thelcars.com
+ Modified: 2025 Jul 27
+
+*/
+
+:root {
+ font-size: 1.375rem;
+ color-scheme: dark;
+ --lfw: 240px;
+ --butter: #fec;
+ --daybreak: #f91;
+ --harvestgold: #fa4;
+ --honey: #fc9;
+ --october-sunset: #f40;
+ --orange: #f70;
+ --rich-pumpkin: #c50;
+ --left-frame-top-color: var(--october-sunset);
+ --left-frame-color: var(--october-sunset); /* panel-6 inherits this color */
+ --left-frame-padding: .75rem;
+ --corner-color-top: var(--october-sunset);
+ --corner-color-bottom: var(--october-sunset);
+ --panel-1-color: var(--orange);
+ --panel-4-color: var(--orange);
+ --panel-5-color: var(--harvestgold);
+ --panel-7-color: var(--harvestgold);
+ --panel-top-button-color: var(--honey);
+ --bar-height: 28px;
+ --bar-1-6-width: 10%;
+ --bar-2-7-width: 12%;
+ --bar-3-8-width: 17%;
+ --bar-5-10-width: 5%;
+ --bar-1-color: var(--october-sunset);
+ --bar-2-color: var(--orange);
+ --bar-3-color: var(--honey);
+ --bar-4-color: var(--harvestgold);
+ --bar-5-color: var(--october-sunset);
+ --bar-6-color: var(--october-sunset);
+ --bar-7-color: var(--orange);
+ --bar-8-color: var(--honey);
+ --bar-9-color: var(--harvestgold);
+ --bar-10-color: var(--october-sunset);
+/*
+ NOTE: --font-color also sets the following:
+ 1. horizontal line
color
+ 2. lcars-list bullet color
+ 3. blockquote border color
+ 4. images with the *border* class border color
+*/
+ --font-color: var(--daybreak);
+ --sub-fonts: .875rem;
+ --banner-color: var(--honey);
+ --data-cascade-color: var(--orange);
+ --light-color: white;
+ --h1-color: var(--honey);
+ --h2-color: var(--honey);
+ --h3-color: var(--honey);
+ --h4-color: var(--honey);
+ --light-color: var(--butter);
+ --link-color: var(--october-sunset);
+ --code-color: var(--october-sunset);
+ --nav-width: 240px;
+ --nav-1-color: var(--honey);
+ --nav-2-color: var(--harvestgold);
+ --nav-3-color: var(--orange);
+ --nav-4-color: var(--october-sunset);
+ --button-color: var(--harvestgold);
+ --button-color-sidebar: var(--october-sunset);
+ --radius-top: 0 0 0 100px;
+ --radius-bottom: 100px 0 0 0;
+ --radius-content-top: 0 0 0 44px;
+ --radius-content-bottom: 44px 0 0 0;
+ --panel-border: .4rem solid black;
+ --bar-border: .4rem solid black;
+ --bar-cut-width: 32%;
+ --bar-cut-out-width: 40%;
+ --bar-cut-out-top-color: var(--october-sunset);
+ --bar-cut-out-bottom-color: var(--october-sunset);
+ --divider-height: .75rem;
+ --lcars-bar-color: var(--october-sunset);
+ --lcars-bar-start-color: var(--harvestgold);
+ --lcars-bar-end-color: var(--harvestgold);
+ --lcars-bar-text-color: var(--harvestgold);
+
+/* Image Frame */
+
+ --image-border-color: var(--honey);
+ --primary-color: var(--orange);
+ --secondary-color: var(--harvestgold);
+ --accent-color: var(--honey);
+ --spacers: .65rem;
+ --frame-height: 40px;
+}
+
+@media (max-width: 1500px) {
+ :root {
+ --lfw: 200px;
+ --nav-width: 210px;
+ --bar-height: 20px;
+ --divider-height: .5rem;
+ }
+}
+
+@media (max-width: 1300px) {
+ :root {
+ font-size: 1.2rem;
+ --sub-fonts: .9rem;
+ --lfw: 180px;
+ --radius-top: 0 0 0 80px;
+ --radius-bottom: 80px 0 0 0;
+ --radius-content-top: 0 0 0 30px;
+ --radius-content-bottom: 30px 0 0 0;
+ --nav-width: 180px;
+ }
+}
+
+@media (max-width: 950px) {
+ :root {
+ --lfw: 150px;
+ }
+}
+
+@media (max-width: 750px) {
+ :root {
+ --lfw: 120px;
+ --radius-top: 0 0 0 60px;
+ --radius-bottom: 60px 0 0 0;
+ --radius-content-top: 0 0 0 24px;
+ --radius-content-bottom: 24px 0 0 0;
+ --bar-height: 16px;
+ --spacers: .5rem;
+ --frame-height: 25px;
+ }
+}
+
+@media (max-width: 525px) {
+ :root {
+ --lfw: 62px;
+ --left-frame-padding: .5rem;
+ --radius-top: 0 0 0 30px;
+ --radius-bottom: 30px 0 0 0;
+ --bar-height: 10px;
+ --panel-border: .25rem solid black;
+ --bar-border: .25rem solid black;
+ --divider-height: .345rem;
+ }
+}
+
+@media (max-width: 450px) {
+ :root {
+ --nav-width: 48%;
+ }
+}
+
+*, *:after, *:before {
+ box-sizing: border-box;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ font: inherit;
+}
+
+img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
+input, textarea, button, select {
+ font: inherit;
+}
+
+@font-face {
+ font-family: 'Antonio';
+ font-weight: 400;
+ src: url('Antonio-Regular.woff2') format('woff2'),
+ url('Antonio-Regular.woff') format('woff');
+}
+
+@font-face {
+ font-family: 'Antonio';
+ font-weight: 700;
+ src: url('Antonio-Bold.woff2') format('woff2'),
+ url('Antonio-Bold.woff') format('woff')
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ display: flex;
+ padding-top: 10px;
+ padding-left: 5px;
+ background-color: black;
+ font-family: 'Antonio', 'Arial Narrow', 'Avenir Next Condensed', sans-serif;
+ font-weight: 400;
+ line-height: 1.5;
+ color: var(--font-color);
+}
+
+a {
+ text-decoration: underline;
+ text-decoration-thickness: 2px;
+ text-underline-offset: .2rem;
+ color: var(--link-color);
+}
+
+a:hover {
+ filter: brightness(115%);
+ animation: none;
+}
+
+a:active {
+ filter: brightness(80%);
+ outline: none;
+}
+
+button {
+ border: none;
+ outline: none;
+ color: black;
+ transition: width 1s;
+}
+
+button:hover {
+ cursor: pointer;
+ animation: none;
+ filter: brightness(115%);
+ color: black;
+}
+
+button:active {
+ filter: brightness(85%);
+}
+
+.wrap-all {
+ width: 100%;
+}
+
+.wrap {
+ display: flex;
+ margin-inline: auto;
+ padding-left: 5px;
+ padding-right: 15px;
+ overflow: hidden;
+}
+
+.divider {
+ display: flex;
+ width: 100%;
+ flex-wrap: nowrap;
+ height: var(--divider-height);
+}
+
+.scroll-top {
+ display: none;
+}
+
+.left-frame-top,
+.left-frame {
+ width: var(--lfw);
+ text-align: right;
+ font-size: clamp(.875rem, 2vw, 1rem);
+ line-height: 1.2;
+ font-weight: bold;
+ color: black;
+ transition: width 1s;
+}
+
+.left-frame-top {
+ background-color: var(--left-frame-top-color);
+ border-radius: var(--radius-top);
+}
+
+.left-frame-top a,
+.left-frame a {
+ text-decoration: none;
+ color: black;
+}
+
+.left-frame {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding-top: 19vh;
+ background-color: var(--left-frame-color);
+ border-radius: var(--radius-bottom);
+}
+
+.left-frame:has(.sidebar-nav) {
+ padding-top: 10vh;
+}
+
+.left-frame:has(button) {
+ padding-top: 10vh;
+}
+
+@keyframes panel-1-color-change {
+ 0%,
+ 49.99% {
+ background-color: var(--orange);
+ }
+ 50%,
+ 100% {
+ background-color: var(--butter);
+ }
+}
+
+.right-frame-top {
+ flex: 1;
+ align-content: flex-end;
+ position: relative;
+}
+
+.right-frame-top::before {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(to top right, var(--corner-color-top) 50%, black 50%);
+ position: absolute;
+ left: 0;
+ bottom: var(--bar-height);
+ z-index: -1;
+}
+
+.right-frame-top::after {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background-color: black;
+ border-radius: var(--radius-content-top);
+ position: absolute;
+ left: 0;
+ bottom: var(--bar-height);
+ z-index: -1;
+}
+
+.banner {
+ padding-bottom: 1rem;
+ padding-left: 5px;
+ text-align: right;
+ text-transform: uppercase;
+ line-height: 1.1;
+ font-size: clamp(1.25rem, 0.75rem + 4vw, 3.75rem);
+ color: var(--banner-color);
+}
+
+.banner a {
+ color: var(--banner-color);
+ text-decoration: none;
+}
+
+.data-cascade-button-group {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ padding-bottom: .75rem;
+}
+
+.data-cascade-button-group:has(.header-content) {
+ justify-content: flex-start;
+}
+
+@media (max-width: 1440px) {
+ .data-cascade-button-group:has(.header-content) {
+ row-gap: 1rem;
+ }
+
+ .data-cascade-button-group:has(.header-content) > nav {
+ width: 100%;
+ }
+}
+
+.header-content {
+ flex: 1;
+ min-height: 184px;
+ padding-right: 2rem;
+ padding-left: clamp(20px, 3vw, 50px);
+}
+
+.header-content > *:first-child {
+ margin-top: 0;
+}
+
+.header-content > *:last-child {
+ margin-bottom: 0;
+}
+
+/* Data Stream */
+
+.data-wrapper {
+ flex: 1;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ align-items: flex-end;
+ max-width: 100%;
+ height: 184px;
+ overflow: hidden;
+ padding-top: 10px;
+ padding-right: 2rem;
+ padding-left: clamp(20px, 3vw, 50px);
+ column-gap: .5rem;
+ font-size: 1.25rem;
+}
+
+.data-column {
+ display: grid;
+ grid-template-columns: 1fr;
+ text-align: right;
+ line-height: 1;
+ --dc-row-height: 45px;
+}
+
+@media (max-width: 1300px) {
+ .data-wrapper {
+ height: 150px;
+ font-size: 1rem;
+ }
+
+ .data-column {
+ --dc-row-height: 35px;
+ }
+
+}
+
+@media (max-width: 740px) {
+ .data-wrapper {
+ display: none;
+ }
+}
+
+.dc-row-1 {
+ min-height: var(--dc-row-height);
+ color: var(--october-sunset);
+}
+
+.dc-row-2 {
+ min-height: var(--dc-row-height);
+ color: var(--honey);
+}
+
+.dc-row-3 {
+ min-height: var(--dc-row-height);
+ color: var(--october-sunset);
+}
+
+.dc-row-4 {
+ min-height: var(--dc-row-height);
+ color: var(--harvestgold);
+}
+
+.darkspace {
+ min-width: 4rem;
+ text-align: center;
+ color: var(--october-sunset);
+}
+
+.darkfont {
+ color: black;
+}
+
+@media (max-width: 1100px) {
+ .hide-data {
+ display: none;
+ }
+
+ .data-wrapper {
+ padding-right: 1rem;
+ }
+}
+
+/* Navigation & buttons */
+
+.lcars-button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ margin-block: 1rem;
+ width: 224px;
+ height: 80px;
+ padding-inline: 1.75rem;
+ padding-bottom: .675rem;
+ background-color: var(--button-color);
+ border-radius: 100vmax;
+ border-width: 0;
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.panel-1 a {
+ display: flex;
+ width: var(--lfw);
+ justify-content: flex-end;
+ align-items: flex-end;
+ background-color: var(--panel-1-color);
+ height: clamp(90px, 11vw, 160px);
+ padding: var(--left-frame-padding);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ text-decoration: none;
+ color: black;
+ transition: width 1s;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.panel-1-button {
+ display: flex;
+ width: var(--lfw);
+ justify-content: flex-end;
+ align-items: flex-end;
+ background-color: var(--panel-1-color);
+ min-height: clamp(90px, 11vw, 160px);
+ overflow: hidden;
+ padding: var(--left-frame-padding);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ text-decoration: none;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+nav {
+ display: flex;
+ flex-wrap: wrap;
+ align-self: center;
+ width: calc(var(--nav-width) + var(--nav-width) + 1rem);
+ justify-content: flex-end;
+ column-gap: .5rem;
+ row-gap: .65rem;
+}
+
+@media (max-width: 1500px) {
+ nav {
+ column-gap: .375rem;
+ row-gap: .5rem;
+ }
+}
+
+@media (max-width: 1440px) {
+ .data-cascade-button-group:has(.header-content) {
+ row-gap: 1rem;
+ }
+
+ .data-cascade-button-group:has(.header-content) > nav {
+ width: 100%;
+ }
+}
+
+nav a,
+nav button,
+.buttons button {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ align-items: flex-end;
+ width: var(--nav-width);
+ height: calc(var(--nav-width) / 2.8);
+ padding-inline: 1.5rem;
+ padding-bottom: .7rem;
+ border-radius: 100vmax;
+ background-color: var(--button-color);
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ text-transform: uppercase;
+ font-weight: bold;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+nav a:nth-child(1),
+nav button:nth-child(1) {
+ background-color: var(--nav-1-color);
+}
+
+nav a:nth-child(2),
+nav button:nth-child(2) {
+ background-color: var(--nav-2-color);
+}
+
+nav a:nth-child(3),
+nav button:nth-child(3) {
+ background-color: var(--nav-3-color);
+}
+
+nav a:nth-child(4),
+nav button:nth-child(4) {
+ background-color: var(--nav-4-color);
+}
+
+@media (max-width: 1300px) {
+ nav {
+ padding-left: .5rem;
+ gap: .5rem;
+ }
+
+ nav button {
+ padding-bottom: .5rem;
+ font-size: .875rem;
+ padding-inline: 1.25rem;
+ }
+}
+
+@media (max-width: 780px) {
+ nav {
+ flex: 1;
+ }
+
+ .data-cascade-button-group:has(.header-content) > *:nth-child(2) {
+ flex: none;
+ }
+}
+
+@media (max-width: 450px) {
+ nav a,
+ nav button {
+ height: 63px;
+ }
+}
+
+.buttons {
+ display: flex;
+ flex-wrap: wrap;
+ gap: .5rem;
+ margin-block: 2rem;
+}
+
+.justify-space-between {
+ justify-content: space-between;
+}
+
+.justify-center {
+ justify-content: center;
+}
+
+.justify-flex-end {
+ justify-content: flex-end;
+}
+
+.justify-space-around {
+ justify-content: space-around;
+}
+
+.justify-space-evenly {
+ justify-content: space-evenly;
+}
+
+.buttons a,
+.buttons button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ width: 224px;
+ height: 80px;
+ padding-inline: 1.75rem;
+ padding-bottom: .675rem;
+ background-color: var(--button-color);
+ border-radius: 100vmax;
+ border-width: 0;
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.sidebar-nav button,
+.sidebar-nav a,
+.sidebar-button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ min-height: 3.75rem;
+ width: var(--lfw);
+ background-color: var(--button-color-sidebar);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ padding: var(--left-frame-padding);
+ text-decoration: none;
+ text-align: right;
+ word-break: break-all;
+ text-transform: uppercase;
+ color: black;
+}
+
+@media (max-width: 1300px) {
+ .lcars-button,
+ .buttons a,
+ .buttons button {
+ width: 200px;
+ height: 74px;
+ }
+}
+
+.panel-button {
+ display: flex;
+ justify-content: flex-end;
+ width: var(--lfw);
+ border-radius: 0;
+ padding: var(--left-frame-padding);
+ border-bottom: var(--panel-border);
+}
+
+.pan-0 /* use this if you're not mimicking an established panel */ {
+ height: 18vh;
+ max-height: 240px;
+ background-color: var(--button-color-sidebar);
+}
+
+.pan-4 {
+ height: 14vh;
+ background-color: var(--panel-4-color) !important;
+}
+
+.pan-5 {
+ height: 30vh;
+ background-color: var(--panel-5-color) !important;
+}
+
+.pan-7 {
+ height: 30vh;
+ background-color: var(--panel-7-color) !important;
+}
+
+.text-bottom {
+ align-items: flex-end;
+}
+
+#topBtn {
+ display: none;
+ position: fixed;
+ bottom: 0;
+ z-index: 99;
+ border-radius: 0;
+ border-top: var(--panel-border);
+ border-right: none;
+ border-bottom: var(--panel-border);
+ border-left: none;
+ outline: none;
+ width: var(--lfw);
+ padding: var(--left-frame-padding);
+ padding-bottom: 10vh;
+ background-color: var(--panel-top-button-color);
+ text-align: right;
+ line-height: 1;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ cursor: pointer;
+}
+
+#topBtn:hover {
+ filter: brightness(115%);
+}
+
+#topBtn:active {
+ filter: brightness(80%);
+}
+
+@media (max-width: 525px) {
+ .sidebar-button,
+ .sidebar-nav a,
+ .sidebar-nav button,
+ .panel-button {
+ font-size: .75rem;
+ }
+
+ #topBtn {
+ padding-bottom: 6vh;
+ }
+}
+
+zack {
+
+}
+
+.bar-panel {
+ display: flex;
+ height: var(--bar-height);
+}
+
+.first-bar-panel {
+ margin-top: 2.5vh;
+ position: relative;
+}
+
+.bar-1,
+.bar-2,
+.bar-3,
+.bar-4,
+.bar-5,
+.bar-6,
+.bar-7,
+.bar-9,
+.bar-10 {
+ height: var(--bar-height);
+}
+
+.bar-1,
+.bar-2,
+.bar-3,
+.bar-6,
+.bar-7,
+.bar-8 {
+ border-right: var(--bar-border);
+}
+
+.bar-5,
+.bar-10 {
+ border-left: var(--bar-border);
+}
+
+.bar-1 {
+ width: var(--bar-1-6-width);
+ background-color: var(--bar-1-color);
+}
+
+.bar-2 {
+ width: var(--bar-2-7-width);
+ background-color: var(--bar-2-color);
+}
+
+.bar-3 {
+ width: var(--bar-3-8-width);
+ background-color: var(--bar-3-color);
+}
+
+.bar-4 {
+ flex: 1;
+ position: relative;
+ background-color: var(--bar-4-color);
+ z-index: -1;
+}
+
+.bar-5 {
+ width: var(--bar-5-10-width);
+ background-color: var(--bar-5-color);
+}
+
+.bar-6 {
+ width: var(--bar-1-6-width);
+ background-color: var(--bar-6-color);
+}
+
+.bar-7 {
+ width: var(--bar-2-7-width);
+ background-color: var(--bar-7-color);
+}
+
+.bar-8 {
+ width: var(--bar-3-8-width);
+ background-color: var(--bar-8-color);
+}
+
+.bar-9 {
+ flex: 1;
+ position: relative;
+ background-color: var(--bar-9-color);
+ z-index: -1;
+}
+
+.bar-4::before {
+ content: '';
+ display: block;
+ width: var(--bar-cut-out-width);
+ height: 8px;
+ background-color: var(--bar-cut-out-top-color);
+ position: absolute;
+ top: -22px;
+ left: 0;
+}
+
+.bar-4::after {
+ content: "";
+ display: block;
+ width: var(--bar-cut-width);
+ height: 48%;
+ background-color: black;
+ border-radius: 0 8px 0 0;
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ z-index: -1;
+}
+
+.bar-9::before {
+ content: '';
+ display: block;
+ width: var(--bar-cut-out-width);
+ height: 8px;
+ background-color: var(--bar-cut-out-bottom-color);
+ position: absolute;
+ bottom: -22px;
+ left: 0;
+}
+
+.bar-9::after {
+ content: "";
+ display: block;
+ width: var(--bar-cut-width);
+ height: 48%;
+ background-color: black;
+ border-radius: 0 0 8px 0;
+ position: absolute;
+ left: 0;
+ top: 0;
+ z-index: -1;
+}
+
+.bar-10 {
+ width: var(--bar-5-10-width);
+ background-color: var(--bar-10-color);
+}
+
+/* Bottom half */
+
+#gap {
+ margin-top: var(--divider-height);
+}
+
+.panel-3,
+.panel-4,
+.panel-5 {
+ border-bottom: var(--panel-border);
+}
+
+.panel-2,
+.panel-3,
+.panel-4,
+.panel-5,
+.panel-6,
+.panel-7 {
+ padding: var(--left-frame-padding);
+}
+
+.panel-4 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ min-height: 14vh;
+ background-color: var(--panel-4-color);
+}
+
+.panel-5 {
+ height: 30vh;
+ background-color: var(--panel-5-color);
+}
+
+/* Note: panel-6 height is fluid and governed by the amount of content on the page. Background color is inherited from :root --left-frame-color */
+
+.panel-6 {
+ min-height: 15vh;
+}
+
+.panel-7 {
+ height: 30vh;
+ border-top: var(--panel-border);
+ background-color: var(--panel-7-color);
+}
+
+.right-frame {
+ flex: 1;
+ position: relative;
+}
+
+.right-frame::before {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(to bottom right, var(--corner-color-bottom) 50%, #000 50%);
+ position: absolute;
+ left: 0;
+ top: var(--bar-height);
+ z-index: -1;
+}
+
+.right-frame::after {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background-color: black;
+ border-radius: var(--radius-content-bottom);
+ position: absolute;
+ left: 0;
+ top: var(--bar-height);
+ z-index: -1;
+}
+
+main {
+ padding-top: clamp(1.5rem, 5vw, 60px);
+ padding-bottom: .5rem;
+ padding-left: clamp(20px, 3vw, 50px);
+}
+
+main > *:first-child,
+article > *:first-child {
+ margin-top: 0;
+}
+
+main:has(.floor-heading) > *:nth-child(2) {
+ margin-top: 0;
+}
+
+.flexbox {
+ display: flex;
+ gap: 1.2rem;
+ flex-wrap: wrap;
+}
+
+.col {
+ flex: 1 1 360px;
+}
+
+.col > *:first-child {
+ margin-top: 0;
+}
+
+h1, h2, h3, h4 {
+ margin-block: 1.75rem;
+ font-weight: normal;
+ line-height: 1.2;
+ text-transform: uppercase;
+ text-box: trim-both cap alphabetic;
+}
+
+h1 {
+ font-size: clamp(1.5rem, 1.25rem + 3.5vw, 4rem);
+ text-align: right;
+ color: var(--h1-color);
+}
+
+h2 {
+ font-size: clamp(1.4rem, 1.1rem + 2.25vw, 2.3rem);
+ color: var(--h2-color);
+}
+
+h3 {
+ font-size: clamp(1.15rem, 1.05rem + 1.25vw, 1.875rem);
+ color: var(--h3-color);
+}
+
+h4 {
+ font-size: clamp(1.025rem, 1rem + 1.125vw, 1.575rem);
+ color: var(--h4-color);
+}
+
+.floor-heading {
+ display: flex;
+ justify-content: flex-end;
+ width: 100%;
+ position: fixed;
+ left: 50%;
+ transform: translate(-50%, 0);
+ bottom: 10px;
+ z-index:4;
+}
+
+.floor-heading > * {
+ margin-block: 0;
+ width: fit-content;
+ padding-inline: .5rem;
+ padding-bottom: .5rem;
+ background-color: black;
+}
+
+p {
+ margin-block: 1.75rem;
+ text-box: trim-both cap alphabetic;
+}
+
+.caption {
+ margin-top: -1rem;
+ text-align: center;
+ font-size: var(--sub-fonts);
+}
+
+.indent {
+ padding-left: 1.5rem;
+}
+
+.postmeta {
+ margin-block: 1.25rem;
+ text-align: right;
+ font-size: clamp(1.2rem, 0.88rem + 1.28vw, 1.6rem);
+ line-height: 1.2;
+ text-transform: uppercase;
+}
+
+article h1 {
+ margin-bottom: 0;
+}
+
+code {
+ font-family: monospace;
+ font-size: .9rem;
+ color: var(--code-color);
+}
+
+.code {
+ width: 100%;
+ min-height: 5rem;
+ padding-block: .5rem;
+ padding-inline: 1rem;
+ background-color: #232323;
+ border-color: #4c4c4c;
+ tab-size: 4;
+ font-family: monospace;
+ font-size: .85rem;
+ color: #dcdcdc;
+}
+
+hr {
+ margin-block: 1.5rem;
+ height: 6px;
+ border: none;
+ background-color: var(--font-color);
+ border-radius: 3px;
+}
+
+blockquote {
+ margin-block: 1.75rem;
+ margin-left: 1.5rem;
+ padding-block: .25rem;
+ padding-left: 1.5rem;
+ position: relative;
+ text-box: trim-both cap alphabetic;
+}
+
+blockquote::before {
+ content: '';
+ display: block;
+ width: 10px;
+ height: 100%;
+ background-color: var(--font-color);
+ border-radius: 5px;
+ position: absolute;
+ left: 0;
+ top: 0;
+}
+
+blockquote > *:first-child {
+ margin-top: 0;
+}
+
+blockquote > * {
+ margin-bottom: 0;
+}
+
+.flush {
+ margin-top: -1rem;
+}
+
+.nomar {
+ margin-block: 0 !important;
+}
+
+.go-center {
+ text-align: center !important;
+}
+
+.go-right {
+ text-align: right !important;
+}
+
+.go-left {
+ text-align: left !important;
+}
+
+.go-big {
+ font-size: clamp(1.2rem, 1rem + 1vw, 1.275rem);
+}
+
+.uppercase {
+ text-transform: uppercase;
+}
+
+.strike {
+ text-decoration: line-through;
+ text-decoration-thickness: .15rem;
+}
+
+.now {
+ white-space: nowrap;
+}
+
+.big-sky {
+ margin-top: 5rem;
+}
+
+strong {
+ font-weight: bold;
+}
+
+code {
+ font-family: monospace;
+ font-size: .9rem;
+ color: var(--code-color);
+}
+
+.code {
+ width: 100%;
+ min-height: 5rem;
+ padding-block: .5rem;
+ padding-inline: 1rem;
+ tab-size: 4;
+ font-family: monospace;
+ font-size: .85rem;
+}
+
+@keyframes blink {
+ 0% {opacity: 0}
+ 49%{opacity: 0}
+ 50% {opacity: 1}
+}
+
+.blink-slow {
+ animation: blink 3500ms infinite;
+}
+
+.blink {
+ animation: blink 2s infinite;
+}
+
+.blink-fast {
+ animation: blink 1s infinite;
+}
+
+@keyframes pulse {
+ 0% {filter: brightness(1.0)}
+ 50% {filter: brightness(.25)}
+}
+
+.pulse {
+ animation: pulse 2300ms infinite;
+}
+
+.pulse-rate-high {
+ animation: pulse 1s infinite;
+}
+
+.accordion-wrapper {
+ display: block;
+ margin: 1.75rem auto;
+ width: 100%;
+}
+
+.limit-width {
+ max-width: 1240px;
+}
+
+.accordion {
+ display: flex;
+ align-items: center;
+ min-height: 3rem;
+ width: 100%;
+ padding-right: 2.75rem;
+ padding-left: 1rem;
+ border-radius: 100vmax;
+ background-color: var(--harvestgold);
+ border-left: solid 3rem var(--orange);
+ text-align: left;
+ font-size: 1.25rem;
+ color: black;
+ cursor: pointer;
+ transition: 0.4s ease;
+ position: relative;
+}
+
+.active, .accordion:hover {
+ background-color: var(--honey);
+ border-left-color: var(--butter);
+ filter: none;
+}
+
+.accordion:before {
+ content: '';
+ display: block;
+ width: .5rem;
+ height: 4rem;
+ background-color: black;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.accordion:after {
+ display: block;
+ content: '\276F';
+ position: absolute;
+ right: 1.5rem;
+ top: 21%;
+ transform: rotate(90deg);
+ transition: 0.4s;
+ font-weight: bold;
+ color: black;
+}
+
+.active:after {
+ content: "\276F";
+ transform:rotate(-90deg);
+ transition: 0.4s ease;
+}
+
+.accordionContent {
+ padding-block: .5rem;
+ padding-inline: 3.5rem;
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 0.25s ease-out;
+}
+
+.accordionContent ul {
+ margin-left: 0;
+}
+
+@media (max-width: 525px) {
+ .accordion {
+ min-height: 2.5rem;
+ font-size: 1rem;
+ border-left-width: 2.5rem;
+ }
+}
+
+/* Images */
+
+.pics-right {
+ float: right;
+ margin-inline: 1rem;
+ margin-bottom: 2rem;
+ max-width: 500px;
+}
+
+.pics-left {
+ float: left;
+ margin-inline: 1rem;
+ margin-bottom: 2rem;
+ max-width: 500px;
+}
+
+@media (max-width: 1060px) {
+ .pics-right,
+ .pics-left {
+ float: none;
+ margin-inline: auto;
+ }
+
+ .pics-right img,
+ .pics-left img {
+ margin-inline: auto;
+ }
+}
+
+.pics {
+ margin-block: 2rem;
+ margin-inline: auto;
+}
+
+.border {
+ border: 2px solid var(--font-color);
+}
+
+.lcars-list {
+ margin-block: 1.15rem;
+ margin-left: 2rem;
+ list-style: none;
+}
+
+.lcars-list li {
+ position: relative;
+ padding-block: .45rem;
+ padding-left: 2.25rem;
+ text-box: trim-both cap alphabetic;
+}
+
+.lcars-list li::before {
+ content: '';
+ display: block;
+ width: 34px;
+ height: 18px;
+ border-radius: 50%;
+ background-color: var(--font-color);
+ position: absolute;
+ top: .45rem;
+ left: 0;
+}
+
+@media (max-width: 650px) {
+
+ .lcars-list {
+ margin-left: .5rem;
+ }
+
+ .lcars-list li::before {
+ transform: scale(90%);
+ }
+}
+
+.lcars-bar {
+ margin-block: 1.75rem;
+ height: clamp(15px, 2vh, 25px);
+ background: transparent;
+ border-right: clamp(15px, 2vh, 25px) solid var(--lcars-bar-end-color);
+ border-left: clamp(15px, 2vh, 25px) solid var(--lcars-bar-start-color);
+ border-radius: 100vmax;
+ position: relative;
+}
+
+.lcars-bar::after {
+ content: '';
+ display: block;
+ height: clamp(15px, 2vh, 25px);
+ width: 100%;
+ background-color: var(--lcars-bar-color);
+ border-right: .8vh solid black;
+ border-left: .8vh solid black;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.lcars-text-bar {
+ display: flex;
+ position: relative;
+ height: clamp(16px, 4vh, 41px);
+ margin-block: 2.75rem;
+ overflow: visible;
+ border-radius: 100vmax;
+ background-color: var(--lcars-bar-color);
+ border-right: clamp(16px, 4vh, 43px) solid var(--lcars-bar-end-color);
+ border-left: clamp(16px, 4vh, 43px) solid var(--lcars-bar-start-color);
+}
+
+.the-end {
+ justify-content: flex-end;
+}
+
+.lcars-text-bar h2,
+.lcars-text-bar h3,
+.lcars-text-bar h4,
+.lcars-text-bar span {
+ margin-block: 0;
+ background-color: black;
+ height: clamp(20px, 5.5vh, 60px); /* Setting height is a Firefox fix */
+ overflow: visible;
+ border-top: 1px solid black;
+ padding-inline: 1vh;
+ font-size: clamp(17px, 4.5vh, 46px);
+ line-height: 1;
+ text-transform: uppercase;
+ color: var(--lcars-bar-text-color);
+ z-index: 1;
+ text-box: trim-both cap alphabetic;
+}
+
+.lcars-text-bar::before {
+ content: '';
+ background-color: black;
+ display: block;
+ width: 1vh;
+ height: 6vh;
+ position: absolute;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+}
+
+.lcars-text-bar::after {
+ content: '';
+ background-color: black;
+ display: block;
+ width: 1vh;
+ height: 6vh;
+ position: absolute;
+ top: 0;
+ right: 0;
+ overflow: hidden;
+}
+
+/* LCARS Image Frame */
+
+.image-frame {
+ margin: 2.75rem auto;
+ width: fit-content;
+ background: linear-gradient(var(--primary-color) 56%, var(--secondary-color) 44%);
+ border-radius: 50px 25px 0 50px;
+ position: relative;
+}
+
+.image-frame::before {
+ content: '';
+ display: block;
+ width: 40px;
+ height: 40px;
+ background-color: black;
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+.image-frame::after {
+ content: '';
+ display: block;
+ border-top: var(--spacers) solid black;
+ border-bottom: var(--spacers) solid black;
+ width: 45px;
+ height: 80px;
+ background-color: var(--secondary-color);
+ position: absolute;
+ left: 0;
+ top: 56%;
+}
+
+.imgf-title {
+ display: flex;
+ justify-content: flex-end;
+ height: 40px;
+ border-right: 40px solid var(--secondary-color);
+ border-radius: 25px 100vh 100vh 0;
+ position: relative;
+ z-index: 1;
+ text-align: right;
+}
+
+.h4-wrapper {
+ width: fit-content;
+ height: var(--frame-height);
+ background-color: black;
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-title h4 {
+ margin-block: 0;
+ width: fit-content;
+ background-color: black;
+ padding-left: var(--spacers);
+ font-size: 2.05rem;
+ color: var(--title-color);
+ line-height: 40px;
+ text-transform: uppercase;
+}
+
+.imgf-image-body {
+ margin-left: 45px;
+ background-color: black;
+ width: fit-content;
+ padding: 1rem;
+ border-radius: 28px 0 0 28px;
+}
+
+.image-holder {
+ width: fit-content;
+ padding: 1rem;
+ border: 2px solid var(--image-border-color);
+ border-radius: 20px;
+}
+
+.imgf-base {
+ display: grid;
+ grid-template-columns: 20% 13% 35px 15% 1fr;
+ margin-left: 80px;
+ border-left: var(--spacers) solid black;
+}
+
+.imgf-block-1 {
+ height: var(--frame-height);
+ background-color: var(--accent-color);
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-block-2 {
+ height: var(--frame-height);
+ background-color: var(--secondary-color);
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-block-3 {
+ height: var(--frame-height);
+ background-color: black;
+ border-right: 10px solid var(--secondary-color);
+ border-left: 10px solid var(--accent-color);
+}
+
+.imgf-block-4 {
+ height: var(--frame-height);
+ background-color: var(--secondary-color);
+ border-left: var(--spacers) solid black;
+}
+
+.imgf-block-5 {
+ height: var(--frame-height);
+ background-color: black;
+}
+
+@media (max-width: 750px) {
+
+ .image-frame {
+ border-radius: 40px 25px 0 40px;
+ }
+
+ .image-frame::after {
+ width: 25px;
+ height: 60px;
+ top: 50%;
+ }
+
+ .imgf-title {
+ height: 25px;
+ border-right: 24px solid var(--secondary-color);
+ }
+
+ .h4-wrapper {
+ width: fit-content;
+ height: var(--frame-height);
+ background-color: black;
+ }
+
+ .imgf-title h4 {
+ font-size: 1.45rem;
+ }
+
+ .imgf-image-body {
+ margin-left: 25px;
+ padding: .75rem;
+ border-radius: 20px 0 0 20px;
+ }
+
+ .image-holder {
+ padding: .75rem;
+ border-radius: 10px;
+ }
+}
+
+/* color styles */
+
+.font-butter {
+ color: var(--butter) !important;
+}
+
+.button-butter,
+.background-butter,
+.bullet-butter::before {
+ background-color: var(--butter) !important;
+}
+
+.font-daybreak {
+ color: var(--daybreak) !important;
+}
+
+.button-daybreak,
+.background-daybreak,
+.bullet-daybreak::before {
+ background-color: var(--daybreak) !important;
+}
+
+.font-harvestgold {
+ color: var(--harvestgold) !important;
+}
+
+.button-harvestgold,
+.background-harvestgold,
+.bullet-harvestgold::before {
+ background-color: var(--harvestgold) !important;
+}
+
+.font-honey {
+ color: var(--honey) !important;
+}
+
+.button-honey,
+.background-honey,
+.bullet-honey::before {
+ background-color: var(--honey) !important;
+}
+
+.font-october-sunset {
+ color: var(--october-sunset) !important;
+}
+
+.button-october-sunset,
+.background-october-sunset,
+.bullet-october-sunset::before {
+ background-color: var(--october-sunset) !important;
+}
+
+.font-orange {
+ color: var(--orange) !important;
+}
+
+.button-orange,
+.background-orange,
+.bullet-orange::before {
+ background-color: var(--orange) !important;
+}
+
+.font-rich-pumpkin{
+ color: var(--rich-pumpkin) !important;
+}
+
+.button-rich-pumpkin,
+.background-rich-pumpkin,
+.bullet-rich-pumpkin::before {
+ background-color: var(--rich-pumpkin) !important;
+}
+
+/* Footer */
+
+footer {
+ margin-top: 2rem;
+ padding-bottom: 3rem;
+ padding-left: clamp(20px, 3vw, 50px);
+ font-size: .875rem;
+}
+
+.headtrim {
+ height: 10px;
+ width: 100%;
+ background-color: black;
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 999;
+}
+
+.baseboard {
+ height: 10px;
+ width: 100%;
+ background-color: black;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ z-index: 999;
+}
+
+/* Grouped @Media */
+
+@media (max-width: 650px) {
+
+ .first-bar-panel {
+ margin-top: .75rem;
+ }
+
+ .bar-4::before {
+ height: 5px;
+ top: -12px;
+ }
+
+ .bar-9::before {
+ height: 5px;
+ bottom: -12px;
+ }
+}
+
+@media (max-width: 525px) {
+
+ body {
+ padding: .25rem;
+ }
+
+ .baseboard {
+ display: none;
+ }
+
+ .wrap {
+ padding-left: 0;
+ padding-right: .25rem;
+ }
+
+ .hop {
+ display: none;
+ }
+
+ .left-frame {
+ padding-top: 8vh;
+ }
+
+ .left-frame:has(.sidebar-nav) {
+ padding-top: 5vh;
+ }
+
+ .left-frame:has(button) {
+ padding-top: 5vh;
+ }
+
+ .floor-heading {
+ bottom: 0;
+ }
+
+ blockquote {
+ margin-left: .75rem;
+ }
+}
+
+ @-moz-document url-prefix() {
+
+ main {
+ padding-top: clamp(1rem, 4vw, 30px);
+ }
+
+ h1, h2, h3, h4, p {
+ margin-block: 1.15rem;
+ }
+
+ .postmeta {
+ margin-top: .75rem;
+ }
+
+ .lcars-text-bar h2,
+ .lcars-text-bar h3,
+ .lcars-text-bar h4,
+ .lcars-text-bar span {
+ position: absolute;
+ top: -.7vh;
+ }
+
+ .meta-data {
+ height: 1.15rem;
+ line-height: 1rem;
+ }
+}
\ No newline at end of file
diff --git a/lcars/frontend/assets/nemesis-blue.css b/lcars/frontend/assets/nemesis-blue.css
new file mode 100644
index 0000000..e763cc4
--- /dev/null
+++ b/lcars/frontend/assets/nemesis-blue.css
@@ -0,0 +1,2830 @@
+@charset "utf-8";
+
+/*
+
+ CSS Document
+ LCARS Nemesis Blue Theme
+ Version 24.2
+ By Jim Robertus www.thelcars.com
+ Modified: 2025 Aug 11
+
+*/
+
+:root {
+ font-size: 1.375rem;
+ color-scheme: dark;
+ --lfw: 240px;
+ --cardinal: #c23;
+ --cool: #69f;
+ --evening: #26f;
+ --galaxy-gray: #52526a;
+ --ghost: #8bf;
+ --grape: #96c;
+ --honey: #fc9;
+ --lawn: #9a2;
+ --martian: #9c3; /* 9d6/9c3 (#982 dark olive & #992 light olive or yellow-green) (#9a2 lawn) (#971 flat-dark-grass) */
+ --midnight: #23f;
+ --moonbeam: #ebf0ff;
+ --pumpkinshade: #f74;
+ --roseblush: #c66;
+ --tangerine: #f83;
+ --wheat: #ca8;
+ --left-frame-top-color: var(--evening);
+ --left-frame-color: var(--evening); /* panel-9 inherits this color */
+ --left-frame-padding: .75rem;
+ --corner-color-top: var(--evening);
+ --corner-color-bottom: var(--evening);
+ --panel-1-color: var(--wheat);
+ --panel-2-color: var(--moonbeam);
+ --panel-4-color: var(--evening);
+ --panel-5-color: var(--cool);
+ --panel-6-color: var(--evening);
+ --panel-7-color: var(--evening);
+ --panel-8-color: var(--moonbeam);
+ --panel-10-color: var(--evening);
+ --panel-top-button-color: var(--tangerine);
+ --bar-height: 28px;
+ --bar-1-6-width: 40%;
+ --bar-2-7-width: 4%;
+ --bar-3-8-width: 17%;
+ --bar-5-10-width: 4%;
+ --bar-1-color: var(--evening);
+ --bar-2-color: var(--honey);
+ --bar-3-color: var(--cool);
+ --bar-4-color: var(--midnight);
+ --bar-5-color: var(--galaxy-gray);
+ --bar-6-color: var(--evening);
+ --bar-7-color: var(--tangerine);
+ --bar-8-color: var(--moonbeam);
+ --bar-9-color: var(--evening);
+ --bar-10-color: var(--galaxy-gray);
+
+/* Ultra layout elements */
+
+ --section-2-color: var(--cool);
+ --pill-1-color: var(--evening);
+ --pill-2-color: var(--cool);
+ --pill-3-color: var(--cool);
+ --pill-4-color: var(--evening);
+ --pill-5-color: var(--midnight);
+ --pill-6-color: var(--midnight);
+ --pill-a1-color: var(--midnight);
+ --pill-a2-color: var(--cool);
+ --pill-a3-color: black;
+ --pill-a4-color: var(--evening);
+ --pill-a5-color: var(--tangerine);
+ --pill-a6-color: var(--honey);
+ --panel-11-color: var(--cool);
+ --panel-12-color: var(--cool);
+ --panel-13-color: var(--honey);
+ --panel-14-color: var(--evening);
+ --panel-15-color: var(--cool);
+
+ /* End Ultra layout elements */
+
+ --radius-top: 0 0 0 160px;
+ --radius-bottom: 160px 0 0 0;
+ --radius-content-top: 0 0 0 60px;
+ --radius-content-bottom: 60px 0 0 0;
+ --panel-border: .25rem solid black;
+ --bar-border: .25rem solid black;
+ --bar-cut-width: 34%;
+ --bar-cut-out-width: 34%;
+ --divider-height: .5rem;
+/*
+ NOTE: --font-color also sets the following:
+ 1. horizontal line
color
+ 2. lcars-list default bullet color
+ 3. blockquote border color
+ 4. images with the *border* class border color
+*/
+ --font-color: var(--cool);
+ --sub-fonts: .875rem;
+ --dc-font-size: .875rem;
+ --dc-row-height: calc(var(--dc-font-size) + .125rem);
+ --banner-color: var(--ghost);
+ --data-cascade-color: var(--evening);
+ --light-color: var(--moonbeam);
+ --h1-color: var(--ghost);
+ --h2-color: var(--ghost);
+ --h3-color: var(--ghost);
+ --h4-color: var(--ghost);
+ --link-color: var(--cool);
+ --code-color: var(--martian);
+ --nav-width: 240px;
+ --nav-1-color: var(--cool);
+ --nav-2-color: var(--roseblush);
+ --nav-3-color: var(--honey);
+ --nav-4-color: var(--cardinal);
+ --button-color: var(--cool);
+ --button-color-sidebar: var(--cool);
+ --lcars-bar-color: var(--evening);
+ --lcars-bar-start-color: var(--midnight);
+ --lcars-bar-end-color: var(--midnight);
+ --lcars-bar-text-color: var(--moonbeam);
+
+/* Image Frame */
+
+ --image-border-color: var(--cool);
+ --primary-color: var(--evening);
+ --secondary-color: var(--cool);
+ --accent-color: #adcaff; /* bdd1ff */
+ --spacers: .65rem;
+ --frame-height: 40px;
+}
+
+@media (max-width: 1500px) {
+ :root {
+ --lfw: 200px;
+ --radius-top: 0 0 0 130px;
+ --radius-bottom: 130px 0 0 0;
+ --divider-height: .4rem;
+ --nav-width: 210px;
+ --dc-font-size: .75rem;
+ --bar-height: 24px;
+ }
+}
+
+@media (max-width: 1300px) {
+ :root {
+ font-size: 1.2rem;
+ --sub-fonts: .9rem;
+ --lfw: 180px;
+ --nav-width: 180px;
+ --radius-top: 0 0 0 100px;
+ --radius-bottom: 100px 0 0 0;
+ --radius-content-top: 0 0 0 40px;
+ --radius-content-bottom: 40px 0 0 0;
+ --bar-height: 20px;
+ }
+}
+
+@media (max-width: 950px) {
+ :root {
+ --lfw: 150px;
+ }
+}
+
+@media (max-width: 750px) {
+ :root {
+ --panel-border: .25rem solid black;
+ --bar-border: .25rem solid black;
+ --lfw: 120px;
+ --radius-top: 0 0 0 80px;
+ --radius-bottom: 80px 0 0 0;
+ --radius-content-top: 0 0 0 34px;
+ --radius-content-bottom: 34px 0 0 0;
+ --nav-width: 174px;
+ --bar-height: 16px;
+ --spacers: .5rem;
+ --frame-height: 25px;
+ }
+}
+
+@media (max-width: 525px) {
+ :root {
+ --lfw: 62px;
+ --left-frame-padding: .5rem;
+ --radius-top: 0 0 0 40px;
+ --radius-bottom: 40px 0 0 0;
+ --bar-height: 10px;
+ --divider-height: .3rem;
+ }
+}
+
+@media (max-width: 450px) {
+ :root {
+ --nav-width: 48%;
+ }
+}
+
+*, *:after, *:before {
+ box-sizing: border-box;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ font: inherit;
+}
+
+img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
+input, textarea, button, select {
+ font: inherit;
+}
+
+@font-face {
+ font-family: 'Antonio';
+ font-weight: 400;
+ src: url('Antonio-Regular.woff2') format('woff2'),
+ url('Antonio-Regular.woff') format('woff');
+}
+
+@font-face {
+ font-family: 'Antonio';
+ font-weight: 700;
+ src: url('Antonio-Bold.woff2') format('woff2'),
+ url('Antonio-Bold.woff') format('woff')
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ display: flex;
+ padding-top: 10px;
+ padding-left: 5px;
+ background-color: black;
+ font-family: 'Antonio', 'Arial Narrow', 'Avenir Next Condensed', sans-serif;
+ font-weight: 400;
+ line-height: 1.5;
+ color: var(--font-color);
+}
+
+a {
+ text-decoration: underline;
+ text-decoration-thickness: 2px;
+ text-underline-offset: .2rem;
+ color: var(--link-color);
+}
+
+a:hover {
+ filter: brightness(115%);
+ animation: none;
+}
+
+a:active {
+ filter: brightness(80%);
+ outline: none;
+}
+
+button {
+ border: none;
+ outline: none;
+ color: black;
+ transition: width 1s;
+}
+
+button:hover {
+ cursor: pointer;
+ animation: none;
+ filter: brightness(115%);
+ color: black;
+}
+
+button:active {
+ filter: brightness(85%);
+}
+
+/* Ultra Layout elements */
+
+.wrap-everything {
+ display: flex;
+ width: 100%;
+ column-gap: 10px;
+}
+
+#column-1 {
+ width: 350px;
+ padding: 10px 10px 10px 20px;
+ transition: 800ms;
+}
+
+#column-2 {
+ width: var(--lfw);
+ background-color: var(--section-2-color);
+ text-align: right;
+ font-weight: bold;
+ line-height: 1.2;
+ color: black;
+ transition: 800ms;
+ z-index: 2;
+}
+
+#column-2 a {
+ color: black;
+ text-decoration: none;
+}
+
+#column-3 {
+ flex: 1;
+ margin-inline: auto;
+}
+
+.wrap {
+ display: flex;
+ margin-inline: auto;
+ padding-left: 5px;
+ padding-right: 15px;
+ overflow: hidden;
+}
+
+@media (max-width: 1680px) {
+ #column-1 {
+ margin-left: -370px;
+ }
+
+ #column-2 {
+ margin-left: -230px;
+ }
+
+ .wrap-everything {
+ column-gap: 5px;
+ }
+}
+
+@media (max-width: 1500px) {
+ #column-1,
+ #column-2 {
+ display: none;
+ }
+}
+
+.lcars-frame {
+ display: flex;
+ min-height: 280px;
+ position: relative;
+ --frame-color: var(--evening);
+}
+
+.frame-col-1 {
+ width: 20px;
+ height: 280px;
+ background: var(--frame-color);
+ border-radius: 16px 0 0 16px;
+ position: relative;
+}
+
+.frame-col-1:before {
+ content: '';
+ display: block;
+ width: 20px;
+ height: 200px;
+ border-top: 5px solid black;
+ border-bottom: 5px solid black;
+ background-color: var(--frame-color);
+ position: absolute;
+ top: 40px;
+ left: 0;
+}
+
+.frame-col-1-cell-a {
+ width: 14px;
+ height: 65px;
+ background-color: var(--grape);
+ border-left: 4px solid black;
+ border-bottom: 4px solid black;
+ position: absolute;
+ top: 45px;
+ right: 0;
+ z-index: 2;
+}
+
+.frame-col-1-cell-b {
+ width: 14px;
+ height: 70px;
+ background-color: var(--cool);
+ border-left: 4px solid black;
+ position: absolute;
+ top: 110px;
+ right: 0;
+ z-index: 2;
+}
+
+.frame-col-1-cell-c {
+ width: 14px;
+ height: 65px;
+ background-color: var(--grape);
+ border-top: 4px solid black;
+ border-left: 4px solid black;
+ position: absolute;
+ bottom: 45px;
+ right: 0;
+ z-index: 2;
+}
+
+.frame-col-1-blocks:before {
+ content: '';
+ display: block;
+ width: 10px;
+ height: 3px;
+ background-color: black;
+ position: absolute;
+ top: 54px;
+ left: 0;
+}
+
+.frame-col-2 {
+ width:20px;
+ height: 280px;
+ background-color: var(--frame-color);
+ position: relative;
+}
+
+.frame-col-2:before {
+ content: '';
+ display: block;
+ width: 20px;
+ height: 240px;
+ background-color: black;
+ border-radius: 10px 0 0 10px;
+ position: absolute;
+ top: 20px;
+ left: 0;
+}
+
+.frame-col-3 {
+ display: flex;
+ width: 240px;
+ height: 280px;
+ align-items: center;
+ justify-content: center;
+}
+
+.frame-col-4 {
+ width:20px;
+ height: 280px;
+ background-color: var(--frame-color);
+ position: relative;
+}
+
+.frame-col-4:before {
+ content: '';
+ display: block;
+ width: 20px;
+ height: 240px;
+ background-color: black;
+ border-radius: 0 10px 10px 0;
+ position: absolute;
+ top: 20px;
+ left: 0;
+}
+
+.display-horizontal {
+ rotate: 90deg;
+}
+
+.frame-col-5 {
+ width:20px;
+ height: 280px;
+ background-color: var(--frame-color);
+ border-radius: 0 16px 16px 0;
+ padding-top: 40px;
+ position: relative;
+}
+
+.frame-col-5:before {
+ content: '';
+ display: block;
+ width: 20px;
+ height: 200px;
+ border-top: 5px solid black;
+ border-bottom: 5px solid black;
+ background-color: var(--frame-color);
+}
+
+.frame-col-5-cell-a {
+ width: 14px;
+ height: 65px;
+ background-color: var(--tangerine);
+ border-bottom: 4px solid black;
+ border-right: 4px solid black;
+ position: absolute;
+ top: 45px;
+ left: 0;
+ z-index: 2;
+}
+
+.frame-col-5-cell-b {
+ width: 14px;
+ height: 70px;
+ background-color: var(--cool);
+ border-right: 4px solid black;
+ position: absolute;
+ top: 110px;
+ left: 0;
+ z-index: 2;
+}
+
+.frame-col-5-cell-c {
+ width: 14px;
+ height: 65px;
+ background-color: var(--tangerine);
+ border-top: 4px solid black;
+ border-right: 4px solid black;
+ position: absolute;
+ bottom: 45px;
+ left: 0;
+ z-index: 2;
+}
+
+.line {
+ height: 20px;
+ width: 12px;
+ background: linear-gradient(#600, #f20, #600);
+}
+
+.line:nth-child(1) {
+ animation: animateLine6 1s 0.2s infinite;
+}
+
+.line:nth-child(2) {
+ animation: animateLine5 1s 0.3s infinite;
+}
+
+.line:nth-child(3) {
+ animation: animateLine3 1s 0.4s infinite;
+}
+
+.line:nth-child(4) {
+ animation: animateLine3 1s 0.5s infinite;
+}
+
+.line:nth-child(5) {
+ animation: animateLine2 1s 0.6s infinite;
+}
+
+.line:nth-child(6) {
+ animation: animateLine2 1s 0.7s infinite;
+}
+
+.line:nth-child(7) {
+ animation: animateLine2 1s 0.8s infinite;
+}
+
+/* 8 & 9 are middle lines*/
+
+.line:nth-child(8) {
+ animation: animateLine4 1s 0.9s infinite;
+}
+
+.line:nth-child(9) {
+ animation: animateLine4 1s 1s infinite;
+}
+
+.line:nth-child(10) {
+ animation: animateLine2 1s 0.8s infinite;
+}
+
+.line:nth-child(11) {
+ animation: animateLine2 1s 0.7s infinite;
+}
+
+.line:nth-child(12) {
+ animation: animateLine2 1s 0.6s infinite;
+}
+
+.line:nth-child(13) {
+ animation: animateLine3 1s 0.5s infinite;
+}
+
+.line:nth-child(14) {
+ animation: animateLine3 1s 0.4s infinite;
+}
+
+.line:nth-child(15) {
+ animation: animateLine5 1s 0.3s infinite;
+}
+
+.line:nth-child(16) {
+ animation: animateLine6 1s 0.2s infinite;
+}
+
+@keyframes animateLine2 {
+ 0% {
+ height: 180px;
+ }
+ 50% {
+ height: 90px;
+ }
+ 100% {
+ height: 180px;
+ }
+}
+
+@keyframes animateLine3 {
+ 0% {
+ height: 120px;
+ }
+ 50% {
+ height: 60px;
+ }
+ 100% {
+ height: 120px;
+ }
+}
+
+@keyframes animateLine4 {
+ 0% {
+ height: 230px;
+ }
+ 50% {
+ height: 115px;
+ }
+ 100% {
+ height: 230px;
+ }
+}
+
+@keyframes animateLine5 {
+ 0% {
+ height: 60px;
+ }
+ 50% {
+ height: 30px;
+ }
+ 100% {
+ height: 60px;
+ }
+}
+
+@keyframes animateLine6 {
+ 0% {
+ height: 30px;
+ }
+ 50% {
+ height: 15px;
+ }
+ 100% {
+ height: 30px;
+ }
+}
+
+.pillbox,
+.pillbox-2 {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-gap: 8px;
+ margin: 1.25rem auto;
+ text-align: right;
+ font-size: var(--sub-fonts);
+}
+
+.pill,
+.pill-2 {
+ display: flex;
+ width: 100%;
+ height: 56px;
+ justify-content: flex-end;
+ align-items: flex-end;
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+ padding-right: .75rem;
+ padding-bottom: .35rem;
+}
+
+.pillbox a {
+ display: flex;
+ width: 100%;
+ height: 56px;
+ justify-content: flex-end;
+ align-items: flex-end;
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+ padding-right: .75rem;
+ padding-bottom: .35rem;
+}
+
+.pillbox-2 a {
+ display: flex;
+ width: 100%;
+ height: 56px;
+ justify-content: flex-end;
+ align-items: flex-end;
+ text-decoration: none;
+ color: black;
+ font-weight: bold;
+ padding-right: .75rem;
+ padding-bottom: .35rem;
+}
+
+.pill:hover,
+.pill-2:hover {
+ filter: brightness(115%);
+}
+
+.pill:active,
+.pill-2:active {
+ filter: brightness(80%);
+}
+
+.pill:nth-child(1),
+.pillbox a:nth-child(1) {
+ border-radius: 100vmax 0 0 100vmax;
+ background-color: var(--pill-1-color);
+}
+
+.pill:nth-child(2),
+.pillbox a:nth-child(2) {
+ background-color: var(--pill-2-color);
+}
+
+.pill:nth-child(3),
+.pillbox a:nth-child(3) {
+ border-radius: 100vmax 0 0 100vmax;
+ background-color: var(--pill-3-color);
+}
+
+.pill:nth-child(4),
+.pillbox a:nth-child(4) {
+ background-color: var(--pill-4-color);
+}
+
+.pill:nth-child(5),
+.pillbox a:nth-child(5) {
+ background-color: var(--pill-5-color);
+ border-radius: 100vmax 0 0 100vmax;
+}
+
+.pill:nth-child(6),
+.pillbox a:nth-child(6) {
+ background-color: var(--pill-6-color);
+}
+
+.pill-2:nth-child(1),
+.pillbox-2 a:nth-child(1) {
+ border-radius: 100vmax 0 0 100vmax;
+ background-color: var(--pill-a1-color);
+}
+
+.pill-2:nth-child(2),
+.pillbox-2 a:nth-child(2) {
+ background-color: var(--pill-a2-color);
+ border-radius: 0 100vmax 100vmax 0;
+ padding-right: 1rem;
+}
+
+.pill-2:nth-child(3),
+.pillbox-2 a:nth-child(3) {
+ background-color: var(--pill-a3-color);
+}
+
+.pill-2:nth-child(4),
+.pillbox-2 a:nth-child(4) {
+ background-color: var(--pill-a4-color);
+ border-radius: 0 100vmax 100vmax 0;
+ padding-right: 1rem;
+}
+
+.pill-2:nth-child(5),
+.pillbox-2 a:nth-child(5) {
+ background-color: var(--pill-a5-color);
+ border-radius: 100vmax 0 0 100vmax;
+}
+
+.pill-2:nth-child(6),
+.pillbox-2 a:nth-child(6) {
+ background-color: var(--pill-a6-color);
+ border-radius: 0 100vmax 100vmax 0;
+ padding-right: 1rem;
+}
+
+.lcars-list-2 ul {
+ list-style: none;
+}
+
+.lcars-list-2 {
+ margin: 0 auto 50px auto;
+ padding-left: 5px;
+}
+
+.lcars-list-2 li {
+ position: relative;
+ padding-bottom: 5px;
+ padding-left: 38px;
+ font-size: var(--sub-fonts);
+ color: var(--font-color);
+}
+
+.lcars-list-2 li::before {
+ content: '';
+ display: block;
+ width: 24px;
+ height: 14px;
+ border-radius: 50%;
+ background-color: var(--font-color);
+ position: absolute;
+ top: 8px;
+ left: 0;
+}
+
+.panel-11 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 23vh;
+ max-height: 275px;
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ background-color: var(--panel-11-color);
+ border-bottom: var(--panel-border);
+}
+
+.panel-12 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 15vh;
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ background-color: var(--panel-12-color);
+ border-bottom: var(--panel-border);
+}
+
+.panel-13 a {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ padding: 1.5rem .75rem .75rem 2px;
+ background-color: var(--panel-13-color);
+ border-bottom: var(--panel-border);
+}
+
+.panel-14 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 20vh;
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ background-color: var(--panel-14-color);
+ border-bottom: var(--panel-border);
+}
+
+.panel-15 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 20vh;
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ background-color: var(--panel-15-color);
+ border-bottom: var(--panel-border);
+}
+
+.section-2-buttons a {
+ display: block;
+ text-decoration: none;
+ text-align: right;
+ border-bottom: var(--panel-border);
+ padding: 1.5rem .75rem .75rem 2px;
+ background-color: var(--evening);
+ text-transform: uppercase;
+ color: black;
+}
+
+.section-2-buttons a:nth-child(2) {
+ background-color: var(--moonbeam);
+}
+
+/* End Ultra layout elements */
+
+.scroll-top {
+ display: none;
+}
+
+.left-frame-top,
+.left-frame {
+ width: var(--lfw);
+ text-align: right;
+ font-size: clamp(.875rem, 2vw, 1rem);
+ line-height: 1.2;
+ font-weight: bold;
+ color: black;
+ transition: width 1s;
+}
+
+.left-frame-top {
+ background-color: var(--left-frame-top-color);
+ border-radius: var(--radius-top);
+}
+
+.left-frame-top a,
+.left-frame a {
+ text-decoration: none;
+ color: black;
+}
+
+.left-frame {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding-top: 100px;
+ background-color: var(--left-frame-color);
+ border-radius: var(--radius-bottom);
+}
+
+.panel-1 a {
+ display: block;
+ background-color: var(--panel-1-color);
+ padding-top: clamp(40px, 8vw, 110px);
+ padding-right: .75rem;
+ padding-bottom: .75rem;
+ border-bottom: var(--panel-border);
+ text-decoration: none;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.right-frame-top {
+ flex: 1;
+ align-content: flex-end;
+ position: relative;
+}
+
+.right-frame-top:before {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(to top right, var(--corner-color-top) 50%, black 50%);
+ position: absolute;
+ left: 0;
+ bottom: var(--bar-height);
+ z-index: -1;
+}
+
+.right-frame-top:after {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background-color: black;
+ border-radius: var(--radius-content-top);
+ position: absolute;
+ left: 0;
+ bottom: var(--bar-height);
+ z-index: -1;
+}
+
+@media (max-width: 650px) {
+ .right-frame-top:before {
+ bottom: 16px;
+ }
+
+ .right-frame-top:after {
+ bottom: 16px;
+ }
+}
+
+@media (max-width: 525px) {
+ .right-frame-top:before {
+ bottom: 10px;
+ }
+
+ .right-frame-top:after {
+ bottom: 10px;
+ }
+}
+
+.banner {
+ padding-bottom: 1rem;
+ padding-left: 5px;
+ text-align: right;
+ text-transform: uppercase;
+ line-height: 1.1;
+ font-size: clamp(1.25rem, 0.75rem + 4vw, 3.75rem);
+ color: var(--banner-color);
+}
+
+.banner a {
+ color: var(--banner-color);
+ text-decoration: none;
+}
+
+.data-cascade-button-group {
+ display: flex;
+ justify-content: flex-end;
+}
+
+@media (max-width: 1440px) {
+ .data-cascade-button-group:has(.header-content) {
+ row-gap: 1rem;
+ }
+
+ .data-cascade-button-group:has(.header-content) > nav {
+ width: 100%;
+ }
+}
+
+.header-content {
+ flex: 1;
+ min-height: 180px;
+ padding-right: .5rem;
+ padding-left: clamp(20px, 3vw, 50px);
+}
+
+.header-content > *:first-child {
+ margin-top: 0;
+}
+
+.header-content > *:last-child {
+ margin-bottom: 0;
+}
+
+/* Data Cascade 2025 */
+
+.data-cascade-wrapper {
+ flex: 1;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ max-width: 100%;
+ height: calc(var(--dc-row-height) * 9); /* 204px */
+ overflow: hidden;
+ padding-right: .5rem;
+ padding-left: clamp(20px, 3vw, 50px);
+ column-gap: .5rem;
+}
+
+.data-column {
+ display: grid;
+ grid-template-columns: 1fr;
+ margin-top: 1px;
+ text-align: right;
+ font-size: var(--dc-font-size); /* .938 */
+ line-height: 1;
+ text-box: trim-both cap alphabetic;
+ color: black;
+}
+
+.dc-row-1,
+.dc-row-2,
+.dc-row-3,
+.dc-row-4,
+.dc-row-5,
+.dc-row-6,
+.dc-row-7 {
+ text-box: trim-both cap alphabetic;
+ height: var(--dc-row-height);
+}
+
+@media (max-width: 850px) {
+ .data-cascade-wrapper {
+ display: none;
+ }
+}
+
+
+@keyframes data-group-1 {
+
+ 0%,
+ 3.99% {
+ color: black;
+ }
+
+ 4%,
+ 45.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 46%,
+ 49.99% {
+ color: var(--light-color);
+ }
+
+ 50%,
+ 63.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 64%,
+ 67.99% {
+ color: var(--light-color);
+ }
+
+ 68%,
+ 100% {
+ color: var(--data-cascade-color);
+ }
+
+
+}
+
+@keyframes data-group-1a {
+
+ 0%,
+ 4.99% {
+ color: black;
+ }
+
+ 5%,
+ 45.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 46%,
+ 49.99% {
+ color: var(--light-color);
+ }
+
+ 50%,
+ 63.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 64%,
+ 67.99% {
+ color: var(--light-color);
+ }
+
+ 68%,
+ 100% {
+ color: var(--data-cascade-color);
+ }
+
+
+}
+
+@keyframes data-group-2 {
+
+ 0%,
+ 12.99% {
+ color: black;
+ }
+
+ 13%,
+ 49.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 50%,
+ 53.99% {
+ color: var(--light-color);
+ }
+
+ 54%,
+ 67.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 68%,
+ 71.99% {
+ color: var(--light-color);
+ }
+
+ 72%,
+ 100% {
+ color: var(--data-cascade-color);
+ }
+
+
+}
+
+@keyframes data-group-2b {
+
+ 0%,
+ 14.99% {
+ color: black;
+ }
+
+ 15%,
+ 49.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 50%,
+ 53.99% {
+ color: var(--light-color);
+ }
+
+ 54%,
+ 67.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 68%,
+ 71.99% {
+ color: var(--light-color);
+ }
+
+ 72%,
+ 81.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 82%,
+ 100% {
+ color: var(--light-color);
+ }
+
+
+}
+
+@keyframes data-group-3 {
+
+ 0%,
+ 26.99% {
+ color: black;
+ }
+
+ 27%,
+ 40.99% {
+ color: var(--light-color);
+ }
+
+ 41%,
+ 53.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 54%,
+ 57.99% {
+ color: var(--light-color);
+ }
+
+ 58%,
+ 71.99% {
+ color: var(--data-cascade-color);
+ }
+
+ 72%,
+ 75.99% {
+ color: var(--light-color);
+ }
+
+ 76%,
+ 100% {
+ color: var(--data-cascade-color);
+ }
+
+}
+
+.dc-row-1 {
+ animation: data-group-1 6000ms ease 200ms infinite;
+}
+
+.dc-row-2 {
+ animation: data-group-1a 6000ms ease 200ms infinite;
+}
+
+.dc-row-3 {
+ animation: data-group-2 6000ms ease 200ms infinite;
+}
+
+.dc-row-4 {
+ animation: data-group-2b 6000ms ease 200ms infinite;
+}
+
+.dc-row-5 {
+ animation: data-group-3 6000ms ease 200ms infinite;
+}
+
+.dc-row-6 {
+ animation: data-group-3 6000ms ease 200ms infinite;
+}
+
+.dc-row-7 {
+ animation: data-group-3 6000ms ease 200ms infinite;
+}
+
+
+/* Static data cascade */
+
+.data-cascade-wrapper#frozen .dc-row-1 {
+ animation: none;
+ color: var(--data-cascade-color);
+}
+
+.data-cascade-wrapper#frozen .dc-row-2 {
+ animation: none;
+ color: var(--data-cascade-color);
+}
+
+.data-cascade-wrapper#frozen .dc-row-3 {
+ animation: none;
+ color: var(--data-cascade-color);
+}
+
+.data-cascade-wrapper#frozen .dc-row-4 {
+ animation: none;
+ color: var(--data-cascade-color);
+}
+
+.data-cascade-wrapper#frozen .dc-row-5 {
+ animation: none;
+ color: var(--light-color);
+}
+
+.data-cascade-wrapper#frozen .dc-row-6 {
+ animation: none;
+ color: var(--light-color);
+}
+
+.data-cascade-wrapper#frozen .dc-row-7 {
+ animation: none;
+ color: var(--light-color);
+}
+
+/* Navigation & Buttons */
+
+.lcars-button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ margin-block: 1rem;
+ width: 224px;
+ height: 80px;
+ padding-inline: 1.75rem;
+ padding-bottom: .675rem;
+ background-color: var(--button-color);
+ border-radius: 100vmax;
+ border-width: 0;
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.panel-1 a {
+ display: flex;
+ width: var(--lfw);
+ justify-content: flex-end;
+ align-items: flex-end;
+ background-color: var(--panel-1-color);
+ height: clamp(90px, 11vw, 160px);
+ padding: var(--left-frame-padding);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ text-decoration: none;
+ color: black;
+ transition: width 1s;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.panel-1-button {
+ display: flex;
+ width: var(--lfw);
+ justify-content: flex-end;
+ align-items: flex-end;
+ background-color: var(--panel-1-color);
+ min-height: clamp(90px, 11vw, 160px);
+ overflow: hidden;
+ padding: var(--left-frame-padding);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ text-decoration: none;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.panel-2 a {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ min-height: 3.75rem;
+ padding: var(--left-frame-padding);
+ background-color: var(--panel-2-color);
+ border-bottom: var(--panel-border);
+}
+
+nav {
+ display: flex;
+ flex-wrap: wrap;
+ align-self: center;
+ width: calc(var(--nav-width) + var(--nav-width) + 1rem);
+ justify-content: flex-end;
+ column-gap: .5rem;
+ row-gap: .65rem;
+}
+
+@media (max-width: 1500px) {
+ nav {
+ column-gap: .375rem;
+ row-gap: .5rem;
+ }
+}
+
+@media (max-width: 1440px) {
+ .data-cascade-button-group:has(.header-content) {
+ row-gap: 1rem;
+ }
+
+ .data-cascade-button-group:has(.header-content) > nav {
+ width: 100%;
+ }
+}
+
+nav a,
+nav button,
+.buttons button {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ align-items: flex-end;
+ width: var(--nav-width);
+ height: calc(var(--nav-width) / 2.8);
+ padding-inline: 1.5rem;
+ padding-bottom: .7rem;
+ border-radius: 100vmax;
+ background-color: var(--button-color);
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ text-transform: uppercase;
+ font-weight: bold;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+nav a:nth-child(1),
+nav button:nth-child(1) {
+ background-color: var(--nav-1-color);
+}
+
+nav a:nth-child(2),
+nav button:nth-child(2) {
+ background-color: var(--nav-2-color);
+}
+
+nav a:nth-child(3),
+nav button:nth-child(3) {
+ background-color: var(--nav-3-color);
+}
+
+nav a:nth-child(4),
+nav button:nth-child(4) {
+ background-color: var(--nav-4-color);
+}
+
+@media (max-width: 1300px) {
+ nav {
+ padding-left: .5rem;
+ gap: .5rem;
+ }
+
+ nav button {
+ padding-bottom: .5rem;
+ font-size: .875rem;
+ padding-inline: 1.25rem;
+ }
+}
+
+@media (max-width: 780px) {
+ nav {
+ flex: 1;
+ }
+
+ .data-cascade-button-group:has(.header-content) > *:nth-child(2) {
+ flex: none;
+ }
+}
+
+@media (max-width: 450px) {
+ nav a,
+ nav button {
+ height: 63px;
+ }
+}
+
+.buttons {
+ display: flex;
+ flex-wrap: wrap;
+ gap: .5rem;
+ margin-block: 2rem;
+}
+
+.justify-space-between {
+ justify-content: space-between;
+}
+
+.justify-center {
+ justify-content: center;
+}
+
+.justify-flex-end {
+ justify-content: flex-end;
+}
+
+.justify-space-around {
+ justify-content: space-around;
+}
+
+.justify-space-evenly {
+ justify-content: space-evenly;
+}
+
+.buttons a,
+.buttons button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ width: 224px;
+ height: 80px;
+ padding-inline: 1.75rem;
+ padding-bottom: .675rem;
+ background-color: var(--button-color);
+ border-radius: 100vmax;
+ border-width: 0;
+ text-align: right;
+ line-height: 1.175;
+ text-decoration: none;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.sidebar-nav button,
+.sidebar-nav a,
+.sidebar-button {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-end;
+ min-height: 3.75rem;
+ width: var(--lfw);
+ background-color: var(--button-color-sidebar);
+ border-radius: 0;
+ border-bottom: var(--panel-border);
+ padding: var(--left-frame-padding);
+ text-decoration: none;
+ text-align: right;
+ word-break: break-all;
+ text-transform: uppercase;
+ color: black;
+}
+
+@media (max-width: 1300px) {
+ .lcars-button,
+ .buttons a,
+ .buttons button {
+ width: 200px;
+ height: 74px;
+ }
+}
+
+.panel-button {
+ display: flex;
+ justify-content: flex-end;
+ width: var(--lfw);
+ border-radius: 0;
+ padding: var(--left-frame-padding);
+ border-bottom: var(--panel-border);
+}
+
+.pan-0 /* use this if you're not mimicking an established panel */ {
+ height: 18vh;
+ max-height: 240px;
+ background-color: var(--button-color-sidebar);
+}
+
+.pan-4 {
+ height: 22vh;
+ max-height: 300px;
+ background-color: var(--panel-4-color) !important;
+}
+
+.pan-5 {
+ height: 4.25rem;
+ background-color: var(--panel-5-color) !important;
+ align-items: center;
+}
+
+.pan-6 {
+ height: 29vh;
+ max-height: 360px;
+ background-color: var(--panel-6-color) !important;
+}
+
+.pan-7 {
+ height: 27vh;
+ max-height: 350px;
+ background-color: var(--panel-7-color) !important;
+}
+
+.pan-8 {
+ height: 15vh;
+ background-color: var(--panel-8-color) !important;
+}
+
+.pan-10 {
+ height: 30vh;
+ background-color: var(--panel-10-color) !important;
+}
+
+.text-bottom {
+ align-items: flex-end;
+}
+
+#topBtn {
+ display: none;
+ position: fixed;
+ bottom: 0;
+ z-index: 99;
+ border-radius: 0;
+ border-top: var(--panel-border);
+ border-right: none;
+ border-bottom: var(--panel-border);
+ border-left: none;
+ outline: none;
+ width: var(--lfw);
+ padding: var(--left-frame-padding);
+ padding-bottom: 10vh;
+ background-color: var(--panel-top-button-color);
+ text-align: right;
+ line-height: 1;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: black;
+ cursor: pointer;
+}
+
+#topBtn:hover {
+ filter: brightness(115%);
+}
+
+#topBtn:active {
+ filter: brightness(80%);
+}
+
+@media (max-width: 525px) {
+ .sidebar-button,
+ .sidebar-nav a,
+ .sidebar-nav button,
+ .panel-button {
+ font-size: .75rem;
+ }
+
+ #topBtn {
+ padding-bottom: 6vh;
+ }
+}
+
+/* --- Horizontal bar panels & sidebar panels --- */
+
+.floor-text {
+ padding-top: .25rem;
+ font-size: 1.4rem;
+ text-align: right;
+ text-transform: uppercase;
+ color: var(--cool);
+}
+
+.bar-panel {
+ display: flex;
+ height: var(--bar-height);
+}
+
+.first-bar-panel {
+ margin-top: .5rem;
+}
+
+.bar-1,
+.bar-2,
+.bar-3,
+.bar-4,
+.bar-5,
+.bar-6,
+.bar-7,
+.bar-9,
+.bar-10 {
+ height: var(--bar-height);
+}
+
+.bar-1,
+.bar-2,
+.bar-3,
+.bar-4,
+.bar-6,
+.bar-7,
+.bar-8,
+.bar-9 {
+ border-right: var(--bar-border);
+}
+
+.bar-1 {
+ width: var(--bar-1-6-width);
+ background-color: var(--bar-1-color);
+}
+
+.bar-2 {
+ width: var(--bar-2-7-width);
+ background-color: var(--bar-2-color);
+}
+
+.bar-3 {
+ width: var(--bar-3-8-width);
+ background-color: var(--bar-3-color);
+}
+
+.bar-4 {
+ flex: 1;
+ background-color: var(--bar-4-color);
+}
+
+.bar-5 {
+ width: var(--bar-5-10-width);
+ background-color: var(--bar-5-color);
+}
+
+.bar-6 {
+ width: var(--bar-1-6-width);
+ background-color: var(--bar-6-color);
+}
+
+.bar-7 {
+ width: var(--bar-2-7-width);
+ background-color: var(--bar-7-color);
+}
+
+.bar-8 {
+ width: var(--bar-3-8-width);
+ height: 50%;
+ background-color: var(--bar-8-color);
+}
+
+.bar-9 {
+ flex: 1;
+ background-color: var(--bar-9-color);
+}
+
+.bar-10 {
+ width: var(--bar-5-10-width);
+ background-color: var(--bar-10-color);
+}
+
+/* LCARS bottom section */
+
+#gap {
+ margin-top: var(--divider-height);
+}
+
+.panel-3,
+.panel-4,
+.panel-5,
+.panel-6,
+.panel-7,
+.panel-8 {
+ border-bottom: var(--panel-border);
+}
+
+.panel-3,
+.panel-4,
+.panel-6,
+.panel-7,
+.panel-8,
+.panel-10 {
+ padding: var(--left-frame-padding);
+}
+
+.panel-4 {
+ display: flex;
+ align-items: flex-end;
+ justify-content: flex-end;
+ height: 22vh;
+ max-height: 300px;
+ background-color: var(--panel-4-color);
+}
+
+.panel-5 {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ height: 4.25rem;
+ padding: var(--left-frame-padding);
+ background-color: var(--panel-5-color);
+}
+
+.panel-6 {
+ height: 29vh;
+ max-height: 360px;
+ background-color: var(--panel-6-color);
+}
+
+.panel-7 {
+ height: 27vh;
+ max-height: 350px;
+ background-color: var(--panel-7-color);
+}
+
+.panel-8 {
+ height: 15vh;
+ background-color: var(--panel-8-color);
+}
+
+/* panel-9 height is fluid and governed by the amount of content on the page. Background color is inherited from :root --left-frame-color */
+
+.panel-9 {
+ min-height: 27vh;
+ padding: var(--left-frame-padding);
+}
+
+.panel-10 {
+ height: 30vh;
+ border-top: var(--panel-border);
+ background-color: var(--panel-10-color);
+}
+
+@media (max-width: 525px) {
+ .panel-4,
+ .panel-6,
+ .panel-7 {
+ height: 18vh;
+ }
+}
+
+.right-frame {
+ flex: 1;
+ position: relative;
+}
+
+.right-frame:before {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background: linear-gradient(to bottom right, var(--corner-color-bottom) 50%, black 50%);
+ position: absolute;
+ left: 0;
+ top: var(--bar-height);
+ z-index: -1;
+}
+
+.right-frame:after {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 60px;
+ background-color: black;
+ border-radius: var(--radius-content-bottom);
+ position: absolute;
+ left: 0;
+ top: var(--bar-height);
+ z-index: -1;
+}
+
+main {
+ padding-top: clamp(1rem, 4vw, 45px);
+ padding-bottom: .5rem;
+ padding-left: clamp(20px, 3vw, 50px);
+}
+
+main > *:first-child,
+article > *:first-child {
+ margin-top: 0;
+}
+
+main:has(.floor-heading) > *:nth-child(2) {
+ margin-top: 0;
+}
+
+.flexbox {
+ display: flex;
+ gap: 1.2rem;
+ flex-wrap: wrap;
+}
+
+.col {
+ flex: 1 1 360px;
+}
+
+.col > *:first-child {
+ margin-top: 0;
+}
+
+h1, h2, h3, h4 {
+ margin-block: 1.75rem;
+ font-weight: normal;
+ line-height: 1.2;
+ text-transform: uppercase;
+ text-box: trim-both cap alphabetic;
+}
+
+h1 {
+ font-size: clamp(1.5rem, 1.25rem + 3.5vw, 4rem);
+ text-align: right;
+ color: var(--h1-color);
+}
+
+h2 {
+ font-size: clamp(1.4rem, 1.1rem + 2.25vw, 2.3rem);
+ color: var(--h2-color);
+}
+
+h3 {
+ font-size: clamp(1.15rem, 1.05rem + 1.25vw, 1.875rem);
+ color: var(--h3-color);
+}
+
+h4 {
+ font-size: clamp(1.025rem, 1rem + 1.125vw, 1.575rem);
+ color: var(--h4-color);
+}
+
+.floor-heading {
+ display: flex;
+ justify-content: flex-end;
+ width: 100%;
+ position: fixed;
+ left: 50%;
+ transform: translate(-50%, 0);
+ bottom: 10px;
+ z-index:4;
+}
+
+.floor-heading > * {
+ margin-block: 0;
+ width: fit-content;
+ padding-inline: .5rem;
+ padding-bottom: .5rem;
+ background-color: black;
+}
+
+p {
+ margin-block: 1.75rem;
+ text-box: trim-both cap alphabetic;
+}
+
+.caption {
+ margin-top: -1rem;
+ text-align: center;
+ font-size: var(--sub-fonts);
+}
+
+.indent {
+ padding-left: 1.5rem;
+}
+
+.postmeta {
+ margin-block: 1.25rem;
+ text-align: right;
+ font-size: clamp(1.2rem, 0.88rem + 1.28vw, 1.6rem);
+ line-height: 1.2;
+ text-transform: uppercase;
+}
+
+article h1 {
+ margin-bottom: 0;
+}
+
+hr {
+ margin-block: 1.5rem;
+ height: 6px;
+ border: none;
+ background-color: var(--font-color);
+ border-radius: 3px;
+}
+
+blockquote {
+ margin-block: 1.75rem;
+ margin-left: 1.5rem;
+ padding-block: .25rem;
+ padding-left: 1.5rem;
+ position: relative;
+ text-box: trim-both cap alphabetic;
+}
+
+blockquote::before {
+ content: '';
+ display: block;
+ width: 10px;
+ height: 100%;
+ background-color: var(--font-color);
+ border-radius: 5px;
+ position: absolute;
+ left: 0;
+ top: 0;
+}
+
+blockquote > *:first-child {
+ margin-top: 0;
+}
+
+blockquote > * {
+ margin-bottom: 0;
+}
+
+iframe {
+ display: block;
+ width: 100%;
+ border: none;
+}
+
+.flush {
+ margin-top: -1rem;
+}
+
+.nomar {
+ margin-block: 0 !important;
+}
+
+.go-center {
+ text-align: center !important;
+}
+
+.go-right {
+ text-align: right !important;
+}
+
+.go-left {
+ text-align: left !important;
+}
+
+.go-big {
+ font-size: clamp(1.2rem, 1rem + 1vw, 1.275rem);
+}
+
+.uppercase {
+ text-transform: uppercase;
+}
+
+.strike {
+ text-decoration: line-through;
+ text-decoration-thickness: .15rem;
+}
+
+.now {
+ white-space: nowrap;
+}
+
+.big-sky {
+ margin-top: 5rem;
+}
+
+.smoke-glass {
+ opacity: .35;
+}
+
+strong {
+ font-weight: bold;
+}
+
+code {
+ font-family: monospace;
+ font-size: .9rem;
+ color: var(--code-color);
+}
+
+.code {
+ width: 100%;
+ min-height: 5rem;
+ padding-block: .5rem;
+ padding-inline: 1rem;
+ background-color: #232323;
+ border-color: #4c4c4c;
+ tab-size: 4;
+ font-family: monospace;
+ font-size: .85rem;
+ color: #dcdcdc;
+}
+
+@keyframes blink {
+ 0% {opacity: 0}
+ 49%{opacity: 0}
+ 50% {opacity: 1}
+}
+
+.blink-slow {
+ animation: blink 3500ms infinite;
+}
+
+.blink {
+ animation: blink 2s infinite;
+}
+
+.blink-fast {
+ animation: blink 1s infinite;
+}
+
+@keyframes pulse {
+ 0% {filter: brightness(1.0)}
+ 50% {filter: brightness(.25)}
+}
+
+.pulse {
+ animation: pulse 2s infinite;
+}
+
+.pulse-rate-high {
+ animation: pulse 1s infinite;
+}
+
+.pulse-rate-low {
+ animation: pulse 3s infinite;
+}
+
+.accordion-wrapper {
+ display: block;
+ margin: 1.75rem auto;
+ width: 100%;
+}
+
+.limit-width {
+ max-width: 1240px;
+}
+
+.accordion {
+ display: flex;
+ align-items: center;
+ min-height: 3rem;
+ width: 100%;
+ padding-right: 2.75rem;
+ padding-left: 1rem;
+ border-radius: 100vmax;
+ background-color: var(--cool);
+ border-left: solid 3rem var(--evening);
+ text-align: left;
+ font-size: 1.25rem;
+ color: black;
+ cursor: pointer;
+ transition: 0.4s ease;
+ position: relative;
+}
+
+.active, .accordion:hover {
+ background-color: var(--ghost); /* 8bf */
+ border-left-color: var(--moonbeam);
+ filter: none;
+}
+
+.accordion:before {
+ content: '';
+ display: block;
+ width: .5rem;
+ height: 4rem;
+ background-color: black;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.accordion:after {
+ display: block;
+ content: '\276F';
+ position: absolute;
+ right: 1.5rem;
+ top: 21%;
+ transform: rotate(90deg);
+ transition: 0.4s;
+ font-weight: bold;
+ color: black;
+}
+
+.active:after {
+ content: "\276F";
+ transform:rotate(-90deg);
+ transition: 0.4s ease;
+}
+
+.accordionContent {
+ padding-block: .5rem;
+ padding-inline: 3.5rem;
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 0.25s ease-out;
+}
+
+.accordionContent ul {
+ margin-left: 0;
+}
+
+@media (max-width: 525px) {
+ .accordion {
+ min-height: 2.5rem;
+ font-size: 1rem;
+ border-left-width: 2.5rem;
+ }
+}
+
+/* Images */
+
+.pics-right {
+ float: right;
+ margin-inline: 1rem;
+ margin-bottom: 2rem;
+ max-width: 500px;
+}
+
+.pics-left {
+ float: left;
+ margin-inline: 1rem;
+ margin-bottom: 2rem;
+ max-width: 500px;
+}
+
+@media (max-width: 1060px) {
+ .pics-right,
+ .pics-left {
+ float: none;
+ margin-inline: auto;
+ }
+
+ .pics-right img,
+ .pics-left img {
+ margin-inline: auto;
+ }
+}
+
+.pics {
+ margin-block: 2rem;
+ margin-inline: auto;
+}
+
+.border {
+ border: 2px solid var(--font-color);
+}
+
+.lcars-list {
+ margin-block: 1.15rem;
+ margin-left: 2rem;
+ list-style: none;
+}
+
+.lcars-list li {
+ position: relative;
+ padding-block: .45rem;
+ padding-left: 2.25rem;
+ text-box: trim-both cap alphabetic;
+}
+
+.lcars-list li::before {
+ content: '';
+ display: block;
+ width: 34px;
+ height: 18px;
+ border-radius: 50%;
+ background-color: var(--font-color);
+ position: absolute;
+ top: .45rem;
+ left: 0;
+}
+
+@media (max-width: 650px) {
+
+ .lcars-list {
+ margin-left: .5rem;
+ }
+
+ .lcars-list li::before {
+ transform: scale(90%);
+ }
+}
+
+.lcars-bar {
+ margin-block: 1.75rem;
+ height: clamp(15px, 2vh, 25px);
+ background: transparent;
+ border-right: clamp(15px, 2vh, 25px) solid var(--lcars-bar-end-color);
+ border-left: clamp(15px, 2vh, 25px) solid var(--lcars-bar-start-color);
+ border-radius: 100vmax;
+ position: relative;
+}
+
+.lcars-bar::after {
+ content: '';
+ display: block;
+ height: clamp(15px, 2vh, 25px);
+ width: 100%;
+ background-color: var(--lcars-bar-color);
+ border-right: .8vh solid black;
+ border-left: .8vh solid black;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.lcars-text-bar {
+ display: flex;
+ position: relative;
+ height: clamp(16px, 4vh, 41px);
+ margin-block: 2.75rem;
+ overflow: visible;
+ border-radius: 100vmax;
+ background-color: var(--lcars-bar-color);
+ border-right: clamp(16px, 4vh, 43px) solid var(--lcars-bar-end-color);
+ border-left: clamp(16px, 4vh, 43px) solid var(--lcars-bar-start-color);
+}
+
+.the-end {
+ justify-content: flex-end;
+}
+
+.lcars-text-bar h2,
+.lcars-text-bar h3,
+.lcars-text-bar h4,
+.lcars-text-bar span {
+ margin-block: 0;
+ background-color: black;
+ height: clamp(20px, 5.5vh, 60px); /* Setting height is a Firefox fix */
+ overflow: visible;
+ border-top: 1px solid black;
+ padding-inline: 1vh;
+ font-size: clamp(17px, 4.5vh, 46px);
+ line-height: 1;
+ text-transform: uppercase;
+ color: var(--lcars-bar-text-color);
+ z-index: 1;
+ text-box: trim-both cap alphabetic;
+}
+
+.lcars-text-bar::before {
+ content: '';
+ background-color: black;
+ display: block;
+ width: 1vh;
+ height: 6vh;
+ position: absolute;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+}
+
+.lcars-text-bar::after {
+ content: '';
+ background-color: black;
+ display: block;
+ width: 1vh;
+ height: 6vh;
+ position: absolute;
+ top: 0;
+ right: 0;
+ overflow: hidden;
+}
+
+/* Image Frame */
+
+.image-frame {
+ display: block;
+ margin: 2.75rem auto;
+ width: fit-content;
+ background: linear-gradient(var(--primary-color) 56%, var(--secondary-color) 44%);
+ border-radius: 50px 25px 0 50px;
+ position: relative;
+}
+
+.image-frame::before {
+ content: '';
+ display: block;
+ width: 40px;
+ height: 40px;
+ background-color: black;
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+.image-frame::after {
+ content: '';
+ display: block;
+ border-top: var(--spacers) solid black;
+ border-bottom: var(--spacers) solid black;
+ width: 45px;
+ height: 80px;
+ background-color: var(--secondary-color);
+ position: absolute;
+ left: 0;
+ top: 56%;
+}
+
+.imgf-title {
+ display: flex;
+ justify-content: flex-end;
+ height: 40px;
+ border-right: 40px solid var(--secondary-color);
+ border-radius: 25px 100vh 100vh 0;
+ position: relative;
+ z-index: 1;
+ text-align: right;
+}
+
+.h4-wrapper {
+ width: fit-content;
+ height: var(--frame-height);
+ background-color: black;
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-title h4 {
+ margin-block: 0;
+ width: fit-content;
+ background-color: black;
+ padding-left: var(--spacers);
+ font-size: 2.05rem;
+ color: var(--title-color);
+ line-height: 40px;
+ text-transform: uppercase;
+}
+
+.imgf-image-body {
+ margin-left: 45px;
+ background-color: black;
+ width: fit-content;
+ padding: 1rem;
+ border-radius: 28px 0 0 28px;
+
+}
+
+.image-holder {
+ width: fit-content;
+ padding: 1rem;
+ border: 2px solid var(--image-border-color);
+ border-radius: 20px;
+}
+
+.imgf-base {
+ display: grid;
+ grid-template-columns: 20% 13% 35px 15% 1fr;
+ margin-left: 80px;
+ border-left: var(--spacers) solid black;
+
+}
+
+.imgf-block-1 {
+ height: var(--frame-height);
+ background-color: var(--accent-color);
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-block-2 {
+ height: var(--frame-height);
+ background-color: var(--secondary-color);
+ border-right: var(--spacers) solid black;
+}
+
+.imgf-block-3 {
+ height: var(--frame-height);
+ background-color: black;
+ border-right: 10px solid var(--secondary-color);
+ border-left: 10px solid var(--accent-color);
+}
+
+.imgf-block-4 {
+ height: var(--frame-height);
+ background-color: var(--secondary-color);
+ border-left: var(--spacers) solid black;
+}
+
+.imgf-block-5 {
+ height: var(--frame-height);
+ background-color: black;
+}
+
+@media (max-width: 750px) {
+
+ .image-frame {
+ border-radius: 40px 25px 0 40px;
+ }
+
+ .image-frame::after {
+ width: 25px;
+ height: 60px;
+ top: 50%;
+ }
+
+ .imgf-title {
+ height: 25px;
+ border-right: 24px solid var(--secondary-color);
+ }
+
+ .h4-wrapper {
+ width: fit-content;
+ height: var(--frame-height);
+ background-color: black;
+ }
+
+ .imgf-title h4 {
+ font-size: 1.45rem;
+ }
+
+ .imgf-image-body {
+ margin-left: 25px;
+ padding: .75rem;
+ border-radius: 20px 0 0 20px;
+ }
+
+ .image-holder {
+ padding: .75rem;
+ border-radius: 10px;
+ }
+}
+
+/* color styles */
+
+.font-cardinal {
+ color: var(--cardinal) !important;
+}
+
+.button-cardinal,
+.background-cardinal,
+.bullet-cardinal::before {
+ background-color: var(--cardinal) !important;
+}
+
+.font-cool {
+ color: var(--cool) !important;
+}
+
+.button-cool,
+.background-cool,
+.bullet-cool::before {
+ background-color: var(--cool) !important;
+}
+
+.font-evening {
+ color: var(--evening) !important;
+}
+
+.button-evening,
+.background-evening,
+.bullet-evening::before {
+ background-color: var(--evening) !important;
+}
+
+.font-galaxy-gray {
+ color: var(--galaxy-gray) !important;
+}
+
+.button-galaxy-gray,
+.background-galaxy-gray,
+.bullet-galaxy-gray::before {
+ background-color: var(--galaxy-gray) !important;
+}
+
+.font-ghost {
+ color: var(--ghost) !important;
+}
+
+.button-ghost,
+.background-ghost,
+.bullet-ghost::before {
+ background-color: var(--ghost) !important;
+}
+
+.font-grape {
+ color: var(--grape) !important;
+}
+
+.button-grape,
+.background-grape,
+.bullet-grape::before {
+ background-color: var(--grape) !important;
+}
+
+.font-honey {
+ color: var(--honey) !important;
+}
+
+.button-honey,
+.background-honey,
+.bullet-honey::before {
+ background-color: var(--honey) !important;
+}
+
+.font-lawn {
+ color: var(--lawn) !important;
+}
+
+.button-lawn,
+.background-lawn,
+.bullet-lawn::before {
+ background-color: var(--lawn) !important;
+}
+
+.font-martian {
+ color: var(--martian) !important;
+}
+
+.button-martian,
+.background-martian,
+.bullet-martian::before {
+ background-color: var(--martian) !important;
+}
+
+.font-midnight {
+ color: var(--midnight) !important;
+}
+
+.button-midnight,
+.background-midnight,
+.bullet-midnight::before {
+ background-color: var(--midnight) !important;
+}
+
+.font-moonbeam {
+ color: var(--moonbeam) !important;
+}
+
+.button-moonbeam,
+.background-moonbeam,
+.bullet-moonbeam::before {
+ background-color: var(--moonbeam) !important;
+}
+
+.font-pumpkinshade {
+ color: var(--pumpkinshade) !important;
+}
+
+.button-pumpkinshade,
+.background-pumpkinshade,
+.bullet-pumpkinshade::before {
+ background-color: var(--pumpkinshade) !important;
+}
+
+.font-roseblush {
+ color: var(--roseblush) !important;
+}
+
+.button-roseblush,
+.background-roseblush,
+.bullet-roseblush::before {
+ background-color: var(--roseblush) !important;
+}
+
+.font-tangerine {
+ color: var(--tangerine) !important;
+}
+
+.button-tangerine,
+.background-tangerine,
+.bullet-tangerine::before {
+ background-color: var(--tangerine) !important;
+}
+
+.font-wheat {
+ color: var(--wheat) !important;
+}
+
+.button-wheat,
+.background-wheat,
+.bullet-wheat::before {
+ background-color: var(--wheat) !important;
+}
+
+/* Footer */
+
+footer {
+ margin-top: 2.5vw;
+ padding-bottom: 3rem;
+ padding-left: clamp(20px, 3vw, 50px);
+ font-size: .875rem;
+}
+
+.meta-data {
+ margin-bottom: 1rem;
+ width: fit-content;
+ font-size: 1.25rem;
+ border-right: 24px solid var(--cool);
+ border-left: 24px solid var(--cool);
+ border-radius: 0 100vmax 100vmax 0;
+ padding-inline: .375rem;
+ position: relative;
+ line-height: 1;
+ text-box: trim-both cap alphabetic;
+}
+
+.meta-data:before {
+ content: '';
+ display: block;
+ width: 5px;
+ height: 2rem;
+ background-color: black;
+ position: absolute;
+ top: 0;
+ left: -20px;
+}
+
+.headtrim {
+ height: 10px;
+ width: 100%;
+ background-color: black;
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 999;
+}
+
+.baseboard {
+ height: 10px;
+ width: 100%;
+ background-color: black;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ z-index: 999;
+}
+
+/* Grouped @media */
+
+@media (max-width: 525px) {
+
+ body {
+ padding: .25rem;
+ }
+
+ .baseboard {
+ display: none;
+ }
+
+ .wrap {
+ padding-left: 0;
+ padding-right: .25rem;
+ }
+
+ .left-frame {
+ padding-top: 25px;
+ }
+
+ .floor-heading {
+ bottom: 0;
+ }
+
+ .hop {
+ display: none;
+ }
+
+ .floor-text {
+ font-size: 1.125rem;
+ }
+
+ blockquote {
+ margin-left: .75rem;
+ }
+}
+
+@-moz-document url-prefix() {
+
+ main {
+ padding-top: clamp(1rem, 4vw, 30px);
+ }
+
+ h1, h2, h3, h4, p {
+ margin-block: 1.1rem;
+ }
+
+ .lcars-text-bar h2,
+ .lcars-text-bar span {
+ position: absolute;
+ top: -.6vh;
+ }
+
+ .meta-data {
+ height: 1.15rem;
+ line-height: 1rem;
+ }
+}
+
+@-moz-document url-prefix() {
+
+ main {
+ padding-top: clamp(1rem, 4vw, 30px);
+ }
+
+ h1, h2, h3, h4, p {
+ margin-block: 1.15rem;
+ }
+
+ .imgf-title h4 {
+ margin-top: -4px;
+ margin-bottom: 0;
+ }
+
+ .postmeta {
+ margin-top: .75rem;
+ }
+
+ .lcars-text-bar h2,
+ .lcars-text-bar h3,
+ .lcars-text-bar h4,
+ .lcars-text-bar span {
+ position: absolute;
+ top: -.7vh;
+ }
+
+ .lcars-list li::before {
+ top: .85rem;
+ }
+
+ .meta-data {
+ height: 1.15rem;
+ line-height: 1rem;
+ }
+}
\ No newline at end of file
diff --git a/lcars/frontend/index copy 2.html b/lcars/frontend/index copy 2.html
new file mode 100644
index 0000000..c80719d
--- /dev/null
+++ b/lcars/frontend/index copy 2.html
@@ -0,0 +1,321 @@
+
+
+
+
+ Lower Decks PADD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
03-111968
+
04-041969
+
05-1701D
+
06-071984
+
+
+
+
+
+
+
+
+
+
+
+ CRITICAL: Core logic node recursion detected! ACK
+ required!
+
+
+
+
+
+
+ ALERT: Warp coil 7C emitting sparks
+
+
+
+
+
+
+ WARNING: Deck 4 humidity spike detected
+
+
+
+
+
+
+ NOTICE: Deck 8 greenhouse light cycling
+
+
+
+
+
+
+ INFO: Crewman Mariner spilled coffee
+
+
+
+
+
+
+ CRITICAL: Warp field instability detected! Immediate
+ action!
+
+
+
+
+
+
+ ALERT: Transporter buffer overload
+
+
+
+
+
+
+ WARNING: Engineering turbolift malfunctioning
+
+
+
+
+
+
+ NOTICE: Hydration levels on Deck 5 nominal
+
+
+
+
+
+
+ INFO: Crewman Boimler practicing tap-dancing in
+ holodeck
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lcars/frontend/index copy.html b/lcars/frontend/index copy.html
new file mode 100644
index 0000000..1583566
--- /dev/null
+++ b/lcars/frontend/index copy.html
@@ -0,0 +1,235 @@
+
+
+
+ Lower Decks PADD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
03-111968
+
04-041969
+
05-1701D
+
06-071984
+
+
+
+
+
+
+
+
+
+ Hello
+ Welcome to LCARS Lower Decks PADD Theme
+ Version 24.2
+ Replace This Content With Your Own
+ Live long and prosper.
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lcars/frontend/index.html b/lcars/frontend/index.html
new file mode 100644
index 0000000..1963f18
--- /dev/null
+++ b/lcars/frontend/index.html
@@ -0,0 +1,345 @@
+
+
+
+
+ Lower Decks PADD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
03-111968
+
04-041969
+
05-1701D
+
06-071984
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CRITICAL: Core logic node recursion detected! ACK
+ required!
+
+
×1
+
+
+
+
+
+
+
+
+ ALERT: Warp coil 7C emitting sparks
+
+
×1
+
+
+
+
+
+
+
+
+ WARNING: Deck 4 humidity spike detected
+
+
×1
+
+
+
+
+
+
+
+
+ NOTICE: Deck 8 greenhouse light cycling
+
+
×1
+
+
+
+
+
+
+
+
+ INFO: Crewman Mariner spilled coffee
+
+
×1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lcars/go.mod b/lcars/go.mod
new file mode 100644
index 0000000..5a773da
--- /dev/null
+++ b/lcars/go.mod
@@ -0,0 +1,18 @@
+module ld
+
+go 1.25.0
+
+require modernc.org/sqlite v1.39.0
+
+require (
+ github.com/dustin/go-humanize v1.0.1 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/ncruces/go-strftime v0.1.9 // indirect
+ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
+ golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
+ golang.org/x/sys v0.34.0 // indirect
+ modernc.org/libc v1.66.3 // indirect
+ modernc.org/mathutil v1.7.1 // indirect
+ modernc.org/memory v1.11.0 // indirect
+)
diff --git a/lcars/go.sum b/lcars/go.sum
new file mode 100644
index 0000000..c46fb2d
--- /dev/null
+++ b/lcars/go.sum
@@ -0,0 +1,49 @@
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
+github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
+github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
+golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
+golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
+golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
+golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
+golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
+golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
+golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
+modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
+modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
+modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
+modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
+modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
+modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
+modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
+modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
+modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
+modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
+modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
+modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
+modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
+modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
+modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
+modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
+modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
+modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
+modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
+modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
+modernc.org/sqlite v1.39.0 h1:6bwu9Ooim0yVYA7IZn9demiQk/Ejp0BtTjBWFLymSeY=
+modernc.org/sqlite v1.39.0/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
+modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
+modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
+modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
diff --git a/lcars/interval/interval.go b/lcars/interval/interval.go
new file mode 100644
index 0000000..e755d5e
--- /dev/null
+++ b/lcars/interval/interval.go
@@ -0,0 +1,94 @@
+package interval
+
+import (
+ "errors"
+ "math/rand"
+ "sync"
+ "time"
+)
+
+type MutexMap[K comparable, V any] struct {
+ mutex sync.Mutex
+ m map[K]V
+}
+
+func (m *MutexMap[K, V]) Get(key K) (V, error) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ v, ok := m.m[key]
+ if !ok {
+ return v, errors.New("unknown key")
+ }
+ return v, nil
+}
+
+func (m *MutexMap[K, V]) Set(key K, value V) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ m.m[key] = value
+}
+
+func (m *MutexMap[K, V]) Delete(key K) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ delete(m.m, key)
+}
+
+var stopChannels = MutexMap[int, chan bool]{
+ m: make(map[int]chan bool),
+}
+
+// SetInterval schedules a repeating task to be executed at a specified interval.
+func SetInterval(f func(), milliseconds int) (id int) {
+ for {
+ id = rand.Int()
+ if _, err := stopChannels.Get(id); err == nil {
+ continue // ID collision, keep looking for another unique random value
+ }
+ break
+ }
+
+ stop := make(chan bool)
+ stopChannels.Set(id, stop)
+
+ ticker := time.NewTicker(time.Duration(milliseconds) * time.Millisecond)
+
+ go func() {
+ for {
+ select {
+ case <-stop:
+ ticker.Stop()
+ return
+ case <-ticker.C:
+ f()
+ }
+ }
+ }()
+
+ return
+}
+
+// ClearInterval stops a scheduled interval identified by the specified interval ID.
+func ClearInterval(id int) error {
+ stop, err := stopChannels.Get(id)
+ if err != nil {
+ return err
+ }
+ stop <- true
+ stopChannels.Delete(id)
+
+ return nil
+}
+
+// SetTimeout schedules a one-time task to be executed after a specified interval.
+func SetTimeout(f func(), milliseconds int) {
+ timer := time.NewTimer(time.Duration(milliseconds) * time.Millisecond)
+ go func() {
+ <-timer.C
+ timer.Stop()
+ f()
+ }()
+}
diff --git a/lcars/ld.log b/lcars/ld.log
new file mode 100644
index 0000000..e69de29
diff --git a/lcars/main.go b/lcars/main.go
new file mode 100644
index 0000000..8bf7efc
--- /dev/null
+++ b/lcars/main.go
@@ -0,0 +1,153 @@
+package main
+
+import (
+ "embed"
+ "fmt"
+ "ld/server"
+ "ld/sqlite"
+ "log"
+ "os"
+ "os/signal"
+ "path/filepath"
+ "strings"
+ "syscall"
+)
+
+const (
+ exitCodeErr = 1
+ exitCodeInterrupt = 2
+)
+
+var AppRoot = "./" // path for supporting files that sit in app root folder in production
+
+//go:embed frontend/*
+var frontend embed.FS
+
+//go:embed embed
+var embedded embed.FS
+
+// main specific variables
+var ExecutableName string
+
+func main() {
+ fmt.Println("Here")
+ err := run()
+ if err != nil {
+ os.Exit(exitCodeErr)
+ }
+ ExecutableName, err = getExecutableName()
+ if err != nil {
+ os.Exit(exitCodeErr)
+ }
+
+ readCrew()
+
+ readMessages()
+
+ run()
+
+}
+
+func run() error {
+
+ // setting up logging to file
+ logFileName := AppRoot + ExecutableName + ".log"
+ logFile, err := os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
+ if err != nil {
+ log.Printf("error opening file: %v", err)
+ os.Exit(exitCodeErr)
+ }
+ defer logFile.Close()
+ log.SetOutput(logFile)
+
+ stateDB, err := createStateDB(true)
+ if err != nil {
+ log.Fatalf("Failed to create internal StateDB: %v", err)
+ }
+
+ return nil
+ // setting up the server
+ server, err := server.New(
+ logFileName,
+ stateDB,
+ embedded,
+ )
+
+ if err != nil {
+ log.Fatalf("server-app not created - error: %v", err)
+ }
+
+ // tasks.SetupTasks(server)
+
+ err = server.Start()
+ if err != nil {
+ fmt.Printf("server not started - error: %v \n", err)
+ log.Fatalf("server not started - error: %v", err)
+ }
+
+ // listen for os shutdown events, report them into log file and exit application
+ chanOS := make(chan os.Signal, 2)
+ signal.Notify(chanOS, os.Interrupt, syscall.SIGTERM)
+ go func() {
+ <-chanOS
+ log.Println("shutting down request signal received")
+ server.Close()
+ os.Exit(exitCodeInterrupt)
+ }()
+
+ // setting up the routes, hooking up API endpoints with backend functions
+ // routes.SetupRoutes(server)
+
+ return nil
+
+}
+
+// some internal functions
+
+func getExecutableName() (string, error) {
+ name, err := os.Executable()
+ if err != nil {
+ return "", err
+ }
+ name = filepath.Base(name)
+ return strings.TrimSuffix(name, filepath.Ext(name)), nil
+}
+
+func createStateDB(StateDBDelete bool) (*sqlite.Database, error) {
+
+ // fileName := fmt.Sprintf("state-%s.db", ulid.Make())
+
+ fileName := "state.db"
+
+ if StateDBDelete {
+ _, err := os.Stat(fileName)
+ if err == nil {
+ err := os.Remove(fileName)
+ if err != nil {
+ log.Fatal("error deleting statedb-file:", err)
+ }
+ }
+ }
+
+ db, err := sqlite.New(fileName)
+ if err != nil {
+ return nil, err
+ }
+
+ err = db.Open()
+ if err != nil {
+ return nil, err
+ }
+
+ query, err := embedded.ReadFile("embed/create_state_db.sql")
+ if err != nil {
+ return nil, err
+ }
+
+ _, err = db.DB().Exec(string(query))
+ if err != nil {
+ return nil, err
+ }
+
+ return db, nil
+}
diff --git a/lcars/server/server.go b/lcars/server/server.go
new file mode 100644
index 0000000..8215953
--- /dev/null
+++ b/lcars/server/server.go
@@ -0,0 +1,103 @@
+// Copyright 2024 codeM GmbH
+// Author: Thomas Hedeler
+package server
+
+import (
+ "embed"
+ "ld/interval"
+ "ld/sqlite"
+ "log"
+)
+
+type Server struct {
+ SQLiteVersion string
+ ServerInfo map[string]any
+ StateDB *sqlite.Database
+ Embedded embed.FS
+ LogFileName string
+ TokenDuration int // TODO einbauen
+ Secret []byte
+ Header string
+ intervalID int
+ Tasks map[string]TaskFunc
+}
+
+func New(
+ logfilename string,
+ StateDB *sqlite.Database,
+ embedded embed.FS,
+
+) (*Server, error) {
+
+ // creating the server
+ return &Server{
+ LogFileName: logfilename,
+ StateDB: StateDB,
+ Embedded: embedded,
+ Tasks: make(map[string]func(s *Server) error),
+ }, nil
+}
+
+func (s *Server) Start() error {
+
+ // query, err := s.Embedded.ReadFile("embed/win/server_info.sql")
+ // if err != nil {
+ // return err
+ // }
+
+ // res, err := s.StundenDB.ReadRecords(string(query))
+ // if err != nil {
+ // return err
+ // }
+
+ // s.ServerInfo = res[0]
+
+ // err = inits.LoadLogins(s.StundenDB, s.StateDB)
+ // if err != nil {
+ // return err
+ // }
+
+ // err = inits.LoadTasks(s.StundenDB, s.StateDB)
+ // if err != nil {
+ // return err
+ // }
+
+ // // start the task engine
+ // if s.Production {
+ // s.intervalID = interval.SetInterval(s.interval, 60000) // check for executable tasks every 60 seconds
+ // } else {
+ // s.intervalID = interval.SetInterval(s.interval, 30000) // check for executable tasks every 30 seconds
+ // }
+ return nil
+}
+
+func (s *Server) Close() error {
+
+ // stop the task engine
+ err := interval.ClearInterval(s.intervalID)
+ if err != nil {
+ log.Print(err)
+ }
+
+ err = s.StateDB.Close()
+ if err != nil {
+ log.Print(err)
+ }
+ return err
+}
+
+// func to dispatch routes to all parts of the application:
+// they receive references to the server and the current fiber context via closures
+// this way all functions have access to server properties and can handle the
+// incoming requests themselves.
+
+// type HandlerFunc = func(s *Server, c *fiber.Ctx) error
+
+// func (s *Server) Handler(handler HandlerFunc) func(c *fiber.Ctx) error {
+// return func(c *fiber.Ctx) error {
+// return handler(s, c)
+// }
+// }
+
+// signature for internal tasks
+type TaskFunc = func(s *Server) error
diff --git a/lcars/server/taskengine.go b/lcars/server/taskengine.go
new file mode 100644
index 0000000..7062e81
--- /dev/null
+++ b/lcars/server/taskengine.go
@@ -0,0 +1,118 @@
+package server
+
+import (
+ "log"
+ "time"
+)
+
+// this function schedules the tasks and will be called periodically, see server.Start()
+func (s *Server) interval() {
+
+ // read scheduled task list from stateDB
+ // check for next executable task:
+ // - if there is one or more tasks ready for execution then select one of them.
+ // - if there is a selected task, update its next execution field and execute the task
+
+ defer func() {
+ if r := recover(); r != nil {
+ // fmt.Println("Recovered from panic in worker:", r)
+ log.Printf("recovered from panic in taskengine: %v ", r)
+ }
+ }()
+
+ tasks, err := s.StateDB.ReadRecords("SELECT * FROM tasks ORDER by next_execution limit 1;")
+
+ if err != nil {
+ log.Printf("error in taskengine: %s ", err)
+ return
+ }
+
+ if len(tasks) < 1 {
+ log.Printf("error in taskengine: %s ", "found no task for execution")
+ return
+ }
+
+ task := tasks[0] // pick the one task with the smallest next execution time, see previous sql statement
+
+ task_name, haveTask := task["task_name"].(string)
+ if !haveTask {
+ log.Printf("error in taskengine: task %s is of wrong type", task["task_name"])
+ return
+ }
+
+ nextExecution := task["next_execution"].(int64)
+ startTime := task["start_time"].(int64)
+ execInterval := task["interval"].(int64)
+ nowSeconds := time.Now().Unix()
+
+ if nowSeconds < nextExecution { // task execution is not yet due
+ return
+ }
+
+ // calculate next execution time
+ for nextExecution = startTime; nowSeconds > nextExecution; nextExecution += execInterval {
+ // add as many intervals to the starttime until the next execution lies in the future
+ }
+
+ task["start_time"] = task["next_execution"]
+ task["next_execution"] = nextExecution
+
+ /*
+ no_executions INTEGER, -- how often executed
+ duration INTEGER, -- duration of the last exec in ms
+ no_errors INTEGER, -- error count
+ last_error_text TEXT,
+
+ */
+
+ if count, ok := task["no_executions"].(int64); ok {
+ task["no_executions"] = count + 1
+ }
+
+ // update next_execution in state database
+ _, err = s.StateDB.UpsertRecord("tasks", "task_id", task)
+ if err != nil {
+ log.Printf("error in taskengine: cannot update task record - before execution %s ", err)
+ return
+ }
+
+ task_func, haveTask := s.Tasks[task_name] // select the function with the matching name
+
+ if !haveTask {
+ log.Printf("error in taskengine: task %s is not defined", task_name)
+ }
+
+ if haveTask {
+
+ start := time.Now()
+ // if !s.Production {
+ // fmt.Println("Taskengine: executing task:", task_name, start)
+ // }
+ err = task_func(s) // finally execute the task; attention: a task that panics will kill the server!
+
+ task["duration"] = int(time.Since(start).Milliseconds())
+
+ if err != nil {
+ log.Printf("taskengine: execution task: %s failed with error: %s ", task_name, err)
+ task["last_error_text"] = err.Error()
+ if count, ok := task["no_errors"].(int64); ok {
+ task["no_errors"] = count + 1
+ }
+ // if !s.Production {
+ // fmt.Println("Taskengine: failed task:", task_name, err)
+ // }
+ } else {
+ // if !s.Production {
+ // fmt.Println("Taskengine: successfully completed task:", task_name, time.Now())
+ // }
+ }
+
+ _, err = s.StateDB.UpsertRecord("tasks", "task_id", task)
+ if err != nil {
+ log.Printf("error in taskengine: cannot update task record - after execution %s ", err)
+ return
+ }
+
+ }
+
+}
diff --git a/lcars/sqlite/database.go b/lcars/sqlite/database.go
new file mode 100644
index 0000000..f3c4b83
--- /dev/null
+++ b/lcars/sqlite/database.go
@@ -0,0 +1,428 @@
+package sqlite // name the package as you see fit
+
+import (
+ "database/sql"
+ "errors"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+
+ _ "modernc.org/sqlite"
+)
+
+// This is the data type to exchange data with the database
+type Record = map[string]any
+
+type Database struct {
+ databaseName string
+ database *sql.DB
+}
+
+type Transaction struct {
+ tx *sql.Tx
+ err error
+}
+
+type Action func(tx *sql.Tx) error
+
+func New(DBName string) (*Database, error) {
+ return &Database{databaseName: DBName}, nil
+}
+
+func (d *Database) Close() error {
+ return d.database.Close()
+}
+
+// provides access to the internal database object
+func (d *Database) DB() *sql.DB {
+ return d.database
+}
+
+func (d *Database) Name() string {
+ return d.databaseName
+}
+
+func (d *Database) Open() (err error) {
+ d.database, err = openSqliteDB(d.databaseName)
+ return err
+}
+
+func (d *Database) OpenInMemory() (err error) {
+ d.database, err = sql.Open("sqlite", ":memory:")
+ return err
+}
+
+func openSqliteDB(databasefilename string) (*sql.DB, error) {
+
+ _, err := os.Stat(databasefilename)
+ if errors.Is(err, os.ErrNotExist) {
+ return createDB(databasefilename)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return sql.Open("sqlite", databasefilename)
+
+}
+
+func createDB(dbfileName string) (*sql.DB, error) {
+
+ query := `
+ PRAGMA page_size = 4096;
+ PRAGMA synchronous = off;
+ PRAGMA foreign_keys = off;
+ PRAGMA journal_mode = WAL;
+ PRAGMA user_version = 1;
+ `
+ db, err := sql.Open("sqlite", dbfileName)
+ if err != nil {
+
+ return nil, err
+ }
+ _, err = db.Exec(query)
+ if err != nil {
+ return nil, err
+ }
+ return db, nil
+}
+
+func (d *Database) TableList() (result []Record, err error) {
+ return d.ReadRecords("select name from sqlite_master where type='table';")
+}
+
+func (d *Database) ReadTable(tablename string) (result []Record, err error) {
+
+ return d.ReadRecords(fmt.Sprintf("select * from '%s';", tablename))
+}
+
+func (d *Database) ReadRecords(query string, args ...any) (result []Record, err error) {
+
+ rows, err := d.DB().Query(query, args...)
+ if err != nil {
+ return result, err
+ }
+ defer rows.Close()
+ return Rows2records(rows)
+
+}
+
+func (d *Database) GetRecord(tablename string, idfield string, key any) (result Record, err error) {
+
+ query := fmt.Sprintf("select * from %s where %s = ?;", tablename, idfield)
+ res, err := d.DB().Query(query, key)
+ if err != nil {
+ return result, err
+ }
+ defer res.Close()
+ return Rows2record(res)
+
+}
+
+func (d *Database) UpsertRecord(tablename string, idfield string, record Record) (result Record, err error) {
+
+ return upsert(d.DB(), tablename, idfield, record)
+
+}
+
+func (d *Database) DeleteRecord(tablename string, idfield string, id any) (err error) {
+
+ return deleteRecord(d.DB(), tablename, idfield, id)
+
+}
+
+// *sql.DB and *sql.Tx both have a method named 'Query',
+// this way they can both be passed into upsert and deleteRecord function
+type iquery interface {
+ Query(query string, args ...any) (*sql.Rows, error)
+}
+
+func upsert(t iquery, tablename string, idfield string, record Record) (result Record, err error) {
+
+ fields := []string{}
+ data := []any{}
+ for k, v := range record {
+ fields = append(fields, k)
+ data = append(data, v)
+ }
+ query, err := buildUpsertCommand(tablename, idfield, fields)
+ if err != nil {
+ return result, err
+ }
+ res, err := t.Query(query, data...) // res contains the full record - see SQLite: RETURNING *
+ if err != nil {
+ return result, err
+ }
+ defer res.Close()
+ return Rows2record(res)
+
+}
+
+func deleteRecord(t iquery, tablename string, idfield string, id any) (err error) {
+
+ query := fmt.Sprintf("DELETE FROM \"%s\" WHERE \"%s\" = ?;", tablename, idfield)
+ _, err = t.Query(query, id)
+ return err
+
+}
+
+func buildUpsertCommand(tablename string, idfield string, fields []string) (string, error) {
+ var sb strings.Builder
+ sb.Grow(256 + len(fields)*20) // rough preallocation
+
+ // INSERT INTO
+ sb.WriteString(`INSERT INTO "`)
+ sb.WriteString(tablename)
+ sb.WriteString(`"(`)
+ for i, f := range fields {
+ sb.WriteString(` "`)
+ sb.WriteString(f)
+ sb.WriteByte('"')
+ if i < len(fields)-1 {
+ sb.WriteByte(',')
+ }
+ }
+ sb.WriteString(")\n\tVALUES(")
+
+ // VALUES
+ for i := 0; i < len(fields); i++ {
+ sb.WriteString(" ?")
+ sb.Write(strconv.AppendInt(nil, int64(i+1), 10))
+ if i < len(fields)-1 {
+ sb.WriteByte(',')
+ }
+ }
+ sb.WriteString(")\n\tON CONFLICT(\"")
+ sb.WriteString(tablename)
+ sb.WriteString(`"."`)
+ sb.WriteString(idfield)
+ sb.WriteString("\")\n\tDO UPDATE SET ")
+
+ // UPDATE SET
+ for i, f := range fields {
+ sb.WriteByte('"')
+ sb.WriteString(f)
+ sb.WriteString(`"= ?`)
+ sb.Write(strconv.AppendInt(nil, int64(i+1), 10))
+ if i < len(fields)-1 {
+ sb.WriteByte(',')
+ }
+ }
+ sb.WriteString("\n\tRETURNING *;")
+
+ return sb.String(), nil
+}
+
+// func buildUpsertCommand(tablename string, idfield string, fields []string) (result string, err error) {
+
+// pname := map[string]string{} // assign correct index for parameter name
+// // parameter position, starts at 1 in sql! So it needs to be calculated by function pname inside template
+
+// for i, k := range fields {
+// pname[k] = strconv.Itoa(i + 1)
+// }
+// funcMap := template.FuncMap{
+// "pname": func(fieldname string) string {
+// return pname[fieldname]
+// },
+// }
+// tableDef := struct {
+// Tablename string
+// KeyField string
+// LastField int
+// FieldNames []string
+// }{
+// Tablename: tablename,
+// KeyField: idfield,
+// LastField: len(fields) - 1,
+// FieldNames: fields,
+// }
+// var templString = `{{$last := .LastField}}INSERT INTO "{{ .Tablename }}"({{ range $i,$el := .FieldNames }} "{{$el}}"{{if ne $i $last}},{{end}}{{end}})
+// VALUES({{ range $i,$el := .FieldNames }} ?{{pname $el}}{{if ne $i $last}},{{end}}{{end}})
+// ON CONFLICT("{{ .Tablename }}"."{{.KeyField}}")
+// DO UPDATE SET {{ range $i,$el := .FieldNames }}"{{$el}}"= ?{{pname $el}}{{if ne $i $last}},{{end}}{{end}}
+// RETURNING *;`
+
+// dbTempl, err := template.New("upsertDB").Funcs(funcMap).Parse(templString)
+// if err != nil {
+// return result, err
+// }
+// var templBytes bytes.Buffer
+// err = dbTempl.Execute(&templBytes, tableDef)
+// if err != nil {
+// return result, err
+// }
+// return templBytes.String(), nil
+// }
+
+func Rows2record(rows *sql.Rows) (Record, error) {
+ columns, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+ values := make([]any, len(columns))
+ valuePtrs := make([]any, len(columns))
+ for i := range values {
+ valuePtrs[i] = &values[i]
+ }
+ result := Record{}
+ for rows.Next() {
+ if err := rows.Scan(valuePtrs...); err != nil {
+ return nil, err
+ }
+ for i, col := range columns {
+ result[col] = values[i]
+ }
+ }
+ if len(result) == 0 {
+ return nil, errors.New("no rows found")
+ }
+ return result, nil
+}
+
+func Rows2records(rows *sql.Rows) ([]Record, error) {
+ columns, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+ recLength := len(columns)
+ results := []Record{}
+ for rows.Next() {
+ values := make([]any, recLength)
+ valuePtrs := make([]any, recLength)
+ for i := range values {
+ valuePtrs[i] = &values[i]
+ }
+ record := Record{}
+ if err := rows.Scan(valuePtrs...); err != nil {
+ return nil, err
+ }
+ for i, col := range columns {
+ record[col] = values[i]
+ }
+ results = append(results, record)
+ }
+ if len(results) == 0 {
+ return nil, errors.New("no rows found")
+ }
+ return results, nil
+}
+
+func (d *Database) Version() (string, error) {
+ result := ""
+ sqliteversion, err := d.ReadRecords("SELECT sqlite_version();")
+ if len(sqliteversion) == 1 {
+ result = sqliteversion[0]["sqlite_version()"].(string)
+ }
+ return result, err
+}
+
+func (d *Database) UserVersion() (int64, error) {
+
+ var result int64
+ userversion, err := d.ReadRecords("PRAGMA user_version;")
+ if len(userversion) == 1 {
+ result = userversion[0]["user_version"].(int64)
+ }
+ return result, err
+}
+
+func (d *Database) Begin() *Transaction {
+ tx, err := d.database.Begin()
+ return &Transaction{tx, err}
+}
+
+func (t *Transaction) Next(action Action) *Transaction {
+ if t.err != nil {
+ return t
+ }
+ t.err = action(t.tx)
+ return t
+}
+
+func (t *Transaction) End() error {
+
+ if t.err != nil {
+ err := t.tx.Rollback()
+ if err != nil {
+ t.err = errors.Join(t.err, err)
+ }
+ return t.err
+ }
+ t.err = t.tx.Commit()
+ return t.err
+}
+
+func (t *Transaction) GetRecord(tablename string, idfield string, key any, output Record) *Transaction {
+
+ if t.err != nil {
+ return t
+ }
+ query := fmt.Sprintf("select * from %s where %s = ?;", tablename, idfield)
+ res, err := t.tx.Query(query, key)
+ if err != nil {
+ t.err = err
+ return t
+ }
+ defer res.Close()
+ result, err := Rows2record(res)
+ if err != nil {
+ t.err = err
+ return t
+ }
+ for k := range output {
+ delete(output, k)
+ }
+ for k, v := range result {
+ output[k] = v
+ }
+ return t
+}
+
+func (t *Transaction) UpsertRecord(tablename string, idfield string, record Record, output Record) *Transaction {
+
+ if t.err != nil {
+ return t
+ }
+ result, err := upsert(t.tx, tablename, idfield, record)
+ if err != nil {
+ t.err = err
+ return t
+ }
+ for k := range output {
+ delete(output, k)
+ }
+ for k, v := range result {
+ output[k] = v
+ }
+ return t
+}
+
+func (t *Transaction) DeleteRecord(tablename string, idfield string, id any) *Transaction {
+
+ if t.err != nil {
+ return t
+ }
+ err := deleteRecord(t.tx, tablename, idfield, id)
+ if err != nil {
+ t.err = err
+ }
+ return t
+}
+
+// returns a value of the provided type, if the field exist and if it can be cast into the provided type parameter
+func Value[T any](rec Record, field string) (value T, ok bool) {
+ var v any
+ if v, ok = rec[field]; ok {
+ value, ok = v.(T)
+ }
+ return
+}
+
+// don't report an error if there are simply just 'no rows found'
+func NoRowsOk(recs []Record, err error) ([]Record, error) {
+ if err != nil && err.Error() != "no rows found" {
+ return recs, err
+ }
+ return recs, nil
+}
diff --git a/lcars/state.db b/lcars/state.db
new file mode 100644
index 0000000..4db8e95
Binary files /dev/null and b/lcars/state.db differ
diff --git a/lcars/state.db-shm b/lcars/state.db-shm
new file mode 100644
index 0000000..22ce83b
Binary files /dev/null and b/lcars/state.db-shm differ
diff --git a/lcars/state.db-wal b/lcars/state.db-wal
new file mode 100644
index 0000000..25c0e12
Binary files /dev/null and b/lcars/state.db-wal differ