/* 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; min-height: var(--header-h); display: flex; flex-wrap: wrap; align-items: center; gap: 8px 24px; background: var(--surface-raised); border-bottom: 1px solid var(--line); padding: 8px 20px; } /* The planner header is two rows tall (brand + tools, then the section pills) — its sticky offsets (dock, anchor scroll margins) must account for that. :has() scopes it to the one page that has the plan form. */ body:has(#plan-form) { --header-h: 112px; } .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); } /* Always its own full-width row under the brand/actions — never squeezed beside them, never an overflow scroller (a hidden horizontal scroll region in a header is undiscoverable, especially on touch). At ≤720px it becomes a fixed four-column grid. */ .section-nav { display: flex; align-items: center; gap: 2px; order: 3; flex-basis: 100%; flex-wrap: wrap; } .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: 6px 8px; flex: 1 1 auto; flex-wrap: wrap; justify-content: flex-end; min-width: 0; } .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; } .check-tile.informational .check-why { display: block; } .check-tile.informational .check-band { font-style: italic; } .refine-suggestion { margin-top: 6px; padding: 8px 10px; border-radius: var(--radius-sm); border: 1px solid var(--ember-soft); background: var(--ember-soft); display: flex; flex-direction: column; align-items: flex-start; gap: 6px; } .refine-suggestion span { font-size: 11.5px; color: var(--ink-2); line-height: 1.4; } .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-row { display: flex; align-items: stretch; gap: 6px; } .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-delete { flex-shrink: 0; align-self: center; } .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; display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; } .section-nav a { min-height: 40px; display: inline-flex; align-items: center; justify-content: center; padding: 7px 2px; font-size: 11.5px; text-align: center; } .header-actions { order: 3; width: 100%; display: flex; align-items: center; gap: 8px; min-width: 0; } /* The chip yields space to the buttons rather than forcing the row wide. */ .header-actions .autosave-chip { flex: 1 1 0; min-width: 0; justify-content: center; overflow: hidden; } .header-actions .autosave-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .header-actions #btn-print, .header-actions #btn-expand-why { 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: "✗ "; } td.passcell.informational::before { content: "— "; color: var(--mid); } } /* ─── 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; } /* ── Brewer picker + brew log (brews/beans pages) ─────────────────────── */ .brewer-cat-title { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3); margin: 14px 0 6px; } .brewer-cat-title:first-child { margin-top: 0; } .brewer-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(94px, 1fr)); gap: 8px; } .brewer-tile { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 10px 6px 8px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-raised); cursor: pointer; color: var(--ink-2); font: inherit; transition: border-color 0.12s, background 0.12s, color 0.12s; } .brewer-tile:hover { border-color: var(--line-strong); color: var(--ink); } .brewer-tile.selected { border-color: var(--ember); color: var(--ember-strong); background: var(--ember-soft); box-shadow: inset 0 0 0 1px var(--ember); } .brewer-tile svg { width: 42px; height: 42px; display: block; } .brewer-tile .brewer-name { font-size: 11.5px; line-height: 1.2; text-align: center; } .method-chip { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; } .method-chip svg { width: 20px; height: 20px; flex: none; color: var(--ink-2); } .rating-pill { display: inline-block; min-width: 34px; text-align: center; padding: 2px 8px; border-radius: 999px; background: var(--surface-sunken); font-variant-numeric: tabular-nums; font-weight: 600; } .rating-pill.good { background: var(--pass-bg); color: var(--pass); } .rating-pill.poor { background: var(--fail-bg); color: var(--fail); } .token-reveal { background: var(--surface-sunken); border: 1px dashed var(--line-strong); border-radius: var(--radius-sm); padding: 10px 12px; font-family: var(--font-mono, ui-monospace, monospace); font-size: 12.5px; word-break: break-all; user-select: all; } /* ── Plan chat drawer ─────────────────────────────────────────────────── */ .chat-thread { display: flex; flex-direction: column; gap: 8px; max-height: 50vh; overflow-y: auto; margin-bottom: 10px; } .chat-msg { padding: 8px 11px; border-radius: var(--radius-md); font-size: 13px; line-height: 1.45; white-space: pre-wrap; max-width: 92%; } .chat-msg.user { align-self: flex-end; background: var(--ember-soft); color: var(--ember-strong); } .chat-msg.assistant { align-self: flex-start; background: var(--surface-sunken); color: var(--ink); } .chat-msg.pending { color: var(--ink-3); font-style: italic; } .chat-input-row { display: flex; gap: 8px; align-items: flex-end; } .chat-input-row textarea { flex: 1; resize: vertical; min-height: 40px; } /* ── Planner header tools (page-scoped actions moved out of the left nav) ── Desktop: the tools render inline in the header actions row (display: contents). ≤720px: they collapse behind a single "Tools ▾" disclosure that opens a dropdown card, so the mobile header keeps its fixed three-row height (--header-h) with no wrapped or horizontally scrolling button rows. */ .header-tools { flex-wrap: wrap; justify-content: flex-end; row-gap: 6px; max-width: 100%; position: relative; } .header-tools-toggle { display: none; } .header-tools-group { display: contents; } @media (max-width: 720px) { .header-tools { flex-wrap: nowrap; } .header-tools-toggle { display: inline-flex; align-items: center; flex: 0 0 auto; } .header-tools-toggle[aria-expanded="true"] { background: var(--ember-soft); border-color: var(--ember); color: var(--ember-strong); } .header-tools-group { display: none; } .header-tools-group.open { display: flex; flex-direction: column; align-items: stretch; gap: 6px; position: absolute; right: 0; top: calc(100% + 6px); z-index: 70; min-width: 220px; padding: 10px; background: var(--surface-raised); border: 1px solid var(--line); border-radius: var(--radius-md); box-shadow: var(--shadow-pop); } .header-tools-group .ghost-btn { justify-content: flex-start; text-align: left; width: 100%; } .header-tools-group .header-divider { display: block; width: 100%; height: 1px; margin: 2px 0; } } /* ── Academy ──────────────────────────────────────────────────────────── */ .academy-lessons { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; } .academy-lesson { display: flex; align-items: center; gap: 12px; padding: 14px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-raised); cursor: pointer; font: inherit; text-align: left; transition: border-color 0.12s, background 0.12s; } .academy-lesson:hover { border-color: var(--ember); background: var(--ember-soft); } .academy-lesson-num { flex: none; width: 30px; height: 30px; border-radius: 50%; background: var(--surface-sunken); display: inline-flex; align-items: center; justify-content: center; font-weight: 700; color: var(--ember); } .academy-lesson-label { font-weight: 600; flex: 1; } .academy-lesson-meta { font-size: 11.5px; color: var(--ink-3); white-space: nowrap; } .academy-stage { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 8px; } .academy-stage svg { width: 100%; max-width: 760px; display: block; margin: 0 auto; } .academy-caption { font-size: 15px; line-height: 1.55; max-width: 720px; margin: 14px auto 10px; min-height: 3.2em; } .academy-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: center; } .academy-dots { display: flex; gap: 6px; } .academy-dot { width: 10px; height: 10px; border-radius: 50%; border: 1px solid var(--line-strong); background: var(--surface-raised); padding: 0; cursor: pointer; } .academy-dot.active { background: var(--ember); border-color: var(--ember); } /* Scene animations — restarted per slide because the SVG is rebuilt. */ @keyframes ac-draw-kf { from { stroke-dashoffset: 900; } to { stroke-dashoffset: 0; } } .ac-draw { stroke-dasharray: 900; animation: ac-draw-kf 2.6s ease-out forwards; } @keyframes ac-pulse-kf { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } } .ac-pulse { animation: ac-pulse-kf 1.6s ease-in-out infinite; } @keyframes ac-breathe-kf { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.045); } } .ac-breathe { animation: ac-breathe-kf 3s ease-in-out infinite; transform-box: fill-box; transform-origin: center; } @keyframes ac-steam-kf { 0% { transform: translateY(6px); opacity: 0; } 30% { opacity: 0.55; } 100% { transform: translateY(-16px); opacity: 0; } } .ac-steam { animation: ac-steam-kf 2.4s ease-out infinite; } @keyframes ac-grow-w-kf { from { width: 0; } to { width: var(--w); } } .ac-grow-w { animation: ac-grow-w-kf 1.4s ease-out forwards; } @keyframes ac-grow-h-kf { from { height: 0; } to { height: 100px; } } .ac-grow-h { animation: ac-grow-h-kf 1.6s ease-out forwards; } @keyframes ac-grow-h-down-kf { from { height: 0; } to { height: 20px; } } .ac-grow-h-down { animation: ac-grow-h-down-kf 2.4s ease-out forwards; } @keyframes ac-spin-kf { to { transform: rotate(360deg); } } .ac-spin { animation: ac-spin-kf 9s linear infinite; transform-box: fill-box; transform-origin: center; } .ac-spin-slow { animation: ac-spin-kf 8s linear infinite; } @keyframes ac-sparkle-kf { 0%, 100% { opacity: 0.2; transform: scale(0.7); } 50% { opacity: 1; transform: scale(1.25); } } .ac-sparkle { animation: ac-sparkle-kf 1.8s ease-in-out infinite; transform-box: fill-box; transform-origin: center; } @keyframes ac-drift-kf { 0% { transform: translate(0, 0); opacity: 0.9; } 100% { transform: translate(26px, -20px); opacity: 0; } } .ac-drift { animation: ac-drift-kf 2.6s ease-out infinite; } @keyframes ac-shake-kf { 0%, 100% { transform: translate(0); } 25% { transform: translate(-1.5px, 1px); } 75% { transform: translate(1.5px, -1px); } } .ac-shake { animation: ac-shake-kf 0.28s linear infinite; } @keyframes ac-ring-kf { from { transform: scale(0.6); opacity: 0.9; } to { transform: scale(1.5); opacity: 0; } } .ac-ring { animation: ac-ring-kf 1.7s ease-out infinite; transform-box: fill-box; transform-origin: center; } @keyframes ac-crack-kf { from { stroke-dashoffset: 120; } to { stroke-dashoffset: 0; } } .ac-crackline { stroke-dasharray: 120; animation: ac-crack-kf 1.4s ease-in forwards; } @keyframes ac-dissolve-kf { 0% { transform: translate(0, 0); opacity: 1; } 100% { transform: translate(56px, -34px); opacity: 0; } } .ac-dissolve { animation: ac-dissolve-kf 2.4s ease-out infinite; } @keyframes ac-float-kf { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } } .ac-float { animation: ac-float-kf 2.6s ease-in-out infinite; } @keyframes ac-rise-kf { 0% { transform: translateY(8px); opacity: 0; } 40% { opacity: 1; } 100% { transform: translateY(-22px); opacity: 0; } } .ac-rise { animation: ac-rise-kf 2s ease-out infinite; } @keyframes ac-drip-kf { 0% { transform: translateY(0); opacity: 1; } 100% { transform: translateY(14px); opacity: 0; } } .ac-drip { animation: ac-drip-kf 1.1s ease-in infinite; } @keyframes ac-tamp-kf { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(9px); } } .ac-tamp { animation: ac-tamp-kf 2s ease-in-out infinite; } @keyframes ac-slide-x-kf { 0%, 100% { transform: translateX(-46px); } 50% { transform: translateX(46px); } } .ac-slide-x { animation: ac-slide-x-kf 5s ease-in-out infinite; } @keyframes ac-dial-kf { 0%, 100% { transform: rotate(-38deg); } 50% { transform: rotate(38deg); } } .ac-dial { animation: ac-dial-kf 6s ease-in-out infinite; } @keyframes ac-pour-kf { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } } .ac-pourline { animation: ac-pour-kf 1.6s ease-in-out infinite; } @keyframes ac-maillard-kf { 0% { fill: #d9ba5a; } 100% { fill: #8a5a30; } } .ac-maillard-shift ellipse:first-child { animation: ac-maillard-kf 5s ease-in-out infinite alternate; } @keyframes ac-tumble-kf { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-8px) rotate(6deg); } } .ac-tumble { animation: ac-tumble-kf 1.6s ease-in-out infinite; transform-box: fill-box; transform-origin: center; } @keyframes ac-flame-kf { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(1.12); } } .ac-flame { animation: ac-flame-kf 0.9s ease-in-out infinite; transform-box: fill-box; transform-origin: bottom center; } .ac-delay-0 { animation-delay: 0s; } .ac-delay-1 { animation-delay: 0.55s; } .ac-delay-2 { animation-delay: 1.1s; } .ac-delay-3 { animation-delay: 1.65s; } .ac-delay-4 { animation-delay: 2.2s; } @media (prefers-reduced-motion: reduce) { .academy-stage svg * { animation: none !important; } } /* Academy v2: progress + refined scene animations */ .academy-lesson-mid { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; } .academy-lesson-num.done { background: var(--pass-bg); color: var(--pass); } .academy-progress { display: block; height: 4px; border-radius: 2px; background: var(--surface-sunken); overflow: hidden; } .academy-progress-fill { display: block; height: 100%; background: var(--ember); border-radius: 2px; } @keyframes ac-fill-up-kf { from { transform: scaleY(0.12); } to { transform: scaleY(0.86); } } .ac-fill-up { transform-box: fill-box; transform-origin: bottom; animation: ac-fill-up-kf 5s ease-out forwards; } @keyframes ac-dry-shift-kf { 0% { fill: #7a8c5e; } 100% { fill: #d9ba5a; } } .ac-dry-shift > ellipse:first-child { animation: ac-dry-shift-kf 6s ease-in-out infinite alternate; } @keyframes ac-needle-kf { 0%, 100% { transform: rotate(-52deg); } 50% { transform: rotate(52deg); } } .ac-needle { animation: ac-needle-kf 7s ease-in-out infinite; } @keyframes ac-pulse-soft-kf { 0%, 100% { opacity: 1; } 50% { opacity: 0.75; } } .ac-pulse-soft { animation: ac-pulse-soft-kf 2.4s ease-in-out infinite; }