Files
cnx_vinyl/docs/pitch/index.html
T
grabowski 744a56d22b
Deploy test site / deploy (push) Successful in 1m2s
Order tool: fan funding; deck: light and dark, 60/40 shareholding
User, 20 Oct 2026: can the deck also be made in a light and dark mode;
the new order tool should also offer fan funding, so the platform
screenshots match and use the same calculator; the main investor holds
at least 60 %, so 40 % is for sale.

Order tool (docs/order.html, /demos/order/):
- New step 15 "How to pay": pay the plant directly (deposit) or let
  fans fund it; step 16 becomes "Review & launch" with the campaign
  simulation and its three outcomes.
- Campaign maths ported from make-a-record (fan price, length, cap, own
  copies, VAT; fans needed = max(100, break-even); keep at the goal and
  with more fans; price hint); R gains fee (3.5 %), VAT and campaign
  length with sources; no price changed.
- Quote card shows fan price, fans needed and the artist's keep on the
  fan route; route and campaign in the #q= hash; deep links
  #s=<step>&p=<preset>&route=fans.
- Parity with make-a-record: 206/206 checks over 40 configurations;
  default quotes unchanged (37,836 / 217 / 102,936 THB; partner
  mastering 43,676).

Investor deck (docs/pitch/):
- Light and dark theme like the other site pages (cnx-theme, T key and
  button), presenter and overview follow; charts from CSS variables;
  text pairs at WCAG AA in both themes; tool screenshots in both themes.
- Two PDFs: dark and light (print-friendly), each ~1.5 MB.
- Platform slide: both pictures from the order tool (Splatter 12" order
  step and its fan-funding view: 100 fans at 1,200 THB, break-even 46,
  ~480 THB a copy kept).
- BOI and financing slide: shareholding bar, lead investor at least 60 %,
  at most 40 % for co-investors (~32M / ~22M THB at the ~54M equity
  floor, arithmetic only); the ask offers up to 40 %.

Docs: artist-platform.md (fan funding in the order tool), README and
landing page entries.
2026-10-20 14:31:40 +07:00

1466 lines
122 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<title>CNX Vinyl: investment proposal</title>
<script>
(function(){var t=null;try{t=localStorage.getItem("cnx-theme")}catch(e){}
if(t!=="light"&&t!=="dark")t=window.matchMedia&&matchMedia("(prefers-color-scheme: light)").matches?"light":"dark";
var q=/[?&]th=(light|dark)(&|$)/.exec(location.search);if(q)t=q[1];
document.documentElement.setAttribute("data-theme",t);})();
</script>
<style>
:root{
color-scheme:dark;
--outer:#07080b; --bg:#0d0f14; --panel:#131720; --panel2:#171c26;
--line:rgba(236,230,218,.15); --line2:rgba(236,230,218,.08);
--tx:#efe9dd; --tx2:#d2cbbd; --mu:#a59e91; --dim:#8a8377;
/* --ac: accent for text, --acf: accent for fills (the same in the dark theme) */
--ac:#e8b04a; --acf:#e8b04a; --acs:rgba(232,176,74,.14); --acl:rgba(232,176,74,.45); --acb:rgba(232,176,74,.35);
--b:#8eaac3; --bf:#8eaac3; --bs:rgba(142,170,195,.16);
--neg:#d6876c; --negf:#d6876c; --negs:rgba(214,135,108,.14);
/* surfaces */
--card-a:rgba(255,255,255,.035); --card-b:rgba(255,255,255,.015); --card-sh:none;
--fs-a:rgba(255,255,255,.04); --fs-b:rgba(255,255,255,.012);
--warn-a:rgba(232,176,74,.07); --warn-b:rgba(232,176,74,.025);
--fig-bg:#101319; --fig-sh:0 24px 70px rgba(0,0,0,.5); --stage-sh:0 0 0 1px rgba(255,255,255,.03);
--groove:rgba(232,176,74,.07); --chain-a:rgba(232,176,74,.7); --chain-b:rgba(232,176,74,.25);
--disc-hole:#0d0f14; --disc-rim:#0d0f14; --disc-g1:#2b2f37; --disc-g2:#16191f; --disc-ring:rgba(255,255,255,.1);
/* chart series */
--zero:rgba(236,230,218,.35); --whisk:rgba(236,230,218,.6); --rev:rgba(236,230,218,.32); --elec:rgba(236,230,218,.35);
--track:rgba(236,230,218,.12); --cmpline:rgba(236,230,218,.4); --cmpleg:rgba(236,230,218,.45); --dash:rgba(236,230,218,.22); --par:rgba(142,170,195,.55);
/* tags */
--tag-hi:#eaa58d; --tag-hi-bd:rgba(214,135,108,.5); --tag-md-bd:rgba(232,176,74,.45);
--tag-ok:#a9cdb1; --tag-ok-bd:rgba(156,192,164,.45); --tag-ok-bg:rgba(156,192,164,.1);
--tag-no:#c7c0b3; --tag-no-bd:rgba(236,230,218,.25);
/* chrome: control bar, overview, presenter */
--ctl-bg:rgba(18,21,28,.86); --ctl-bd:rgba(236,230,218,.12); --ctl-sh:0 10px 30px rgba(0,0,0,.4); --hov:rgba(255,255,255,.08);
--jump-bg:rgba(18,21,28,.9); --th-bd:rgba(236,230,218,.14); --th-hov:rgba(232,176,74,.6); --lab-a:rgba(7,8,11,.92); --lab-b:rgba(7,8,11,0);
--pv-bg:#0a0b0f; --btn:rgba(255,255,255,.06); --btn-h:rgba(255,255,255,.12); --nav-btn:rgba(255,255,255,.05); --well:rgba(255,255,255,.02);
--serif:"Iowan Old Style","Palatino Linotype",Palatino,"Book Antiqua",Georgia,serif;
--sans:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
/* light theme: warm paper, ink text, the accents darkened for text contrast (WCAG AA, pairs in the skill note) */
html[data-theme=light]{
color-scheme:light;
--outer:#e2dacb; --bg:#f4eee3; --panel:#faf6ee; --panel2:#efe7d8;
--line:rgba(60,45,20,.18); --line2:rgba(60,45,20,.10);
--tx:#1c1914; --tx2:#38332b; --mu:#5a5348; --dim:#675f53;
--ac:#875606; --acf:#b97e17; --acs:rgba(196,137,31,.15); --acl:rgba(185,126,23,.55); --acb:rgba(160,108,18,.38);
--b:#2c5677; --bf:#557ea3; --bs:rgba(85,126,163,.14);
--neg:#9b3d24; --negf:#b3563a; --negs:rgba(179,86,58,.10);
--card-a:rgba(255,255,255,.62); --card-b:rgba(255,255,255,.40); --card-sh:0 1px 2px rgba(60,45,20,.05);
--fs-a:rgba(255,255,255,.66); --fs-b:rgba(255,255,255,.40);
--warn-a:rgba(196,137,31,.12); --warn-b:rgba(196,137,31,.05);
--fig-bg:#ffffff; --fig-sh:0 18px 46px rgba(70,50,20,.16); --stage-sh:0 0 0 1px rgba(60,45,20,.08),0 20px 60px rgba(60,45,20,.12);
--groove:rgba(150,104,24,.09); --chain-a:rgba(185,126,23,.85); --chain-b:rgba(185,126,23,.3);
--disc-hole:#f4eee3; --disc-rim:#2a2621; --disc-g1:#4a443b; --disc-g2:#2a2621; --disc-ring:rgba(60,45,20,.18);
--zero:rgba(28,25,20,.45); --whisk:rgba(28,25,20,.6); --rev:#a59a87; --elec:#a59a87;
--track:rgba(60,45,20,.12); --cmpline:rgba(60,45,20,.55); --cmpleg:rgba(60,45,20,.55); --dash:rgba(60,45,20,.3); --par:rgba(85,126,163,.6);
--tag-hi:#9b3d24; --tag-hi-bd:rgba(155,61,36,.4); --tag-md-bd:rgba(135,86,6,.4);
--tag-ok:#2f6a3f; --tag-ok-bd:rgba(47,106,63,.4); --tag-ok-bg:rgba(47,106,63,.08);
--tag-no:#4a443a; --tag-no-bd:rgba(60,45,20,.28);
--ctl-bg:rgba(251,248,241,.92); --ctl-bd:rgba(60,45,20,.16); --ctl-sh:0 10px 30px rgba(60,45,20,.16); --hov:rgba(60,45,20,.08);
--jump-bg:rgba(251,248,241,.95); --th-bd:rgba(60,45,20,.16); --th-hov:rgba(185,126,23,.7); --lab-a:rgba(244,238,227,.95); --lab-b:rgba(244,238,227,0);
--pv-bg:#ebe4d7; --btn:rgba(60,45,20,.06); --btn-h:rgba(60,45,20,.12); --nav-btn:rgba(255,255,255,.55); --well:rgba(255,255,255,.5);
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
html,body{margin:0;height:100%;background:var(--outer);overflow:hidden}
body{font-family:var(--sans);color:var(--tx);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
button{font:inherit;color:inherit}
/* ---------- canvas: 1920 x 1080, scaled to fit, letterboxed ---------- */
.stage{position:fixed;left:0;top:0;width:1920px;height:1080px;transform-origin:0 0;background:var(--bg);overflow:hidden;box-shadow:var(--stage-sh)}
.slide{position:absolute;inset:0;width:1920px;height:1080px;padding:80px 104px 150px;display:flex;flex-direction:column;background:var(--bg);
font-variant-numeric:tabular-nums lining-nums;opacity:0;visibility:hidden;transform:translateX(26px);
transition:opacity .42s ease,transform .42s ease,visibility 0s linear .42s}
.slide.active{opacity:1;visibility:visible;transform:none;transition:opacity .42s ease,transform .42s ease,visibility 0s}
.slide.before{transform:translateX(-26px)}
@media (prefers-reduced-motion:reduce){.slide,.slide.active,.slide.before{transition:none;transform:none}}
/* vinyl groove motif: fine concentric rings, faded out with a mask */
.slide.grooves::before{content:"";position:absolute;inset:0;pointer-events:none;
background:repeating-radial-gradient(circle at var(--gx,104%) var(--gy,108%),var(--groove) 0 1px,transparent 1.4px 9px);
-webkit-mask-image:radial-gradient(circle at var(--gx,104%) var(--gy,108%),#000 0,#000 18%,transparent var(--gr,44%));
mask-image:radial-gradient(circle at var(--gx,104%) var(--gy,108%),#000 0,#000 18%,transparent var(--gr,44%))}
.slide > *{position:relative}
/* ---------- type ---------- */
.kicker{font-size:17px;letter-spacing:.2em;text-transform:uppercase;color:var(--ac);font-weight:600;display:flex;align-items:center;gap:14px;height:24px}
.kicker .no{color:var(--dim);font-weight:500}
.disc{width:22px;height:22px;border-radius:50%;flex:none;
background:radial-gradient(circle,var(--disc-hole) 0 7%,var(--acf) 8% 30%,var(--disc-rim) 31% 35%,transparent 36%),
repeating-radial-gradient(circle,var(--disc-g1) 0 1px,var(--disc-g2) 1px 2.2px);box-shadow:0 0 0 1px var(--disc-ring)}
h1{font-family:var(--serif);font-weight:500;font-size:132px;line-height:1.12;margin:28px 0 0;letter-spacing:-.01em}
h2{font-family:var(--serif);font-weight:500;font-size:54px;line-height:1.2;margin:14px 0 0;max-width:1712px;letter-spacing:-.005em}
h3{font-size:16px;letter-spacing:.16em;text-transform:uppercase;color:var(--mu);font-weight:600;margin:0 0 14px}
p{margin:0}
.lede{font-size:24px;line-height:1.42;color:var(--tx2);margin-top:14px;max-width:1500px}
.bd{flex:1;min-height:0;margin-top:40px;display:grid;gap:48px}
.t{font-size:20px;line-height:1.42;color:var(--tx2)}
.t b,.t strong{color:var(--tx);font-weight:600}
.s{font-size:17px;line-height:1.4;color:var(--mu)}
.e{font-style:normal;font-size:.78em;letter-spacing:.04em;color:var(--dim);margin-left:.25em}
.ac{color:var(--ac)} .bc{color:var(--b)} .neg{color:var(--neg)}
.serif{font-family:var(--serif)}
/* source line and running footer */
.src{position:absolute;left:104px;right:104px;bottom:64px;font-size:15px;line-height:1.4;color:var(--dim)}
.src b{color:var(--mu);font-weight:600}
.ft{position:absolute;left:104px;right:104px;bottom:24px;height:30px;display:flex;justify-content:space-between;align-items:flex-end;
font-size:14px;letter-spacing:.08em;color:var(--dim);border-top:1px solid var(--line2);text-transform:uppercase}
.ft .pg{color:var(--mu);letter-spacing:.12em}
/* building blocks */
.card{background:linear-gradient(180deg,var(--card-a),var(--card-b));border:1px solid var(--line2);border-radius:14px;padding:24px 28px;box-shadow:var(--card-sh)}
.card.warn{border-color:var(--acb);background:linear-gradient(180deg,var(--warn-a),var(--warn-b))}
.stat{display:flex;flex-direction:column;gap:8px}
.stat .v{font-family:var(--serif);font-size:58px;line-height:1.16;color:var(--ac);white-space:nowrap}
.stat .v.b{color:var(--b)}
.stat .l{font-size:18px;line-height:1.4;color:var(--tx2)}
.rule{height:1px;background:var(--line);margin:22px 0}
.list{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:14px}
.list li{position:relative;padding-left:22px;font-size:20px;line-height:1.42;color:var(--tx2)}
.list li::before{content:"";position:absolute;left:0;top:12px;width:8px;height:8px;border-radius:50%;background:var(--acf);opacity:.85}
.list.sm li{font-size:18px}
.list.sm li::before{top:10px}
.dl{display:grid;grid-template-columns:150px 1fr;gap:14px 22px;margin:0}
.dl dt{font-size:15px;letter-spacing:.14em;text-transform:uppercase;color:var(--ac);font-weight:600;padding-top:4px}
.dl dd{margin:0;font-size:19px;line-height:1.42;color:var(--tx2)}
.tag{display:inline-block;font-size:13px;letter-spacing:.1em;text-transform:uppercase;padding:3px 10px;border-radius:99px;border:1px solid var(--line);color:var(--mu);font-weight:600;white-space:nowrap}
.tag.hi{color:var(--tag-hi);border-color:var(--tag-hi-bd);background:var(--negs)}
.tag.md{color:var(--ac);border-color:var(--tag-md-bd);background:var(--acs)}
.tag.ok{color:var(--tag-ok);border-color:var(--tag-ok-bd);background:var(--tag-ok-bg)}
.tag.no{color:var(--tag-no);border-color:var(--tag-no-bd)}
figure{margin:0}
.fig{border-radius:14px;overflow:hidden;border:1px solid var(--line);box-shadow:var(--fig-sh);background:var(--fig-bg);line-height:0}
.fig img{display:block;width:100%;height:auto}
figcaption{font-size:15px;line-height:1.4;color:var(--dim);margin-top:12px}
table.tb{width:100%;border-collapse:collapse;font-size:18px;line-height:1.35}
.tb th{text-align:left;font-size:13px;letter-spacing:.14em;text-transform:uppercase;color:var(--mu);font-weight:600;padding:0 12px 10px;border-bottom:1px solid var(--line)}
.tb td{padding:10px 12px;border-bottom:1px solid var(--line2);vertical-align:top;color:var(--tx2)}
.tb td.n,.tb th.n{text-align:right;white-space:nowrap}
.tb tr.tot td{color:var(--tx);font-weight:600;border-bottom:none;border-top:1px solid var(--line)}
.legend{display:flex;gap:26px;font-size:16px;color:var(--mu);align-items:center}
.legend i{display:inline-block;width:14px;height:14px;border-radius:3px;margin-right:8px;vertical-align:-2px}
svg.chart{display:block;overflow:visible}
svg.chart text{font-family:var(--sans);fill:var(--mu);font-size:16px}
svg.chart .ax{stroke:var(--line);stroke-width:1}
svg.chart .gr{stroke:var(--line2);stroke-width:1}
svg.chart .z{stroke:var(--zero);stroke-width:1}
/* ---------- slide 1 ---------- */
.s1{--gx:72%;--gy:52%;--gr:62%}
.s1 .bd{grid-template-columns:700px 1fr;gap:56px;margin-top:0;align-items:center}
.s1 .sub{font-family:var(--serif);font-size:38px;line-height:1.22;color:var(--tx);margin-top:26px}
.s1 .plans{font-size:22px;line-height:1.45;color:var(--tx2);margin-top:26px}
.s1 .keys{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;margin-top:40px}
.s1 .keys .v{font-family:var(--serif);font-size:40px;color:var(--ac);line-height:1.16;white-space:nowrap}
.s1 .keys .v.b{color:var(--b)}
.s1 .keys .l{font-size:15px;line-height:1.4;color:var(--mu);margin-top:10px}
.s1 .by{font-size:16px;line-height:1.5;color:var(--mu);margin-top:40px}
/* ---------- slide 2 ---------- */
.s2 .bd{grid-template-columns:520px 620px 1fr;gap:50px}
.s2 .card .list li{font-size:19.5px}
.s2 .card .list{gap:18px}
.big3{display:flex;flex-direction:column;gap:26px}
.big3 .v{font-family:var(--serif);font-size:44px;line-height:1.16;color:var(--ac)}
.big3 .l{font-size:19.5px;line-height:1.42;color:var(--tx2);margin-top:8px}
/* ---------- slide 3 ---------- */
.chain{display:grid;grid-template-columns:repeat(8,1fr);gap:0;margin-top:34px;position:relative}
.chain .st{position:relative;padding:0 14px 0 0}
.chain .dot{width:30px;height:30px;border-radius:50%;border:2px solid var(--ac);background:var(--bg);display:grid;place-items:center;font-size:14px;font-weight:700;color:var(--ac);position:relative;z-index:1}
.chain .st::before{content:"";position:absolute;left:30px;right:0;top:15px;height:2px;background:linear-gradient(90deg,var(--chain-a),var(--chain-b))}
.chain .st:last-child::before{display:none}
.chain .nm{font-family:var(--serif);font-size:25px;color:var(--tx);margin-top:16px;line-height:1.22}
.chain .vd{font-size:16px;line-height:1.4;color:var(--mu);margin-top:8px}
.s3 .bd{grid-template-columns:1fr 860px;gap:56px;margin-top:40px}
.tiles3{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.tiles3 .card{padding:22px 22px}
.tiles3 .stat .v{font-size:46px}
.tiles3 .stat .l{font-size:16px}
/* ---------- slide 4 ---------- */
.flow{display:flex;align-items:stretch;margin-top:34px;gap:0}
.flow .fs{flex:1 1 0;min-width:0;border:1px solid var(--line2);background:linear-gradient(180deg,var(--fs-a),var(--fs-b));border-radius:12px;padding:16px 14px 16px}
.flow .fs .n{font-size:13px;letter-spacing:.14em;color:var(--ac);font-weight:700}
.flow .fs .nm{font-family:var(--serif);font-size:21px;line-height:1.22;color:var(--tx);margin-top:6px}
.flow .fs .ck{font-size:14.5px;line-height:1.38;color:var(--mu);margin-top:8px}
.flow .ar{flex:0 0 18px;align-self:center;height:2px;background:var(--acl)}
.flow .gate{flex:0 0 96px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px}
.flow .gate .dm{width:34px;height:34px;transform:rotate(45deg);border:2px solid var(--ac);background:var(--acs)}
.flow .gate .gl{font-size:13px;line-height:1.3;text-align:center;color:var(--ac);font-weight:600}
.s4 .bd{grid-template-columns:1fr 640px;gap:52px;margin-top:34px}
.pill4{display:grid;grid-template-columns:1fr 1fr;gap:34px 44px;align-content:start}
.pill4 .h{font-family:var(--serif);font-size:27px;line-height:1.22;color:var(--tx)}
.pill4 .x{font-size:19.5px;line-height:1.45;color:var(--tx2);margin-top:8px}
.s4 figure{align-self:start}
.kp4{grid-column:1 / -1;display:grid;grid-template-columns:repeat(3,1fr);gap:0;border-top:1px solid var(--line);margin-top:6px}
.kp4 > div{padding:22px 24px 0 0}
.kp4 > div + div{padding-left:24px;border-left:1px solid var(--line2)}
.kp4 .v{font-family:var(--serif);font-size:46px;line-height:1.16;color:var(--ac);white-space:nowrap}
.kp4 .l{font-size:17px;line-height:1.4;color:var(--mu);margin-top:10px}
/* ---------- slide 5: two views of the same order tool ---------- */
.s5 .bd{grid-template-columns:1fr 1fr;gap:56px;margin-top:26px;align-content:start}
.pf{display:flex;flex-direction:column;min-width:0}
.pf .h{font-family:var(--serif);font-size:26px;line-height:1.22;color:var(--tx);margin-top:24px;padding-bottom:.1em}
.pf .x{font-size:18px;line-height:1.45;color:var(--tx2);margin-top:6px}
/* ---------- slide 6 ---------- */
.s6 .bd{grid-template-columns:1000px 1fr;gap:52px;margin-top:36px}
.s6 .dl{grid-template-columns:132px 1fr;gap:15px 20px}
.s6 .dl dd{font-size:18px}
/* ---------- slide 7 ---------- */
.s7 .bd{grid-template-columns:repeat(4,1fr);gap:28px;margin-top:36px}
.s7 .card{display:flex;flex-direction:column;gap:14px;padding:24px 24px}
.s7 .card .t{font-size:19px;line-height:1.45}
.s7 .stat .v{font-size:50px}
.note7{margin-top:22px;font-size:17px;line-height:1.45;color:var(--mu);padding-left:18px;border-left:2px solid var(--acl)}
/* ---------- slide 8 ---------- */
.s8 .bd{grid-template-columns:1000px 1fr;gap:56px;margin-top:30px}
.tot2{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.tot2 .card{padding:20px 22px}
.tot2 .h{font-size:14px;letter-spacing:.16em;text-transform:uppercase;font-weight:700}
.tot2 .v{font-family:var(--serif);font-size:50px;line-height:1.16;margin-top:6px;white-space:nowrap}
.tot2 .l{font-size:15.5px;line-height:1.42;color:var(--tx2);margin-top:8px}
.lev td{font-size:17px;padding:7px 10px}
.lev th{padding:0 10px 8px}
/* ---------- slide 9 ---------- */
.s9 .bd{grid-template-columns:900px 1fr;gap:56px;margin-top:28px}
.op td{font-size:17px;padding:7px 10px}
.op th{padding:0 10px 8px}
.op td.k{color:var(--mu)}
/* ---------- slide 10 ---------- */
.s10 .bd{grid-template-columns:1000px 1fr;gap:56px;margin-top:30px}
.kp td{font-size:19px;padding:11px 10px}
.kp th{padding:0 10px 8px}
/* ---------- slide 11 ---------- */
.s11 .bd{grid-template-columns:1fr 720px;gap:56px;margin-top:26px}
.boi{display:flex;flex-direction:column;gap:0}
.boi .r{display:grid;grid-template-columns:170px 1fr 150px;gap:20px;padding:13px 0;border-bottom:1px solid var(--line2);align-items:start}
.boi .r .k{font-size:14px;letter-spacing:.14em;text-transform:uppercase;color:var(--ac);font-weight:600;padding-top:3px}
.boi .r .x{font-size:17.5px;line-height:1.42;color:var(--tx2)}
.boi .r .st{text-align:right;padding-top:1px}
.fin .need{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.fin .need .card{padding:18px 22px}
.fin .need .v{font-family:var(--serif);font-size:44px;line-height:1.16;white-space:nowrap}
.fin .need .l{font-size:15.5px;color:var(--mu);margin-top:6px;line-height:1.4}
.share{margin-top:18px}
.share h3{margin-bottom:12px}
.share .bar,.share .lbl{display:grid;grid-template-columns:60fr 40fr;gap:6px}
.share .bar i{display:block;height:14px;border-radius:4px}
.share .bar .a{background:var(--acf)}
.share .bar .c{background:var(--acs);border:1px dashed var(--acb)}
.share .lbl{margin-top:10px}
.share .lbl b{display:block;font-family:var(--serif);font-weight:500;font-size:25px;line-height:1.2;color:var(--tx);padding-bottom:.1em}
.share .lbl b.ac{color:var(--ac)}
.share .lbl span{display:block;font-size:15.5px;line-height:1.4;color:var(--mu);margin-top:2px}
.share .nt{font-size:15px;line-height:1.4;color:var(--dim);margin-top:8px}
.fin .tbd{margin-top:18px;font-family:var(--serif);font-size:23px;line-height:1.3;color:var(--tx);padding:16px 22px;border-left:3px solid var(--acf);background:var(--acs);border-radius:0 10px 10px 0}
/* ---------- slide 12 ---------- */
.s12 .bd{grid-template-rows:auto auto;gap:16px;margin-top:20px}
.s12 .cols{display:grid;grid-template-columns:1fr 1fr 1fr;gap:40px}
.s12 .cols .t{font-size:17px}
.seq{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:9px;counter-reset:q}
.seq li{counter-increment:q;position:relative;padding-left:40px;font-size:17px;line-height:1.4;color:var(--tx2)}
.seq li::before{content:counter(q);position:absolute;left:0;top:-1px;width:26px;height:26px;border-radius:50%;border:2px solid var(--ac);color:var(--ac);font-size:13px;font-weight:700;display:grid;place-items:center}
/* ---------- slide 13 ---------- */
.rk td{font-size:16.5px;padding:8px 12px;line-height:1.36}
.rk td:first-child{width:118px}
.rk td:nth-child(2){width:760px;color:var(--tx)}
.rk th{padding:0 12px 9px}
/* ---------- slide 14 ---------- */
.s14 .bd{grid-template-columns:520px 470px 1fr;gap:48px;margin-top:34px}
.ppl{display:flex;flex-direction:column;gap:20px}
.ppl .nm{font-family:var(--serif);font-size:27px;color:var(--tx);line-height:1.22}
.ppl .ro{font-size:15px;letter-spacing:.12em;text-transform:uppercase;color:var(--ac);font-weight:600;margin-top:6px}
.ppl .x{font-size:17px;line-height:1.42;color:var(--tx2);margin-top:6px}
.vd td{font-size:16.5px;padding:9px 10px}
.vd td:first-child{color:var(--tx);font-weight:600;white-space:nowrap}
/* ---------- slide 15 ---------- */
.s15{--gx:100%;--gy:0%;--gr:40%}
.s15 .bd{grid-template-columns:1fr 600px;grid-template-rows:auto 1fr;gap:30px 56px;margin-top:24px}
.unl{grid-column:1 / -1;display:grid;grid-template-columns:repeat(3,1fr);gap:0;border-top:1px solid var(--line);align-self:end}
.unl .u{padding:20px 30px 0 0}
.unl .u + .u{padding-left:30px;border-left:1px solid var(--line2)}
.unl .h{font-family:var(--serif);font-size:25px;line-height:1.22;color:var(--ac)}
.unl .x{font-size:17.5px;line-height:1.45;color:var(--tx2);margin-top:8px}
.ask .v{font-family:var(--serif);font-size:56px;line-height:1.16;color:var(--ac);white-space:nowrap}
.tr td{font-size:17px;padding:9px 10px;line-height:1.35}
.tr th{padding:0 10px 8px}
.tr td:first-child{color:var(--ac);font-weight:700;width:44px}
.chk{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:10px}
.chk li{position:relative;padding-left:30px;font-size:17px;line-height:1.4;color:var(--tx2)}
.chk li::before{content:"";position:absolute;left:0;top:4px;width:16px;height:16px;border:2px solid var(--ac);border-radius:4px}
/* ---------- Plan B additions ---------- */
.s1 .plans b{color:var(--ac);font-weight:600}
.tb th.ac{color:var(--ac)}
.tb tr.tot td.neg{color:var(--neg)}
.tot2 .v .usd{font-family:var(--sans);font-size:19px;color:var(--mu);margin-left:14px;letter-spacing:.02em;vertical-align:.35em}
.cmpA{display:flex;justify-content:space-between;align-items:center;gap:20px;margin-top:14px;padding:12px 22px;border:1px dashed var(--dash);border-radius:12px;font-size:16px;line-height:1.4;color:var(--mu)}
.cmpA b{font-family:var(--serif);font-size:28px;line-height:1.16;font-weight:500;color:var(--tx2);white-space:nowrap;padding-bottom:.1em}
.s8b .lede{font-size:22px;max-width:1640px}
.s8b .bd{grid-template-columns:1fr 760px;gap:60px;margin-top:40px}
.why{display:grid;grid-template-columns:1fr 1fr;gap:40px 44px;align-content:start}
.why .n{font-size:14px;letter-spacing:.12em;color:var(--ac);font-weight:700}
.why .h{font-family:var(--serif);font-size:27px;line-height:1.22;color:var(--tx);margin-top:4px;padding-bottom:.1em}
.why .x{font-size:19px;line-height:1.45;color:var(--tx2);margin-top:6px}
.cmp td{font-size:18.5px;padding:11px 10px}
.op3{margin-top:26px}
.op3 .v{font-size:40px}
.op3 .l{font-size:15.5px}
.cmp th{padding:0 10px 8px}
.cmp td.n:nth-child(2),.kp td.n:nth-child(2){color:var(--tx);font-weight:600}
.cmp td.n:nth-child(3),.kp td.n:nth-child(3){color:var(--mu)}
/* Palatino's descent runs below the line box: keep it inside the element */
h1,h2,.stat .v,.s1 .keys .v,.big3 .v,.tot2 .v,.fin .need .v,.ask .v,.kp4 .v,.chain .nm,.flow .fs .nm,.ppl .nm,.pill4 .h,.feat .h,.unl .h{padding-bottom:.1em}
h1{padding-bottom:.14em}
/* screenshots of the web tools: a dark and a light version, swapped with the theme */
.thl{display:none!important}
html[data-theme=light] .thl{display:block!important}
html[data-theme=light] .thd{display:none!important}
.ctl .ic-moon{display:none}
html[data-theme=light] .ctl .ic-sun{display:none}
html[data-theme=light] .ctl .ic-moon{display:block}
/* notes are for the presenter only */
.notes{display:none}
/* ---------- controls ---------- */
.ctl{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);display:flex;align-items:center;gap:2px;padding:5px 6px;border-radius:99px;
background:var(--ctl-bg);border:1px solid var(--ctl-bd);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
box-shadow:var(--ctl-sh);opacity:0;transition:opacity .5s ease;z-index:20;font-size:14px;color:var(--tx2)}
body.ui .ctl,.ctl:focus-within,.ctl:hover{opacity:1}
.ctl button,.ctl a{width:36px;height:34px;border:0;background:transparent;border-radius:99px;display:grid;place-items:center;cursor:pointer;color:var(--tx2);text-decoration:none}
.ctl button:hover,.ctl a:hover{background:var(--hov);color:var(--tx)}
.ctl svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.ctl .cnt{min-width:66px;text-align:center;font-variant-numeric:tabular-nums;letter-spacing:.04em;color:var(--tx)}
.ctl .sep{width:1px;height:20px;background:var(--th-bd);margin:0 4px}
.ctl a.pdf{width:auto;padding:0 12px;font-size:12.5px;font-weight:700;letter-spacing:.1em}
.jump{position:fixed;left:50%;top:18px;transform:translateX(-50%);background:var(--jump-bg);border:1px solid var(--line);border-radius:10px;padding:8px 16px;font-size:18px;color:var(--ac);z-index:30;font-variant-numeric:tabular-nums}
/* ---------- overview ---------- */
.ov{position:fixed;inset:0;background:var(--outer);z-index:40;overflow:auto;padding:28px 32px 40px}
.ov h4{margin:0 0 18px;font-size:14px;letter-spacing:.2em;text-transform:uppercase;color:var(--mu);font-weight:600}
.ov .grid{display:grid;grid-template-columns:repeat(5,1fr);gap:18px}
.ov .th{position:relative;aspect-ratio:16/9;border-radius:8px;overflow:hidden;border:1px solid var(--th-bd);cursor:pointer;background:var(--bg);padding:0}
.ov .th.cur{outline:2px solid var(--ac);outline-offset:2px}
.ov .th:hover{border-color:var(--th-hov)}
.ov .th .lab{position:absolute;left:0;right:0;bottom:0;padding:6px 10px;font-size:12px;text-align:left;background:linear-gradient(0deg,var(--lab-a),var(--lab-b));color:var(--tx2);z-index:2}
.mini{position:absolute;left:0;top:0;width:1920px;height:1080px;transform-origin:0 0;pointer-events:none;overflow:hidden;background:var(--bg)}
.mini .slide{transition:none!important;transform:none!important;opacity:1!important;visibility:visible!important}
@media (max-width:900px){.ov .grid{grid-template-columns:repeat(2,1fr)}}
/* ---------- presenter view ---------- */
.pv{position:fixed;inset:0;background:var(--pv-bg);display:grid;grid-template-rows:56px 1fr;color:var(--tx);font-family:var(--sans)}
.pv header{display:flex;align-items:center;gap:22px;padding:0 22px;border-bottom:1px solid var(--line2);font-size:15px;color:var(--mu)}
.pv header .ttl{letter-spacing:.16em;text-transform:uppercase;font-weight:600;color:var(--ac);font-size:13px}
.pv header .cn{font-size:20px;color:var(--tx);font-variant-numeric:tabular-nums}
.pv header .tm{font-size:24px;color:var(--tx);font-variant-numeric:tabular-nums;margin-left:auto;font-family:var(--serif)}
.pv header button{background:var(--btn);border:1px solid var(--line);border-radius:8px;padding:6px 12px;cursor:pointer;font-size:13px;color:var(--tx2)}
.pv header button:hover{background:var(--btn-h)}
.pv .body{display:grid;grid-template-columns:1.55fr 1fr;gap:20px;padding:18px 22px;min-height:0}
.pv .col{display:flex;flex-direction:column;gap:10px;min-height:0}
.pv .lb{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--dim);font-weight:600}
.pv .frame{position:relative;aspect-ratio:16/9;width:100%;border-radius:8px;overflow:hidden;border:1px solid var(--line);background:var(--bg)}
.pv .frame.next{opacity:.92}
.pv .frame .end{position:absolute;inset:0;display:grid;place-items:center;color:var(--dim);font-size:16px}
.pv .nt{flex:1;min-height:0;overflow:auto;font-size:19px;line-height:1.55;color:var(--tx2);padding:14px 18px;border:1px solid var(--line2);border-radius:8px;background:var(--well)}
.pv .nav{display:flex;gap:10px}
.pv .nav button{flex:1;background:var(--nav-btn);border:1px solid var(--line);border-radius:8px;padding:10px;cursor:pointer;color:var(--tx2)}
body.pvmode .stage,body.pvmode .ctl{display:none}
/* ---------- print / PDF: one slide per page, backgrounds on ---------- */
@page{size:1920px 1080px;margin:0}
@media print{
html,body{overflow:visible!important;height:auto!important;background:var(--bg)!important;width:1920px}
*{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
.stage{position:static!important;transform:none!important;width:1920px!important;height:auto!important;overflow:visible!important;box-shadow:none}
.slide{position:relative!important;opacity:1!important;visibility:visible!important;transform:none!important;transition:none!important;
break-after:page;page-break-after:always;break-inside:avoid;overflow:hidden}
.slide:last-child{break-after:auto;page-break-after:auto}
.notes,.ctl,.ov,.pv,.jump{display:none!important}
.fig{box-shadow:none}
}
</style>
</head>
<body>
<div class="stage" id="stage">
<!-- 1 -->
<section class="slide s1 grooves" data-title="CNX Vinyl">
<div class="kicker"><i class="disc"></i>Investment proposal: Plan B, ~215M THB<span class="no">· Draft for discussion · October 2026</span></div>
<div class="bd">
<div>
<h1>CNX Vinyl</h1>
<p class="sub">A full-chain vinyl record plant for Asia-Pacific, in Chiang Mai, Thailand</p>
<p class="plans">Investment proposal: <b>Plan B, ~215M THB</b>, the launch plant with everything in. October 2026, draft. Plan A, the lowest-capital variant (~182M THB), appears only as the comparison.</p>
<div class="keys">
<div><div class="v">~217M</div><div class="l">THB at the midpoint (~6.56M USD), labelled ~215M: ramp-up reserve in, no contingency</div></div>
<div><div class="v">0.58 to 0.90M</div><div class="l">records a year at 60&nbsp;% specials on three Newbilt presses <span class="e">est.</span></div></div>
<div><div class="v">Whole chain</div><div class="l">cutting, galvanics, stamper prep and pressing in-house from the first release</div></div>
</div>
<p class="by">Simona Meesaiyati, Project Lead · Alexander Grabowski, Technical Consultant · supported by buildfor.life · label name Poy Records</p>
</div>
<figure>
<div class="fig"><img src="img/plan-b-cover.webp" width="1800" height="1010" alt="Isometric render of the planned plant: factory hall with the owned solar array on its roof, office, utility yard, car park and road"></div>
<figcaption>Plan B as the plant and business plan calculator draws it: the owned ~520 kWp array on the factory roof, office roof and canopies PV-ready. Schematic, on the Payap scenario layout and a placeholder plot; the site is not decided.</figcaption>
</figure>
</div>
<p class="src"><b>Source:</b> business plan B, draft 0.3 (October 2026), cover and section 1; total-investment.md "Two plans". Render: fleet-calculator.html, Plan B preset (business plan B, figure 8). The price includes the 10M to 20M THB ramp-up reserve and carries no contingency.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">CNX Vinyl, label name Poy Records, is a planned record plant near Chiang Mai with the whole chain under one roof. We propose Plan B: about 217M THB at the midpoint, labelled 215M, everything in, reserve included, no contingency. The main investor wants to go with Plan B. If asked why not Plan A: it builds the same three presses for about 182M by leasing the roof array, renting a crane, taking a Chinese compressor and deferring metrology, the corridor and cassette and CD-R; slide 9 sets out plainly what Plan B's extra buys and what it does not earn. Nothing is ordered or signed yet. Sources: business plan B, section 1; total-investment.md "Two plans".</aside>
</section>
<!-- 2 -->
<section class="slide s2" data-title="The opportunity">
<div class="kicker"><i class="disc"></i>The opportunity</div>
<h2>Asia-Pacific labels press on other continents. We know of no full-chain plant in the region.</h2>
<div class="bd">
<div>
<h3>Where regional customers press today</h3>
<div class="big3">
<div><div class="v">Europe, the US, Japan</div><div class="l">Labels in Southeast Asia, Australia and much of East Asia ship masters there, wait in those queues and pay freight and time zones on top.</div></div>
<div><div class="v">10 to 16 weeks</div><div class="l">roughly, for a standard black LP at European and US plants today, after 6 to 9 months at the 2021 to 2022 peak.</div></div>
<div><div class="v">One shop</div><div class="l">The one Thai pressing shop we have seen presses only: it buys its stampers abroad, has no climate control, and cannot economically serve runs under 300 units.</div></div>
</div>
</div>
<div>
<h3>Price per record: the two market data points on file</h3>
<svg class="chart" id="chPrice" width="620" height="330" viewBox="0 0 620 330" role="img" aria-label="Price per copy at 300 copies"></svg>
<p class="s" style="margin-top:18px">Scopes differ. Europe: black pressing with printed labels and a paper inner sleeve at 300 copies. CNX: the plan's assumed price, USD 3.76 to 5.27, covering pressing, labels, inner sleeve and assembly at 60&nbsp;% specials. Thailand: the per-unit price from the August 2026 invoices (650 to 950 THB all-in at 300), a printed jacket included; several times the CNX assumption even after 100 to 150 THB for the jacket.</p>
</div>
<div class="card warn" style="align-self:start">
<h3 class="ac">What is still open</h3>
<ul class="list sm">
<li><b>Market validation is open</b> (roadmap item 1): no buyer prices yet. The Thai data is one buyer, one plant, one afternoon.</li>
<li>Making Vinyl Europe, Haarlem: strong supplier interest in full traceability. Supply-side evidence, not buyer validation.</li>
<li>The plan keeps its assumed price until buyers have been asked.</li>
<li>We compete on capability, not on price: in-house plating, climate control, metrology, traceability, short runs and represses from archived metal.</li>
</ul>
</div>
</div>
<p class="src"><b>Source:</b> business plan B, sections 1, 3.1 to 3.3 and 9.1; revenue-model.md "First market data point" and "Record Industry's calculator"; roadmap.md item 1. THB at 33 per USD.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">The customer we serve presses abroad today. Lead times have normalised to roughly 10 to 16 weeks for a standard black LP, but freight, time zones and the queue remain. The one Thai shop we have seen presses only; its stampers come from abroad, so short runs and represses are slow and expensive. On price, our assumption of USD 3.76 to 5.27 a record sits between the European ex-works level of about USD 2.43 and the Thai market. Be clear with lenders: buyer-side validation, roadmap item 1, is still open, and the Thai evidence is one supplier and one afternoon. The financing case waits on it. Sources: business plan B, sections 3.1 to 3.3; revenue-model.md; roadmap.md.</aside>
</section>
<!-- 3 -->
<section class="slide s3 grooves" data-title="What we build" style="--gx:100%;--gy:100%;--gr:36%">
<div class="kicker"><i class="disc"></i>What we build</div>
<h2>The complete vinyl chain under one roof, bought in one capital round</h2>
<div class="chain">
<div class="st"><div class="dot">1</div><div class="nm">Mastering</div><div class="vd">Console and chain; one to two engineers</div></div>
<div class="st"><div class="dot">2</div><div class="nm">Lacquer cutting</div><div class="vd">Sillitoe SDMS cutting system</div></div>
<div class="st"><div class="dot">3</div><div class="nm">Galvanics</div><div class="vd">Pheenix Alpha line; zero liquid discharge</div></div>
<div class="st"><div class="dot">4</div><div class="nm">Stamper prep</div><div class="vd">Sibert back-finish, punch, coin</div></div>
<div class="st"><div class="dot">5</div><div class="nm">Pressing</div><div class="vd">Newbilt Duplex system and Classic</div></div>
<div class="st"><div class="dot">6</div><div class="nm">Finishing</div><div class="vd">Trim, sleeve, shrink; optical QC</div></div>
<div class="st"><div class="dot">7</div><div class="nm">Packaging</div><div class="vd">Ploy Printing, Chiang Mai</div></div>
<div class="st"><div class="dot">8</div><div class="nm">Fulfilment</div><div class="vd">Bulk to labels, parcels to fans</div></div>
</div>
<div class="bd">
<div>
<div class="tiles3">
<div class="card stat"><div class="v">3 presses</div><div class="l">One Newbilt Duplex system (two semi-automatic HRP 12, EX 50 main and EX 35 colour extruder) and one Newbilt Classic, fully automatic</div></div>
<div class="card stat"><div class="v">0.58 to 0.90M</div><div class="l">records a year on two shifts at 60&nbsp;% specials, central 0.74M <span class="e">est.</span>; 0.71 to 0.97M at a standard-heavy mix</div></div>
<div class="card stat"><div class="v">60&nbsp;%+</div><div class="l">special pressings: splatter, marble, colour effects. They run only on the Duplex, which sets the pace</div></div>
</div>
<ul class="list sm" style="margin-top:26px">
<li><b>Metal archive:</b> the father and the mother of every side are kept, so a repress starts from our own metal: 2 to 3 weeks from an archived mother (planning figure).</li>
<li><b>Also from launch:</b> cassette and CD-R duplication in-house. Full CD replication is a separate investor decision, outside the price.</li>
<li><b>Growth from revenue, not in the plan price:</b> a second Duplex system lifts output to ~1.0 to 1.5M records a year; its floor, utility positions and pipe tees are reserved.</li>
</ul>
</div>
<figure>
<div class="fig"><img src="img/plan-b-iso-hall.webp" width="1800" height="1010" alt="Isometric view of the press hall with the Duplex system, the Classic, the crane, the visitor corridor and reserved bays"></div>
<figcaption>Press hall, Plan B preset: Duplex system 1 and the Classic, the 5 t crane, the visitor corridor and the cassette and CD-R rooms; dashed bays reserved for growth. Schematic (fleet-calculator.html).</figcaption>
</figure>
</div>
<p class="src"><b>Source:</b> business plan B, sections 1, 4.0 to 4.4, 4.7 and 4.9, figure 10; total-investment.md "Launch plant"; scale.md; production-timeline.md; equipment/pressing-line.md.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">This is a plant, not a pressing shop: cutting, galvanics and stamper preparation are in-house from the first release, with no bridge stampers. Three Newbilt presses: a Duplex system of two semi-automatic presses with a colour extruder, and one fully automatic Classic at about 128 records an hour (vendor flyer). At the planned 60&nbsp;% special pressings the Duplex sets the pace: 0.58 to 0.90 million records a year on two shifts, central 0.74 million; the calculator gives about 0.94 million at the team's planning rates. Why the whole chain at once is answered on the risk slide. In Plan B the cassette and CD-R lines run from launch too. Sources: business plan B, sections 4.0 to 4.4; scale.md.</aside>
</section>
<!-- 4 -->
<section class="slide s4" data-title="How a record is made">
<div class="kicker"><i class="disc"></i>How a record is made here</div>
<h2>Nine stages, three artist approval gates, every record traceable</h2>
<div class="flow">
<div class="fs"><div class="n">1</div><div class="nm">Audio intake</div><div class="ck">Checksums, format, technical QA</div></div>
<div class="gate"><div class="dm"></div><div class="gl">Gate 1<br>master plan</div></div>
<div class="fs"><div class="n">2</div><div class="nm">Vinyl mastering</div><div class="ck">Engineer QA of the vinyl master</div></div>
<div class="gate"><div class="dm"></div><div class="gl">Gate 2<br>master</div></div>
<div class="fs"><div class="n">3</div><div class="nm">Lathe cutting</div><div class="ck">Groove microscope, test cut, dead-wax scribe</div></div>
<div class="ar"></div>
<div class="fs"><div class="n">4</div><div class="nm">Silvering, electro&shy;forming</div><div class="ck">Bath logs, XRF thickness per part</div></div>
<div class="ar"></div>
<div class="fs"><div class="n">5</div><div class="nm">Stamper prep</div><div class="ck">Concentricity, dimensions, visual</div></div>
<div class="ar"></div>
<div class="fs"><div class="n">6</div><div class="nm">Pressing</div><div class="ck">Telemetry per cycle, 100&nbsp;% optical, sampled audio</div></div>
<div class="gate"><div class="dm"></div><div class="gl">Gate 3<br>test pressing</div></div>
<div class="fs"><div class="n">7</div><div class="nm">Finishing</div><div class="ck">Trim quality, sleeving</div></div>
<div class="ar"></div>
<div class="fs"><div class="n">8</div><div class="nm">Packaging</div><div class="ck">Matrix code scanned against the jacket barcode</div></div>
<div class="ar"></div>
<div class="fs"><div class="n">9</div><div class="nm">Fulfilment</div><div class="ck">Carrier tracking per shipment</div></div>
</div>
<div class="bd">
<div class="pill4">
<div><div class="h">Traceability</div><div class="x">A record's key is stamper + press + cycle timestamp + side. Logs are append-only; a complaint resolves to one stamper, press, shift and material lot.</div></div>
<div><div class="h">One system of record</div><div class="x">ERPNext holds orders, gates, job cards, stock, inspections and metal-part genealogy; machine data sits beside it, keyed to the same names.</div></div>
<div><div class="h">Layered quality control</div><div class="x">Inline optical inspection of every record, press telemetry every cycle, sampled audio comparison against the master; a lab gate for every PVC lot.</div></div>
<div><div class="h">Metrology and climate</div><div class="x">Every stamper measured from the first release: XRF, laser confocal, wide-area profiler. The plant at 20 to 26 °C, 35 to 60&nbsp;% RH, filtered.</div></div>
<div class="kp4">
<div><div class="v">100&nbsp;%</div><div class="l">of records through inline optical inspection</div></div>
<div><div class="v">3 gates</div><div class="l">artist approvals recorded before work continues</div></div>
<div><div class="v">~10 sets</div><div class="l">of stampers per archived mother; 5 to 10 mothers per father (rules of thumb)</div></div>
</div>
</div>
<figure>
<div class="fig"><img class="thd" src="img/record-passport-dark.webp" width="1590" height="816" alt="Record passport demo: the production history of one pressed copy"><img class="thl" src="img/record-passport-light.webp" width="1590" height="816" alt="Record passport demo: the production history of one pressed copy"></div>
<figcaption>Record passport demo: what a fan sees from the code on the sleeve (IDs simulated, make-a-record.html).</figcaption>
</figure>
</div>
<p class="src"><b>Source:</b> process-flow.md; traceability.md; quality-automation.md; digital-factory.md; business plan B, sections 1, 4 and 5.1 to 5.3.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">The three diamonds are the artist's approvals: the master plan, the master and the test pressing; nothing proceeds without a recorded approval. What a lender should take away is control: every lacquer, father, mother, stamper, press cycle and material lot gets an identity that knows its parents, so a complaint resolves backwards in minutes. Every record passes inline optical inspection; audio is compared on samples, first-off per stamper and more often as a stamper ages. At Making Vinyl Europe this traceability was what the industry responded to most; that is supplier feedback, not buyer validation. Plan B measures every stamper fully from the first release. Sources: process-flow.md; traceability.md; quality-automation.md; business plan B, sections 5.1 and 5.2.</aside>
</section>
<!-- 5 -->
<section class="slide s5" data-title="The platform">
<div class="kicker"><i class="disc"></i>The platform</div>
<h2>One order tool: an itemised quote, or a pressing funded by fans</h2>
<div class="bd">
<div class="pf">
<figure>
<div class="fig"><img class="thd" src="img/order-splatter-dark.webp" width="1800" height="1125" alt="Order tool, step 4: a 12 inch splatter record configured, with the estimated price and ship date"><img class="thl" src="img/order-splatter-light.webp" width="1800" height="1125" alt="Order tool, step 4: a 12 inch splatter record configured, with the estimated price and ship date"></div>
<figcaption>Step 4 with the "Splatter 12"" preset: the picture follows every option, the quote card every line. Prices and dates indicative, not an offer.</figcaption>
</figure>
<div class="h">An itemised offer for every customer</div>
<div class="x">Every line priced from one price list, per title and per copy, with audio and artwork checked at upload. ERPNext is the system of record: the order, the three gates and the invoice live there. Public early with indicative prices, so every configuration is market data.</div>
</div>
<div class="pf">
<figure>
<div class="fig"><img class="thd" src="img/order-fans-dark.webp" width="1800" height="1125" alt="Order tool, fan-funding view: fan price, fans needed and what the artist keeps"><img class="thl" src="img/order-fans-light.webp" width="1800" height="1125" alt="Order tool, fan-funding view: fan price, fans needed and what the artist keeps"></div>
<figcaption>The same record funded by fans: at 1,200 THB a copy it needs 100 fans (break-even 46), and the artist keeps ~480 THB a copy after setup, pressing and fees. Indicative; the platform's name is not decided.</figcaption>
</figure>
<div class="h">Fan-funded campaigns</div>
<div class="x">Fans pre-order into a ring-fenced account and are refunded automatically if the goal is missed (the recommended model). The fans needed are the larger of break-even and the 100-copy minimum run, a planning figure. Campaigns open only after the first releases ship.</div>
</div>
</div>
<p class="src"><b>Source:</b> artist-platform.md ("The goal", campaigns and payments); business plan B, sections 4.8 and 5.1; digital-factory.md "ERPNext: the plant's ERP". Screenshots: order.html, the step-by-step order tool (demo). Public side ~USD 40k to 90k <i>est.</i>, inside the software block.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">The platform is the plant's front door, and both pictures are the same tool. Anyone, from a bedroom artist to a label, configures a record and gets an itemised offer from the price list that drives the invoice in ERPNext. The same order can instead be funded by fans: the artist sets the fan price, and the fans needed follow as the larger of break-even and the 100-copy minimum. Fans pay into a ring-fenced account and are refunded automatically if the goal is missed; the rule comes from Qrates, whose wind-down caught funded projects. A Thai legal opinion comes first, and seller of record is open. Sources: artist-platform.md; business plan B, section 4.8.</aside>
</section>
<!-- 6 -->
<section class="slide s6 grooves" data-title="Site and building" style="--gx:100%;--gy:100%;--gr:30%">
<div class="kicker"><i class="disc"></i>Site and building</div>
<h2>A ~1,500 m² plant on leased land, utilities built for four modules</h2>
<div class="bd">
<figure>
<div class="fig"><img src="img/plan-b-iso-gf.webp" width="1800" height="1010" alt="Isometric ground floor with the roofs cut away, labelled zones from galvanics to warehouse, the crane, the visitor corridor and the battery"></div>
<figcaption>Ground floor with the roofs cut away, Plan B preset: galvanics, stamper prep, PVC lab, press hall, inspection, packaging and warehouse; the office with the cutting room; the 5 t crane, the visitor corridor and the 271 kWh battery. Schematic of the calculator's Payap scenario layout on a placeholder plot.</figcaption>
</figure>
<dl class="dl">
<dt>Site</dt><dd>Long lease, working candidate in Lamphun (Mueang Chi), Chiang Mai options under review; Northern Region Industrial Estate as fallback. Nothing signed.</dd>
<dt>Land</dt><dd>~4 rai minimum, 6 to 8 rai with expansion. Leasing keeps ~12M to 32M THB of land purchase out of the round; rent ~0.3M to 1.0M THB a year <span class="e">est.</span></dd>
<dt>Building</dt><dd>Steel portal frame, 60 × 25 m (1,500 m²), with its own 5 t crane; 30 kN/m² slab raised 1.2 m for dock-height loading; insulated, airtight.</dd>
<dt>Visitors</dt><dd>A glazed corridor in process order, built from the start: tours and the Payap programmes without stopping production.</dd>
<dt>Utility yard</dt><dd>~379 m²: boiler hall and chiller compound for four modules each. Launch: two 500 kg/h biomass boilers, two 250 kW chillers, Thai-supplied.</dd>
<dt>Roof</dt><dd>~520 kWp PV on the factory roof and a 271 kWh battery, owned <span class="e">est.</span>; office roof and canopies PV-ready.</dd>
<dt>Growth</dt><dd>Duplex bays and utility positions reserved; a 45 × 38 m (1,710 m²) second building fits the Mueang Chi plot.</dd>
</dl>
</div>
<p class="src"><b>Source:</b> business plan B, sections 6.1 and 6.2; site-candidates.md; scale.md; crane-workshop-tours.md; render: fleet-calculator.html, Plan B preset (figure 9). The 60 × 25 m hall is the Mueang Chi design; the layout is fixed once a plot passes zoning and lease-ability.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">The strategy is a long lease of raw land with a purpose-built plant on it. That keeps an estimated 12M to 32M THB of land purchase out of the round, but it also means there is no land to pledge: the lease must give the company ownership of the buildings and the right to mortgage or assign them. Mueang Chi in Lamphun is the working candidate; its zoning is still unverified. The point Payap University gave in Chiang Mai failed zoning; its other land is unchecked. Plan B builds the crane and the visitor corridor from the start. The render shows the calculator's Payap scenario layout, not a decided site. Sources: business plan B, sections 6.1 and 6.2; site-candidates.md.</aside>
</section>
<!-- 7 -->
<section class="slide s7" data-title="Sustainability and utilities">
<div class="kicker"><i class="disc"></i>Sustainability, utilities and the region</div>
<h2>Biomass steam, owned solar and zero liquid discharge, in the North</h2>
<div class="bd">
<div class="card">
<h3>Steam from biomass</h3>
<svg class="chart" id="chSteam" width="370" height="132" viewBox="0 0 370 132" role="img" aria-label="Steam energy cost a year, biomass against electric"></svg>
<p class="t">Two 500 kg/h modules on wood chips: <b>~0.67M THB</b> of fuel a year against <b>~4.9M THB</b> for electric steam, saving ~4.2M THB <span class="e">est.</span> Grid demand ~264 kW instead of ~980 kW.</p>
<p class="t">Heat recovered first: flue-gas economiser, condensate return, a heat pump lifting the presses' ~60 °C cooling return into feedwater.</p>
</div>
<div class="card">
<h3>Owned solar on the roof</h3>
<div class="stat"><div class="v">~520 kWp</div></div>
<svg class="chart" id="chSolar" width="370" height="76" viewBox="0 0 370 76" role="img" aria-label="Share of the plant's electricity from the roof array"></svg>
<p class="t">~730 to 780 MWh a year, ~87&nbsp;% used on site: ~633 MWh, ~61&nbsp;% of the plant's ~1,036 MWh <span class="e">est.</span> Zero export on PEA's grid.</p>
<p class="t"><b class="ac">Owned, with the 271 kWh battery:</b> ~2.3M to 2.5M THB a year off the power bill, payback ~4.1 to 5.8 years <span class="e">est.</span></p>
</div>
<div class="card">
<h3>Water and material</h3>
<div class="stat"><div class="v">Zero</div><div class="l">process water discharged</div></div>
<p class="t">Closed cooling loop. Galvanics rinse water goes through a zero-discharge evaporator with metal recovery, in its own treatment building.</p>
<p class="t">80&nbsp;% virgin, 20&nbsp;% regrind: edge trim and rejects are ground in-house and go back to the extruder.</p>
</div>
<div class="card">
<h3>Northern Thailand</h3>
<div class="stat"><div class="v">~15 to 25</div><div class="l">jobs across shifts at volume <span class="e">est.</span></div></div>
<p class="t">Skilled roles in galvanics, cutting, QC and IT; Payap University programmes from day one through the visitor corridor (under discussion).</p>
<p class="t">Chiang Mai, Lamphun and Lampang: the Northern Economic Corridor, promoted by BOI as a creative-economy base. Packaging from Chiang Mai.</p>
</div>
</div>
<p class="note7">Biomass brings fuel contracts, a monsoon stock, ash handling, a 14 m stack with a dust collector and a certified boiler attendant; the stack is a visible point source in Chiang Mai's burning season, so dust control is drawn from the start.</p>
<p class="src"><b>Source:</b> business plan B, sections 1, 5.4, 8.4 and 12.1; biomass.md; sustainability.md "PV size with biomass steam" and "PV, not solar thermal"; boi.md "Regional angle"; scale.md. Fuel at ~850k records, planning price 1.35 to 2 THB/kg delivered.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">Steam is the plant's largest energy load, and both plans burn biomass from launch: about 0.67M THB of wood chips a year against about 4.9M THB for electric steam, and grid demand of about 264 kW instead of 980. Solar goes on the factory roof only: about 520 kWp sized to the load, mostly used on site under PEA's zero export. The BOI no longer promotes solar since 1 July 2025, so Plan B owns the array for the bill saving, about 2.3M to 2.5M THB a year, not for a tax benefit. Galvanics runs zero liquid discharge, the strongest answer to the effluent permit. Estimates. Sources: biomass.md; sustainability.md; business plan B, sections 5.4 and 8.4.</aside>
</section>
<!-- 8 -->
<section class="slide s8" data-title="Investment">
<div class="kicker"><i class="disc"></i>Investment, Plan B</div>
<h2>Plan B: ~217M THB at the midpoint, the launch plant with everything in</h2>
<div class="bd">
<div>
<div class="legend" style="margin-bottom:12px"><span><i style="background:var(--acf)"></i>Plan B, block midpoint</span><span><i style="background:transparent;height:2px;border-top:2px solid var(--whisk);border-radius:0;vertical-align:4px"></i>low to high end of the block</span><span>M THB <span class="e">est.</span></span></div>
<svg class="chart" id="chInvest" width="1000" height="600" viewBox="0 0 1000 600" role="img" aria-label="Plan B investment by block group, low, midpoint and high"></svg>
</div>
<div>
<div class="tot2" style="grid-template-columns:1fr">
<div class="card warn"><div class="h ac">Plan B, the proposal</div><div class="v ac">~217M THB <span class="usd">~6.56M USD</span></div><div class="l">201.6M for the launch plant plus the 10M to 20M ramp-up reserve; ~156M to 277M from the low to the high end of every block. Labelled ~215M.</div></div>
</div>
<div class="cmpA"><span>Plan A, the lowest-capital variant: the same plant with six cost levers</span><b>~182M THB</b></div>
<h3 style="margin-top:24px">What Plan B buys that Plan A leases or defers</h3>
<table class="tb lev">
<tr><th>In at launch in Plan B</th><th>Plan A instead</th><th class="n">M THB</th></tr>
<tr><td>Solar ~520 kWp and 271 kWh battery, owned</td><td>Solar PPA</td><td class="n">~14.7</td></tr>
<tr><td>Full metrology: confocal and profiler too</td><td>From revenue</td><td class="n">~8.8</td></tr>
<tr><td>Visitor corridor and accessible entrance</td><td>Built later</td><td class="n">~3.6</td></tr>
<tr><td>Cassette and CD-R duplication</td><td>Added later</td><td class="n">~4.0</td></tr>
<tr><td>5 t overhead crane, bought</td><td>Rented</td><td class="n">~3.2</td></tr>
<tr><td>Compressor from a Thai supplier</td><td>Chinese route</td><td class="n">~0.25</td></tr>
<tr class="tot"><td>Together, at the midpoint</td><td></td><td class="n">~34.5</td></tr>
</table>
<p class="s" style="margin-top:16px"><b class="ac">No contingency in the price</b> (at 15&nbsp;%: ~30M THB). Not in it: the central hydraulic plant, archive shelving, CD replication, financing costs, currency moves, duty and import VAT (BOI assumed).</p>
</div>
</div>
<p class="src"><b>Source:</b> business plan B, sections 8.1 and 8.2; total-investment.md "Launch plant" and "Two plans". Block groups and their midpoints are arithmetic on the block ranges. Plan A: ~182M THB at the midpoint, ~132M to 233M low to high.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">Plan B is the launch plant with every block in: 201.6M THB at the midpoint plus the ramp-up reserve, about 217M, labelled 215M. Low to high it runs about 156M to 277M, with no contingency: at 15&nbsp;% that would be about 30M more. The table is what Plan B buys that Plan A leases or defers, about 34.5M at the midpoint. Why not Plan A? It reaches about 182M by leasing the array, renting the crane and deferring the rest. Expect two questions: the Classic and Pheenix line are unpriced by their vendors, and the building waits on a soil test. Sources: total-investment.md "Two plans"; business plan B, sections 8.1 to 8.4.</aside>
</section>
<!-- 9 -->
<section class="slide s8b grooves" data-title="Why Plan B" style="--gx:0%;--gy:100%;--gr:30%">
<div class="kicker"><i class="disc"></i>Why Plan B</div>
<h2>Ownership and control, not a higher modelled return</h2>
<p class="lede">Both plans build the same three presses and make the same records, so demand does not decide between them. The choice is ~35M THB more at launch for what Plan A leases or buys later, and fewer moving parts to finance once the plant runs.</p>
<div class="bd">
<div class="why">
<div><div class="n">~14.7M THB</div><div class="h">No power contract on the roof</div><div class="x">The ~520 kWp array and the 271 kWh battery are owned: no 15 to 25 year PPA and no ~1.9M THB a year PPA bill; the array pays back in ~4.1 to 5.8 years <span class="e">est.</span></div></div>
<div><div class="n">~8.8M THB</div><div class="h">Traceability from the first release</div><div class="x">The laser confocal and the wide-area profiler at launch, not from revenue: every stamper fully measured, the core of the pitch to labels.</div></div>
<div><div class="n">~3.6M THB</div><div class="h">Tours and Payap from day one</div><div class="x">A glazed corridor in process order for artists, labels, investors and schools, and the student programmes; cheaper to build in than to add.</div></div>
<div><div class="n">~4.0M THB</div><div class="h">Cassette and CD-R sales</div><div class="x">Both formats sold from launch next to vinyl. Their revenue is not in the operating model.</div></div>
<div><div class="n">~3.2M THB</div><div class="h">Its own crane</div><div class="x">A 5 t crane over the production compartment: machines moved in, out and around without hiring a mobile crane.</div></div>
<div><div class="n">~0.25M THB</div><div class="h">Local service</div><div class="x">The compressor from a Thai supplier, like the rest of the utilities, instead of the Chinese route.</div></div>
</div>
<div>
<h3>What the model says, Plan B against Plan A</h3>
<table class="tb cmp">
<tr><th>Midpoints, estimates</th><th class="n ac">Plan B</th><th class="n">Plan A</th></tr>
<tr><td>Plan price, reserve in</td><td class="n">~217M THB</td><td class="n">~182M THB</td></tr>
<tr><td>EBITDA, central, revenue model</td><td class="n">1.73M USD</td><td class="n">1.73M USD</td></tr>
<tr><td>EBITDA, calculator: no PPA bill in B</td><td class="n">2.23M USD</td><td class="n">2.17M USD</td></tr>
<tr><td>Depreciation, central</td><td class="n">0.49M USD</td><td class="n">0.40M USD</td></tr>
<tr><td>Operating profit, central / adverse</td><td class="n">1.24M / -0.55M</td><td class="n">1.33M / -0.41M</td></tr>
<tr><td>Payback on the plan price, central</td><td class="n">~3.8 yr</td><td class="n">~3.2 yr</td></tr>
<tr><td>Cash payback and IRR, calculator</td><td class="n">yr ~4.2, ~22&nbsp;%</td><td class="n">yr ~3.8, ~26&nbsp;%</td></tr>
</table>
<div class="card warn" style="margin-top:20px;padding:18px 22px"><p class="t" style="font-size:17.5px"><b>Read plainly:</b> Plan A has the better modelled return, Plan B the more complete plant. Most of B's items could be added to A later from revenue; the corridor is cheaper from the start, and every year the array is leased costs the PPA bill. Owning the array earns no BOI tax benefit: solar was dropped on 1 July 2025, only the battery may qualify.</p></div>
</div>
</div>
<p class="src"><b>Source:</b> total-investment.md "Two plans" ("What decides between them"); revenue-model.md "Operating pictures of Plan A and Plan B"; business plan B, sections 1, 8.2, 8.4 and 9.4; fleet-calculator.html, presets planA and planB; sustainability.md "Leasing instead (Plan A)" (PPA terms 15 to 25 years) and "PV, not solar thermal"; boi.md.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">Present this slide plainly; a lender will weigh it. Both plans make the same records, so demand does not decide between them. On the modelled numbers Plan B's extra 35M THB earns little: the same EBITDA in the revenue model, about 0.06M USD a year more in the calculator for the PPA bill the owned array avoids, more depreciation and a longer payback. Its case is ownership and control: no long power contract on the roof, full stamper metrology for the traceability pitch, tours and the Payap programmes from day one, cassette and CD-R sales, local service. Why not A? Its better modelled return leaves those items to finance or contract later. Sources: total-investment.md; business plan B, section 8.2.</aside>
</section>
<!-- 10 -->
<section class="slide s9" data-title="Operating picture">
<div class="kicker"><i class="disc"></i>Operating picture, Plan B, mature year</div>
<h2>Plan B's central case works; the adverse case loses money</h2>
<div class="bd">
<div>
<div class="legend" style="margin-bottom:10px"><span><i style="background:var(--rev)"></i>Revenue</span><span><i style="background:var(--acf)"></i>EBITDA</span><span><i style="background:var(--bf)"></i>Operating profit, after Plan B's depreciation</span><span>M USD a year</span></div>
<svg class="chart" id="chOp" width="900" height="560" viewBox="0 0 900 560" role="img" aria-label="Plan B revenue, EBITDA and operating profit by scenario"></svg>
</div>
<div>
<table class="tb op">
<tr><th>USD a year, Plan B</th><th class="n">Adverse</th><th class="n">Central</th><th class="n">Favourable</th></tr>
<tr><td class="k">Records</td><td class="n">0.58M</td><td class="n">0.74M</td><td class="n">0.90M</td></tr>
<tr><td class="k">Price per record</td><td class="n">3.76</td><td class="n">4.49</td><td class="n">5.27</td></tr>
<tr><td class="k">Direct cost per record</td><td class="n">2.70</td><td class="n">2.00</td><td class="n">1.35</td></tr>
<tr><td class="k">Revenue</td><td class="n">2.28M</td><td class="n">3.65M</td><td class="n">5.41M</td></tr>
<tr><td class="k">Revenue, THB</td><td class="n">~75M</td><td class="n">~120M</td><td class="n">~179M</td></tr>
<tr><td class="k">EBITDA</td><td class="n">0.23M</td><td class="n">1.73M</td><td class="n">3.79M</td></tr>
<tr><td class="k">Depreciation</td><td class="n">0.78M</td><td class="n">0.49M</td><td class="n">0.29M</td></tr>
<tr class="tot"><td class="k">Operating profit</td><td class="n neg">-0.55M</td><td class="n">1.24M</td><td class="n">3.50M</td></tr>
<tr><td class="k">Payback, plan price</td><td class="n">~37 yr</td><td class="n">~3.8 yr</td><td class="n">~1.2 yr</td></tr>
</table>
<div class="card warn" style="margin-top:20px;padding:18px 22px">
<p class="t" style="font-size:17.5px"><b>Read the adverse column as the honest one.</b> A central EBITDA margin of ~47&nbsp;% is far above the 15 to 25&nbsp;% pressing plants earn: the direct cost leaves out indirect labour, freight, scrap above the reject allowance, sales commissions and the extra cost of specials.</p>
</div>
<div class="kp4 op3">
<div><div class="v">~0.24M</div><div class="l">records a year: break-even on operating profit</div></div>
<div><div class="v">~1.1M</div><div class="l">records the adverse case needs to cover depreciation; the presses make 0.58M</div></div>
<div><div class="v">~2.8 USD</div><div class="l">(~92 THB) a record covers depreciation at central cost and volume</div></div>
</div>
</div>
</div>
<p class="src"><b>Source:</b> revenue-model.md "Operating pictures of Plan A and Plan B (October 2026)"; business plan B, section 9.4. Estimates, mature year at 60&nbsp;% specials; depreciation on Plan B's blocks; payback is EBITDA on the plan price including the reserve. Plan A for comparison: operating profit -0.41 / 1.33 / 3.54M USD.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">These are mature-year pictures for Plan B, not a forecast. Each column is internally consistent: adverse pairs low volume with the low price, the high cost, the high capital and the high overheads. The central case gives EBITDA of about USD 1.73M, roughly 57M THB, and operating profit of about 1.24M after Plan B's depreciation; it breaks even at about 0.24 million records. Lenders should read the adverse column: it loses about 0.55M USD, and the central margin of about 47&nbsp;% is too good to trust until indirect labour, freight, commissions and the cost of specials are in. Why not A? Same EBITDA, less depreciation: 1.33M central. Sources: revenue-model.md; business plan B, section 9.4.</aside>
</section>
<!-- 11 -->
<section class="slide s10" data-title="Cash and returns">
<div class="kicker"><i class="disc"></i>Cash and returns, Plan B</div>
<h2>Ten years of cash: Plan B back to zero in year ~4.2, IRR ~22&nbsp;%</h2>
<div class="bd">
<div>
<div class="legend" style="margin-bottom:10px"><span><i style="background:var(--acf)"></i>Plan B, cumulative cash</span><span><i style="background:transparent;height:2px;border-top:2px dashed var(--cmpleg);border-radius:0;vertical-align:4px"></i>Plan A, for comparison</span><span>M USD, year 0 to 10</span></div>
<svg class="chart" id="chCash" width="1000" height="640" viewBox="0 0 1000 640" role="img" aria-label="Cumulative cash over ten years, Plan B, with Plan A faint for comparison"></svg>
</div>
<div>
<table class="tb kp">
<tr><th>Calculator presets</th><th class="n ac">Plan B</th><th class="n">Plan A</th></tr>
<tr><td>Cash payback</td><td class="n">year ~4.2</td><td class="n">year ~3.8</td></tr>
<tr><td>IRR, ten years</td><td class="n">~22&nbsp;%</td><td class="n">~26&nbsp;%</td></tr>
<tr><td>NPV at 10&nbsp;%</td><td class="n">~4.7M USD</td><td class="n">~5.4M USD</td></tr>
<tr><td>Year 0: investment and reserve</td><td class="n">6.56M USD</td><td class="n">5.52M USD</td></tr>
<tr><td>Mature-year EBITDA</td><td class="n">2.23M USD</td><td class="n">2.17M USD</td></tr>
<tr><td>Simple payback: plan price before the reserve / mature EBITDA</td><td class="n">2.7 yr</td><td class="n">2.3 yr</td></tr>
</table>
<p class="t" style="margin-top:20px;font-size:17px"><b>Basis:</b> 850k records at 60&nbsp;% specials at the team's planning rate (3 records a minute per Duplex system), the calculator's own cost build-up with biomass fuel and the cutting engineer; Plan B pays no PPA power price. Ramp 35, 70 and 90&nbsp;% in years 1 to 3. Pre-tax, pre-financing, no BOI tax holiday, no terminal value: a sketch, not a forecast.</p>
<div class="card warn" style="margin-top:18px;padding:16px 22px"><p class="t" style="font-size:17px">In the revenue model's adverse case, EBITDA pays Plan B's price back in ~37 years. A year-by-year cash model for the ramp is a phase 0 output.</p></div>
</div>
</div>
<p class="src"><b>Source:</b> business plan B, section 9.4 and figure 12; fleet-calculator.html, presets planB and planA, cash view (values read from the page, October 2026); revenue-model.md. Estimates.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">This curve is the calculator's, not the operating table's: 850,000 records at the team's faster Duplex planning rate, its own cost build-up including biomass fuel, no PPA bill because Plan B owns the array, and a ramp of 35, 70 and 90&nbsp;% before the mature year. Year 0 is the midpoint investment plus a 15M THB reserve, 6.56M USD. On that basis Plan B is back to zero in year 4.2, with an IRR of about 22&nbsp;% and an NPV of about 4.7M USD at 10&nbsp;%. Pre-tax, pre-financing, no terminal value. Why not A? Its curve turns earlier on less capital. Put the adverse case beside it: about 37 years. Sources: fleet-calculator.html; business plan B, section 9.4.</aside>
</section>
<!-- 12 -->
<section class="slide s11" data-title="BOI and financing">
<div class="kicker"><i class="disc"></i>BOI promotion and financing</div>
<h2>Duty-free machinery under BOI, debt-to-equity no higher than 3:1</h2>
<div class="bd">
<div class="boi">
<div class="r"><div class="k">Category</div><div class="x">Basic plastic consumer-goods manufacturing (Category 6), incentive Group B: no tax holiday on the core activity. Sub-code to confirm with a BOI-registered consultant.</div><div class="st"><span class="tag no">To confirm</span></div></div>
<div class="r"><div class="k">Privileges</div><div class="x">Machinery import duty exemption, with import VAT; 100&nbsp;% foreign ownership; visas and work permits through the One Stop Service; raw-material duty relief for export production.</div><div class="st"><span class="tag ok">Group B core</span></div></div>
<div class="r"><div class="k">Upgrades</div><div class="x">Smart and Sustainable Industry (15/2565, refreshed July 2025): typically 3 years of CIT exemption, capped around 50&nbsp;% of the upgrade spend; R&amp;D merit at 1&nbsp;% of revenue adds 1 to 3 years.</div><div class="st"><span class="tag md">To structure</span></div></div>
<div class="r"><div class="k">Solar</div><div class="x">No longer promoted since 1 July 2025 (Announcement 3/2568): Plan B's owned array earns the bill saving, no tax benefit. The 271 kWh battery may still qualify, capped at 12M THB per MW.</div><div class="st"><span class="tag hi">Solar dropped</span></div></div>
<div class="r"><div class="k">Conditions</div><div class="x">Debt-to-equity no higher than 3:1 for the life of the privileges; minimum 1M THB excluding land and working capital; modern machinery; progress and annual reports; machinery imported within ~30 months, full operation by ~36 months.</div><div class="st"><span class="tag md">Binding</span></div></div>
<div class="r"><div class="k">Process</div><div class="x">e-Investment filing and analyst meeting; ~40 working days to a decision under 200M THB, 60 to 90 above; 4 to 6 months in practice. No machinery order before the certificate.</div><div class="st"><span class="tag no">Phase 0</span></div></div>
<div class="r"><div class="k">Without BOI</div><div class="x">~5&nbsp;% duty on ~2.3M USD of imported machinery (~0.11M USD) and ~0.17M USD of import VAT up front <span class="e">est.</span></div><div class="st"><span class="tag no">Avoided</span></div></div>
</div>
<div class="fin">
<h3>Funding need, Plan B: reserve in, no contingency</h3>
<div class="need">
<div class="card warn"><div class="v ac">~217M THB</div><div class="l">~6.56M USD at the midpoint, labelled ~215M; ~156M to 277M low to high</div></div>
<div class="card"><div class="v">~54M THB</div><div class="l">equity at least: a quarter of the funding at the 3:1 ceiling (arithmetic)</div></div>
</div>
<div class="share">
<h3>Shareholding</h3>
<div class="bar"><i class="a"></i><i class="c"></i></div>
<div class="lbl">
<div><b class="ac">At least 60&nbsp;%</b><span>the lead investor: at least ~32M THB</span></div>
<div><b>At most 40&nbsp;%</b><span>co-investors (Ploy Printing as intended strategic co-investor, the founders, others): at most ~22M THB</span></div>
</div>
<p class="nt">At the ~54M THB equity floor and one share price, arithmetic only: share price, valuation and in-kind shares not agreed. Terms in the shareholders' agreement.</p>
</div>
<dl class="dl" style="margin-top:16px;grid-template-columns:130px 1fr;gap:12px 18px">
<dt>Collateral</dt><dd style="font-size:17.5px">The machinery and equipment, in Plan B including the owned PV array and battery. The building only if the lease grants its ownership and the right to mortgage it. No land collateral: the site is a long lease of raw land.</dd>
<dt>Use of funds</dt><dd style="font-size:17.5px">Equipment (presses, metal path, metrology, lab) ~85M THB; building, crane, workshop and corridor ~40M; utilities ~35M; solar and battery ~15M; software, cassette and CD-R, freight, working capital and phase 0 ~28M (block midpoints <span class="e">est.</span>), plus the reserve, in tranches (slide 16).</dd>
</dl>
<p class="tbd">Financing structure to be agreed: no loan terms, interest rate or debt-service figures are on file.</p>
</div>
</div>
<p class="src"><b>Source:</b> boi.md; business plan B, sections 1 ("The ask"), 6.1, 7.1, 8.1, 8.4 and 12.2; total-investment.md "Launch plant" and "Two plans" (shareholding, 20 October 2026). Equity floor, the 60/40 amounts and use-of-funds groups are arithmetic on the plan's figures, before financing costs.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">Vinyl pressing lands in BOI Group B: duty-free machinery, full foreign ownership and work permits, no tax holiday on the core activity. Solar is no longer promoted; Plan B owns its array for the bill. The condition that shapes financing is debt-to-equity no higher than 3:1: at least a quarter of Plan B's 217M, about 54M THB, is equity. The lead investor holds at least 60&nbsp;% of the company, so at most 40&nbsp;% is open to co-investors; at that floor and one share price, about 32M and 22M THB, arithmetic only. No loan terms are on file. Collateral: machinery, array, battery; the land is leased. Sources: boi.md; business plan B, sections 7.1 and 12.2.</aside>
</section>
<!-- 13 -->
<section class="slide s12" data-title="Schedule">
<div class="kicker"><i class="disc"></i>Schedule</div>
<h2>First paying release: Q4 2028 to mid-2029, and still open</h2>
<div class="bd">
<svg class="chart" id="chGantt" width="1712" height="448" viewBox="0 0 1712 448" role="img" aria-label="Calendar from phase 0 to first paying release"></svg>
<div class="cols">
<div>
<h3>Commissioning sequence</h3>
<ol class="seq">
<li>Cutting room and galvanics, with the Sibert line: the presses need their stampers.</li>
<li>Newbilt Duplex system, with boilers B-1 and B-2 and chillers CH-1 and CH-2.</li>
<li>Newbilt Classic, once the metal path and the press process are stable.</li>
</ol>
<p class="s" style="margin-top:10px">Alongside: cassette and CD-R lines. In the build: owned PV and battery, 5 t crane, visitor corridor.</p>
</div>
<div>
<h3>Long-lead orders, after the certificate</h3>
<p class="t">Pheenix galvanics line, Sillitoe lathe, Sibert line, Newbilt Duplex system and Classic, two biomass boiler modules, the first two chiller modules. Press build 9 to 12 months, assumed; the other build times are not quoted.</p>
</div>
<div>
<h3>What moves the date</h3>
<p class="t">The BOI filing date and the vendor build times: a 15 to 18 month press build moves launch to mid-2029. Ordering with a deposit before the certificate, if BOI allows it, could bring launch 6 to 9 months earlier.</p>
</div>
</div>
</div>
<p class="src"><b>Source:</b> project-timeline.md, calendar anchored August 2026 (nothing in phase 0 has started, so it shifts day for day; not yet redrawn for the launch plant); business plan B, sections 10, 11 and 11.1; roadmap.md phases 0 to 4.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">This is the August 2026 calendar, the only one on file, drawn for a one-press launch: commissioning the cutting room and galvanics first is not yet in it, and only the press build time is assumed. The date is open: Q4 2028 at best, mid-2029 if the press build takes 15 to 18 months. Phase 0 has not started, so it slides day for day. The critical path runs through the BOI certificate and the vendor builds; construction has three to five months of float. Plan B's array, battery, crane and corridor go in during the build; PEA's interconnection review, 3 to 6 months, is filed with procurement. Sources: project-timeline.md; business plan B, sections 10 and 11.</aside>
</section>
<!-- 14 -->
<section class="slide s13" data-title="Risks and mitigations">
<div class="kicker"><i class="disc"></i>Risks and mitigations</div>
<h2>The risks we carry, and what retires each one</h2>
<div class="bd" style="margin-top:28px">
<table class="tb rk">
<tr><th>Severity</th><th>Risk</th><th>Mitigation and status</th></tr>
<tr><td><span class="tag hi">High</span></td><td>Pricing power is unproven: market validation (roadmap item 1) is open, with no buyer prices yet</td><td>Validation gate with regional labels; the platform's calculator as an interest list; no large spending before it clears</td></tr>
<tr><td><span class="tag hi">High</span></td><td>Utilisation and price: the adverse case loses money; the extra cost of specials is not yet in the direct cost</td><td>Treat the adverse column as the honest one; specials gain an upper bound until compound quotes; second Duplex waits for demand</td></tr>
<tr><td><span class="tag hi">High</span></td><td>No contingency in Plan B's price: at 15&nbsp;% ~30M THB; the high end of every block ~277M with the reserve</td><td>Quotes before commitment; release in tranches; how overruns are funded agreed up front</td></tr>
<tr><td><span class="tag hi">High</span></td><td>Galvanics and cutting on the critical path, galvanics the hardest skill to hire. Sibert advised getting presses, boilers, operators and feedback loops right first</td><td>Advice carried as a risk: commission the metal path first and measure it; hire early, train at the vendors; Stamper Discs and SST as overflow contacts</td></tr>
<tr><td><span class="tag hi">High</span></td><td>Prices not on file: the Newbilt Classic (placeholder band) and the Pheenix line (OMS reference + 20&nbsp;%, no Pheenix quote)</td><td>Newbilt re-quote and Pheenix quote in phase 0; OMS's offer as the galvanics benchmark</td></tr>
<tr><td><span class="tag hi">High</span></td><td>Vendor build times unknown: the press build carries ~40&nbsp;% of the schedule</td><td>Build time and order-book position required in every re-quote</td></tr>
<tr><td><span class="tag hi">High</span></td><td>Zoning unverified: the Payap point failed, Mueang Chi is unchecked</td><td>Verify on the land-use map and with counsel before the BOI filing; industrial estate as fallback</td></tr>
<tr><td><span class="tag md">Medium</span></td><td>Plan B's ~35M THB more than Plan A earns little in the model: payback ~3.8 against ~3.2 years, a deeper adverse case</td><td>Phase 0 prices each item against its lever (PPA offer beside the purchase quote); the corridor is cheaper from the start</td></tr>
<tr><td><span class="tag md">Medium</span></td><td>Lease terms and building ownership: a weak lease makes the building hard to finance</td><td>Registered 30 to 50 year lease with renewal, building ownership and assignment rights; Thai counsel in phase 0</td></tr>
<tr><td><span class="tag md">Medium</span></td><td>Galvanics effluent and biomass emissions in the first factory licence</td><td>Zero-discharge design and treatment building from the start; dust collector and 14 m stack drawn; fuel contracts</td></tr>
<tr><td><span class="tag md">Medium</span></td><td>Currency: EUR on Newbilt and Pheenix, USD on Sibert and Sillitoe, costs in THB</td><td>Currency and validity in every quote; THB pricing regionally; hedging decided with the investor</td></tr>
</table>
</div>
<p class="src"><b>Source:</b> business plan B, sections 11.2 and 13 (severities as the plan states them); revenue-model.md "Honest caveats"; roadmap.md item 1; site-candidates.md.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">The severities are the business plan's own. The top three are commercial: pricing power is unproven, the adverse case loses money, and Plan B's price carries no contingency, about 30M THB at 15&nbsp;%. The next group is execution: galvanics and cutting now sit on the critical path, the Classic and the Pheenix line have no vendor price, and no build time is quoted. Sibert advised getting presses, boilers and operators right first; we carry that advice as a risk. Plan B adds its own: higher launch capital than Plan A and a longer payback. Most of these retire in phase 0, before any capital is committed. Source: business plan B, section 13.</aside>
</section>
<!-- 15 -->
<section class="slide s14" data-title="Team and vendors">
<div class="kicker"><i class="disc"></i>Team, partners and vendors</div>
<h2>A small founding team, specialist vendors and a local packaging partner</h2>
<div class="bd">
<div class="ppl">
<h3>Team</h3>
<div><div class="nm">Simona Meesaiyati</div><div class="ro">Project Lead</div><div class="x">Management and the audio side.</div></div>
<div><div class="nm">Alexander Grabowski</div><div class="ro">Technical Consultant</div><div class="x">The technical side: equipment and vendors, utilities, the building model, the traceability and QC specification, the factory software.</div></div>
<div><div class="nm">buildfor.life</div><div class="ro">Consulting</div><div class="x">Supports the project in a consulting capacity.</div></div>
<div><div class="x"><b style="color:var(--tx)">To hire at launch:</b> a galvanics process engineer and a cutting engineer (critical path), a plant manager, two shifts of operators, QC, maintenance, sales. About 20 people at the start <span class="e">est.</span>, plus 1 to 2 for the cassette and CD-R lines.</div></div>
</div>
<div class="ppl">
<h3>Partners</h3>
<div><div class="nm">Ploy Printing</div><div class="ro">Chiang Mai · packaging</div><div class="x">Jackets, sleeves and inserts; intends to co-invest within the 40&nbsp;% open beside the lead investor, terms not yet discussed. Supply priced at arm's length, Ploy abstaining on its own contract.</div></div>
<div><div class="nm">Payap University</div><div class="ro">Chiang Mai · under discussion</div><div class="x">Joint student programmes in mastering, lathe cutting, IT and business. Nothing agreed yet.</div></div>
<div><div class="nm">Advisers, phase 0</div><div class="ro">To engage</div><div class="x">A BOI-registered consultant; Thai counsel for the lease, the company and the shareholder agreement.</div></div>
</div>
<div>
<h3>Vendors</h3>
<table class="tb vd">
<tr><th>Vendor</th><th>Supplies</th><th>On file</th></tr>
<tr><td>Newbilt<br><span class="s">Germany</span></td><td>Duplex system and Classic presses</td><td>2024 quote EUR 200,500 for the Single 2.1, expired; Classic not priced</td></tr>
<tr><td>Pheenix Alpha<br><span class="s">Sweden</span></td><td>Galvanics: PASU, PPPU, PDPU, PMPB</td><td>No quote; priced at OMS's reference + 20&nbsp;%</td></tr>
<tr><td>Sibert<br><span class="s">UK</span></td><td>Stamper prep: VSF, VSP, VSC</td><td>USD 250k budget confirmed, October 2026</td></tr>
<tr><td>Sillitoe<br><span class="s">Australia</span></td><td>SDMS cutting system</td><td>List price USD 125k</td></tr>
<tr><td>Ploy Printing<br><span class="s">Chiang Mai</span></td><td>Print and packaging</td><td>Partner</td></tr>
</table>
<p class="s" style="margin-top:16px">Making Vinyl Europe, Haarlem, 30 September to 2 October 2026: strong interest in full traceability; Pheenix offered references and a factory visit, OMS sent galvanics reference prices, SST Frankfurt offered help. Supplier-side evidence.</p>
</div>
</div>
<p class="src"><b>Source:</b> README "Team"; business plan B, sections 1, 12.1 and 12.2; equipment/pressing-line.md; equipment/galvanics.md; suppliers/vendor-landscape.md.</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">The founding team is two people: Simona leads the project and the audio side, Alexander the technical side; buildfor.life supports in a consulting capacity. The plant needs most of its crew from the start, and two hires are on the critical path: a galvanics process engineer and a cutting engineer. The vendors are established specialists: Newbilt for all three presses, Pheenix Alpha for galvanics, Sibert for stamper preparation, Sillitoe for the cutting system. Ploy Printing intends to co-invest and will supply packaging, so that contract is priced at arm's length and Ploy abstains on it. Shareholder terms are open. Sources: README; business plan B, section 12.</aside>
</section>
<!-- 16 -->
<section class="slide s15 grooves" data-title="The ask">
<div class="kicker"><i class="disc"></i>The ask and next steps</div>
<h2>Approve phase 0 now; commit to Plan B in tranches once the gates clear</h2>
<div class="bd">
<div>
<div class="ask" style="display:grid;grid-template-columns:1fr 1fr;gap:44px;align-items:start">
<div><h3>Now: phase 0</h3><div class="v">1.3M to 2.6M THB</div><p class="s" style="margin-top:8px">~USD 40k to 80k over 3 to 6 months, part of the plan price: the BOI consultant, Thai counsel for the lease, zoning and the shareholders' agreement, a soil test, the quotes.</p></div>
<div><h3>On offer to co-investors</h3><div class="v">Up to 40&nbsp;%</div><p class="s" style="margin-top:8px">of the company, beside the lead investor's at least 60&nbsp;%: Ploy Printing, the founders, others. Share price and valuation not agreed; terms in the shareholders' agreement.</p></div>
</div>
<h3 style="margin-top:24px">Then Plan B at ~215M THB in tranches: share of the investment before the reserve <span class="e">est.</span></h3>
<table class="tb tr">
<tr><th>#</th><th>Trigger</th><th>Covers</th><th class="n">Share</th><th class="n">M THB</th></tr>
<tr><td>0</td><td>Now</td><td>Phase 0 work</td><td class="n">1 to 2&nbsp;%</td><td class="n">1.3 to 2.6</td></tr>
<tr><td>1</td><td>BOI certificate, lease, zoning, soil, pricing validated</td><td>Deposits on every long-lead order</td><td class="n">20 to 30&nbsp;%</td><td class="n">~40 to 60</td></tr>
<tr><td>2</td><td>Building permit; build dates in writing</td><td>Construction, utility yard, utilities, balance payments</td><td class="n">35 to 45&nbsp;%</td><td class="n">~71 to 91</td></tr>
<tr><td>3</td><td>Building watertight; first machinery shipped</td><td>Fit-out, ZLD, owned PV and battery, crane, corridor, full metrology, software, cassette and CD-R</td><td class="n">15 to 20&nbsp;%</td><td class="n">~30 to 40</td></tr>
<tr><td>4</td><td>Metal path commissioned; first stampers</td><td>Working capital; the reserve from here on</td><td class="n">8 to 12&nbsp;%</td><td class="n">~16 to 24</td></tr>
</table>
</div>
<div>
<h3>The quotes that firm the numbers</h3>
<ul class="chk">
<li>Newbilt re-quote: Duplex with the EX 35, the Classic, build time</li>
<li>Pheenix galvanics quote, against OMS's reference</li>
<li>Sillitoe quote and build time; Sibert's itemised quotation</li>
<li>Building RFQ to two or three Thai contractors, with the soil test</li>
<li>Metrology quotes; solar and battery purchase quotes, a PPA offer beside them</li>
<li>Biomass boilers from Getabec and Chinese makers; fuel offers</li>
</ul>
<h3 style="margin-top:26px">Decisions for the investor</h3>
<ul class="list sm">
<li>The equity and debt mix within 3:1 (equity at least ~54M THB)</li>
<li>The shareholders' agreement: the split of the 40&nbsp;%, board, reserved matters and tranche releases</li>
<li>How quotes landing above the plan price are funded</li>
<li>Each Plan B item weighed against its lever; CD replication before the BOI filing; hedging of the EUR orders</li>
</ul>
</div>
<div class="unl">
<div class="u"><div class="h">Phase 0 unlocks</div><div class="x">The BOI filing, the lease heads of terms and the zoning answer, and quotes in place of the estimated blocks: the next version of the plan.</div></div>
<div class="u"><div class="h">The round unlocks</div><div class="x">Every long-lead order and the build, commissioned in sequence: metal path, Duplex, Classic, with the owned PV, crane and corridor in place. First paying release Q4 2028 to mid-2029, open.</div></div>
<div class="u"><div class="h">Revenue then funds</div><div class="x">The second Duplex system (~15.4M THB at the midpoint <span class="e">est.</span>), PV on the PV-ready office roof and canopies (~120 kWp, ~2.4M to 3.1M THB <span class="e">est.</span>), the phase 2 building.</div></div>
</div>
</div>
<p class="src"><b>Source:</b> business plan B, sections 8.3, 12.2, 14.1 and 14.2; total-investment.md "Two plans" (shareholding); roadmap.md phases 0 to 2. Tranche amounts are the shares applied to Plan B's ~201.6M THB before the reserve (arithmetic). Gates before any order: market validation, the BOI certificate, and land (zoning, lease terms, soil).</p>
<footer class="ft"><span>CNX Vinyl · Investment proposal, Plan B (~215M THB) · Draft for discussion, October 2026</span><span class="pg"></span></footer>
<aside class="notes">Now: approve phase 0, about 1.3M to 2.6M THB over three to six months, for the BOI consultant, counsel for the lease and the shareholders' agreement, the soil test and the quotes. Then, once validation, the BOI certificate and the land gates clear, a commitment in principle to Plan B at about 215M THB, released in tranches. On offer to co-investors: up to 40&nbsp;% of the company, as the lead investor holds at least 60&nbsp;%; share price and valuation are not agreed. Decisions: the equity and debt mix within 3:1, the shareholders' agreement, funding quotes that land high. Why not A? It stays on file as the lowest-capital variant. Sources: business plan B, sections 8.3, 12.2 and 14.</aside>
</section>
</div>
<nav class="ctl" id="ctl" aria-label="Slide controls">
<button type="button" id="bPrev" title="Previous slide (Left)" aria-label="Previous slide"><svg viewBox="0 0 24 24"><path d="M15 5l-7 7 7 7"/></svg></button>
<span class="cnt" id="cnt">1 / 16</span>
<button type="button" id="bNext" title="Next slide (Right)" aria-label="Next slide"><svg viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg></button>
<span class="sep"></span>
<button type="button" id="bOv" title="Overview (O)" aria-label="Overview"><svg viewBox="0 0 24 24"><rect x="4" y="4" width="6.5" height="6.5" rx="1"/><rect x="13.5" y="4" width="6.5" height="6.5" rx="1"/><rect x="4" y="13.5" width="6.5" height="6.5" rx="1"/><rect x="13.5" y="13.5" width="6.5" height="6.5" rx="1"/></svg></button>
<button type="button" id="bPv" title="Presenter view (P)" aria-label="Presenter view"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="12" rx="1.5"/><path d="M8 20h8M12 16v4"/></svg></button>
<button type="button" id="bFs" title="Fullscreen (F)" aria-label="Fullscreen"><svg viewBox="0 0 24 24"><path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/></svg></button>
<button type="button" id="bTh" title="Light / dark (T)" aria-label="Light or dark theme"><svg class="ic-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2.6v2.3M12 19.1v2.3M2.6 12h2.3M19.1 12h2.3M5.4 5.4l1.6 1.6M17 17l1.6 1.6M5.4 18.6L7 17M17 7l1.6-1.6"/></svg><svg class="ic-moon" viewBox="0 0 24 24"><path d="M19.5 14.6A7.8 7.8 0 0 1 9.4 4.5a7.8 7.8 0 1 0 10.1 10.1z"/></svg></button>
<a class="pdf" id="bPdf" href="CNX-Vinyl-Investor-Deck.pdf" download title="Download the PDF, dark theme">PDF</a>
</nav>
<script>
"use strict";
/* ======================================================================
DATA: every charted value with its source key (see SRC).
Units: M THB, M USD, M THB a year, MWh a year; dates ISO.
====================================================================== */
const SRC = {
bp33: "Business plan B, section 3.3; revenue-model.md 'First market data point' and 'Record Industry's calculator'",
bp91: "Business plan B, section 9.1 (blended price USD 3.76 to 5.27 at 60 % specials)",
ti: "total-investment.md 'Launch plant' (block ranges) and 'Two plans'",
bpB81: "Business plan B, section 8.1, all blocks",
rm: "revenue-model.md 'Operating pictures of Plan A and Plan B (October 2026)'; business plan B, section 9.4",
calcB: "fleet-calculator.html preset planB, cash view (business plan B, figure 12)",
calcA: "fleet-calculator.html preset planA, cash view (comparison only)",
bio: "biomass.md; business plan B, sections 1 and 8.4",
pv: "sustainability.md 'What ~520 kWp means on the grid'; business plan B, section 1",
tl: "project-timeline.md, Gantt (August 2026 calendar)",
bp10: "Business plan B, section 10"
};
const DATA = {
fx: { usdThb: 33, src: "ti" },
price: [
{ k: "eu", label: "European plant, ex works (Record Industry, Haarlem)", lo: 80.2, hi: 80.2, txt: "USD 2.43, ~80 THB", src: "bp33" },
{ k: "cnx", label: "CNX assumed blended price, 60\u00a0% specials", lo: 124, hi: 174, txt: "124 to 174 THB", src: "bp91" },
{ k: "th", label: "Thai market, per unit incl. a printed jacket (ResurRec)", lo: 535, hi: 780, txt: "535 to 780 THB", src: "bp33" }
],
/* Plan B block groups, [low, high] in M THB; the nine groups sum to 146.15 to 257.1 (midpoint 201.6) */
invest: {
blocks: [
{ k: "press", label: "Presses: Newbilt Duplex and Classic", b: [21.2, 27.4], src: "ti" },
{ k: "metal", label: "Metal path: cutting, galvanics, ZLD, prep", b: [30.5, 46.0], src: "ti" },
{ k: "metro", label: "Metrology, full stack, and PVC lab", b: [13.4, 31.7], src: "bpB81" },
{ k: "util", label: "Utilities, HVAC, utility yard", b: [24.45, 45.2], src: "bpB81" },
{ k: "bldg", label: "Building, crane, workshop, corridor", b: [27.8, 51.2], src: "bpB81" },
{ k: "solar", label: "Solar PV and battery, owned", b: [12.4, 17.0], src: "bpB81" },
{ k: "soft", label: "Software, ERPNext, platform", b: [3.8, 10.2], src: "ti" },
{ k: "cass", label: "Cassette and CD-R duplication", b: [1.8, 6.3], src: "bpB81" },
{ k: "other", label: "Phase 0, freight and installation, working capital", b: [10.8, 22.1], src: "ti" },
{ k: "res", label: "Ramp-up reserve", b: [10, 20], src: "ti" }
],
total: { sub: [146, 201.6, 257], price: [156, 217, 277], src: "bpB81" },
planA: { price: 182, lo: 132, hi: 233, src: "ti" }
},
op: {
cases: ["Adverse", "Central", "Favourable"],
revenue: [2.28, 3.65, 5.41], ebitda: [0.23, 1.73, 3.79], op: [-0.55, 1.24, 3.50], src: "rm",
opA: [-0.41, 1.33, 3.54] /* Plan A, comparison only (source line) */
},
cash: {
years: [0,1,2,3,4,5,6,7,8,9,10],
b: { cum: [-6.56,-6.08,-4.66,-2.70,-0.47,1.76,3.99,6.22,8.46,10.69,12.92], pay: 4.2, src: "calcB" },
a: { cum: [-5.52,-5.06,-3.68,-1.77,0.41,2.58,4.75,6.93,9.10,11.27,13.45], pay: 3.8, src: "calcA" }
},
steam: { biomass: 0.67, electric: 4.9, src: "bio" },
solar: { used: 633, plant: 1036, share: 61, src: "pv" },
gantt: { from: "2026-08-01", to: "2029-07-31", src: "tl", rows: [
{ g: "Phase 0", label: "Market validation", s: "2026-08-01", e: "2026-11-29" },
{ g: "Phase 0", label: "BOI consultant and application", s: "2026-08-01", e: "2026-12-14" },
{ g: "Phase 0", label: "Newbilt re-quote, with build time", s: "2026-08-01", e: "2026-10-30" },
{ g: "BOI", label: "BOI review to certificate", s: "2026-12-15", e: "2027-05-14", crit: 1, ms: [["2026-12-15", "filed"], ["2027-05-15", "certificate"]] },
{ g: "BOI", label: "Company incorporation", s: "2027-05-15", e: "2027-06-14", crit: 1 },
{ g: "Site", label: "Zoning, soil test, lease", s: "2026-09-01", e: "2027-02-28" },
{ g: "Site", label: "Design and building permit", s: "2027-03-01", e: "2027-06-29" },
{ g: "Site", label: "Construction", s: "2027-07-01", e: "2028-02-26" },
{ g: "Site", label: "Factory licence (Ror.Ngor. 4)", s: "2028-01-01", e: "2028-05-30" },
{ g: "Site", label: "Utilities, PV, battery, fit-out", s: "2028-03-01", e: "2028-06-29" },
{ g: "Machines", label: "Press build at vendor (assumed)", s: "2027-06-15", e: "2028-04-10", crit: 1 },
{ g: "Machines", label: "Sea freight", s: "2028-04-10", e: "2028-05-30", crit: 1 },
{ g: "Machines", label: "Install, commission, train", s: "2028-07-01", e: "2028-09-14", crit: 1 },
{ g: "Launch", label: "Hire and train the launch team", s: "2028-05-01", e: "2028-08-29" },
{ g: "Launch", label: "Test pressings, QC baseline", s: "2028-09-15", e: "2028-10-30", crit: 1 },
{ g: "Launch", label: "First paying release", s: "2028-11-15", e: "2029-06-30", open: 1 }
] }
};
/* ======================================================================
Charts: inline SVG built from DATA
====================================================================== */
const NS = "http://www.w3.org/2000/svg";
/* fill and stroke go through inline style so they can take var(): the charts follow the theme
without a redraw (SVG presentation attributes cannot take var()) */
const STY = { fill: 1, stroke: 1 };
function el(p, tag, a, txt) {
const e = document.createElementNS(NS, tag);
for (const k in a) { if (STY[k]) e.style.setProperty(k, a[k]); else e.setAttribute(k, a[k]); }
if (txt != null) e.textContent = txt;
p.appendChild(e); return e;
}
const f1 = x => x.toFixed(1);
const f2 = x => (x < 0 ? "-" : "") + Math.abs(x).toFixed(2);
const fr = x => (Math.round(x * 100) % 10 ? x.toFixed(2) : x.toFixed(1));
const mid = r => (r[0] + r[1]) / 2;
/* every chart colour is a CSS variable (see :root and html[data-theme=light]); ac/b/neg for text, acf/bf/negf for fills */
const C = {};
["ac", "acf", "acs", "b", "bf", "neg", "negf", "tx", "tx2", "mu", "dim", "bg", "line", "line2", "rev", "elec", "track", "cmpline", "par"].forEach(k => { C[k] = "var(--" + k + ")"; });
function chPrice() {
const s = document.getElementById("chPrice"); if (!s) return;
const x0 = 10, x1 = 590, max = 1000, X = v => x0 + v / max * (x1 - x0);
for (const t of [0, 250, 500, 750, 1000]) {
el(s, "line", { x1: X(t), x2: X(t), y1: 20, y2: 292, class: "gr" });
el(s, "text", { x: X(t), y: 318, "text-anchor": t === 0 ? "start" : t === 1000 ? "end" : "middle", "font-size": 15 }, t === 1000 ? "1,000 THB" : t.toLocaleString("en-US"));
}
DATA.price.forEach((r, i) => {
const y = 36 + i * 92, col = r.k === "cnx" ? C.acf : r.k === "th" ? C.negf : C.bf;
el(s, "text", { x: x0, y: y, fill: C.tx2, "font-size": 17 }, r.label);
if (r.hi - r.lo < 1) {
el(s, "circle", { cx: X(r.lo), cy: y + 30, r: 9, fill: col });
} else {
el(s, "rect", { x: X(r.lo), y: y + 19, width: Math.max(4, X(r.hi) - X(r.lo)), height: 22, rx: 4, fill: col });
}
const right = X(r.hi) + 14 + 150 > x1;
el(s, "text", { x: right ? X(r.lo) - 14 : X(r.hi) + 14, y: y + 36, "text-anchor": right ? "end" : "start", fill: C.tx, "font-size": 18, "font-weight": 600 }, r.txt);
});
}
/* Plan B by block group: midpoint bar, low-to-high whisker, the range in the right column, totals below */
function chInvest() {
const s = document.getElementById("chInvest"); if (!s) return;
const D = DATA.invest, B = D.blocks, L = 372, R = 790, W = 1000, max = 55, X = v => L + v / max * (R - L), rh = 46, top = 30;
const bot = top + rh * B.length;
el(s, "text", { x: W - 2, y: 16, "text-anchor": "end", "font-size": 12.5, "font-weight": 600, "letter-spacing": "1.6", fill: C.mu }, "LOW TO HIGH");
el(s, "text", { x: X(0), y: 16, "font-size": 12.5, "font-weight": 600, "letter-spacing": "1.6", fill: C.mu }, "MIDPOINT");
for (const t of [0, 10, 20, 30, 40, 50]) {
el(s, "line", { x1: X(t), x2: X(t), y1: top, y2: bot, class: "gr" });
el(s, "text", { x: X(t), y: bot + 22, "text-anchor": "middle", "font-size": 14 }, t + "M");
}
B.forEach((b, i) => {
const y = top + i * rh, res = b.k === "res", r = b.b, m = mid(r), yy = y + 14;
el(s, "text", { x: 0, y: y + 29, fill: res ? C.mu : C.tx2, "font-size": 16.5 }, b.label);
el(s, "rect", { x: X(0), y: yy, width: X(m) - X(0), height: 19, rx: 3, fill: C.acf, "fill-opacity": res ? 0.3 : 0.92, stroke: res ? C.acf : "none", "stroke-dasharray": res ? "4 3" : "" });
const yc = yy + 9.5, wk = { stroke: C.tx, "stroke-opacity": .6, "stroke-width": 1.5 };
el(s, "line", Object.assign({ x1: X(r[0]), x2: X(r[1]), y1: yc, y2: yc }, wk));
el(s, "line", Object.assign({ x1: X(r[0]), x2: X(r[0]), y1: yc - 6, y2: yc + 6 }, wk));
el(s, "line", Object.assign({ x1: X(r[1]), x2: X(r[1]), y1: yc - 6, y2: yc + 6 }, wk));
el(s, "text", { x: Math.max(X(r[1]), X(m)) + 10, y: yc + 5.5, fill: res ? C.mu : C.ac, "font-size": 16, "font-weight": 700 }, f1(m));
el(s, "text", { x: W - 2, y: yc + 5.5, "text-anchor": "end", fill: C.mu, "font-size": 15 }, res ? r[0] + " to " + r[1] : f1(r[0] + 1e-9) + " to " + f1(r[1] + 1e-9));
if (i < B.length - 1) el(s, "line", { x1: 0, x2: W, y1: y + rh, y2: y + rh, stroke: C.line2 });
});
const T = D.total, y1 = bot + 66, y2 = y1 + 40;
el(s, "line", { x1: 0, x2: W, y1: bot + 38, y2: bot + 38, stroke: C.line });
el(s, "text", { x: 0, y: y1, fill: C.tx2, "font-size": 16.5 }, "Launch plant, every block");
el(s, "text", { x: X(0), y: y1, fill: C.tx, "font-size": 17, "font-weight": 600 }, f1(T.sub[1]) + "M THB");
el(s, "text", { x: W - 2, y: y1, "text-anchor": "end", fill: C.mu, "font-size": 15 }, "~" + T.sub[0] + " to " + T.sub[2]);
el(s, "text", { x: 0, y: y2, fill: C.ac, "font-size": 19, "font-weight": 700 }, "Plan B, reserve in");
el(s, "text", { x: X(0), y: y2, fill: C.ac, "font-size": 19, "font-weight": 700 }, "~" + T.price[1] + "M THB");
el(s, "text", { x: W - 2, y: y2, "text-anchor": "end", fill: C.tx2, "font-size": 16, "font-weight": 600 }, "~" + T.price[0] + " to " + T.price[2]);
}
function chOp() {
const s = document.getElementById("chOp"); if (!s) return;
const D = DATA.op, L = 64, R = 890, T = 20, B = 500, lo = -1, hi = 6, Y = v => T + (hi - v) / (hi - lo) * (B - T);
for (let t = lo; t <= hi; t++) {
el(s, "line", { x1: L, x2: R, y1: Y(t), y2: Y(t), class: t === 0 ? "z" : "gr" });
el(s, "text", { x: L - 12, y: Y(t) + 5, "text-anchor": "end", "font-size": 14 }, t + "M");
}
const gw = (R - L) / 3, bw = 62, gap = 16;
const ser = [["revenue", C.rev, C.tx2], ["ebitda", C.acf, C.ac], ["op", C.bf, C.b]];
D.cases.forEach((c, i) => {
const gx = L + gw * i + (gw - (bw * ser.length + gap * (ser.length - 1))) / 2;
ser.forEach(([k, fill, tcol], j) => {
const v = D[k][i], x = gx + j * (bw + gap), y0 = Y(0), y1 = Y(v);
el(s, "rect", { x: x, y: Math.min(y0, y1), width: bw, height: Math.max(1, Math.abs(y1 - y0)), rx: 3, fill: v < 0 ? C.negf : fill });
el(s, "text", { x: x + bw / 2, y: v < 0 ? y1 + 20 : y1 - 8, "text-anchor": "middle", fill: v < 0 ? C.neg : tcol, "font-size": 15, "font-weight": 600 }, f2(v));
});
el(s, "text", { x: L + gw * i + gw / 2, y: B + 40, "text-anchor": "middle", fill: C.tx, "font-size": 18, "font-weight": 600 }, c);
if (i) el(s, "line", { x1: L + gw * i, x2: L + gw * i, y1: T, y2: B + 10, stroke: C.line2 });
});
}
/* Plan B leads; Plan A is drawn faint, dashed, for comparison */
function chCash() {
const s = document.getElementById("chCash"); if (!s) return;
const D = DATA.cash, L = 70, R = 900, T = 20, B = 580, lo = -8, hi = 14;
const X = y => L + y / 10 * (R - L), Y = v => T + (hi - v) / (hi - lo) * (B - T);
for (let t = lo; t <= hi; t += 2) {
el(s, "line", { x1: L, x2: R, y1: Y(t), y2: Y(t), class: t === 0 ? "z" : "gr" });
el(s, "text", { x: L - 12, y: Y(t) + 5, "text-anchor": "end", "font-size": 14 }, t + "M");
}
D.years.forEach(y => el(s, "text", { x: X(y), y: B + 28, "text-anchor": "middle", "font-size": 14 }, y === 0 ? "Y0" : String(y)));
const a = D.a.cum, b = D.b.cum;
// Plan A, faint
el(s, "polyline", { points: a.map((v, i) => X(i).toFixed(1) + "," + Y(v).toFixed(1)).join(" "), fill: "none", stroke: C.cmpline, "stroke-width": 2, "stroke-dasharray": "7 6", "stroke-linejoin": "round" });
el(s, "text", { x: X(10) + 12, y: Y(a[10]) - 4, fill: C.mu, "font-size": 14 }, "A " + f2(a[10]));
// Plan B payback marker
const px = X(D.b.pay);
el(s, "line", { x1: px, x2: px, y1: Y(0), y2: Y(-4.6), stroke: C.acf, "stroke-dasharray": "3 4", "stroke-width": 1.5 });
el(s, "text", { x: px + 8, y: Y(-5.4), fill: C.ac, "font-size": 15.5, "font-weight": 700 }, "back to zero, year ~" + D.b.pay);
// Plan B
el(s, "polyline", { points: b.map((v, i) => X(i).toFixed(1) + "," + Y(v).toFixed(1)).join(" "), fill: "none", stroke: C.acf, "stroke-width": 4, "stroke-linejoin": "round" });
b.forEach((v, i) => el(s, "circle", { cx: X(i), cy: Y(v), r: 5.5, fill: C.bg, stroke: C.acf, "stroke-width": 2.8 }));
el(s, "text", { x: X(0) + 12, y: Y(b[0]) + 26, fill: C.ac, "font-size": 16, "font-weight": 700 }, f2(b[0]));
el(s, "text", { x: X(10) + 12, y: Y(b[10]) + 18, fill: C.ac, "font-size": 16, "font-weight": 700 }, f2(b[10]));
}
function chSteam() {
const s = document.getElementById("chSteam"); if (!s) return;
const D = DATA.steam, L = 0, R = 228, X = v => L + v / 5 * (R - L);
[["Biomass fuel", D.biomass, C.acf], ["Electric steam", D.electric, C.elec]].forEach(([lab, v, col], i) => {
const y = 6 + i * 62;
el(s, "text", { x: 0, y: y + 14, fill: C.tx2, "font-size": 15 }, lab);
el(s, "rect", { x: X(0), y: y + 22, width: X(v) - X(0), height: 20, rx: 3, fill: col });
el(s, "text", { x: X(v) + 10, y: y + 38, fill: i ? C.tx2 : C.ac, "font-size": 15, "font-weight": 600 }, "~" + v.toFixed(2).replace(/0$/, "") + "M THB/yr");
});
}
function chSolar() {
const s = document.getElementById("chSolar"); if (!s) return;
const D = DATA.solar, W = 370;
el(s, "rect", { x: 0, y: 24, width: W - 2, height: 18, rx: 3, fill: C.track });
el(s, "rect", { x: 0, y: 24, width: (W - 2) * D.used / D.plant, height: 18, rx: 3, fill: C.acf });
el(s, "text", { x: 0, y: 16, fill: C.tx2, "font-size": 14 }, "Share of the plant's electricity");
el(s, "text", { x: 0, y: 64, fill: C.ac, "font-size": 15, "font-weight": 600 }, "~" + D.share + "\u00a0%");
el(s, "text", { x: W - 2, y: 64, "text-anchor": "end", fill: C.mu, "font-size": 14 }, "~" + D.used + " of ~" + D.plant.toLocaleString("en-US") + " MWh");
}
function chGantt() {
const s = document.getElementById("chGantt"); if (!s) return;
const D = DATA.gantt, L = 390, R = 1700, T = 30, rh = 24;
const d = x => new Date(x + "T00:00:00Z").getTime(), t0 = d(D.from), t1 = d(D.to);
const X = x => L + (d(x) - t0) / (t1 - t0) * (R - L);
const n = D.rows.length, Bt = T + n * rh;
// month grid and year labels
for (let y = 2026; y <= 2029; y++) for (let m = 0; m < 12; m++) {
const iso = y + "-" + String(m + 1).padStart(2, "0") + "-01";
if (d(iso) < t0 || d(iso) > t1) continue;
const x = X(iso);
el(s, "line", { x1: x, x2: x, y1: T - 6, y2: Bt, stroke: m === 0 ? C.line : C.line2 });
if (m % 3 === 0) el(s, "text", { x: x + 4, y: T - 12, "font-size": 13, fill: m === 0 ? C.tx2 : C.dim }, (m === 0 ? y + " " : "") + ["Jan", "Apr", "Jul", "Oct"][m / 3]);
}
let g = "";
D.rows.forEach((r, i) => {
const y = T + i * rh;
if (r.g !== g) { g = r.g; if (i) el(s, "line", { x1: 0, x2: R, y1: y, y2: y, stroke: C.line }); el(s, "text", { x: 0, y: y + 17, fill: C.ac, "font-size": 12, "font-weight": 700, "letter-spacing": "1.5" }, g.toUpperCase()); }
el(s, "text", { x: 112, y: y + 17, fill: r.crit ? C.tx : C.tx2, "font-size": 15 }, r.label);
if (r.open) {
const x = X(r.s);
el(s, "rect", { x: x, y: y + 6, width: X(r.e) - x, height: 13, rx: 3, fill: C.acs, stroke: C.acf, "stroke-dasharray": "4 3" });
el(s, "path", { d: `M${x} ${y + 3} l7 9.5 -7 9.5 -7 -9.5z`, fill: C.acf });
el(s, "text", { x: x - 14, y: y + 17, "text-anchor": "end", fill: C.ac, "font-size": 14.5, "font-weight": 600 }, "Q4 2028 to mid-2029, open");
return;
}
el(s, "rect", { x: X(r.s), y: y + 6, width: Math.max(4, X(r.e) - X(r.s)), height: 13, rx: 3, fill: r.crit ? C.acf : C.par });
(r.ms || []).forEach(([dt, lab]) => {
const x = X(dt);
el(s, "path", { d: `M${x} ${y + 3} l6 9.5 -6 9.5 -6 -9.5z`, fill: C.tx, stroke: C.bg, "stroke-width": 1.5 });
});
});
// milestone labels for the BOI row, placed beside the bar
const boi = D.rows.findIndex(r => r.ms), yb = T + boi * rh;
el(s, "text", { x: X("2027-05-15") + 12, y: yb + 17, fill: C.tx, "font-size": 13.5 }, "certificate ~May 2027");
// metal-path note at the test-pressing row
const tp = D.rows.findIndex(r => r.label.startsWith("Test")), yt = T + tp * rh;
el(s, "text", { x: X("2028-09-15") - 12, y: yt + 17, "text-anchor": "end", fill: C.neg, "font-size": 13.5 }, "metal-path commissioning comes first; not yet in this calendar");
el(s, "line", { x1: 0, x2: R, y1: Bt, y2: Bt, stroke: C.line });
// legend
const ly = Bt + 26;
el(s, "rect", { x: 112, y: ly - 11, width: 26, height: 12, rx: 3, fill: C.acf });
el(s, "text", { x: 146, y: ly, "font-size": 14 }, "critical path");
el(s, "rect", { x: 260, y: ly - 11, width: 26, height: 12, rx: 3, fill: C.par });
el(s, "text", { x: 294, y: ly, "font-size": 14 }, "parallel track with float");
el(s, "text", { x: R, y: ly, "text-anchor": "end", "font-size": 14, fill: C.dim }, "August 2026 calendar, optimistic end drawn; every duration an estimate");
}
/* ======================================================================
Deck engine: scaling, navigation, hash, overview, presenter, sync
====================================================================== */
const stage = document.getElementById("stage");
const slides = Array.from(stage.querySelectorAll(":scope > .slide"));
const N = slides.length;
const params = new URLSearchParams(location.search);
const PRESENTER = params.has("presenter");
let cur = 0, pvWin = null, buf = "", bufT = 0;
const ME = Math.random().toString(36).slice(2);
let lastMsg = "";
slides.forEach((s, i) => { const pg = s.querySelector(".pg"); if (pg) pg.textContent = String(i + 1).padStart(2, "0") + " / " + String(N).padStart(2, "0"); });
function fit() {
const W = window.innerWidth, H = window.innerHeight, k = Math.min(W / 1920, H / 1080);
stage.style.transform = `translate(${(W - 1920 * k) / 2}px, ${(H - 1080 * k) / 2}px) scale(${k})`;
}
function fromHash() {
const m = /^#(\d+)/.exec(location.hash);
return m ? Math.min(N, Math.max(1, +m[1])) - 1 : 0;
}
function go(n, opt) {
opt = opt || {};
n = Math.min(N - 1, Math.max(0, n | 0));
const changed = n !== cur;
cur = n;
slides.forEach((s, i) => { s.classList.toggle("active", i === n); s.classList.toggle("before", i < n); s.setAttribute("aria-hidden", i === n ? "false" : "true"); });
const c = document.getElementById("cnt"); if (c) c.textContent = (n + 1) + " / " + N;
const h = "#" + (n + 1);
if (location.hash !== h) history.replaceState(null, "", location.pathname + location.search + h);
if (PRESENTER) renderPresenter();
if (ovOpen) markOverview();
if (!opt.remote && (changed || opt.force)) send(n);
}
const next = () => go(cur + 1), prev = () => go(cur - 1);
/* --- sync between the audience window and the presenter window: slide and theme --- */
const KEY = "cnx-pitch-sync";
let bc = null;
try { bc = new BroadcastChannel(KEY); bc.onmessage = e => recv(e.data); } catch (e) { bc = null; }
function post(m) {
m.from = ME; m.t = Date.now() + Math.random();
lastMsg = m.from + ":" + m.t;
try { if (bc) bc.postMessage(m); } catch (e) {}
try { localStorage.setItem(KEY, JSON.stringify(m)); } catch (e) {}
try { if (window.opener && !window.opener.closed) window.opener.postMessage({ cnxPitch: m }, "*"); } catch (e) {}
try { if (pvWin && !pvWin.closed) pvWin.postMessage({ cnxPitch: m }, "*"); } catch (e) {}
}
const send = n => post({ n: n });
function recv(m) {
if (!m || m.from === ME) return;
const id = m.from + ":" + m.t; if (id === lastMsg) return; lastMsg = id;
if (m.th) { setTheme(m.th, { remote: true }); return; }
if (typeof m.n === "number" && m.n !== cur) go(m.n, { remote: true });
}
window.addEventListener("storage", e => {
if (e.key === KEY && e.newValue) { try { recv(JSON.parse(e.newValue)); } catch (err) {} }
if (e.key === "cnx-theme" && (e.newValue === "light" || e.newValue === "dark")) setTheme(e.newValue, { remote: true });
});
window.addEventListener("message", e => { if (e.data && e.data.cnxPitch) recv(e.data.cnxPitch); });
/* --- light / dark: localStorage cnx-theme, else prefers-color-scheme (followed live until the user picks) --- */
const de = document.documentElement;
const PDFS = { dark: ["CNX-Vinyl-Investor-Deck.pdf", "Download the PDF, dark theme"],
light: ["CNX-Vinyl-Investor-Deck-light.pdf", "Download the PDF, light theme (print-friendly)"] };
const theme = () => de.getAttribute("data-theme") === "light" ? "light" : "dark";
function setTheme(th, opt) {
opt = opt || {};
if (th !== "light" && th !== "dark") return;
if (opt.save) { try { localStorage.setItem("cnx-theme", th); } catch (e) {} }
const changed = th !== theme();
de.setAttribute("data-theme", th);
themeUi();
if (changed && !opt.remote) post({ th: th });
}
function themeUi() {
const th = theme(), a = document.getElementById("bPdf"), b = document.getElementById("bTh"), pb = document.getElementById("pvTheme");
if (a) { a.setAttribute("href", PDFS[th][0]); a.title = PDFS[th][1]; }
if (b) b.setAttribute("aria-pressed", th === "light" ? "true" : "false");
if (pb) pb.textContent = th === "light" ? "Dark (T)" : "Light (T)";
}
const toggleTheme = () => setTheme(theme() === "light" ? "dark" : "light", { save: true });
const mq = window.matchMedia && matchMedia("(prefers-color-scheme: light)");
if (mq && mq.addEventListener) mq.addEventListener("change", e => {
let s = null; try { s = localStorage.getItem("cnx-theme"); } catch (_) {}
if (s !== "light" && s !== "dark") setTheme(e.matches ? "light" : "dark", { remote: true });
});
function openPresenter() {
const url = location.href.split("#")[0].split("?")[0] + "?presenter&th=" + theme() + "#" + (cur + 1);
pvWin = window.open(url, "cnxPitchPresenter", "width=1280,height=800");
}
function fullscreen() {
const d = document;
if (!d.fullscreenElement) { const r = d.documentElement.requestFullscreen; if (r) r.call(d.documentElement).catch(() => {}); }
else if (d.exitFullscreen) d.exitFullscreen();
}
/* --- overview --- */
let ov = null, ovOpen = false;
function cloneSlide(i) {
const c = slides[i].cloneNode(true);
c.classList.add("active"); c.classList.remove("before");
c.removeAttribute("aria-hidden");
c.querySelectorAll(".notes").forEach(x => x.remove());
c.querySelectorAll("[id]").forEach(x => x.removeAttribute("id"));
const m = document.createElement("div"); m.className = "mini"; m.appendChild(c);
return m;
}
function buildOverview() {
ov = document.createElement("div"); ov.className = "ov"; ov.id = "ov"; ov.hidden = true;
ov.innerHTML = '<h4>All slides · click to open · O or Esc to close · T light / dark</h4><div class="grid"></div>';
const g = ov.querySelector(".grid");
slides.forEach((s, i) => {
const b = document.createElement("button"); b.type = "button"; b.className = "th"; b.dataset.i = i;
b.setAttribute("aria-label", "Slide " + (i + 1) + ": " + (s.dataset.title || ""));
b.appendChild(cloneSlide(i));
const lab = document.createElement("span"); lab.className = "lab"; lab.textContent = (i + 1) + " · " + (s.dataset.title || ""); b.appendChild(lab);
b.addEventListener("click", () => { toggleOverview(false); go(i); });
g.appendChild(b);
});
document.body.appendChild(ov);
}
function scaleMinis(root) {
root.querySelectorAll(".mini").forEach(m => { const w = m.parentElement.clientWidth; if (w) m.style.transform = "scale(" + (w / 1920) + ")"; });
}
function markOverview() { if (ov) ov.querySelectorAll(".th").forEach(b => b.classList.toggle("cur", +b.dataset.i === cur)); }
function toggleOverview(on) {
if (!ov) buildOverview();
ovOpen = on == null ? !ovOpen : on;
ov.hidden = !ovOpen;
if (ovOpen) { scaleMinis(ov); markOverview(); const c = ov.querySelector(".th.cur"); if (c) c.focus({ preventScroll: true }); }
}
/* --- presenter view (same file, ?presenter) --- */
let pv = null, t0 = Date.now(), paused = 0, pauseAt = 0;
function buildPresenter() {
document.body.classList.add("pvmode");
document.title = "Presenter · CNX Vinyl: investment proposal";
pv = document.createElement("div"); pv.className = "pv"; pv.id = "pv";
pv.innerHTML =
'<header><span class="ttl">CNX Vinyl · presenter view</span><span class="cn" id="pvCnt"></span><span id="pvTitle"></span>' +
'<span class="tm" id="pvTime">00:00:00</span><button type="button" id="pvPause">Pause</button><button type="button" id="pvReset">Reset timer</button><button type="button" id="pvTheme" title="Light / dark for both windows (T)">Light (T)</button></header>' +
'<div class="body"><div class="col"><div class="lb">Current slide</div><div class="frame" id="pvCur"></div>' +
'<div class="nav"><button type="button" id="pvPrev">Previous</button><button type="button" id="pvNext">Next</button></div></div>' +
'<div class="col"><div class="lb">Next</div><div class="frame next" id="pvNext2"></div><div class="lb">Speaker notes</div><div class="nt" id="pvNotes"></div></div></div>';
document.body.appendChild(pv);
document.getElementById("pvReset").onclick = () => { t0 = Date.now(); paused = 0; if (pauseAt) pauseAt = Date.now(); tick(); };
document.getElementById("pvPause").onclick = e => {
if (pauseAt) { paused += Date.now() - pauseAt; pauseAt = 0; e.target.textContent = "Pause"; }
else { pauseAt = Date.now(); e.target.textContent = "Resume"; }
};
document.getElementById("pvPrev").onclick = prev;
document.getElementById("pvNext").onclick = next;
document.getElementById("pvTheme").onclick = toggleTheme;
setInterval(tick, 500); tick();
}
function tick() {
const el2 = document.getElementById("pvTime"); if (!el2) return;
const ms = (pauseAt || Date.now()) - t0 - paused, s = Math.max(0, Math.floor(ms / 1000));
el2.textContent = [Math.floor(s / 3600), Math.floor(s / 60) % 60, s % 60].map(x => String(x).padStart(2, "0")).join(":");
}
function renderPresenter() {
if (!pv) return;
const a = document.getElementById("pvCur"), b = document.getElementById("pvNext2");
a.innerHTML = ""; a.appendChild(cloneSlide(cur));
b.innerHTML = "";
if (cur + 1 < N) b.appendChild(cloneSlide(cur + 1)); else b.innerHTML = '<div class="end">End of the deck</div>';
const nt = slides[cur].querySelector(".notes");
document.getElementById("pvNotes").textContent = nt ? nt.textContent.trim() : "";
document.getElementById("pvCnt").textContent = (cur + 1) + " / " + N;
document.getElementById("pvTitle").textContent = slides[cur].dataset.title || "";
scaleMinis(pv);
}
/* --- input --- */
function onKey(e) {
if (e.altKey || e.ctrlKey || e.metaKey) return;
const k = e.key;
if (/^[0-9]$/.test(k)) { buf += k; bufT = Date.now(); showJump(); return; }
if (k === "Enter" && buf) { const n = parseInt(buf, 10); buf = ""; showJump(); if (n >= 1) go(n - 1); e.preventDefault(); return; }
if (k === "Backspace" && buf) { buf = buf.slice(0, -1); showJump(); return; }
if (k === "Escape") { if (ovOpen) toggleOverview(false); buf = ""; showJump(); return; }
if (ovOpen && !/^[oOtT]$/.test(k)) return;
switch (k) {
case "ArrowRight": case "ArrowDown": case "PageDown": next(); break;
case " ": e.shiftKey ? prev() : next(); break;
case "ArrowLeft": case "ArrowUp": case "PageUp": prev(); break;
case "Home": go(0); break;
case "End": go(N - 1); break;
case "f": case "F": if (!PRESENTER) fullscreen(); break;
case "o": case "O": if (!PRESENTER) toggleOverview(); break;
case "p": case "P": if (!PRESENTER) openPresenter(); break;
case "t": case "T": toggleTheme(); break;
default: return;
}
e.preventDefault();
}
let jumpEl = null;
function showJump() {
if (!jumpEl) { jumpEl = document.createElement("div"); jumpEl.className = "jump"; document.body.appendChild(jumpEl); }
jumpEl.hidden = !buf; jumpEl.textContent = "Go to slide " + buf + " ↵";
}
setInterval(() => { if (buf && Date.now() - bufT > 4000) { buf = ""; showJump(); } }, 1000);
let idle = 0;
function wake() { document.body.classList.add("ui"); clearTimeout(idle); idle = setTimeout(() => document.body.classList.remove("ui"), 2600); }
function init() {
chPrice(); chInvest(); chOp(); chCash(); chSteam(); chSolar(); chGantt();
fit();
window.addEventListener("resize", () => { fit(); if (ovOpen) scaleMinis(ov); if (pv) scaleMinis(pv); });
document.addEventListener("keydown", onKey);
window.addEventListener("hashchange", () => { const n = fromHash(); if (n !== cur) go(n); });
document.getElementById("bPrev").onclick = prev;
document.getElementById("bNext").onclick = next;
document.getElementById("bOv").onclick = () => toggleOverview();
document.getElementById("bPv").onclick = openPresenter;
document.getElementById("bFs").onclick = fullscreen;
document.getElementById("bTh").onclick = toggleTheme;
document.addEventListener("mousemove", wake);
// touch: swipe left or right
let sx = 0, sy = 0, st = 0;
stage.addEventListener("touchstart", e => { const t = e.changedTouches[0]; sx = t.clientX; sy = t.clientY; st = Date.now(); wake(); }, { passive: true });
stage.addEventListener("touchend", e => {
const t = e.changedTouches[0], dx = t.clientX - sx, dy = t.clientY - sy;
if (Math.abs(dx) > 40 && Math.abs(dx) > Math.abs(dy) * 1.3 && Date.now() - st < 800) { dx < 0 ? next() : prev(); }
}, { passive: true });
if (PRESENTER) buildPresenter();
themeUi();
cur = -1; go(fromHash(), { remote: true });
if (!PRESENTER) wake();
}
init();
</script>
</body>
</html>