[object Object]

← back to Cncp Failures Mockups

v7 age-adaptive: 3→95 slider morphs UI through 8 design bands (toddler/kid/tween/teen/adult/mature/senior/elder) with band-specific palette/font/density/copy + index tile

12220e81071e92c966f0f6d8478221b5fb71d461 · 2026-05-11 14:41:39 -0700 · SteveStudio2

Files touched

Diff

commit 12220e81071e92c966f0f6d8478221b5fb71d461
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date:   Mon May 11 14:41:39 2026 -0700

    v7 age-adaptive: 3→95 slider morphs UI through 8 design bands (toddler/kid/tween/teen/adult/mature/senior/elder) with band-specific palette/font/density/copy + index tile
---
 public/index.html           |   3 +-
 public/v7-age-adaptive.html | 321 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 323 insertions(+), 1 deletion(-)

diff --git a/public/index.html b/public/index.html
index cfa07a3..677fd43 100644
--- a/public/index.html
+++ b/public/index.html
@@ -58,7 +58,8 @@ const variants = [
   { id:'v3-heatmap',    title:'3. Heatmap · Dashboard',     desc:'Density-first: 24-hour severity grid + glanceable stat cells. SRE dashboard energy.' },
   { id:'v4-kanban',     title:'4. Kanban · Status Board',   desc:'Four lanes (triage → investigating → resolved → dismissed). Atlassian-ish, action-oriented.' },
   { id:'v5-timeline',   title:'5. Timeline · APM Trace',    desc:'Each process is a lane; spans show event windows. Datadog-style, time-as-axis.' },
-  { id:'v6-brutalist',  title:'6. Brutalist · Data-Dense',  desc:'Hard borders, full table, every field visible. No chrome — just the facts.' }
+  { id:'v6-brutalist',  title:'6. Brutalist · Data-Dense',  desc:'Hard borders, full table, every field visible. No chrome — just the facts.' },
+  { id:'v7-age-adaptive', title:'7. Age-Adaptive · 3 → 95',  desc:'Slider morphs the whole UI through 8 design bands — toddler primary colors → elementary playful → tween → teen neon → adult → mature serif → senior hi-contrast → elder essentials only.' }
 ];
 
 function renderGrid(mode) {
diff --git a/public/v7-age-adaptive.html b/public/v7-age-adaptive.html
new file mode 100644
index 0000000..d2de4be
--- /dev/null
+++ b/public/v7-age-adaptive.html
@@ -0,0 +1,321 @@
+<!doctype html><html><head>
+<meta charset="utf-8"><title>v7 — Age-adaptive (3 → 95)</title>
+<style>
+  /* The 8 age bands. Each rewrites the page's design system. */
+  :root {
+    --bg:#0a0c12; --panel:#161a26; --fg:#e6eef8; --muted:#7c8aa5; --border:#252b3d;
+    --accent:#7c3aed; --ok:#22c55e; --warn:#f59e0b; --bad:#ef4444;
+    --font:'-apple-system',BlinkMacSystemFont,'SF Pro Text',system-ui,sans-serif;
+    --font-mono:ui-monospace,Menlo,monospace;
+    --font-size-body:13px; --font-size-h:16px; --font-size-small:11px;
+    --gap:14px; --radius:8px; --padding:12px;
+    --vibe:'modern';
+  }
+  /* AGE 3-5 — Sesame Street primary colors, emoji-first, huge tap targets */
+  body[data-band="toddler"] {
+    --bg:#fff9e6; --panel:#fff; --fg:#222; --muted:#666; --border:#f4d000;
+    --accent:#ff3b30; --ok:#00b34a; --warn:#ffcc00; --bad:#ff3b30;
+    --font:'Marker Felt','Comic Sans MS',sans-serif;
+    --font-size-body:24px; --font-size-h:48px; --font-size-small:18px;
+    --gap:24px; --radius:24px; --padding:24px;
+  }
+  /* AGE 6-9 — Elementary, playful but legible */
+  body[data-band="kid"] {
+    --bg:#fff; --panel:#fef9c3; --fg:#1f2937; --muted:#4b5563; --border:#fbbf24;
+    --accent:#3b82f6; --ok:#16a34a; --warn:#f59e0b; --bad:#dc2626;
+    --font:'Comic Sans MS','Chalkboard SE',sans-serif;
+    --font-size-body:18px; --font-size-h:30px; --font-size-small:14px;
+    --gap:18px; --radius:14px; --padding:18px;
+  }
+  /* AGE 10-12 — Tween, structured, still color */
+  body[data-band="tween"] {
+    --bg:#fef3c7; --panel:#fff; --fg:#1f2937; --muted:#6b7280; --border:#fbbf24;
+    --accent:#6366f1; --ok:#16a34a; --warn:#f59e0b; --bad:#dc2626;
+    --font:'Avenir',-apple-system,sans-serif;
+    --font-size-body:15px; --font-size-h:22px; --font-size-small:12px;
+    --gap:14px; --radius:10px; --padding:14px;
+  }
+  /* AGE 13-19 — Teen / neon dark mode */
+  body[data-band="teen"] {
+    --bg:#0a0014; --panel:#1a0030; --fg:#f0f0ff; --muted:#8a7fb0; --border:#2a0050;
+    --accent:#ff00ff; --ok:#00ff88; --warn:#ffaa00; --bad:#ff0066;
+    --font:'JetBrains Mono','SF Mono',ui-monospace,monospace;
+    --font-size-body:12px; --font-size-h:18px; --font-size-small:10px;
+    --gap:10px; --radius:4px; --padding:10px;
+  }
+  /* AGE 20-44 — Adult, modern default */
+  body[data-band="adult"] {
+    --bg:#0a0c12; --panel:#161a26; --fg:#e6eef8; --muted:#7c8aa5; --border:#252b3d;
+    --accent:#7c3aed; --ok:#22c55e; --warn:#f59e0b; --bad:#ef4444;
+    --font-size-body:13px; --font-size-h:18px; --font-size-small:11px;
+    --gap:14px; --radius:8px; --padding:12px;
+  }
+  /* AGE 45-59 — Mature, slightly larger and calmer */
+  body[data-band="mature"] {
+    --bg:#f8f6f1; --panel:#fff; --fg:#1c1917; --muted:#57534e; --border:#d6d3d1;
+    --accent:#7c2d12; --ok:#15803d; --warn:#b45309; --bad:#991b1b;
+    --font:'Georgia','Iowan Old Style',serif;
+    --font-size-body:15px; --font-size-h:22px; --font-size-small:13px;
+    --gap:16px; --radius:6px; --padding:16px;
+  }
+  /* AGE 60-79 — Senior, hi-contrast, large readable */
+  body[data-band="senior"] {
+    --bg:#fff; --panel:#fff; --fg:#000; --muted:#333; --border:#000;
+    --accent:#0050a0; --ok:#006400; --warn:#a05000; --bad:#a00000;
+    --font:'Helvetica Neue',Helvetica,Arial,sans-serif;
+    --font-size-body:20px; --font-size-h:30px; --font-size-small:16px;
+    --gap:20px; --radius:6px; --padding:20px;
+  }
+  /* AGE 80+ — Elder, max contrast, essentials only */
+  body[data-band="elder"] {
+    --bg:#000; --panel:#000; --fg:#fff; --muted:#cccccc; --border:#fff;
+    --accent:#ffff00; --ok:#00ff00; --warn:#ffff00; --bad:#ff0000;
+    --font:'Helvetica Neue',Helvetica,Arial,sans-serif;
+    --font-size-body:26px; --font-size-h:42px; --font-size-small:20px;
+    --gap:28px; --radius:0; --padding:28px;
+  }
+
+  * { box-sizing:border-box; }
+  body { margin:0; background:var(--bg); color:var(--fg); font-family:var(--font); font-size:var(--font-size-body); min-height:100vh; padding:0 0 60px; transition: background 0.4s, color 0.4s; }
+
+  /* Slider bar — always visible at top */
+  .slider-bar { position:sticky; top:0; z-index:10; padding:14px 24px; background:var(--panel); border-bottom:2px solid var(--border); display:flex; gap:16px; align-items:center; flex-wrap:wrap; }
+  .slider-bar .age-num { font-weight:700; font-size:1.6em; color:var(--accent); min-width:80px; text-align:center; }
+  .slider-bar .age-band-label { color:var(--muted); font-size:0.9em; text-transform:uppercase; letter-spacing:1.5px; }
+  .slider-bar input[type=range] { flex:1; min-width:280px; accent-color:var(--accent); height:12px; }
+  .slider-bar .tagline { color:var(--muted); font-size:0.85em; flex-basis:100%; margin-top:6px; }
+
+  .container { padding:24px; }
+  h1 { font-size:var(--font-size-h); margin:0 0 8px; }
+  .sub { color:var(--muted); font-size:var(--font-size-small); margin-bottom:var(--gap); }
+
+  .grid { display:grid; gap:var(--gap); grid-template-columns:repeat(auto-fill,minmax(320px,1fr)); }
+  body[data-band="toddler"] .grid, body[data-band="senior"] .grid, body[data-band="elder"] .grid { grid-template-columns:1fr; }
+  body[data-band="kid"] .grid { grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); }
+
+  .card { background:var(--panel); border:2px solid var(--border); border-radius:var(--radius); padding:var(--padding); display:flex; flex-direction:column; gap:8px; }
+  .card .name { font-family:var(--font-mono); font-weight:600; word-break:break-all; }
+  body[data-band="toddler"] .card .name, body[data-band="kid"] .card .name { font-family:var(--font); }
+  .card .last { color:var(--muted); font-size:var(--font-size-small); white-space:pre-wrap; line-height:1.4; max-height:4.2em; overflow:hidden; }
+  .card .action { color:var(--accent); font-style:italic; font-size:var(--font-size-small); }
+  body[data-band="senior"] .card .action, body[data-band="elder"] .card .action { font-style:normal; font-weight:700; }
+
+  /* Severity ribbon — meaning shifts across ages */
+  .sev-badge { display:inline-block; padding:4px 10px; border-radius:var(--radius); font-weight:700; font-size:var(--font-size-small); text-transform:uppercase; letter-spacing:1px; }
+  .sev-badge.high { background:var(--bad); color:#fff; }
+  .sev-badge.medium { background:var(--warn); color:#000; }
+  .sev-badge.low { background:var(--ok); color:#fff; }
+  body[data-band="senior"] .sev-badge, body[data-band="elder"] .sev-badge { padding:8px 14px; }
+
+  /* Hermes panel — collapses progressively */
+  .hermes { margin-top:var(--gap); padding:var(--padding); background:var(--panel); border:2px solid var(--border); border-radius:var(--radius); }
+  .hermes h2 { font-size:1.1em; margin:0 0 8px; }
+  .hermes .stat-line { color:var(--muted); font-size:var(--font-size-small); }
+  body[data-band="elder"] .hermes, body[data-band="toddler"] .hermes { display:none; }
+  body[data-band="kid"] .hermes .stat-line, body[data-band="senior"] .hermes .recent-feed { display:none; }
+
+  /* Toddler-mode: feeling face for each card */
+  .face { font-size:64px; line-height:1; }
+  body:not([data-band="toddler"]) .face { display:none; }
+  body[data-band="toddler"] .name { display:none; }
+  body[data-band="toddler"] .last, body[data-band="toddler"] .action, body[data-band="toddler"] .meta, body[data-band="toddler"] .footer { display:none; }
+  body[data-band="toddler"] .toddler-label { display:block; font-size:1.4em; text-align:center; margin-top:8px; font-weight:700; }
+  body:not([data-band="toddler"]) .toddler-label { display:none; }
+
+  /* Kid mode: simple emoji + name only */
+  .kid-icon { font-size:36px; }
+  body:not([data-band="kid"]) .kid-icon { display:none; }
+
+  /* Elder mode: just one row of essential info */
+  body[data-band="elder"] .card .last, body[data-band="elder"] .card .meta, body[data-band="elder"] .card .footer { display:none; }
+  body[data-band="elder"] .card { padding:32px; }
+  body[data-band="elder"] .card .name { font-size:0.7em; font-weight:400; color:var(--muted); }
+  body[data-band="elder"] .card .action { font-size:1.4em; line-height:1.3; }
+  body[data-band="elder"] .container h1 { font-size:1.5em; }
+
+  /* Teen mode: scan-lines */
+  body[data-band="teen"] { background-image: linear-gradient(rgba(255,0,255,0.03) 1px, transparent 1px); background-size: 100% 4px; }
+
+  .meta { color:var(--muted); font-size:var(--font-size-small); display:flex; gap:8px; flex-wrap:wrap; }
+  .footer { display:flex; gap:8px; justify-content:space-between; align-items:center; }
+  button.action-btn { background:var(--accent); color:#fff; border:none; padding:6px 12px; border-radius:var(--radius); font-size:var(--font-size-small); cursor:pointer; font-family:var(--font); font-weight:600; }
+  body[data-band="senior"] button.action-btn, body[data-band="elder"] button.action-btn { padding:18px 28px; font-size:1.2em; }
+  body[data-band="toddler"] button.action-btn { padding:22px 32px; font-size:1.4em; border-radius:32px; }
+</style>
+</head><body data-band="toddler">
+
+<div class="slider-bar">
+  <div>
+    <div class="age-num"><span id="ageDisplay">3</span> yr</div>
+    <div class="age-band-label" id="bandLabel">toddler</div>
+  </div>
+  <input id="ageSlider" type="range" min="3" max="95" value="3" />
+  <div class="tagline" id="tagline">Primary colors, huge taps. One face = good. One face = sad.</div>
+</div>
+
+<div class="container">
+  <h1 id="hed">Failures</h1>
+  <div class="sub" id="dek">A reading of what's going wrong, tuned to your age.</div>
+  <div class="grid" id="grid"></div>
+
+  <div class="hermes">
+    <h2>Hermes 3 <span style="font-size:0.7em; color:var(--muted);">(the helper)</span></h2>
+    <div class="stat-line" id="hStats"></div>
+    <div class="recent-feed" id="hRecent" style="margin-top:8px;"></div>
+  </div>
+</div>
+
+<script>
+// Age band rules — keep in sync with CSS [data-band=].
+function bandFor(age) {
+  if (age <= 5)  return { id:'toddler', label:'toddler', tagline:'Primary colors, huge taps. One face = good. One face = sad.', hed:'Things to fix!' };
+  if (age <= 9)  return { id:'kid',     label:'elementary', tagline:'Friendly emoji and clear names. Easy actions.', hed:'Stuff to fix' };
+  if (age <= 12) return { id:'tween',   label:'tween', tagline:'Brighter palette, more info per card, still simple actions.', hed:'Open issues' };
+  if (age <= 19) return { id:'teen',    label:'teen', tagline:'Dense, dark, neon. Mono fonts. Power-user nav.', hed:'> FAILURES' };
+  if (age <= 44) return { id:'adult',   label:'adult', tagline:'Modern dashboard. Sort + density + full Hermes panel.', hed:'Failures' };
+  if (age <= 59) return { id:'mature',  label:'mature', tagline:'Serif typeface, calmer palette, larger touch targets.', hed:'Open Failures' };
+  if (age <= 79) return { id:'senior',  label:'senior', tagline:'High contrast. Larger text. Clear primary actions.', hed:'What needs your attention' };
+  return            { id:'elder',   label:'elder', tagline:'Black & white, maximum text size, only the essentials. One issue at a time.', hed:'Today' };
+}
+
+const slider = document.getElementById('ageSlider');
+const ageDisplay = document.getElementById('ageDisplay');
+const bandLabel = document.getElementById('bandLabel');
+const tagline = document.getElementById('tagline');
+const hed = document.getElementById('hed');
+
+function applyBand(age) {
+  const b = bandFor(age);
+  document.body.dataset.band = b.id;
+  ageDisplay.textContent = age;
+  bandLabel.textContent = b.label;
+  tagline.textContent = b.tagline;
+  hed.textContent = b.hed;
+  renderGrid(b);
+}
+
+slider.addEventListener('input', e => applyBand(Number(e.target.value)));
+
+let _cachedData = null;
+async function loadData() {
+  const r = await fetch('/data' + location.search);
+  _cachedData = await r.json();
+  applyBand(Number(slider.value));
+}
+
+function emojiFor(severity, status) {
+  if (status === 'resolved') return '✅';
+  if (status === 'dismissed') return '😴';
+  if (severity === 'high')   return '🚨';
+  if (severity === 'medium') return '⚠️';
+  if (severity === 'low')    return '🟢';
+  return '❓';
+}
+function faceFor(severity, status) {
+  if (status === 'resolved' || status === 'dismissed') return '😊';
+  if (severity === 'high')   return '😟';
+  if (severity === 'medium') return '😐';
+  if (severity === 'low')    return '🙂';
+  return '😶';
+}
+function plainLabel(f) {
+  if (f.severity === 'high')   return 'Something is BROKEN!';
+  if (f.severity === 'medium') return 'Something needs a look';
+  if (f.severity === 'low')    return 'Everything is OK';
+  return 'Not sure yet';
+}
+
+function renderGrid(b) {
+  if (!_cachedData) return;
+  const grid = document.getElementById('grid');
+  let items = (_cachedData.failures || []).filter(f => f.status === 'triage' || f.status === 'investigating');
+
+  // Information density shrinks with age extremes
+  if (b.id === 'toddler') items = items.slice(0, 3);
+  else if (b.id === 'kid') items = items.slice(0, 4);
+  else if (b.id === 'elder') items = items.slice(0, 1);
+  else if (b.id === 'senior') items = items.slice(0, 3);
+
+  grid.innerHTML = items.map(f => {
+    const sevClass = f.severity || 'medium';
+    const ageBand = b.id;
+    if (ageBand === 'toddler') {
+      return `
+        <div class="card">
+          <div class="face">${faceFor(f.severity, f.status)}</div>
+          <div class="toddler-label">${plainLabel(f)}</div>
+        </div>`;
+    }
+    if (ageBand === 'kid') {
+      return `
+        <div class="card">
+          <div style="display:flex; align-items:center; gap:12px;">
+            <div class="kid-icon">${emojiFor(f.severity, f.status)}</div>
+            <div>
+              <div style="font-weight:700;">${f.processName}</div>
+              <div class="last">${(f.lastLines||'').split('\\n')[0].slice(0,80)}</div>
+            </div>
+          </div>
+          <button class="action-btn">Tell me more</button>
+        </div>`;
+    }
+    if (ageBand === 'elder') {
+      return `
+        <div class="card">
+          <div class="action">${f.suggestedAction || 'Click here when ready.'}</div>
+          <div class="name">${f.processName}</div>
+          <button class="action-btn">Done</button>
+        </div>`;
+    }
+    if (ageBand === 'senior') {
+      return `
+        <div class="card">
+          <div style="display:flex; gap:10px; align-items:center; margin-bottom:8px;">
+            <span class="sev-badge ${sevClass}">${f.severity || 'check'}</span>
+            <span style="font-weight:600;">${f.processName}</span>
+          </div>
+          <div class="last">${(f.lastLines||'').split('\\n')[0].slice(0,120)}</div>
+          ${f.suggestedAction ? `<div class="action">${f.suggestedAction}</div>` : ''}
+          <button class="action-btn">View details</button>
+        </div>`;
+    }
+    // adult / mature / tween / teen — full standard card with band-specific styling via CSS vars
+    return `
+      <div class="card">
+        <div class="meta">
+          <span class="sev-badge ${sevClass}">${f.severity || '—'}</span>
+          <span>${f.source}</span>
+          <span>×${f.eventCount}</span>
+        </div>
+        <div class="name">${f.processName}</div>
+        <div class="last">${(f.lastLines||'').split('\\n').filter(Boolean)[0]?.slice(0,120) || ''}</div>
+        ${f.suggestedAction ? `<div class="action">→ ${f.suggestedAction}</div>` : ''}
+        <div class="footer">
+          <span style="font-size:var(--font-size-small); color:var(--muted);">${new Date(f.lastSeen||f.created).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})}</span>
+          <button class="action-btn">Open</button>
+        </div>
+      </div>`;
+  }).join('');
+  if (items.length === 0) grid.innerHTML = '<div class="card" style="text-align:center; padding:40px;">All clear!</div>';
+
+  // Hermes summary
+  const h = _cachedData.hermes ? _cachedData.hermes.last24h : null;
+  if (h) {
+    document.getElementById('hStats').innerHTML = b.id === 'kid'
+      ? `Hermes checked <b>${h.total}</b> things today and answered <b>${h.ok}</b> right.`
+      : `${h.total} calls · ${h.byType.classify} cls · ${h.byType['idea-chat']} idea · ${(h.avgLatencyMs/1000).toFixed(1)}s avg · ${h.fail} failed`;
+    const recent = (_cachedData.hermes.recent || []).slice(0, b.id==='senior'?2:b.id==='mature'?3:5);
+    document.getElementById('hRecent').innerHTML = recent.map(c => {
+      const ok = c.ok ? '✓' : '✗';
+      const t = new Date(c.ts).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'});
+      return `<div style="font-size:var(--font-size-small); color:var(--muted); margin-top:2px;">${ok} ${t} ${c.type} · ${(c.target||'').slice(0,40)}</div>`;
+    }).join('');
+  } else {
+    document.getElementById('hStats').textContent = '';
+    document.getElementById('hRecent').textContent = '';
+  }
+}
+
+loadData();
+</script>
+</body></html>

← 5d00c00 live-data toggle: /data proxies real CNCP failures + hermes  ·  back to Cncp Failures Mockups  ·  v7 UI hooks: 8 band quick-jump pills, per-band modal/toast h 788f2be →