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]>
This commit is contained in:
2026-07-29 15:41:35 -04:00
co-authored by Claude Sonnet 5
commit fedaf29847
22 changed files with 5000 additions and 0 deletions
+359
View File
@@ -0,0 +1,359 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Roast Planner</title>
<link rel="stylesheet" href="/worksheet.css">
<link rel="stylesheet" href="/app.css">
</head>
<body>
<div class="app-toolbar">
<h1>Roast Planner</h1>
<button id="btn-toggle-prefill" type="button">Prefill from URL</button>
<button id="btn-toggle-alog" type="button">Reference curve (.alog)</button>
<span class="spacer"></span>
<button id="btn-load" type="button">Load plan</button>
<input id="file-load" type="file" accept="application/json">
<button id="btn-save" type="button">Download plan</button>
<button id="btn-print" type="button">Print</button>
<span class="status" id="autosave-status"></span>
</div>
<div class="panel hidden" id="panel-prefill">
<h3>Prefill from a bean product URL</h3>
<div class="row">
<input type="url" id="prefill-url" placeholder="https://roaster.example/shop/some-coffee">
<label><input type="checkbox" id="prefill-overwrite"> overwrite filled fields</label>
<button id="prefill-go" type="button">Fetch &amp; prefill</button>
<button id="prefill-undo" type="button" disabled>Undo prefill</button>
</div>
<div id="prefill-result"></div>
</div>
<div class="panel hidden" id="panel-alog">
<h3>Attach an Artisan .alog as a reference curve</h3>
<div class="row">
<label class="filebtn">Choose .alog file<input type="file" id="alog-file" accept=".alog"></label>
<button id="alog-library-refresh" type="button">Show local library</button>
</div>
<ul class="lib-list hidden" id="alog-library-list"></ul>
<div id="alog-result"></div>
</div>
<div class="sheet-wrap">
<div class="sheet">
<form id="plan-form">
<!-- ══════════════════ FRONT ══════════════════ -->
<section class="first">
<div class="wsbar">
<span class="t">Manual Roast Plan — Worksheet</span>
<span class="r">Sheet 1 of 2 · Plan | Hottop KN-8828B-2K+</span>
</div>
<div class="wsbox">
<div class="hd"><span class="id">THE COFFEE</span><span class="ref">[n.n] = field ID · cited everywhere it feeds a number</span></div>
<div class="bd wsline tightln">
<span class="fid">0.1</span><span class="lb">Coffee / lot</span> <input class="f ws-input" style="min-width:44mm" name="0.1"><span class="pv"></span>
<span class="sep">|</span><span class="fid">0.2</span><span class="lb">Date</span> <input class="f n ws-input" type="date" name="0.2"><span class="pv"></span>
<span class="sep">|</span><span class="fid">0.3</span><span class="lb">Batch&nbsp;#</span> <input class="f ws-input" style="min-width:10mm" name="0.3"><span class="pv"></span>
<span class="sep">|</span><span class="fid">0.4</span><span class="lb">Green in</span> <input class="f n ws-input" name="0.4">g<span class="pv"></span><br>
<span class="stepdot">1</span><span class="fid">1.1</span><span class="lb">Cultivar</span> <input class="f ws-input" style="min-width:30mm" name="1.1" list="cultivar-list"><span class="pv"></span>
<span class="sep">|</span><span class="fid">1.2</span><span class="lb">Group</span>
<span class="circ" data-radio-group="1.2">
<label class="opt"><input type="radio" name="1.2" value="typica"><span>Typica</span></label>
<label class="opt"><input type="radio" name="1.2" value="bourbon"><span>Bourbon</span></label>
<label class="opt"><input type="radio" name="1.2" value="ethiopian"><span>Ethiopian</span></label>
<label class="opt"><input type="radio" name="1.2" value="hybrid"><span>Hybrid</span></label>
</span>
<span class="sep">|</span><span class="fid">1.3</span><span class="lb">Origin</span> <input class="f ws-input" style="min-width:20mm" name="1.3"><span class="pv"></span><br>
<span class="fid" style="margin-left:4.6mm">1.4</span><span class="lb">FC anchor</span> <input class="f n ws-input" name="1.4"><span class="pv"></span>
<span class="sep">|</span><span class="fid">1.5</span><span class="lb">Profile mm:ss|mm:ss|mm:ss</span> <input class="f ws-input" style="min-width:22mm" name="1.5"><span class="pv"></span>
<span class="sep">|</span><small>your cultivar's row, Step 1 — not a group number</small><br>
<span class="fid" style="margin-left:4.6mm">1.6</span><span class="lb">± Refine → ledger 2</span> <input class="f n ws-input" name="1.6"><span class="pv"></span> <small>0 first cook, else Step 12</small>
<span class="sep">|</span><span class="fid">1.7</span><span class="lb">± Dev mod → ledger 9</span> <input class="f n ws-input" name="1.7"><span class="pv"></span> <small>Step 1's profile 3rd # 1:30</small><br>
<span class="stepdot">3</span><span class="fid">3.1</span><span class="lb">Process</span>
<span class="circ" data-radio-group="3.1">
<label class="opt"><input type="radio" name="3.1" value="washed"><span>washed</span></label>
<label class="opt"><input type="radio" name="3.1" value="yellow-honey"><span>yellow/white honey</span></label>
<label class="opt"><input type="radio" name="3.1" value="red-black-honey"><span>red/black honey</span></label>
<label class="opt"><input type="radio" name="3.1" value="natural"><span>natural</span></label>
<label class="opt"><input type="radio" name="3.1" value="anaerobic"><span>anaerobic/CM</span></label>
</span>
<span class="sep">|</span><span class="fid">3.2</span><span class="lb">Dev mod → ledger 8</span> <input class="f n ws-input" name="3.2"><span class="pv"></span> <small>Step 3 table</small><br>
<span class="stepdot">4</span><span class="fid">4.1</span><span class="lb">Roast level</span>
<span class="circ" data-radio-group="4.1">
<label class="opt"><input type="radio" name="4.1" value="light"><span>light</span></label>
<label class="opt"><input type="radio" name="4.1" value="light-medium"><span>light-med</span></label>
<label class="opt"><input type="radio" name="4.1" value="medium"><span>medium</span></label>
<label class="opt"><input type="radio" name="4.1" value="dark"><span>dark</span></label>
<label class="opt"><input type="radio" name="4.1" value="very-dark"><span>very dark</span></label>
</span>
<span class="sep">|</span><span class="fid">4.2</span><span class="lb">Target WL</span> <input class="f n ws-input" name="4.2">%<span class="pv"></span>
<span class="sep">|</span><span class="fid">4.3</span><span class="lb">Dev base → ledger 7</span> <input class="f n ws-input" style="min-width:12mm" name="4.3"><span class="pv"></span><br>
<span class="stepdot">5</span><span class="fid">5.1</span><span class="lb">Moisture</span> <input class="f n ws-input" name="5.1">%<span class="pv"></span>
<span class="sep">|</span><span class="fid">5.2</span><span class="lb">Density</span> <input class="f n ws-input" name="5.2">g/L<span class="pv"></span>
<span class="sep">|</span><span class="fid">5.3</span><span class="lb">Altitude</span> <input class="f n ws-input" name="5.3"><span class="pv"></span>
<span class="sep">|</span><span class="fid">5.4</span><span class="lb">Screen</span> <input class="f n ws-input" name="5.4"><span class="pv"></span>
<span class="sep">|</span><span class="fid">5.5</span><span class="lb">Sweet</span>
<span class="circ" data-radio-group="5.5">
<label class="opt"><input type="radio" name="5.5" value="narrow"><span>narrow</span></label>
<label class="opt"><input type="radio" name="5.5" value="med"><span>med</span></label>
<label class="opt"><input type="radio" name="5.5" value="wide"><span>wide</span></label>
</span><br>
<span class="fid" style="margin-left:4.6mm">5.6</span><span class="lb">± Net correction → ledger 3</span> <input class="f n ws-input" name="5.6"><span class="pv"></span> <small>judgment, rarely &gt;±0:15</small>
</div>
</div>
<div class="wsbox">
<div class="hd"><span class="n">2 · BLEND</span><span class="ref">skip if single-origin → Step 2</span></div>
<div class="bd">
<div class="wsline tightln">
<span class="fid">2.1</span>This lot is:
<span class="circ" data-radio-group="2.1">
<label class="opt"><input type="radio" name="2.1" value="single" checked><span>single-origin — skip this box</span></label>
<label class="opt"><input type="radio" name="2.1" value="blend"><span>blend or community lot</span></label>
</span>
</div>
<table class="tight">
<thead><tr><th><span class="fid">2.2</span>Component</th><th style="width:22mm">Group</th><th style="width:24mm">Process</th>
<th class="num" style="width:14mm">Share %</th><th class="num" style="width:20mm">FC anchor</th></tr></thead>
<tbody id="blend-rows"></tbody>
</table>
<div class="wsline tightln">
<span class="fid">2.3</span>Method:
<span class="circ" data-radio-group="2.3">
<label class="opt"><input type="radio" name="2.3" value="dominant"><span>dominant (≥60%)</span></label>
<label class="opt"><input type="radio" name="2.3" value="weighted"><span>weighted average</span></label>
<label class="opt"><input type="radio" name="2.3" value="split"><span>split-roast, blend after</span></label>
</span>
</div>
<div class="wsline tightln">
<span class="fid">2.4</span><span class="lb">Resulting FC anchor → ledger line 1</span> <input class="f n ws-input" name="2.4"><span class="pv"></span>
<span class="sep">|</span><span class="fid">2.5</span><span class="lb">Resulting processing modifier → ledger line 8</span> <input class="f n ws-input" name="2.5"><span class="pv"></span>
</div>
</div>
</div>
<div class="wsbox">
<div class="hd"><span class="n">6 · TIME LEDGER</span><span class="ref">method &amp; why → Step 6</span></div>
<div class="bd">
<table class="ledger">
<tr class="row"><td class="ln">1</td><td class="desc">Cultivar/blend anchor
<span class="sub">from 1.4, or 2.4 if blend</span></td><td class="val"><output data-out="l1"></output></td></tr>
<tr class="row"><td class="ln">2</td><td class="desc"><span class="op">±</span> Refinement — from 1.6
<span class="sub">0 first cook, else Step 12</span></td><td class="val"><output data-out="l2"></output></td></tr>
<tr class="row"><td class="ln">3</td><td class="desc"><span class="op">±</span> Bean condition — from 5.6</td><td class="val"><output data-out="l3"></output></td></tr>
<tr class="row"><td class="ln">4</td><td class="desc"><span class="op">±</span> Batch size vs. Step 9 reference weights
<span class="sub">225 g ≈ 1:302:00 later than 200 g</span></td><td class="val"><input class="f n ws-input" name="6.4" placeholder="0"><span class="pv"></span></td></tr>
<tr class="total"><td class="ln">A</td><td class="desc"><strong>FIRST CRACK</strong></td><td class="val"><output data-out="A"></output></td></tr>
<tr class="grp"><td class="ln"></td><td class="desc" colspan="2">B · YELLOW / MAILLARD — informational only. NOT summed into C.</td></tr>
<tr class="row"><td class="ln">5</td><td class="desc"><strong>YELLOW</strong> = A × 0.56</td><td class="val"><output data-out="yellow"></output></td></tr>
<tr class="row"><td class="ln">6</td><td class="desc"><strong>MAILLARD</strong> = A line 5</td><td class="val"><output data-out="maillard"></output></td></tr>
<tr class="grp"><td class="ln"></td><td class="desc" colspan="2">C · DEVELOPMENT — sum ONLY rows 7 + 8 + 9</td></tr>
<tr class="row"><td class="ln">7</td><td class="desc">Development base — from 4.3</td><td class="val"><output data-out="l7"></output></td></tr>
<tr class="row"><td class="ln">8</td><td class="desc"><span class="op">±</span> Processing modifier — from 3.2, or 2.5 if blend</td><td class="val"><output data-out="l8"></output></td></tr>
<tr class="row"><td class="ln">9</td><td class="desc"><span class="op">±</span> Cultivar dev modifier — from 1.7</td><td class="val"><output data-out="l9"></output></td></tr>
<tr class="total"><td class="ln">C</td><td class="desc"><strong>DEVELOPMENT</strong> = 7 + 8 + 9 only</td><td class="val"><output data-out="C"></output></td></tr>
<tr class="total"><td class="ln">D</td><td class="desc"><strong>DROP (end of roast)</strong> = A + C</td><td class="val"><output data-out="D"></output></td></tr>
</table>
</div>
</div>
<div class="wsbox">
<div class="hd"><span class="n">7 · SANITY CHECKS</span><span class="ref">a fail sends you back to box 6 → Step 7</span></div>
<div class="bd">
<table class="tight">
<thead><tr><th style="width:44mm">Check</th><th class="num" style="width:24mm">Should be</th>
<th class="num" style="width:24mm">Yours</th><th class="num" style="width:16mm">Pass?</th><th>Failure means</th></tr></thead>
<tbody>
<tr><td>Drying share</td><td class="num">4351 %</td><td class="num"><output data-out="check-drying"></output></td>
<td class="num passcell unknown" data-pass="drying"></td><td>high = late yellow · low = rushed</td></tr>
<tr><td>Maillard share</td><td class="num">3339 %</td><td class="num"><output data-out="check-maillard"></output></td>
<td class="num passcell unknown" data-pass="maillard"></td><td>low = too little browning</td></tr>
<tr><td>Development ratio</td><td class="num">1220 %</td><td class="num"><output data-out="check-dtr"></output></td>
<td class="num passcell unknown" data-pass="dtr"></td><td>under 12 % = under-developed</td></tr>
<tr><td>Development ceiling</td><td class="num">&lt; 4:30</td><td class="num"><output data-out="check-ceiling"></output></td>
<td class="num passcell unknown" data-pass="ceiling"></td><td>over 4:30 mutes any roast</td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- ══════════════════ BACK ══════════════════ -->
<section>
<div class="wsbar">
<span class="t">Roast &amp; Record</span>
<span class="r">Sheet 2 of 2 | coffee: <span id="back-coffee-name"></span></span>
</div>
<div class="wsbox">
<div class="hd"><span class="n">8 · TEMPERATURES &amp; RATE-OF-RISE</span><span class="ref">→ Step 8</span></div>
<div class="bd">
<table class="tight">
<thead><tr><th style="width:28mm">Milestone</th><th class="num" style="width:20mm">Time</th>
<th class="num" style="width:18mm">°C</th><th class="num" style="width:36mm">Your logged band</th></tr></thead>
<tbody>
<tr><td>Charge</td><td class="num"><output data-out="t-charge">0:00</output></td>
<td class="num"><input class="f n ws-input" name="temps.charge.tempC"><span class="pv"></span></td><td class="num">133195 °C</td></tr>
<tr><td>Turning point</td><td class="num"><input class="f n ws-input" name="temps.tp.time" placeholder="m:ss"><span class="pv"></span></td>
<td class="num"><input class="f n ws-input" name="temps.tp.tempC"><span class="pv"></span></td><td class="num">77100 °C @ 0:471:15</td></tr>
<tr><td>Yellow</td><td class="num"><output data-out="t-yellow"></output></td>
<td class="num"><input class="f n ws-input" name="temps.yellow.tempC"><span class="pv"></span></td><td class="num">148166 °C</td></tr>
<tr><td>First crack</td><td class="num"><output data-out="t-fc"></output></td>
<td class="num"><input class="f n ws-input" name="temps.fc.tempC"><span class="pv"></span></td><td class="num">176187 °C</td></tr>
<tr><td>Drop</td><td class="num"><output data-out="t-drop"></output></td>
<td class="num"><input class="f n ws-input" name="temps.drop.tempC"><span class="pv"></span></td><td class="num">183197 °C light</td></tr>
</tbody>
</table>
</div>
</div>
<div class="wsbox">
<div class="hd"><span class="n">9 · ACTUATOR SCHEDULE (planned)</span><span class="ref">→ Step 9</span></div>
<div class="bd">
<table class="tight">
<thead><tr><th class="num" style="width:20mm">Time</th><th class="num" style="width:18mm">Heat %</th>
<th class="num" style="width:18mm">Fan %</th><th class="num" style="width:26mm">Expected BT</th><th>Why this change</th></tr></thead>
<tbody id="actuator-rows"></tbody>
</table>
</div>
</div>
<div class="wsbox wsgrid">
<div class="hd"><span class="n">10 · THE CURVE</span><span class="ref">plan drawn live · reference from an .alog if attached → Step 10</span></div>
<div class="bd">
<div class="grid-wrap">
<svg id="curve-svg" viewBox="0 0 714 334" width="100%" role="img" aria-label="Roast curve plotting grid">
<defs><style>
.maj{stroke:#8b8078;stroke-width:.7} .min{stroke:#e2dad2;stroke-width:.45}
.ax{stroke:#1a1512;stroke-width:1.3} .al{font-size:8px;fill:#5a5048}
.alr{font-size:8px;fill:#7a3b12} .band{fill:#7a3b12;opacity:.05}
.ttl{font-size:8px;font-weight:700;fill:#1a1512;letter-spacing:.5px}
</style></defs>
<rect x="52" y="24" width="600" height="270" fill="#fff"/>
<rect class="band" x="52" y="65.4" width="600" height="19.8"/>
<text x="58" y="62.4" class="alr" style="font-size:7px">your first-crack band, 176187 °C</text>
<g class="min">
<line x1="72" y1="24" x2="72" y2="294"/><line x1="112" y1="24" x2="112" y2="294"/>
<line x1="152" y1="24" x2="152" y2="294"/><line x1="192" y1="24" x2="192" y2="294"/>
<line x1="232" y1="24" x2="232" y2="294"/><line x1="272" y1="24" x2="272" y2="294"/>
<line x1="312" y1="24" x2="312" y2="294"/><line x1="352" y1="24" x2="352" y2="294"/>
<line x1="392" y1="24" x2="392" y2="294"/><line x1="432" y1="24" x2="432" y2="294"/>
<line x1="472" y1="24" x2="472" y2="294"/><line x1="512" y1="24" x2="512" y2="294"/>
<line x1="552" y1="24" x2="552" y2="294"/><line x1="592" y1="24" x2="592" y2="294"/>
<line x1="632" y1="24" x2="632" y2="294"/>
</g>
<g class="maj">
<line x1="92" y1="24" x2="92" y2="294"/><line x1="132" y1="24" x2="132" y2="294"/>
<line x1="172" y1="24" x2="172" y2="294"/><line x1="212" y1="24" x2="212" y2="294"/>
<line x1="252" y1="24" x2="252" y2="294"/><line x1="292" y1="24" x2="292" y2="294"/>
<line x1="332" y1="24" x2="332" y2="294"/><line x1="372" y1="24" x2="372" y2="294"/>
<line x1="412" y1="24" x2="412" y2="294"/><line x1="452" y1="24" x2="452" y2="294"/>
<line x1="492" y1="24" x2="492" y2="294"/><line x1="532" y1="24" x2="532" y2="294"/>
<line x1="572" y1="24" x2="572" y2="294"/><line x1="612" y1="24" x2="612" y2="294"/>
</g>
<g class="min">
<line x1="52" y1="276" x2="652" y2="276"/><line x1="52" y1="240" x2="652" y2="240"/>
<line x1="52" y1="204" x2="652" y2="204"/><line x1="52" y1="168" x2="652" y2="168"/>
<line x1="52" y1="132" x2="652" y2="132"/><line x1="52" y1="96" x2="652" y2="96"/>
<line x1="52" y1="60" x2="652" y2="60"/>
</g>
<g class="maj">
<line x1="52" y1="258" x2="652" y2="258"/><line x1="52" y1="222" x2="652" y2="222"/>
<line x1="52" y1="186" x2="652" y2="186"/><line x1="52" y1="150" x2="652" y2="150"/>
<line x1="52" y1="114" x2="652" y2="114"/><line x1="52" y1="78" x2="652" y2="78"/>
<line x1="52" y1="42" x2="652" y2="42"/>
</g>
<line class="ax" x1="52" y1="24" x2="52" y2="294"/>
<line class="ax" x1="52" y1="294" x2="652" y2="294"/>
<line class="ax" x1="652" y1="24" x2="652" y2="294"/>
<g class="al" text-anchor="end">
<text x="47" y="296.8">60</text><text x="47" y="260.8">80</text><text x="47" y="224.8">100</text>
<text x="47" y="188.8">120</text><text x="47" y="152.8">140</text><text x="47" y="116.8">160</text>
<text x="47" y="80.8">180</text><text x="47" y="44.8">200</text>
</g>
<text x="14" y="159" class="ttl" transform="rotate(-90 14 159)" text-anchor="middle">BEAN TEMP °C</text>
<g class="al" text-anchor="middle">
<text x="52" y="304">0:00</text><text x="92" y="304">1:00</text><text x="132" y="304">2:00</text>
<text x="172" y="304">3:00</text><text x="212" y="304">4:00</text><text x="252" y="304">5:00</text>
<text x="292" y="304">6:00</text><text x="332" y="304">7:00</text><text x="372" y="304">8:00</text>
<text x="412" y="304">9:00</text><text x="452" y="304">10:00</text><text x="492" y="304">11:00</text>
<text x="532" y="304">12:00</text><text x="572" y="304">13:00</text><text x="612" y="304">14:00</text>
<text x="652" y="304">15:00</text>
</g>
<text x="368" y="320" style="font-size:8px;fill:#5a5048">TIME FROM CHARGE · solid = plan · dashed = reference (.alog)</text>
<g id="plan-curve"></g>
<g id="ref-curve"></g>
</svg>
</div>
</div>
</div>
<div class="wsbox">
<div class="hd"><span class="n">11 · AT THE MACHINE — PLAN vs ACTUAL</span><span class="ref">→ Step 11</span></div>
<div class="bd">
<table class="tight">
<thead><tr><th style="width:28mm">Milestone</th><th class="num" style="width:20mm">Plan time</th>
<th class="num" style="width:22mm">Actual time</th><th class="num" style="width:18mm">Actual BT</th><th>Note</th></tr></thead>
<tbody>
<tr><td>Charge</td><td class="num"><output data-out="pa-charge">0:00</output></td>
<td class="num"><input class="f n ws-input" name="planActual.charge.actualTime" placeholder="0:00"><span class="pv"></span></td>
<td class="num"><input class="f n ws-input" name="planActual.charge.actualBt"><span class="pv"></span></td>
<td><input class="f ws-input" style="min-width:52mm" name="planActual.charge.note"><span class="pv"></span></td></tr>
<tr><td>Turning point</td><td class="num"><output data-out="pa-tp"></output></td>
<td class="num"><input class="f n ws-input" name="planActual.tp.actualTime"><span class="pv"></span></td>
<td class="num"><input class="f n ws-input" name="planActual.tp.actualBt"><span class="pv"></span></td>
<td><input class="f ws-input" style="min-width:52mm" name="planActual.tp.note"><span class="pv"></span></td></tr>
<tr><td>Yellow</td><td class="num"><output data-out="pa-yellow"></output></td>
<td class="num"><input class="f n ws-input" name="planActual.yellow.actualTime"><span class="pv"></span></td>
<td class="num"><input class="f n ws-input" name="planActual.yellow.actualBt"><span class="pv"></span></td>
<td><input class="f ws-input" style="min-width:52mm" name="planActual.yellow.note"><span class="pv"></span></td></tr>
<tr><td>First crack</td><td class="num"><output data-out="pa-fc"></output></td>
<td class="num"><input class="f n ws-input" name="planActual.fc.actualTime"><span class="pv"></span></td>
<td class="num"><input class="f n ws-input" name="planActual.fc.actualBt"><span class="pv"></span></td>
<td><input class="f ws-input" style="min-width:52mm" name="planActual.fc.note"><span class="pv"></span></td></tr>
<tr><td>Drop</td><td class="num"><output data-out="pa-drop"></output></td>
<td class="num"><input class="f n ws-input" name="planActual.drop.actualTime"><span class="pv"></span></td>
<td class="num"><input class="f n ws-input" name="planActual.drop.actualBt"><span class="pv"></span></td>
<td><input class="f ws-input" style="min-width:52mm" name="planActual.drop.note"><span class="pv"></span></td></tr>
</tbody>
</table>
</div>
</div>
<div class="wsbox">
<div class="hd"><span class="n">12 · AFTER THE ROAST</span><span class="ref">→ Step 12</span></div>
<div class="bd wsline tightln">
<span class="lb">Green in</span> <input class="f n ws-input" name="afterRoast.greenIn"><span class="pv"></span>g
<span class="sep">|</span><span class="lb">Out</span> <input class="f n ws-input" name="afterRoast.out"><span class="pv"></span>g
<span class="sep">|</span><span class="lb">Weight loss</span> <input class="f n ws-input" name="afterRoast.weightLossPct"><span class="pv"></span>%
<span class="sep">|</span><span class="lb">Actual DTR</span> <input class="f n ws-input" name="afterRoast.actualDtrPct"><span class="pv"></span>%<br>
<span class="lb">Colour WB/GR</span> <input class="f ws-input" name="afterRoast.colour"><span class="pv"></span>
<span class="sep">|</span><span class="lb">Rested</span> <input class="f n ws-input" name="afterRoast.restedDays"><span class="pv"></span>days
<span class="sep">|</span><span class="lb">Brew ratio</span> <input class="f ws-input" name="afterRoast.brewRatio"><span class="pv"></span>
<span class="sep">|</span><span class="lb">Method</span> <input class="f w ws-input" name="afterRoast.method"><span class="pv"></span><br>
<span class="lb">Cup notes</span> <input class="f xw ws-input" style="min-width:152mm" name="afterRoast.cupNotes"><span class="pv"></span><br>
<span class="lb" style="color:var(--accent)">One change next batch</span> <input class="f ws-input" style="min-width:58mm" name="afterRoast.oneChange"><span class="pv"></span>
<span class="sep">|</span><span class="lb">What would prove it wrong</span> <input class="f ws-input" style="min-width:34mm" name="afterRoast.disproof"><span class="pv"></span>
</div>
</div>
</section>
</form>
</div>
</div>
<datalist id="cultivar-list"></datalist>
<script type="module" src="/js/main.js"></script>
</body>
</html>