diff --git a/index.html b/index.html
index 3e4671b..4ad0232 100644
--- a/index.html
+++ b/index.html
@@ -25,6 +25,13 @@
+
+ Standalone experiments
+
+
+
Standalone vibe coded utilities/experiments
diff --git a/standalone/tables-1/common.css b/standalone/tables-1/common.css
new file mode 100644
index 0000000..37c3d9f
--- /dev/null
+++ b/standalone/tables-1/common.css
@@ -0,0 +1,50 @@
+body {
+ background-color: #224;
+ --table-border-color: #467;
+}
+
+table {
+ background-color: #aad;
+ min-width: 1em;
+ min-height: 1em;
+ border-collapse: collapse;
+}
+
+table td ol {
+ margin: 0em 0.5em;
+ padding-left: 1em;
+}
+
+table td ol li {
+ font-style: italic;
+}
+
+table th {
+ min-width: 1em;
+ padding: 0em 0.5em;
+}
+
+table thead th:nth-child(n+2) {
+ border-left: 3px solid var(--table-border-color);
+}
+
+table tbody th {
+ text-align: right;
+}
+
+table thead tr {
+ border-bottom: 3px solid var(--table-border-color);
+}
+
+table tbody tr:nth-last-child(n+2) {
+ border-bottom: 1px solid var(--table-border-color);
+}
+
+table td {
+ padding: 0em 0.5em;
+ border-left: 1px solid var(--table-border-color);
+}
+
+table th, table td {
+ vertical-align: top;
+}
\ No newline at end of file
diff --git a/standalone/tables-1/t10.html b/standalone/tables-1/t10.html
new file mode 100644
index 0000000..9d78671
--- /dev/null
+++ b/standalone/tables-1/t10.html
@@ -0,0 +1,17 @@
+
+
+
+ t10
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/standalone/tables-1/t10.json b/standalone/tables-1/t10.json
new file mode 100644
index 0000000..6df5fef
--- /dev/null
+++ b/standalone/tables-1/t10.json
@@ -0,0 +1,123 @@
+[
+ {
+ "planet": "Zebulon V",
+ "atmosphere": "Methane-rich",
+ "temperature": {
+ "value": -120,
+ "unit": "C"
+ },
+ "population": 0,
+ "notes": [
+ "Uninhabitable, but stunning purple clouds"
+ ]
+ },
+ {
+ "planet": "Arcturus Prime",
+ "atmosphere": "Oxygen-Nitrogen",
+ "temperature": {
+ "value": 22,
+ "unit": "C"
+ },
+ "population": 4300000,
+ "notes": [
+ "Peaceful and highly advanced civilization",
+ "Systems main exporter of misery"
+ ]
+ },
+ {
+ "planet": "Draconis IX",
+ "atmosphere": "Carbon Dioxide",
+ "temperature": {
+ "value": 480,
+ "unit": "C"
+ },
+ "population": 0,
+ "notes": [
+ "Volcanic activity off the charts"
+ ]
+ },
+ {
+ "planet": "Elara",
+ "atmosphere": "Thin Oxygen",
+ "temperature": {
+ "value": -30,
+ "unit": "C"
+ },
+ "population": 1200,
+ "notes": [
+ "Small scientific outpost"
+ ]
+ },
+ {
+ "planet": "Nova Terra",
+ "atmosphere": "Oxygen-Nitrogen",
+ "temperature": {
+ "value": 16,
+ "unit": "C"
+ },
+ "population": 15000000,
+ "notes": [
+ "Primary human colony"
+ ]
+ },
+ {
+ "planet": "Thalos",
+ "atmosphere": "Sulfuric",
+ "temperature": {
+ "value": 300,
+ "unit": "C"
+ },
+ "population": 0,
+ "notes": [
+ "Extreme weather makes landings risky"
+ ]
+ },
+ {
+ "planet": "Minerva",
+ "atmosphere": "Oxygen-rich",
+ "temperature": {
+ "value": 10,
+ "unit": "C"
+ },
+ "population": 85000,
+ "notes": [
+ "Home to rare crystal-based lifeforms"
+ ]
+ },
+ {
+ "planet": "Oberon Station",
+ "atmosphere": "Artificial",
+ "temperature": {
+ "value": 21,
+ "unit": "C"
+ },
+ "population": 560,
+ "notes": [
+ "Space station orbiting gas giant"
+ ]
+ },
+ {
+ "planet": "Xentari Alpha",
+ "atmosphere": "Ammonia",
+ "temperature": {
+ "value": -80,
+ "unit": "C"
+ },
+ "population": 0,
+ "notes": [
+ "Strange magnetic fields"
+ ]
+ },
+ {
+ "planet": "Helios Gate",
+ "atmosphere": "Plasma storms",
+ "temperature": {
+ "value": 1500,
+ "unit": "C"
+ },
+ "population": 0,
+ "notes": [
+ "Suspected to be a wormhole source"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/standalone/tables-1/t10.mjs b/standalone/tables-1/t10.mjs
new file mode 100644
index 0000000..4a02041
--- /dev/null
+++ b/standalone/tables-1/t10.mjs
@@ -0,0 +1,122 @@
+// Here we are going to try to present something
+
+class Style_Definitions {
+ constructor(definitions) {
+ Object.assign(this, { definitions });
+ }
+}
+
+class Capture {
+ constructor(target, associative_array) {
+ Object.assign(this, { target, associative_array });
+ }
+}
+
+function C(target, associative_array) {
+ return new Capture(target, associative_array);
+}
+
+function S(style_definitions) {
+ return new Style_Definitions(style_definitions);
+}
+
+function T(contents) {
+
+ //For now - assume plain text
+ return document.createTextNode(contents);
+
+}
+
+function E(tagName, ...members) {
+
+ const e = document.createElement(tagName);
+ for (const m of members) {
+ //For now we assume some sort of node but could be different stuff later
+ if (m instanceof Capture) {
+ for (const [name, sub_member] of Object.entries(m.associative_array)) {
+ m.target[name] = sub_member;
+ e.appendChild(sub_member);
+ }
+ } else if (m instanceof Style_Definitions) {
+ Object.assign(e.style, m.definitions);
+ } else {
+
+ switch (m.nodeType) {
+ case Node.ELEMENT_NODE:
+ case Node.TEXT_NODE:
+ e.appendChild(m);
+ break;
+
+ default:
+ throw new Error(`Unrecognized member: ${m}`);
+ }
+ }
+ }
+ return e;
+
+}
+
+
+
+export async function test_table() {
+
+ const data = await (await fetch('./t10.json')).json();
+ const headings = ['Planet', 'Atmosphere', 'Temperature', 'Population', 'Notes'];
+
+ const table = E('table',
+ E('thead',
+ E('tr', ...headings.map(h => E('th', T(h)))),
+ ),
+ E('tbody',
+ ...data.map(e => E('tr',
+ E('th', T(e.planet)),
+ E('td', T(e.atmosphere)),
+ E('td', T(`${e.temperature.value.toLocaleString()} ${e.temperature.unit}`)),
+ E('td', T(e.population.toLocaleString())),
+ E('td', E('ol', ...e.notes.map(n => E('li', T(n))))),
+ )),
+ ),
+ );
+
+ document.body.appendChild(table);
+
+ const lut = {};
+ const info = E('div',
+ S({
+ color: '#FF0',
+ }),
+
+ E('span',
+ S({
+ fontWeight: 'bold',
+ minWidth: '8em',
+ display: 'inline-block',
+ textAlign: 'right',
+ paddingRight: '.5em',
+ }),
+ C(lut, {cell: T('Cell')}),
+ ),
+ T('ยท'),
+ E('span',
+ S({
+ fontStyle: 'italic',
+ paddingLeft: '.5em',
+ color: '#C80',
+ }),
+ C(lut, {text: T('???')}),
+ ),
+ );
+
+ document.body.appendChild(info);
+
+ table.addEventListener('mousemove', (e) => {
+ const section = e.target.closest('tbody') ||e.target.closest('thead');
+ const cell = e.target.closest('td') ||e.target.closest('th');
+ const row = e.target.closest('tr');
+ lut.text.data = cell?.innerHTML || '???';
+ lut.cell.data = `${section.tagName} ${row.sectionRowIndex}:${cell.cellIndex}`;
+
+ })
+
+
+}