Initial commit

This commit is contained in:
2026-07-29 19:01:43 -04:00
parent fedaf29847
commit 106b1104c3
4 changed files with 1111 additions and 146 deletions
+430 -31
View File
@@ -2,48 +2,447 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Roast Planner</title>
<link rel="stylesheet" href="/worksheet.css">
<link rel="stylesheet" href="/worksheet.css" media="print">
<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>
<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>
<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>
<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>
<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>
<ul class="lib-list hidden" id="alog-library-list"></ul>
<div id="alog-result"></div>
</header>
<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 &amp; 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 &amp; 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 4351%</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 3339%</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 1220%</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:.7}
#curve-svg-live .min{stroke:var(--surface-sunken);stroke-width:.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:.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, 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>
<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">
<form id="plan-form">
<!-- ══════════════════ FRONT ══════════════════ -->
<section class="first">
@@ -348,7 +747,7 @@
</div>
</section>
</form>
</div>
</div>
</div>