diff --git a/.gitignore b/.gitignore index 2104bfb..00322e1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,16 @@ __pycache__/ worker/node_modules/ worker/.wrangler/ .dev.vars + +# Marketing automation +marketing/content/*.json +marketing/media/generated/* +marketing/logs/*.log +marketing/config/settings.json +marketing/.env + +# Post exports (generated packs incl. media copies) +marketing/exports/ + +# Concept mockups (drafts pending validation) +mockups/ diff --git a/404.html b/404.html index 349770a..df10a6b 100644 --- a/404.html +++ b/404.html @@ -3,13 +3,17 @@
- +{len(cards)} posts ready. Copy the caption, save the media, post via the Instagram app. Batch {stamp}.
+{total} cards across {len(groups)} groups. Copy the caption, save the card, post via the Instagram app. Batch {stamp}.
+マシンドロー · DRAWN BY MACHINE · EST. 2026
+Universal-Century mobile suits, redrawn as vector line work and traced stroke by stroke in pigment ink on archival Bristol by an AxiDraw plotter. Nothing is printed. Every impression is drawn.
+Universal-Century mobile suits redrawn as precise vector line work and traced in pigment ink on archival Bristol by an AxiDraw plotter. The color shows the plot order — first stroke to last.
+Every order is plotted live. Follow the pen from the first vertex to the signature — stroke by stroke — drawn, never printed.
+ +We use Google Analytics to count visits and see which editions get attention — no ads, no data resale. Analytics only loads if you accept.
' + + ''; + el.querySelector('.ok').addEventListener('click', function () { + store('granted'); loadGA(); removeBanner(); + }); + el.querySelector('.no').addEventListener('click', function () { + store('denied'); removeBanner(); + }); + document.body.appendChild(el); + } + + // "Cookie settings" reopen link, appended to the footer bottom row on every page + function addFooterLink() { + var foot = document.querySelector('.foot-bot'); + if (!foot) return; + var a = document.createElement('span'); + a.className = 'pf-consent-link'; + a.textContent = 'COOKIE SETTINGS'; + a.addEventListener('click', function () { removeBanner(); showBanner(); }); + foot.appendChild(a); + } + + var c = choice(); + if (c === 'granted') loadGA(); + else if (c !== 'denied') showBanner(); + addFooterLink(); +})(); diff --git a/scripts/plotter.js b/scripts/plotter.js index a0d654f..914051b 100644 --- a/scripts/plotter.js +++ b/scripts/plotter.js @@ -23,8 +23,14 @@ var ink = $('ink'), inktot = $('inktot'), elapsed = $('elapsed'), total = $('total'); var bed = $('bed'), bedlabel = $('bedlabel'), selSuit = $('suit'), playBtn = $('play'), replay = $('replay'); - var FEED = 1100; // mm/min, shown in HUD + used for plot-time - var BASE = 22; // seconds for a full plot at 1x speed + var FEED = 1100; // mm/min pen-down draw speed, shown in HUD + var TRAVEL_FEED = 6600; // mm/min pen-up travel (the carriage moves much faster between strokes) + var LIFT_S = 0.10; // seconds per pen lift+lower cycle (servo up + down) + // Plot time = draw + pen-up travel + lift cycles — tuned against PlotGrid + // actuals (~0.45 s/stroke incl. overhead), not just ink-length ÷ feed. + var BASE = 30; // seconds the simulation runs at 1x watch speed — + // the machine-speed multiplier shown in the dock is + // derived from the real plot time (≈ x45–x60) var INK = '#e8351f'; var cur, len = 1, drawn = 0, painted = 0, playing = true, speed = 1, mmPerUnit = 1, totalMin = 1; var vb = { x: 0, y: 0, w: 1, h: 1 }; @@ -44,6 +50,24 @@ return String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0'); } + // Walk the raw path string once: pen-down length, pen-up travel length, and + // lift count (each M after the first is a pen lift + travel + pen lower). + function pathStats(d) { + var draw = 0, travel = 0, lifts = 0, px = null, py = null, pen = false; + var re = /([ML])\s*(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)/g, m; + while ((m = re.exec(d)) !== null) { + var x = +m[2], y = +m[3]; + if (m[1] === 'M') { + if (px !== null) { travel += Math.hypot(x - px, y - py); lifts++; } + pen = false; + } else if (px !== null) { + draw += Math.hypot(x - px, y - py); + } + px = x; py = y; + } + return { draw: draw, travel: travel, lifts: lifts }; + } + // Map a point in suit/user coordinates to canvas device pixels, matching the // SVG stage's preserveAspectRatio="xMidYMid meet" so the pen lines up. function mapX(ux) { return (tf.ox + (ux - vb.x) * tf.s) * tf.dpr; } @@ -115,7 +139,11 @@ len = ppath.getTotalLength(); if (!len || !isFinite(len)) len = 1; var md = Math.max(vb.w, vb.h); mmPerUnit = 420 / md; // longest side ≈ 420mm - totalMin = (len * mmPerUnit) / FEED; + var st = pathStats(cur.d); + totalMin = (st.draw * mmPerUnit) / FEED + + (st.travel * mmPerUnit) / TRAVEL_FEED + + (st.lifts * LIFT_S) / 60; + updateSimX(); penC.setAttribute('r', md * 0.014); penX.setAttribute('d', 'M ' + (-md * 0.032) + ' 0 H ' + (md * 0.032) + ' M 0 ' + (-md * 0.032) + ' V ' + (md * 0.032)); pen.setAttribute('opacity', '0'); @@ -159,6 +187,14 @@ if (playBtn) playBtn.textContent = 'Pause'; } + // honest speed disclosure: the sim runs the real plot in BASE/speed seconds + function updateSimX() { + var el = $('simx'); + if (!el || !totalMin) return; + var x = Math.round((totalMin * 60) / (BASE / speed)); + el.textContent = 'SIM \u2248 \u00d7' + x + ' machine speed'; + } + var last = null; function frame(t) { if (last == null) last = t; @@ -188,6 +224,7 @@ var b = e.target.closest('button'); if (!b) return; [].forEach.call(e.currentTarget.children, function (x) { x.classList.remove('on'); }); b.classList.add('on'); speed = parseFloat(b.dataset.s); + updateSimX(); }); if (selSuit) selSuit.addEventListener('change', function (e) { load(e.target.value); }); if (replay) replay.addEventListener('click', restart); diff --git a/scripts/product.js b/scripts/product.js index fd5429c..27d5bb6 100644 --- a/scripts/product.js +++ b/scripts/product.js @@ -101,7 +101,7 @@ function makePreview(s, inkHex) { var svg = $("pdSvg"), path = $("pdPath"), canvas = $("pdCanvas"), replay = $("pdReplay"); var ctx = canvas.getContext("2d"); - var BASE = 18; // seconds for a full preview plot + var BASE = 18; // seconds for a full preview plot (product page keeps a quicker cut) var ink = inkHex; var len = 1, drawn = 0, painted = 0, playing = true, last = null; var vb = { x: 0, y: 0, w: s.w || 1, h: s.h || 1 }; diff --git a/shipping.html b/shipping.html index 4b3c52b..30297e5 100644 --- a/shipping.html +++ b/shipping.html @@ -3,13 +3,17 @@ - +