Files
roast_command_center/public/app.css
T
snowspeederandFable 0a0356b86e
Test and deploy / test-and-deploy (push) Successful in 1m25s
Fix field-help review findings (Fable review + Opus verification)
Highest severity: injected "?" buttons were hijacking the implicit
<label> association on 14 fields, so clicking the label text opened
the help dialog instead of focusing the input and screen readers lost
the input's accessible name. ensureLabelAssociation() now gives each
affected input an explicit id/for pairing before the button lands.

Also: milestones-ror/actuators topic placement is now text-matched
with a warning fallback instead of blind array indexing; every topic
warns if it resolves zero containers; dialog temperatures (milestone
table, RoR budget, worked example, both charge-setting tables) now
respect the °C/°F toggle instead of being stuck in °C; the print
worksheet's duplicate curve chart was missing the data-tempc
attributes its live counterpart had, so only its title/caption (not
its axis numbers) converted; the "?" trigger's touch target grew from
15px to a WCAG-conformant ~25px without changing the visual dot; two
genuine coverage gaps closed (a roast-log checklist topic, a
curve-reading topic); a worked feasibility example was restored; two
claims that didn't trace back to the source worksheet were corrected
(an invented "±15 seconds" sweet-spot tolerance, a wrong
moisture-to-time mapping); three cultivar cells regained detail lost
in the original transcription; a data-transcription footnote no
longer leaks into an unrelated topic; checklist items keep their list
semantics under VoiceOver.

Co-Authored-By: Fable <[email protected]>
Co-Authored-By: Opus <[email protected]>
2026-07-31 07:17:44 -04:00

2833 lines
55 KiB
CSS

/* Roast Planner — screen UI. worksheet.css is print-only (loaded with media="print"); this file
owns the entire on-screen experience and is deliberately independent of the print layout. */
:root {
--ink: #1b1614;
--ink-2: #5b524b;
--ink-3: #8c8179;
--surface: #fbf8f4;
--surface-raised: #ffffff;
--surface-sunken: #f2ece4;
--line: #e4dcd2;
--line-strong: #d3c8ba;
--ember: #a8481a;
--ember-soft: #fbefe6;
--ember-strong: #7f350f;
--pass: #2f6f4e;
--pass-bg: #e8f3ed;
--warn: #a8741a;
--warn-bg: #fbf3e1;
--fail: #b03a22;
--fail-bg: #fbeae5;
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--shadow-card:
0 1px 2px rgba(27, 22, 20, 0.04), 0 8px 24px -12px rgba(27, 22, 20, 0.12);
--shadow-pop: 0 12px 32px -8px rgba(27, 22, 20, 0.28);
--font-ui:
-apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Inter,
Helvetica, Arial, sans-serif;
--font-serif:
"Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
"Times New Roman", serif;
--font-mono: "SF Mono", Menlo, Consolas, monospace;
--header-h: 60px;
--roast: #2a1d16;
--cream: #fbf8f4;
--nav-w: 232px;
--nav-w-collapsed: 64px;
}
* {
box-sizing: border-box;
}
.hidden {
display: none !important;
}
html {
background: var(--surface);
scroll-padding-top: calc(var(--header-h) + 18px);
}
.panel-card {
scroll-margin-top: calc(var(--header-h) + 18px);
}
body {
margin: 0;
background: var(--surface);
color: var(--ink);
font-family: var(--font-ui);
font-size: 15px;
line-height: 1.45;
-webkit-font-smoothing: antialiased;
}
h1,
h2,
h3 {
margin: 0;
font-family: var(--font-ui);
}
output {
font-variant-numeric: tabular-nums;
}
/* ─── Header ──────────────────────────────────────────────────────────── */
.app-header {
position: sticky;
top: 0;
z-index: 30;
height: var(--header-h);
display: flex;
align-items: center;
gap: 24px;
background: var(--surface-raised);
border-bottom: 1px solid var(--line);
padding: 0 20px;
}
.header-row-top {
display: contents;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
flex: 0 0 auto;
}
.brand-mark {
font-size: 22px;
color: var(--ember);
line-height: 1;
}
.brand-text h1 {
font-size: 15px;
font-weight: 700;
letter-spacing: -0.1px;
}
.brand-sub {
margin: 0;
font-size: 11.5px;
color: var(--ink-3);
}
.section-nav {
display: flex;
align-items: center;
gap: 2px;
flex: 1 1 auto;
overflow-x: auto;
scrollbar-width: none;
}
.section-nav::-webkit-scrollbar {
display: none;
}
.section-nav a {
flex: 0 0 auto;
padding: 6px 12px;
border-radius: 999px;
font-size: 12.5px;
font-weight: 500;
color: var(--ink-2);
text-decoration: none;
white-space: nowrap;
transition:
background 0.15s,
color 0.15s;
}
.section-nav a:hover {
background: var(--surface-sunken);
color: var(--ink);
}
.section-nav a.active {
background: var(--ember-soft);
color: var(--ember-strong);
font-weight: 600;
}
.header-actions {
display: flex;
align-items: center;
gap: 8px;
flex: 0 0 auto;
}
.header-divider {
width: 1px;
height: 22px;
background: var(--line);
margin: 0 2px;
}
.account-menu {
position: relative;
}
.account-menu summary {
list-style: none;
}
.account-menu summary::-webkit-details-marker {
display: none;
}
.account-menu-popover {
position: absolute;
right: 0;
top: calc(100% + 6px);
z-index: 35;
min-width: 132px;
padding: 6px;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
background: var(--surface-raised);
box-shadow: var(--shadow-pop);
}
.account-email {
margin: 4px 10px 7px;
font-size: 11px;
color: var(--ink-3);
overflow-wrap: anywhere;
}
.account-menu-popover a,
.account-menu-popover button {
display: block;
width: 100%;
padding: 9px 10px;
border: 0;
border-radius: var(--radius-sm);
background: transparent;
color: var(--ink-2);
font: inherit;
font-size: 13px;
text-align: left;
text-decoration: none;
cursor: pointer;
}
.account-menu-popover a:hover,
.account-menu-popover button:hover {
background: var(--surface-sunken);
color: var(--ink);
}
.ghost-btn,
.primary-btn,
label.filebtn {
font-family: var(--font-ui);
font-size: 12.5px;
font-weight: 600;
cursor: pointer;
border-radius: var(--radius-sm);
padding: 7px 12px;
white-space: nowrap;
border: 1px solid transparent;
transition:
background 0.15s,
border-color 0.15s,
color 0.15s;
}
.ghost-btn {
background: transparent;
color: var(--ink-2);
border-color: var(--line);
}
.ghost-btn:hover {
background: var(--surface-sunken);
color: var(--ink);
}
.ghost-btn[aria-pressed="true"] {
background: var(--ember-soft);
border-color: var(--ember);
color: var(--ember-strong);
}
.ghost-btn.small {
font-size: 12px;
padding: 5px 10px;
}
.ghost-btn:disabled {
opacity: 0.45;
cursor: default;
}
.primary-btn,
label.filebtn {
background: var(--ember);
color: #fff;
border-color: var(--ember);
}
.primary-btn:hover,
label.filebtn:hover {
background: var(--ember-strong);
}
.primary-btn:disabled {
opacity: 0.5;
cursor: default;
}
#file-load {
display: none;
}
label.filebtn input[type="file"] {
display: none;
}
.autosave-chip {
display: flex;
align-items: center;
gap: 6px;
font-size: 11.5px;
color: var(--ink-3);
padding: 4px 10px;
border-radius: 999px;
background: var(--surface-sunken);
margin-left: 2px;
}
.autosave-chip .dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--ink-3);
transition: background 0.2s;
}
.autosave-chip.failed .dot {
background: var(--fail);
}
.autosave-chip.saving .dot {
background: var(--warn);
animation: pulse 1s ease-in-out infinite;
}
.autosave-chip.saved .dot {
background: var(--pass);
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.35;
}
}
/* ─── Drawers ─────────────────────────────────────────────────────────── */
.drawer-overlay {
position: fixed;
inset: 0;
background: rgba(27, 22, 20, 0.32);
z-index: 40;
opacity: 1;
transition: opacity 0.18s;
}
.drawer-overlay.hidden {
display: none;
}
.drawer {
position: fixed;
top: 0;
right: 0;
bottom: 0;
width: min(420px, 92vw);
z-index: 41;
background: var(--surface-raised);
box-shadow: var(--shadow-pop);
display: flex;
flex-direction: column;
transform: translateX(0);
transition: transform 0.2s;
}
.drawer.hidden {
display: none;
}
.drawer-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid var(--line);
}
.drawer-head h3 {
font-size: 14.5px;
font-weight: 700;
}
.icon-btn {
background: none;
border: none;
font-size: 14px;
color: var(--ink-3);
cursor: pointer;
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
}
.icon-btn:hover {
background: var(--surface-sunken);
color: var(--ink);
}
.drawer-body {
padding: 18px 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 12px;
}
.stack-label {
font-size: 12px;
font-weight: 600;
color: var(--ink-2);
}
.text-input {
width: 100%;
padding: 9px 11px;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
font-size: 13.5px;
font-family: var(--font-ui);
background: var(--surface-raised);
color: var(--ink);
}
.text-input:focus {
outline: none;
border-color: var(--ember);
box-shadow: 0 0 0 3px var(--ember-soft);
}
.checkbox-row {
display: flex;
align-items: center;
gap: 7px;
font-size: 13px;
color: var(--ink-2);
}
.drawer-actions {
display: flex;
gap: 8px;
}
.drawer-result {
font-size: 12.5px;
color: var(--ink-2);
line-height: 1.55;
}
.drawer-result a {
color: var(--ember);
}
.drawer-result .warnings {
color: var(--warn);
margin: 6px 0 0;
padding-left: 18px;
}
.lib-list {
list-style: none;
margin: 0;
padding: 0;
max-height: 220px;
overflow-y: auto;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
}
.lib-list.hidden {
display: none;
}
.lib-list li {
padding: 8px 10px;
font-size: 12.5px;
border-bottom: 1px solid var(--line);
cursor: pointer;
}
.lib-list li:last-child {
border-bottom: none;
}
.lib-list li:hover {
background: var(--surface-sunken);
}
/* ─── Field-help dialog ───────────────────────────────────────────────── */
.help-dialog {
padding: 0;
border: none;
border-radius: var(--radius-lg);
background: var(--surface-raised);
box-shadow: var(--shadow-pop);
width: min(680px, 94vw);
max-height: min(82vh, 780px);
}
.help-dialog::backdrop {
background: rgba(27, 22, 20, 0.32);
}
.help-card {
display: flex;
flex-direction: column;
max-height: inherit;
}
.help-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
padding: 16px 20px 12px;
border-bottom: 1px solid var(--line);
}
.help-kicker {
display: block;
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ember);
}
.help-head h3 {
font-size: 15px;
font-weight: 700;
margin-top: 2px;
}
.help-body {
padding: 14px 20px;
overflow-y: auto;
font-size: 12.5px;
line-height: 1.55;
color: var(--ink-2);
}
.help-body h4 {
margin: 14px 0 6px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--ink-2);
}
.help-body h4:first-child {
margin-top: 0;
}
.help-body p {
margin: 0 0 8px;
}
.help-body dl {
margin: 0;
}
.help-checklist {
list-style: none;
margin: 0 0 10px;
padding: 0;
display: flex;
flex-direction: column;
gap: 5px;
}
.help-checklist li {
position: relative;
padding-left: 20px;
}
.help-checklist li::before {
content: "";
position: absolute;
left: 0;
top: 5px;
width: 10px;
height: 10px;
border: 1.5px solid var(--line-strong);
border-radius: 2px;
}
.help-table-wrap {
overflow-x: auto;
margin: 6px 0 10px;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
}
.help-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
.help-table th {
text-align: left;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--ink-3);
padding: 7px 10px 5px;
border-bottom: 1px solid var(--line-strong);
background: var(--surface);
white-space: nowrap;
}
.help-table td {
padding: 6px 10px;
border-bottom: 1px solid var(--line);
vertical-align: top;
}
.help-table tbody tr:last-child td {
border-bottom: none;
}
.help-table td.num {
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
white-space: nowrap;
font-size: 11.5px;
}
.help-table tr.grp td {
background: var(--ember-soft);
color: var(--ember-strong);
font-weight: 700;
font-size: 11px;
}
.help-table tr.current td {
background: var(--ember-soft);
box-shadow: inset 3px 0 0 var(--ember);
}
.help-aside {
border-left: 3px solid var(--ember);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
background: var(--ember-soft);
padding: 10px 12px;
margin: 10px 0;
}
.help-aside .tag {
display: block;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ember-strong);
margin-bottom: 4px;
}
.help-aside p:last-child {
margin-bottom: 0;
}
.help-aside.warn {
border-left-color: var(--warn);
background: var(--warn-bg);
}
.help-aside.warn .tag {
color: var(--warn);
}
.help-formula {
font-family: var(--font-mono);
font-size: 12px;
text-align: center;
background: var(--surface-sunken);
border-radius: var(--radius-sm);
padding: 8px 10px;
margin: 8px 0;
overflow-x: auto;
white-space: nowrap;
}
.help-attrib {
margin: 0;
padding: 10px 20px 14px;
border-top: 1px solid var(--line);
font-size: 11px;
color: var(--ink-3);
}
@media (max-width: 720px) {
.help-dialog {
width: 100vw;
max-width: none;
margin: auto 0 0;
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
max-height: 88vh;
}
}
@media print {
.help-btn,
.help-dialog {
display: none !important;
}
}
/* ─── Workspace layout ────────────────────────────────────────────────── */
.workspace-grid {
display: grid;
grid-template-columns: 1fr 380px;
gap: 28px;
align-items: start;
max-width: 1360px;
margin: 0 auto;
padding: 28px 24px 80px;
}
.form-col {
min-width: 0;
display: flex;
flex-direction: column;
}
.dock {
position: sticky;
top: calc(var(--header-h) + 20px);
display: flex;
flex-direction: column;
gap: 18px;
max-height: calc(100vh - var(--header-h) - 40px);
overflow-y: auto;
padding-bottom: 4px;
}
@media (max-width: 1180px) {
.workspace-grid {
grid-template-columns: 1fr;
}
.dock {
position: static;
max-height: none;
}
}
@media (max-width: 1080px) {
.app-header {
flex-wrap: wrap;
height: auto;
padding: 10px 14px;
gap: 10px;
}
.section-nav {
order: 3;
flex-basis: 100%;
}
}
@media (max-width: 720px) {
.workspace-grid {
padding: 18px 14px 60px;
}
}
/* ─── Panel cards (form sections) ────────────────────────────────────── */
.panel-card {
background: var(--surface-raised);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
margin-bottom: 20px;
box-shadow: var(--shadow-card);
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px 22px;
border-bottom: 1px solid var(--line);
}
.panel-head h2 {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.1px;
}
.panel-body {
padding: 20px 22px 22px;
}
.subhead {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
font-weight: 700;
color: var(--ink-3);
margin: 20px 0 12px;
}
.subhead:first-child {
margin-top: 0;
}
/* ─── Plan vs. roast-day phase staging ──────────────────────────────────── */
.phase-divider {
display: flex;
align-items: baseline;
gap: 12px;
margin: 6px 0 20px;
padding-top: 18px;
border-top: 2px solid var(--line-strong);
}
.phase-divider-label {
font-size: 11px;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ember-strong);
white-space: nowrap;
}
.phase-divider-text {
font-size: 12px;
color: var(--ink-3);
line-height: 1.45;
}
.phase-chip {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-3);
background: var(--surface-sunken);
padding: 3px 9px;
border-radius: 999px;
white-space: nowrap;
}
.panel-card.phase-later {
opacity: 0.72;
transition: opacity 0.2s;
}
.panel-card.phase-later:hover,
.panel-card.phase-later:focus-within,
.panel-card.phase-later.has-data {
opacity: 1;
}
/* ─── Fields ──────────────────────────────────────────────────────────── */
.field-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 14px 18px;
margin-bottom: 14px;
}
.field-grid:last-child {
margin-bottom: 0;
}
.field {
position: relative;
display: flex;
flex-direction: column;
gap: 5px;
}
.field.wide {
grid-column: 1 / -1;
}
.field.accent .field-label {
color: var(--ember-strong);
}
.field-label {
font-size: 12px;
font-weight: 600;
color: var(--ink-2);
display: flex;
align-items: center;
gap: 4px;
}
.help-btn {
position: relative;
flex: 0 0 auto;
width: 15px;
height: 15px;
padding: 0;
border: 1px solid var(--line-strong);
border-radius: 50%;
background: none;
color: var(--ink-3);
font: 600 9.5px/1 var(--font-ui);
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* Keeps the 15px visual dot but gives touch/mouse a ~25px target (WCAG's 24px minimum) —
matters on a mobile PWA. Kept modest rather than the fuller 44px so it doesn't swallow
clicks meant for the label text 4px away or the input just below. */
.help-btn::after {
content: "";
position: absolute;
inset: -5px;
}
.help-btn:hover,
.help-btn:focus-visible {
border-color: var(--ember);
color: var(--ember);
background: var(--ember-soft);
}
.help-btn:focus-visible {
outline: 2px solid var(--ember);
outline-offset: 1px;
}
.subhead .help-btn,
.dock-card-head .help-btn,
.panel-head h2 .help-btn {
margin-left: 6px;
vertical-align: 1px;
}
/* ─── Teaching layer ─────────────────────────────────────────────────────
Replaces the old per-field "?" tooltip system: a collapsible "why" panel
per section, a permanent one-line note under only the causally-loaded
fields, and fail-only reasoning on the sanity-check tiles. */
.why-panel {
margin: 0 0 16px;
border-left: 3px solid var(--ember);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
background: var(--ember-soft);
}
.why-panel summary {
cursor: pointer;
padding: 9px 12px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--ember-strong);
list-style-position: inside;
}
.why-panel summary:focus-visible {
outline: 2px solid var(--ember);
outline-offset: 2px;
}
.why-panel p {
padding: 0 14px 10px;
font-size: 12.5px;
line-height: 1.55;
color: var(--ink-2);
margin: 0 0 6px;
}
.why-panel p:last-child {
padding-bottom: 12px;
}
.field-note {
display: block;
font-size: 11px;
line-height: 1.45;
color: var(--ink-3);
margin: 2px 0 0;
}
/* ─── Cupping session ────────────────────────────────────────────────────── */
.cup-score-row,
.cup-tick-row,
.cup-stepper-row {
display: grid;
grid-template-columns: 140px 1fr auto auto;
align-items: center;
gap: 10px;
padding: 8px 0;
border-bottom: 1px solid var(--line);
}
.cup-stepper-row {
grid-template-columns: 140px auto auto auto;
}
.cup-score-row:last-of-type,
.cup-tick-row:last-of-type {
border-bottom: none;
}
.cup-score-label {
font-size: 12.5px;
font-weight: 600;
color: var(--ink-2);
}
.cup-score-row input[type="range"] {
width: 100%;
accent-color: var(--ember);
}
.cup-score-row.unscored input[type="range"] {
opacity: 0.45;
}
.cup-score-value {
font-family: var(--font-mono);
font-size: 13px;
font-weight: 700;
color: var(--ink);
min-width: 40px;
text-align: right;
}
.cup-score-clear {
padding: 3px 7px;
}
.cup-tick-cells {
display: flex;
gap: 4px;
flex-wrap: wrap;
}
.cup-tick {
width: 24px;
height: 24px;
border-radius: var(--radius-sm);
border: 1px solid var(--line-strong);
background: var(--surface-raised);
cursor: pointer;
}
.cup-tick[aria-pressed="true"] {
background: var(--ember);
border-color: var(--ember);
}
.flavor-chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 6px 5px 12px;
border-radius: 999px;
border: 1px solid var(--ember);
background: var(--ember-soft);
color: var(--ember-strong);
font-size: 12.5px;
font-weight: 500;
}
.flavor-chip-remove {
width: 24px;
height: 24px;
border: none;
border-radius: 50%;
background: transparent;
color: var(--ember-strong);
cursor: pointer;
font-size: 11px;
}
.flavor-chip-remove:hover {
background: rgba(168, 72, 26, 0.18);
}
.cup-stepper-row output {
min-width: 20px;
text-align: center;
font-family: var(--font-mono);
font-weight: 700;
}
.cupping-defects {
margin-top: 10px;
}
.cupping-defects summary {
cursor: pointer;
font-size: 12px;
font-weight: 700;
color: var(--ink-2);
padding: 6px 0;
}
.flavor-family {
margin-bottom: 8px;
border: 1px solid var(--line);
border-radius: var(--radius-md);
padding: 4px 12px;
}
.flavor-family summary {
cursor: pointer;
padding: 8px 0;
font-size: 13px;
font-weight: 600;
color: var(--ink-2);
}
.flavor-family .subhead {
margin: 10px 0 6px;
}
#cup-radar {
width: 100%;
height: auto;
aspect-ratio: 1;
}
/* ─── Planner inventory/cupping integration ─────────────────────────────── */
.inventory-consume {
display: flex;
align-items: center;
gap: 10px;
margin-top: 14px;
padding: 10px 12px;
background: var(--surface-sunken);
border-radius: var(--radius-md);
}
.inventory-consume-label {
font-size: 12.5px;
color: var(--ink-2);
flex: 1 1 auto;
}
.cupping-link {
display: flex;
align-items: center;
gap: 12px;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid var(--line);
}
.field-input {
width: 100%;
padding: 8px 10px;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
font-family: var(--font-ui);
font-size: 13.5px;
color: var(--ink);
background: var(--surface-raised);
transition:
border-color 0.15s,
box-shadow 0.15s;
}
.field-input.sm {
text-align: center;
font-family: var(--font-mono);
font-size: 13px;
}
.field-input.note {
font-family: var(--font-ui);
}
.field-input:focus {
outline: none;
border-color: var(--ember);
box-shadow: 0 0 0 3px var(--ember-soft);
}
.field-input.prefilled {
background: #fff6de;
border-color: #e4c766;
}
.field-input::placeholder {
color: var(--ink-3);
opacity: 0.7;
}
.unit-input {
position: relative;
}
.unit-input .field-input {
padding-right: 32px;
}
.unit-input.sm .field-input {
padding-right: 26px;
}
.unit-input .unit {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
font-size: 11.5px;
color: var(--ink-3);
pointer-events: none;
}
.unit-input.sm .unit {
right: 7px;
font-size: 10.5px;
}
/* field-id badge — hidden until hover/focus, or pinned via "Field IDs" toggle */
.field[data-fid]::after {
content: attr(data-fid);
position: absolute;
top: -6px;
right: 0;
font-family: var(--font-mono);
font-size: 9.5px;
font-weight: 700;
color: var(--ember);
background: var(--ember-soft);
padding: 1px 5px;
border-radius: 5px;
opacity: 0;
transition: opacity 0.12s;
pointer-events: none;
}
.field[data-fid]:hover::after,
.field[data-fid]:focus-within::after {
opacity: 1;
}
body.show-fids .field[data-fid]::after {
opacity: 1;
}
/* ─── Choice controls (segmented + chips) ───────────────────────────── */
.segmented {
display: inline-flex;
flex-wrap: wrap;
gap: 2px;
background: var(--surface-sunken);
padding: 2px;
border-radius: var(--radius-md);
}
.segmented.small {
padding: 2px;
}
.seg-opt {
position: relative;
cursor: pointer;
}
.seg-opt input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.seg-opt span {
display: block;
padding: 6px 12px;
border-radius: 8px;
font-size: 12.5px;
font-weight: 500;
color: var(--ink-2);
white-space: nowrap;
transition:
background 0.15s,
color 0.15s,
box-shadow 0.15s;
}
.seg-opt input:checked + span {
background: var(--surface-raised);
color: var(--ember-strong);
font-weight: 700;
box-shadow: 0 1px 2px rgba(27, 22, 20, 0.12);
}
.seg-opt input:focus-visible + span {
outline: 2px solid var(--ember);
outline-offset: 1px;
}
.chip-group {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.chip-opt {
cursor: pointer;
}
.chip-opt input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.chip-opt span {
display: inline-flex;
align-items: center;
padding: 7px 14px;
border-radius: 999px;
border: 1px solid var(--line);
font-size: 12.5px;
font-weight: 500;
color: var(--ink-2);
background: var(--surface-raised);
transition: all 0.15s;
}
.chip-opt input:checked + span {
background: var(--ember-soft);
border-color: var(--ember);
color: var(--ember-strong);
font-weight: 700;
}
.chip-opt input:focus-visible + span {
outline: 2px solid var(--ember);
outline-offset: 1px;
}
/* ─── Blend cards ─────────────────────────────────────────────────────── */
#blend-body.collapsed .blend-cards,
#blend-body.collapsed .blend-total,
#blend-body.collapsed #btn-add-blend,
#blend-body.collapsed .subhead,
#blend-body.collapsed .field.wide,
#blend-body.collapsed .field-grid {
display: none;
}
.blend-cards {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 12px;
}
.blend-card {
display: grid;
grid-template-columns: 1.4fr 1fr 1fr 0.7fr 0.8fr auto;
gap: 10px;
align-items: end;
background: var(--surface-sunken);
border-radius: var(--radius-md);
padding: 12px;
}
.blend-card .field-label {
font-size: 10.5px;
}
.blend-remove {
background: none;
border: 1px solid var(--line);
color: var(--ink-3);
width: 30px;
height: 30px;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 13px;
}
.blend-remove:hover {
background: var(--fail-bg);
color: var(--fail);
border-color: var(--fail);
}
.blend-total {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 14px;
}
.blend-total-bar {
flex: 1 1 auto;
height: 6px;
border-radius: 999px;
background: var(--surface-sunken);
overflow: hidden;
}
.blend-total-fill {
height: 100%;
background: var(--ember);
width: 0%;
transition:
width 0.2s,
background 0.2s;
}
.blend-total-fill.over,
.blend-total-fill.under {
background: var(--fail);
}
.blend-total-label {
font-size: 11.5px;
font-family: var(--font-mono);
color: var(--ink-3);
white-space: nowrap;
}
@media (max-width: 640px) {
.blend-card {
grid-template-columns: 1fr 1fr;
}
}
/* ─── Milestone lists (temps / roast log) ────────────────────────────── */
.milestone-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.milestone-row {
display: grid;
grid-template-columns: 110px 64px 1fr 90px;
gap: 12px;
align-items: center;
padding: 8px 4px;
border-bottom: 1px solid var(--line);
}
.milestone-row:last-child {
border-bottom: none;
}
.milestone-row.log-row {
grid-template-columns: 110px 64px 72px 64px 1fr;
}
.milestone-label {
font-size: 13px;
font-weight: 600;
color: var(--ink);
}
.milestone-plan {
font-family: var(--font-mono);
font-size: 13px;
color: var(--ink-2);
text-align: center;
background: var(--surface-sunken);
border-radius: var(--radius-sm);
padding: 6px 4px;
}
.milestone-time {
text-align: center;
}
.band-track {
position: relative;
height: 6px;
border-radius: 999px;
background: var(--surface-sunken);
}
.band-range {
position: absolute;
top: 0;
bottom: 0;
background: var(--pass-bg);
border-radius: 999px;
}
.band-marker {
position: absolute;
top: 50%;
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--ink);
border: 2px solid var(--surface-raised);
box-shadow: 0 0 0 1px var(--ink-3);
transform: translate(-50%, -50%);
display: none;
transition: left 0.15s;
}
.band-marker.out-of-band {
background: var(--fail);
box-shadow: 0 0 0 1px var(--fail);
}
@media (max-width: 640px) {
.milestone-row,
.milestone-row.log-row {
grid-template-columns: 1fr;
gap: 6px;
}
.band-track {
order: 3;
}
}
/* ─── Actuator timeline ───────────────────────────────────────────────── */
.actuator-timeline {
display: flex;
flex-direction: column;
}
.actuator-step {
display: flex;
gap: 14px;
position: relative;
padding-bottom: 16px;
}
.actuator-step:last-child {
padding-bottom: 0;
}
.actuator-rail {
display: flex;
flex-direction: column;
align-items: center;
flex: 0 0 auto;
}
.actuator-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--ember);
margin-top: 10px;
flex: 0 0 auto;
}
.actuator-line {
width: 1px;
flex: 1 1 auto;
background: var(--line);
margin-top: 4px;
}
.actuator-step:last-child .actuator-line {
display: none;
}
.actuator-card {
flex: 1 1 auto;
background: var(--surface-sunken);
border-radius: var(--radius-md);
padding: 12px 14px;
display: grid;
grid-template-columns: 70px 1fr 1fr auto;
gap: 10px;
align-items: end;
}
.actuator-card .field-label {
font-size: 10.5px;
}
.actuator-why {
grid-column: 1 / -1;
}
.actuator-remove {
background: none;
border: 1px solid var(--line);
color: var(--ink-3);
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 12px;
align-self: start;
}
.actuator-remove:hover {
background: var(--fail-bg);
color: var(--fail);
border-color: var(--fail);
}
@media (max-width: 640px) {
.actuator-card {
grid-template-columns: 1fr 1fr;
}
}
/* ─── Dock: shared card chrome ───────────────────────────────────────── */
.dock-card {
background: var(--surface-raised);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
padding: 18px 20px;
}
.dock-card-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.dock-card-head h2 {
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--ink-2);
}
/* ─── Ledger ──────────────────────────────────────────────────────────── */
.ledger-group {
display: flex;
flex-direction: column;
}
.ledger-row {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 0;
border-bottom: 1px solid var(--line);
}
.ledger-row:last-child {
border-bottom: none;
}
.l-fid {
font-family: var(--font-mono);
font-size: 9.5px;
color: var(--ink-3);
background: var(--surface-sunken);
border-radius: 4px;
padding: 1px 4px;
flex: 0 0 auto;
}
.l-label {
font-size: 12.5px;
color: var(--ink-2);
flex: 1 1 auto;
}
.l-val {
font-family: var(--font-mono);
font-size: 13px;
font-weight: 600;
color: var(--ink);
text-align: right;
min-width: 52px;
}
.ledger-input {
width: 60px;
text-align: right;
font-family: var(--font-mono);
font-size: 13px;
font-weight: 600;
padding: 4px 7px;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
background: var(--surface-sunken);
color: var(--ink);
}
.ledger-input:focus {
outline: none;
border-color: var(--ember);
box-shadow: 0 0 0 3px var(--ember-soft);
background: var(--surface-raised);
}
.ledger-total {
display: flex;
align-items: baseline;
justify-content: space-between;
margin: 10px 0;
padding-top: 8px;
border-top: 1.5px solid var(--ink);
}
.ledger-total span {
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--ink-2);
}
.ledger-total output {
font-family: var(--font-serif);
font-size: 26px;
font-weight: 600;
color: var(--ink);
}
.ledger-total.drop {
border-top: none;
margin-top: 4px;
padding-top: 10px;
background: var(--ember-soft);
border-radius: var(--radius-md);
padding: 12px 14px;
margin-bottom: 0;
}
.ledger-total.drop span {
color: var(--ember-strong);
}
.ledger-total.drop output {
color: var(--ember-strong);
font-size: 30px;
}
.ledger-info {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
margin: 10px 0 16px;
padding: 8px 10px;
border-left: 2px solid var(--line-strong);
background: var(--surface-sunken);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.info-pill {
display: flex;
align-items: center;
gap: 5px;
font-size: 11.5px;
color: var(--ink-2);
}
.info-pill span {
color: var(--ink-3);
}
.info-pill output {
font-family: var(--font-mono);
font-weight: 600;
}
.info-note {
font-size: 10px;
color: var(--ink-3);
flex-basis: 100%;
}
/* ─── Sanity checks ───────────────────────────────────────────────────── */
.checks-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.check-tile {
border: 1px solid var(--line);
border-radius: var(--radius-md);
padding: 10px 12px;
display: flex;
flex-direction: column;
gap: 2px;
position: relative;
}
.check-name {
font-size: 10.5px;
color: var(--ink-3);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.02em;
}
.check-value {
font-family: var(--font-mono);
font-size: 18px;
font-weight: 700;
color: var(--ink);
}
.check-band {
font-size: 10.5px;
color: var(--ink-3);
}
.check-tile::before {
content: "";
position: absolute;
top: 10px;
right: 10px;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--ink-3);
}
.check-tile.pass::before {
background: var(--pass);
}
.check-tile.fail::before {
background: var(--fail);
}
.check-tile.pass {
border-color: var(--pass-bg);
background: var(--pass-bg);
}
.check-tile.fail {
border-color: var(--fail-bg);
background: var(--fail-bg);
}
.check-tile.pass .check-value {
color: var(--pass);
}
.check-tile.fail .check-value {
color: var(--fail);
}
.check-why {
display: none;
font-size: 10.5px;
line-height: 1.4;
color: var(--ink-2);
}
.check-tile.fail .check-why {
display: block;
}
.dock-note {
font-size: 10.5px;
color: var(--ink-3);
line-height: 1.4;
margin: -4px 0 10px;
}
/* ─── Curve card ──────────────────────────────────────────────────────── */
.curve-legend {
display: flex;
align-items: center;
gap: 10px;
font-size: 10.5px;
color: var(--ink-3);
text-transform: none;
letter-spacing: 0;
font-weight: 500;
}
.curve-legend .sw {
display: inline-block;
width: 12px;
height: 2px;
margin-right: 3px;
vertical-align: middle;
}
.sw-plan {
background: var(--ink);
}
.sw-ref {
background: var(--ink-3);
border-top: 1px dashed var(--ink-3);
background: none;
height: 0;
border-bottom: 1.5px dashed var(--ink-3);
}
.curve-wrap {
border-radius: var(--radius-md);
overflow: hidden;
border: 1px solid var(--line);
}
.ledger-warnings {
padding: 10px 12px;
border: 1px solid #e8d49e;
border-radius: var(--radius-md);
background: var(--warn-bg);
color: #705318;
font-size: 12px;
}
.ledger-warnings p {
margin: 0;
}
.plan-list {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: 8px;
}
.plan-list-item {
width: 100%;
display: grid;
gap: 3px;
padding: 12px;
text-align: left;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
background: var(--surface-raised);
color: var(--ink);
font: inherit;
cursor: pointer;
}
.plan-list-item:hover,
.plan-list-item.active {
border-color: var(--ember);
background: var(--ember-soft);
}
.plan-list-item strong {
font-size: 13px;
}
.plan-list-item span {
font-size: 11px;
color: var(--ink-3);
}
/* ─── Side navigation (app shell) ──────────────────────────────────────── */
.app-shell {
display: flex;
min-height: 100vh;
}
.side-nav {
position: fixed;
top: 0;
bottom: 0;
left: 0;
width: var(--nav-w);
/* Above .drawer-overlay (40) and .drawer (41): when the mobile off-canvas nav is open, its
own backdrop must not intercept clicks on the nav's items (matches how right-side drawers
already sit above the same shared overlay). */
z-index: 42;
display: flex;
flex-direction: column;
background: var(--roast);
color: var(--cream);
transition: width 0.18s ease;
overflow-x: hidden;
}
.side-nav-brand {
display: flex;
align-items: center;
gap: 10px;
padding: 16px;
flex: 0 0 auto;
white-space: nowrap;
}
.side-nav-brand .brand-mark {
color: var(--ember-soft, #fbefe6);
font-size: 22px;
}
.side-nav-brand span.brand-name {
font-weight: 700;
font-size: 14.5px;
letter-spacing: -0.1px;
}
.nav-group {
display: flex;
flex-direction: column;
gap: 2px;
padding: 6px 8px;
}
.nav-group-title {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 700;
color: rgba(251, 248, 244, 0.45);
padding: 12px 10px 6px;
white-space: nowrap;
}
.nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 9px 10px;
border-radius: var(--radius-sm);
color: rgba(251, 248, 244, 0.82);
text-decoration: none;
font-size: 13.5px;
font-weight: 500;
background: transparent;
border: none;
width: 100%;
text-align: left;
cursor: pointer;
font-family: inherit;
white-space: nowrap;
position: relative;
}
.nav-item:hover {
background: rgba(251, 248, 244, 0.08);
color: var(--cream);
}
.nav-item .nav-icon {
flex: 0 0 auto;
width: 18px;
text-align: center;
font-size: 15px;
}
.nav-item[aria-current="page"] {
background: rgba(168, 72, 26, 0.35);
color: #fff;
font-weight: 700;
}
.nav-item[aria-current="page"]::before {
content: "";
position: absolute;
left: -8px;
top: 6px;
bottom: 6px;
width: 3px;
border-radius: 2px;
background: var(--ember);
}
.nav-spacer {
flex: 1 1 auto;
}
.nav-collapse-toggle {
margin: 8px;
align-self: flex-start;
}
.nav-user {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 16px;
border-top: 1px solid rgba(251, 248, 244, 0.12);
}
.nav-user-avatar {
flex: 0 0 auto;
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--ember);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 12.5px;
font-weight: 700;
}
.nav-user-detail {
min-width: 0;
display: flex;
flex-direction: column;
white-space: nowrap;
}
.nav-user-email {
font-size: 12px;
color: var(--cream);
overflow: hidden;
text-overflow: ellipsis;
}
.nav-user-logout {
font-size: 11px;
color: rgba(251, 248, 244, 0.55);
background: none;
border: none;
padding: 0;
text-align: left;
cursor: pointer;
font-family: inherit;
}
.nav-user-logout:hover {
color: var(--cream);
text-decoration: underline;
}
.app-workspace {
flex: 1 1 auto;
min-width: 0;
margin-left: var(--nav-w);
transition: margin-left 0.18s ease;
}
.app-workspace.nav-collapsed {
margin-left: var(--nav-w-collapsed);
}
.nav-hamburger {
display: none;
}
/* The user-toggled collapse (desktop only — the toggle button itself is hidden below 901px, but
the persisted .collapsed class isn't otherwise scoped, and its higher specificity than the
<900px off-canvas rules would otherwise still shrink/relabel the mobile drawer if a user had
collapsed the rail on desktop before viewing on a phone). */
@media (min-width: 901px) {
.side-nav.collapsed {
width: var(--nav-w-collapsed);
}
.side-nav.collapsed .brand-name,
.side-nav.collapsed .nav-label,
.side-nav.collapsed .nav-group-title,
.side-nav.collapsed .nav-user-detail {
display: none;
}
.side-nav.collapsed .nav-item {
justify-content: center;
padding: 10px;
}
}
/* Icon-only rail: only between the mobile off-canvas breakpoint and the desktop breakpoint —
scoped with min-width so it never fights the <900px off-canvas rules below for the same
element (that previously caused a CSS-specificity conflict that broke the mobile nav width). */
@media (min-width: 901px) and (max-width: 1180px) {
.side-nav {
width: var(--nav-w-collapsed);
}
.side-nav .brand-name,
.side-nav .nav-label,
.side-nav .nav-group-title,
.side-nav .nav-user-detail {
display: none;
}
.side-nav .nav-item {
justify-content: center;
padding: 10px;
}
.app-workspace {
margin-left: var(--nav-w-collapsed);
}
}
@media (max-width: 900px) {
.side-nav {
transform: translateX(-100%);
transition: transform 0.2s;
width: min(280px, 82vw);
}
.side-nav.mobile-open {
transform: translateX(0);
}
.side-nav .brand-name,
.side-nav .nav-label,
.side-nav .nav-group-title,
.side-nav .nav-user-detail {
display: block;
}
.side-nav .nav-item {
justify-content: flex-start !important;
}
.app-workspace {
margin-left: 0 !important;
}
.nav-hamburger {
display: inline-flex;
}
.nav-collapse-toggle {
display: none;
}
}
/* ─── Account / admin content shell ─────────────────────────────────────── */
.page-content {
max-width: 860px;
margin: 0 auto;
padding: 28px 24px 80px;
display: flex;
flex-direction: column;
gap: 20px;
}
.page-content.wide {
max-width: 1160px;
}
.panel-head p.panel-sub {
margin: 2px 0 0;
font-size: 12px;
color: var(--ink-3);
font-weight: 400;
}
.field-row {
display: flex;
gap: 14px;
flex-wrap: wrap;
}
.field-row .field {
flex: 1 1 200px;
}
.session-device-label {
font-size: 12.5px;
color: var(--ink-2);
display: inline-block;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
.danger-zone {
border-color: var(--fail-bg);
}
.danger-zone .panel-head {
background: var(--fail-bg);
}
.danger-zone .panel-head h2 {
color: var(--fail);
}
.inline-form {
display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: flex-end;
}
.inline-form .field {
flex: 1 1 160px;
}
.empty-state {
font-size: 13px;
color: var(--ink-3);
padding: 14px 0;
text-align: center;
}
@media (max-width: 720px) {
.page-content {
padding: 16px 14px 60px;
}
}
/* ─── Data table (admin, account) ──────────────────────────────────────── */
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.data-table th {
text-align: left;
font-size: 10.5px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--ink-3);
font-weight: 700;
padding: 9px 10px;
background: var(--surface-sunken);
border-bottom: 1px solid var(--line);
white-space: nowrap;
}
.data-table td {
padding: 10px;
border-bottom: 1px solid var(--line);
vertical-align: middle;
}
.data-table tbody tr:hover {
background: var(--surface-sunken);
}
.data-table td.num {
text-align: right;
font-variant-numeric: tabular-nums;
}
.data-table-actions {
display: flex;
gap: 6px;
justify-content: flex-end;
flex-wrap: wrap;
}
.table-wrap {
overflow-x: auto;
border: 1px solid var(--line);
border-radius: var(--radius-md);
}
.table-wrap .data-table {
border: none;
}
.data-table tr.archived {
opacity: 0.55;
}
.lot-remaining {
display: flex;
align-items: center;
gap: 8px;
min-width: 160px;
}
.lot-remaining .blend-total-bar {
flex: 1 1 auto;
}
.lot-remaining .blend-total-label {
white-space: nowrap;
}
/* ─── Badges, switch, toast, stat tiles ─────────────────────────────────── */
.badge {
display: inline-flex;
align-items: center;
padding: 2px 9px;
border-radius: 999px;
font-size: 10.5px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.03em;
}
.badge-admin {
background: var(--ember-soft);
color: var(--ember-strong);
}
.badge-user {
background: var(--surface-sunken);
color: var(--ink-2);
}
.badge-disabled {
background: var(--fail-bg);
color: var(--fail);
}
.badge-current {
background: var(--pass-bg);
color: var(--pass);
}
.switch {
position: relative;
display: inline-block;
width: 38px;
height: 22px;
flex: 0 0 auto;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.switch-track {
position: absolute;
inset: 0;
background: var(--line-strong);
border-radius: 999px;
cursor: pointer;
transition: background 0.15s;
}
.switch-track::before {
content: "";
position: absolute;
top: 2px;
left: 2px;
width: 18px;
height: 18px;
border-radius: 50%;
background: #fff;
transition: transform 0.15s;
box-shadow: 0 1px 2px rgba(27, 22, 20, 0.3);
}
.switch input:checked + .switch-track {
background: var(--ember);
}
.switch input:checked + .switch-track::before {
transform: translateX(16px);
}
.switch input:focus-visible + .switch-track {
outline: 2px solid var(--ember);
outline-offset: 2px;
}
.toast-stack {
position: fixed;
right: 18px;
bottom: 18px;
z-index: 60;
display: flex;
flex-direction: column;
gap: 8px;
max-width: min(360px, calc(100vw - 36px));
}
.toast {
background: var(--ink);
color: var(--cream);
padding: 11px 14px;
border-radius: var(--radius-sm);
font-size: 12.5px;
box-shadow: var(--shadow-pop);
display: flex;
align-items: center;
gap: 10px;
animation: toast-in 0.18s ease-out;
}
.toast.warn {
background: var(--warn);
}
.toast.fail {
background: var(--fail);
}
@keyframes toast-in {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.stat-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 14px;
}
.stat-tile {
background: var(--surface-raised);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
padding: 16px 18px;
}
.stat-tile-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--ink-3);
font-weight: 700;
}
.stat-tile-value {
font-family: var(--font-serif);
font-size: 26px;
font-weight: 600;
color: var(--ink);
margin-top: 4px;
}
/* ─── Auth pages (login/signup/forgot/reset/setup) ─────────────────────── */
.auth-page {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 32px 18px;
}
.auth-card {
width: 100%;
max-width: 400px;
padding: 30px 28px 28px;
display: flex;
flex-direction: column;
gap: 14px;
}
.auth-card .auth-brand {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 2px;
}
.auth-card .auth-brand .brand-mark {
color: var(--ember);
font-size: 22px;
}
.auth-card h1 {
font-size: 19px;
font-weight: 700;
}
.auth-card p.lede {
margin: -6px 0 4px;
font-size: 13px;
color: var(--ink-2);
}
.auth-card form {
display: flex;
flex-direction: column;
gap: 12px;
}
.auth-card label {
display: flex;
flex-direction: column;
gap: 5px;
font-size: 12.5px;
font-weight: 600;
color: var(--ink-2);
}
.auth-card .primary-btn {
padding: 10px 12px;
font-size: 13.5px;
margin-top: 4px;
}
.auth-links {
display: flex;
justify-content: space-between;
font-size: 12.5px;
}
.auth-links a {
color: var(--ember);
text-decoration: none;
}
.auth-links a:hover {
text-decoration: underline;
}
.auth-message {
font-size: 12.5px;
border-radius: var(--radius-sm);
padding: 9px 11px;
}
.auth-message:empty {
display: none;
}
.auth-message.error {
background: var(--fail-bg);
color: var(--fail);
}
.auth-message.info {
background: var(--pass-bg);
color: var(--pass);
}
.auth-page .muted {
font-size: 11.5px;
color: var(--ink-3);
text-align: center;
margin: 0;
}
/* ─── Marketing landing page ────────────────────────────────────────────── */
.marketing-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 28px;
max-width: 1160px;
margin: 0 auto;
}
.marketing-header .brand {
display: flex;
align-items: center;
gap: 8px;
font-weight: 700;
font-size: 15px;
}
.marketing-header .brand .brand-mark {
color: var(--ember);
font-size: 20px;
}
.marketing-header nav {
display: flex;
gap: 10px;
}
.hero {
background: var(--roast);
color: var(--cream);
}
.hero-inner {
max-width: 1160px;
margin: 0 auto;
padding: 48px 28px 76px;
display: grid;
grid-template-columns: 1.1fr 1fr;
gap: 40px;
align-items: center;
}
.hero h1 {
font-family: var(--font-serif);
font-size: clamp(32px, 4.6vw, 44px);
line-height: 1.12;
letter-spacing: -0.5px;
margin: 0 0 16px;
}
.hero p.lede {
font-size: 16px;
line-height: 1.55;
color: rgba(251, 248, 244, 0.82);
margin: 0 0 24px;
max-width: 46ch;
}
.hero-ctas {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.hero-ctas .ghost-btn {
color: var(--cream);
border-color: rgba(251, 248, 244, 0.35);
}
.hero-ctas .ghost-btn:hover {
background: rgba(251, 248, 244, 0.12);
color: #fff;
}
.hero-visual {
background: var(--surface-raised);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-pop);
padding: 18px 20px;
color: var(--ink);
}
.marketing-section {
max-width: 1160px;
margin: 0 auto;
padding: 64px 28px;
}
.marketing-section h2 {
font-size: 24px;
font-weight: 700;
text-align: center;
margin: 0 0 8px;
}
.marketing-section p.section-lede {
text-align: center;
color: var(--ink-2);
max-width: 52ch;
margin: 0 auto 36px;
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 18px;
}
.feature-card {
background: var(--surface-raised);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
padding: 20px 22px;
}
.feature-card .feature-icon {
font-size: 20px;
color: var(--ember);
margin-bottom: 10px;
}
.feature-card h3 {
font-size: 15px;
font-weight: 700;
margin: 0 0 8px;
}
.feature-card p {
font-size: 13.5px;
color: var(--ink-2);
line-height: 1.5;
margin: 0;
}
.feature-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 14px;
margin-top: 14px;
}
.feature-row-item {
display: flex;
gap: 10px;
align-items: flex-start;
font-size: 13px;
color: var(--ink-2);
}
.feature-row-item .feature-icon {
color: var(--ember);
flex: 0 0 auto;
}
.steps-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 22px;
counter-reset: step;
}
.step-item {
position: relative;
padding-left: 40px;
}
.step-item::before {
counter-increment: step;
content: counter(step);
position: absolute;
left: 0;
top: 0;
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--ember-soft);
color: var(--ember-strong);
font-weight: 700;
font-size: 13px;
display: flex;
align-items: center;
justify-content: center;
}
.step-item h3 {
font-size: 14px;
font-weight: 700;
margin: 0 0 4px;
}
.step-item p {
font-size: 13px;
color: var(--ink-2);
margin: 0;
line-height: 1.5;
}
.cta-band {
background: var(--ember);
color: #fff;
text-align: center;
padding: 52px 28px;
}
.cta-band h2 {
font-family: var(--font-serif);
font-size: 26px;
margin: 0 0 18px;
}
.cta-band .primary-btn {
background: #fff;
color: var(--ember-strong);
border-color: #fff;
padding: 11px 20px;
font-size: 14px;
}
.cta-band .primary-btn:hover {
background: var(--surface-sunken);
}
.marketing-footer {
padding: 22px 28px 40px;
max-width: 1160px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12.5px;
color: var(--ink-3);
}
.marketing-footer a {
color: var(--ink-2);
margin-left: 14px;
text-decoration: none;
}
.marketing-footer a:hover {
text-decoration: underline;
}
@media (max-width: 860px) {
.hero-inner {
grid-template-columns: 1fr;
padding-bottom: 48px;
}
.hero-visual {
order: -1;
}
}
/* ─── PWA and mobile layout ───────────────────────────────────────────── */
.connection-status {
position: sticky;
top: var(--header-h);
z-index: 29;
padding: 8px 14px;
background: var(--warn-bg);
border-bottom: 1px solid #e8d49e;
color: #705318;
font-size: 13px;
font-weight: 600;
text-align: center;
}
.connection-status.hidden {
display: none;
}
@media (max-width: 720px) {
:root {
/* The mobile header is multi-row (top bar / section pills / actions); keep this in sync
with its actual stacked height so sticky/scroll-anchor offsets stay correct. */
--header-h: 166px;
}
body {
font-size: 16px;
padding-bottom: env(safe-area-inset-bottom);
}
.app-header {
position: sticky;
padding: 10px 12px;
gap: 8px;
padding-top: max(10px, env(safe-area-inset-top));
}
.header-row-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
width: 100%;
order: 0;
}
.brand-mark {
font-size: 24px;
}
.brand-text h1 {
font-size: 16px;
}
.brand-sub {
font-size: 12px;
}
.section-nav {
width: 100%;
order: 2;
margin: 0 -2px;
}
.section-nav a {
min-height: 40px;
display: inline-flex;
align-items: center;
padding: 7px 12px;
}
.header-actions {
order: 3;
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.header-actions .autosave-chip {
flex: 1 1 auto;
justify-content: center;
}
.header-actions #btn-print {
flex: 0 0 auto;
min-height: 44px;
padding: 8px 16px;
}
.header-divider {
display: none;
}
.autosave-chip {
min-height: 32px;
margin: 0;
}
.ghost-btn,
.primary-btn,
label.filebtn {
min-height: 44px;
}
.drawer-body .ghost-btn,
.drawer-body .primary-btn,
.drawer-body label.filebtn {
width: 100%;
text-align: center;
}
.workspace-grid {
padding: 14px 10px 48px;
gap: 14px;
}
.panel-card {
margin-bottom: 14px;
border-radius: 12px;
}
.panel-head {
padding: 14px;
align-items: flex-start;
}
.panel-head h2 {
font-size: 16px;
padding-top: 4px;
}
.panel-body {
padding: 14px;
}
.why-panel p {
font-size: 13px;
}
.field-grid {
grid-template-columns: 1fr;
gap: 12px;
}
.field-input,
.text-input {
min-height: 44px;
font-size: 16px;
}
.field-input.sm {
font-size: 16px;
}
.segmented {
display: flex;
width: 100%;
}
.seg-opt {
flex: 1 1 auto;
}
.seg-opt span {
min-height: 40px;
display: flex;
align-items: center;
justify-content: center;
padding: 6px 9px;
text-align: center;
white-space: normal;
}
.chip-group {
gap: 7px;
}
.chip-opt {
flex: 1 1 auto;
}
.chip-opt span {
min-height: 40px;
justify-content: center;
width: 100%;
padding: 7px 10px;
text-align: center;
}
.blend-card {
padding: 12px;
gap: 10px;
}
.blend-remove,
.actuator-remove,
.icon-btn {
min-width: 44px;
min-height: 44px;
}
.milestone-row,
.milestone-row.log-row {
padding: 12px 0;
}
.milestone-plan {
min-height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.actuator-step {
gap: 0;
}
.actuator-rail {
display: none;
}
.actuator-card {
padding: 12px;
}
.dock-card {
padding: 14px;
border-radius: 12px;
}
.checks-grid {
grid-template-columns: 1fr;
}
.check-tile {
min-height: 76px;
}
.phase-divider {
flex-direction: column;
gap: 4px;
}
.panel-head {
flex-wrap: wrap;
}
.curve-wrap {
overflow-x: auto;
}
#curve-svg-live {
min-width: 520px;
}
.drawer {
width: 100%;
max-width: none;
}
.drawer-head {
padding: 14px;
}
.drawer-body {
padding: 14px;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
transition-duration: 0.01ms !important;
}
}
/* ─── Print: hide the screen shell, show the paper worksheet ───────────── */
#print-sheet {
display: none;
}
.pv {
display: none;
}
@media print {
.side-nav,
.app-header,
.drawer,
.drawer-overlay,
.workspace-grid {
display: none !important;
}
/* .app-shell/.app-workspace are otherwise flex/margin containers sized for the on-screen
nav; with everything inside them hidden above, they'd still occupy space and push
#print-sheet (a sibling further down the body) below a blank page. */
.app-shell,
.app-workspace {
display: contents;
}
body {
background: #fff;
}
#print-sheet {
display: block;
}
.ws-input {
display: none !important;
}
.pv {
display: inline-block;
min-width: inherit;
border-bottom: 0.9pt solid var(--ink);
}
.opt input:checked + span {
border: 0.9pt solid var(--ink);
background: none;
color: var(--ink);
border-radius: 50%;
padding: 0 1.4mm;
}
td.passcell.pass::after,
td.passcell.fail::after {
content: none;
}
td.passcell.pass::before {
content: "✓ ";
}
td.passcell.fail::before {
content: "✗ ";
}
}
/* ─── Legacy .opt styling reused by the (hidden-on-screen) print worksheet's own radios ── */
.opt {
display: inline-block;
cursor: pointer;
margin-right: 1.5mm;
}
.opt input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.opt span {
padding: 0 0.8mm;
border-radius: 3mm;
}
.opt input:checked + span {
background: #f6efe7;
border: 0.9pt solid #7a3b12;
color: #7a3b12;
font-weight: 700;
}
input.f,
select.f {
border: none;
border-bottom: 0.9pt solid #1a1512;
background: transparent;
font: inherit;
height: 4.2mm;
padding: 0 0.5mm;
color: #1a1512;
}