diff --git a/docs/design-tokens.md b/docs/design-tokens.md index bc71c8f..660231b 100644 --- a/docs/design-tokens.md +++ b/docs/design-tokens.md @@ -178,6 +178,7 @@ Application rules: - Use `ops-flat-panel` for secondary decision zones and narrative guidance inside a section. - Use `ops-inline-alert` for non-critical banners that need tone without a full notched frame. - Use `.ops-recovery-support-panel` for steady backup, undo, and storage guidance blocks that need cockpit structure without alert escalation. +- Use `.ops-preview-fact-card` for import preview facts so backup metadata, row counts, and impact summaries keep one inset chip rhythm. - Use `tactical-subpanel` only when a nested control truly needs cockpit framing. - Use `tactical-subpanel-strong` when a detail brief or selected-state summary should read one step higher than surrounding support strips. - Use `tactical-chip-panel` for compact meta blocks, legends, and grouped facts that should feel inset rather than flat. @@ -228,6 +229,7 @@ History selection chrome: - Use `.ops-history-selection-axis` on cells that belong to the selected desktop day column. - Use `.ops-history-selection-row-header` on the selected desktop sector row header. - Use `.ops-history-selection-node` on the exact selected desktop cell or selected mobile day. +- Mobile selected day buttons use the same selection tokens as desktop selected nodes. Do not map mobile selection to nominal readiness color. - Keep `aria-selected`, `aria-current`, and `aria-pressed` as the semantic source of truth. These classes are visual reinforcement only. - Do not use readiness status colors for selection axes. Status colors remain reserved for the cell payload, badges, and spines. @@ -280,10 +282,16 @@ They are available for future rollouts even where core Tailwind spacing utilitie - `--ops-chip-min-h-sm`: `36px` - `--ops-chip-min-h`: `44px` - `--ops-chip-min-h-lg`: `52px` +- `--ops-chip-pad-x`: `0.875rem` +- `--ops-chip-pad-y`: `0.75rem` +- `--ops-chip-pad-y-tight`: `0.625rem` +- `--ops-chip-gap`: `0.625rem` Application rules: - Use the shared chip height tokens through `.ops-action-button`, `.ops-action-button-sm`, `.ops-action-button-lg`, telemetry chips, radio chips, and compact controls. +- Use `.ops-chip-rhythm`, `.ops-chip-rhythm-tight`, and `.ops-chip-rhythm-large` when a clipped chip needs shared density without a bespoke padding stack. +- Radio chips, telemetry chips, preview fact cards, and recovery guidance chips should stay on the shared chip rhythm unless a fixed-format layout needs an explicit exception. - Avoid new ad hoc `min-h-[...]` values unless the control has a fixed-format layout requirement that cannot use the shared scale. ## Action button chrome diff --git a/src/components/DomainCard.tsx b/src/components/DomainCard.tsx index 3a22efa..f4bf1b0 100644 --- a/src/components/DomainCard.tsx +++ b/src/components/DomainCard.tsx @@ -234,10 +234,10 @@ export function DomainCard({ }} onKeyDown={(event) => handleRadioKeyDown(event, optionIndex)} className={[ - 'ops-focus-ring-chip ops-radio-chip tactical-chip-panel ops-tracking-grid min-h-[var(--ops-chip-min-h)] border px-2 py-2 text-center text-[11px] font-semibold uppercase', + 'ops-focus-ring-chip ops-radio-chip ops-chip-rhythm tactical-chip-panel ops-tracking-grid border text-center text-[11px] font-semibold uppercase', busy ? 'cursor-wait opacity-70' : '', isSelected - ? `${content.classes} shadow-[inset_0_1px_0_rgba(255,255,255,0.08)]` + ? content.classes : 'ops-radio-chip-ghost text-ops-text-secondary hover:text-ops-text-primary', ].join(' ')} > diff --git a/src/components/HeaderTelemetry.tsx b/src/components/HeaderTelemetry.tsx index 1adf502..48ab464 100644 --- a/src/components/HeaderTelemetry.tsx +++ b/src/components/HeaderTelemetry.tsx @@ -174,7 +174,7 @@ function TelemetryChip({
-
+

{markedCount} / {totalCount} @@ -85,7 +85,7 @@ function DayCompletionRollup({

{SECTORS.map((sector, index) => { diff --git a/src/features/export/ImportRestoreSection.tsx b/src/features/export/ImportRestoreSection.tsx index a77c3bd..d2637e1 100644 --- a/src/features/export/ImportRestoreSection.tsx +++ b/src/features/export/ImportRestoreSection.tsx @@ -290,7 +290,7 @@ export function ImportRestoreSection({ {pendingImportRequiresAcknowledgment ? (
-
+

Review the staged file risk before merge or @@ -316,7 +316,7 @@ export function ImportRestoreSection({ ) : (

-
+
Local data unchanged. Select a different JSON backup or clear this preview.
@@ -386,7 +386,7 @@ export function ImportRestoreSection({
-
+
{importMode === 'replace' ? 'Destructive restore path selected. ' : 'Merge path selected. '} @@ -396,7 +396,7 @@ export function ImportRestoreSection({ {!pendingImportCanCommit ? (
-
+
Acknowledge the staged file risk before the write path unlocks.
@@ -413,7 +413,7 @@ export function ImportRestoreSection({ >
-
+
Step 1 - secure a pre-replace backup.{' '} {replaceCheckpointStepTwoText} Step 3 - arm the destructive path. Step 4 - execute the replace @@ -425,7 +425,7 @@ export function ImportRestoreSection({ {replaceBackupState.phase === 'manual-awaiting-ack' ? (
-
+

{manualBackupInstructionText} @@ -481,7 +481,7 @@ export function ImportRestoreSection({ {replaceBackupState.phase === 'ready' ? (

-
+

Backup ready - {replaceBackupState.fileName} .{' '} diff --git a/src/features/export/exportPanelShared.tsx b/src/features/export/exportPanelShared.tsx index c7e49fc..3bc9bd9 100644 --- a/src/features/export/exportPanelShared.tsx +++ b/src/features/export/exportPanelShared.tsx @@ -149,7 +149,7 @@ export function SignalCard({ className="ops-signal-frame" data-tone={tone} outerClassName={chromeClasses.outer} - innerClassName={`min-h-[7.5rem] p-4 sm:p-5 ${chromeClasses.inner}`} + innerClassName={`p-4 sm:p-5 ${chromeClasses.inner}`} >

-

-
-

- {label} -

-

- {value} -

- {detail ? ( -

- {detail} -

- ) : null} -
-
-
+ +

+ {label} +

+

+ {value} +

+ {detail ? ( +

{detail}

+ ) : null} +
); } diff --git a/src/features/history/HistoryDetailBrief.tsx b/src/features/history/HistoryDetailBrief.tsx index c99153f..59198f9 100644 --- a/src/features/history/HistoryDetailBrief.tsx +++ b/src/features/history/HistoryDetailBrief.tsx @@ -52,7 +52,7 @@ export function HistoryDetailBrief({ return (
-
+

{title} @@ -80,7 +80,10 @@ export function HistoryDetailBrief({

+

{selectedStatusSummary}

diff --git a/src/styles/index.css b/src/styles/index.css index 2a5573a..2e7c08f 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -156,6 +156,10 @@ --ops-chip-min-h-sm: 36px; --ops-chip-min-h: 44px; --ops-chip-min-h-lg: 52px; + --ops-chip-pad-x: 0.875rem; + --ops-chip-pad-y: 0.75rem; + --ops-chip-pad-y-tight: 0.625rem; + --ops-chip-gap: 0.625rem; --ops-text-on-sky: #e0f2fe; --ops-text-on-amber: #fef3c7; --ops-text-on-orange: #ffedd5; @@ -218,15 +222,15 @@ ); --ops-history-selection-bg: color-mix( in srgb, - var(--color-ops-accent) 8%, + var(--color-ops-accent) 10%, transparent ); --ops-history-selection-bg-strong: color-mix( in srgb, - var(--color-ops-accent) 12%, + var(--color-ops-accent) 15%, var(--color-ops-surface-2) ); - --ops-history-selection-border: rgba(183, 247, 218, 0.28); + --ops-history-selection-border: rgba(183, 247, 218, 0.34); --ops-radio-chip-status-border: rgba(255, 255, 255, 0.18); --ops-frame-calibration: rgba(183, 247, 218, 0.34); --ops-frame-calibration-muted: rgba(255, 255, 255, 0.085); @@ -1514,6 +1518,21 @@ h3, box-shadow: var(--ops-focus-ring-base-shadow); } +.ops-chip-rhythm { + min-height: var(--ops-chip-min-h); + padding: var(--ops-chip-pad-y) var(--ops-chip-pad-x); +} + +.ops-chip-rhythm-tight { + min-height: var(--ops-chip-min-h-sm); + padding: var(--ops-chip-pad-y-tight) var(--ops-chip-pad-x); +} + +.ops-chip-rhythm-large { + min-height: var(--ops-chip-min-h-lg); + padding: var(--ops-chip-pad-y) var(--ops-chip-pad-x); +} + .tactical-chip-panel-neutral { background: linear-gradient( @@ -1599,6 +1618,8 @@ h3, position: relative; overflow: hidden; isolation: isolate; + min-height: calc(var(--ops-chip-min-h-lg) + 1rem); + padding: var(--ops-chip-pad-y) var(--ops-chip-pad-x); background: linear-gradient(90deg, var(--ops-frame-calibration-muted), transparent 24%) left top / 100% 1px no-repeat, @@ -1668,11 +1689,14 @@ h3, } .ops-telemetry-label-row { - margin-bottom: 0.65rem; + flex-wrap: wrap; + margin-bottom: var(--ops-chip-gap); + row-gap: 0.35rem; } .ops-telemetry-register { flex: 0 0 auto; + margin-left: auto; --notch: 4px; border: 1px solid var(--color-ops-panel-border-strong); background: @@ -1703,6 +1727,18 @@ h3, color: color-mix(in srgb, var(--color-ops-text-secondary) 86%, white 14%); } +@media (max-width: 380px) { + .ops-telemetry-chip { + padding-inline: var(--ops-chip-pad-y-tight); + } + + .ops-telemetry-register { + margin-left: 0; + padding-inline: 0.32rem; + font-size: 0.56rem; + } +} + .ops-telemetry-value { display: block; text-shadow: @@ -1724,6 +1760,16 @@ h3, } } +@media (max-width: 380px) { + .ops-telemetry-value-primary { + font-size: 1.875rem; + } + + .ops-telemetry-value-secondary { + font-size: 0.875rem; + } +} + .ops-telemetry-chip-attention { background: linear-gradient(90deg, rgba(245, 158, 11, 0.22), transparent 32%) left top / @@ -1742,7 +1788,9 @@ h3, .ops-telemetry-detail { display: block; - margin-top: 0.35rem; + margin-top: calc(var(--ops-chip-gap) * 0.65); + padding-top: calc(var(--ops-chip-gap) * 0.55); + border-top: 1px solid var(--color-ops-panel-border); color: color-mix(in srgb, var(--color-ops-text-muted) 88%, white 12%); } @@ -1756,6 +1804,7 @@ h3, .ops-telemetry-spark { display: flex; height: 16px; + margin-top: var(--ops-chip-gap); align-items: end; gap: 2px; border: 1px solid rgba(255, 255, 255, 0.08); @@ -1818,6 +1867,10 @@ h3, } @media (min-width: 1024px) { + .ops-telemetry-chip { + padding-inline: var(--spacing-ops-4); + } + .ops-telemetry-grid > .ops-telemetry-chip { border-top: 0; border-right: 0; @@ -1878,6 +1931,8 @@ h3, .ops-radio-chip { position: relative; overflow: hidden; + min-height: var(--ops-chip-min-h); + padding: var(--ops-chip-pad-y-tight) 0.5rem; border-radius: 2px; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.34); transform: translateZ(0); @@ -1895,7 +1950,7 @@ h3, } .ops-radio-chip-ghost { - border-color: rgba(255, 255, 255, 0.18); + border-color: var(--color-ops-panel-border-strong); background: linear-gradient( 180deg, @@ -1910,7 +1965,7 @@ h3, } .ops-radio-chip-ghost:hover { - border-color: rgba(255, 255, 255, 0.22); + border-color: var(--color-ops-border-strong); background: linear-gradient( 180deg, @@ -2070,7 +2125,7 @@ h3, linear-gradient(180deg, var(--ops-history-selection-bg), transparent 70%), var(--ops-history-selection-bg-strong); box-shadow: - inset 0 3px 0 var(--ops-history-selection-border), + inset 0 4px 0 var(--ops-history-selection-border), inset 1px 0 0 var(--ops-history-selection-border), inset -1px 0 0 rgba(183, 247, 218, 0.18), inset 0 -1px 0 rgba(255, 255, 255, 0.08); @@ -2096,7 +2151,7 @@ h3, .ops-history-selection-node { background: - linear-gradient(180deg, rgba(183, 247, 218, 0.1), transparent 72%), + linear-gradient(180deg, rgba(183, 247, 218, 0.12), transparent 72%), var(--ops-history-selection-bg-strong); } @@ -2132,6 +2187,8 @@ h3, .ops-mobile-day-button { transform: translateZ(0); + min-height: 3.5rem; + padding: var(--ops-chip-pad-y) 0.5rem; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.055), inset 0 -1px 0 rgba(0, 0, 0, 0.28); @@ -2160,27 +2217,27 @@ h3, } .ops-mobile-day-button[aria-pressed='true'] { - border-color: var(--ops-status-nominal-border); + border-color: var(--ops-history-selection-border); background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 40%), - linear-gradient(90deg, rgba(110, 231, 183, 0.13), transparent 72%), - rgba(110, 231, 183, 0.18); + linear-gradient(90deg, var(--ops-history-selection-bg), transparent 72%), + var(--ops-history-selection-bg-strong); box-shadow: - inset 0 0 0 1px var(--ops-status-nominal-border), - inset 0 3px 0 rgba(183, 247, 218, 0.72), + inset 0 0 0 1px var(--ops-history-selection-border), + inset 0 3px 0 rgba(183, 247, 218, 0.58), inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -1px 0 rgba(2, 44, 28, 0.36); } .ops-mobile-day-button-selected { - color: var(--ops-status-nominal-text); + color: color-mix(in srgb, var(--color-ops-accent-muted) 88%, white 12%); } .ops-history-selected-cell { box-shadow: - 0 0 0 1px rgba(183, 247, 218, 0.42), + 0 0 0 1px var(--ops-history-selection-border), inset 0 0 0 1px rgba(10, 15, 13, 0.84), - inset 0 0 0 2px var(--ops-focus-ring); + inset 0 0 0 2px rgba(183, 247, 218, 0.36); } .ops-history-selected-cell:focus-visible, @@ -2206,11 +2263,12 @@ h3, } .ops-rollup-count-plate { + min-height: var(--ops-chip-min-h-lg); border: 1px solid var(--color-ops-panel-border-strong); background: linear-gradient(180deg, rgba(255, 255, 255, 0.055), transparent 42%), rgba(10, 15, 13, 0.38); - padding: 0.75rem 0.875rem; + padding: var(--ops-chip-pad-y) var(--ops-chip-pad-x); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07), inset 0 -1px 0 rgba(0, 0, 0, 0.34), @@ -2219,7 +2277,9 @@ h3, .ops-day-rollup-meter { border: 1px solid var(--color-ops-panel-border-strong); - background: rgba(4, 8, 7, 0.34); + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 42%), + rgba(4, 8, 7, 0.34); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.035), inset 0 1px 0 rgba(255, 255, 255, 0.055), @@ -2229,6 +2289,7 @@ h3, .ops-rollup-pip-cell { position: relative; overflow: hidden; + min-height: var(--ops-chip-min-h); color: var(--color-ops-text-secondary); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.075), @@ -2335,12 +2396,20 @@ h3, color: color-mix(in srgb, var(--color-ops-text-muted) 90%, white 10%); } +.ops-history-detail-header { + padding-bottom: var(--ops-chip-pad-y-tight); +} + .ops-history-detail-date { text-shadow: 0 0 18px rgba(110, 231, 183, 0.12), 0 1px 0 rgba(0, 0, 0, 0.72); } +.ops-history-summary-copy { + color: color-mix(in srgb, var(--color-ops-text-secondary) 92%, white 8%); +} + .ops-history-momentum-panel { border-color: var(--color-ops-panel-border-strong); background: @@ -2662,6 +2731,7 @@ h3, .ops-signal-card { --ops-signal-rail: var(--color-ops-border-struct); + min-height: 7.5rem; background: linear-gradient(90deg, var(--ops-frame-calibration-muted), transparent 36%) left top / 100% 1px no-repeat, @@ -2703,6 +2773,18 @@ h3, --ops-signal-rail: var(--color-ops-border-struct); } +.ops-preview-fact-card { + min-height: var(--ops-chip-min-h-lg); + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.045), transparent 36%), + linear-gradient(90deg, rgba(110, 231, 183, 0.026), transparent 44%), + var(--color-ops-surface-base); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.055), + inset 0 -1px 0 rgba(0, 0, 0, 0.3), + inset 0 0 0 1px var(--color-ops-panel-border-strong); +} + .ops-export-accordion { transform: translateZ(0); } @@ -2861,6 +2943,7 @@ h3, .ops-telemetry-chip-attention, .ops-telemetry-register, .ops-signal-card, + .ops-preview-fact-card, .ops-action-button, .ops-radio-chip, .ops-rollup-count-plate,