Business plan draft 0.3 in two versions: Plan A ~180M and Plan B ~225M THB
Deploy test site / deploy (push) Successful in 52s

- One build makes both documents: plan_variant.py switches the
  plan-specific text (pick / is_a / label); build_plan.py writes
  CNX-Vinyl-Business-Plan-A and -B (docx), export_pdf.py exports both PDFs
  with the table of contents filled in. Draft 0.2 (single launch-plant
  plan with contingency) is removed; it stays in the history.
- Plan A: the launch plant with every machine and six cost levers, ~177M
  THB with the ramp-up reserve, no contingency. Plan B: the second Duplex
  at launch, five presses, ~224M THB on the same basis. Each version reads
  as a complete plan with one table comparing it with the other; the
  216M investor-question text is gone, Pheenix is priced at OMS's
  reference plus 20 %.
- Calculator renders in both documents, each from its own preset: the
  plant at a glance (Figure 4), the budget check (Figure 5) and ten years
  of cash (Figure 6), in docs/renders/calculator/.
- Calculator budget card: the verdict and third KPI now name the tightest
  budget the midpoint fits (Plan A: 3.3M to spare under 180M; Plan B:
  0.8M under 225M) instead of always 225M.
- Fixes: CD replication cross-references point to section 4.10; the
  blended price in section 3.3 and roadmap item 1 is 3.76 to 5.27 USD at
  60 % specials (3.50 to 4.60 at a standard-heavy mix).
This commit is contained in:
grabowski committed 2026-10-17 15:42:35 +07:00
1 parent f2ff1eac1f
commit bd357ab03f
19 files changed
+1618 -685

No files matched your search

Binary file not shown.
Binary file not shown.
+22 -9
View File
@@ -1,9 +1,12 @@
"""Build the CNX Vinyl business plan .docx.
"""Build the CNX Vinyl business plan .docx, in its two versions (October 2026).
python build_plan.py -> ../CNX-Vinyl-Business-Plan.docx
python build_plan.py out.docx -> custom path
python build_plan.py -> ../CNX-Vinyl-Business-Plan-A.docx and -B.docx
python build_plan.py A -> ../CNX-Vinyl-Business-Plan-A.docx
python build_plan.py B out.docx -> Plan B to a custom path
Images are resolved relative to docs/ (the parent of business-plan/).
Plan A (~180M THB) and Plan B (~225M THB) share every chapter; the content modules
switch the plan-specific text with plan_variant.pick(). Images are resolved relative
to docs/ (the parent of business-plan/).
"""
from __future__ import annotations
@@ -14,14 +17,21 @@ HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE))
from plan_lib import Renderer # noqa: E402
import plan_variant # noqa: E402
import content_a, content_b, content_c # noqa: E402
DOCS = HERE.parent.parent # .../docs
OUT = HERE.parent / "CNX-Vinyl-Business-Plan.docx"
VERSION = "draft 0.3, October 2026"
def main(out: Path = OUT) -> Path:
r = Renderer(DOCS, header_text="CNX Vinyl / Poy Records: Business plan, draft 0.2, October 2026",
def out_path(plan: str) -> Path:
return HERE.parent / f"CNX-Vinyl-Business-Plan-{plan}.docx"
def main(plan: str = "A", out: Path | None = None) -> Path:
plan_variant.PLAN = plan
out = out or out_path(plan)
r = Renderer(DOCS, header_text=f"CNX Vinyl / Poy Records: Business plan, {plan_variant.label()}, {VERSION}",
footer_text="Confidential")
r.render(content_a.blocks() + content_b.blocks() + content_c.blocks())
r.save(out)
@@ -29,5 +39,8 @@ def main(out: Path = OUT) -> Path:
if __name__ == "__main__":
target = Path(sys.argv[1]) if len(sys.argv) > 1 else OUT
print(main(target))
args = sys.argv[1:]
plans = [args.pop(0).upper()] if args and args[0].upper() in ("A", "B") else ["A", "B"]
target = Path(args[0]) if args else None
for p in plans:
print(main(p, target if len(plans) == 1 else None))
+350 -140
View File
@@ -1,9 +1,15 @@
"""Part A: cover, reading guide, executive summary, opportunity, anatomy of the plant."""
"""Part A: cover, reading guide, executive summary, opportunity, anatomy of the plant.
Builds Plan A (~180M THB) or Plan B (~225M THB): plan-specific text is switched with
plan_variant.pick() inside blocks(), never at import time.
"""
from plan_lib import H1, H2, H3, P, B, N, T, KV, IMG, CALL, PB, TOC, COVER
from plan_variant import pick, is_a, is_b, label, name
def blocks():
out = []
LP = pick("Launch plant", "At launch") # 'When' column of the 4.0 checklist
# ------------------------------------------------------------ cover
out.append(COVER(
@@ -17,12 +23,19 @@ def blocks():
"**Prepared by:** Simona Meesaiyati (Project Lead) and Alexander Grabowski (Technical Consultant), "
"supported by buildfor.life, with Ploy Printing (Chiang Mai) as packaging partner and "
"strategic co-investor",
"**Version:** Draft 0.2, October 2026",
f"**Version:** Draft 0.3, October 2026: **{name()}, {pick('~180M THB', '~225M THB')}**",
],
note="Confidential. All figures in USD and THB at 1 USD = 33 THB (August 2026). Vendor quotes are "
"snapshots at their file dates, not commitments. Every number in this plan is labelled as quoted, "
"researched, or estimated; see section 2. Draft 0.2 replaces draft 0.1 (September 2026): the plant "
"now launches with the whole chain, cutting and galvanics included, in one capital round.",
"researched, or estimated; see section 2. " + pick(
"This version is Plan A: the launch plant with the whole chain, cutting and galvanics "
"included, three presses, brought to ~180M THB (~177M) with six cost levers. A second "
"version, Plan B (~225M THB), launches with five presses and takes none of the levers. ",
"This version is Plan B: the whole chain, cutting and galvanics included, with five presses "
"at launch and everything in, at ~225M THB (~224M). A second version, Plan A (~180M THB), "
"launches with three presses and six cost levers. ")
+ "The plan price includes the ramp-up reserve and carries no contingency. Draft 0.3 replaces "
"draft 0.2 (October 2026), which described a single launch plant priced with a contingency.",
))
out.append(TOC())
@@ -41,40 +54,79 @@ def blocks():
"could go wrong. It is built from the project's planning repository and vendor documents, and it "
"keeps the honest parts: where a number is an estimate, it says so; where the evidence is thin, it "
"says that too."),
P("**What changed since draft 0.1.** In October 2026 the plan moved from a phased entry (one press at "
"launch, stampers bought in from the UK, cutting bought in from Europe) to a **launch plant** that "
"has the whole chain from the first day, bought in one capital round and commissioned in sequence. "
"There are no bridge stampers and no outsourced cutting in the plan any more. Every section below "
"is written for that plant."),
P("**What changed since the earlier drafts.** In October 2026 the plan moved from a phased entry (one "
"press at launch, stampers bought in from the UK, cutting bought in from Europe) to a **launch plant** "
"that has the whole chain from the first day, bought in one capital round and commissioned in "
"sequence. There are no bridge stampers and no outsourced cutting in the plan any more. Draft 0.3 "
"prices that plant in **two versions**, both including the ramp-up reserve and neither carrying a "
"contingency: **Plan A (~180M THB)**, the launch plant's three presses with six cost levers, and "
"**Plan B (~225M THB)**, five presses with everything in. " + pick(
"**This document is Plan A**, and every section below is written for it; a short table after "
"the numbers compares the two versions.",
"**This document is Plan B**, and every section below is written for it; a short table after "
"the numbers compares the two versions.")),
H2("What we will build"),
B([
"**The complete vinyl chain under one roof:** mastering, lacquer cutting, galvanics (growing the "
"nickel stampers), stamper preparation, pressing, finishing, packaging with our Chiang Mai print "
"partner Ploy Printing, and fulfilment including direct-to-fan dropshipping.",
"**Three presses at launch:** one Newbilt Duplex system (two semi-automatic presses with a main "
"and a colour extruder, for colour, splatter and short runs) and one Newbilt Classic fully "
"automatic press for long standard runs. Special prints are the focus (60 % and more of the "
"records), and specials run only on the Duplex, so together they make about 0.58 to 0.90 million "
"records a year on two shifts (0.71 to 0.97 million at a standard-heavy mix; estimates). A second "
"Duplex system is a growth option with its floor, utility "
"positions and pipe connections reserved, not part of the investment.",
pick(
"**Three presses at launch:** one Newbilt Duplex system (two semi-automatic presses with a main "
"and a colour extruder, for colour, splatter and short runs) and one Newbilt Classic fully "
"automatic press for long standard runs. Special prints are the focus (60 % and more of the "
"records), and specials run only on the Duplex, so together they make about 0.58 to 0.90 "
"million records a year on two shifts (0.71 to 0.97 million at a standard-heavy mix; "
"estimates). A second Duplex system is a growth step from revenue, with its floor, utility "
"positions and pipe connections reserved, not part of the investment.",
"**Five presses at launch:** two Newbilt Duplex systems (each two semi-automatic presses with a "
"main and a colour extruder, for colour, splatter and short runs) and one Newbilt Classic fully "
"automatic press for long standard runs. Special prints are the focus (60 % and more of the "
"records) and run only on the Duplex systems; with four semi-automatic presses they no longer "
"set the pace, so the five presses make about 1.0 to 1.5 million records a year on two shifts "
"(1.25 million in the central case; estimates). The press hall keeps room for more Duplex "
"systems and Classics as an option, not part of the investment."),
"**The metal path in-house:** a Sillitoe SDMS cutting room, a Pheenix Alpha galvanics line with "
"zero-discharge water treatment, and the Sibert stamper-prep line. The father and the mother of "
"every side are archived, so represses start from our own metal.",
"**A modular utility plant:** a boiler hall and a chiller compound built once for four modules "
"each, with two 500 kg/h boiler modules and two 250 kW chiller modules installed at launch; "
"further presses add modules, not rooms. The steam fuel (electric or biomass) is still open.",
"**Cassette duplication and CD-R duplication in-house** from launch. Full CD replication "
"each, with " + pick(
"two 500 kg/h boiler modules (B-1, B-2, bought through the Chinese route) and two 250 kW "
"chiller modules (CH-1, CH-2)",
"two 500 kg/h boiler modules (B-1, B-2) and three 250 kW chiller modules (CH-1 to CH-3)")
+ " installed at launch; further presses add modules, not rooms. The steam fuel (electric or "
"biomass) is still open.",
pick("**Cassette duplication and CD-R duplication as a growth step** after launch, not in the "
"plan price; both fit inside the building programme. ",
"**Cassette duplication and CD-R duplication in-house** from launch. ")
+ "Full CD replication "
"(injection-moulded discs) is a separate, much larger decision, outside the totals; both CD "
"routes are set out in section 4.10 for the investor to decide.",
"**A purpose-built ~1,500 m² plant on long-term leased land**, with a detached utility yard, "
"rooftop solar and room for a second building.",
+ pick("rooftop solar through a solar power purchase agreement (PPA)",
"owned rooftop solar with a battery")
+ " and room for a second building.",
"**Commissioned in sequence:** the cutting room and galvanics first (the presses need their "
"stampers), then the Duplex system, then the Classic. That puts galvanics, the hardest skill to "
"stampers), then " + pick("the Duplex system, then the Classic",
"Duplex system 1, then the Classic, then Duplex system 2")
+ ". That puts galvanics, the hardest skill to "
"hire, on the path to the first paying release. Sibert, the stamper-prep supplier in the plan, "
"advised after the Haarlem trade show to get presses, boilers, operators and feedback loops "
"right first; the launch plant was decided anyway, and that advice is carried as a risk "
"(sections 11.2 and 13).",
"right first; " + pick("the launch plant", "the whole chain at launch")
+ " was decided anyway, and that advice is carried as a risk (sections 11.2 and 13).",
pick(
"**Six cost levers bring the plant to ~180M THB**, from ~194M to ~162M THB at the midpoint "
"before the ramp-up reserve (estimates, section 8): rooftop solar and battery through a solar "
"PPA (the installer owns and finances the array, the plant buys the power); metrology in two "
"steps (XRF, inline vision and the audio station at launch, the laser confocal and the "
"wide-area profiler bought from revenue); the visitor corridor built later, so no public tours "
"at first; cassette and CD-R duplication later; a mobile crane rented for the installations, "
"with frame, runway and machine door kept crane-ready; boilers and compressor through the "
"Chinese route. Every press, the cutting room, the galvanics line and the stamper-prep line "
"stay in.",
"**Everything in, no cost levers:** owned rooftop solar and battery (and with them the BOI "
"solar measure), the full metrology stack from launch, the visitor corridor and public tours "
"from the start, a bought overhead crane, cassette and CD-R duplication, and Thai-supplied "
"boilers and compressor."),
]),
H2("What makes it top-notch"),
P("Four things separate this plant from a pressing shop, and they are the reason it can charge "
@@ -92,8 +144,11 @@ def blocks():
"regrown from the father without a new cut. At Making Vinyl Europe in Haarlem this was what the "
"industry responded to most.",
"**Metrology and layered quality control.** The nickel parts are measured without contact "
"(laser confocal, wide-area profiler, XRF thickness) and logged per serial, which is ahead of "
"current industry practice. Every pressed record passes 100% inline optical inspection, every "
+ pick("and logged per serial, which is ahead of current industry practice: XRF thickness from "
"launch, the laser confocal and the wide-area profiler bought from revenue. ",
"(laser confocal, wide-area profiler, XRF thickness) and logged per serial, which is ahead "
"of current industry practice. ")
+ "Every pressed record passes 100% inline optical inspection, every "
"press cycle is logged, and sampled records are compared automatically against the source "
"master.",
"**Efficiency designed in.** Heat is recovered before energy is generated: in order, a flue-gas "
@@ -101,7 +156,11 @@ def blocks():
"heat pump that lifts the presses' ~60 °C cooling return into boiler feedwater. The main roof "
"carries **~247 kWp** of solar (about 335 to 360 MWh a year, a quarter to a third of mature "
"two-shift use; a second building's roof would take the total to ~498 kWp), with a 271 kWh "
"battery for ride-through. Cooling runs as a closed loop, no process water is discharged, "
"battery for ride-through" + pick(
", both through the solar PPA: the installer owns and finances them, the plant buys the "
"power. ",
", both owned by the plant. ")
+ "Cooling runs as a closed loop, no process water is discharged, "
"galvanics rinse water goes through a zero-discharge evaporator, and 20% of the feedstock is "
"in-house regrind. Boilers and chillers come in identical modules, so spares and service are one "
"routine. At reported northern fuel prices biomass heat costs a quarter to a tenth of electric "
@@ -113,41 +172,114 @@ def blocks():
]),
H2("The numbers"),
KV([
["Capacity", "Launch plant ~0.58M to 0.90M records/yr at 60 % specials (2 semi-automatic "
"presses + 1 Classic, 2 shifts; the Duplex sets the pace; estimate; the press fleet "
"calculator gives ~0.94M at our planning rates). Second Duplex as growth option: "
"~1.0M to 1.5M"],
["Capacity", pick(
"~0.58M to 0.90M records/yr at 60 % specials (2 semi-automatic presses + 1 Classic, 2 shifts; "
"the Duplex sets the pace; estimate; the press fleet calculator gives ~0.94M at our planning "
"rates). Second Duplex as a growth step from revenue: ~1.0M to 1.5M",
"~1.0M to 1.5M records/yr at 60 % specials, central case 1.25M (4 semi-automatic presses + 1 "
"Classic, 2 shifts; the four semi-automatic presses carry the specials, so the Duplex does not "
"set the pace; estimate; the press fleet calculator gives ~1.5M at our planning rates). Full "
"hall as a later option: ~2.9M to 4.4M")],
["Site", "~1,500 m² main building plus a ~379 m² utility yard built for four boiler and four "
"chiller modules, on leased land (~4 rai minimum, 6 to 8 rai with expansion); second "
"building as expansion"],
["Investment, launch plant", "~USD 4.9M to 8.9M (~THB 161M to 292M) including 15% contingency, "
"midpoint ~USD 6.9M (~THB 227M). Not in it: a ramp-up reserve of "
"~THB 10M to 20M, CD replication, financing costs, the central "
"hydraulic plant (not yet costed)"],
["The investor's 180M THB question", "**216M THB is a realistic target** for the full equipment "
"list with four levers that keep every machine (~203M to "
"213M with the ramp-up reserve); **180M holds only if the "
"open quotes land low**"],
[pick("Investment, Plan A", "Investment, Plan B"), pick(
"**~THB 177M (~USD 5.35M), rounded to ~180M THB**: the plan investment, ~THB 161.7M (~USD "
"4.90M) at the midpoint with the six levers taken, plus a ramp-up reserve of ~THB 10M to 20M "
"(staff and overheads before volume covers them). From the low to the high end of every "
"block: ~THB 127M to 226M. Estimates: no quote yet for the Classic, the galvanics line "
"(priced at OMS's reference plus 20 %) or the building",
"**~THB 224M (~USD 6.79M), rounded to ~225M THB**: the plan investment, ~THB 209.2M (~USD "
"6.34M) at the midpoint with everything in, plus a ramp-up reserve of ~THB 10M to 20M (staff "
"and overheads before volume covers them). From the low to the high end of every block: ~THB "
"162M to 286M. Estimates: no quote yet for the Classic, the galvanics line (priced at OMS's "
"reference plus 20 %), the EX 35 or the building")],
["Not in the plan price", "**No contingency** (at the 15 % used in the cost documents, ~THB "
+ pick("24M", "31M") + " at the midpoint): quotes that land high are "
"funded outside the plan or by scope steps (section 8). Also not in "
"it: the central hydraulic plant (not yet costed), the archive's mobile "
"shelving (to quote), CD replication, financing costs, currency moves, "
"and Thai duty and import VAT (the blocks assume BOI promotion)"],
["Land lease", "Not negotiated; the plan carries an estimated THB 0.3M to 1.0M per year "
"(USD 9k to 30k), section 6.1"],
["Operating result, mature year", "Central case at 60 % specials: revenue ~USD 3.65M (~THB 120M), "
"EBITDA ~USD 1.73M (~THB 57M), operating profit ~USD 1.21M (~THB "
"40M), payback ~3.8 years (2.8 to 5.1 on the 4.9M to 8.9M band); "
"break-even on operating profit at ~0.25M records a year. **The "
"adverse case loses money:** EBITDA ~USD 0.23M, operating loss "
"~USD 0.66M (~THB 22M). Utilisation and price decide the "
"outcome, not upside (section 9)"],
["Operating result, mature year", pick(
"Central case at 60 % specials: revenue ~USD 3.65M (~THB 120M), EBITDA ~USD 1.73M (~THB "
"57M), operating profit ~USD 1.34M (~THB 44M); break-even on operating profit at ~0.2M "
"records a year. **The adverse case loses money:** EBITDA ~USD 0.23M, operating loss ~USD "
"0.39M (~THB 13M); it would need ~0.9M records a year to cover depreciation and the three "
"presses make 0.58M at that rate. The solar PPA's power price (~USD 0.03M a year in the "
"calculator) is not in these figures. Utilisation and price decide the outcome, not upside "
"(section 9)",
"Central case at 60 % specials: revenue ~USD 6.05M (~THB 200M), EBITDA ~USD 3.14M (~THB "
"104M), operating profit ~USD 2.63M (~THB 87M); break-even on operating profit at ~0.22M "
"records a year. **The adverse case only just covers depreciation:** EBITDA ~USD 0.83M, "
"operating profit ~USD 0.02M. **All of it needs the records to sell:** ~1.25M a year in the "
"central case, ~0.75M of them specials, and no market evidence shows that demand yet. "
"Utilisation and price decide the outcome, not upside (section 9)")],
["Payback", pick(
"~3.1 years on the plan price in the central case (2.2 to 4.0 from the low to the high end of "
"the price); ~1.0 year in the favourable case, ~30 years in the adverse case (EBITDA, mature "
"year, ramp-up reserve included; estimates)",
"~2.2 years on the plan price in the central case (1.6 to 2.8 from the low to the high end of "
"the price); ~0.8 year in the favourable case, ~10.5 years in the adverse case (EBITDA, mature "
"year, ramp-up reserve included; estimates)")],
["First paying release", "**Open.** The August 2026 calendar gives Q4 2028 to mid-2029 (27 to 33 "
"months from the start of phase 0), but it was drawn for a one-press "
"launch. Commissioning the cutting room and galvanics before the presses "
"is not yet in it, and the lathe, galvanics line and stamper-prep build "
"times are not on file, so the date stays open until those vendors "
"quote. Ordering machinery before the BOI certificate, if allowed, could "
"quote." + pick("", " Plan B commissions its second Duplex system last, "
"after the Classic.")
+ " Ordering machinery before the BOI certificate, if allowed, could "
"still pull it forward by 6 to 9 months (section 10)"],
["People", "~15 to 25 across shifts at volume (estimate); the launch plant needs most of that "
"crew, because two shifts still need press, finishing, galvanics, QC and maintenance "
"staff (13 to 19 direct production staff in the revenue model)"],
["People", pick(
"~15 to 25 across shifts at volume (estimate); the launch plant needs most of that "
"crew, because two shifts still need press, finishing, galvanics, QC and maintenance "
"staff (13 to 19 direct production staff in the revenue model)",
"~15 to 25 across shifts at volume (estimate); two shifts need press, finishing, galvanics, QC "
"and maintenance staff whether the plant runs three presses or five, so Plan B's five presses "
"run on the same direct crews (13 to 19 direct production staff in the revenue model) and "
"spread them over more records")],
["The ask", pick(
"~THB 177M (~USD 5.35M), the ~180M THB plan, released in tranches against milestones "
"(section 8), starting with phase 0 (~THB 1.3M to 2.6M). BOI caps debt-to-equity at 3:1, so "
"at least a quarter is equity: ~THB 44M (arithmetic, before financing costs)",
"~THB 224M (~USD 6.79M), the ~225M THB plan, released in tranches against milestones "
"(section 8), starting with phase 0 (~THB 1.3M to 2.6M). BOI caps debt-to-equity at 3:1, so "
"at least a quarter is equity: ~THB 56M (arithmetic, before financing costs)")],
]),
H2("Two versions of this plan"),
P("This plan comes in two versions, built from the same documents and the same machine choices. " + pick(
"This document is **Plan A**. The other version, **Plan B (~225M THB)**, puts the second Duplex "
"system in at launch, for five presses, and takes none of the six levers. ",
"This document is **Plan B**. The other version, **Plan A (~180M THB)**, launches with three "
"presses (one Duplex system and the Classic), takes six cost levers and keeps the second Duplex "
"as a growth step from revenue. ")
+ "Both carry the ramp-up reserve and neither carries a contingency. What decides between them is "
"demand: Plan B's central case needs about 70 % more records sold than Plan A's (1.25 against 0.74 "
"million a year), and the market validation gate (section 3.2) is the evidence for that. Section 8 "
"sets the two side by side."),
T(["", "Plan A", "Plan B"], [
["Plan price (ramp-up reserve in, no contingency)", "~THB 177M (~USD 5.35M); ~127M to 226M from "
"the low to the high end", "~THB 224M (~USD 6.79M); ~162M to 286M from the low to the high end"],
["Presses", "3: one Duplex system (2 semi-automatic) and one Classic",
"5: two Duplex systems (4 semi-automatic) and one Classic"],
["Records a year at 60 % specials", "~0.58M to 0.90M (central 0.74M)",
"~1.0M to 1.5M (central 1.25M)"],
["EBITDA, central case", "~USD 1.73M (~THB 57M)", "~USD 3.14M (~THB 104M)"],
["Operating profit, central case", "~USD 1.34M (~THB 44M)", "~USD 2.63M (~THB 87M)"],
["Payback on the plan price, central case", "~3.1 years (2.2 to 4.0)", "~2.2 years (1.6 to 2.8)"],
["Adverse case", "Loses money: EBITDA ~USD 0.23M, operating loss ~USD 0.39M",
"Just covers depreciation: EBITDA ~USD 0.83M, operating profit ~USD 0.02M"],
["What Plan A gives up, what Plan B needs", "At launch: solar through a PPA (very likely without "
"the BOI solar measure), no confocal or profiler, no visitor corridor or tours, no cassette or "
"CD-R, a rented crane; Chinese-made boilers and compressor",
"~1.25M records sold a year, ~0.75M of them specials, against 0.74M for Plan A; no market "
"evidence of that demand yet"],
], widths_mm=[42, 64, 64], font_pt=8.5,
caption="Estimates: mature year at 60 % specials, from the revenue model; payback counted on the "
"whole plan price including the reserve. Central EBITDA margins of ~47 % (A) and ~52 % (B) "
"are far above the 15 to 25 % pressing plants earn; read the adverse case as the honest one."),
H2("Where we are, and what we ask"),
P("The project is at the planning stage. Industry outreach has run for several years. Alexander and "
"Simona attended **Making Vinyl Europe in Haarlem** (30 September to 2 October 2026): most people "
@@ -170,18 +302,33 @@ def blocks():
CALL("What the investor is asked to fund now", [
"**Phase 0 (next 3 to 6 months): ~USD 40k to 80k (~THB 1.3M to 2.6M)** for the BOI consultant, "
"Thai legal work on the lease and zoning, a soil boring test, and the requests that settle most "
"of the investment band: the Newbilt re-quote for the Duplex and the Classic, a Pheenix quote for "
"the galvanics line, the building price request with the soil test, and metrology distributor "
"quotes.",
"**Then the launch plant round, targeted at ~THB 216M (~USD 6.55M)**, released in tranches "
"against milestones: BOI certificate and lease signed, long-lead orders (lathe, galvanics, "
"stamper prep, presses, first boiler and chiller modules), building contract, fit-out, "
"commissioning in sequence (section 8.3). The full estimate band is THB 161M to 292M; section "
"8.2 shows the levers that bring the midpoint inside 216M.",
"**Decisions needed from the investor:** the equity/debt mix (BOI requires debt-to-equity of at "
"most 3:1); CD replication, yes or no (it must be in the BOI application from the start); the "
"steam fuel, electric or biomass; and which budget levers to take (solar through a PPA, "
"metrology bought in two steps, the visitor corridor later).",
"of the investment band: " + pick(
"the Newbilt re-quote for the Duplex system (with an EX 35 price) and the Classic, a Pheenix "
"quote for the galvanics line, the building price request with the soil test, metrology "
"distributor quotes and a solar PPA offer.",
"the Newbilt re-quote for both Duplex systems (with an EX 35 price) and the Classic, a Pheenix "
"quote for the galvanics line, the building price request with the soil test, and metrology "
"distributor quotes."),
pick(
"**Then the Plan A round, ~THB 177M (~USD 5.35M), rounded to ~180M THB**, including the "
"ramp-up reserve and without contingency, released in tranches against milestones: BOI "
"certificate and lease signed, long-lead orders (lathe, galvanics, stamper prep, presses, "
"boiler and chiller modules), building contract, fit-out, commissioning in sequence "
"(section 8). From the low to the high end of every block the plan runs from ~THB 127M to "
"226M; quotes that land high are funded outside the plan or by scope steps.",
"**Then the Plan B round, ~THB 224M (~USD 6.79M), rounded to ~225M THB**, including the "
"ramp-up reserve and without contingency, released in tranches against milestones: BOI "
"certificate and lease signed, long-lead orders (lathe, galvanics, stamper prep, both Duplex "
"systems and the Classic, boiler and chiller modules), building contract, fit-out, "
"commissioning in sequence (section 8). From the low to the high end of every block the plan "
"runs from ~THB 162M to 286M; quotes that land high are funded outside the plan or by scope "
"steps."),
"**Decisions needed from the investor:** which version to fund, Plan A or Plan B (demand decides "
"first, from market validation; then the Classic, Pheenix and EX 35 quotes, the solar PPA offer "
"and the building quote); the equity/debt mix (BOI requires debt-to-equity of at most 3:1, so at "
"least ~THB " + pick("44M of Plan A's ~177M", "56M of Plan B's ~224M") + " is equity); CD "
"replication, yes or no (it must be in the BOI application from the start); and the steam fuel, "
"electric or biomass.",
"**Co-investment:** Ploy Printing, the Chiang Mai print and packaging house that will produce "
"the plant's jackets and sleeves, has expressed the intention to invest alongside the lead "
"investor. Terms are not yet discussed; the intended structure is described in section 12.",
@@ -189,6 +336,12 @@ def blocks():
# ----------------------------------------------- 2 reading guide
H1("2. How to read the numbers"),
P("**Two versions.** This business plan comes in two versions, built from the same documents: "
"**Plan A (~180M THB)**, the launch plant's three presses with six cost levers, and **Plan B "
"(~225M THB)**, five presses with everything in. Both plan prices include the ramp-up reserve and "
"neither carries a contingency. This document is **" + label() + "**. Chapters that do not depend "
"on the choice read the same in both; where the plans differ, this version gives its own figures, "
"and sections 1 and 8 compare the two."),
P("Every figure in this plan carries one of three confidence levels. Where a table does not say, "
"the basis column or caption does."),
T(["Label", "Meaning", "Examples"], [
@@ -202,15 +355,18 @@ def blocks():
"pressing invoices (August 2026); the PEA electricity tariff; vendor "
"flyers and datasheets; reported biomass fuel prices; BOI measures"],
["Estimate", "Engineering ratio or industry norm with no vendor confirmation. Treat as "
"plus or minus 30 to 50%.", "Building cost per m²; the Pheenix galvanics line and the "
"Newbilt Classic (no price on file); the cutting room "
"beyond the lathe; metrology stack; land lease; all "
"cassette and CD figures"],
"plus or minus 30 to 50%.", "Building cost per m²; the Pheenix galvanics line (no "
"Pheenix price on file; priced at OMS's reference plus "
"20 %) and the Newbilt Classic (no price on file); the "
"cutting room beyond the lathe; metrology stack; land "
"lease; " + pick("the solar PPA power price; ", "")
+ "all cassette and CD figures"],
], widths_mm=[22, 70, 78]),
P("Currency: USD and THB at 1 USD = 33 THB (5 August 2026). Vendor quotes issued in EUR or GBP are "
"kept in their currency next to their source and converted at 1 EUR = 1.155 USD and "
"1 GBP = 1.35 USD. Currency exposure is real and is discussed in section 8.5."),
P("Deliberately **not** in this plan: a financing structure or terms (to be agreed with the investor); "
P("Deliberately **not** in this plan: a contingency in the plan price (section 8); a financing "
"structure or terms (to be agreed with the investor); "
"a signed customer; a signed lease; firm quotes for the Classic, the galvanics line, the metrology "
"stack or the building; a price for the central hydraulic plant; and a year-by-year cash model. "
"Each of these is a named next step in section 14."),
@@ -236,7 +392,9 @@ def blocks():
"is the gap. It is also the plan's biggest assumption: that enough regional customers will pay for "
"a closer, faster, more accountable plant at a price that works. That pricing power is unproven. "
"The Haarlem trade show produced strong supplier interest but no buyer prices, and the market "
"validation gate exists to test it before capital is committed."),
"validation gate exists to test it before capital is committed." + pick(
"", " Plan B makes the assumption heavier: its central case sells ~1.25 million records a year, "
"~0.75 million of them special editions, against 0.74 million for the three-press Plan A.")),
H2("3.3 What the market shows: two price points"),
P("Two sets of real prices now exist: invoices from the one Thai pressing shop we have seen, and the "
"published calculator of a European plant. Neither is used to re-base the financial model, which "
@@ -260,9 +418,12 @@ def blocks():
P("What the tour showed: the shop operates from a small non-industrial premises; it does not plate "
"(stampers are bought in from abroad and resold); there is no temperature, humidity or particulate "
"control on the pressing floor; and it runs two Newbilt presses, a Classic fully automatic and one "
"semi-automatic, about 190 to 210 records an hour at nameplate. That is a real capital fleet. Our "
"launch plant's three presses (248 to 288 records an hour at nameplate, planned for two shifts) "
"out-press it on paper, which the one-press launch of draft 0.1 did not. Neither figure is "
"semi-automatic, about 190 to 210 records an hour at nameplate. That is a real capital fleet. " + pick(
"Our launch plant's three presses (248 to 288 records an hour at nameplate, planned for two "
"shifts) ",
"Plan B's five presses (four semi-automatic at 60 to 80 records an hour each plus the Classic at "
"~128, 368 to 448 records an hour at nameplate, planned for two shifts) ")
+ "out-press it on paper, which the one-press launch of draft 0.1 did not. Neither figure is "
"measured output: their shifts and order book were not observed."),
H3("A European plant: Record Industry's calculator"),
P("Record Industry (Haarlem, Netherlands) publishes an online calculator. For a 12-inch 140 g black "
@@ -287,8 +448,8 @@ def blocks():
"been asked (roadmap item 1)."),
P("Four readings follow, each with its caveat:"),
B([
"**Our assumed price sits between the two markets.** The plan prices a record at USD 3.50 to 4.60 "
"blended (115 to 150 THB). The Thai market pays 535 to 780 THB on a comparable scope, several "
"**Our assumed price sits between the two markets.** The plan prices a record at USD 3.76 to 5.27 "
"blended at 60 % specials (124 to 174 THB; 3.50 to 4.60 at a standard-heavy mix). The Thai market pays 535 to 780 THB on a comparable scope, several "
"times more, even after allowing 100 to 150 THB for the jacket. A European plant's published "
"price for black pressing with labels and an inner sleeve (~USD 2.43 at 300) is below our "
"assumption, though our blend also carries colour work and assembly. Two caveats: a sole "
@@ -324,7 +485,9 @@ def blocks():
"add-on with modest capital. CDs remain a large format in Asia (Japan and Korea in particular), "
"but industrial CD replication is an established, price-competitive business with existing "
"capacity across the region. Sections 4.9 and 4.10 lay out both formats and the two very "
"different CD routes; cassette and CD-R duplication are in the launch plant."),
"different CD routes; " + pick(
"Plan A adds cassette and CD-R duplication after launch, as a growth step outside its price.",
"cassette and CD-R duplication are in Plan B from launch.")),
# ------------------------------------------ 4 anatomy of the plant
H1("4. Anatomy of a top-notch plant: the full chain"),
@@ -341,62 +504,63 @@ def blocks():
T(["What a top-notch plant needs", "Why it matters", "This plan", "When"], [
["A quiet, vibration-free cutting room with a modern lathe", "The groove is born here; every "
"defect propagates to every record", "Sillitoe SDMS complete cutting system in-house; Agnew "
"Type 612 as the other candidate", "Launch plant, "
"commissioned first"],
"Type 612 as the other candidate", LP + ", commissioned first"],
["In-house galvanics with zero-discharge water", "The regional bottleneck; enables represses in "
"days and short runs", "Pheenix Alpha line (PASU, PPPU, one PDPU, PMPB); evaporator-based "
"zero discharge and DI water in a treatment building next to it",
"Launch plant, commissioned first"],
LP + ", commissioned first"],
["Stamper preparation machines", "Turns a raw nickel shell into a press-ready part",
"Sibert back-finisher, punch, coiner (USD 250k budget confirmed)", "Launch plant, with "
"galvanics"],
"Sibert back-finisher, punch, coiner (USD 250k budget confirmed)", LP + ", with galvanics"],
["A mixed press fleet", "Semi-automatic for colour and short runs, automatic for long runs",
"Newbilt Duplex system (2 presses, main + colour extruder) and one Newbilt Classic",
"Launch plant; second Duplex as growth option"],
pick("Newbilt Duplex system (2 presses, main + colour extruder) and one Newbilt Classic",
"Two Newbilt Duplex systems (4 presses, each system with main + colour extruder) and one "
"Newbilt Classic"),
pick("Launch plant; second Duplex as growth option",
"At launch, second Duplex system commissioned last; full hall as an option")],
["Steam, cooling and compressed air engineered locally", "The largest energy loads; efficiency "
"lives here", "Boiler hall and chiller compound for four modules each; 2 x 500 kg/h boilers and "
"2 x 250 kW chillers; heat recovery designed in", "Launch plant; modules added "
"with presses"],
"lives here", "Boiler hall and chiller compound for four modules each; " + pick(
"2 x 500 kg/h boilers (Chinese route) and 2 x 250 kW chillers",
"2 x 500 kg/h boilers and 3 x 250 kW chillers") + "; heat recovery designed in",
LP + "; modules added with presses"],
["Whole-plant climate and particulate control", "Moisture and dust are the two classic record "
"defects", "20 to 26 °C, 35 to 60% RH, filtration for the burning season, tighter in clean "
"zones", "Launch plant"],
"zones", LP],
["Metrology for the metal parts", "Data-backed accept/reject before a stamper is mounted",
"Laser confocal, wide-area profiler, XRF thickness, inline vision", "Launch plant (confocal "
"and profiler can follow "
"from revenue)"],
pick("XRF thickness and inline vision at launch; laser confocal and wide-area profiler from "
"revenue",
"Laser confocal, wide-area profiler, XRF thickness, inline vision"),
pick("Launch plant (confocal and profiler from revenue)", "At launch, full stack")],
["Layered QC for every record", "Catch defects at line speed without playing every record",
"100% optical inspection, per-cycle telemetry, sampled audio comparison", "Launch plant"],
"100% optical inspection, per-cycle telemetry, sampled audio comparison", LP],
["Traceability from file to shipment", "Complaints resolve backwards; customers see their "
"order; machine data access is a purchasing condition", "ERPNext as the only system of record, "
"machine data keyed to it, customer "
"portal", "Launch plant (v1 before "
"the machines arrive)"],
"portal", LP + " (v1 before the "
"machines arrive)"],
["A materials lab", "PVC lots differ; a bad lot is a noisy run", "Per-lot gate: moisture, heat "
"stability, colour, density, granulate; release by test pressing", "Launch plant (day-one "
"tier)"],
"stability, colour, density, granulate; release by test pressing", LP + " (day-one tier)"],
["A packaging partner and packaging QC", "Jackets are the path that slips; the wrong "
"record in the right jacket is the classic error", "Ploy Printing (Chiang Mai) for jackets, "
"sleeves, J-cards; matrix code scanned "
"against jacket barcode", "Launch plant"],
"against jacket barcode", LP],
["An archive for fathers and mothers", "Irreplaceable metal; repress economics",
"Father and mother of every side; fire-rated, climate-controlled room, clean-agent suppression, "
"mobile shelving (to quote)", "Launch plant"],
"mobile shelving (to quote)", LP],
["Renewable energy and closed-loop water", "Cost, resilience, brand, BOI tax value",
"~247 kWp roof PV, 271 kWh battery, closed cooling loop, zero process discharge", "Launch plant "
"(PV possibly "
"through a "
"solar PPA)"],
pick("~247 kWp roof PV and 271 kWh battery through a solar PPA (installer-owned), closed cooling "
"loop, zero process discharge",
"~247 kWp roof PV and 271 kWh battery (owned), closed cooling loop, zero process discharge"),
pick("Launch plant (PV and battery through a solar PPA)", "At launch")],
["Trained people and a safety program", "Steam at 10 bar, hydraulics at 180 bar, plating "
"chemistry", "Vendor commissioning training, in-house program, PPE, clean rooms", "Launch plant; "
"galvanics "
"hiring first"],
"chemistry", "Vendor commissioning training, in-house program, PPE, clean rooms",
LP + "; galvanics hiring first"],
["An ordering platform for every customer", "Short runs need a channel; transparency is the "
"brand", "Itemised price calculator, upload checks, fan-funded campaigns (section 4.8)",
"Calculator early; campaigns after first releases ship"],
["Cassette and CD lines", "Same customers, one order, one shipment", "In-house cassette "
"duplication; CD-R duplication; CD replication as a separate option", "Launch plant "
"(replication only if "
"chosen)"],
"duplication; CD-R duplication; CD replication as a separate option",
pick("Growth step after launch, not in the plan price (replication only if chosen)",
"At launch (replication only if chosen)")],
], widths_mm=[40, 45, 55, 30], font_pt=8.5),
H2("4.1 Mastering and lacquer cutting"),
@@ -453,7 +617,8 @@ def blocks():
"treatment and metal recovery system. The chemistry (silver nitrate, nickel salts) sets the safety "
"and permitting requirements: decontamination procedures, PPE, locked chemical storage, eyewash "
"and emergency shower, and hazardous-substance registration. Because galvanics is now in the "
"launch plant, the site's zoning and the first factory licence have to allow electroplating from "
+ pick("launch plant", "plant from launch")
+ ", the site's zoning and the first factory licence have to allow electroplating from "
"the start."),
P("**Who supplies it.** The plan is a **Pheenix Alpha** line (Sweden): the PASU automatic silvering "
"unit, the PPPU pre-plating unit (four masters at once), one PDPU dual plating unit (two cells, up "
@@ -470,12 +635,16 @@ def blocks():
"gives about 15 to 30 minutes for pre-plate, ~75 minutes for a father, about an hour for a mother "
"and ~45 minutes for a stamper (silvering and bench times are estimates). With loading and rinsing, "
"a cell turns a part in about 1 to 1.5 hours, so one PDPU makes 20 to 32 parts in a two-shift day "
"against 8 to 20 parts a working day even for the larger five-press fleet. **One PDPU covers the "
"against 8 to 20 parts a working day "
+ pick("even for the larger five-press fleet", "for Plan B's five presses")
+ ". **One PDPU covers the "
"demand; a second would buy redundancy, not capacity.** By OMS's rule of thumb (one person runs "
"about 20 stampers a day) that is one galvanics operator per shift."),
P("**Cost and risk.** Estimated USD 320k to 650k (THB 10.6M to 21.4M) for the Pheenix line, with no "
"Pheenix price on file; OMS's reference prices sit at the low end, and pricing near them is one of "
"the four budget levers. The zero-discharge and DI water plant adds an estimated USD 97k to 258k "
P("**Cost and risk.** Pheenix has not quoted, so the plan prices its line at **OMS's reference plus "
"20 %**: OMS's 4-cell wet room with the EUR 10,000 know-how transfer (nobody on the team has run a "
"plating room), EUR 234,000 to 321,000, times 1.2 gives EUR 280,800 to 385,200, ~USD 324k to 445k "
"(THB 10.7M to 14.7M; an estimate, replacing the earlier estimate of USD 320k to 650k). The "
"zero-discharge and DI water plant adds an estimated USD 97k to 258k "
"and its building USD 52k to 87k. Galvanics is the hardest skill to hire and it is now on the "
"path to the first release, because the presses need its stampers. Sibert advised getting each "
"main step right and earning some return before every manufacturing step is in place; that advice "
@@ -513,30 +682,48 @@ def blocks():
"Duplex share mould inserts, so the plant runs one mould family, one set of spares and one service "
"relationship; the cost is a single-vendor dependency."),
T(["Machine", "Role", "Basis", "Cost"], [
["Newbilt Duplex system (Alsdorf, Germany)", "Two HRP 12 hydraulic presses (~150 t), EX 50 main "
[pick("Newbilt Duplex system (Alsdorf, Germany)", "Newbilt Duplex system 1 (Alsdorf, Germany)"),
"Two HRP 12 hydraulic presses (~150 t), EX 50 main "
"extruder and EX 35 colour extruder, 12-inch book moulds for 140/180 g, trimmer, label dry oven, "
"shrink wrapper, PVC grinder, label punch, spindles and plates; one operator's cell",
"Quoted: EUR 200,500 (~USD 232k) for the Single 2.1 system incl. sea freight, June 2024, plus "
"the quoted second press and extruder (~USD 115k); expired, re-quote required with build time",
"USD 350k to 370k (THB 11.6M to 12.2M)"],
*pick([], [[
"Newbilt Duplex system 2", "Two more HRP 12 presses with an EX 50 main and an EX 35 colour "
"extruder, on the same central hydraulics; commissioned after the Classic",
"Estimate on the 2024 quote: two presses at the quoted ~USD 115k per press and extruder set, "
"plus a hydraulic unit and the EX 35; no EX 35 price on file",
"USD 230k to 300k (THB 7.6M to 9.9M)"]]),
["Newbilt Classic", "Fully automatic press with built-in extruder, automatic patty, label and "
"record handling; 28 s cycle, ~128 records/h at 180 g; Siemens safety PLC with OPC UA; "
"vendor-claimed 95% yield and uptime", "Estimate: placeholder band; **no Newbilt price on "
"file**", "USD 290k to 460k (THB 9.6M to 15.2M)"],
["Second Duplex system (growth option)", "Two more semi-automatic presses on the same central "
"hydraulics; floor, utility positions and ring-main tees reserved", "Estimate; not in the "
"investment",
"~USD 230k to 300k"],
*pick([["Second Duplex system (growth option)", "Two more semi-automatic presses on the same central "
"hydraulics; floor, utility positions and ring-main tees reserved", "Estimate; not in the "
"investment",
"~USD 230k to 300k"]], []),
], widths_mm=[34, 66, 42, 28], font_pt=8.5),
P("The Pheenix Alpha AD12 automatic press left the plan in October 2026 and stays the comparison if "
"the Classic re-quote disappoints."),
P("**What Newbilt deliberately excludes**, and what is engineered and sourced locally: steam and "
"cooling. At Newbilt's per-machine figures the three presses draw about 450 to 540 kg/h of steam "
"at 10 bar / 180 °C and up to ~390 kW of cooling on a hot afternoon (estimates; the Classic is "
"counted as one press and extruder until Newbilt states its own figures). They are met by **two "
"500 kg/h boiler modules and two 250 kW chiller modules**, in a boiler hall and an open chiller "
"compound built for four modules each; a second Duplex adds a third chiller module, and the fourth "
"positions are reserve and growth. The presses share one central hydraulic plant and one "
"cooling. " + pick(
"At Newbilt's per-machine figures the three presses draw about 450 to 540 kg/h of steam "
"at 10 bar / 180 °C and up to ~390 kW of cooling on a hot afternoon (estimates; the Classic is "
"counted as one press and extruder until Newbilt states its own figures). They are met by **two "
"500 kg/h boiler modules (B-1, B-2) and two 250 kW chiller modules (CH-1, CH-2)**, in a boiler "
"hall and an open chiller compound built for four modules each; a second Duplex adds a third "
"chiller module, and the fourth positions are reserve and growth. Plan A buys the two boiler "
"modules through the Chinese route: ASME-stamped 1.25 MPa units from makers with Thai "
"references, installed by a Thai contractor and verified under the Thai rules for imported "
"boilers (section 5.4). ",
"At Newbilt's per-machine figures the five presses draw about 750 to 900 kg/h of steam "
"at 10 bar / 180 °C and up to ~650 kW of cooling with extruders and hydraulics on a hot "
"afternoon (estimates; the Classic is counted as one press and extruder until Newbilt states "
"its own figures). They are met by **two 500 kg/h boiler modules (B-1, B-2) and three 250 kW "
"chiller modules (CH-1 to CH-3)**, in a boiler hall and an open chiller compound built for four "
"modules each; the fourth positions are reserve and growth. ")
+ "The presses share one central hydraulic plant and one "
"compressed-air plant through ring mains with a valved tee at every press position, so a new press "
"is connected without stopping the others; the standby pump set, accumulators and ring main are "
"not yet costed. These are the largest energy systems in the plant and the reason the efficiency "
@@ -546,7 +733,7 @@ def blocks():
"materials and finished goods. A press weighs 1,350 kg on under one square metre, so the floor slab "
"must carry several tonnes per square metre and is a building-selection criterion; the floor must "
"be flat, non-slip and solvent-resistant."),
T(["Stage", "Fleet", "Rough output per year"], [
T(["Stage", "Fleet", "Rough output per year"], pick([
["Launch plant", "1 Duplex system (2 semi-automatic) + 1 Classic, 2 shifts",
"~0.58 to 0.90 million records at 60 % specials (0.71 to 0.97 at ~20 %)"],
["Growth option: second Duplex", "4 semi-automatic + 1 Classic, 2 shifts; room reserved, not in "
@@ -554,13 +741,26 @@ def blocks():
["Full hall (option)", "5 Duplex systems + 4 Classics, the press hall laid out for Duplex "
"(special pressings first, ~73/27 by floor); a utility phase of its own",
"~2.9 to 4.4 million records at 60 % specials (estimate)"],
], widths_mm=[40, 80, 50],
caption="Basis: semi-automatic 60 to 80 records/h assumed; Classic ~128/h (vendor flyer); 16 h/day, "
"300 days/yr, 60 to 70% overall equipment effectiveness; specials (60 % and more) only on the Duplex. "
"The launch plant is 248 to 288 records/h at nameplate, but at 60 % specials the Duplex caps "
"it at 200 to 267/h, ~120 to 187/h after effectiveness; the press fleet calculator at "
"the team's planning rates gives ~0.94 million. Neither option is in the revenue model "
"or the investment."),
], [
["Plan B at launch", "2 Duplex systems (4 semi-automatic) + 1 Classic, 2 shifts",
"~1.0 to 1.5 million records at 60 % specials (central case 1.25 million)"],
["Full hall (option)", "5 Duplex systems + 4 Classics, the press hall laid out for Duplex "
"(special pressings first, ~73/27 by floor); a utility phase of its own",
"~2.9 to 4.4 million records at 60 % specials (estimate)"],
]), widths_mm=[40, 80, 50],
caption=pick(
"Basis: semi-automatic 60 to 80 records/h assumed; Classic ~128/h (vendor flyer); 16 h/day, "
"300 days/yr, 60 to 70% overall equipment effectiveness; specials (60 % and more) only on the "
"Duplex. The launch plant is 248 to 288 records/h at nameplate, but at 60 % specials the "
"Duplex caps it at 200 to 267/h, ~120 to 187/h after effectiveness; the press fleet calculator "
"at the team's planning rates gives ~0.94 million. Neither option is in the revenue model "
"or the investment.",
"Basis: semi-automatic 60 to 80 records/h assumed; Classic ~128/h (vendor flyer); 16 h/day, "
"300 days/yr, 60 to 70% overall equipment effectiveness; specials (60 % and more) only on the "
"Duplex systems. The five presses are 368 to 448 records/h at nameplate; the four "
"semi-automatic presses (240 to 320/h) carry the specials, so at 60 % the Duplex does not "
"limit the output; the press fleet calculator at the team's planning rates gives ~1.5 million. "
"The full hall is in neither the revenue model nor the investment.")),
P("**On the 60 to 70% effectiveness figure.** Newbilt publishes 95% yield and 95% uptime for the "
"Classic. We have not adjusted the plan on that basis, deliberately: a vendor's machine uptime "
"excludes stamper and mould changes, colour changeovers, compound lot changes, first-article QC "
@@ -687,16 +887,21 @@ def blocks():
"(National Audio Company in the US is the largest tape source; shell moulders exist in China and "
"should be sought in Thailand). Supply risk is real and the plan is to hold stock. **Positioning:** "
"cassettes are a boutique add-on for the same indie customers, with shells in colours that match "
"the record. **Effect on the plan:** in the launch plant, starting at tier A or a small tier B; "
"cassette and CD-R together are one estimated block of USD 55k to 190k in the investment; "
"schedule-neutral; fits inside the building programme; the activity goes into the BOI "
"application. Moving both later is one of the scope cuts that would be needed to approach "
"180M THB."),
"the record. **Effect on the plan:** " + pick(
"Plan A adds cassette and CD-R duplication after launch, as a growth step from revenue, "
"starting at tier A or a small tier B. Together they are one estimated block of USD 55k to "
"190k (~THB 4.0M at the midpoint), not in Plan A's price: moving them later is one of its six "
"cost levers. They fit inside the building programme, and the activity can still go into the "
"BOI application from the start (to confirm with the BOI consultant).",
"in Plan B from launch, starting at tier A or a small tier B; cassette and CD-R together are "
"one estimated block of USD 55k to 190k in the investment; schedule-neutral; fits inside the "
"building programme; the activity goes into the BOI application. Plan A, the other version, "
"moves both to after launch as one of its cost levers.")),
H2("4.10 CD: two very different routes (investor decision)"),
P("'CD manufacturing' can mean two things that differ by two orders of magnitude in capital, and the "
"investor is asked to choose."),
T(["", "Option A: CD-R duplication", "Option B: CD replication"], [
T(["", "Route 1: CD-R duplication", "Route 2: CD replication"], [
["What it is", "Blank recordable discs are burned and printed by robotic duplicators. The "
"result is a CD-R, playable in almost all players, with full-colour on-disc print.",
"Discs are injection-moulded from polycarbonate against a nickel stamper (made by glass "
@@ -726,14 +931,19 @@ def blocks():
"electroforming skill set and the clean-room discipline of the vinyl galvanics room, but not "
"the equipment"],
], widths_mm=[24, 68, 78], font_pt=8.5,
caption="Estimate throughout. Option B figures are order-of-magnitude and would need a dedicated "
caption="Estimate throughout. Route 2 figures are order-of-magnitude and would need a dedicated "
"feasibility study."),
P("**Recommendation.** Option A is in the launch plant: it is cheap, schedule-neutral, and serves the "
"plant's customers. Treat Option B as a separate project with its own market study, because CD "
P("**Recommendation.** " + pick(
"Route 1 is the CD route for this plant: it is cheap, schedule-neutral, and serves the plant's "
"customers; Plan A adds it after launch together with the cassette line. ",
"Route 1 is in Plan B from launch: it is cheap, schedule-neutral, and serves the plant's "
"customers. ")
+ "Treat Route 2 as a separate project with its own market study, because CD "
"replication is a mature, competitive, declining business outside Asia's strongholds and would "
"add USD 1.5M to 4.0M and a second building to a plan whose vinyl economics do not depend on it. "
"If the investor wants Option B, the decision must be taken before the BOI application is filed "
"(section 7), and the second building should be designed for it. This plan's totals include "
"Option A only; Option B is shown as a separate line."),
"If the investor wants Route 2, the decision must be taken before the BOI application is filed "
"(section 7), and the second building should be designed for it. " + pick(
"Plan A's price includes neither route; Route 1 is a growth step and Route 2 a separate line.",
"This plan's totals include Route 1 only; Route 2 is shown as a separate line.")),
]
return out
+245 -85
View File
@@ -1,6 +1,9 @@
"""Part B: what makes it top-notch (traceability, metrology, QC, efficiency); site and building;
regulatory (BOI and licences). Draft 0.2, October 2026."""
regulatory (BOI and licences). Draft 0.3, October 2026: one text for both versions, Plan A (~180M
THB, the launch plant with six cost levers) and Plan B (~225M THB, five presses, everything in); the
plan-specific passages switch with plan_variant.pick(), called inside blocks()."""
from plan_lib import H1, H2, H3, P, B, N, T, KV, IMG, CALL, PB
from plan_variant import pick, is_a, is_b, label, name
def blocks():
@@ -13,7 +16,7 @@ def blocks():
"capabilities are what a regional customer cannot buy today and what justifies export-grade "
"pricing. None of it is exotic: the instruments exist, the data interfaces exist, and the "
"engineering is well understood. What is unusual is putting all of it in one plant from the "
"start rather than bolting it on later. The launch plant makes that possible: with the cutting "
"start rather than bolting it on later. The plan makes that possible: with the cutting "
"room and galvanics in-house from the first release, the whole chain from lacquer to box runs "
"through our own floor, with no bought-in stamper whose history we cannot see."),
@@ -148,16 +151,16 @@ def blocks():
T(["Instrument", "What it measures", "How it is used", "When"], [
["XRF thickness gauge (Fischer or Bowman class)", "Nickel deposit thickness, non-destructive",
"Per serial, read on the outer land zones; replaces the ten-point micrometer. Target total "
"thickness variation 10 to 20 microns", "Launch tier"],
["Inline machine vision", "Cosmetic defects", "100% of parts, fast", "Launch tier"],
"thickness variation 10 to 20 microns", "At launch"],
["Inline machine vision", "Cosmetic defects", "100% of parts, fast", "At launch"],
["3D laser confocal microscope (Keyence VK-X class)", "Groove cross-section, wall roughness, "
"micro-defects; 20 nm vertical, ~120 nm lateral resolution, non-contact", "Sampled radial "
"zones (lead-in, outer, mid, inner) on fathers, mothers and stampers; the full spiral is not "
"feasible and not needed. The optical-disc industry verified its nickel stampers with this "
"instrument class", "Full stack; from revenue if the lever is used"],
"instrument class", pick("From revenue", "At launch")],
["Wide-area optical profiler (Keyence VR-6000 class)", "Form, flatness, gross defects; 0.1 "
"micron height resolution, ~1 s per field, HDR mode for mirror-bright nickel",
"Fast whole-part mapping", "Full stack; from revenue if the lever is used"],
"Fast whole-part mapping", pick("From revenue", "At launch")],
["Stylus profilometer", "Traceable reference", "Calibrates the optical instruments",
"With the optical instruments"],
], widths_mm=[38, 46, 56, 30], font_pt=8.5),
@@ -173,21 +176,34 @@ def blocks():
"scripting, but there is no off-the-shelf connector to a factory system. A small watcher at the "
"metrology bench stamps each result with the part serial scanned from its back, writes the "
"verdict to the part's quality inspection in ERPNext and the raw file to the machine-data store."),
CALL("Metrology: the investor's choice", [
"**Full stack at launch.** The metrology and QC block is USD 350k to 810k (11.6M to 26.7M THB, "
"estimate, section 8): XRF, laser confocal, wide-area profiler, inline vision on the three "
"presses and the audio station. Every father, mother and stamper is measured before the next "
"plating generation or the press.",
"**Launch tier.** XRF, inline vision and the audio station at launch; the laser confocal and "
"the wide-area profiler bought from revenue. That saves ~10.1M THB at the midpoint with its "
"share of the contingency, one of the four levers that bring the plant inside 216M THB. Until "
"they arrive, groove geometry on the metal is judged indirectly, through the test pressing and "
"the audio station, as most plants do today; thickness and cosmetics are still measured on "
"every part.",
"Either way the data path is the same, so the later instruments plug in without rework. "
CALL(f"Metrology in {name()}: " + pick("the launch tier", "the full stack at launch"), pick([
"**At launch: XRF, inline vision and the audio station**, USD 170k to 460k (5.6M to 15.2M THB, "
"estimate, section 8). Every father, mother and stamper still gets its nickel thickness "
"measured per serial by XRF and its surface screened by vision, and every record is still "
"inspected optically on the three presses, logged per cycle and sampled by the audio station "
"(section 5.3).",
"**From revenue: the laser confocal and the wide-area profiler**, USD 180k to 350k (estimate). "
"Buying them later takes ~8.8M THB out of the capital round at the midpoint, one of the six "
"levers that bring the plan to ~180M THB. Until they arrive, groove geometry and form on the "
"metal are judged indirectly, through the test pressing and the audio station, as most plants "
"do today.",
"The data path is the same from the first day, so the later instruments plug in without "
"rework. Distributor quotes for the metrology stack are one of the four requests that settle "
"the budget.",
], [
"**At launch: the full stack.** The metrology and QC block is USD 350k to 810k (11.6M to 26.7M "
"THB, estimate, section 8): XRF, laser confocal, wide-area profiler, inline vision on the presses "
"and the audio station. Every father, mother and stamper is measured before the next plating "
"generation or the press. The block was priced with inline vision on three presses, so the "
"cameras for the second Duplex system are to quote.",
"**If quotes land high,** the laser confocal and the wide-area profiler (USD 180k to 350k, "
"estimate) are the part that can wait and be bought from revenue: thickness and cosmetics "
"would still be measured on every part, and groove geometry judged through the test pressing "
"and the audio station meanwhile, as most plants do today.",
"Either way the data path is the same, so later instruments plug in without rework. "
"Distributor quotes for the metrology stack are one of the four requests that settle the "
"budget.",
]),
])),
H2("5.3 Quality control: three layers for every record, one gate for every material"),
P("Full automated audio checking of every record is physically impossible: a 12-inch record plays "
@@ -202,7 +218,8 @@ def blocks():
"stamper), stitching and flow lines, warp, off-centre spindle holes, surface debris, wrong or "
"off-centre labels. Non-fill and stitching correlate with audible defects, so this is a strong "
"proxy for audio quality", "Custom integration of general machine vision (Keyence or Cognex "
"class) on all three presses; no vinyl-specific product exists"],
"class) on all " + pick("three", "five") + " presses; no vinyl-specific "
"product exists"],
["2. Press telemetry, every cycle", "Mould temperatures, pressures, cycle time, extruder values, "
"yields and drift alerts from the press PLC", "Process drift before it becomes a defect; the "
"record-level context for every other check", "Proven in the industry (Viryl's ADAPT model). "
@@ -214,7 +231,8 @@ def blocks():
"from lead-in silence, channel balance; score and pass/fail to the record", "Wrong master cut "
"into good-looking grooves, EQ and mastering errors, speed and pitch problems; what no camera "
"can see", "Buildable from proven parts (Stamper Discs' Fonograph approach, iZotope-class "
"algorithms); first of its kind as a whole; in the launch tier"],
"algorithms); first of its kind as a whole; " + pick("in the launch tier",
"at launch")],
], widths_mm=[30, 46, 54, 40], font_pt=8.5),
P("**Sampling rule for layer 3:** every test pressing and first-off per stamper, plus pulled "
"samples densifying as the stamper ages (a stamper lasts roughly 1,000 to 2,000 records; the "
@@ -247,10 +265,14 @@ def blocks():
H2("5.4 Efficiency: recover before you generate"),
P("The pressing machinery is a modest electrical load: 32 kVA connected for a full Duplex line. The "
"real demand sits in the utilities around it. At launch the three presses need 450 to 540 kg/h of "
"saturated steam at 10 bar / 180 °C and about 390 kW of cooling at a hot-afternoon peak; the "
"utility backbone is built for the five-press fleet (750 to 900 kg/h of steam, up to ~650 kW of "
"cooling). On top comes whole-plant air conditioning in a tropical climate. Newbilt excludes "
"real demand sits in the utilities around it. " + pick(
"At launch the three presses need 450 to 540 kg/h of saturated steam at 10 bar / 180 °C and "
"~391.5 kW of cooling at a hot-afternoon peak; the utility backbone is built for the "
"five-press fleet (750 to 900 kg/h of steam, up to ~650 kW of cooling). ",
"At launch the five presses need 750 to 900 kg/h of saturated steam at 10 bar / 180 °C and "
"~652.5 kW of cooling at a hot-afternoon peak, on a utility backbone with rooms for four "
"boiler and four chiller modules. ") +
"On top comes whole-plant air conditioning in a tropical climate. Newbilt excludes "
"steam and cooling from its scope, so these systems are ours to design, and the design rests on "
"one observation: the steam's job is to heat the moulds and the cooling water's job is to remove "
"that same heat again. Roughly 98% of the press cooling load is heat the steam put into the "
@@ -261,9 +283,19 @@ def blocks():
"for the five-press fleet: the boiler hall and the chiller compound with room for four modules "
"each, the steam main and condensate return, the cooling mains, the heat-exchanger frame and "
"tanks, and hydraulic and compressed-air ring mains with a valved, capped tee at every press "
"position. The machines that make steam and cold water come in identical modules: at launch "
"two 500 kg/h boilers (B-1, B-2) and two 250 kW air-cooled chillers rated at 40 °C ambient "
"(CH-1, CH-2), sequenced lead and lag by one controller. Identical modules mean one set of spares "
"position. The machines that make steam and cold water come in identical modules: " + pick(
"at launch two 500 kg/h boilers (B-1, B-2) for the 450 to 540 kg/h peak and two 250 kW "
"air-cooled chillers rated at 40 °C ambient (CH-1, CH-2) for ~391.5 kW, sequenced lead and "
"lag by one controller. Either boiler alone carries 93 to 111% of the steam demand; one "
"chiller out leaves ~64% of the cooling peak. The boiler bodies come through the Chinese "
"route and the compressor from a Chinese brand's Thai office (below). ",
"at launch two 500 kg/h boilers (B-1, B-2) for the 750 to 900 kg/h peak and three 250 kW "
"air-cooled chillers rated at 40 °C ambient (CH-1 to CH-3) for ~652.5 kW, sequenced lead "
"and lag by one controller; the boilers and the compressor come from Thai suppliers "
"(below). Five presses on two boilers leave no spare: a third module (B-3) is an RFQ option "
"outside the plan price, and without it a boiler fault costs about two presses until it is "
"fixed; one chiller out leaves ~77% of the cooling peak. ") +
"Identical modules mean one set of spares "
"and one service routine, N+1 later is one more module rather than a redesign, and the plant "
"never runs one oversized boiler at poor part load. Four boilers carry about eight presses with "
"one in reserve; four 250 kW chillers carry the five-press fleet with the fourth in reserve, and "
@@ -311,14 +343,17 @@ def blocks():
"weekdays) and 2.6037 THB/kWh off-peak, plus the Ft fuel adjustment (+0.1623 THB/kWh for "
"September to December 2026; it resets every four months) and reclaimable VAT. Two shifts put "
"about two thirds of process energy into peak hours: a blended ~3.81 THB/kWh before the demand "
"charge. For the five-press fleet the backbone is built for, without electric steam, that is "
"charge. " + pick("For the five-press fleet the backbone is built for", "For the five presses") +
", without electric steam, that is "
"roughly 4.5M to 6.8M THB a year before solar, 0.7M to 1.1M of it demand charge (estimates), on "
"an 800 to 1,000 kVA connection. Three design consequences:"),
B([
"**Biomass heat is far cheaper than electric steam** (below): the demand charge follows the "
"boiler's full-power draw, and the peak window covers 13 of the 16 production hours.",
"**The roof gets PV, not solar thermal.** Self-consumed PV is worth ~3.7 THB/kWh; it cannot "
"lower the demand charge, because the peak window runs to 22:00, long after sunset.",
"**The roof gets PV, not solar thermal.** Self-consumed PV is worth ~3.7 THB/kWh" + pick(
" of grid power displaced, less the PPA price the plant pays for it (below)", "") +
"; it cannot lower the demand charge, because the peak window runs to 22:00, long after "
"sunset.",
"**The battery is for ride-through, not peak shaving.** Charging off-peak and discharging at "
"peak earns ~70k THB a year for the 271 kWh stack, and it can hold demand down by only ~20 kW "
"(~30k THB a year; estimates). Its case is riding through grid dips and outages.",
@@ -327,8 +362,12 @@ def blocks():
KV([
["Rooftop PV", "~247 kWp on the 1,523 m² monopitch roof (425 modules of a generic 580 W "
"placeholder; no module chosen yet), ~335 to 360 MWh a year, ~25 to 35% of mature "
"two-shift use (1,000 to 1,500 MWh). Saves ~1.2M to 1.3M THB a year and pays back "
"in ~4.5 to 7 years at 25,000 to 35,000 THB/kWp (estimates)"],
"two-shift use (1,000 to 1,500 MWh). " + pick(
"Taken through a solar PPA (below): the plant pays the PPA price for this "
"power instead of buying the array, so its saving is the gap between that "
"price and the ~3.7 THB/kWh of grid power it displaces",
"Saves ~1.2M to 1.3M THB a year and pays back in ~4.5 to 7 years at 25,000 "
"to 35,000 THB/kWp (estimates)")],
["Canopies and the second building", "~40 kWp on the parking canopies (construction RFQ; ~54 "
"kWp if fully covered). A phase 2 building's roof adds "
"~251 kWp, ~498 kWp across both roofs"],
@@ -343,27 +382,39 @@ def blocks():
"burning-season dust"],
["Open checks", "Battery rack voltage against the chosen Deye model's window; Gobel safety "
"certifications (not published); EPC warranty on a mixed-vendor DC bus; yield "
"against PVGIS before committing"],
["Solar PPA (option)", "Instead of buying the PV and battery, an EPC owns and finances them and "
"the plant buys the power: ~12.3M THB less capital at the midpoint with "
"its contingency share, one of the four levers in section 8. The PPA "
"price is not quoted yet, and the BOI solar measure (section 7) rewards "
"the plant's own solar investment, so its tax value probably goes with "
"the array (to confirm with the BOI consultant)"],
"against PVGIS before committing" + pick("; the PPA offer itself, price and "
"terms", "")],
pick(["Solar PPA (this plan)", "An installer owns and finances the PV and the battery, and the "
"plant buys the power at a price still to quote (the calculator carries 3.0 THB/kWh, an "
"estimate, ~0.03M USD a year on the energy line). That keeps the PV EPC (USD 227k to "
"318k) and the battery (USD 42k to 62k) out of the capital round: ~10.7M THB less at the "
"midpoint, one of the six levers in section 8. The BOI solar measure (section 7) rewards "
"the plant's own solar investment, so its tax value very likely goes with the array (to "
"confirm with the BOI consultant). The PPA offer is asked against the specification "
"above"],
["Owned (this plan)", "The plant buys the array and the battery: rooftop PV EPC USD 227k "
"to 318k (7.5M to 10.5M THB) and the battery USD 42k to 62k (1.4M to 2.0M THB), "
"estimates, section 8. Owning them is what the BOI solar measure rewards (section 7). A "
"solar PPA (an installer owns and finances them, the plant buys the power at a price "
"still to quote) would take ~10.7M THB out of the capital round at the midpoint, but "
"very likely gives up that measure"]),
]),
H3("Steam: the open engineering choice"),
P("The boilers' fuel is still open; the modular bank works with either. There is no natural gas "
"pipeline in Northern Thailand, so gas means a bulk LPG tank on site with hazardous-storage "
"separation and licensing, and diesel means a fuel tank with price exposure. The two routes "
"without a fossil tank, compared for the same heat (~1,120 to 1,280 MWh a year at about 1.0 "
"million records, the top of the launch plant's range; estimates):"),
"million records, " + pick("the top of the plant's range", "the low end of the five presses' "
"range") + "; estimates):"),
T(["", "Electric boiler bank (~98%)", "Biomass boiler bank (~80%)"], [
["Price of heat", "~3.9 THB/kWh, plus the demand charge", "~0.5 to 0.9 THB/kWh at 1.35 to 2 "
"THB/kg delivered (reported market prices, October 2026; no supplier quote yet)"],
["Running cost, mature two shifts", "~5.2M to 6.0M THB a year, including 0.8M to 1.0M of extra "
"demand charge for the five-press bank", "~0.6M to 1.2M THB a year of fuel, plus handling "
"and ash"],
["Boiler package, 2 × 500 kg/h", "4.5M to 7M THB (Thai suppliers); the investment's baseline",
["Boiler package, 2 × 500 kg/h", pick("~2.8M to 5.5M THB through the Chinese route, the "
"plan's price (4.5M to 7M from Thai suppliers)",
"4.5M to 7M THB (Thai suppliers); the plan's price"),
"~6M to 10M THB, plus the fuel store"],
["Grid", "Peak ~950 to 1,330 kW: PEA Schedule 4, 1,250 to 1,600 kVA", "Unchanged, ~800 to "
"1,000 kVA"],
@@ -378,7 +429,9 @@ def blocks():
"land a biomass stack would be a hard sell, which favours electric or LPG there (section 6.1)."),
P("**Fuel.** A biomass bank burns roughly 350 to 400 tonnes of air-dry wood a year at about 1.0 "
"million records (~540 to 620 t as fresh chips, which is what the store and the monsoon stock are "
"sized for). Lamphun alone produces more than 900,000 t of farm residues a year (Maejo "
"sized for)" + pick("", ", up to ~550 t air-dry at 1.5 million (fuel follows records pressed, "
"not presses)") +
". Lamphun alone produces more than 900,000 t of farm residues a year (Maejo "
"University), so supply is not the constraint; price is. Reported prices follow diesel, so the "
"fuel contract gets a diesel clause and sources within ~50 km. The local price-setter is Ratch "
"Pathana's biomass buying centre at Pa Sak, Mueang Lamphun, which feeds a 9.6 MW cogeneration "
@@ -387,9 +440,19 @@ def blocks():
"that is worth only ~0.1M to 0.25M THB a year, so its value is supply security and the story "
"('records pressed with steam from our own trees'), not the fuel bill. Next step: written quotes "
"from Pa Sak and two or three chip suppliers, priced by moisture, before the boiler decision."),
P("**Where the boilers come from.** Chinese makers with Thai references (ZOZEN, Sitong, Fangkuai, "
"Taiguo) could supply the two modules for roughly 2.8M to 5.5M THB installed against 4.5M to 7M "
"from Thai suppliers, about 1.6M THB less at the midpoint (estimates from listings, not quotes). "
P("**Where the boilers come from.** " + pick(
"The plan buys the two modules through the Chinese route: boiler bodies from a Chinese maker "
"with Thai references (ZOZEN, Sitong, Fangkuai, Taiguo), installed with the feed tank, pumps, "
"header, controls and piping by a Thai boiler contractor, for roughly 2.8M to 5.5M THB "
"installed against 4.5M to 7M from Thai suppliers, about 1.6M THB less at the midpoint. The "
"compressor is a Chinese brand bought through its Thai office (Kaishan and Denair have one), "
"so service stays local; with it the route saves ~1.9M THB at the midpoint (estimates from "
"listings, not quotes). ",
"The plan buys the two modules and the compressor from Thai suppliers (4.5M to 7M THB "
"installed for the boilers). Chinese makers with Thai references (ZOZEN, Sitong, Fangkuai, "
"Taiguo) could supply the boilers for roughly 2.8M to 5.5M THB installed, about 1.6M THB "
"less at the midpoint, and with a Chinese compressor brand bought through its Thai office "
"~1.9M less (estimates from listings, not quotes): a step if quotes land high. ") +
"The specification is a 1.25 MPa design to ASME Section I with the S stamp; most Chinese packaged "
"electric units are rated 0.4 to 0.8 MPa and cannot deliver 10 bar. An imported boiler must meet "
"TIS, ASME, JIS, EN or an equivalent standard and be verified by a certification body registered "
@@ -425,10 +488,12 @@ def blocks():
CALL("Efficiency in one paragraph, for the investor", "Steam and cooling are built once as a "
"backbone and filled with identical modules as presses arrive; heat is recovered from the "
"flue gas, the condensate and the press returns before more is generated; the roof carries PV "
"worth a quarter to a third of mature electricity use, with a battery for ride-through; no "
"worth a quarter to a third of mature electricity use" + pick(" (through a solar PPA)", "") +
", with a battery for ride-through; no "
"process water is discharged; a fifth of the plastic is reclaimed; and biomass steam, if "
"chosen, costs a quarter to a tenth of electric steam. The same choices push the energy line "
"toward its low end, hedge Thai tariff rises, and convert into BOI tax value (section 7). They "
"toward its low end, hedge Thai tariff rises, and convert into BOI tax value" + pick(
" through the Smart and Sustainable Industry measure", "") + " (section 7). They "
"also make the plant's sustainability story true rather than claimed, which matters to the "
"labels it sells to."),
@@ -458,8 +523,8 @@ def blocks():
["Neighbours", "Agricultural", "Industrial", "A pharmaceutical facility under construction on "
"the same land"],
], widths_mm=[28, 44, 44, 54], font_pt=8.5),
P("**Galvanics now raises the bar for every site.** With the electroplating line in the launch "
"plant, a plot has to permit record pressing with a steam boiler **and** electroplating from day "
P("**Galvanics now raises the bar for every site.** With the electroplating line in the plan, a "
"plot has to permit record pressing with a steam boiler **and** electroplating from day "
"one, not in a later phase. That favours the industrial estate (licensing through the estate, "
"shared effluent treatment) and makes zoning the first check everywhere else."),
P("**Payap University** approached the project and is interested in leasing land next to a "
@@ -543,25 +608,77 @@ def blocks():
["Packaging and finished-goods warehouse", "~300 to 500 m²"],
["Utility rooms (boiler, compressor, chiller, electrical)", "~100 to 150 m²"],
["Offices, QC and listening room, archive, IT, entrance", "~150 to 200 m²"],
["Maintenance workshop and machine bay (crane-served)", "~100 to 150 m²"],
["Visitor corridor (glazed, ground level)", "~110 to 130 m²"],
["Maintenance workshop and machine bay (" + pick("crane-ready", "crane-served") + ")",
"~100 to 150 m²"],
[pick("Visitor corridor (glazed, ground level; built later, its strip kept free)",
"Visitor corridor (glazed, ground level)"), "~110 to 130 m²"],
["Circulation, walls, miscellaneous", "~150 to 250 m²"],
["**Total under roof**", "**~1,400 to 2,000 m²**, against 1,500 m² built"],
], widths_mm=[110, 60],
caption="Estimates. The boiler and chillers have since moved to the detached utility yard, which "
"frees floor the workshop and corridor take; water treatment is proposed in its own "
"building next to galvanics; cassette and CD-R duplication (~40 to 80 m² and ~20 to "
"40 m²) are not in the table yet. At the lower half of the table everything fits in "
"40 m²" + pick(", a later step in this plan", "") + ") are not in the table yet. " + pick(
"", "The press rows size one Duplex system and the Classic; the second Duplex "
"system needs its own bay on top (~110 m² at the calculator's floor figure, "
"estimate). ") +
"At the lower half of the table everything fits in "
"1,500 m²; at the upper half, finished goods move to a second building."),
IMG("production-hall-layout-sketch.png", 170, "Figure 3. Rough layout sketch of the 60 × 25 m hall "
"(October 2026): machine door and workshop at the ramp end, then mastering, galvanics, stamper "
"prep, material area and lab, the press hall, inspection, QC and packaging along the glazed "
"visitor corridor, with the 5 t crane runway over the production compartment up to the "
"warehouse fire wall. It still draws two Classic positions (the launch plant installs one) and "
"visitor corridor" + pick(" (built later in this plan)", "") + ", with the 5 t crane runway "
"over the production compartment up to the warehouse fire wall. " + pick(
"It still draws two Classic positions (the plan installs one)",
"It still draws one Duplex system and two Classic positions (the plan installs two Duplex "
"systems and one Classic)") + " and "
"puts reception and the cutting room under the crane's travel, a conflict the Payap scenario "
"model resolves with a separate office building. All positions are estimates."),
P("Figure 4 shows the same plant as the calculator draws it for " + label() + ": the ground floor of a "
"whole site with the press hall cut open, the machines this plan installs and the free positions it "
"keeps for growth."),
IMG(pick("renders/calculator/plan-a-iso-gf.png", "renders/calculator/plan-b-iso-gf.png"), 170,
"Figure 4. The plant at a glance in the plant and business plan calculator, " + pick(
"Plan A preset: Duplex system 1 and the Classic in a press hall laid out for five Duplex bays "
"and four Classic positions, boiler modules B-1/B-2 and chillers CH-1/CH-2 (the free yard "
"positions are marked), the frame crane-ready with the crane rented",
"Plan B preset: Duplex systems 1 and 2 and the Classic in a press hall laid out for five Duplex "
"bays and four Classic positions, boiler modules B-1/B-2 and chillers CH-1 to CH-3 (the free "
"yard positions are marked), the 5 t crane and the cassette room") +
". Schematic render (docs/fleet-calculator.html, also at cnxvinyl.lol/demos/fleet-calculator/) of a "
"generic site after the Payap scenario model and the utility yard of scale.md, not the Mueang Chi "
"parcel of Figure 2; positions and envelopes are estimates, not to scale where the vendors publish "
"no footprint."),
H3("Crane, workshop and visitor corridor"),
B([
B(pick([
"**A crane-ready frame, with the crane rented at first.** The frame, the runway and the machine "
"door are built for a 5 t overhead crane (single-girder, ~24 m span, inverter drives, radio "
"remote) over the whole production compartment, roughly 40 to 48 m of the 60 m, stopping at "
"the fire wall to the finished-goods warehouse. For the installations a mobile crane is hired, "
"and the crane itself is bought later if machine moves and maintenance lifts make it worth it. "
"The building is shaped for it from the start: a frame designed for crane loads, no "
"full-height cross walls in the crane zone (rooms there are lidded boxes, with removable "
"hatches where heavy kit goes in), and the cutting room kept outside it, away from the frame's "
"vibration. 5 t covers the heaviest known lift (a Newbilt HRP 12 press, 1,350 kg) with margin "
"until Newbilt states the Classic's weight; 3.2 t is the alternative price.",
"**A machine door** of ~4.5 × 5.0 m at the ramp end, opened a few times a year. Until a crane "
"is bought, the hired crane sets each machine on skates at the door threshold; with the crane, "
"a truck reverses up the ramp into the machine bay and the crane lifts the machine off the bed "
"inside.",
"**A maintenance workshop** of ~100 to 150 m² beside it, where a bought crane would park: a "
"service bay for one press-sized machine, benches, a hydraulic hose crimper, a spares store "
"tracked in ERPNext, and a hot-work corner, held at negative pressure so its dust never "
"reaches the presses. Newbilt service comes from Germany and Pheenix from Sweden; spares on "
"the shelf are the uptime lever.",
"**A glazed visitor corridor, built later.** The plan runs without public tours at first, but "
"the route is designed in: the strip along the car-park wall stays free and the stations "
"follow the process order, from the cutting-room window to packaging. Built, it is ~45 to 55 m "
"long and ~2.4 m clear, with its own filtered air at slight positive pressure and acoustic "
"glazing that brings the 80 to 90 dB(A) near the presses down to roughly 40 to 50 dB(A) "
"(estimates), so a guide talks without a headset and visitors need no PPE; a 1:12 accessible "
"ramp serves the entrance. Until it is built there are no public tours, and the tour route and "
"the Payap programmes start later.",
], [
"**A 5 t overhead crane** (single-girder, ~24 m span, inverter drives, radio remote) runs over "
"the whole production compartment, roughly 40 to 48 m of the 60 m, and stops at the fire wall "
"to the finished-goods warehouse. Machines are moved in, out and around without hiring a mobile "
@@ -584,13 +701,14 @@ def blocks():
"headset and visitors need no PPE; a 1:12 accessible ramp serves the entrance. Tours for "
"artists, labels, investors and schools can run several times a day without stopping "
"production.",
]),
])),
H3("Utility yard and water-treatment building"),
T(["Detached utility yard (decided October 2026)", "Size (estimate)"], [
["Boiler hall, four 500 kg/h module positions (B-1 and B-2 at launch)", "~162 m² (18 × 9 m), "
"6.0 m high"],
["Biomass fuel store", "42 m²"],
["Chiller compound, open and screened, four pads (CH-1 and CH-2 at launch)", "~135 m² (12.5 × "
["Chiller compound, open and screened, four pads (" + pick("CH-1 and CH-2", "CH-1 to CH-3") +
" at launch)", "~135 m² (12.5 × "
"10.8 m)"],
["Cooling pump and heat-exchanger room", "~40 m² (8 × 5 m)"],
["Flue stack", "14 m"],
@@ -626,10 +744,16 @@ def blocks():
"plot's narrowing southern end with a two-bay sawtooth roof carrying ~251 kWp and a 20 m yard "
"between the two for fire separation and shared loading; coverage stays at 39% of the deed area. "
"It is where finished goods move to free production floor, where pressing capacity can double, "
"or where a CD replication line would go if that option is chosen (section 4.9). Where the "
"second Duplex system stands on the Mueang Chi footprint is not drawn yet; the Payap scenario "
"model draws the five-press fleet; its press hall is now planned for Duplex systems (five Duplex "
"bays and four Classic positions), because the plant focuses on special pressings."),
"or where a CD replication line would go if that option is chosen (section 4.10). **The press "
"hall** is planned for Duplex systems, because the plant focuses on special pressings and "
"specials run only on the Duplex: five Duplex bays and four Classic positions (~73/27 by floor, "
"estimate). " + pick(
"This plan installs one Duplex system and one Classic; growth adds Duplex systems first, into "
"bays whose floor, utility positions and ring-main tees are kept from the start. Where the "
"second Duplex system would stand on the Mueang Chi footprint is not drawn yet; ",
"This plan installs two Duplex systems and one Classic at launch; growth adds Duplex systems "
"first. Where the second Duplex system stands on the Mueang Chi footprint is not drawn yet; ") +
"the Payap scenario model draws the five-press fleet."),
H3("What the construction RFQ asks"),
P("The budget-level construction RFQ, with quantities from the parametric model, asks contractors "
"for: the shell rate per m² at the insulated-panel specification (not a bare shed); heavy-duty "
@@ -641,21 +765,29 @@ def blocks():
"PV, air conditioning, boilers, chillers and tanks, sprinklers, fit-out and the PEA transformer, "
"and asks whether permit drawings for the building permit and the factory licence are included."),
H3("What the building costs, and what is not known"),
T(["Block (launch plant)", "USD", "THB", "Basis"], [
T(["Block", "USD", "THB", "Basis"], [
["Building ~1,500 m² plus civil and site works", "650k to 1,100k", "21.4M to 36.3M",
"estimate at 12,000 to 20,000 THB/m² for Thai pre-engineered steel to the insulated-panel "
"specification, plus ~20% site works"],
["Overhead crane, runway, frame premium, machine door", "97k to 227k", "3.2M to 7.5M", "estimate"],
pick(["Crane-ready frame, runway and machine door; mobile crane rented for the installations",
"39k to 91k", "1.3M to 3.0M", "estimate: 40% of the crane block (USD 97k to 227k); the "
"crane is bought later if needed"],
["Overhead crane, runway, frame premium, machine door", "97k to 227k", "3.2M to 7.5M",
"estimate"]),
["Maintenance workshop and machine bay fit-out", "30k to 76k", "1.0M to 2.5M", "estimate; a "
"workshop lathe and CNC mill not priced"],
["Visitor corridor and accessible entrance", "67k to 148k", "2.2M to 4.9M", "estimate; "
"building it later is a lever worth ~4.1M THB"],
pick(["Visitor corridor and accessible entrance", "built later", "built later", "USD 67k to "
"148k (2.2M to 4.9M THB) when built, estimate; not in the plan price"],
["Visitor corridor and accessible entrance", "67k to 148k", "2.2M to 4.9M", "estimate; "
"building it later would take ~3.6M THB out at the midpoint"]),
["Utility yard for four boiler and four chiller modules", "73k to 122k", "2.4M to 4.0M",
"estimate at the building rate"],
["Water-treatment building next to galvanics", "52k to 87k", "1.7M to 2.9M", "estimate at the "
"building rate"],
["**Together**", "**~0.97M to 1.76M**", "**~32M to 58M**", "sum of the blocks, before the 15% "
"contingency (section 8)"],
pick(["**Together**", "**~0.84M to 1.48M**", "**~28M to 49M**", "sum of the blocks; the plan "
"carries no contingency (section 8)"],
["**Together**", "**~0.97M to 1.76M**", "**~32M to 58M**", "sum of the blocks; the plan "
"carries no contingency (section 8)"]),
], widths_mm=[50, 26, 26, 68], font_pt=8.5),
P("The single largest unknown is the ground. The Mueang Chi parcel is likely soft former paddy, and "
"a piled slab can add several hundred to 1,000+ THB/m² across the building, 4,387 m² of paving "
@@ -663,9 +795,13 @@ def blocks():
"before any real number, and the building RFQ with a soil test is one of the four requests that "
"settle the budget. Also open: the crane's hook height under the 7.0 m eave; the archive's "
"mobile shelving (to quote); the valved branches and feeders for the empty utility positions; "
"and the fire-water volume, which the hydraulic design and the warehouse racking height set. "
"Renting a crane for the installations while the frame stays crane-ready would save ~3.7M THB, "
"but that is a scope cut toward the 180M THB case, not part of the plan."),
"and the fire-water volume, which the hydraulic design and the warehouse racking height set. " +
pick("Renting a crane for the installations while the frame, runway and machine door stay "
"crane-ready takes ~3.2M THB out at the midpoint, one of the six levers; the 5 t crane itself "
"is USD 30k to 76k supplied and installed (estimate) when it is bought.",
"Renting a mobile crane for the installations while the frame, runway and machine door stay "
"crane-ready would take ~3.2M THB out at the midpoint (estimate): a step if quotes land "
"high, not part of the plan.")),
H2("6.3 Safety, security and the irreplaceable"),
P("Worker safety is a founding priority on equal footing with the environmental commitments. The "
@@ -686,7 +822,8 @@ def blocks():
"THB a head a year for insurance, ~2,500 for the check-up plus ~1,500 for risk-factor tests, and "
"workwear ~USD 9k a year at ~20 staff."),
P("Physical security is zoned: production, galvanics, chemical storage, the metal archive, IT, "
"shipping and the visitor corridor each get badge or biometric access with logs; CCTV at "
"shipping and " + pick("(once built) ", "") + "the visitor corridor each get badge or biometric "
"access with logs; CCTV at "
"entrances, perimeter, dock, production, chemicals and archive under a PDPA-compliant retention "
"policy. Two things are irreplaceable and get their own protection: **the metal archive** (a "
"destroyed father means recutting and re-plating a release; fire-rated room, climate control in "
@@ -709,7 +846,9 @@ def blocks():
"pressing is basic plastic consumer-goods manufacturing (incentive Group B): no corporate income "
"tax holiday on the core activity, but the full non-tax package and machinery duty exemption. "
"The tax value is engineered on top through measures where this project happens to be strong, so "
"the application is pitched on solar, automation and technology content, not on pressing plastic "
"the application is pitched on " + pick("automation, technology content and energy efficiency",
"solar, automation and technology content") +
", not on pressing plastic "
"discs. The research behind this dates from July 2026, BOI revises its measures roughly every "
"year, and the exact activity sub-code needs confirmation by a BOI-registered consultant before "
"filing."),
@@ -718,7 +857,9 @@ def blocks():
"(Germany, Sweden, the UK, Australia, Japan, China). Machinery imported duty-free is also "
"exempt from VAT at import (Revenue Department answer on BOI's FAQ, checked October 2026). The "
"investment blocks assume promotion: without it, ~5% duty on ~2.3M USD of imported machinery "
"(~0.11M USD) plus ~0.17M USD of import VAT, reclaimed only later (estimates)."],
"(~0.11M USD) plus ~0.17M USD of import VAT, reclaimed only later (estimates for the " +
pick("full equipment list; this plan imports less at first",
"three-press equipment list; the second Duplex system adds to both") + ")."],
["100% foreign ownership and land privileges", "Overrides Foreign Business Act restrictions "
"for the promoted activity. The land-ownership privilege is unused under the lease strategy."],
["Visas and work permits via the One Stop Service", "Foreign commissioning engineers and the "
@@ -728,7 +869,12 @@ def blocks():
["Solar and battery measure (Announcement 2/2568, April 2025)", "CIT exemption based on the "
"solar investment; PV modules, inverters, battery and battery inverters, and mounting are "
"eligible, capped at THB 12M, 30M or 40M of investment per MW depending on investment type. "
"It rewards a plant-owned array, which matters if the solar PPA lever is used (section 5.4)."],
"It rewards a plant-owned array. " + pick(
"This plan takes the PV and the battery through a solar PPA, so the installer owns them "
"and the plant very likely gives up this measure (to confirm with the BOI consultant; "
"section 5.4).",
"This plan buys the ~247 kWp array and the 271 kWh battery, so their cost is the base of "
"the exemption (section 5.4).")],
["Smart and Sustainable Industry measure (Announcement 15/2565, refreshed July 2025)",
"Rewards automation, smart machinery and energy efficiency, typically 3 years of CIT exemption "
"capped around 50% of the qualifying upgrade spend. The automatic Classic, inline inspection, "
@@ -744,7 +890,9 @@ def blocks():
"machinery and raw materials lives, from the first capex invoice."),
P("**Process:** file via the e-Investment system and present at an analyst meeting. BOI quotes "
"about 40 working days to a decision for projects under THB 200M and 60 to 90 working days for "
"larger tiers; at ~161M to 292M THB the launch plant sits around that line, so the consultant "
"larger tiers; at " + pick("~127M to 226M THB with the ramp-up reserve (~177M at the midpoint)",
"~162M to 286M THB with the ramp-up reserve (~224M at the midpoint)") +
" the plan sits around that line, so the consultant "
"confirms which applies. In practice it is roughly 4 to 6 months end to end to the certificate; "
"the company is incorporated under it; machinery is imported within a window commonly 30 months "
"from issuance; full operation is due by about 36 months. The Northern Economic Corridor "
@@ -754,7 +902,7 @@ def blocks():
H2("7.2 Factory licence, galvanics and boilers"),
P("BOI promotion does not replace the factory licence (Ror.Ngor. 4), hazardous-substance "
"registration for the plating chemistry, wastewater permitting or the boiler rules. **Galvanics "
"is now in the launch plant, so the electroplating line, its chemistry and its zero-discharge "
"is in the plant from launch, so the electroplating line, its chemistry and its zero-discharge "
"effluent system are in the first factory licence application**, not a later amendment. The "
"earlier plan to keep galvanics out of the first licence to protect the schedule no longer "
"applies. Effluent is the most likely regulatory friction point regardless of BOI, and it now "
@@ -772,8 +920,9 @@ def blocks():
"engineer; the construction RFQ asks whether contractors include them (shell prices usually do "
"not), and stormwater attenuation may be a permit condition.",
]),
P("**Boilers (DIW rules).** The Ministerial Regulation on boilers B.E. 2549 and the matching "
"notification on boiler safety of the same year apply to every module, Thai or imported: a design "
P("**Boilers (DIW rules).** The Ministerial Regulation on boilers B.E. 2549 and the Ministry of "
"Industry notification on boiler safety of the same year apply to every module, Thai or "
"imported: a design "
"to TIS or an international standard (ASME, JIS, EN or equivalent) checked and certified by a "
"DIW-registered certification body or licensed engineer; for an imported boiler, verification by "
"a registered certification body and the manufacturer's data report, design and inspection "
@@ -781,11 +930,18 @@ def blocks():
"reported to DIW within 30 days; **a licensed boiler controller on site**; and **inspection at "
"least once a year**. The relief for small once-through units does not apply at ~10.5 bar (our "
"reading), and the labour-side regulation of 2021 adds licensed-engineer installation and "
"testing and a feedwater standard. A Chinese boiler built to the Chinese GB code would rest on "
"'or equivalent' with no precedent found, hence the ASME specification. DIW has announced a "
"testing and a feedwater standard. " + pick(
"**This plan's two boiler bodies come from a Chinese maker, so they take the imported-boiler "
"route of the notification:** verification by a registered certification body with the full "
"dossier, and an ASME Section I design at 1.25 MPa with the S stamp, because a boiler built "
"to the Chinese GB code would rest on 'or equivalent' with no precedent found. ",
"The plan's modules come from Thai suppliers to the same ASME specification; a Chinese "
"boiler built to the Chinese GB code would rest on 'or equivalent' with no precedent found. ") +
"DIW has announced a "
"revision of the boiler, pressure-vessel and refrigeration rules, so they are rechecked before "
"the RFQ, and a certification body is engaged before any boiler order."),
P("**Other permits and duties:** the same 2021 labour regulation covers the overhead crane (a "
P("**Other permits and duties:** the same 2021 labour regulation covers the overhead crane" +
pick(" once one is bought", "") + " (a "
"crane-industry summary puts cranes above 3 t on an engineer inspection every three months; to "
"confirm against the text before the capacity is chosen); hazardous-substance registration and "
"locked, inventoried storage for silver nitrate and nickel salts; the occupational health "
@@ -797,9 +953,13 @@ def blocks():
"operation) start at the certificate, and the margin on them is consumed by one scenario: "
"filing early and then finding the site does not work. Verify the chosen plot's zoning, "
"including plating, first.",
"**Scope.** Cassette and CD-R duplication go into the application from the start. CD "
"replication would need new activity codes, which cannot be added casually afterwards, so the "
"investor's decision on CD (section 4.9) is needed before the filing date.",
"**Scope.** " + pick(
"Cassette and CD-R duplication come later in this plan; whether they still go into the "
"first application (their machines then due inside its import window) or into a later "
"amendment is a question for the consultant. ",
"Cassette and CD-R duplication go into the application from the start. ") +
"CD replication would need new activity codes, which cannot be added casually afterwards, so the "
"investor's decision on CD (section 4.10) is needed before the filing date.",
"**Sequencing.** The plan's rule is that no machinery purchase order precedes the certificate, "
"because duty exemption is not retroactive. But the exemption attaches at importation, not at "
"contract signature. If equipment can be ordered with a deposit while the application is in "
File diff suppressed because it is too large. Load diff
+19 -17
View File
@@ -1,5 +1,5 @@
"""Export CNX-Vinyl-Business-Plan.docx to PDF with LibreOffice, filling in the table of contents.
"""Export CNX-Vinyl-Business-Plan-A.docx and -B.docx to PDF with LibreOffice, filling in the table of contents.
Run with LibreOffice's own Python (it ships the uno module):
"C:/Program Files/LibreOffice/program/python.exe" export_pdf.py
@@ -8,8 +8,8 @@ A plain `soffice --convert-to pdf` leaves the Word TOC field unfilled.
import uno, sys, time, subprocess, os
from pathlib import Path
HERE = Path(__file__).resolve().parent
DOCX = HERE.parent / "CNX-Vinyl-Business-Plan.docx"
PDF = HERE.parent / "CNX-Vinyl-Business-Plan.pdf"
DOCXS = [HERE.parent / f"CNX-Vinyl-Business-Plan-{p}.docx" for p in ("A", "B")]
DOCXS = [d for d in DOCXS if d.exists()]
from com.sun.star.beans import PropertyValue
def pv(n, v):
p = PropertyValue(); p.Name = n; p.Value = v; return p
@@ -24,20 +24,22 @@ for i in range(60):
time.sleep(0.5)
smgr = ctx.ServiceManager
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
src = uno.systemPathToFileUrl(str(DOCX))
doc = desktop.loadComponentFromURL(src, "_blank", 0, (pv("Hidden", True),))
idx = doc.getDocumentIndexes()
print("indexes", idx.getCount())
for i in range(idx.getCount()):
idx.getByIndex(i).update()
doc.refresh()
# update fields (page numbers)
doc.getTextFields().refresh()
for i in range(idx.getCount()):
idx.getByIndex(i).update()
out = uno.systemPathToFileUrl(str(PDF))
doc.storeToURL(out, (pv("FilterName", "writer_pdf_Export"),))
doc.close(True)
for DOCX in DOCXS:
PDF = DOCX.with_suffix(".pdf")
src = uno.systemPathToFileUrl(str(DOCX))
doc = desktop.loadComponentFromURL(src, "_blank", 0, (pv("Hidden", True),))
idx = doc.getDocumentIndexes()
print(DOCX.name, "indexes", idx.getCount())
for i in range(idx.getCount()):
idx.getByIndex(i).update()
doc.refresh()
# update fields (page numbers)
doc.getTextFields().refresh()
for i in range(idx.getCount()):
idx.getByIndex(i).update()
out = uno.systemPathToFileUrl(str(PDF))
doc.storeToURL(out, (pv("FilterName", "writer_pdf_Export"),))
doc.close(True)
try:
desktop.terminate()
except Exception:
+36
View File
@@ -0,0 +1,36 @@
"""Which version of the business plan is being built (October 2026).
Plan A, ~180M THB: the launch plant (1 Duplex system + 1 Classic, every machine) with six cost levers.
Plan B, ~225M THB: five presses (the second Duplex system at launch), everything in.
Both are priced without contingency and with the ramp-up reserve
(docs/finance/total-investment.md, "Two plans").
build_plan.py sets PLAN before it calls the content modules' blocks(); call pick() and the
helpers inside blocks(), never at import time.
"""
PLAN = "A"
NAME = {"A": "Plan A", "B": "Plan B"}
PRICE = {"A": "~180M THB", "B": "~225M THB"}
def is_a() -> bool:
return PLAN == "A"
def is_b() -> bool:
return PLAN == "B"
def pick(a, b):
"""The Plan A or the Plan B variant of a value, text or list of blocks."""
return a if PLAN == "A" else b
def name() -> str:
return NAME[PLAN]
def label() -> str:
"""e.g. 'Plan A (~180M THB)'."""
return f"{NAME[PLAN]} ({PRICE[PLAN]})"
+4 -3
View File
@@ -3743,14 +3743,15 @@ function renderBudget(e) {
`<div class="bgap">against ${M(B1)}: ${gap(tot, B1)} · against ${M(B2)}: ${gap(tot, B2)}</div>`;
}).join("");
const mid = V[1], mt = mid.cap + mid.res, rLo = cx.res.lo * fx, rHi = cx.res.hi * fx;
const tgt = mt <= B1 ? B1 : B2; // the tightest budget the midpoint fits (Plan A: 180M, Plan B: 225M)
$("budK").innerHTML = kpi(M(mid.cap) + " THB", `investment at the midpoint, ${cx.ctgIn ? "contingency included" : "without contingency"}`) +
kpi(`${M(mid.cap + rLo)} to ${M(mid.cap + rHi)}`, `THB with the ramp-up reserve (${M(rLo)} to ${M(rHi)})`) +
kpi(mt <= B2 ? M(B2 - mt) : "+" + M(mt - B2), `${mt <= B2 ? "to spare under" : "over"} ${M(B2)} THB at the midpoint${inB ? ", reserve included" : ""}`);
const verdict = mt <= B2 ? `Mid fits ${M(B2)} with ${M(B2 - mt)} to spare` : `Mid is ${M(mt - B2)} over ${M(B2)}`;
kpi(mt <= tgt ? M(tgt - mt) : "+" + M(mt - B2), `${mt <= tgt ? "to spare under" : "over"} ${M(tgt)} THB at the midpoint${inB ? ", reserve included" : ""}`);
const verdict = mt <= tgt ? `Mid fits ${M(tgt)} with ${M(tgt - mt)} to spare` : `Mid is ${M(mt - B2)} over ${M(B2)}`;
const detail = inB
? `the investment ${M(mid.cap)} plus the ramp-up reserve, ${M(mid.cap + rLo)} to ${M(mid.cap + rHi)} THB with the reserve at ${M(rLo)} to ${M(rHi)}`
: `the investment ${M(mid.cap)} THB without the reserve; with it, ${M(mid.cap + rLo)} to ${M(mid.cap + rHi)}`;
const vs180 = mt <= B1 ? `it fits ${M(B1)} too, with ${M(B1 - mt)} to spare` : `against ${M(B1)} it is ${M(mt - B1)} over`;
const vs180 = mt <= B1 ? `against ${M(B2)} it has ${M(B2 - mt)} to spare` : `against ${M(B1)} it is ${M(mt - B1)} over`;
$("budV").innerHTML = `<div class="flag ${mt <= B2 ? "ok" : "bad"}"><b>${verdict}</b>: ${detail}; ${vs180}. ` +
`The low end is ${M(V[0].cap + V[0].res)}, the high end ${M(V[2].cap + V[2].res)} THB.</div>`;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 KiB

+5 -2
View File
@@ -8,8 +8,11 @@ Rough durations are estimates.
These three run in parallel and gate everything downstream:
1. **Market validation**: test the 3.50 to 4.60 USD blended pressing price
and regional demand with Asian labels and distributors, starting at
1. **Market validation**: test the blended pressing price (3.76 to 5.27
USD at the planned 60 % specials; 3.50 to 4.60 at a standard-heavy mix)
and regional demand, which decides between Plan A (~0.74M records a year)
and Plan B (~1.25M) in [total investment](finance/total-investment.md#two-plans-a-at-180m-thb-and-b-at-225m-thb-october-2026),
with Asian labels and distributors, starting at
Making Vinyl in Haarlem (Alexander and Simona). This is the biggest
assumption in the [revenue model](finance/revenue-model.md); the
financing case waits on it. **Partially started**: a first buy-side