# Main page: 5-group weighing row + form restyle ## Context The weighing detail panel doesn't match the layout the user wants (reference screenshot: a single row of five groups — CURRENT READING | TARE | GROSS | NET | actions). Three things differ today: 1. **Current Reading is a separate full-width block** (`.reading-display`) sitting *above* the card row, not part of it. 2. **There is a redundant "Scale" card** duplicating the same live weight, which exists only to host the Measure / Manual entry buttons. The user's call: these two are the same thing — **merge into one card**. 3. **Tare and Gross are all-or-nothing.** Today a lone first reading renders as a single neutral "First weighing" card, and Tare/Gross only appear once both exist. The user wants the five groups always visible, with the first reading shown plainly in its slot. Result: a stable five-column row that never changes its column count. ### The one behaviour being deliberately traded away [Main.jsx:604-605](frontend/src/components/Main.jsx#L604-L605) documents why the merged card exists: the backend parks a lone reading in `tare`, but a **lighter second reading moves it to `gross`**. So a single reading labelled "TARE" can later jump to the GROSS box. The user chose "show it plainly" — accepting that jump. `firstWeighing` (Main.jsx:290-309) becomes dead and is removed. ## Approach ### 1. Merge Current Reading + Scale into one card Delete the standalone `.reading-display` block (Main.jsx:581-600) and the Scale card (647-677). One card takes their place as the row's first column, keeping the **richer** of the two behaviours — the reading-display value logic, which the Scale card lacked (disconnected state, `readingError` line): ```jsx
Current Reading
{currentWeight !== null && !isNaN(currentWeight) ? `${currentWeight} kg` : (isConnected ? 'Waiting...' : 'Disconnected')}
{isConnected && readingError && latestReading && (
{readingError}
)} {canMeasure && !isFinalized && (<> {!licenseAllowed &&
{licenseDetail}
} {currentUser?.can_manually_measure && ( )} )}
``` Keep the `.reading-value--balanced/--unbalanced` class names on the value so the existing colour rules (Main.css:547-553) and cockpit glow keep applying unchanged. The `--reading` modifier carries the larger font that `.reading-value` had. ### 2. Tare / Gross / Net always render Replace the ternary at Main.jsx:606-644 with three unconditional cards. Each shows `—` and no meta when its value is null: - Tare — `selectedVehicle.tare`, `tare_date`, `tare_user_name`, `tare_manual` - Gross — same shape with `gross_*` - Net — `netWeight` (null until both exist) Add modifier classes `weight-card--tare`, `--gross` (Net already has `--net`). **This is a prerequisite, not polish:** Main.css:576-578 states the cockpit rules use `:nth-child` precisely because these cards have no distinguishing class, and the column count is about to change. Those `:nth-child` selectors must be rewritten to the modifiers in the same pass or cockpit chrome lands on the wrong cards. Net keeps Print Notice + the Design Notice split button inside it (unchanged markup, including the dropdown menu and backdrop). Gate them on `netWeight !== null` so an empty Net card shows no dead buttons. ### 3. Row becomes a grid `.weighing-section` moves from `flex` + `flex-wrap` to a 5-column grid, following the ratio proven in the concept file ([ScalesAppCyberpunk.css:94](frontend/src/components/concepts/ScalesAppCyberpunk.css#L94)): ```css .weighing-section { display: grid; grid-template-columns: 1.3fr 1fr 1fr 1fr; gap: 16px; margin-bottom: 24px; align-items: stretch; } ``` Four columns, because Print/Design stay inside Net rather than becoming a separate action column — the user kept them there. Reading gets the wider track. Remove `.reading-display`'s `margin-bottom: 20px` block along with the element. Lift `.vehicle-detail { max-width: 700px }` (Main.css:115) — four readouts do not fit in 700px. The cockpit override at 851-853 that already does this then becomes redundant and should go. Responsive, replacing the cockpit-only `@media (max-width:1150px)` rule: ```css @media (max-width: 1150px) { .weighing-section { grid-template-columns: 1fr 1fr; } } @media (max-width: 620px) { .weighing-section { grid-template-columns: 1fr; } } ``` These apply to **both** themes now, so drop the `[data-theme="cockpit"]` prefix from the wrap rules at 858-872. ### 4. Form restyle (styling only, no new capability) Confirmed scope: the create form only. There is no edit form and none is being added. In Main.css: - `.extra-field input/select` — set explicit `background: var(--surface)` and `color: var(--text)`. They currently inherit UA defaults and survive cockpit only by accident of `color-scheme: dark`. - `.extra-fields` — add `@media (max-width: 768px) { grid-template-columns: 1fr }`; it is a hard `1fr 1fr` at every width today. - `.vehicle-form-actions` — add `justify-content: flex-end` to match the `.manual-actions` precedent (Main.css:539+). ## Critical files - [frontend/src/components/Main.jsx](frontend/src/components/Main.jsx) — lines 581-741 replaced; `firstWeighing` memo (290-309) deleted - [frontend/src/components/Main.css](frontend/src/components/Main.css) — `.reading-display` block removed, `.weighing-section` → grid, `max-width` cap lifted, cockpit `:nth-child` → modifier classes, form rules - [frontend/src/components/concepts/ScalesAppCyberpunk.css](frontend/src/components/concepts/ScalesAppCyberpunk.css) — reference only, not modified. Its hardcoded hex and `var(--cyan)`/`var(--green)` must **not** be copied; translate to the semantic tokens in [frontend/src/styles/tokens.css](frontend/src/styles/tokens.css) (`--accent`, `--success`). tokens.css is semantic-by-rule (header comment, lines 5-9). No backend, serializer or API change. No new dependency. ## Verification 1. `cd frontend && npx vitest run` — 201 tests still pass 2. Browser check at http://localhost:5172, **both themes** via the header toggle, walking the full weighing lifecycle on one vehicle: - no vehicle selected → placeholder unchanged - fresh vehicle, no readings → 4 cards, all `—`, no dead buttons - after first Measure → value appears in TARE, Gross/Net still `—` - after second Measure → Tare+Gross+Net populated, Measure button gone, Print/Design appear in Net 3. Confirm the merged card still shows: `Disconnected` when the bridge is down, `Waiting...` when connected with no reading, and the balanced/unbalanced colour on a live value 4. Widths 1400 / 1100 / 600px — grid collapses 4 → 2 → 1 with no overflow 5. Cockpit chrome (clipped corner, lit top edge, Net glow) lands on the right cards after the `:nth-child` → modifier rewrite 6. Create form: 2-col above 768px, 1-col below; Cancel/Create right-aligned; inputs readable in cockpit