Files
roast_command_center/public/app.css
T
snowspeederandClaude Sonnet 5 fedaf29847 Initial roast planner webapp: fillable worksheet, live ledger, curve, URL prefill, .alog reference curve
- shared/ ports the worksheet's ledger math, time parsing, and reference tables
  (cultivars/processes/roast-levels/machine bands) as browser-safe ESM, imported
  by both the server and the browser so the arithmetic can't drift between them.
- server/prefill.js runs a zero-tool Pi Coding Agent SDK turn to extract page facts
  from a bean product URL, then derives worksheet field IDs deterministically from
  reference-data.js — the model never invents a first-crack anchor or a modifier.
- server/alog.js ports the Python alog_parser.py's format handling, including the
  tokenizer-based Python-dict-literal-to-JSON conversion the real files need.
- public/ is the worksheet reproduced as a live HTML form: worksheet.css is a
  verbatim copy of the paper worksheet's print CSS, print.js bakes values into
  the print layout so the same DOM renders both on screen and on paper.
- Ledger math verified against both of the paper worksheet's worked examples;
  alog parser verified against all 14 real logs in ref/roasts/.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-07-29 15:41:35 -04:00

86 lines
4.2 KiB
CSS

/* Screen chrome + form-control styling layered on top of worksheet.css. Everything here is
either screen-only (hidden in print) or makes an <input>/<select>/<radio> look exactly like
the paper's blank/circle it replaces. */
body { background: #ddd4c8; }
.app-toolbar {
position: sticky; top: 0; z-index: 10;
display: flex; align-items: center; gap: 3mm; flex-wrap: wrap;
background: #1a1512; color: #fff; padding: 3mm 4mm;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 9pt;
}
.app-toolbar h1 { font-size: 12pt; margin: 0; color: #fff; flex: 0 0 auto; }
.app-toolbar .spacer { flex: 1 1 auto; }
.app-toolbar button, .app-toolbar label.filebtn {
background: #7a3b12; color: #fff; border: none; border-radius: 1mm;
padding: 1.6mm 3mm; font-size: 8.6pt; cursor: pointer; font-family: inherit;
}
.app-toolbar button:hover, .app-toolbar label.filebtn:hover { background: #94481a; }
.app-toolbar button:disabled { opacity: .5; cursor: default; }
.app-toolbar input[type="file"] { display: none; }
.app-toolbar .status { font-size: 7.6pt; color: #cbbfae; }
.panel {
max-width: 186mm; margin: 4mm auto 0; padding: 3mm 4mm;
background: #fff8ee; border: 1px solid #d8cdb9; border-radius: 1mm;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 8.6pt;
}
.panel.hidden { display: none; }
.panel h3 { margin: 0 0 2mm; font-size: 9.5pt; color: #7a3b12; }
.panel .row { display: flex; gap: 2mm; align-items: center; margin-bottom: 2mm; flex-wrap: wrap; }
.panel input[type="url"], .panel input[type="text"] {
flex: 1 1 260px; padding: 1.6mm 2mm; border: 1px solid #c9bfb5; border-radius: 1mm; font-size: 8.6pt;
}
.panel .warnings { color: #8a5c10; font-size: 7.8pt; }
.panel .warnings li { margin-bottom: .6mm; }
.panel .lib-list { list-style: none; padding: 0; margin: 0; max-height: 40mm; overflow-y: auto; }
.panel .lib-list li { padding: 1mm 0; border-bottom: 1px solid #eee; cursor: pointer; font-size: 8.2pt; }
.panel .lib-list li:hover { background: #f6efe7; }
.sheet-wrap { max-width: 186mm; margin: 6mm auto; box-shadow: 0 2px 12px rgba(0,0,0,.25); background: #fff; }
/* --- form controls that must look like the paper's .f blank / .circ options --- */
input.f, select.f {
border: none; border-bottom: .9pt solid var(--ink); background: transparent;
font: inherit; height: 4.2mm; padding: 0 .5mm; color: var(--ink);
}
input.f:focus, select.f:focus { outline: 1px dashed var(--accent); outline-offset: 1px; }
select.f { -webkit-appearance: none; appearance: none; }
textarea.f {
border: none; border-bottom: .9pt solid var(--ink); background: transparent;
font: inherit; resize: none; padding: 0 .5mm; color: var(--ink); vertical-align: bottom;
}
.opt { display: inline-block; cursor: pointer; margin-right: 1.5mm; }
.opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.opt span { padding: 0 .8mm; border-radius: 3mm; }
.opt input:checked + span {
background: var(--accent-bg); border: .9pt solid var(--accent); color: var(--accent); font-weight: 700;
}
.ledger td.val { position: relative; }
.ledger td.val output { display: block; }
.ledger tr.row.check-fail td.val { background: #fbe7e2 !important; }
.ledger tr.row.check-pass td.val { background: #e6f3e6 !important; }
td.passcell.pass::after { content: "✓"; color: #2a7a2a; font-weight: 700; }
td.passcell.fail::after { content: "✗"; color: #a8371a; font-weight: 700; }
td.passcell.unknown::after { content: "—"; color: var(--faint); }
input.prefilled, select.prefilled { background: #fff3c4; }
.pv { display: none; }
/* --- print: bake values in, hide screen-only chrome --- */
@media print {
.app-toolbar, .panel, .sheet-wrap { box-shadow: none; margin: 0; max-width: none; }
.app-toolbar, .panel { display: none !important; }
body { background: #fff; }
.ws-input { display: none !important; }
.pv { display: inline-block; min-width: inherit; border-bottom: .9pt solid var(--ink); }
.opt input:checked + span { border: .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: "✗ "; }
}