Add investor business plan (draft 0.1) with its Python builder

Leased land instead of purchase, in-house cassette duplication, CD-R
duplication with CD replication shown as a separate investor decision,
Ploy Printing as packaging partner and intended co-investor, and a
dedicated section on traceability, metrology, QC and efficiency.
This commit is contained in:
grabowski committed 2026-09-10 10:20:05 +02:00
1 parent 1bbef055c4
commit 3934dc1dc4
7 files changed
+1860

No files matched your search

+10
View File
@@ -110,11 +110,21 @@ The detail lives in dedicated documents:
- [Revenue model](docs/finance/revenue-model.md): rough full-capacity
revenue (~3.7M to 6.8M USD/yr), direct cost and margin per record, and
the levers and caveats behind the numbers
- [Business plan](docs/business-plan/CNX-Vinyl-Business-Plan.docx): the
investor-facing roll-up of everything above, built from
`docs/business-plan/build/` (run `python build_plan.py` to regenerate
after the numbers move). Draft 0.1 carries the land-lease decision, the
cassette and CD scope, and Ploy Printing as packaging partner and
intended co-investor
## Repository Layout
```
docs/
├── business-plan/
│ ├── CNX-Vinyl-Business-Plan.docx Investor business plan (generated)
│ └── build/ Python source of the plan; regenerate with
│ build_plan.py
├── consulting/ Consultancy proposals and overviews
├── equipment/
│ ├── newbilt/ Offers, site requirements, process schematic,
Binary file not shown.
+33
View File
@@ -0,0 +1,33 @@
"""Build the CNX Vinyl business plan .docx.
python build_plan.py -> ../CNX-Vinyl-Business-Plan.docx
python build_plan.py out.docx -> custom path
Images are resolved relative to docs/ (the parent of business-plan/).
"""
from __future__ import annotations
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE))
from plan_lib import Renderer # noqa: E402
import content_a, content_b, content_c # noqa: E402
DOCS = HERE.parent.parent # .../docs
OUT = HERE.parent / "CNX-Vinyl-Business-Plan.docx"
def main(out: Path = OUT) -> Path:
r = Renderer(DOCS, header_text="CNX Vinyl / Poy Records: Business plan, draft 0.1, September 2026",
footer_text="Confidential")
r.render(content_a.blocks() + content_b.blocks() + content_c.blocks())
r.save(out)
return out
if __name__ == "__main__":
target = Path(sys.argv[1]) if len(sys.argv) > 1 else OUT
print(main(target))
+522
View File
@@ -0,0 +1,522 @@
"""Part A: cover, reading guide, executive summary, opportunity, anatomy of the plant."""
from plan_lib import H1, H2, H3, P, B, N, T, KV, IMG, CALL, PB, TOC, COVER
def blocks():
out = []
# ------------------------------------------------------------ cover
out.append(COVER(
kicker="CNX VINYL / POY RECORDS",
title="What It Takes to Build a Top-Notch Vinyl Plant",
subtitle="Business plan for a full-service vinyl, cassette and CD manufacturing plant "
"in the Chiang Mai region, Northern Thailand",
image="renders/aerial-hero.png",
lines=[
"**Prepared for:** the project's lead investor",
"**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.1, September 2026",
],
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.",
))
out.append(TOC())
# ------------------------------------------------ 1 executive summary
out += [
H1("1. Executive summary"),
P("CNX Vinyl (label name Poy Records) is a planned record manufacturing plant for the Chiang Mai "
"region of Northern Thailand. Its premise is simple: a label or artist in Asia-Pacific should be "
"able to send an audio file to one place and receive finished, packaged, fully traceable records at "
"their door, without shipping a fragile lacquer disc across the world and without waiting for metal "
"stampers to come back from Europe. Today that customer presses in Europe, the United States or "
"Japan. The one operating pressing shop in Thailand presses only: it buys its stampers abroad, runs "
"without climate control, and cannot economically serve runs under 300 units."),
P("This document explains, for a reader who is not a vinyl specialist, what a top-notch plant "
"consists of, why each part matters, what it costs, what it can earn, how long it takes, and what "
"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."),
H2("What we will build"),
B([
"**The complete vinyl chain under one roof:** mastering, lacquer cutting, galvanics (making the "
"nickel stampers), pressing, finishing, packaging with our Chiang Mai print partner Ploy "
"Printing, and fulfilment including direct-to-fan dropshipping.",
"**A mixed press fleet:** semi-automatic presses for colour, splatter and short runs, fully "
"automatic presses for volume. Launch with one semi-automatic line (90,000 to 135,000 records a "
"year); grow to four presses (1.0 to 1.5 million records a year).",
"**Cassette duplication in-house** and **short-run CD duplication** from launch. Full CD "
"replication (injection-moulded discs) is a separate, much larger decision; both CD options are "
"priced in section 4.9 for the investor to decide.",
"**A purpose-built 1,500 m² plant on long-term leased land**, with a detached utility yard, "
"a rooftop solar array, and room for a second building. The land is now planned as a "
"registered long lease rather than a purchase, which lowers upfront capital by roughly "
"USD 0.4 to 1.0 million.",
]),
H2("What makes it top-notch"),
P("Four things separate this plant from a pressing shop, and they are the reason it can charge "
"export-grade prices in a region that currently has none:"),
N([
"**Traceability from audio file to shipment.** Every lacquer, father, mother, stamper, press "
"cycle, compound lot and carton lot gets an identity that knows its parents. A customer sees "
"their order's full chain; a complaint resolves to one stamper, one press, one shift and one "
"material lot. Machine data comes from the machines, not from paperwork.",
"**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 "
"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 (the presses' own "
"60 °C cooling return pre-heats the boiler feedwater), ~290 kWp of rooftop solar at phase 1 "
"grows to ~500 kWp with the second building, a 271 kWh battery shaves peaks, cooling runs as a "
"closed loop, no process water is discharged, and 20% of the feedstock is in-house regrind. One "
"operator runs up to four automatic presses.",
"**Climate control as product quality.** The whole plant runs at 20 to 26 °C, 35 to 60% "
"relative humidity, with particulate filtration sized for Northern Thailand's burning season. "
"Moisture and dust are the two classic causes of noisy records; the incumbent has neither "
"control.",
]),
H2("The numbers"),
KV([
["Capacity", "Launch ~90k to 135k records/yr (1 semi-automatic line, 1 shift); mature ~1.0M to "
"1.5M records/yr (2 semi-automatic + 2 automatic presses, 2 shifts)"],
["Site", "~1,500 m² main building plus ~180 m² utility yard on ~5 to 8 rai of leased land; "
"second 1,710 m² building as expansion"],
["Investment, full vision", "~USD 4.4M to 8.0M (~THB 144M to 264M) including 15% contingency "
"(was 4.7M to 8.9M before the decision to lease the land)"],
["Investment, phase 1 launch", "~USD 2.2M to 3.9M (~THB 73M to 128M) including the building, "
"one press line, climate control, rooftop solar, cassette and CD-R "
"lines, working capital and contingency"],
["Land lease", "Estimated THB 0.3M to 1.0M per year (USD 9k to 30k), to be replaced by the "
"negotiated figure"],
["Revenue at maturity", "~USD 3.7M to 6.8M per year (~THB 122M to 224M)"],
["Operating result at maturity", "Central case EBITDA ~USD 2.6M, operating profit ~USD 2.0M, "
"payback 2 to 4 years. Adverse case is EBITDA-positive but "
"does not cover depreciation. Utilisation and price "
"realisation decide the outcome, not upside."],
["First paying release", "Q4 2028 to mid-2029 if the gates below start now; one regulatory "
"question (ordering machinery before the BOI certificate) could pull "
"that forward by 6 to 9 months"],
["People", "~15 to 25 at maturity across shifts, fewer than half that at launch"],
]),
H2("Where we are, and what we ask"),
P("The project is at the planning stage. Industry outreach has run for several years; vendor quotes "
"exist for the pressing line, utilities have been costed with Thai suppliers, a parametric building "
"model has produced quantities for a construction price request, and a candidate parcel in Mueang "
"Chi, Lamphun has been tested against the design. Nothing has been ordered."),
P("Three gates run now and in parallel: **market validation** with regional labels and at the Making "
"Vinyl trade show in Haarlem (autumn 2026); the **BOI investment promotion** track with a "
"registered consultant; and **land**: zoning verification, lease terms, and a soil test. Nothing "
"large is spent until its gate clears."),
CALL("What the investor is asked to fund now", [
"**Phase 0 (next 3 to 6 months): ~USD 40k to 80k** for the BOI consultant, Thai legal work on "
"the lease and zoning, a soil boring test, the trade-show trip, firm re-quotes from the press, "
"galvanics and building vendors, and optional specialist consultancy.",
"**Phase 1 (commitment after the BOI certificate and validated pricing): ~USD 2.2M to 3.9M** "
"released in tranches against milestones: lease signed, press order, building contract, "
"fit-out, commissioning.",
"**Decisions needed from the investor:** CD scope (duplication only, or full replication), "
"whether galvanics moves into phase 1, and the equity/debt mix (BOI requires debt-to-equity "
"of at most 3:1).",
"**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.",
]),
# ----------------------------------------------- 2 reading guide
H1("2. How to read the numbers"),
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"], [
["Quoted", "A vendor document exists, in its original currency. Quotes may have expired and "
"need re-confirmation.", "Newbilt pressing line (EUR 200,500, June 2024); Newbilt "
"service rates; Gobel battery list price"],
["Researched", "Published or market data gathered by the team, including one set of real Thai "
"invoices.", "Thai utility supplier ranges; Pheenix AD12 datasheet; domestic "
"pressing prices, August 2026; BOI measures"],
["Estimate", "Engineering ratio or industry norm with no vendor confirmation. Treat as "
"plus or minus 30 to 50%.", "Building cost per m²; galvanics line; mastering "
"room; metrology stack; land lease; 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); "
"a signed customer; a signed lease; firm quotes for the automatic presses, the galvanics line, the "
"mastering room or the building. Each of these is a named next step in section 14."),
# ------------------------------------------------- 3 opportunity
H1("3. The opportunity"),
H2("3.1 The product"),
P("A vinyl record is made by cutting the audio into a soft lacquer disc, growing a set of nickel "
"metal parts from that lacquer by electroplating (the 'stampers'), and pressing hot PVC between two "
"stampers under about 150 tonnes of force with steam heating and water cooling. The record is then "
"trimmed, labelled, sleeved, put into a printed jacket, shrink-wrapped and shipped. Each of these "
"steps has its own equipment, skills and failure modes, and most plants in the world do only some "
"of them, sending the rest out to specialists, often on other continents."),
P("Demand for vinyl has grown for well over a decade from a small base. The 2021 to 2022 boom "
"pushed lead times at European and US plants to six to nine months; they have since normalised to "
"roughly 10 to 16 weeks for a standard black LP, and global capacity has been added. Demand is "
"cyclical and this plan does not assume it only rises; it assumes the plant can sell what it "
"presses, which is the single assumption most worth testing (section 3.3)."),
H2("3.2 Where regional labels press today"),
P("Labels and artists in Southeast Asia, Australia and much of East Asia ship their masters to "
"Europe, the United States or Japan, wait in those plants' queues, and pay freight and time zones "
"on top. There is no industrial-grade, climate-controlled, full-service plant in the region. That "
"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 and "
"the market validation gate exists to test it before capital is committed."),
H2("3.3 What the Thai market shows: one real data point"),
P("In August 2026 the team compared real invoices from the one operating pressing shop in Thailand, "
"ResurRec in Bangkok (with whom a collaboration is also under discussion), and toured its plant. "
"This is one buyer, one plant, one afternoon, and it is deliberately not used to re-base the "
"financial model. But it is the only real market evidence so far, and it points in a useful "
"direction."),
T(["Item", "THB", "USD", "Note"], [
["Audio mastering", "5,000 to 20,000 per song", "150 to 605", "Market range from several engineers"],
["Stamper set (father, mother, silvering)", "up to 22,000 per title", "up to 665",
"Residual between a full-service and a pressing-only invoice; an upper bound that also carries "
"cutting, test pressings and margin"],
["All-in per 12-inch record at 300 units", "650 to 950", "19.70 to 28.80",
"Includes a printed jacket; discounts of 5% at 500, 15% at 1,000 to 3,000, 30% above 3,000, "
"with 2 to 4 weeks added lead time above 3,000"],
["Minimum run", "150 units, 'rarely accepted'", "", "Sub-300 runs are effectively unavailable "
"in Thailand"],
], widths_mm=[42, 36, 24, 68],
caption="Researched: buy-side invoices and published discount ladder, August 2026."),
P("What the tour showed: the incumbent 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, one fully automatic "
"and one semi-automatic, which is a real capital fleet that on paper out-presses our launch "
"configuration two to three times over."),
P("Four readings follow, each with its caveat:"),
B([
"**Domestic willingness to pay is higher than our model assumes.** The plan prices a record at "
"USD 3.50 to 4.60 blended (Western export benchmark). 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. The "
"caveat: a sole domestic supplier can hold an import-parity price that a second entrant "
"compresses. We kept the conservative price until a second data point exists.",
"**The floor of the market is unserved.** Runs under 300 are uneconomic for a shop whose "
"per-title cost is an imported stamper plus outsourced mastering. In-house cutting and plating "
"collapse that fixed cost, which makes short runs viable for CNX in a way they structurally are "
"not for the incumbent.",
"**Lead time degrades with run size, probably because of metalwork.** Three thousand units is "
"about two days of pressing on their fleet, yet they add two to four weeks. A stamper lasts "
"1,000 to 2,000 pressings, so a 3,000-unit run needs two or three sets, each an overseas order. "
"Jacket printing or floor space could produce the same symptom, and the cause should be "
"confirmed before it is treated as a structural weakness.",
"**The gap we close is capability, not capacity.** No plating, no climate control, no metrology, "
"no traceability. The incumbent sells commercially from that room today, which bounds how much "
"the Thai market currently pays for the difference. Our bet is on export-grade customers and on "
"regional labels who currently ship to Europe.",
]),
CALL("Investor takeaway", "CNX should not fight a price war at 200-unit runs against an operator "
"with low rent and no climate-control load. It competes on what a non-plating, unconditioned "
"shop cannot offer at any run size: export-grade quality control and traceability, titles that "
"need plating, consistent output through the burning season, repress in days from an archived "
"mother, and short runs. A stamper-supply relationship with the incumbent is a plausible early "
"revenue line for the galvanics room and is under discussion."),
H2("3.4 Cassettes and CDs"),
P("The investor has asked for cassette and CD capability on the site. Both fit the same customer: "
"the independent label that presses 300 to 2,000 records also orders 100 to 500 cassettes and a "
"short run of CDs for the same release, and prefers one supplier, one artwork approval and one "
"shipment. Cassette sales have grown from a very low base in the US and UK over the last decade; "
"volumes are small and the equipment market is mostly refurbished, but the format is a natural "
"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. Section 4.8 and 4.9 lay out both formats and the two very different "
"CD routes."),
# ------------------------------------------ 4 anatomy of the plant
H1("4. Anatomy of a top-notch plant: the full chain"),
P("The chain from audio file to delivered record has nine stages and three points where the artist or "
"label must approve before work continues. Every stage writes to the traceability database, and "
"the instruments behind every check are specified in section 5. The chart on the next page shows "
"the whole chain; the table after it is the checklist of what a top-notch plant needs and where "
"this plan puts each item."),
PB(),
IMG("process-flow.png", 138, "Figure 1. Process flow from audio file to delivered product, with every "
"check and the three artist approval gates. Source: project repository."),
PB(),
H2("4.0 The checklist"),
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", "Agnew Analog Type 612 lathe and Type 602 head, "
"in-house at maturity; brokered through a European partner at launch", "Phase 5 (earlier if "
"demand shows)"],
["In-house galvanics with zero-discharge water", "The regional bottleneck; enables repress in "
"days and short runs", "Digital Matrix plating line, clean room, evaporator-based water "
"recycling", "Phase 5; case for moving to phase 1 under review"],
["Stamper preparation machines", "Turns a raw nickel shell into a press-ready part",
"Sibert punch, back-finisher, coiner", "With galvanics"],
["A mixed press fleet", "Automatic for volume, semi-automatic for colour and short runs",
"Newbilt Single growing to Duplex; two automatic presses (Pheenix AD12 or Newbilt Classic)",
"Phase 1, then 5"],
["Steam, cooling and compressed air engineered locally", "The largest energy loads; efficiency "
"lives here", "Thai-sourced packages with heat recovery designed in", "Phase 1"],
["Whole-plant climate and particulate control", "Moisture and dust are the two classic record "
"defects", "20 to 26 °C, 35 to 60% RH, F7 to F9 filtration, HEPA in tight zones", "Phase 1"],
["Metrology for the metal parts", "Data-backed accept/reject before a stamper is mounted",
"Laser confocal, wide-area profiler, XRF thickness, inline vision", "Phase 5; basic at launch"],
["Layered QC for every record", "Catch defects at line speed without playing every record",
"100% optical inspection, per-cycle telemetry, sampled audio comparison", "Basic at phase 1, "
"full at phase 5"],
["Traceability from file to shipment", "Complaints resolve backwards; customers see their "
"order; machine data access is a purchasing condition", "Batch genealogy database, PLC data "
"capture, customer portal", "Phase 1 (v1)"],
["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", "Phase 1"],
["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", "Phase 1"],
["An archive for masters and mothers", "Irreplaceable metal; repress economics",
"Fire-rated, climate-controlled room with clean-agent suppression", "Phase 1"],
["Renewable energy and closed-loop water", "Cost, resilience, brand, BOI tax value",
"~290 kWp PV, 271 kWh battery, closed cooling loop, zero process discharge", "Phase 1 to 2"],
["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", "Phase 1"],
["Cassette and CD lines", "Same customers, one order, one shipment", "In-house cassette "
"duplication; CD-R duplication; CD replication as a separate option", "Phase 1 (replication "
"if chosen: phase 2)"],
], widths_mm=[40, 45, 55, 30], font_pt=8.5),
H2("4.1 Mastering and lacquer cutting"),
P("**What happens.** A mastering engineer prepares the audio for vinyl (levels, side length, "
"low-frequency handling), then a cutting lathe drives a heated stylus into a lacquer-coated "
"aluminium disc, cutting the groove in real time. The lacquer is soft nitrocellulose: heat- and "
"shock-sensitive, and it wants plating within one to three days. Lacquers do not travel well."),
P("**What it needs.** A disk mastering lathe and stereo cutter head, matched cutting amplifiers, a "
"mastering console and chain, a preview and pitch automation system, vacuum for platter hold-down "
"and chip suction, a groove inspection microscope, and a low-vibration, temperature-controlled "
"room away from the presses and compressor. Consumables are lacquer blanks and styli."),
P("**Who supplies it.** Agnew Analog Reference Instruments (Greece) builds the Type 612 lathe, "
"unveiled in 2023 and billed as the first all-new lacquer lathe since the 1960s, with the Type 602 "
"cutter head; bespoke, low-volume manufacturing with long build times. Cutter amplifiers, console, "
"preview automation and lacquer supply are open items."),
P("**Cost and phasing.** Estimated USD 290k to 580k for the room (bespoke market, no public "
"pricing). At launch, mastering is done by an external engineer and cutting is brokered through "
"Stamper Discs' European partners, so that the lacquer is cut and plated in the same region and "
"only nickel travels. Mastering is labour-bound: an engineer masters perhaps two to four songs a "
"day, which caps the title-services line and is discussed in section 9."),
H2("4.2 Galvanics: silvering and electroforming"),
P("**What happens.** The lacquer is cleaned and sprayed with a conductive silver layer, then "
"electroplated with nickel to grow a negative metal copy, the 'father'. From the father a positive "
"'mother' is grown (playable, and archived), and from the mother the working 'stampers' that go "
"into the press. This three-step process means a fresh stamper can be pulled from an archived "
"mother in a day or two; a cheaper one-step stamper is single-use and a repress restarts at "
"cutting."),
P("**Why it is the key to the region.** No plating capability has been found in Thailand; the one "
"operating plant buys its stampers abroad. Every title in the country therefore waits on an "
"overseas order, which is the most plausible cause of the incumbent's lead-time penalty on larger "
"runs and its inability to take small ones. Bringing cutting and plating in-house takes an "
"estimated 1.5 to 5 weeks out of the lead time and USD 175 to 665 per title out of the cost, and "
"it is the one capability nobody in the country has."),
P("**What it needs.** A lacquer cleaning and silvering station, a low-stress nickel pre-plate, "
"production electroforming cells for the three generations, an electro-clean and passivation "
"station, a clean room with controlled access, fume extraction and scrubbing, and a closed-loop "
"rinse-water 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."),
P("**Who supplies it.** Plating Power / Digital Matrix Corporation (New York) is the dominant "
"turnkey vendor for vinyl galvanics (silvering, pre-plate, multi-cell electroforming, "
"passivation). They supply no water treatment: the zero-discharge system comes from an industrial "
"water vendor (Veolia or Hydrotek in Thailand integrating an imported evaporator), following the "
"zero-discharge plating room at Gotta Groove Records in Ohio as the reference model."),
P("**Cost and phasing.** Estimated USD 320k to 650k for the plating line plus USD 90k to 240k for "
"the water plant, plus a clean room. At launch, press-ready stampers come from Stamper Discs "
"(Sheffield, UK), a trade galvanics service that makes stampers to fit Newbilt moulds and ships to "
"30+ countries, under a written turnaround agreement. The roadmap places in-house galvanics in "
"phase 5, but the August 2026 market evidence argues for costing an earlier start: it addresses "
"the market's actual bottleneck, it is a fraction of the full capital, and it could earn from the "
"incumbent's own presses before CNX's press hall exists. Section 11 sets out that question."),
H2("4.3 Stamper preparation"),
P("A raw electroformed shell is not yet a stamper. It is centre-punched to the press spindle, "
"back-finished flat, and trimmed and formed ('coined') to the mould. Sibert Instruments (UK, in "
"this niche since the early 1970s) makes the three machines: the VSP punch, VSF back-finisher and "
"VSC coiner. Estimated USD 60k to 170k, pricing unpublished, bought together with the galvanics "
"line. Concentricity and dimensional checks after each step feed the traceability record."),
H2("4.4 Pressing"),
P("**What happens.** An extruder melts PVC compound into a 'patty', which is placed with two labels "
"between two stampers in a steam-heated mould. The press closes at about 150 tonnes, the mould is "
"then water-cooled, and the record is ejected and trimmed. A cycle takes about 25 to 30 seconds on "
"an automatic press; on a semi-automatic press an operator handles the patty and the record, which "
"is slower but is exactly what colour, splatter and special editions need."),
P("**The fleet strategy: 50/50.** Half the fleet fully automatic for standard volume, half "
"semi-automatic for effects, special editions and small runs, where an operator working the mould "
"directly is an advantage rather than a cost."),
T(["Machine", "Role", "Basis", "Cost"], [
["Newbilt Single 2.1 system (Alsdorf, Germany)", "Launch line: VEX 50 extruder, HRP 12 "
"hydraulic press (~150 t), 12-inch book mould set for 140/180 g, Vario trimmer (7/10-inch "
"upgrade path), label dry oven, shrink wrapper, PVC grinder, label punch, spindles and plates. "
"Hydraulics already sized for a second press.", "Quoted: EUR 200,500 (~USD 232k, ~THB 7.7M) "
"including sea freight and packaging, June 2024, "
"expired; re-quote required with build time and "
"order-book position", "USD 230k to 260k"],
["Duplex expansion", "Second press and extruder on the same hydraulic aggregate",
"Quoted option", "~USD 115k"],
["Two fully automatic presses", "Volume production, one operator per four presses. Candidates: "
"Pheenix Alpha AD12 (Sweden, successor of Toolex Alpha, 24 to 26 s cycle, ~140/h, 25 kW, "
"closed platen cooling) or Newbilt Classic 12-inch (28 s cycle, ~128/h, built-in extruder, "
"Siemens PLC with OPC UA, vendor-claimed 95% yield and uptime)", "Estimate: ~USD 340k per "
"press indicative, unverified, "
"plus delivery and commissioning",
"USD 580k to 920k"],
], widths_mm=[36, 74, 40, 20], font_pt=8.5),
P("**What Newbilt deliberately excludes**, and what will be engineered and sourced in Thailand: a "
"~400 kg/h steam generator at 10 bar / 180 °C with water treatment, and a ~250 kW cooling system. "
"These are the two largest energy systems in the plant and the reason the efficiency architecture "
"in section 5.4 is ours to design."),
P("**Site requirements.** Roughly 200 m² of production floor for the Duplex, 32 kVA connected load, "
"saturated steam, compressed air and cooling circuits, and side rooms for materials, compressor, "
"chiller 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(["Phase", "Fleet", "Rough output per year"], [
["Launch", "1 semi-automatic press, 1 shift", "~90,000 to 135,000 records"],
["Duplex", "2 semi-automatic presses, 1 to 2 shifts", "~175,000 to 540,000 records"],
["Mature", "2 semi-automatic + 2 automatic (50/50 fleet), 2 shifts", "~1.0 to 1.5 million "
"records"],
], widths_mm=[25, 80, 65],
caption="Basis: AD12 ~140 records/h (datasheet); semi-automatic 60 to 80/h assumed; 16 h/day, "
"300 days/yr, 60 to 70% overall equipment effectiveness. Theoretical ceiling of the mature "
"fleet at 100% uptime is 1.9 to 2.1 million."),
P("**On the 60 to 70% effectiveness figure.** Newbilt publishes 95% yield and 95% uptime for its "
"automatic press. 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 and breaks, which are exactly the losses that dominate a short-run boutique "
"plant. Both press vendors are being asked for the basis of their figures as part of the "
"re-quote. If they hold, they argue for a higher effectiveness on the automatic half only."),
H2("4.5 Finishing, packaging and fulfilment"),
P("After pressing, records are trimmed inline, labels have been pre-dried in an oven (moisture in a "
"label causes bubbles), and records are sleeved, assembled into jackets, shrink-wrapped and packed. "
"Jackets, sleeves, inserts, J-cards and CD packaging come from **Ploy Printing** (Ploy Karnpim "
"Limited Partnership, Pa Daet, Mueang Chiang Mai), an established print house with offset, "
"digital, risograph, UV inkjet and latex inkjet presses and vinyl packaging production. That "
"keeps a labour-intensive, freight-heavy item within the province, gives the plant a partner who "
"already knows record packaging, and, because Ploy Printing intends to co-invest (section 12), "
"aligns the two businesses on the same schedule and quality. Risograph and UV print also open "
"boutique packaging options (spot colours, special stocks, printed shells and J-cards) that suit "
"the short-run and colour-vinyl market the semi-automatic line serves. Two points matter "
"commercially:"),
B([
"**Packaging is the path that slips.** Printed jackets need 10 to 20 working days from approved "
"artwork. The metal path and the packaging path run in parallel and converge at assembly; when "
"a label delivers artwork late, jackets become the critical path and every day saved on "
"metalwork is wasted. Artwork deadline is therefore contracted as a fourth approval gate.",
"**The wrong record in the right jacket is the classic error**, and a misprinted barcode is a "
"rejected delivery at the distributor. Each jacket is scanned against the runout matrix code of "
"the record going into it, and every unit's barcode is verified.",
]),
P("**Fulfilment** covers bulk pallet delivery to labels and distributors, and dropshipping single "
"parcels directly to end customers on behalf of labels and artists with carrier tracking recorded "
"per shipment. Dropshipping fees are charged per parcel and are not in the revenue model."),
H2("4.6 Materials: PVC compound"),
P("Records are pressed from a vinyl chloride / vinyl acetate copolymer compound, about 150 to 160 g "
"per 12-inch record. The plant runs an 80% virgin / 20% regrind feed: trimmings and rejects go "
"through an in-house grinder and back to the extruder, so scrap is feedstock, not landfill. The "
"current supplier list is European (Netherlands, UK, Italy, France, Germany) with one US option; "
"the preference is a qualified Thai or regional compound if it meets record specifications, "
"which cuts freight, cost and footprint. The incumbent's floor was stacked with sacks marked "
"'Made in Thailand', a lead worth following. Every incoming lot passes the material lab gate in "
"section 5.3 before it is released to a press."),
H2("4.7 The order, end to end: what a customer is promised"),
P("A defensible lead time is not the sum of process steps: it carries queue and buffer, and it is "
"quoted from the last customer approval, because a plant cannot be held to a clock the customer "
"keeps stopping. The plan's quotable lead times, from test-pressing approval:"),
T(["Order type", "Quoted lead time", "Basis"], [
["New title, full service, launch configuration (bridge stampers from the UK)", "10 to 14 "
"weeks", "Stage sum plus the external metal queue plus buffer; at parity with the 10 to 16 "
"weeks reported for EU/US plants, but closer and in the customer's time zone"],
["New title, full service, mature configuration (cutting and plating in-house)", "7 to 9 "
"weeks", "Removes 13 to 27 days of outsourced metal plus freight"],
["Customer supplies jackets", "1 to 2 weeks less", "Removes the print path"],
["Repress from an archived mother, no changes", "2 to 3 weeks", "Pull stamper (1 to 2 days), "
"prep, press, finish; the highest-margin order the plant will take"],
["Rush slot", "Premium, capped per month", "Only credible with reserved capacity"],
], widths_mm=[62, 28, 80]),
P("Lead time is a capacity decision, not a process decision. A plant running at 90% quotes 16 weeks "
"with the same equipment that quotes 6 weeks at 50%. Any turnaround promise to the market is a "
"promise to hold spare capacity, and the revenue model in section 9 is built on that basis."),
H2("4.8 Cassette duplication (new scope)"),
P("**What happens.** A studio master is duplicated onto tape either in real time on a bank of decks, "
"or at high speed on a loop-bin duplicator that copies a running master loop onto reels of blank "
"tape ('pancakes') many times faster than real time. The tape is then loaded into empty cassette "
"shells ('C-0s') by a loader that cuts to length and splices, shells are printed or labelled, "
"J-cards are printed and folded, and the cassette is boxed and shrink-wrapped."),
T(["", "Tier A: real-time bank", "Tier B: professional high-speed line"], [
["Equipment", "10 to 20 duplicating decks, a cassette loader, shell printer or label printer, "
"shrink wrapper", "Loop-bin high-speed duplicator (typically refurbished 1980s to 1990s "
"Lyrec, Gauss or similar, or a current Chinese-built unit), 2 to 3 loaders, "
"pad printer for on-shell print, imprinter, shrink wrapper"],
["Throughput", "~100 to 300 cassettes per day", "~1,000 to 3,000 cassettes per day"],
["Space", "~30 to 40 m²", "~60 to 80 m², climate-controlled (tape and PVC are both "
"humidity-sensitive)"],
["Staff", "1, part time", "1 to 2"],
["Capital", "USD 15k to 40k", "USD 40k to 150k"],
["Quality", "Adequate for short runs; audio quality is master-limited", "Industry standard; "
"chrome or ferric tape"],
], widths_mm=[24, 68, 78],
caption="Estimate. The professional cassette equipment market is largely refurbished; new "
"high-speed lines are rare. Prices to be confirmed with two or three vendors."),
P("**Supply chain.** Blank tape pancakes and C-0 shells come from a handful of suppliers worldwide "
"(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:** schedule-neutral; fits inside the building programme; add "
"the activity to the BOI application; recommended to start at tier A or a small tier B in phase 1."),
H2("4.9 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"], [
["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 "
"mastering and electroforming), metallised, lacquered, and printed. The result is a 'pressed' "
"CD identical to major-label product."],
["Equipment", "2 to 4 robotic autoloader duplicators with inkjet or thermal print (Rimage, "
"Microboards, Primera or Epson class); packaging printed by Ploy Printing; "
"shrink wrapper", "Laser beam recorder for glass mastering (or outsourced glass masters), "
"electroforming line, one or two injection-moulding lines with inline "
"metalliser, lacquer coater and scanner, screen or offset disc printer, "
"packaging automation, ISO 7/8 clean room"],
["Throughput", "~200 to 800 discs per day per unit", "~10,000 to 30,000 discs per day per line"],
["Space", "~20 to 40 m²", "~300 to 600 m² plus clean room; realistically the phase 2 building"],
["Capital", "USD 15k to 40k", "USD 1.5M to 4.0M (new-build lines); used lines from closed "
"plants can halve that at higher risk"],
["Staff", "Shared with cassette line", "6 to 10 across shifts, including a process engineer"],
["Market fit", "Short runs of 50 to 500 for indie releases, merch and promo; same customers as "
"vinyl and cassette", "Volume runs of 1,000+; competes with established replicators in "
"Japan, Taiwan, Malaysia and elsewhere on thin margins"],
["Regulatory", "No separate BOI activity expected (confirm)", "Separate BOI activity code, "
"must be in the application from "
"the start; larger factory "
"licence scope"],
["Effect on timeline", "None", "Substantial: a second commissioning programme and a bigger "
"building; would push first CD output well past first vinyl"],
["Synergy", "Shares packaging, fulfilment, artwork approval, traceability", "Shares the "
"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 "
"feasibility study."),
P("**Recommendation.** Include Option A in phase 1: 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 "
"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 phase 2 building should be designed for it. This plan's totals include "
"Option A only; Option B is shown as a separate line."),
]
return out
+426
View File
@@ -0,0 +1,426 @@
"""Part B: what makes it top-notch (traceability, metrology, QC, efficiency); site and building; BOI."""
from plan_lib import H1, H2, H3, P, B, N, T, KV, IMG, CALL, PB
def blocks():
return [
# ------------------------------------------------- 5 top-notch
H1("5. What makes it top-notch: traceability, metrology, QC and efficiency"),
P("A pressing plant makes records. A top-notch plant knows, for every record it has ever made, "
"exactly how it was made, can prove it to the customer, and wastes as little energy, water and "
"material as the physics allows. This section is the heart of the plan, because these four "
"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."),
H2("5.1 Traceability: every record knows its history"),
P("Every step of production is logged continuously, so that every record is traceable end to end "
"for the customer. This is a fixed requirement, and it is the core specification for the factory "
"software the project will build. The natural identity of a record is **stamper ID + press ID + "
"cycle timestamp + side**, and everything else hangs off that key."),
T(["Stage", "What is recorded"], [
["Audio intake", "File received, checksums, versions, who handled it, approval trail"],
["Mastering", "Session log, engineer, settings, customer approvals"],
["Cutting", "Lacquer IDs, lathe parameters, stylus used, inspection result, dead-wax matrix "
"scribe"],
["Galvanics", "Silvering and plating batch IDs, bath chemistry logs, father/mother/stamper "
"genealogy (which stamper came from which mother from which father), or the "
"external supplier and batch if bridged"],
["Pressing", "Press ID, operator, shift, mould and stamper IDs, per-cycle temperatures, "
"pressures and cycle time, PVC compound lot and virgin/regrind mix, label lot"],
["Quality control", "Test-pressing approvals, inspection results, reject counts and reasons"],
["Finishing and packaging", "Trimming, sleeving, carton lot, packaging partner batch"],
["Fulfilment", "Warehouse location, shipment or dropshipping consignment, carrier tracking per "
"end customer"],
], widths_mm=[38, 132]),
P("**Principles.** Log at the source: machine data comes from the machine (press PLCs, plating "
"rectifiers, the AD12's remote monitoring), never from manual transcription; manual steps get a "
"simple operator check-in. Batch and genealogy first: every physical artefact (lacquer, father, "
"mother, stamper, press run, box) gets an ID and knows its parents. Environment attached: the "
"plant's temperature, humidity and particulate logs sit on the same timeline, so any production "
"window can be correlated with plant conditions. Immutable and retained: logs are append-only "
"and kept for the life of the customer relationship; stamper genealogy for as long as the archive "
"holds the metal."),
P("**Identity travels with the product.** The matrix code scribed into the lacquer's dead wax "
"propagates through father, mother and stamper to every pressed record. A small angular fiducial "
"scribed beside it makes each part's rotation camera-readable at any stage, and lets the packing "
"station read the code on a finished record and match it to the jacket barcode. Nickel parts "
"additionally get a laser-marked serial on the back, as the optical-disc industry marked its "
"stampers; lacquers are never lasered (nitrocellulose is flammable) and records are never lasered "
"(burning PVC releases hydrochloric acid)."),
CALL("Why this is a product feature, not an internal file", [
"**Customers see their order.** A per-order portal or report covers the full chain above, "
"without exposing other customers' data. Labels ask about pre-release security and provenance "
"before they send masters; this answers both.",
"**A complaint resolves backwards.** One noisy record resolves to its stamper, press, cycle, "
"operator, compound lot and packaging lot, and from the stamper back through mother and father "
"to the lacquer and the approved master. Root cause takes minutes, not a week of guessing.",
"**Stampers retire on data.** Audio scores trending down against a stamper's play count become "
"an automatic retirement signal instead of a customer complaint.",
"**Machine data access is a purchasing condition.** Newbilt, Pheenix Alpha and Digital Matrix "
"are asked which PLC interfaces they expose before a purchase order is signed. That question "
"is free now and expensive later.",
]),
P("**Cost and build.** The traceability and factory software is estimated at USD 60k to 170k and "
"is a development project, not a purchase; no vendor sells a turnkey version. Version 1 (intake, "
"batch genealogy, press telemetry capture) is built during commissioning so the first test "
"pressings are already in the system. The project repository is the eventual home of this "
"software. Open design work: the data model across all stages, retention and PDPA handling for "
"operator-linked records, and whether test-pressing approval workflows live in the same system "
"from day one."),
H2("5.2 Metrology: measuring the metal before it presses a single record"),
P("A stamper is a 0.2 to 0.3 mm nickel disc carrying a spiral ridge 40 to 80 microns wide and about "
"28 microns high, 200 to 350 turns per inch. Industry practice, even at respected stamper houses, "
"is to check its thickness with a hand micrometer at ten points on sacrificial blanks, with "
"nothing logged. A top-notch plant measures the metal without contact, per serial, and makes an "
"accept or reject decision on data before the next plating generation or the press mounting."),
T(["Instrument", "What it measures", "How it is used"], [
["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; scanning the full spiral "
"is not feasible and not needed. The optical-disc industry verified its nickel stampers with "
"this instrument class, so the technique transfers directly"],
["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 of every part"],
["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"],
["Stylus profilometer", "Traceable reference", "Calibration of the optical instruments"],
["Inline machine vision", "Cosmetic screening", "100% of parts, fast"],
], widths_mm=[42, 58, 70], font_pt=8.5),
P("Integration is real but not turnkey: the instruments export data and support scripting, and "
"the inline sensors speak standard industrial protocols, but there is no off-the-shelf connector "
"to a factory database. A light middleware service stamps each measurement with the part serial "
"and batch ID and writes it to the genealogy record. Budget for the full stack (metrology plus "
"the record-QC layers below, plus the audio station) is USD 350k to 810k, phaseable; a basic "
"tier (microscope, benchtop PVC lab, sampled listening) runs from launch."),
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 "
"for about 40 minutes and a press makes a new one every 25 to 30 seconds, so one press outruns "
"roughly 80 to 95 full-time listening stations. No playback-QC robot exists anywhere. The credible "
"maximum-automation design is layered, and it should be described to customers exactly as it is: "
"100% optical plus per-cycle telemetry plus sampled audio, with human ears kept in the loop for "
"test-pressing approval."),
T(["Layer", "What", "Catches", "Status"], [
["1. 100% inline optical inspection", "Darkfield line-scan cameras at the press unloader "
"inspect every record at line speed", "Non-fill (dull patches where PVC did not fill the "
"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 without playing the record", "Custom integration of general machine "
"vision (Keyence or Cognex class); no "
"vinyl-specific product exists; CNX "
"would be an early adopter"],
["2. Press telemetry, every cycle", "Nozzle pressure, mould temperatures, cycle time, cake "
"weight, yields and drift alerts streamed from the press PLC", "Process drift before it "
"becomes a defect; the record-level context for every other check", "Proven and deployed "
"in the industry "
"(Viryl ADAPT model); "
"AD12 and Newbilt "
"Classic expose the "
"data"],
["3. Automated audio comparison, sampled", "A needle-drop capture station with automated "
"analysis: spectral-envelope comparison against the source master, click and pop counting, "
"noise floor from lead-in silence, channel balance; numeric score and pass/fail to the "
"database", "Wrong master cut into good-looking grooves, EQ and mastering errors, speed and "
"pitch problems; the things no camera can see", "Buildable from proven parts "
"(Stamper Discs' Fonograph "
"approach, iZotope-class "
"algorithms); first-of-kind as a "
"whole"],
], widths_mm=[32, 48, 55, 35], 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 "
"industry rule of thumb is a spot-play around every 50th)."),
H3("The materials lab"),
P("Every incoming compound lot passes a gate before it reaches a press, and its lot ID follows every "
"record pressed from it. Per lot, in-house from day one, on benchtop instruments with data "
"export: certificate of analysis against the agreed specification, moisture (the most common "
"cause of noisy surfaces), heat stability (PVC degradation releases hydrochloric acid and shows as "
"discoloration and noise), density, colour and gloss, granulate size and dust. New lots, and "
"especially new suppliers, are released only by test pressing. As the lab matures, torque "
"rheometry, DSC and FTIR are added; supplier qualification uses an external lab for K-value, "
"stabiliser identification (legacy lead rejected) and heavy-metals compliance for export. The "
"in-house regrind stream has its own contamination gate. This protocol is how Thai and regional "
"compounds are qualified against the European references."),
H3("Packaging QC"),
P("Incoming cartons and sleeves are checked per delivery: dimensions (records must fit without "
"force; spine width per title), board grammage, print quality against the customer-approved "
"proof (spectrophotometer Delta E for colour-critical jobs), lamination and seam strength, inner "
"sleeve scuff risk. At assembly, the runout matrix code is scanned against the jacket barcode and "
"every unit's barcode is verified. Results go back to Ploy Printing per packaging lot, which is "
"the practical advantage of a partner ten kilometres away over a supplier ten thousand."),
H2("5.4 Efficiency: recover before you generate"),
P("The pressing machinery is a modest electrical load (32 kVA connected for a full semi-automatic "
"line). The real energy demand sits in the utilities around it: the steam generator, the 250 kW "
"cooling system, and, in a tropical climate, the whole-plant air conditioning. Because Newbilt "
"excludes steam and cooling from its scope, these systems are ours to design, and the plan's "
"architecture is built 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 cooling load is heat that the "
"steam put into the moulds, and the press cooling return leaves at about 60 °C. That warm return "
"is a premium heat source, and the plant reuses it before generating anything new."),
T(["Measure", "What it does", "Effect"], [
["Feedwater heat recovery", "A water-to-water heat pump lifts the 60 °C press cooling return "
"into boiler feedwater at 90 to 95 °C", "Each kWh does double duty: cuts the boiler's work "
"and removes load from the chiller. With condensate "
"return, saves roughly 10 to 15% of steam energy and "
"shrinks the cooling system"],
["Condensate return", "Steam condensate goes back to the treated feedwater cycle", "80% "
"return assumed; less water, less treatment, less fuel"],
["Waste heat for low-grade consumers", "The label dry oven and the air conditioning's "
"dehumidification reheat run on recovered press heat", "Displaces fresh electricity in two "
"continuous loads"],
["Solar-thermal feedwater preheat", "Flat-plate or evacuated-tube collectors reach 60 to 90 °C "
"trivially in the Thai sun", "Cheap, proven, stackable with the heat pump"],
["Steam accumulator", "Presses draw steam in bursts; an accumulator (a Newbilt option) lets a "
"smaller boiler ride the peaks", "Smaller boiler, steadier firing"],
["Closed-loop cooling", "Chiller or hybrid tower with plate heat exchanger; no open 'dirty "
"water' loop as in the reference schematic", "No process water discharged; the largest "
"water draw (tower evaporation) shrinks with "
"every kW the heat pump removes"],
["Regrind loop", "In-house grinder returns trimmings and rejects to the extruder at 20% of "
"the feed", "Scrap is feedstock, not landfill; ~20% less virgin compound"],
["One operator per four automatic presses", "Automatic patty, label and cooling-plate "
"handling", "Labour per record on the automatic half is a fraction of the semi-automatic "
"line's"],
], widths_mm=[38, 68, 64], font_pt=8.5),
P("Two things were checked and ruled out: heat pumps cannot make the final 10 bar / 180 °C steam "
"(commercial units top out around 120 to 165 °C, and core utility reliability is the wrong place "
"to pioneer), and ground-source heat exchange does not work in Thailand (the ground sits at "
"28 to 30 °C, too cold for process heat and warmer than the cooling loop wants). The boiler only "
"does the final lift, from already-hot feedwater."),
H3("Renewable energy"),
KV([
["Rooftop PV, phase 1", "~247 kWp on the 1,523 m² monopitch roof (425 modules of the current "
"580 W class) plus ~40 kWp on the parking canopies, ~290 kWp total; "
"expected yield ~335 to 360 MWh/yr from the roof, ~25 to 35% of mature "
"two-shift consumption"],
["Rooftop PV, with phase 2 building", "~498 kWp across both roofs, which removes the need for "
"ground-mount solar"],
["Battery", "271 kWh Gobel Power LiFePO4 rack (15 stacked 18 kWh packs, 8,000+ cycles), "
"USD 40k to 58k listed, 50 to 60 day lead. Peak shaving and ride-through, roughly "
"two to three hours of the chiller's draw, not off-grid autonomy"],
["Inverters", "Multiple Deye three-phase hybrid units (30 to 80 kW class), Thai distribution "
"via Solar Nicolo; Deye is a supported protocol on the Gobel battery management"],
["Roof design", "Monopitch at 10° falling toward ESE puts the whole roof on one plane at ~98% "
"of ideal yield; at this latitude orientation barely matters (under 2% "
"across a wide azimuth swing) and the building stays aligned with the plot. "
"10° rather than shallower so the rains self-clean burning-season dust"],
["Open checks", "Battery rack voltage vs the chosen Deye model's window; Gobel safety "
"certifications (not published); EPC warranty on a mixed-vendor DC bus; "
"confirm yield against PVGIS before committing"],
]),
H3("Steam: the open engineering choice"),
P("There is no natural gas pipeline in Northern Thailand, so gas means a bulk LPG tank on site and "
"diesel means a fuel tank with price exposure. The plan keeps two fossil-free routes open. An "
"**electric boiler** (THB 2.5M to 4.0M installed, Miura or Bangkok Industrial Boilers class) is "
"fast, clean to permit, and maximises the solar share, at a ~300 kW draw that pushes the grid "
"connection toward 750 kVA. A **biomass boiler** (THB 4M to 7M, Getabec, Thai-fabricated) burns "
"wood chips, longan orchard prunings (Lamphun is Thailand's longan heartland and prunings are a "
"traded fuel), corn cobs and rice husk, roughly 350 to 400 tonnes a year at maturity; it makes "
"the steam genuinely renewable at the cost of fuel logistics, ash handling and an emissions "
"stack. The team's own 60 rai of forestry-classified land could supply 80 to 135 tonnes a year "
"as short-rotation coppice, most of early operations' need, but only after its legal "
"classification is verified. The staged path is the working assumption: **start electric, "
"plant the coppice now, add biomass at the automatic-line expansion** when demand and fuel supply "
"have both matured. The building already has a boiler hall and a fuel store in its utility yard."),
H3("Water"),
P("Estimated demand at mature two-shift operation is about 15 m³ per day, called 20 with margin: "
"cooling tower evaporation and blowdown (over half), boiler makeup, galvanics rinse after "
"recycling, and domestic use for 25 staff. Source is open (PWA mains or a licensed well) and "
"drives part of the utilities budget. Storage is a dedicated ~200 m³ fire reserve plus a ~50 m³ "
"raw-water tank feeding a softener or RO skid. **No process water is discharged:** cooling is a "
"closed loop, condensate is returned, galvanics rinse water goes through closed-loop treatment "
"and metal recovery (an evaporator, THB 3M to 8M, with the galvanics line), and solvents are "
"contained and disposed of as controlled waste. This is a fixed design requirement, not an "
"aspiration to be traded away during engineering."),
H3("Climate control as a product"),
P("The whole plant runs under continuous control: 20 to 26 °C, 35 to 60% relative humidity, and "
"particulate filtration (F7 to F9 general, HEPA with positive pressure in the cutting room and "
"galvanics clean room), with sensors logging in every zone. Moisture in PVC compound causes "
"bubbles, non-fill and surface defects and drives cooling rate, hence flatness and warp; airborne "
"particulate embeds in the record surface and is the classic source of clicks and pops. Northern "
"Thailand's burning season (February to April) brings some of the world's worst PM2.5 episodes, "
"so a plant without filtration would press different records in March than in September. This "
"is the plant's largest utility line (THB 7.5M to 15M for ~1,500 m², plus THB 1.2M to 3.4M for "
"the two clean zones) and it is treated here as part of the product, not as overhead. The 35% "
"humidity floor is aggressive for the tropics and drives desiccant cost; if 45% proves "
"acceptable, the cost drops materially, and that question is on the RFQ list."),
CALL("Efficiency in one paragraph, for the investor", "Heat is recovered from the presses before "
"any fuel is burned; a quarter to a third of electricity comes from the roof at phase 1 and "
"up to half with the second building; a battery shaves the peaks; cooling water is never "
"discharged; a fifth of the plastic is reclaimed; and one operator runs four automatic presses. "
"The same choices cut the energy line in the cost model toward its low end, hedge Thai tariff "
"rises, and convert into BOI tax value (section 7). They also make the plant's sustainability "
"story true rather than claimed, which matters to the labels it sells to."),
# -------------------------------------------- 6 site and building
H1("6. Site and building"),
H2("6.1 Land: a long lease in Lamphun"),
P("The project's site strategy has changed from purchase to **long-term lease of raw land**, on "
"which the purpose-built plant is still constructed. This lowers upfront capital by roughly "
"USD 0.4M to 1.0M (THB 12M to 32M at the 2 to 4 million THB per rai the plan carried for "
"purchase), removes land from the balance sheet, and matches how most foreign-invested "
"manufacturing in Thailand actually holds its site. It brings its own conditions, which Thai "
"counsel will resolve before signature:"),
B([
"**Term and registration.** A lease over three years must be registered at the Land Office to "
"be enforceable; the ordinary maximum is 30 years, and for industrial and commercial use the "
"Hire of Immovable Property for Commerce and Industry Act allows up to 50 years for qualifying "
"projects. The plan assumes a registered 30-year lease with a contractual renewal option, and "
"will test the 50-year route with counsel.",
"**Building ownership.** The lease must state clearly that the buildings, foundations, tanks "
"and fixed plant belong to the company for the term, what happens to them at expiry, and that "
"the company may mortgage or assign them. Without this, financing the building is difficult.",
"**Lessor's title and encumbrances.** A clean Chanote title, no mortgage that could foreclose "
"over the tenant, and consent from any co-owners. A lease of Sor Por Kor or forestry-classified "
"land is not acceptable.",
"**Zoning and factory permission.** The candidate parcel in Mueang Chi, Lamphun is agricultural "
"land whose zoning for factory use is unverified; this gates everything and is the cheapest "
"schedule insurance available. The Northern Region Industrial Estate in Lamphun (IEAT) is the "
"fallback: leased plots, licensing handled by the estate, and shared central effluent treatment "
"that eases the galvanics permitting.",
"**BOI interaction.** BOI's land-ownership privilege for foreign-held promoted companies is "
"simply not used; leasing does not affect the promotion.",
"**Rent.** Not yet negotiated. The plan carries THB 0.3M to 1.0M per year (USD 9k to 30k) as "
"an estimate for 5 to 8 rai of agricultural land converted to industrial use in the Lamphun "
"valley; IEAT plots run higher. The negotiated figure replaces this.",
]),
P("Land requirements are unchanged: ~4 rai minimum, 6 to 8 rai comfortable with expansion, truck "
"access and turning, parking, the outdoor cooling tower, a separated battery enclosure, and a "
"500 to 1,000 kVA medium-voltage connection from PEA."),
H2("6.2 The building"),
IMG("renders/site-plan.png", 120, "Figure 2. Site plan on the candidate Mueang Chi parcel: phase 1 "
"main building, detached utility yard, fire and raw water "
"tanks, parking canopies, and the phase 2 building footprint. "
"Source: parametric model, project repository."),
P("The phase 1 building is a single-storey steel portal frame of **60 × 25 m (1,500 m²)** with a "
"10° monopitch roof carrying the PV array, low eave 7.0 m, high side ~11.4 m. The floor is a "
"200 mm slab rated 30 kN/m² (3 t/m², the standard industrial class and what the presses and "
"racking need) raised 1.2 m above ground so that two sealed loading docks work at trailer height "
"and the docked trailer acts as the airlock. The envelope is insulated PIR sandwich panel "
"(100 mm roof, 75 mm walls) on a 2.5 m masonry dado, taped and gasketed, because the building is "
"air-conditioned and must be airtight against smoke-season particulate; mineral-wool panels "
"where fire compartmentation (EI 60 or 120) or acoustics require them."),
T(["Zone", "Area"], [
["Semi-automatic press hall (Newbilt Duplex specification)", "~200 m²"],
["Automatic press zone", "~100 to 150 m²"],
["Galvanics clean room, water treatment, chemical storage", "~80 to 150 m²"],
["Mastering and cutting room", "~40 to 60 m²"],
["Stamper preparation", "~30 m²"],
["Cassette duplication and CD-R duplication (new)", "~60 to 100 m²"],
["Packaging, assembly and finished-goods warehouse", "~300 to 500 m²"],
["Utility rooms inside (compressor, electrical, IT)", "~50 to 100 m²"],
["Offices, QC and listening room, archive, entrance", "~150 to 200 m²"],
["Circulation, walls, miscellaneous", "~150 to 250 m²"],
["**Total under roof**", "**~1,200 to 1,700 m²**, against 1,500 m² built"],
], widths_mm=[110, 60],
caption="The boiler hall (66 m²), biomass fuel store (42 m²), chiller room (60 m²) and cooling "
"tower pad sit in a detached utility yard, which frees the main floor for production and "
"keeps the boiler in its own room as Thai regulations require."),
P("Site works include a ~444 m perimeter wall, one 10 m sliding gate with a guard house, 4,387 m² "
"of paving in two classes (heavy-duty for trucks, general for parking and yard), roofed parking "
"for 10 cars and 30 scooters with ~40 kWp of PV on the canopies, foundations for a 9 m fire "
"water tank and a 5 m raw-water tank, a fire ring main and hydrants, stormwater drainage "
"(66% of the plot becomes sealed, so a detention pond may be required for the permit), and "
"domestic sanitation."),
P("**A second building is expansion, not a gap.** The whole zone table fits inside the phase 1 "
"building. A 45 × 38 m (1,710 m²) phase 2 building fits the plot's narrowing southern end with a "
"two-bay sawtooth roof carrying ~251 kWp, a 20 m yard between the two for fire separation and "
"shared loading; total coverage stays at 39% of the deed area. It is where a CD replication line "
"would go if that option is chosen (section 4.9), or where finished goods move to free production "
"floor."),
H3("What the building costs, and what is not known"),
P("Building and civil works are estimated at **USD 650k to 1.1M** (THB 21M to 36M) at 12,000 to "
"20,000 THB/m² for Thai pre-engineered steel construction to the insulated-panel specification, "
"plus ~20% site works. A budget-level price request with quantities from the parametric model is "
"ready to send to contractors. The single largest unknown is the ground: the 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 and a 1,711 m³ fill platform. A soil boring test (THB 30k to 50k) "
"comes before any real number and is in the phase 0 budget."),
IMG("renders/aerial-southwest.png", 150, "Figure 3. Massing render from the south-west: phase 1 "
"building with rooftop PV, utility yard, tanks and parking "
"canopies. Source: parametric model."),
H2("6.3 Safety, security and the irreplaceable"),
P("Worker safety is a founding priority on equal footing with the environmental commitments. The "
"pressing line combines hydraulics at ~180 bar, saturated steam at 10 bar / 180 °C and hot PVC; "
"the galvanics room handles silver nitrate and nickel salts. The plan provides vendor-led "
"commissioning training extending into an in-house program, mandatory PPE per area, controlled "
"access and decontamination for the clean rooms, eyewash and emergency shower at the plating "
"line, a licensed boiler operator, and fume-extraction interlocks (no extraction, no plating)."),
P("Physical security is zoned: production, galvanics, chemical storage, the master and stamper "
"archive, IT, and shipping 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 re-cutting and re-plating a release; fire-rated room, climate control, access logging, "
"clean-agent gas suppression because sprinkler water would destroy lacquers and paper and corrode "
"the metal) and **client audio** (pre-release music is a leak target; isolated encrypted storage, "
"need-to-know access, and a written policy the sales team can show labels)."),
P("Fire suppression is zoned, not full-building gas: clean agent only in small sealed high-value "
"rooms (archive, IT, switchrooms, control cabinets), water mist or sprinklers in production and "
"the carton-heavy warehouse, detection and containment rather than suppression for the battery "
"enclosure. The site carries its own ~200 m³ fire reserve and pump set because mains flow in "
"Mueang Chi cannot be assumed. The property insurer is involved before the design is fixed."),
# ----------------------------------------------------- 7 BOI
H1("7. Regulatory: BOI promotion and licences"),
P("The project will apply for promotion by the Thailand Board of Investment. In BOI terms vinyl "
"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 "
"discs."),
T(["What BOI gives", "Why it matters here"], [
["Machinery import duty exemption", "Almost the entire equipment list is imported (Germany, "
"Sweden, USA, UK, Japan). Significant across every equipment block."],
["100% foreign ownership and land privileges", "Overrides Foreign Business Act restrictions "
"for the promoted activity. Land ownership privilege is unused under the lease strategy."],
["Visas and work permits via One Stop Service", "Foreign commissioning engineers and the "
"technical consultant, without the four-Thai-employees-per-permit rule."],
["Raw material duty relief", "Exemption for export production; up to 90% reduction for "
"domestic sales. Relevant for imported PVC compound until a Thai source is qualified."],
["Solar and battery measure (alternative-energy, 2025)", "CIT exemption based on the solar "
"investment; PV modules, inverters, battery and mounting explicitly eligible. The planned "
"installation converts directly into tax-exemption headroom."],
["Smart and Sustainable Industry upgrade measure", "Rewards automation, smart machinery and "
"energy efficiency, typically 3 years of CIT exemption capped around 50% of the qualifying "
"spend. The automatic presses, inline inspection, metrology and heat recovery are the natural "
"fit."],
["R&D merit", "At least 1% of revenue in Thai R&D adds 1 to 3 exemption years. Galvanics "
"process development, recycled-PVC work and the audio-QC system could be framed as "
"qualifying development."],
], widths_mm=[58, 112]),
P("**Requirements:** minimum THB 1M investment excluding land and working capital (cleared by the "
"first press line alone); debt-to-equity no higher than 3:1 for the life of the privileges, which "
"shapes the financing mix; modern machinery and process; progress reporting. BOI promotion does "
"not replace the factory licence (Ror.Ngor. 4), boiler registration, hazardous-substance "
"registration for the plating chemistry, or wastewater permitting. The galvanics effluent is the "
"most likely regulatory friction point regardless of BOI, which is one argument for the "
"industrial estate's shared treatment."),
P("**Process:** file via the e-Investment system and present at an analyst meeting; decision in "
"about 40 working days for projects under THB 200M, roughly 4 to 6 months end to end to the "
"certificate; incorporate the company under it; import machinery within a window commonly 30 "
"months from issuance; reach full operation by about 36 months. The Northern Economic Corridor "
"(Chiang Mai, Lamphun, Lampang) is promoted as a creative-economy base, which a music "
"manufacturing story fits; current area add-ons must be confirmed with BOI directly."),
CALL("Two things to settle before filing", [
"**Scope.** Cassette duplication and CD-R duplication should be in the application from the "
"start. CD replication is a separate activity code and cannot be added casually afterwards; "
"the investor's decision on section 4.9 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 a press can be ordered with a deposit while the application is in "
"review, provided it clears customs after issuance, the launch pulls forward by 6 to 9 "
"months. This is the first question for the BOI consultant and the only item in the plan "
"worth half a year.",
]),
P("Keep any future label, content or retail arm in a separate entity: media and service activities "
"carry different, sometimes Thai-ownership, conditions and would muddy a clean manufacturing "
"application. Poy Records as a label brand is therefore a separate question from the plant."),
]
+549
View File
@@ -0,0 +1,549 @@
"""Part C: investment, revenue and operating model, timeline, phasing, team and ownership, risks, next steps."""
from plan_lib import H1, H2, H3, P, B, N, T, KV, IMG, CALL, PB
def blocks():
return [
# ------------------------------------------------- 8 investment
H1("8. Investment"),
P("Everything priced or estimated so far, rolled into one view. Blocks marked 'estimate' have no "
"vendor confirmation; the rest trace to quotes or researched ranges. Treat all figures as "
"budgetary with a wide margin (plus or minus 30 to 50% on the estimates) until requests for "
"quotation replace them. Land is now a lease and is removed from capital; cassette and CD-R "
"duplication are added."),
H2("8.1 Full vision, all blocks"),
T(["Block", "USD", "THB", "Basis"], [
["Newbilt semi-automatic line, full Duplex", "350k to 370k", "11.5M to 12.2M", "Quoted: 2024 "
"offer EUR 200,500 (~USD 232k) plus quoted second press and extruder ~USD 115k; expired, "
"re-quote required"],
["Two fully automatic presses", "580k to 920k", "19.1M to 30.5M", "Estimate: ~USD 340k per "
"press indicative, unverified, plus delivery and commissioning"],
["Galvanics line (Digital Matrix)", "320k to 650k", "10.7M to 21.4M", "Estimate: six-figure "
"turnkey inference from vendor research"],
["Zero-discharge water treatment", "90k to 240k", "3.0M to 8.0M", "Researched: utilities "
"option row"],
["Stamper preparation (Sibert)", "60k to 170k", "1.9M to 5.7M", "Estimate, pricing unpublished"],
["Mastering and cutting room", "290k to 580k", "9.6M to 19.1M", "Estimate, bespoke market"],
["Metrology and QC stack", "350k to 810k", "11.5M to 26.7M", "Estimate, phaseable"],
["Utilities, solar, battery, HVAC, racking baseline", "730k to 1,270k", "24.0M to 41.6M",
"Researched: Thai supplier ranges, THB-native, plus or minus 30%"],
["Building ~1,500 m² plus civil and site works", "650k to 1,100k", "21.4M to 36.2M",
"Estimate at 12,000 to 20,000 THB/m² plus ~20% site works; RFQ ready"],
["Land", "0 (leased)", "0", "Was 360k to 970k for purchase; rent moves to operating cost"],
["Cassette duplication line", "40k to 150k", "1.3M to 5.0M", "Estimate, section 4.8"],
["CD-R duplication", "15k to 40k", "0.5M to 1.3M", "Estimate, section 4.9 option A"],
["Traceability and factory software", "60k to 170k", "1.9M to 5.7M", "Estimate, development "
"project"],
["Freight, installation, training (non-Newbilt)", "120k to 230k", "3.8M to 7.6M", "Estimate"],
["Working capital, PVC stock, spares, lease deposit", "135k to 290k", "4.5M to 9.6M",
"Estimate; includes lease deposit and first-year rent"],
["Subtotal", "~3.8M to 7.0M", "~125M to 231M", ""],
["Contingency 15%", "~0.6M to 1.0M", "~19M to 35M", ""],
["**Total, full vision**", "**~4.4M to 8.0M**", "**~144M to 264M**", "Before the lease "
"decision: 4.7M to 8.9M"],
["Not included: CD replication (option B)", "1.5M to 4.0M", "50M to 132M", "Separate "
"decision, separate feasibility study, phase 2 building"],
["Not included: phase 2 building", "~750k to 1,250k", "~25M to 41M", "Same THB/m² basis on "
"1,710 m²; expansion "
"beyond the current "
"programme"],
], widths_mm=[52, 30, 30, 58], font_pt=8.5),
H2("8.2 Phase 1: what launch actually costs"),
P("The launch configuration is far smaller than the full vision, and it is the number the "
"investor is asked to commit to after the gates clear. It buys a purpose-built, climate-controlled "
"plant with one semi-automatic press line, bridge stampers from the UK, basic QC, first-version "
"traceability, the rooftop solar array and battery, and the cassette and CD-R lines."),
T(["Block", "USD low", "USD high", "Note"], [
["Phase 0: consultants, legal, soil test, trade show, re-quotes", "40k", "80k", "Section 14"],
["Newbilt Single 2.1 system incl. freight", "230k", "260k", "Re-quote drift on the 2024 EUR "
"figure"],
["Building, civil and site works", "650k", "1,100k", "Soil result is the main variable"],
["Utilities: electric steam, compressed air, chiller, water treatment, racking, whole-plant "
"HVAC, ~290 kWp PV, 271 kWh battery", "690k", "1,160k", "Clean-room adder deferred to the "
"galvanics phase"],
["Basic QC, PVC lab, traceability v1", "70k", "180k", "Microscope, benchtop lab, sampled "
"listening, software v1"],
["Cassette duplication and CD-R lines", "60k", "190k", "Tier A or small tier B cassette"],
["Freight, installation, commissioning beyond Newbilt's 5 days, training", "40k", "100k",
"Newbilt service EUR 84/h plus travel"],
["Working capital, PVC stock, spares, lease deposit, first-year rent", "130k", "290k", ""],
["Subtotal", "~1.9M", "~3.4M", ""],
["Contingency 15%", "~0.3M", "~0.5M", ""],
["**Phase 1 total**", "**~2.2M**", "**~3.9M**", "**~THB 73M to 128M**"],
], widths_mm=[80, 22, 22, 46], font_pt=8.5),
P("Galvanics, the automatic presses, the mastering room, the full metrology stack and the biomass "
"boiler follow as revenue-funded or second-round expansions; the remainder between phase 1 and "
"the full vision is roughly USD 2.2M to 4.1M. The planning already supports the split: the "
"Duplex-ready hydraulics, the bridge role for Stamper Discs, the staged electric-to-biomass "
"steam path and the phaseable metrology stack are all documented for exactly this reason."),
H2("8.3 Tranches"),
P("Phase 1 capital is released against milestones rather than up front, so that each tranche is "
"spent only once the previous gate has cleared:"),
T(["Tranche", "Trigger", "Covers", "Approx. share"], [
["0", "Now", "Phase 0 work", "2 to 3%"],
["1", "BOI certificate issued; lease signed; zoning and soil confirmed; pricing validated",
"Press order deposit, building design and permit, long-lead utility orders", "25 to 30%"],
["2", "Building permit; press build confirmed", "Construction, utilities, solar", "40 to 45%"],
["3", "Building watertight; machinery shipped", "Fit-out, HVAC commissioning, QC and software, "
"cassette and CD-R lines, hiring", "15 to 20%"],
["4", "Commissioning complete", "Working capital, stock, first stamper orders", "8 to 10%"],
], widths_mm=[16, 62, 66, 26]),
H2("8.4 What moves the number"),
B([
"**BOI** machinery duty exemption applies across most of the equipment blocks, and the solar "
"and automation spend converts into tax-exemption headroom; the application precedes every "
"purchase order.",
"**The ground.** Soft paddy soil under the building, paving and fill platform is the single "
"largest cost variable in the building package, and the cheapest to resolve.",
"**Biggest uncertainty bands:** metrology (partly a choice of ambition), the mastering room "
"(bespoke vendor, no public pricing), the automatic presses (one unverified price point), "
"the galvanics line, and every cassette and CD figure.",
"**Every band narrows only with quotes.** The supplier map in the project repository lists who "
"to ask, and the re-quotes are phase 0 work.",
]),
H2("8.5 Currency exposure"),
P("Revenue will be mostly USD and EUR from export customers; the cost base is mostly THB; the "
"largest single equipment block is EUR-denominated and unhedged against an order that will not "
"be placed for roughly a year. A 10% move in EUR/THB between now and the Newbilt order is worth "
"USD 25k to 35k on that line alone. Operationally, roughly two-thirds of direct cost (labour, "
"energy, cartons, maintenance) is THB, so a 10% baht appreciation takes 0.09 to 0.17 USD off the "
"gross margin per record without any operational change. Two mitigations are planned: ask every "
"re-quote to state its currency and validity period, and decide early whether to quote regional "
"customers in THB. Hedging the press order once the BOI timeline is known is a decision for the "
"investor."),
# ------------------------------------- 9 revenue and operating model
H1("9. Revenue and operating model"),
P("A first-pass economic model at mature capacity. Most numbers are estimates from industry pricing "
"norms and the project's own cost research; the point is the shape of the economics and which "
"levers matter, not precision. The pricing assumptions are deliberately held at the conservative "
"Western export benchmark even though the one set of Thai market data (section 3.3) says "
"domestic prices are several times higher: re-basing the whole model on one supplier's price "
"list in a market with one supplier would be worse than leaving it low."),
H2("9.1 Pricing assumptions"),
B([
"**Blended pressing price: USD 3.50 to 4.60 per record** (THB 115 to 150), across run sizes "
"and the standard/colour mix, covering pressing, labels, inner sleeve and assembly into the "
"jacket; deluxe packaging passed through at cost plus margin. Western plants charge roughly "
"USD 2.90 to 5.80 for standard black at volume, with premiums of 0.60 to 1.70 for colour and "
"splatter, 180 g and small runs.",
"**Per-title services: USD 580 to 1,730 per title** (THB 19k to 57k) for mastering, cutting, "
"stampers and test pressings, at roughly 300 to 600 titles a year at full capacity. Observed "
"Thai rates suggest this is understated by up to three to four times for a full album, but "
"mastering is labour-bound (an engineer masters perhaps two to four songs a day) and the plan "
"staffs one to two engineers, which caps this line.",
"**Cassettes and CD-Rs** are add-on lines, not in the totals: illustratively 20k to 60k "
"cassettes a year at USD 2.50 to 4.00 (USD 50k to 240k) and 10k to 40k CD-Rs at USD 1.50 to "
"3.00 (USD 15k to 120k). Estimates with no market data behind them yet.",
"**Dropshipping fees** per parcel are on top and not modelled.",
]),
H2("9.2 Revenue at full capacity"),
T(["Scenario", "Records/yr", "Pressing revenue", "Title services", "Total USD", "Total THB"], [
["Mature, low", "1.0M", "~3.5M", "~0.17M", "**~3.7M/yr**", "**~122M/yr**"],
["Mature, high", "1.5M", "~6.1M", "~0.69M", "**~6.8M/yr**", "**~224M/yr**"],
], widths_mm=[26, 22, 30, 28, 32, 32],
caption="Low: 1.0M × USD 3.50 plus 300 titles × 580. High: 1.5M × 4.05 plus 600 titles × 1,155. "
"A pure-volume plant runs at the low per-title end; a boutique-heavy mix pushes titles up "
"and volume down."),
H2("9.3 Direct cost per record"),
T(["Cost element", "USD/record", "THB/record", "Basis"], [
["PVC compound", "0.29 to 0.46", "9.6 to 15.2", "~150 to 160 g effective virgin at 1.75 to "
"2.90 USD/kg; regrind loop covers scrap"],
["Labels", "0.03 to 0.09", "1.1 to 3.0", "Printed pairs, volume"],
["Inner sleeve", "0.09 to 0.17", "3.0 to 5.7", "Poly-lined"],
["Jacket (standard)", "0.40 to 0.92", "13.3 to 30.4", "Ploy Printing; largely pass-through"],
["Shrink wrap", "0.02 to 0.03", "0.8 to 1.1", "Film"],
["Energy", "0.12 to 0.23", "3.8 to 7.6", "~1.5 to 2 kWh blended per record at grid/solar mix"],
["Production labour", "0.08 to 0.16", "2.6 to 5.4", "13 to 19 direct staff at northern Thai "
"rates, loaded ×1.3, over 1.0 to 1.5M "
"records"],
["Stamper amortisation", "0.03 to 0.12", "1.0 to 4.0", "In-house three-generation set at "
"~USD 60 to 150 marginal over 1,000 "
"to 2,000 pressings"],
["Maintenance, spares, consumables", "0.12 to 0.23", "3.8 to 7.6", "Estimate"],
["Reject allowance", "0.12 to 0.17", "3.8 to 5.7", "5 to 10% reject rate carried by good units"],
["**Total direct cost**", "**~1.30 to 2.58**", "**~43 to 85**", ""],
], widths_mm=[42, 26, 26, 76], font_pt=8.5),
P("Blended price minus direct cost gives a gross margin of about **USD 2.11 per record at the "
"midpoint (~52%)**. The spread is wide and worth stating: the adverse corner (low price meeting "
"high cost) leaves USD 0.92 (26%), the favourable corner USD 3.30 (72%). Price realisation and "
"cost control each move the margin by more than a third on their own."),
H2("9.4 Operating picture at full capacity"),
P("Built from the components above. Three scenarios, each internally consistent: adverse pairs low "
"volume with the low price and high cost; favourable does the opposite; central takes midpoints. "
"Cash overheads (admin, sales, insurance, facility running, IT, professional fees, marketing, and "
"now the land lease) are estimated at USD 210k to 480k a year and paired high-to-adverse. "
"Depreciation is the 4.4M to 8.0M capital over 10 to 15 years."),
T(["USD per year", "Adverse", "Central", "Favourable"], [
["Records", "1.0M", "1.25M", "1.5M"],
["Blended price", "3.50", "4.05", "4.60"],
["Direct cost", "2.58", "1.94", "1.30"],
["Contribution per record", "0.92", "2.11", "3.30"],
["Pressing contribution", "0.92M", "2.64M", "4.95M"],
["Title-services contribution", "0.10M", "0.28M", "0.48M"],
["**Total contribution**", "**1.02M**", "**2.92M**", "**5.43M**"],
["Cash overheads incl. lease", "0.48M", "0.35M", "0.21M"],
["**EBITDA**", "**0.54M**", "**2.57M**", "**5.22M**"],
["Depreciation", "0.80M", "0.50M", "0.29M"],
["**Operating profit**", "**−0.26M**", "**2.07M**", "**4.93M**"],
["Payback on 4.4M to 8.0M capital", "never", "2.1 to 3.9 yr", "0.9 to 1.6 yr"],
], widths_mm=[60, 36, 36, 38]),
CALL("Reading this table honestly", [
"**The adverse case loses money.** At 1.0M records, the low price, the high cost and the high "
"capital, the plant is EBITDA-positive but does not cover depreciation. Utilisation and price "
"realisation are not upside levers; they decide solvency.",
"**The favourable case is not credible**, and that is a finding about the cost table, not the "
"business. A 69% EBITDA margin is roughly triple what pressing plants earn; even the central "
"case at ~47% is well above industry norms of 15 to 25%. The direct cost table counts only "
"per-record consumables and direct payroll and almost certainly understates: missing at "
"minimum are indirect production labour, consumables beyond those listed, scrap above the "
"reject allowance, water and effluent, inbound freight, inventory carrying cost and sales "
"commission. Treat the adverse column as the honest one until those lines exist.",
"**This is a sensitivity, not a forecast.** BOI tax exemptions shield part of early-year "
"profits where there are any. The mix lever is real: colour, splatter and boutique runs carry "
"20 to 50% premiums and suit the semi-automatic line, and the title-services line has "
"software-like margins once the rooms exist.",
]),
H2("9.5 The ramp"),
P("The operating picture above is the mature plant. The launch plant (one line, one shift) presses "
"90k to 135k records a year, which at the same blended price is USD 0.3M to 0.6M of pressing "
"revenue plus title services, cassette and CD-R lines, and possibly stamper supply to the "
"incumbent if galvanics comes early. It will not cover a full overhead base in year one, and "
"early production runs slower and rejects more than any of these figures while the process "
"settles. The Duplex expansion (second press on the existing hydraulics, ~USD 115k) is the "
"first revenue-funded step and roughly doubles capacity for a small sum; it should be planned "
"for year two. A year-by-year cash model belongs to the financing discussion and is a next "
"step, not a claim this plan makes."),
# ----------------------------------------------- 10 timeline
H1("10. Timeline: today to first paying release"),
P("Anchored at September 2026. Every duration is an estimate; one of them is not documented "
"anywhere and governs the whole schedule. **First paying release lands around Q4 2028 to "
"mid-2029, roughly 27 to 33 months from today.** The critical path is not construction:"),
CALL("The critical path", "BOI certificate → press order → press build → sea freight → "
"commissioning. Everything else, including the entire building, has float against that chain. "
"The two things that move the date are the BOI filing date and the press vendor's build time, "
"and the second is not documented anywhere: 9 to 12 months is an industry-typical figure for "
"a built-to-order system from a small German manufacturer and it carries about 40% of the "
"schedule. If the real figure is 15 to 18 months, launch moves to mid-2029. The re-quote now "
"being requested asks for quoted build time and order-book position; it is one line in an "
"email and the single highest-value missing fact in the project."),
T(["#", "Step", "Duration", "Source"], [
["1", "Engage BOI consultant, prepare application", "4 to 5 months", "Estimate"],
["2", "BOI review to certificate", "4 to 6 months", "BOI: ~40 working days decision, 4 to 6 "
"months in practice"],
["3", "Company incorporation under the certificate", "1 to 2 months", "Roadmap"],
["4", "Press order to vendor-ready", "**9 to 12 months**", "**Assumed; not yet quoted**"],
["5", "Sea freight Germany to Thailand", "6 to 8 weeks", "Newbilt quote includes freight"],
["6", "Install, commission, train", "2 to 3 months", "Newbilt includes 5 days; rest at "
"~USD 97/h plus travel"],
["7", "Test pressings, QC baseline, reject shakedown", "1.5 to 2 months", "Roadmap"],
["", "**Total**", "**~27 to 33 months**", ""],
], widths_mm=[8, 70, 34, 58]),
T(["Track (in parallel)", "Needed by", "Float", "Becomes critical if"], [
["Land lease, zoning, soil test", "Design start, ~Q1 2027", "~4 months", "Zoning fails and a "
"new site is needed (~6 months reset)"],
["Phase 1 construction", "Machinery arrival, ~Q3 2028", "~3 to 5 months", "Soft soil forces "
"piling, or permits stall"],
["Factory licence (Ror.Ngor. 4)", "First production, Q4 2028", "~4 months", "Galvanics "
"effluent scope is raised early; keep it out of the phase 1 licence scope unless galvanics "
"moves forward"],
["Solar and battery", "Fit-out, ~Q2 2028", "Large", "Not a risk; battery lead is 50 to 60 "
"days"],
["Cassette and CD-R lines", "Fit-out, ~Q2 2028", "Large", "Not a risk; short lead times"],
["Hiring and training", "Commissioning, ~Q3 2028", "~2 months", "Galvanics expertise if "
"galvanics moves forward: the "
"hardest skill to hire"],
], widths_mm=[40, 40, 24, 66]),
P("**BOI windows are comfortable** on this schedule: machinery clears customs ~Q3 2028 against a "
"30-month import window ending ~Q4 2029, and first production ~Q4 2028 against a ~36-month "
"full-operation deadline. The margin is consumed by exactly one scenario: filing early to start "
"the clock and then discovering the site does not work. Verify zoning before filing."),
P("**Leasing does not by itself pull the launch forward.** Construction has only 3 to 5 months of "
"float against the press chain; removing purchase money from the plan does not move the end date. "
"Only the press order moves it, which is the BOI sequencing question in section 7. The two are "
"one lever, not two."),
P("**What a delay costs.** A month lost engaging the BOI consultant costs a month of launch. A "
"month lost on land or construction costs nothing until the float is gone. A month lost getting "
"the press lead time in writing costs nothing today and may cost six months of surprise later."),
# ------------------------------------ 11 phasing and the galvanics question
H1("11. Phasing, and the one sequencing question that matters"),
P("Nothing in phases 2 to 5 starts before its gate clears: no machinery order before the BOI "
"certificate, no large spending before the market validates the price, no coppice planning "
"before the land-title check. The phased entry is the default path; the full vision is reached "
"through it, not instead of it."),
T(["Phase", "Content", "Gate"], [
["0. Validate and clear the gates (now)", "Market validation at Making Vinyl and with regional "
"labels; BOI consultant, activity code, application; lease terms, zoning, soil test; 60 rai "
"title check; re-quotes from Newbilt, Pheenix, Digital Matrix, Stamper Discs, building "
"contractors, Thai utility vendors; solar and battery engineering checks", "Runs in parallel; "
"gates everything"],
["1. Quotes and site (during the BOI window)", "Firm quotes; site selection between the Mueang "
"Chi lease and the industrial estate; resolve the 35% vs 45% humidity floor", "BOI filed"],
["2. Commit", "Incorporate under the certificate; sign the press order; sign the lease; order "
"long-lead items; written stamper turnaround agreement with Stamper Discs", "BOI certificate "
"and validated "
"pricing"],
["3. Build and commission (~6 to 12 months after commitment)", "Construction, utilities, HVAC, "
"solar, battery, security; machinery installation and vendor commissioning; traceability v1; "
"cassette and CD-R lines; hire and train", "Building permit, press build confirmed"],
["4. Launch (pressing with bridge stampers)", "Test pressings, QC baselines, reject shakedown; "
"first paying releases; per-lot PVC gate live; qualify Thai compound", "Commissioning complete"],
["5. Expand (revenue-gated)", "Duplex second press; galvanics line with zero-discharge water; "
"automatic presses; full metrology and the audio QC station; biomass boiler; mastering and "
"cutting room; phase 2 building; CD replication if chosen", "Revenue and second-round "
"capital"],
], widths_mm=[42, 96, 32], font_pt=8.5),
H2("Should galvanics come first rather than fifth?"),
P("The roadmap places the plating line in phase 5. The August 2026 market evidence argues for "
"costing an earlier start, and the investor should decide with the following in view:"),
T(["For moving galvanics to phase 1", "Against"], [
["It addresses the market's actual bottleneck: imported metalwork, not press capacity. The "
"incumbent turns away small runs and adds weeks above 3,000 units, most plausibly because "
"every stamper set is an overseas order.", "It still needs a clean room, zero-discharge water "
"treatment and hazardous-substance permitting, "
"which is the most likely regulatory friction "
"point in the whole project."],
["It is a fraction of the full capital (USD 320k to 650k plus 90k to 240k water) and is "
"driven by title count, not record volume: payback at 300 titles a year is one to five "
"years depending on which end of the saving range holds.", "It is the hardest skill to hire. "
"No plating expertise exists in "
"the country; the process "
"engineer comes from abroad or is "
"trained abroad."],
["It could earn before CNX's own press hall exists, by supplying stampers into the incumbent's "
"spare press capacity: 300 titles a year at wholesale might net USD 60k to 100k of "
"contribution, a useful offset against the capital.", "That offset is not a business case: "
"it assumes a competitor moves its "
"entire stamper supply to a new "
"entrant who is also its rival, and "
"the incumbent's actual title count "
"is unknown."],
["It removes the launch plan's dependence on a single small UK supplier with no documented "
"Asian customers and two international freight legs per job.", "The saving is measured "
"against the wrong supplier: "
"CNX's avoided cost at launch "
"is the Stamper Discs price, "
"which has not been quoted "
"yet."],
], widths_mm=[85, 85], font_pt=8.5),
P("**Recommendation:** get the Stamper Discs quote and a line-itemised full-service invoice from "
"the incumbent in phase 0, cost the galvanics room properly with Digital Matrix and a Thai water "
"vendor, and put the decision to the investor with real numbers before the BOI application is "
"finalised, because the galvanics effluent changes the licence scope."),
# --------------------------------------- 12 team, ownership, governance
H1("12. Team, ownership and governance"),
H2("12.1 The people"),
T(["Who", "Role"], [
["Simona Meesaiyati", "Project Lead. Owns the commercial side: market validation, label "
"relationships, the Making Vinyl outreach, the BOI track and the site."],
["Alexander Grabowski", "Technical Consultant. Owns the plant: equipment selection and "
"vendor negotiation, the utility and efficiency architecture, the parametric building model, "
"the traceability and QC specification, and the factory software."],
["buildfor.life", "Supports the project in a consulting capacity: engineering, sustainability "
"and project management."],
["Ploy Printing (Ploy Karnpim Limited Partnership, Chiang Mai)", "Packaging partner and "
"intended strategic co-investor: jackets, sleeves, inserts, J-cards and CD packaging on "
"offset, digital, risograph, UV and latex presses, with existing vinyl packaging production."],
["To be hired", "Plant manager; galvanics process engineer (when galvanics is built); "
"mastering and cutting engineer (when the room is built); QC lead; maintenance; press and "
"finishing operators; sales and customer service for the label relationship and the "
"traceability portal."],
["External", "BOI-registered consultant; Thai counsel for the lease, company and shareholder "
"agreement; optional vinyl manufacturing consultancy for layout, installation and training "
"(GBP 10k to 20k packages); Newbilt commissioning; property insurer."],
], widths_mm=[48, 122]),
P("Headcount at maturity is roughly 15 to 25 across shifts (4 to 6 press and finishing operators "
"per shift, 2 to 3 galvanics, 1 to 2 mastering, 1 to 2 QC, 2 maintenance, 3 to 5 admin, sales "
"and logistics), fewer than half that at launch, plus 1 to 2 for the cassette and CD-R lines."),
H2("12.2 Ownership and structure"),
P("Amounts and percentages are not yet agreed and are deliberately left open here. The intended "
"structure is:"),
B([
"**One Thai limited company** holds the plant, the lease, the equipment and the BOI promotion. "
"BOI promotion permits 100% foreign ownership of the promoted manufacturing activity, so the "
"shareholding can be set on commercial terms rather than to satisfy the Foreign Business Act.",
"**Shareholders:** the lead investor; Ploy Printing as strategic co-investor; and the founders. "
"Ploy Printing's participation, whether in cash, in kind (print capacity, premises, working "
"capital in packaging) or both, is to be discussed; its strategic value is a packaging partner "
"whose incentives are aligned with the plant's schedule and quality, within the same province.",
"**Debt-to-equity no higher than 3:1**, maintained for the life of the BOI privileges, which "
"sets the floor on equity in the financing mix.",
"**A shareholders' agreement** covering board composition, reserved matters (capital calls, "
"phase 5 expansions, CD replication), pre-emption and transfer, information rights, and the "
"founders' roles and remuneration.",
"**Related-party discipline.** Ploy Printing will be both a shareholder and the plant's main "
"packaging supplier. The supply agreement is priced at arm's length, benchmarked periodically "
"against other printers, with quality and lead-time terms written down, and Ploy Printing "
"abstains from board decisions on its own contract. This protects both the lead investor and "
"Ploy Printing, and it is what a BOI or bank reviewer will look for.",
"**Any label, content or retail activity (Poy Records as a brand) sits in a separate entity**, "
"so the manufacturing company's BOI application stays clean.",
]),
# ------------------------------------------------------ 13 risks
H1("13. Risks and open questions"),
P("Every plan has risks; the point of listing them is to say which are being retired, by what, and "
"when. The first column is the plan's own honest severity."),
T(["Severity", "Risk", "What it would do", "Mitigation and status"], [
["High", "Pricing power in the region is unproven", "The single biggest assumption in the "
"revenue model; a turnaround advantage nobody pays a premium for is a cost, not a moat",
"Market validation gate: Making Vinyl, regional label conversations, a second Thai data "
"point; no big-block spending before it clears"],
["High", "Utilisation", "The cost structure is mostly fixed; 60% utilisation at mature size "
"cuts operating profit by more than half; the adverse case is loss-making at full volume",
"Phase capacity behind demand: one line, then Duplex, then automatic presses, each "
"revenue-gated; hold spare capacity as the turnaround promise requires"],
["High", "Press build time unknown", "Carries ~40% of the schedule; 15 to 18 months would "
"push launch to mid-2029", "Required field in the Newbilt and Pheenix re-quotes, now"],
["High", "Zoning of the candidate parcel unverified", "Gates phase 1 as much as phase 2; a "
"site reset costs ~6 months", "Verify before the BOI filing; industrial estate as fallback"],
["Medium", "Lease terms and building ownership", "A weak lease makes the building hard to "
"finance and exposes it at expiry", "Registered 30 to 50 year lease with renewal, explicit "
"building ownership and assignment rights; Thai counsel "
"in phase 0"],
["Medium", "Soil conditions", "Piling could add several hundred to 1,000+ THB/m² across "
"building, paving and fill", "Soil boring test in phase 0 (THB 30k to 50k)"],
["Medium", "Bridge stamper supplier", "Launch lead time rests on one small UK supplier with "
"no Asian customers; the 10 to 20 day external metal block is unmeasured", "Written "
"turnaround "
"agreement; cost "
"the galvanics-first "
"option (section 11)"],
["Medium", "Galvanics permitting and skills", "Effluent permitting is the most likely "
"regulatory friction; plating expertise is the hardest hire", "Zero-discharge design from the "
"start; industrial estate "
"option; recruit or train abroad; "
"keep out of the phase 1 licence "
"unless brought forward"],
["Medium", "Currency", "EUR equipment, THB costs, USD/EUR revenue; 10% moves are worth tens "
"of thousands on the press order and 0.09 to 0.17 USD per record on margin", "State currency "
"and validity "
"in quotes; "
"consider THB "
"pricing "
"regionally; "
"hedge the press "
"order"],
["Medium", "Vinyl demand is cyclical", "Global capacity has been added; the model assumes the "
"plant sells what it presses", "Mixed fleet suits boutique work that is less cyclical; "
"cassette and CD-R lines diversify; phase behind demand"],
["Medium", "Reject rate in early production", "Early runs will be worse than the 5 to 10% "
"allowance while the process settles", "Vendor commissioning, materials lab from day one, "
"QC layers, conservative launch promises"],
["Medium", "Competitive response", "The incumbent may add plating, or a second entrant may "
"appear; the domestic price ceiling may compress on our arrival", "Compete on capability "
"(traceability, QC, "
"climate), export "
"customers, and the "
"collaboration under "
"discussion"],
["Low", "Solar and battery integration", "Rack voltage vs inverter window; unpublished "
"certifications; mixed-vendor warranty", "Engineering checks and certificate requests in "
"phase 0; large float"],
["Low", "Cassette supply chain", "Blank tape and shells come from few suppliers", "Hold "
"stock; qualify two sources"],
["Low", "CD replication scope creep", "Adds USD 1.5M to 4.0M and a second commissioning "
"programme", "Decide before BOI filing; treat as a separate project if chosen"],
], widths_mm=[16, 40, 52, 62], font_pt=8),
P("**Open questions with owners and dates** are in the next section. Two standing caveats on the "
"evidence base: the Thai market data is one buyer, one plant and one afternoon, and the "
"underlying invoices and tour notes should be archived with this plan before it goes in front "
"of a bank; and the incumbent is a competitor, so every collaboration scenario assumes they would "
"buy a critical input from a new entrant, which they may decline to do."),
# ------------------------------------------------- 14 next steps
H1("14. Next steps and the ask"),
H2("14.1 Phase 0: the next three to six months"),
T(["#", "Action", "Owner", "Cost", "Retires"], [
["1", "Making Vinyl (Haarlem) and regional label conversations; test the 3.50 to 4.60 USD "
"blended price, run sizes, and whether 7 weeks instead of 14 plus freight is worth a premium",
"Simona, Alexander", "Travel", "Pricing risk"],
["2", "Engage a BOI-registered consultant; confirm the activity sub-code; put the "
"order-before-certificate question first; add cassette and CD-R; decide CD replication",
"Simona", "USD 5k to 15k", "Sequencing, scope"],
["3", "Thai counsel: lease heads of terms with the Mueang Chi lessor (term, renewal, building "
"ownership, assignment), zoning verification, company and shareholder structure including "
"Ploy Printing", "Simona, counsel", "USD 5k to 15k", "Zoning, lease, ownership"],
["4", "Soil boring test on the candidate parcel", "Alexander", "THB 30k to 50k", "Ground risk"],
["5", "Newbilt re-quote with build time, order-book position, PLC data access, currency and "
"validity; same questions to Pheenix Alpha for the AD12 and AD12 Manual", "Alexander",
"Free", "Schedule, price"],
["6", "Stamper Discs quote and written turnaround terms; line-itemised full-service invoice "
"from the incumbent; Digital Matrix galvanics quote and a Thai water-treatment vendor",
"Alexander", "Free", "Galvanics decision"],
["7", "Send the construction price request to two or three Thai PEB contractors", "Alexander",
"Free", "Building estimate"],
["8", "Thai utility RFQs: boiler, chiller, compressed air, HVAC (35% vs 45% humidity floor), "
"solar EPC; Deye and Gobel engineering checks", "Alexander", "Free", "Utilities band"],
["9", "Cassette and CD-R equipment quotes from two vendors each; tape and shell supply",
"Alexander", "Free", "New-scope estimates"],
["10", "60 rai title check for the coppice plan", "Simona", "Small", "Biomass path"],
["11", "Optional: vinyl manufacturing consultancy engagement for layout and installation "
"review", "Alexander", "GBP 10k to 20k", "Layout risk"],
["12", "Archive the Thai invoices and tour notes with this plan", "Simona", "None", "Evidence "
"base"],
], widths_mm=[8, 84, 26, 24, 28], font_pt=8),
H2("14.2 What the investor is asked for"),
KV([
["Now", "Approval of Phase 0 spending of ~USD 40k to 80k, and a decision on CD scope (section "
"4.9) before the BOI application is finalised."],
["On the gates clearing", "Commitment in principle to phase 1 at ~USD 2.2M to 3.9M, released "
"in the tranches of section 8.3, with the financing mix set to hold "
"debt-to-equity at or below 3:1."],
["Decisions", "Whether galvanics moves into phase 1 (section 11), once the phase 0 quotes are "
"in; the shareholder structure with Ploy Printing and the founders; hedging of "
"the press order."],
["Later", "Revenue-gated expansion to the full vision (~USD 4.4M to 8.0M in total) as the "
"launch plant proves the market: Duplex, galvanics, automatic presses, full "
"metrology, mastering room, biomass, and the phase 2 building."],
], widths_mm=[38, 132]),
P("The next version of this plan should carry: firm re-quotes in place of the expired and "
"estimated blocks, the negotiated lease figure, a second market data point, the investor's CD "
"decision, a year-by-year cash model for the ramp, and the shareholder terms. Each of those is "
"a phase 0 output."),
# --------------------------------------------------- 15 appendix
H1("15. Appendix: sources and planning documents"),
P("This plan is compiled from the project's planning repository (cnx_vinyl, GitLab), which is the "
"source of truth and holds the detail behind every section:"),
T(["Document", "Content"], [
["roadmap.md", "Next steps in dependency order, from the three current gates through launch "
"and expansion"],
["scale.md", "Project sizing on one page: capacity, building, land, electrical, solar, steam, "
"people"],
["finance/total-investment.md", "Full-vision and phased-entry capital roll-up with the basis "
"of every block"],
["finance/revenue-model.md", "Pricing, direct cost, margin, operating scenarios, and the "
"August 2026 Thai market data and factory tour"],
["finance/utilities-investment.csv", "Thai supplier candidates and budgetary ranges for "
"steam, cooling, air, water, HVAC, solar and battery"],
["finance/phase1-construction-rfq.md", "Contractor price request with quantities from the "
"parametric model"],
["equipment/pressing-line.md and equipment/newbilt/", "Press fleet strategy; Newbilt "
"budgetary offer, site requirements, "
"process schematic, service rates"],
["suppliers/vendor-landscape.md and equipment-suppliers.md", "Per-vendor narrative and the "
"full needs list with contacts"],
["process-flow.md, production-timeline.md, project-timeline.md", "The chain and its checks; "
"order turnaround stage by "
"stage; the calendar and "
"critical path"],
["traceability.md, quality-automation.md", "The traceability specification; metrology, "
"layered record QC, the materials lab, packaging QC"],
["sustainability.md, biomass.md", "Solar, heat recovery, water, zero discharge, materials; "
"the biomass fuel strategy and the 60 rai coppice"],
["facility-security.md, worker-safety.md", "Climate control, security, fire and life safety, "
"compliance anchors; safety program"],
["boi.md", "BOI promotion strategy, requirements, process, open items"],
["cnx_vinyl_parametric.FCStd and renders/", "The parametric building and site model on the "
"candidate parcel, and its renders"],
], widths_mm=[62, 108], font_pt=8.5),
P("Vendor PDFs and spreadsheets are primary sources and reflect the state of negotiations at "
"their file dates. Treat prices and lead times as snapshots, not commitments."),
]
+320
View File
@@ -0,0 +1,320 @@
"""Tiny document DSL and renderer for the CNX Vinyl business plan.
Blocks are plain tuples so the content files stay readable:
H1("text") / H2 / H3 headings
P("text with **bold** runs") paragraph
B([...]) bullet list
N([...]) numbered list (manual numbers, restart per list)
T(header, rows, widths_mm=None, caption=None, font_pt=9)
KV([[k, v], ...], widths_mm) two-column table without header
IMG(path, width_mm, caption) picture
CALL(title, body) shaded call-out box; body is str or list
PB() page break
TOC() table-of-contents field
"""
from __future__ import annotations
import re
from pathlib import Path
from docx import Document
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Mm, Pt, RGBColor
NAVY = RGBColor(0x1F, 0x38, 0x64)
BLUE = RGBColor(0x2E, 0x55, 0x97)
GREY = RGBColor(0x59, 0x59, 0x59)
HEADER_FILL = "1F3864"
CALLOUT_FILL = "DEEAF6"
ALT_FILL = "F2F5FA"
BODY_FONT = "Calibri"
# ----------------------------------------------------------------- DSL ---
def H1(t): return ("h", 1, t)
def H2(t): return ("h", 2, t)
def H3(t): return ("h", 3, t)
def P(t, style=None): return ("p", t, style)
def B(items): return ("bullets", items)
def N(items): return ("numbers", items)
def T(header, rows, widths_mm=None, caption=None, font_pt=9):
return ("table", header, rows, widths_mm, caption, font_pt)
def KV(rows, widths_mm=(50, 115), font_pt=9.5): return ("kv", rows, widths_mm, font_pt)
def IMG(path, width_mm, caption=None): return ("img", path, width_mm, caption)
def CALL(title, body): return ("callout", title, body)
def PB(): return ("pb",)
def TOC(): return ("toc",)
def COVER(**kw): return ("cover", kw)
# ------------------------------------------------------------ helpers ---
def _shade(cell, fill):
tcPr = cell._tc.get_or_add_tcPr()
shd = OxmlElement("w:shd")
shd.set(qn("w:val"), "clear")
shd.set(qn("w:color"), "auto")
shd.set(qn("w:fill"), fill)
tcPr.append(shd)
def _field(paragraph, instr, placeholder):
run = paragraph.add_run()
b = OxmlElement("w:fldChar"); b.set(qn("w:fldCharType"), "begin")
i = OxmlElement("w:instrText"); i.set(qn("xml:space"), "preserve"); i.text = instr
s = OxmlElement("w:fldChar"); s.set(qn("w:fldCharType"), "separate")
t = OxmlElement("w:t"); t.text = placeholder
e = OxmlElement("w:fldChar"); e.set(qn("w:fldCharType"), "end")
for el in (b, i, s, t, e):
run._r.append(el)
return run
def _set_font(style, name, size=None, bold=None, color=None, italic=None):
f = style.font
f.name = name
rpr = style.element.get_or_add_rPr()
rfonts = rpr.find(qn("w:rFonts"))
if rfonts is None:
rfonts = OxmlElement("w:rFonts"); rpr.append(rfonts)
for k in ("w:ascii", "w:hAnsi", "w:eastAsia", "w:cs"):
rfonts.set(qn(k), name)
if size is not None: f.size = Pt(size)
if bold is not None: f.bold = bold
if italic is not None: f.italic = italic
if color is not None: f.color.rgb = color
_INLINE = re.compile(r"(\*\*.+?\*\*)")
def _runs(paragraph, text, size=None, color=None):
for part in _INLINE.split(text):
if not part:
continue
bold = part.startswith("**") and part.endswith("**")
run = paragraph.add_run(part[2:-2] if bold else part)
run.bold = bold or None
if size is not None: run.font.size = Pt(size)
if color is not None: run.font.color.rgb = color
return paragraph
def _cell_text(cell, text, size, bold=False, color=None, align=None):
cell.text = ""
para = cell.paragraphs[0]
para.paragraph_format.space_after = Pt(1)
para.paragraph_format.space_before = Pt(1)
if align: para.alignment = align
for j, line in enumerate(str(text).split("\n")):
if j:
para = cell.add_paragraph()
para.paragraph_format.space_after = Pt(1)
_runs(para, line, size=size, color=color)
if bold:
for r in para.runs: r.bold = True
def _caption(doc, text):
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(2)
p.paragraph_format.space_after = Pt(8)
r = p.add_run(text)
r.italic = True
r.font.size = Pt(8.5)
r.font.color.rgb = GREY
return p
def _keep_rows_together(table):
for row in table.rows:
trPr = row._tr.get_or_add_trPr()
cs = OxmlElement("w:cantSplit"); trPr.append(cs)
# ----------------------------------------------------------- renderer ---
class Renderer:
def __init__(self, base_dir: Path, header_text: str, footer_text: str):
self.base = Path(base_dir)
self.doc = Document()
self._setup_page()
self._setup_styles()
self._setup_header_footer(header_text, footer_text)
self._update_fields_on_open()
# -- setup
def _setup_page(self):
s = self.doc.sections[0]
s.page_width, s.page_height = Mm(210), Mm(297)
s.top_margin = s.bottom_margin = Mm(22)
s.left_margin = s.right_margin = Mm(20)
s.header_distance = s.footer_distance = Mm(10)
def _setup_styles(self):
st = self.doc.styles
_set_font(st["Normal"], BODY_FONT, 10.5)
pf = st["Normal"].paragraph_format
pf.space_after = Pt(6); pf.line_spacing = 1.08
_set_font(st["Heading 1"], BODY_FONT, 17, True, NAVY)
st["Heading 1"].paragraph_format.space_before = Pt(18)
st["Heading 1"].paragraph_format.space_after = Pt(8)
_set_font(st["Heading 2"], BODY_FONT, 13, True, BLUE)
st["Heading 2"].paragraph_format.space_before = Pt(12)
st["Heading 2"].paragraph_format.space_after = Pt(4)
_set_font(st["Heading 3"], BODY_FONT, 11, True, RGBColor(0x40, 0x40, 0x40))
st["Heading 3"].paragraph_format.space_before = Pt(8)
st["Heading 3"].paragraph_format.space_after = Pt(3)
for name in ("List Bullet", "List Number"):
_set_font(st[name], BODY_FONT, 10.5)
st[name].paragraph_format.space_after = Pt(3)
def _setup_header_footer(self, header_text, footer_text):
s = self.doc.sections[0]
hp = s.header.paragraphs[0]
hp.alignment = WD_ALIGN_PARAGRAPH.RIGHT
r = hp.add_run(header_text); r.font.size = Pt(8.5); r.font.color.rgb = GREY
fp = s.footer.paragraphs[0]
fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = fp.add_run(footer_text + " | Page "); r.font.size = Pt(8.5); r.font.color.rgb = GREY
r = _field(fp, "PAGE", "1"); r.font.size = Pt(8.5); r.font.color.rgb = GREY
r = fp.add_run(" of "); r.font.size = Pt(8.5); r.font.color.rgb = GREY
r = _field(fp, "NUMPAGES", "1"); r.font.size = Pt(8.5); r.font.color.rgb = GREY
def _update_fields_on_open(self):
el = OxmlElement("w:updateFields"); el.set(qn("w:val"), "true")
self.doc.settings.element.append(el)
# -- blocks
def render(self, blocks):
for blk in blocks:
kind = blk[0]
getattr(self, "_b_" + kind)(*blk[1:])
return self.doc
def _b_cover(self, kw):
d = self.doc
for _ in range(3): d.add_paragraph()
p = d.add_paragraph(); p.alignment = WD_ALIGN_PARAGRAPH.LEFT
r = p.add_run(kw["kicker"]); r.font.size = Pt(12); r.font.color.rgb = BLUE; r.bold = True
p = d.add_paragraph()
r = p.add_run(kw["title"]); r.font.size = Pt(30); r.font.color.rgb = NAVY; r.bold = True
p.paragraph_format.space_after = Pt(6)
p = d.add_paragraph()
r = p.add_run(kw["subtitle"]); r.font.size = Pt(13); r.font.color.rgb = GREY
p.paragraph_format.space_after = Pt(14)
if kw.get("image"):
d.add_picture(str(self.base / kw["image"]), width=Mm(170))
p = d.add_paragraph(); p.paragraph_format.space_before = Pt(10)
for line in kw["lines"]:
_runs(p, line, size=10.5); p.add_run().add_break()
p = d.add_paragraph()
r = p.add_run(kw["note"]); r.font.size = Pt(8.5); r.italic = True; r.font.color.rgb = GREY
d.add_paragraph().add_run().add_break(WD_BREAK.PAGE)
def _b_toc(self):
d = self.doc
p = d.add_paragraph(); r = p.add_run("Contents"); r.font.size = Pt(17); r.bold = True; r.font.color.rgb = NAVY
p = d.add_paragraph()
_field(p, 'TOC \\o "1-2" \\h \\z \\u',
"Table of contents: open in Word and press F9 (or accept the update prompt) to populate.")
d.add_paragraph().add_run().add_break(WD_BREAK.PAGE)
def _b_h(self, level, text):
self.doc.add_heading(text, level=level)
def _b_p(self, text, style):
p = self.doc.add_paragraph(style=style) if style else self.doc.add_paragraph()
_runs(p, text)
def _b_bullets(self, items):
for it in items:
_runs(self.doc.add_paragraph(style="List Bullet"), it)
def _b_numbers(self, items):
for i, it in enumerate(items, 1):
p = self.doc.add_paragraph()
p.paragraph_format.left_indent = Mm(8)
p.paragraph_format.first_line_indent = Mm(-6)
p.paragraph_format.space_after = Pt(3)
_runs(p, f"{i}. {it}")
def _b_table(self, header, rows, widths, caption, font_pt):
d = self.doc
n = len(header)
t = d.add_table(rows=1, cols=n)
t.style = d.styles["Table Grid"]
t.alignment = WD_TABLE_ALIGNMENT.CENTER
for j, h in enumerate(header):
c = t.rows[0].cells[j]
_shade(c, HEADER_FILL)
_cell_text(c, h, font_pt, bold=True, color=RGBColor(0xFF, 0xFF, 0xFF))
for i, row in enumerate(rows):
cells = t.add_row().cells
for j in range(n):
val = row[j] if j < len(row) else ""
_cell_text(cells[j], val, font_pt)
if i % 2 == 1: _shade(cells[j], ALT_FILL)
if widths:
for row in t.rows:
for j, w in enumerate(widths):
row.cells[j].width = Mm(w)
# repeat header row
trPr = t.rows[0]._tr.get_or_add_trPr()
th = OxmlElement("w:tblHeader"); th.set(qn("w:val"), "true"); trPr.append(th)
_keep_rows_together(t)
if caption: _caption(d, caption)
else: d.add_paragraph().paragraph_format.space_after = Pt(2)
def _b_kv(self, rows, widths, font_pt):
d = self.doc
t = d.add_table(rows=0, cols=2)
t.style = d.styles["Table Grid"]
t.alignment = WD_TABLE_ALIGNMENT.CENTER
for k, v in rows:
cells = t.add_row().cells
_shade(cells[0], ALT_FILL)
_cell_text(cells[0], k, font_pt, bold=True)
_cell_text(cells[1], v, font_pt)
for row in t.rows:
for j, w in enumerate(widths):
row.cells[j].width = Mm(w)
_keep_rows_together(t)
d.add_paragraph().paragraph_format.space_after = Pt(2)
def _b_img(self, path, width_mm, caption):
self.doc.add_picture(str(self.base / path), width=Mm(width_mm))
self.doc.paragraphs[-1].alignment = WD_ALIGN_PARAGRAPH.CENTER
if caption: _caption(self.doc, caption)
def _b_callout(self, title, body):
d = self.doc
t = d.add_table(rows=1, cols=1)
t.style = d.styles["Table Grid"]
t.alignment = WD_TABLE_ALIGNMENT.CENTER
c = t.rows[0].cells[0]
_shade(c, CALLOUT_FILL)
c.width = Mm(170)
c.text = ""
p = c.paragraphs[0]
p.paragraph_format.space_before = Pt(3); p.paragraph_format.space_after = Pt(3)
r = p.add_run(title); r.bold = True; r.font.color.rgb = NAVY; r.font.size = Pt(10.5)
items = body if isinstance(body, list) else [body]
for it in items:
q = c.add_paragraph()
q.paragraph_format.space_after = Pt(3)
if isinstance(body, list):
q.paragraph_format.left_indent = Mm(4)
_runs(q, "\u2022 " + it, size=10)
else:
_runs(q, it, size=10)
_keep_rows_together(t)
d.add_paragraph().paragraph_format.space_after = Pt(2)
def _b_pb(self):
self.doc.add_paragraph().add_run().add_break(WD_BREAK.PAGE)
def save(self, path):
self.doc.save(str(path))