prototype: slide 'The problem' — 3 options for datamart outputs zone (branch / products band / maturity rail)

This commit is contained in:
Codex
2026-09-07 17:01:53 +02:00
parent 1e139c16ec
commit 8743f0776b
+289
View File
@@ -0,0 +1,289 @@
<!doctype html>
<!-- PROTOTYPE (throwaway) — Three variants of the "outputs from the datamarts" zone
for deck slide "The problem". Switchable via ?variant=A|B|C, floating switcher.
Question: how should Dashboards / Machine Learning / Predictive Statistics
be represented as the three outputs of the Marts stage? -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>PROTOTYPE · Slide “The problem” — outputs from the datamarts (3 options)</title>
<link rel="stylesheet" href="/deck/fonts.css">
<style>
:root {
--bordeaux: #cb333b; --bordeaux-deep: #8f1204; --bordeaux-tint: #f4dfe1;
--surface: #fdfcfc; --surface-alt: #f6f5f5; --surface-sunken: #ecebeb;
--ink: #2a2426; --ink-muted: #6b6568;
--line: #e3e0e1; --line-strong: #c9c5c6;
--sans: 'Source Sans 3', system-ui, sans-serif;
--display: 'Source Serif 4', Georgia, serif;
--mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}
* { box-sizing: border-box; }
body { margin: 0; background: #e9e7e7; font-family: var(--sans); padding: 26px 0 90px; }
.proto-head { text-align: center; margin: 0 0 14px; }
.proto-head b { font: 700 12px/1 var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--bordeaux); }
.proto-head span { display: block; margin-top: 6px; font: 400 13px/1.4 var(--sans); color: var(--ink-muted); }
/* ---- slide frame (deck-like, 1200×690) ---- */
.frame { width: 1200px; height: 690px; margin: 0 auto; background: var(--surface);
display: flex; flex-direction: column; box-shadow: 0 8px 30px rgba(42,36,38,.18); overflow: hidden; }
.head { flex: 0 0 74px; display: flex; align-items: center; justify-content: space-between;
padding: 0 40px; border-bottom: 1px solid var(--line); }
.head img { height: 54px; display: block; }
.head-org { font: 700 11px/1 var(--sans); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-muted); }
.band { flex: 0 0 42px; display: flex; align-items: center; justify-content: space-between;
padding: 0 40px; background: var(--bordeaux); color: var(--surface); }
.band b { font: 700 12.5px/1 var(--sans); letter-spacing: .14em; text-transform: uppercase; }
.sbody { flex: 1; min-height: 0; display: flex; flex-direction: column;
padding: 20px 48px 14px; text-align: left; }
.foot { flex: 0 0 30px; display: flex; align-items: center; justify-content: space-between; gap: 24px;
padding: 0 40px; border-top: 1px solid var(--line);
font: 600 9.5px/1.4 var(--sans); letter-spacing: .03em; color: var(--ink-muted); }
.kicker { font: 700 11.5px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase; color: var(--bordeaux); margin-bottom: 10px; }
h2 { font: 700 27px/1.15 var(--sans); letter-spacing: -.01em; color: var(--ink); margin: 0 0 10px; }
.quote { position: relative; margin: 0 0 14px; padding: 14px 22px 11px 64px; border: 1px solid var(--line);
background: var(--surface-alt); border-radius: 8px;
font: italic 400 16.5px/1.5 var(--display); color: var(--ink); }
.quote::before { content: '\201C'; position: absolute; left: 18px; top: 6px;
font: 700 44px/1 var(--display); color: var(--bordeaux); }
.tr-grid { display: grid; grid-template-columns: 1.12fr 1fr; gap: 22px; align-items: center; }
.tr-grid .it, .tr-grid .en { margin: 0; }
.tr-grid .en { padding-left: 18px; border-left: 1px solid var(--line-strong);
font: 400 12.5px/1.55 var(--sans); color: var(--bordeaux-deep); }
.en-tag { display: inline-block; margin-right: 7px; padding: 2px 7px; border-radius: 4px;
background: var(--bordeaux); color: #fdfcfc; font: 700 9px/1 var(--sans); letter-spacing: .12em; vertical-align: 1px; }
.quote small { display: block; margin-top: 5px; font: 600 9.5px/1.4 var(--sans); font-style: normal;
letter-spacing: .06em; text-transform: uppercase; color: var(--ink-muted); }
.pipeline { display: flex; align-items: stretch; gap: 10px; }
.stage { flex: 1; padding: 10px 8px; text-align: center; border: 1px solid var(--line); border-radius: 8px; background: var(--surface-alt); }
.stage b { display: block; font: 700 12.5px/1.2 var(--sans); margin-bottom: 3px; }
.stage span { display: block; font: 400 10.5px/1.35 var(--sans); color: var(--ink-muted); }
.stage .tag { display: inline-block; margin-top: 4px; padding: 2px 9px; border-radius: 9999px;
background: var(--bordeaux); color: #fdfcfc; font: 700 8.5px/1 var(--sans); letter-spacing: .1em; text-transform: uppercase; }
.stage.ai { background: var(--bordeaux-tint); border-color: var(--bordeaux); }
.arrow { align-self: center; color: var(--line-strong); font: 700 17px/1 var(--sans); }
.stage.hot { background: #fdf1f2; border-color: var(--bordeaux); }
.thoth-wrap { display: flex; justify-content: flex-end; margin-top: 12px; }
.thoth-wrap.left { justify-content: flex-start; }
.thoth-col { width: 500px; text-align: center; }
.thoth-arrow { color: var(--line-strong); font: 700 15px/1 var(--sans); margin: 1px 0; }
.thoth-box { border: 1px solid var(--bordeaux); border-radius: 8px; padding: 9px 14px;
background: var(--surface-alt); font: 600 11.5px/1.4 var(--sans); }
.thoth-box .tag { display: inline-block; margin-right: 7px; padding: 2px 9px; border-radius: 9999px;
background: var(--bordeaux); color: #fdfcfc; font: 700 8.5px/1 var(--sans); letter-spacing: .1em; text-transform: uppercase; }
/* ---- outputs icons ---- */
.oicon { width: 34px; height: 34px; color: var(--bordeaux); display: block; }
.oicon * { vector-effect: non-scaling-stroke; }
/* ---- variant A: branch tree from Marts ---- */
.fanwrap { display: flex; justify-content: flex-end; margin-top: 0; }
.fan { width: 58%; position: relative; padding-top: 16px; }
.fan .drop { position: absolute; top: -6px; right: 128px; width: 2px; height: 22px; background: var(--bordeaux); }
.fan .rail { height: 2px; background: var(--bordeaux); margin: 0 60px 0 40px; position: relative; }
.fan .nodes { display: flex; margin-top: 0; }
.fan .node { flex: 1; text-align: center; position: relative; padding-top: 14px; }
.fan .node::before { content: ''; position: absolute; top: 0; left: 50%; width: 2px; height: 14px;
background: var(--bordeaux); transform: translateX(-50%); }
.fan .node .oicon { margin: 0 auto 5px; }
.fan .node b { display: block; font: 700 13px/1.2 var(--sans); color: var(--ink); }
.fan .node span { display: block; font: 400 10.5px/1.35 var(--sans); color: var(--ink-muted); margin-top: 2px; }
/* ---- variant B: outputs band ---- */
.oband { margin-top: 12px; }
.oband .lbl { font: 700 10px/1 var(--mono); letter-spacing: .14em; text-transform: uppercase;
color: var(--bordeaux); text-align: center; margin-bottom: 8px; }
.oband .tiles { display: flex; gap: 12px; }
.oband .tile { flex: 1; display: flex; align-items: center; gap: 12px; padding: 11px 14px;
border: 1px solid var(--line); border-radius: 8px; background: var(--surface-alt); }
.oband .tile .ic { flex: 0 0 44px; height: 44px; border-radius: 9999px; background: var(--bordeaux-tint);
display: flex; align-items: center; justify-content: center; }
.oband .tile .ic .oicon { width: 26px; height: 26px; }
.oband .tile b { display: block; font: 700 13.5px/1.2 var(--sans); color: var(--ink); }
.oband .tile span { display: block; margin-top: 2px; font: 400 11px/1.35 var(--sans); color: var(--ink-muted); }
/* ---- variant C: maturity rail ---- */
.railwrap { display: flex; justify-content: flex-end; margin-top: 10px; }
.rail { width: 560px; border-left: 2px solid var(--bordeaux); padding: 2px 0 2px 0; }
.rail .row { display: flex; align-items: center; gap: 12px; padding: 7px 14px 7px 16px; position: relative; }
.rail .row::before { content: ''; position: absolute; left: 0; top: 50%; width: 14px; height: 2px; background: var(--bordeaux); }
.rail .row .oicon { flex: 0 0 26px; width: 26px; height: 26px; }
.rail .row b { font: 700 13.5px/1.2 var(--sans); color: var(--ink); }
.rail .row span.d { font: 400 11px/1.3 var(--sans); color: var(--ink-muted); }
.rail .row .st { margin-left: auto; font: 700 9px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
padding: 3px 9px; border-radius: 9999px; }
.rail .row.today .st { background: var(--bordeaux); color: #fdfcfc; }
.rail .row.next { border: 1px dashed var(--line-strong); border-radius: 8px; background: transparent; }
.rail .row.next .oicon { opacity: .55; }
.rail .row.next .st { border: 1px solid var(--line-strong); color: var(--ink-muted); }
.rail .lbl { font: 700 10px/1 var(--mono); letter-spacing: .14em; text-transform: uppercase;
color: var(--bordeaux); margin: 0 0 6px 16px; }
/* ---- variant switcher (prototype chrome) ---- */
.switch { position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%); z-index: 50;
display: flex; align-items: center; gap: 10px; background: #2a2426; color: #fdfcfc;
padding: 8px 12px; border-radius: 9999px; box-shadow: 0 6px 22px rgba(0,0,0,.35);
font: 600 12.5px/1 var(--sans); }
.switch button { border: none; background: #4a4345; color: #fdfcfc; width: 28px; height: 28px;
border-radius: 9999px; font: 700 14px/1 var(--sans); cursor: pointer; }
.switch button:hover { background: var(--bordeaux); }
.caption { max-width: 1200px; margin: 12px auto 0; text-align: center;
font: 400 12.5px/1.5 var(--sans); color: var(--ink-muted); }
.caption b { color: var(--ink); }
</style>
</head>
<body>
<div class="proto-head">
<b>Prototype — slide “The problem” · outputs from the datamarts</b>
<span>Three structural options for Dashboards / Machine Learning / Predictive Statistics. Use the bar below or the arrow keys.</span>
</div>
<div class="frame">
<div class="head">
<img src="/deck/logo.png" alt="">
<span class="head-org">AritmoLab · Policlinico San Donato</span>
</div>
<div class="band"><b>The problem</b><span style="font:700 11px/1 var(--sans)">04 · The problem</span></div>
<div class="sbody">
<div class="kicker">From free text to a research platform</div>
<h2>The clinical truth lives in unstructured columns</h2>
<div class="quote">
<div class="tr-grid">
<p class="it">“Il paziente riferisce sincope ricorrente; ECG basale con sopraslivellamento ST in V1–V3; test provocativo con flecainide positivo per pattern Brugada.”</p>
<p class="en"><span class="en-tag">EN</span>“The patient reports recurrent syncope; baseline ECG with ST-segment elevation in V1–V3; flecainide provocation test positive for a Brugada pattern.”</p>
</div>
</div>
<div class="pipeline" id="pipeline">
<div class="stage"><b>Sources</b><span>Cardioref · Genetic data<br>Omics Portal · ECG</span></div>
<div class="arrow">→</div>
<div class="stage"><b>Staging</b><span>raw replica</span></div>
<div class="arrow">→</div>
<div class="stage ai"><b>Integration</b><span>3NF · text mining</span><span class="tag">AI</span></div>
<div class="arrow">→</div>
<div class="stage"><b>Data Warehouse</b><span>star schema</span></div>
<div class="arrow">→</div>
<div class="stage hot" id="marts"><b>Marts</b><span>dbt · Superset</span></div>
</div>
<div class="variant-zone"></div>
<div class="thoth-wrap" id="thothwrap">
<div class="thoth-col">
<div class="thoth-arrow">↑</div>
<div class="thoth-box"><span class="tag">AI</span>ThothII — natural-language → SQL over the DWH, builds the final datamarts</div>
</div>
</div>
</div>
<div class="foot"><span>Role of AI in the Analysis of Unstructured Clinical Databases</span><span>Dr. Marco Pancotti - MultiPhysixLab</span><span>Dr. Sara Paratico - Gruppo San Donato</span><span>San Donato Milanese, Milan, Italy · 2–3 October 2026</span><span>04 / 14</span></div>
</div>
<p class="caption" id="caption"></p>
<div class="switch">
<button id="prev" aria-label="previous variant">←</button>
<span id="label"></span>
<button id="next" aria-label="next variant">→</button>
</div>
<svg width="0" height="0" style="position:absolute">
<defs>
<g id="i-dash">
<svg class="oicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round">
<path d="M4 29 h26"/><rect x="7" y="17" width="5" height="12" rx="1"/><rect x="15" y="9" width="5" height="20" rx="1"/><rect x="23" y="13" width="5" height="16" rx="1"/>
</svg>
</g>
<g id="i-ml">
<svg class="oicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="7" cy="7" r="2.6"/><circle cx="7" cy="27" r="2.6"/><circle cx="17" cy="17" r="3.4"/><circle cx="27" cy="7" r="2.6"/><circle cx="27" cy="27" r="2.6"/>
<path d="M9.3 8.8 L14.6 14.6"/><path d="M9.3 25.2 L14.6 19.4"/><path d="M19.4 14.6 L24.7 8.8"/><path d="M19.4 19.4 L24.7 25.2"/>
</svg>
</g>
<g id="i-pred">
<svg class="oicon" viewBox="0 0 34 34" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round">
<path d="M3 27 L11 19 L17 22 L23 13"/><path d="M23 13 L30 6" stroke-dasharray="3 3.4"/><circle cx="30" cy="6" r="2.2" fill="currentColor" stroke="none"/><path d="M3 31 h28" opacity=".35"/>
</svg>
</g>
</defs>
</svg>
<script>
const ICON = {
dash: document.getElementById('i-dash').innerHTML,
ml: document.getElementById('i-ml').innerHTML,
pred: document.getElementById('i-pred').innerHTML
};
const VARIANTS = {
A: {
name: 'A — Branch from Marts',
caption: '<b>A · Branch from Marts.</b> The three outputs fan out of the Marts stage as icon nodes on a connector tree: lightest visual weight, reads as “the pipeline flows on”. ThothII moves bottom-left, so left = AI reads the DWH, right = research outputs.',
html: `
<div class="fanwrap">
<div class="fan">
<div class="drop"></div>
<div class="rail"></div>
<div class="nodes">
<div class="node">${ICON.dash}<b>Dashboards</b><span>clinical review on Superset</span></div>
<div class="node">${ICON.ml}<b>Machine Learning</b><span>cohort tables for model training</span></div>
<div class="node">${ICON.pred}<b>Predictive Statistics</b><span>outcomes &amp; risk analysis</span></div>
</div>
</div>
</div>`
},
B: {
name: 'B — Products band',
caption: '<b>B · Products band.</b> A labelled full-width band under the pipeline: three equal tiles, icon in a tinted disc + name + one-liner. Reads as “the three products of the platform” — most explicit, heaviest.',
html: `
<div class="oband">
<div class="lbl">what the datamarts enable</div>
<div class="tiles">
<div class="tile"><span class="ic">${ICON.dash}</span><span><b>Dashboards</b><span>clinical review on Superset dashboards</span></span></div>
<div class="tile"><span class="ic">${ICON.ml}</span><span><b>Machine Learning</b><span>cohort tables, ready for model training</span></span></div>
<div class="tile"><span class="ic">${ICON.pred}</span><span><b>Predictive Statistics</b><span>outcomes, risk and survival analysis</span></span></div>
</div>
</div>`
},
C: {
name: 'C — Maturity rail',
caption: '<b>C · Maturity rail.</b> A rail drops from Marts into three rows that encode the roadmap: Dashboards is live today (solid), Machine Learning and Predictive Statistics are the next steps (dashed). Sets up the “what is still missing” beat later in the talk.',
html: `
<div class="railwrap">
<div class="rail">
<div class="lbl">outputs of the datamarts</div>
<div class="row today">${ICON.dash}<b>Dashboards</b><span class="d">clinical review on Superset</span><span class="st">live today</span></div>
<div class="row next">${ICON.ml}<b>Machine Learning</b><span class="d">cohort tables for model training</span><span class="st">next</span></div>
<div class="row next">${ICON.pred}<b>Predictive Statistics</b><span class="d">outcomes &amp; risk analysis</span><span class="st">next</span></div>
</div>
</div>`
}
};
const keys = Object.keys(VARIANTS);
const cur = () => {
const v = (new URLSearchParams(location.search).get('variant') || 'A').toUpperCase();
return keys.includes(v) ? v : 'A';
};
function render() {
const v = cur();
document.querySelector('.variant-zone').innerHTML = VARIANTS[v].html;
document.getElementById('label').textContent = VARIANTS[v].name;
document.getElementById('caption').innerHTML = VARIANTS[v].caption;
// ThothII placement: A and C put the outputs on the right → ThothII moves bottom-left
document.getElementById('thothwrap').classList.toggle('left', v !== 'B');
}
function cycle(dir) {
const i = keys.indexOf(cur());
const v = keys[(i + dir + keys.length) % keys.length];
const u = new URL(location);
u.searchParams.set('variant', v);
location.replace(u);
}
document.getElementById('prev').onclick = () => cycle(-1);
document.getElementById('next').onclick = () => cycle(1);
addEventListener('keydown', e => {
if (e.key === 'ArrowLeft') cycle(-1);
if (e.key === 'ArrowRight') cycle(1);
});
render();
</script>
</body>
</html>