2004 lines
82 KiB
HTML
2004 lines
82 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Roast Planner</title>
|
||
<meta name="theme-color" content="#A8481A" />
|
||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||
<link rel="manifest" href="/manifest.webmanifest" />
|
||
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
|
||
<link rel="apple-touch-icon" href="/icon.svg" />
|
||
<link rel="stylesheet" href="/worksheet.css" media="print" />
|
||
<link rel="stylesheet" href="/app.css" />
|
||
</head>
|
||
<body>
|
||
<header class="app-header">
|
||
<div class="brand">
|
||
<span class="brand-mark" aria-hidden="true">◐</span>
|
||
<div class="brand-text">
|
||
<h1>Roast Planner</h1>
|
||
<p class="brand-sub" id="header-coffee-name">New plan</p>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="section-nav" id="section-nav" aria-label="Jump to section">
|
||
<a href="#sec-coffee">Coffee</a>
|
||
<a href="#sec-blend">Blend</a>
|
||
<a href="#sec-process">Roast target</a>
|
||
<a href="#sec-bean">Bean condition</a>
|
||
<a href="#sec-machine">Machine plan</a>
|
||
<a href="#sec-roastlog">Roast log</a>
|
||
<a href="#sec-after">After the roast</a>
|
||
</nav>
|
||
|
||
<div class="header-actions">
|
||
<button
|
||
id="btn-toggle-fids"
|
||
type="button"
|
||
class="ghost-btn"
|
||
aria-pressed="false"
|
||
title="Show the worksheet reference codes (e.g. 1.4) on every field"
|
||
>
|
||
Field IDs
|
||
</button>
|
||
<button id="btn-toggle-prefill" type="button" class="ghost-btn">
|
||
Prefill from URL
|
||
</button>
|
||
<button id="btn-toggle-alog" type="button" class="ghost-btn">
|
||
Reference curve
|
||
</button>
|
||
<span class="header-divider" aria-hidden="true"></span>
|
||
<button id="btn-load" type="button" class="ghost-btn">Load</button>
|
||
<input id="file-load" type="file" accept="application/json" />
|
||
<button id="btn-save" type="button" class="ghost-btn">Download</button>
|
||
<details class="account-menu">
|
||
<summary class="ghost-btn">Account</summary>
|
||
<div class="account-menu-popover">
|
||
<a id="menu-admin" class="hidden" href="/admin">Admin</a>
|
||
<button id="btn-logout" type="button">Log out</button>
|
||
</div>
|
||
</details>
|
||
<button id="btn-print" type="button" class="primary-btn">Print</button>
|
||
<span class="autosave-chip" id="autosave-status"
|
||
><span class="dot"></span
|
||
><span class="autosave-text">Not saved yet</span></span
|
||
>
|
||
</div>
|
||
</header>
|
||
|
||
<div
|
||
class="connection-status hidden"
|
||
id="connection-status"
|
||
role="status"
|
||
aria-live="polite"
|
||
></div>
|
||
<div class="drawer-overlay hidden" id="drawer-overlay"></div>
|
||
|
||
<aside class="drawer hidden" id="panel-prefill" aria-hidden="true">
|
||
<div class="drawer-head">
|
||
<h3>Prefill from a bean product URL</h3>
|
||
<button
|
||
class="icon-btn"
|
||
type="button"
|
||
data-close-drawer
|
||
aria-label="Close"
|
||
>
|
||
✕
|
||
</button>
|
||
</div>
|
||
<div class="drawer-body">
|
||
<label class="stack-label" for="prefill-url">Product page URL</label>
|
||
<input
|
||
type="url"
|
||
id="prefill-url"
|
||
class="text-input"
|
||
placeholder="https://roaster.example/shop/some-coffee"
|
||
/>
|
||
<label class="checkbox-row"
|
||
><input type="checkbox" id="prefill-overwrite" /> Overwrite
|
||
already-filled fields</label
|
||
>
|
||
<div class="drawer-actions">
|
||
<button id="prefill-go" type="button" class="primary-btn">
|
||
Fetch & prefill
|
||
</button>
|
||
<button id="prefill-undo" type="button" class="ghost-btn" disabled>
|
||
Undo prefill
|
||
</button>
|
||
</div>
|
||
<div id="prefill-result" class="drawer-result"></div>
|
||
</div>
|
||
</aside>
|
||
|
||
<aside class="drawer hidden" id="panel-alog" aria-hidden="true">
|
||
<div class="drawer-head">
|
||
<h3>Attach an Artisan .alog reference curve</h3>
|
||
<button
|
||
class="icon-btn"
|
||
type="button"
|
||
data-close-drawer
|
||
aria-label="Close"
|
||
>
|
||
✕
|
||
</button>
|
||
</div>
|
||
<div class="drawer-body">
|
||
<label class="filebtn primary-btn"
|
||
>Choose .alog file<input type="file" id="alog-file" accept=".alog"
|
||
/></label>
|
||
<button id="alog-library-refresh" type="button" class="ghost-btn">
|
||
Show local library
|
||
</button>
|
||
<ul class="lib-list hidden" id="alog-library-list"></ul>
|
||
<div id="alog-result" class="drawer-result"></div>
|
||
</div>
|
||
</aside>
|
||
|
||
<form id="plan-form">
|
||
<div class="workspace-grid">
|
||
<div class="form-col">
|
||
<section class="panel-card" id="sec-coffee">
|
||
<div class="panel-head"><h2>The Coffee</h2></div>
|
||
<div class="panel-body">
|
||
<div class="field-grid">
|
||
<label class="field" data-fid="0.1"
|
||
><span class="field-label">Coffee / lot</span
|
||
><input
|
||
class="field-input"
|
||
name="0.1"
|
||
placeholder="e.g. Finca El Ocaso"
|
||
/></label>
|
||
<label class="field" data-fid="0.2"
|
||
><span class="field-label">Date</span
|
||
><input class="field-input" type="date" name="0.2"
|
||
/></label>
|
||
<label class="field" data-fid="0.3"
|
||
><span class="field-label">Batch #</span
|
||
><input class="field-input" name="0.3"
|
||
/></label>
|
||
<label class="field" data-fid="0.4"
|
||
><span class="field-label">Green in</span>
|
||
<div class="unit-input">
|
||
<input class="field-input" name="0.4" /><span class="unit"
|
||
>g</span
|
||
>
|
||
</div></label
|
||
>
|
||
</div>
|
||
|
||
<div class="subhead">Cultivar</div>
|
||
<div class="field-grid">
|
||
<label class="field" data-fid="1.1"
|
||
><span class="field-label">Cultivar</span
|
||
><input
|
||
class="field-input"
|
||
name="1.1"
|
||
list="cultivar-list"
|
||
placeholder="e.g. Caturra"
|
||
autocomplete="off"
|
||
/></label>
|
||
<div class="field" data-fid="1.3">
|
||
<span class="field-label">Origin</span
|
||
><input
|
||
class="field-input"
|
||
name="1.3"
|
||
placeholder="e.g. Huila, Colombia"
|
||
/>
|
||
</div>
|
||
<div class="field wide" data-fid="1.2">
|
||
<span class="field-label">Group</span>
|
||
<div class="segmented" data-radio-group="1.2">
|
||
<label class="seg-opt"
|
||
><input type="radio" name="1.2" value="typica" /><span
|
||
>Typica</span
|
||
></label
|
||
>
|
||
<label class="seg-opt"
|
||
><input type="radio" name="1.2" value="bourbon" /><span
|
||
>Bourbon</span
|
||
></label
|
||
>
|
||
<label class="seg-opt"
|
||
><input type="radio" name="1.2" value="ethiopian" /><span
|
||
>Ethiopian</span
|
||
></label
|
||
>
|
||
<label class="seg-opt"
|
||
><input type="radio" name="1.2" value="hybrid" /><span
|
||
>Hybrid</span
|
||
></label
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="field-grid">
|
||
<label class="field" data-fid="1.4"
|
||
><span class="field-label"
|
||
>FC anchor
|
||
<span class="hint" title="Feeds Time Ledger line 1"
|
||
>ⓘ</span
|
||
></span
|
||
><input class="field-input" name="1.4" placeholder="8:45"
|
||
/></label>
|
||
<label class="field" data-fid="1.5"
|
||
><span class="field-label">Profile mm:ss|mm:ss|mm:ss</span
|
||
><input
|
||
class="field-input"
|
||
name="1.5"
|
||
placeholder="4:15|3:15|1:30"
|
||
/></label>
|
||
<label class="field" data-fid="1.6"
|
||
><span class="field-label"
|
||
>± Refine
|
||
<span
|
||
class="hint"
|
||
title="0 on the first cook of this cultivar, otherwise carried from Step 12"
|
||
>ⓘ</span
|
||
></span
|
||
><input class="field-input" name="1.6" placeholder="0"
|
||
/></label>
|
||
<label class="field" data-fid="1.7"
|
||
><span class="field-label"
|
||
>± Dev modifier
|
||
<span
|
||
class="hint"
|
||
title="Step 1's profile 3rd number, minus 1:30"
|
||
>ⓘ</span
|
||
></span
|
||
><input class="field-input" name="1.7"
|
||
/></label>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel-card" id="sec-blend">
|
||
<div class="panel-head">
|
||
<h2>Blend</h2>
|
||
<div class="segmented small" data-radio-group="2.1">
|
||
<label class="seg-opt"
|
||
><input type="radio" name="2.1" value="single" checked /><span
|
||
>Single-origin</span
|
||
></label
|
||
>
|
||
<label class="seg-opt"
|
||
><input type="radio" name="2.1" value="blend" /><span
|
||
>Blend</span
|
||
></label
|
||
>
|
||
</div>
|
||
</div>
|
||
<div class="panel-body" id="blend-body">
|
||
<div class="blend-cards" id="blend-cards"></div>
|
||
<div class="blend-total" id="blend-total">
|
||
<div class="blend-total-bar">
|
||
<div class="blend-total-fill" id="blend-total-fill"></div>
|
||
</div>
|
||
<span class="blend-total-label" id="blend-total-label"
|
||
>0% of 100%</span
|
||
>
|
||
</div>
|
||
<button type="button" class="ghost-btn small" id="btn-add-blend">
|
||
+ Add component
|
||
</button>
|
||
|
||
<div class="subhead">How the components combine</div>
|
||
<div class="field wide" data-fid="2.3">
|
||
<div class="segmented" data-radio-group="2.3">
|
||
<label class="seg-opt"
|
||
><input type="radio" name="2.3" value="dominant" /><span
|
||
>Dominant (≥60%)</span
|
||
></label
|
||
>
|
||
<label class="seg-opt"
|
||
><input type="radio" name="2.3" value="weighted" /><span
|
||
>Weighted average</span
|
||
></label
|
||
>
|
||
<label class="seg-opt"
|
||
><input type="radio" name="2.3" value="split" /><span
|
||
>Split-roast, blend after</span
|
||
></label
|
||
>
|
||
</div>
|
||
</div>
|
||
<div class="field-grid">
|
||
<label class="field" data-fid="2.4"
|
||
><span class="field-label">Resulting FC anchor</span
|
||
><input class="field-input" name="2.4"
|
||
/></label>
|
||
<label class="field" data-fid="2.5"
|
||
><span class="field-label">Resulting processing modifier</span
|
||
><input class="field-input" name="2.5"
|
||
/></label>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel-card" id="sec-process">
|
||
<div class="panel-head"><h2>Roast Target</h2></div>
|
||
<div class="panel-body">
|
||
<div class="field wide" data-fid="3.1">
|
||
<span class="field-label">Process</span>
|
||
<div class="chip-group" data-radio-group="3.1">
|
||
<label class="chip-opt"
|
||
><input type="radio" name="3.1" value="washed" /><span
|
||
>Washed</span
|
||
></label
|
||
>
|
||
<label class="chip-opt"
|
||
><input type="radio" name="3.1" value="yellow-honey" /><span
|
||
>Yellow / white honey</span
|
||
></label
|
||
>
|
||
<label class="chip-opt"
|
||
><input
|
||
type="radio"
|
||
name="3.1"
|
||
value="red-black-honey"
|
||
/><span>Red / black honey</span></label
|
||
>
|
||
<label class="chip-opt"
|
||
><input type="radio" name="3.1" value="natural" /><span
|
||
>Natural</span
|
||
></label
|
||
>
|
||
<label class="chip-opt"
|
||
><input type="radio" name="3.1" value="anaerobic" /><span
|
||
>Anaerobic / CM</span
|
||
></label
|
||
>
|
||
</div>
|
||
</div>
|
||
<div class="field-grid">
|
||
<label class="field" data-fid="3.2"
|
||
><span class="field-label">Dev modifier</span
|
||
><input class="field-input" name="3.2"
|
||
/></label>
|
||
</div>
|
||
|
||
<div class="subhead">Roast level</div>
|
||
<div class="field wide" data-fid="4.1">
|
||
<div class="chip-group" data-radio-group="4.1">
|
||
<label class="chip-opt"
|
||
><input type="radio" name="4.1" value="light" /><span
|
||
>Light</span
|
||
></label
|
||
>
|
||
<label class="chip-opt"
|
||
><input type="radio" name="4.1" value="light-medium" /><span
|
||
>Light-medium</span
|
||
></label
|
||
>
|
||
<label class="chip-opt"
|
||
><input type="radio" name="4.1" value="medium" /><span
|
||
>Medium</span
|
||
></label
|
||
>
|
||
<label class="chip-opt"
|
||
><input type="radio" name="4.1" value="dark" /><span
|
||
>Dark</span
|
||
></label
|
||
>
|
||
<label class="chip-opt"
|
||
><input type="radio" name="4.1" value="very-dark" /><span
|
||
>Very dark</span
|
||
></label
|
||
>
|
||
</div>
|
||
</div>
|
||
<div class="field-grid">
|
||
<label class="field" data-fid="4.2"
|
||
><span class="field-label">Target weight loss</span>
|
||
<div class="unit-input">
|
||
<input class="field-input" name="4.2" /><span class="unit"
|
||
>%</span
|
||
>
|
||
</div></label
|
||
>
|
||
<label class="field" data-fid="4.3"
|
||
><span class="field-label">Dev base</span
|
||
><input class="field-input" name="4.3"
|
||
/></label>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel-card" id="sec-bean">
|
||
<div class="panel-head"><h2>Bean Condition</h2></div>
|
||
<div class="panel-body">
|
||
<div class="field-grid">
|
||
<label class="field" data-fid="5.1"
|
||
><span class="field-label">Moisture</span>
|
||
<div class="unit-input">
|
||
<input class="field-input" name="5.1" /><span class="unit"
|
||
>%</span
|
||
>
|
||
</div></label
|
||
>
|
||
<label class="field" data-fid="5.2"
|
||
><span class="field-label">Density</span>
|
||
<div class="unit-input">
|
||
<input class="field-input" name="5.2" /><span class="unit"
|
||
>g/L</span
|
||
>
|
||
</div></label
|
||
>
|
||
<label class="field" data-fid="5.3"
|
||
><span class="field-label">Altitude</span
|
||
><input class="field-input" name="5.3"
|
||
/></label>
|
||
<label class="field" data-fid="5.4"
|
||
><span class="field-label">Screen</span
|
||
><input class="field-input" name="5.4"
|
||
/></label>
|
||
</div>
|
||
<div class="field-grid">
|
||
<div class="field" data-fid="5.5">
|
||
<span class="field-label">Sweet spot</span>
|
||
<div class="segmented" data-radio-group="5.5">
|
||
<label class="seg-opt"
|
||
><input type="radio" name="5.5" value="narrow" /><span
|
||
>Narrow</span
|
||
></label
|
||
>
|
||
<label class="seg-opt"
|
||
><input type="radio" name="5.5" value="med" /><span
|
||
>Medium</span
|
||
></label
|
||
>
|
||
<label class="seg-opt"
|
||
><input type="radio" name="5.5" value="wide" /><span
|
||
>Wide</span
|
||
></label
|
||
>
|
||
</div>
|
||
</div>
|
||
<label class="field" data-fid="5.6"
|
||
><span class="field-label"
|
||
>± Net correction
|
||
<span
|
||
class="hint"
|
||
title="Judgment call — rarely more than ±0:15"
|
||
>ⓘ</span
|
||
></span
|
||
><input class="field-input" name="5.6"
|
||
/></label>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel-card" id="sec-machine">
|
||
<div class="panel-head"><h2>Machine Plan</h2></div>
|
||
<div class="panel-body">
|
||
<div class="subhead">Temperatures & rate-of-rise</div>
|
||
<div class="milestone-list">
|
||
<div
|
||
class="milestone-row band-row"
|
||
data-band-lo="133"
|
||
data-band-hi="195"
|
||
>
|
||
<div class="milestone-label">Charge</div>
|
||
<output class="milestone-plan" data-out="t-charge"
|
||
>0:00</output
|
||
>
|
||
<div class="band-track">
|
||
<div class="band-range"></div>
|
||
<div class="band-marker"></div>
|
||
</div>
|
||
<div class="unit-input sm">
|
||
<input
|
||
class="field-input sm"
|
||
name="temps.charge.tempC"
|
||
placeholder="°C"
|
||
/><span class="unit">°C</span>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="milestone-row band-row"
|
||
data-band-lo="77"
|
||
data-band-hi="100"
|
||
>
|
||
<div class="milestone-label">Turning point</div>
|
||
<input
|
||
class="field-input sm milestone-time"
|
||
name="temps.tp.time"
|
||
placeholder="m:ss"
|
||
/>
|
||
<div class="band-track">
|
||
<div class="band-range"></div>
|
||
<div class="band-marker"></div>
|
||
</div>
|
||
<div class="unit-input sm">
|
||
<input
|
||
class="field-input sm"
|
||
name="temps.tp.tempC"
|
||
placeholder="°C"
|
||
/><span class="unit">°C</span>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="milestone-row band-row"
|
||
data-band-lo="148"
|
||
data-band-hi="166"
|
||
>
|
||
<div class="milestone-label">Yellow</div>
|
||
<output class="milestone-plan" data-out="t-yellow">—</output>
|
||
<div class="band-track">
|
||
<div class="band-range"></div>
|
||
<div class="band-marker"></div>
|
||
</div>
|
||
<div class="unit-input sm">
|
||
<input
|
||
class="field-input sm"
|
||
name="temps.yellow.tempC"
|
||
placeholder="°C"
|
||
/><span class="unit">°C</span>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="milestone-row band-row"
|
||
data-band-lo="176"
|
||
data-band-hi="187"
|
||
>
|
||
<div class="milestone-label">First crack</div>
|
||
<output class="milestone-plan" data-out="t-fc">—</output>
|
||
<div class="band-track">
|
||
<div class="band-range"></div>
|
||
<div class="band-marker"></div>
|
||
</div>
|
||
<div class="unit-input sm">
|
||
<input
|
||
class="field-input sm"
|
||
name="temps.fc.tempC"
|
||
placeholder="°C"
|
||
/><span class="unit">°C</span>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="milestone-row band-row"
|
||
data-band-lo="183"
|
||
data-band-hi="197"
|
||
>
|
||
<div class="milestone-label">Drop</div>
|
||
<output class="milestone-plan" data-out="t-drop">—</output>
|
||
<div class="band-track">
|
||
<div class="band-range"></div>
|
||
<div class="band-marker"></div>
|
||
</div>
|
||
<div class="unit-input sm">
|
||
<input
|
||
class="field-input sm"
|
||
name="temps.drop.tempC"
|
||
placeholder="°C"
|
||
/><span class="unit">°C</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="subhead">Actuator schedule (planned)</div>
|
||
<div class="actuator-timeline" id="actuator-timeline"></div>
|
||
<button
|
||
type="button"
|
||
class="ghost-btn small"
|
||
id="btn-add-actuator"
|
||
>
|
||
+ Add step
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel-card" id="sec-roastlog">
|
||
<div class="panel-head"><h2>Roast Log — Plan vs. Actual</h2></div>
|
||
<div class="panel-body">
|
||
<div class="milestone-list">
|
||
<div class="milestone-row log-row">
|
||
<div class="milestone-label">Charge</div>
|
||
<output class="milestone-plan" data-out="pa-charge"
|
||
>0:00</output
|
||
>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.charge.actualTime"
|
||
placeholder="0:00"
|
||
/>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.charge.actualBt"
|
||
placeholder="°C"
|
||
/>
|
||
<input
|
||
class="field-input note"
|
||
name="planActual.charge.note"
|
||
placeholder="Note"
|
||
/>
|
||
</div>
|
||
<div class="milestone-row log-row">
|
||
<div class="milestone-label">Turning point</div>
|
||
<output class="milestone-plan" data-out="pa-tp">—</output>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.tp.actualTime"
|
||
placeholder="0:00"
|
||
/>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.tp.actualBt"
|
||
placeholder="°C"
|
||
/>
|
||
<input
|
||
class="field-input note"
|
||
name="planActual.tp.note"
|
||
placeholder="Note"
|
||
/>
|
||
</div>
|
||
<div class="milestone-row log-row">
|
||
<div class="milestone-label">Yellow</div>
|
||
<output class="milestone-plan" data-out="pa-yellow">—</output>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.yellow.actualTime"
|
||
placeholder="0:00"
|
||
/>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.yellow.actualBt"
|
||
placeholder="°C"
|
||
/>
|
||
<input
|
||
class="field-input note"
|
||
name="planActual.yellow.note"
|
||
placeholder="Note"
|
||
/>
|
||
</div>
|
||
<div class="milestone-row log-row">
|
||
<div class="milestone-label">First crack</div>
|
||
<output class="milestone-plan" data-out="pa-fc">—</output>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.fc.actualTime"
|
||
placeholder="0:00"
|
||
/>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.fc.actualBt"
|
||
placeholder="°C"
|
||
/>
|
||
<input
|
||
class="field-input note"
|
||
name="planActual.fc.note"
|
||
placeholder="Note"
|
||
/>
|
||
</div>
|
||
<div class="milestone-row log-row">
|
||
<div class="milestone-label">Drop</div>
|
||
<output class="milestone-plan" data-out="pa-drop">—</output>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.drop.actualTime"
|
||
placeholder="0:00"
|
||
/>
|
||
<input
|
||
class="field-input sm"
|
||
name="planActual.drop.actualBt"
|
||
placeholder="°C"
|
||
/>
|
||
<input
|
||
class="field-input note"
|
||
name="planActual.drop.note"
|
||
placeholder="Note"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel-card" id="sec-after">
|
||
<div class="panel-head"><h2>After the Roast</h2></div>
|
||
<div class="panel-body">
|
||
<div class="field-grid">
|
||
<label class="field"
|
||
><span class="field-label">Green in</span>
|
||
<div class="unit-input">
|
||
<input class="field-input" name="afterRoast.greenIn" /><span
|
||
class="unit"
|
||
>g</span
|
||
>
|
||
</div></label
|
||
>
|
||
<label class="field"
|
||
><span class="field-label">Out</span>
|
||
<div class="unit-input">
|
||
<input class="field-input" name="afterRoast.out" /><span
|
||
class="unit"
|
||
>g</span
|
||
>
|
||
</div></label
|
||
>
|
||
<label class="field"
|
||
><span class="field-label">Weight loss</span>
|
||
<div class="unit-input">
|
||
<input
|
||
class="field-input"
|
||
name="afterRoast.weightLossPct"
|
||
/><span class="unit">%</span>
|
||
</div></label
|
||
>
|
||
<label class="field"
|
||
><span class="field-label">Actual DTR</span>
|
||
<div class="unit-input">
|
||
<input
|
||
class="field-input"
|
||
name="afterRoast.actualDtrPct"
|
||
/><span class="unit">%</span>
|
||
</div></label
|
||
>
|
||
</div>
|
||
<div class="field-grid">
|
||
<label class="field"
|
||
><span class="field-label">Colour WB/GR</span
|
||
><input class="field-input" name="afterRoast.colour"
|
||
/></label>
|
||
<label class="field"
|
||
><span class="field-label">Rested</span>
|
||
<div class="unit-input">
|
||
<input
|
||
class="field-input"
|
||
name="afterRoast.restedDays"
|
||
/><span class="unit">days</span>
|
||
</div></label
|
||
>
|
||
<label class="field"
|
||
><span class="field-label">Brew ratio</span
|
||
><input class="field-input" name="afterRoast.brewRatio"
|
||
/></label>
|
||
<label class="field"
|
||
><span class="field-label">Method</span
|
||
><input class="field-input" name="afterRoast.method"
|
||
/></label>
|
||
</div>
|
||
<label class="field"
|
||
><span class="field-label">Cup notes</span
|
||
><input class="field-input" name="afterRoast.cupNotes"
|
||
/></label>
|
||
<div class="field-grid">
|
||
<label class="field accent"
|
||
><span class="field-label">One change next batch</span
|
||
><input class="field-input" name="afterRoast.oneChange"
|
||
/></label>
|
||
<label class="field"
|
||
><span class="field-label">What would prove it wrong</span
|
||
><input class="field-input" name="afterRoast.disproof"
|
||
/></label>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<aside class="dock">
|
||
<div class="dock-card ledger-card">
|
||
<div class="dock-card-head"><h2>Time Ledger</h2></div>
|
||
|
||
<div class="ledger-group">
|
||
<div class="ledger-row">
|
||
<span class="l-fid">1.4</span
|
||
><span class="l-label">Cultivar / blend anchor</span
|
||
><output class="l-val" data-out="l1">—</output>
|
||
</div>
|
||
<div class="ledger-row">
|
||
<span class="l-fid">1.6</span
|
||
><span class="l-label">± Refinement</span
|
||
><output class="l-val" data-out="l2">—</output>
|
||
</div>
|
||
<div class="ledger-row">
|
||
<span class="l-fid">5.6</span
|
||
><span class="l-label">± Bean condition</span
|
||
><output class="l-val" data-out="l3">—</output>
|
||
</div>
|
||
<div class="ledger-row">
|
||
<span class="l-fid">6.4</span
|
||
><span class="l-label">± Batch size</span
|
||
><input class="ledger-input" name="6.4" placeholder="0" />
|
||
</div>
|
||
</div>
|
||
<div class="ledger-total hero">
|
||
<span>First crack</span><output data-out="A">—</output>
|
||
</div>
|
||
|
||
<div class="ledger-info">
|
||
<div class="info-pill">
|
||
<span>Yellow</span><output data-out="yellow">—</output>
|
||
</div>
|
||
<div class="info-pill">
|
||
<span>Maillard</span><output data-out="maillard">—</output>
|
||
</div>
|
||
<span class="info-note"
|
||
>informational — not summed into development</span
|
||
>
|
||
</div>
|
||
|
||
<div class="ledger-group">
|
||
<div class="ledger-row">
|
||
<span class="l-fid">4.3</span
|
||
><span class="l-label">Development base</span
|
||
><output class="l-val" data-out="l7">—</output>
|
||
</div>
|
||
<div class="ledger-row">
|
||
<span class="l-fid">3.2</span
|
||
><span class="l-label">± Processing modifier</span
|
||
><output class="l-val" data-out="l8">—</output>
|
||
</div>
|
||
<div class="ledger-row">
|
||
<span class="l-fid">1.7</span
|
||
><span class="l-label">± Cultivar dev modifier</span
|
||
><output class="l-val" data-out="l9">—</output>
|
||
</div>
|
||
</div>
|
||
<div class="ledger-total hero">
|
||
<span>Development</span><output data-out="C">—</output>
|
||
</div>
|
||
<div class="ledger-total hero drop">
|
||
<span>Drop (end of roast)</span><output data-out="D">—</output>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="dock-card checks-card">
|
||
<div class="dock-card-head"><h2>Sanity Checks</h2></div>
|
||
<div class="checks-grid">
|
||
<div class="check-tile unknown" data-pass="drying">
|
||
<span class="check-name">Drying share</span>
|
||
<output class="check-value" data-out="check-drying">—</output>
|
||
<span class="check-band">target 43–51%</span>
|
||
</div>
|
||
<div class="check-tile unknown" data-pass="maillard">
|
||
<span class="check-name">Maillard share</span>
|
||
<output class="check-value" data-out="check-maillard">—</output>
|
||
<span class="check-band">target 33–39%</span>
|
||
</div>
|
||
<div class="check-tile unknown" data-pass="dtr">
|
||
<span class="check-name">Development ratio</span>
|
||
<output class="check-value" data-out="check-dtr">—</output>
|
||
<span class="check-band">target 12–20%</span>
|
||
</div>
|
||
<div class="check-tile unknown" data-pass="ceiling">
|
||
<span class="check-name">Development ceiling</span>
|
||
<output class="check-value" data-out="check-ceiling">—</output>
|
||
<span class="check-band">under 4:30</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="dock-card curve-card">
|
||
<div class="dock-card-head">
|
||
<h2>The Curve</h2>
|
||
<span class="curve-legend"
|
||
><i class="sw sw-plan"></i>plan
|
||
<i class="sw sw-ref"></i>reference</span
|
||
>
|
||
</div>
|
||
<div class="curve-wrap">
|
||
<svg
|
||
id="curve-svg-live"
|
||
viewBox="0 0 714 334"
|
||
width="100%"
|
||
role="img"
|
||
aria-label="Roast curve, live plan"
|
||
>
|
||
<defs>
|
||
<style>
|
||
#curve-svg-live .maj {
|
||
stroke: var(--line);
|
||
stroke-width: 0.7;
|
||
}
|
||
#curve-svg-live .min {
|
||
stroke: var(--surface-sunken);
|
||
stroke-width: 0.6;
|
||
}
|
||
#curve-svg-live .ax {
|
||
stroke: var(--ink-3);
|
||
stroke-width: 1.1;
|
||
}
|
||
#curve-svg-live .al {
|
||
font-size: 8px;
|
||
fill: var(--ink-3);
|
||
}
|
||
#curve-svg-live .band {
|
||
fill: var(--ember);
|
||
opacity: 0.08;
|
||
}
|
||
#curve-svg-live .bandline {
|
||
font-size: 7.5px;
|
||
fill: var(--ember);
|
||
}
|
||
</style>
|
||
</defs>
|
||
<rect
|
||
x="52"
|
||
y="24"
|
||
width="600"
|
||
height="270"
|
||
fill="var(--surface-raised)"
|
||
/>
|
||
<rect class="band" x="52" y="65.4" width="600" height="19.8" />
|
||
<text x="58" y="62.4" class="bandline">
|
||
first-crack band, 176–187 °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>
|
||
<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>
|
||
<g id="plan-curve-live"></g>
|
||
<g id="ref-curve-live"></g>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
</form>
|
||
|
||
<!-- Kept OUTSIDE #plan-form on purpose: identically-named radios here must not fight for
|
||
mutual exclusivity with the screen form's radios (same name + same form owner would
|
||
silently uncheck one another). This whole block is display:none except when printing;
|
||
main.js re-syncs it from state.plan on beforeprint. -->
|
||
<div id="print-sheet">
|
||
<div class="sheet-wrap">
|
||
<div class="sheet">
|
||
<!-- ══════════════════ 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 #</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 >±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 & 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:30–2: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">43–51 %</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">33–39 %</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">12–20 %</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">< 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 & 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 & 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">133–195 °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">77–100 °C @ 0:47–1: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">148–166 °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">176–187 °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">183–197 °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: 0.7;
|
||
}
|
||
.min {
|
||
stroke: #e2dad2;
|
||
stroke-width: 0.45;
|
||
}
|
||
.ax {
|
||
stroke: #1a1512;
|
||
stroke-width: 1.3;
|
||
}
|
||
.al {
|
||
font-size: 8px;
|
||
fill: #5a5048;
|
||
}
|
||
.alr {
|
||
font-size: 8px;
|
||
fill: #7a3b12;
|
||
}
|
||
.band {
|
||
fill: #7a3b12;
|
||
opacity: 0.05;
|
||
}
|
||
.ttl {
|
||
font-size: 8px;
|
||
font-weight: 700;
|
||
fill: #1a1512;
|
||
letter-spacing: 0.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, 176–187 °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>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<datalist id="cultivar-list"></datalist>
|
||
|
||
<script type="module" src="/js/main.js"></script>
|
||
</body>
|
||
</html>
|