diff --git a/docker-compose.yml b/docker-compose.yml index 300cfc0..f8bcc78 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,6 +12,9 @@ services: - ./data/firebase-service-account.json:/app/firebase-service-account.json:ro ports: - "8000:8000" + depends_on: + postgres: + condition: service_healthy networks: - internal @@ -40,6 +43,24 @@ services: networks: - internal + postgres: + image: postgres:16-alpine + container_name: bellsystems-postgres + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - ./data/postgres:/var/lib/postgresql/data + networks: + - internal + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + networks: internal: driver: bridge diff --git a/strategies/Flash Page Rework/bellsystems-console-template/README.md b/strategies/Flash Page Rework/bellsystems-console-template/README.md new file mode 100644 index 0000000..92926d6 --- /dev/null +++ b/strategies/Flash Page Rework/bellsystems-console-template/README.md @@ -0,0 +1,22 @@ +# CODING AGENTS: READ THIS FIRST + +This is a **handoff bundle** from Claude Design (claude.ai/design). + +A user mocked up designs in HTML/CSS/JS using an AI design tool, then exported this bundle so a coding agent can implement the designs for real. + +## What you should do — IMPORTANT + +**Find the primary design file under `bellsystems-console-template/project/` and read it top to bottom.** Then **follow its imports**: open every file it pulls in (shared components, CSS, scripts) so you understand how the pieces fit together before you start implementing. + +**If anything is ambiguous, ask the user to confirm before you start implementing.** It's much cheaper to clarify scope up front than to build the wrong thing. + +## About the design files + +The design medium is **HTML/CSS/JS** — these are prototypes, not production code. Your job is to **recreate them pixel-perfectly** in whatever technology makes sense for the target codebase (React, Vue, native, whatever fits). Match the visual output; don't copy the prototype's internal structure unless it happens to fit. + +**Don't render these files in a browser or take screenshots unless the user asks you to.** Everything you need — dimensions, colors, layout rules — is spelled out in the source. Read the HTML and CSS directly; a screenshot won't tell you anything they don't. + +## Bundle contents + +- `bellsystems-console-template/README.md` — this file +- `bellsystems-console-template/project/` — the `Bellsystems Console (Template)` project files (HTML prototypes, assets, components) diff --git a/strategies/Flash Page Rework/bellsystems-console-template/project/Device Select.html b/strategies/Flash Page Rework/bellsystems-console-template/project/Device Select.html new file mode 100644 index 0000000..d927f5d --- /dev/null +++ b/strategies/Flash Page Rework/bellsystems-console-template/project/Device Select.html @@ -0,0 +1,744 @@ + + +Provisioning Wizard — Device · Bellsystems Console + + + + + + + + + +
+ + +
+
+ +
+

Provisioning Wizard

+

Flash firmware to a Bell Systems board via WebSerial

+
+
+ + +
+ + +
+
+
+ +
+
Modeexisting
+
+
+
+
2
+
Deviceselect
+
+
+
+
3
+
Flashpending
+
+
+
+
4
+
Verifypending
+
+
+
+
5
+
Donepending
+
+
+ + +
+
+

Select a device from inventory

+ Flash existing +
+ + +
+ + + + + +
K
+
+ + +
+ All 0 + Manufactured 0 + Flashed 0 + Provisioned 0 +
+ + Newest first +
+
+ + +
+ + +
+
+ SELECTED + — none — + / + TOTAL ELIGIBLE0 +
+
+ + + Back + + + Continue to flash + + +
+
+
+ + +
+
+

Configure new device

+ Deploy new +
+ +
+ + +
+
Board type *
+
+ + + + + + + + +
+
+ + +
+
Revision *
+
+ + + + +
+
+ + +
+
+ +
+
+
Next serial number
+
Pick a board type to generate…
+
+ +
+ +
+ + +
+
+ BOARD— none — + / + REV1.1 +
+
+ + + Back + + + Mint & continue + + +
+
+
+ +
+ + + + + + + + diff --git a/strategies/Flash Page Rework/bellsystems-console-template/project/Flashing.html b/strategies/Flash Page Rework/bellsystems-console-template/project/Flashing.html new file mode 100644 index 0000000..23f77bb --- /dev/null +++ b/strategies/Flash Page Rework/bellsystems-console-template/project/Flashing.html @@ -0,0 +1,1144 @@ + + + + +Provisioning Wizard — Flash · Bellsystems Console + + + + + + + + +
+ + +
+
+ +
+

Provisioning Wizard

+

Flash firmware to a Bell Systems board via WebSerial

+
+
+ +
+ +
+
+ + +
+
+
+ +
+
ModeProvision
+
+
+
+
+ +
+
DeviceBSVSPR-26D19J
+
+
+
+
3
+
Flashcomplete
+
+
+
+
4
+
Verifypending
+
+
+
+
+ +
+
Donepending
+
+
+ + +
+ + +
+
+

Device to Flash

+
+ + /dev/cu.usbserial-0142A91B  ·  complete +
+
+ + +
+
+
+
Serial number
+
+ BSVSPR-26D19J-STD10R-XSNBBP + +
+ Manufactured · ready to flash +
+
+ +
+
+
Board type
+
Vesper V2
+
+
+
Code name
+
vesper-basic
+
+
+
Revision
+
Rev 1.0 · ESP32-S3
+
+
+
+ + +
+ +
+
+
NVS Variant TEMPORARY
+
+ + +
+
+ +
+
Wipe Flash
+
+
+
Wipe flash before writing
+
+ +
+
+
+
+ + +
+ + +
+ + + +
+
Bootloader
+
0x1000 · bootloader.bin · 24 KB
+
+
+
100%
+
+ +
+ + + +
+
Partition Table
+
0x8000 · partitions.bin · 3 KB
+
+
+
100%
+
+ +
+ + + +
+
NVS (Current Gen)
+
0x9000 · nvs_vesper.bin · 20 KB
+
+
+
100%
+
+ +
+ + + +
+
Firmware
+
0x10000 · bellsys-vesper-2.7.3.bin · 1.4 MB
+
+
+
100%
+
+
+ + +
+
+ BAUD 921600 + · + NVS 0x9000 + · + FW 0x10000 + · + STUB yes +
+ +
+
+ + +
+
+
+ + + +
+
+ + + + +
+
+ +
16:17:41.337INFOWebSerial: requesting user-gated port…
16:17:41.338OKPort granted → /dev/cu.usbserial-0142A91B
16:17:41.339INFOOpening @ 115200 baud (handshake)
16:17:41.339DBGDetecting chip… (up to 3s)
16:17:41.339OKChip detected: ESP32-S3 · rev 0.2 · flash 8MB
16:17:41.339INFOMAC: 84:F7:03:1A:2E:D0
16:17:41.339INFOUploading stub flasher → stub_flasher_s3.bin
16:17:41.339OKStub running. Changing baud → 460800
16:17:41.340INFOReady. Waiting for FLASH DEVICE.
16:17:41.340INFOStarting flash sequence…
16:17:41.340INFOWriting bootloader.bin @ 0x1000
16:17:43.143OKWrote bootloader.bin @ 0x1000 (24,576 B) in 1.80s
16:17:43.143INFOWriting partitions.bin @ 0x8000
16:17:43.744OKWrote partitions.bin @ 0x8000 (3,072 B) in 0.60s
16:17:43.744INFOWriting nvs_vesper.bin @ 0x9000
16:17:44.949OKWrote nvs_vesper.bin @ 0x9000 (20,480 B) in 1.20s
16:17:44.950INFOWriting bellsys-vesper-2.7.3.bin @ 0x10000
16:17:50.161OKWrote bellsys-vesper-2.7.3.bin @ 0x10000 (1,468,400 B) in 5.20s
16:17:50.564OKBootloader verified (md5 d4c3…a912)
16:17:50.565OKPartition Table verified (md5 01fe…88b1)
16:17:50.565OKNVS verified (md5 7a22…e0d4)
16:17:50.565OKFirmware verified (md5 5b9c…72a1)
16:17:50.566INFOHard-resetting device…
16:17:50.566OKFlash complete in 00:47.31
+ +
+
+ 24 lines + 1,516,528 B transferred + 00:47 elapsed +
+
COMPLETE
+
+
+ +
+
+ + + + + +
+

Tweaks

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + + + \ No newline at end of file diff --git a/strategies/Flash Page Rework/bellsystems-console-template/project/Mode Select.html b/strategies/Flash Page Rework/bellsystems-console-template/project/Mode Select.html new file mode 100644 index 0000000..b6740c2 --- /dev/null +++ b/strategies/Flash Page Rework/bellsystems-console-template/project/Mode Select.html @@ -0,0 +1,362 @@ + + +Provisioning Wizard — Mode · Bellsystems Console + + + + + + + + + +
+ + +
+
+ +
+

Provisioning Wizard

+

Flash firmware to a Bell Systems board via WebSerial

+
+
+ +
+ +
+
+ + +
+
+
1
+
Modechoose
+
+
+
+
2
+
Devicepending
+
+
+
+
3
+
Flashpending
+
+
+
+
4
+
Verifypending
+
+
+
+
5
+
Donepending
+
+
+ + +
+
+

Select Mode

+
+ Step1 / 5 +
+
+ +
+
+ +
+
Provisioning Wizard
+

What are we doing today?

+

Choose whether to flash a board that already exists in inventory, or generate a brand-new device record.

+
+ +
+ + +
+ + +
+ + + + +
+ +
+

Flash Existing Device

+

Re-flash or finish provisioning a board that is already on record. Search by serial, or pick from boards not yet shipped.

+
+ + + + 247 eligible + + +
+
+ + Status: Manufactured, Flashed, or Provisioned +
+
+ + Preserves existing serial & inventory record +
+
+ + Ideal for rework or returned units +
+
+
+ + +
+ + +
+ + + + +
+ +
+

Deploy New Device

+

Pick a hardware type & revision. We'll mint a new serial, create the inventory record, and proceed to flash it.

+
+ + + + 7 board types · + bespoke + + +
+
+ + Auto-generates next sequential serial +
+
+ + Creates inventory record at status Manufactured +
+
+ + Ideal for fresh production runs +
+
+
+ +
+ +
+
+ + +
+
+ MODEFlash Existing Device + / + NEXTDevice selection +
+
+ + + Continue + + + +
+
+
+ +
+ + + + + diff --git a/strategies/Flash Page Rework/bellsystems-console-template/project/Verify.html b/strategies/Flash Page Rework/bellsystems-console-template/project/Verify.html new file mode 100644 index 0000000..30b45b1 --- /dev/null +++ b/strategies/Flash Page Rework/bellsystems-console-template/project/Verify.html @@ -0,0 +1,564 @@ + + +Provisioning Wizard — Verify · Bellsystems Console + + + + + + + + + +
+ + +
+
+ +
+

Provisioning Wizard

+

Verify the device is online and provisioned

+
+
+ + +
+ + +
+
+
+
ModeProvision
+
+
+
+
+
DeviceBSVSPR-26D19J
+
+
+
+
+
Flashcomplete
+
+
+
+
4
+
Verifylistening…
+
+
+
+
5
+
Donepending
+
+
+ + +
+ + +
+
+
+ + + +
+
+ + + +
+
+ + +
+ Info + Ok + Warn + Error + Debug + Verbose +
+ + + Auto-scroll + +
+ +
+ +
+ /dev/cu.usbserial-0142A91B · 115200 8N1 +
+ RX 0 lines + Uptime 00:00 + streaming… +
+
+
+ + +
+
+

Device Verification

+ STEP4 / 5 +
+ +
+ + +
+
+ + + + + + +
+
+
MQTT Presence
+
Waiting for device on mqtt.bellsystems.io…
+
client_id = BSVSPR-26D19J-STD10R-XSNBBP
+
+ + + Awaiting + +
+ + +
+
+
+ +
+
+
Device connects to broker
+
mqtt.bellsystems.io:8883 · TLS
+
+ +
+ +
+
+ +
+
+
Publishes online message
+
$bell/<serial>/status
+
+ +
+ +
+
+ +
+
+
Inventory marked Provisioned
+
api.bellsystems.io · PATCH /devices/<id>
+
+ +
+
+ + +
+
Brokermqtt.bellsystems.io:8883
+
Client IDBSVSPR-26D19J-…XSNBBP
+
Last seen
+
FW versionv15.3.2
+
RSSI
+
+
+ + +
+
+ STATEawaiting + / + ELAPSED0.0s +
+
+ + + Back + + +
+
+
+ +
+ +
+ + + + + diff --git a/strategies/Flash Page Rework/bellsystems-console-template/project/uploads/pasted-1776603771860-0.png b/strategies/Flash Page Rework/bellsystems-console-template/project/uploads/pasted-1776603771860-0.png new file mode 100644 index 0000000..ea47acf Binary files /dev/null and b/strategies/Flash Page Rework/bellsystems-console-template/project/uploads/pasted-1776603771860-0.png differ diff --git a/strategies/Flash Page Rework/bellsystems-console-template/project/wizard.css b/strategies/Flash Page Rework/bellsystems-console-template/project/wizard.css new file mode 100644 index 0000000..b89d089 --- /dev/null +++ b/strategies/Flash Page Rework/bellsystems-console-template/project/wizard.css @@ -0,0 +1,555 @@ +:root{ + --bg-0:#07090b; + --bg-1:#0b0f13; + --bg-2:#10161c; + --bg-3:#151c24; + --line:#1e2730; + --line-strong:#2a3642; + --text-0:#eef3f8; + --text-1:#c8d2dc; + --text-2:#8a97a4; + --text-3:#5a6775; + --accent:#22e07a; /* primary green */ + --accent-ink:#062211; + --accent-dim:#0f3a23; + --accent-glow: 0 0 0 1px rgba(34,224,122,.35), 0 0 22px -6px rgba(34,224,122,.6); + --warn:#ffb347; + --danger:#ff5a6a; + --blue:#5ab0ff; + --violet:#b48bff; + --radius:12px; + --radius-sm:8px; + --mono:"JetBrains Mono", ui-monospace, Menlo, monospace; + --sans:"Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; + } + + *{box-sizing:border-box} + html,body{margin:0;padding:0} + body{ + font-family:var(--sans); + color:var(--text-0); + background: + radial-gradient(1200px 600px at 85% -10%, rgba(34,224,122,.06), transparent 60%), + radial-gradient(900px 500px at -10% 110%, rgba(90,176,255,.05), transparent 60%), + var(--bg-0); + min-height:100vh; + -webkit-font-smoothing:antialiased; + font-feature-settings:"ss01","cv11"; + letter-spacing:-0.005em; + } + + /* Subtle noise */ + body::before{ + content:""; + position:fixed;inset:0; + pointer-events:none; + opacity:.035; + background-image:url("data:image/svg+xml;utf8,"); + mix-blend-mode:overlay; + z-index:0; + } + + .shell{position:relative;z-index:1;max-width:1440px;margin:0 auto;padding:28px 40px 48px} + + /* -------- Top bar -------- */ + .topbar{display:flex;align-items:flex-start;justify-content:space-between;gap:24px;margin-bottom:28px} + .brand{display:flex;align-items:center;gap:14px} + .logo{ + width:40px;height:40px;border-radius:10px; + background:linear-gradient(135deg,#0c1117,#161e27); + border:1px solid var(--line-strong); + display:grid;place-items:center; + box-shadow: inset 0 0 0 1px rgba(255,255,255,.02); + position:relative; + } + .logo svg{width:22px;height:22px} + .title h1{font-size:20px;font-weight:600;margin:0;letter-spacing:-0.01em} + .title p{margin:2px 0 0;color:var(--text-2);font-size:13px} + + .crumbs{display:flex;align-items:center;gap:10px;color:var(--text-2);font-size:12.5px;font-family:var(--mono)} + .crumbs a{color:var(--text-2);text-decoration:none} + .crumbs a:hover{color:var(--text-0)} + .crumbs .sep{color:var(--text-3)} + + .top-actions{display:flex;gap:10px;align-items:center} + + /* -------- Stepper -------- */ + .stepper{ + display:grid;grid-template-columns:repeat(5, 1fr); + gap:10px; + padding:14px; + border:1px solid var(--line); + border-radius:14px; + background:linear-gradient(180deg, rgba(255,255,255,.02), rgba(255,255,255,0)); + margin-bottom:24px; + position:relative; + } + .step{ + display:flex;align-items:center;gap:12px; + padding:12px 14px; + border-radius:10px; + position:relative; + min-width:0; + } + .step .num{ + width:26px;height:26px;border-radius:50%; + display:grid;place-items:center; + font-family:var(--mono);font-size:12px;font-weight:600; + background:var(--bg-3);color:var(--text-2); + border:1px solid var(--line-strong); + flex-shrink:0; + } + .step.done .num{background:rgba(34,224,122,.12);color:var(--accent);border-color:rgba(34,224,122,.4)} + .step.active .num{background:var(--accent);color:var(--accent-ink);border-color:transparent;box-shadow:var(--accent-glow)} + .step .label{font-size:12.5px;color:var(--text-2);text-transform:uppercase;letter-spacing:.08em;font-weight:500} + .step.active .label{color:var(--text-0)} + .step.done .label{color:var(--text-1)} + .step .sub{font-size:11px;color:var(--text-3);font-family:var(--mono);margin-top:1px} + .step .meta{display:flex;flex-direction:column;min-width:0} + + .step-divider{ + position:absolute;top:50%;right:-6px;width:12px;height:1px; + background:var(--line-strong); + } + .step:last-child .step-divider{display:none} + + /* -------- Main grid -------- */ + .main{ + display:grid; + grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); + gap:20px; + } + /* Layout: stacked */ + body.layout-stacked .main{grid-template-columns:1fr} + body.layout-stacked .console-body{min-height:260px;max-height:360px} + + /* Layout: drawer */ + body.layout-drawer .main{grid-template-columns:1fr} + body.layout-drawer .console-card{ + position:fixed;top:0;right:0;bottom:0;width:min(640px, 92vw); + z-index:40;border-radius:0;border-left:1px solid var(--line-strong); + transform:translateX(100%);transition:transform .25s ease; + box-shadow:-20px 0 60px -10px rgba(0,0,0,.7); + } + body.layout-drawer.drawer-open .console-card{transform:translateX(0)} + body.layout-drawer .console-body{min-height:0;max-height:none;flex:1} + .drawer-toggle{ + display:none;position:fixed;bottom:20px;right:20px;z-index:35; + padding:12px 16px;border-radius:999px; + background:var(--accent);color:var(--accent-ink);font-weight:600; + border:0;cursor:pointer;font-size:13px; + box-shadow:0 10px 30px -8px rgba(34,224,122,.5); + align-items:center;gap:8px;font-family:var(--sans); + } + body.layout-drawer .drawer-toggle{display:inline-flex} + body.layout-drawer.drawer-open .drawer-toggle{display:none} + + .card{ + background:linear-gradient(180deg, var(--bg-1), var(--bg-2) 60%); + border:1px solid var(--line); + border-radius:var(--radius); + overflow:hidden; + position:relative; + } + .card::before{ + content:"";position:absolute;inset:0 0 auto 0;height:1px; + background:linear-gradient(90deg, transparent, rgba(255,255,255,.05), transparent); + } + + .card-head{ + display:flex;align-items:center;justify-content:space-between; + padding:14px 18px; + border-bottom:1px solid var(--line); + background:rgba(255,255,255,.015); + } + .card-head h2{ + margin:0;font-size:11px;font-weight:600;letter-spacing:.14em; + text-transform:uppercase;color:var(--text-2); + display:flex;align-items:center;gap:10px; + } + .card-head h2 .dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent)} + + /* -------- Device section -------- */ + .device{ + padding:20px 22px 18px; + display:flex;flex-direction:column;gap:20px; + border-bottom:1px solid var(--line); + } + .device-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px} + .serial-label{font-size:10.5px;letter-spacing:.14em;color:var(--text-3);text-transform:uppercase;font-weight:500;margin-bottom:6px} + .serial{ + font-family:var(--mono);font-size:22px;font-weight:600; + letter-spacing:-0.01em;color:var(--text-0); + display:flex;align-items:center;gap:10px; + } + .serial .copy{ + width:26px;height:26px;display:grid;place-items:center; + border-radius:6px;background:transparent;border:1px solid var(--line); + color:var(--text-3);cursor:pointer;transition:.15s; + } + .serial .copy:hover{color:var(--text-0);border-color:var(--line-strong);background:var(--bg-3)} + + .manufactured{ + display:inline-flex;align-items:center;gap:8px; + padding:5px 10px;border-radius:999px; + background:rgba(34,224,122,.08); + border:1px solid rgba(34,224,122,.22); + color:var(--accent); + font-family:var(--mono);font-size:10.5px;font-weight:600; + letter-spacing:.1em;text-transform:uppercase; + margin-top:8px; + } + .manufactured .pulse{ + width:6px;height:6px;border-radius:50%;background:var(--accent); + box-shadow:0 0 8px var(--accent); + animation:pulse 2s infinite; + } + @keyframes pulse{ + 0%,100%{opacity:1;transform:scale(1)} + 50%{opacity:.6;transform:scale(.85)} + } + + .board-grid{ + display:grid;grid-template-columns:repeat(3,minmax(0,1fr)); + gap:10px; + } + .board-cell{ + padding:12px 14px; + border:1px solid var(--line); + border-radius:10px; + background:rgba(255,255,255,.015); + } + .board-cell .k{font-size:10.5px;letter-spacing:.14em;color:var(--text-3);text-transform:uppercase;font-weight:500;margin-bottom:6px} + .board-cell .v{font-family:var(--mono);font-size:14px;color:var(--text-0);font-weight:500;display:flex;align-items:center;gap:8px} + .board-cell .v .sub{color:var(--text-3);font-weight:400;font-size:12.5px} + .board-cell .pill{ + display:inline-block;padding:1px 7px;border-radius:4px; + background:rgba(180,139,255,.12);color:var(--violet); + font-size:10.5px;font-weight:600;letter-spacing:.05em; + border:1px solid rgba(180,139,255,.22); + text-transform:uppercase; + } + + /* -------- Port status -------- */ + .port-bar{ + display:flex;align-items:center;gap:12px; + padding:10px 14px;border-radius:10px; + border:1px solid var(--line); + background:var(--bg-2); + } + .port-bar .status-dot{ + width:8px;height:8px;border-radius:50%; + background:var(--text-3); + flex-shrink:0; + } + .port-bar.connected .status-dot{background:var(--accent);box-shadow:0 0 10px var(--accent)} + .port-bar.flashing .status-dot{background:var(--warn);box-shadow:0 0 10px var(--warn);animation:pulse 1s infinite} + .port-bar .txt{font-family:var(--mono);font-size:12.5px;color:var(--text-1);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} + .port-bar .txt strong{color:var(--text-0);font-weight:600} + .port-bar .baud{ + font-family:var(--mono);font-size:11px;color:var(--text-3); + padding:3px 8px;border-radius:4px;background:var(--bg-3); + border:1px solid var(--line); + } + + /* -------- Settings section -------- */ + .settings{ + padding:18px 22px 20px; + display:flex;flex-direction:column;gap:18px; + border-bottom:1px solid var(--line); + } + .section-label{ + font-size:10.5px;letter-spacing:.14em;color:var(--text-3); + text-transform:uppercase;font-weight:600; + display:flex;align-items:center;gap:10px; + } + .section-label::after{content:"";flex:1;height:1px;background:var(--line)} + + /* Segmented NVS control */ + .segmented{ + display:inline-grid;grid-auto-flow:column; + padding:3px;gap:3px; + background:var(--bg-3);border:1px solid var(--line); + border-radius:8px; + position:relative; + } + .segmented button{ + border:0;background:transparent; + color:var(--text-2);font-family:var(--sans);font-size:12.5px;font-weight:500; + padding:7px 14px;border-radius:6px;cursor:pointer; + display:flex;align-items:center;gap:8px; + transition:.18s; + letter-spacing:.01em; + } + .segmented button .tag{ + font-family:var(--mono);font-size:9.5px; + padding:1px 5px;border-radius:3px; + background:var(--bg-1);color:var(--text-3); + border:1px solid var(--line-strong); + } + .segmented button:hover{color:var(--text-0)} + .segmented button.active{ + background:var(--bg-1);color:var(--text-0); + box-shadow: inset 0 0 0 1px var(--line-strong), 0 1px 0 rgba(0,0,0,.3); + } + .segmented button.active .tag{background:var(--accent);color:var(--accent-ink);border-color:transparent} + .segmented button.active.legacy .tag{background:var(--warn);color:#2a1800} + + .row{display:flex;align-items:center;justify-content:space-between;gap:16px} + + /* Toggle */ + .toggle-row{ + display:flex;align-items:center;justify-content:space-between;gap:16px; + padding:12px 14px;border-radius:10px; + border:1px solid var(--line); + background:rgba(255,255,255,.01); + } + .toggle-row.danger-row{ + border-color:rgba(255,90,106,.2); + background:rgba(255,90,106,.04); + } + .toggle-text .t{font-size:13.5px;color:var(--text-0);font-weight:500} + .toggle-text .d{font-size:12px;color:var(--text-2);margin-top:2px} + .toggle-row.danger-row .toggle-text .t{color:#ffb9c0} + .toggle-row.danger-row .toggle-text .d{color:#d98d95} + + .switch{position:relative;display:inline-block;width:38px;height:22px;flex-shrink:0} + .switch input{opacity:0;width:0;height:0} + .switch .slider{ + position:absolute;inset:0;cursor:pointer; + background:var(--bg-3);border:1px solid var(--line-strong); + border-radius:999px;transition:.2s; + } + .switch .slider::before{ + content:"";position:absolute;height:14px;width:14px;left:3px;top:50%;transform:translateY(-50%); + background:var(--text-2);border-radius:50%;transition:.2s; + } + .switch input:checked + .slider{background:var(--accent-dim);border-color:rgba(34,224,122,.4)} + .switch input:checked + .slider::before{transform:translate(16px, -50%);background:var(--accent)} + .toggle-row.danger-row .switch input:checked + .slider{background:rgba(255,90,106,.2);border-color:rgba(255,90,106,.4)} + .toggle-row.danger-row .switch input:checked + .slider::before{background:var(--danger)} + + /* -------- Flash map -------- */ + .flash-map{ + padding:18px 22px 20px; + display:flex;flex-direction:column;gap:10px; + } + .partition{ + display:grid; + grid-template-columns: 22px minmax(0,1fr) auto; + grid-template-rows: auto auto; + align-items:center; + column-gap:14px; + row-gap:8px; + padding:12px 14px; + border:1px solid var(--line); + border-radius:10px; + background:rgba(255,255,255,.012); + position:relative; + transition:.2s; + } + .partition .icon{grid-column:1;grid-row:1} + .partition .info{grid-column:2;grid-row:1;min-width:0} + .partition .pct{grid-column:3;grid-row:1;min-width:48px} + .partition .bar{grid-column:1 / -1;grid-row:2;width:100%} + .partition.active{ + border-color:rgba(34,224,122,.25); + background:rgba(34,224,122,.03); + } + .partition.complete{ + border-color:rgba(34,224,122,.18); + } + .partition .icon{ + width:22px;height:22px;display:grid;place-items:center; + color:var(--text-3); + } + .partition.active .icon{color:var(--accent)} + .partition.complete .icon{color:var(--accent)} + + .partition .info .name{font-size:13px;color:var(--text-0);font-weight:500} + .partition .info .addr{font-family:var(--mono);font-size:11px;color:var(--text-3);margin-top:2px} + .partition.active .info .addr{color:var(--accent)} + + .bar{ + height:6px;background:var(--bg-3);border-radius:3px;overflow:hidden; + position:relative;border:1px solid var(--line); + } + .bar-fill{ + height:100%;background:var(--accent);border-radius:3px; + transition:width .3s ease; + position:relative; + box-shadow:0 0 8px rgba(34,224,122,.4); + } + .bar-fill::after{ + content:"";position:absolute;inset:0; + background:linear-gradient(90deg, transparent, rgba(255,255,255,.25), transparent); + animation:shimmer 1.4s infinite; + } + .partition.complete .bar-fill::after{display:none} + @keyframes shimmer{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}} + + .partition .pct{ + font-family:var(--mono);font-size:12px;color:var(--text-2); + text-align:right;font-weight:500; + font-variant-numeric:tabular-nums; + } + .partition.active .pct{color:var(--accent)} + .partition.complete .pct{color:var(--accent)} + + /* -------- Footer / Action bar -------- */ + .action-bar{ + display:flex;align-items:center;justify-content:space-between;gap:14px; + padding:14px 22px; + background:rgba(0,0,0,.25); + border-top:1px solid var(--line); + } + .meta-inline{ + font-family:var(--mono);font-size:11px;color:var(--text-3); + display:flex;align-items:center;gap:10px; + flex-wrap:wrap; + } + .meta-inline .sep{color:var(--line-strong)} + .meta-inline .k{color:var(--text-3)} + .meta-inline .v{color:var(--text-1)} + + /* Buttons */ + .btn{ + display:inline-flex;align-items:center;justify-content:center;gap:9px; + padding:10px 16px; + font-family:var(--sans);font-size:13.5px;font-weight:600; + border-radius:8px;border:1px solid var(--line-strong); + background:var(--bg-3);color:var(--text-0); + cursor:pointer;transition:.15s; + letter-spacing:.005em; + } + .btn:hover{background:var(--bg-2);border-color:#3a4756} + .btn svg{width:15px;height:15px} + .btn-ghost{background:transparent;border-color:var(--line)} + .btn-ghost:hover{background:var(--bg-3)} + .btn-primary{ + background:var(--accent);color:var(--accent-ink); + border-color:transparent; + box-shadow:0 1px 0 rgba(255,255,255,.18) inset, 0 8px 24px -10px rgba(34,224,122,.6); + } + .btn-primary:hover{background:#2df088;box-shadow:0 1px 0 rgba(255,255,255,.2) inset, 0 10px 28px -8px rgba(34,224,122,.75)} + .btn-danger{background:rgba(255,90,106,.1);color:#ff8690;border-color:rgba(255,90,106,.25)} + .btn-danger:hover{background:rgba(255,90,106,.15)} + .btn-lg{padding:13px 22px;font-size:14.5px} + .btn .kbd{font-family:var(--mono);font-size:10px;padding:1px 5px;border-radius:3px;background:rgba(0,0,0,.2);color:inherit;border:1px solid rgba(0,0,0,.3);opacity:.7} + .btn-primary .kbd{background:rgba(0,0,0,.15);border-color:rgba(0,0,0,.25)} + + /* -------- Console / Log panel -------- */ + .console-card{ + display:flex;flex-direction:column; + min-height:0; + background:linear-gradient(180deg, #080b0e, #05070a); + } + .console-head{ + display:flex;align-items:center;justify-content:space-between; + padding:14px 18px; + border-bottom:1px solid var(--line); + background:rgba(0,0,0,.35); + } + .console-head .tabs{display:flex;gap:2px;font-family:var(--mono);font-size:11px} + .console-head .tabs button{ + background:transparent;border:0;color:var(--text-3);cursor:pointer; + padding:6px 10px;border-radius:5px; + font-family:inherit;font-size:inherit;letter-spacing:.05em;text-transform:uppercase; + } + .console-head .tabs button.active{color:var(--text-0);background:var(--bg-3)} + .console-head .tools{display:flex;gap:6px} + .console-head .tools button{ + width:28px;height:28px;border-radius:6px; + display:grid;place-items:center; + background:transparent;border:1px solid var(--line); + color:var(--text-3);cursor:pointer; + } + .console-head .tools button:hover{color:var(--text-0);border-color:var(--line-strong);background:var(--bg-3)} + .console-head .tools button svg{width:13px;height:13px} + + .console-body{ + flex:1; + font-family:var(--mono);font-size:12px;line-height:1.65; + padding:14px 18px 12px; + overflow:auto; + color:#b8c4d0; + min-height:440px; + max-height:560px; + scrollbar-width:thin;scrollbar-color: var(--line-strong) transparent; + background: + repeating-linear-gradient(180deg, rgba(255,255,255,.012) 0 1px, transparent 1px 24px); + } + .console-body::-webkit-scrollbar{width:8px;height:8px} + .console-body::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:4px} + + .log-line{display:grid;grid-template-columns: 100px 56px 1fr;gap:14px;padding:1px 0;white-space:pre-wrap;word-break:break-word} + .log-time{color:#4a5562;font-variant-numeric:tabular-nums;white-space:nowrap} + .log-level{font-weight:600;letter-spacing:.04em} + .log-level.info{color:#6aa9ff} + .log-level.ok{color:var(--accent)} + .log-level.warn{color:var(--warn)} + .log-level.err{color:var(--danger)} + .log-level.dbg{color:#8a97a4} + .log-msg .tok-hex{color:#b48bff} + .log-msg .tok-num{color:#5ab0ff} + .log-msg .tok-file{color:var(--accent)} + .log-msg .tok-em{color:#eef3f8;font-weight:600} + .log-msg .tok-mut{color:#6a7683} + + .caret{display:inline-block;width:7px;height:13px;background:var(--accent);vertical-align:middle;animation:blink 1s steps(2) infinite;margin-left:4px} + @keyframes blink{50%{opacity:0}} + + .console-empty{ + display:flex;flex-direction:column;align-items:center;justify-content:center; + height:100%;min-height:420px; + color:var(--text-3);font-family:var(--mono);font-size:12px; + gap:14px; + text-align:center; + } + .console-empty .bigdot{ + width:54px;height:54px;border-radius:50%; + background:radial-gradient(circle at 30% 30%, #1a2430, #0c1319); + border:1px solid var(--line); + display:grid;place-items:center; + } + .console-empty .bigdot svg{width:22px;height:22px;color:var(--text-3)} + + .console-foot{ + display:flex;align-items:center;justify-content:space-between; + padding:6px 18px;border-top:1px solid var(--line); + font-family:var(--mono);font-size:10.5px;color:var(--text-3); + letter-spacing:.05em;text-transform:uppercase; + background:rgba(0,0,0,.25); + line-height:1.4; + } + .console-foot .stats{display:flex;gap:16px} + .console-foot strong{color:var(--text-1);font-weight:600} + + /* -------- Tweaks panel -------- */ + .tweaks{ + position:fixed;bottom:20px;right:20px;z-index:30; + width:260px; + background:var(--bg-1);border:1px solid var(--line-strong); + border-radius:12px; + padding:14px; + display:none; + box-shadow:0 20px 60px -20px rgba(0,0,0,.8); + font-size:12.5px; + } + .tweaks.on{display:block} + .tweaks h3{margin:0 0 10px;font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--text-2)} + .tweaks .tw-row{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:6px 0} + .tweaks .tw-row label{color:var(--text-1)} + .tweaks select{ + background:var(--bg-3);color:var(--text-0); + border:1px solid var(--line-strong);border-radius:6px; + padding:5px 8px;font-family:var(--mono);font-size:11px; + } + + /* Small helpers */ + .hide{display:none !important} + + @media (max-width: 1100px){ + .main{grid-template-columns:1fr} + .stepper{grid-template-columns:repeat(5, minmax(0,1fr));font-size:11px} + }