Creative Technologies Lab | dokuWiki

Repository of academic adventures, experimental technology, accidental brilliance, and collaborative nerdery.

User Tools

Site Tools


extras:codikon:p5js:wireframe-ideen-generator

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

extras:codikon:p5js:wireframe-ideen-generator [2026/10/26 08:26] – created Leonie Winkelmannextras:codikon:p5js:wireframe-ideen-generator [2026/10/26 08:30] (current) – Leonie Winkelmann
Line 1: Line 1:
-<!doctype html> +<HTML>
-<html lang="de"> +
-<head> +
-<meta charset="utf-8"> +
-<meta name="viewport" content="width=device-width, initial-scale=1"> +
-<title>Wireframe-Ideen-Generator</title>+
 <style> <style>
-  :root { +#wfgen { all: initial; display: block; max-width: 1000px; margin: 0 auto; padding: 16px; background: #fff; color: #111111; 
-    --k: #111111; --o: #FF6A13; --b: #2B5BFF; +  font: 16px/1.4 Arial, Helvetica, sans-serif; box-sizing: border-box; color-scheme: light; 
-    --g1: #F3F3F3; --g2: #D4D4D4; --g3: #5D5D5D; +  --k: #111111; --o: #FF6A13; --b: #2B5BFF; --g1: #F3F3F3; --g2: #D4D4D4; --g3: #5D5D5D; } 
-    color-scheme: light; +#wfgen *, #wfgen *::before, #wfgen *::after { box-sizing: border-box; } 
-  } +#wfgen .wf-title { font-size: 22px; font-weight: bold; margin: 0 0 4px; line-height: 1.3; color: var(--k); } 
-  * { box-sizing: border-box; } +#wfgen .wf-hint { margin: 0 0 14px; color: var(--g3); font-size: 14px; line-height: 1.4; } 
-  body { margin: 0; background: #fff; color: var(--k); font: 16px/1.4 Arial, Helvetica, sans-serif; } +#wfgen .wf-bar { display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: center; margin: 0 0 14px; } 
-  #app { max-width: 1000px; margin: 0 auto; padding: 16px; } +#wfgen .wf-grp { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } 
-  h1 { font-size: 22px; margin: 0 0 4px; } +#wfgen .wf-lbl { font-size: 13px; font-weight: bold; color: var(--g3); text-transform: uppercase; } 
-  .hint { margin: 0 0 14px; color: var(--g3); font-size: 14px; } +#wfgen .wf-seg { display: inline-flex; } 
-  .bar { display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: center; margin-bottom: 14px; } +#wfgen button { font: bold 15px/1.2 Arial, Helvetica, sans-serif; color: var(--k); background: #fff; background-image: none; 
-  .grp { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } +  border: 2px solid var(--k); border-radius: 8px; padding: 8px 14px; margin: 0; cursor: pointer; box-shadow: none; text-shadow: none; 
-  .grp > span { font-size: 13px; font-weight: bold; color: var(--g3); text-transform: uppercase; } +  text-transform: none; letter-spacing: normal; width: auto; height: auto; min-width: 0; min-height: 0; } 
-  .seg { display: inline-flex; } +#wfgen button:hover { background: var(--g1); background-image: none; color: var(--k); } 
-  button { +#wfgen button:focus-visible { outline: 3px solid var(--b); outline-offset: 2px; } 
-    font: inherit; font-weight: bold; font-size: 15px; color: var(--k); background: #fff; +#wfgen .wf-seg button { border-radius: 0; margin-left: -2px; padding: 8px 12px; } 
-    border: 2px solid var(--k); border-radius: 8px; padding: 8px 14px; cursor: pointer; +#wfgen .wf-seg button:first-child { border-radius: 8px 0 0 8px; margin-left: 0; } 
-  } +#wfgen .wf-seg button:last-child { border-radius: 0 8px 8px 0; } 
-  button:hover { background: var(--g1); } +#wfgen .wf-seg button[aria-pressed="true"] { background: var(--k); color: #fff; } 
-  button:focus-visible { outline: 3px solid var(--b); outline-offset: 2px; } +#wfgen button.wf-primary { background: var(--o); border-color: var(--o); font-size: 17px; padding: 10px 20px; } 
-  .seg button { border-radius: 0; margin-left: -2px; padding: 8px 12px; } +#wfgen button.wf-primary:hover { background: #ff7f33; border-color: #ff7f33; color: var(--k); } 
-  .seg button:first-child { border-radius: 8px 0 0 8px; margin-left: 0; } +#wfgen .wf-stage { width: 100%; } 
-  .seg button:last-child { border-radius: 0 8px 8px 0; } +#wfgen canvas { display: block; max-width: none; margin: 0; padding: 0; border: 0; } 
-  .seg button[aria-pressed="true"] { background: var(--k); color: #fff; } +#wfgen svg { display: block; } 
-  button.primary { background: var(--o); border-color: var(--o); font-size: 17px; padding: 10px 20px; } +#wfgen .wf-panel { position: relative; border: 2px solid var(--k); border-radius: 14px; padding: 14px 18px; margin-top: 14px; background: #fff; } 
-  button.primary:hover { background: #ff7f33; border-color: #ff7f33; } +#wfgen .wf-h { margin: 0 0 8px; font-size: 13px; font-weight: bold; color: var(--g3); text-transform: uppercase; line-height: 1.4; } 
-  #stage { width: 100%; } +#wfgen .wf-brief { margin: 0; padding: 0; white-space: pre-wrap; font: 16px/1.6 Arial, Helvetica, sans-serif; color: var(--k); } 
-  #stage canvas { display: block; } +#wfgen button.wf-iconbtn { position: absolute; top: 6px; right: 11px; width: 36px; height: 36px; min-width: 36px; padding: 0; border: 0; 
-  .panel { border: 2px solid var(--k); border-radius: 14px; padding: 14px 18px; margin-top: 14px; } +  background: transparent; color: var(--k); display: flex; align-items: center; justify-content: center; border-radius: 8px; } 
-  .panel h2 { margin: 0 0 8px; font-size: 13px; color: var(--g3); text-transform: uppercase; } +#wfgen button.wf-iconbtn:hover { background: var(--g1); } 
-  #brief { margin: 0; white-space: pre-wrap; font: 16px/1.6 Arial, Helvetica, sans-serif; } +#wfgen .wf-timer { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } 
-  .panel { position: relative; } +#wfgen .wf-time { font-size: 40px; font-weight: bold; min-width: 120px; font-variant-numeric: tabular-nums; line-height: 1.2; color: var(--k); } 
-  .iconbtn { position: absolute; top: 6px; right: 11px; width: 36px; height: 36px; padding: 0; border: 0; background: transparent; +#wfgen .wf-time.done { color: var(--o); } 
-             color: var(--k); display: flex; align-items: center; justify-content: center; border-radius: 8px; } +@media (max-width: 480px) { #wfgen { padding: 12px; } #wfgen .wf-time { font-size: 32px; min-width: 96px; } }
-  .iconbtn:hover { background: var(--g1); } +
-  .timer { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } +
-  #time { font-size: 40px; font-weight: bold; min-width: 120px; font-variant-numeric: tabular-nums; } +
-  #time.done { color: var(--o); } +
-  .note { color: var(--b); font-size: 14px; font-weight: bold; } +
-  @media (max-width: 480px) { #app { padding: 12px; } #time { font-size: 32px; min-width: 96px; } }+
 </style> </style>
-</head> +<div id="wfgen"> 
-<body> +  <div class="wf-title">Wireframe-Ideen-Generator</div> 
-<div id="app"> +  <div class="wf-hint">Klick auf eine Karte würfelt sie neu. Das Schloss hält eine Karte fest.</div>
-  <h1>Wireframe-Ideen-Generator</h1> +
-  <p class="hint">Klick auf eine Karte würfelt sie neu. Das Schloss hält eine Karte fest.</p>+
  
-  <div class="bar"> +  <div class="wf-bar"> 
-    <button class="primary" id="rollAll" type="button">Alles würfeln</button> +    <button type="button" class="wf-primary" data-wf="rollAll">Alles würfeln</button> 
-    <div class="grp" role="group" aria-label="Anzahl der Karten"> +    <div class="wf-grp" role="group" aria-label="Anzahl der Karten"> 
-      <span>Karten</span> +      <span class="wf-lbl">Karten</span> 
-      <div class="seg">+      <div class="wf-seg">
         <button type="button" data-n="3" aria-pressed="false">3 · Basis</button>         <button type="button" data-n="3" aria-pressed="false">3 · Basis</button>
         <button type="button" data-n="6" aria-pressed="true">6 · Voll</button>         <button type="button" data-n="6" aria-pressed="true">6 · Voll</button>
       </div>       </div>
     </div>     </div>
-    <div class="grp" role="group" aria-label="Chaos-Level"> +    <div class="wf-grp" role="group" aria-label="Chaos-Level"> 
-      <span>Chaos</span> +      <span class="wf-lbl">Chaos</span> 
-      <div class="seg">+      <div class="wf-seg">
         <button type="button" data-l="1" aria-pressed="false">realistisch</button>         <button type="button" data-l="1" aria-pressed="false">realistisch</button>
         <button type="button" data-l="2" aria-pressed="false">schräg</button>         <button type="button" data-l="2" aria-pressed="false">schräg</button>
Line 72: Line 59:
   </div>   </div>
  
-  <div id="stage" role="img" aria-label="Zufällig gewürfelte Karten: Produkt, Kundin, Thema, Gerät, Hauptaktion, Twist. Die Auswahl steht auch als Text unter den Karten."></div>+  <div class="wf-stage" data-wf="stage" role="img" aria-label="Zufällig gewürfelte Karten: Produkt, Kundin, Thema, Gerät, Hauptaktion, Twist. Die Auswahl steht auch als Text unter den Karten."></div>
  
-  <div class="panel" id="briefPanel"> +  <div class="wf-panel"> 
-    <h2>Dein Briefing</h2> +    <div class="wf-h">Dein Briefing</div> 
-    <button type="button" class="iconbtn" id="copyTxt" aria-label="Briefing kopieren" title="Briefing kopieren"> +    <button type="button" class="wf-iconbtn" data-wf="copy" aria-label="Briefing kopieren" title="Briefing kopieren"> 
-      <svg id="icCopy" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> +      <svg data-wf="icCopy" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="3"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/></svg> 
-        <rect x="8" y="8" width="12" height="12" rx="3"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/> +      <svg data-wf="icDone" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#2B5BFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="display:none"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>
-      </svg> +
-      <svg id="icDone" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#2B5BFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="display:none"> +
-        <path d="M5 12.5l4.5 4.5L19 7.5"/> +
-      </svg>+
     </button>     </button>
-    <pre id="brief" aria-live="polite"></pre>+    <div class="wf-brief" data-wf="brief" aria-live="polite"></div>
   </div>   </div>
  
-  <div class="panel"> +  <div class="wf-panel"> 
-    <h2>Timer</h2> +    <div class="wf-h">Timer</div> 
-    <div class="timer"> +    <div class="wf-timer"> 
-      <div id="time" aria-live="off">10:00</div> +      <div class="wf-time" data-wf="time">10:00</div> 
-      <button type="button" id="tStart">Start</button> +      <button type="button" data-wf="tStart">Start</button> 
-      <button type="button" id="tReset">Zurück</button> +      <button type="button" data-wf="tReset">Zurück</button> 
-      <div class="seg" role="group" aria-label="Zeit wählen">+      <div class="wf-seg" role="group" aria-label="Zeit wählen">
         <button type="button" data-min="10" aria-pressed="true">10 Min · Skizzen</button>         <button type="button" data-min="10" aria-pressed="true">10 Min · Skizzen</button>
         <button type="button" data-min="25" aria-pressed="false">25 Min · Mid-Fi</button>         <button type="button" data-min="25" aria-pressed="false">25 Min · Mid-Fi</button>
Line 100: Line 83:
   </div>   </div>
 </div> </div>
- +<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.min.js"></script>
-<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.min.js" +
-        onerror="document.getElementById('stage').textContent='p5.js konnte nicht geladen werden. Bitte Internetverbindung prüfen.'"></script>+
 <script> <script>
-/* ---------- Farben (wie in den Praktikumsfolien) ---------- */ +(function () { 
-const K = '#111111', O = '#FF6A13', B = '#2B5BFF', G1 = '#F3F3F3', G2 = '#D4D4D4', G3 = '#5D5D5D';+  'use strict'; 
 +  var root = document.getElementById('wfgen'); 
 +  if (!root) return; 
 +  var q = function (s) { return root.querySelector(s); }; 
 +  var qa = function (s) { return root.querySelectorAll(s); }; 
 +  var w = function (n) { return q('[data-wf="' + n + '"]'); }; 
 +  var host = w('stage'); 
 +  if (typeof p5 === 'undefined') { host.textContent = 'p5.js konnte nicht geladen werden (Internetverbindung oder Sicherheitseinstellung des Wikis prüfen).'; return; }
  
-/* ---------- Inhalte: [Text, Chaos-Stufe 1–3, (Breite, Höhe nur bei Gerät)] ---------- */ +  /* ---------- Farben (wie in den Praktikumsfolien) ---------- */ 
-const POOLS = [ +  const K = '#111111', O = '#FF6A13', B = '#2B5BFF', G2 = '#D4D4D4', G3 = '#5D5D5D';
-  { name: 'Produkt', label: 'PRODUKT', icon: 'produkt', items: [ +
-    ['Mobile App', 1], ['Website mit Landingpage', 1], ['Onlineshop', 1], ['Buchungs-Plattform', 1], +
-    ['Dashboard', 1], ['Lern-Plattform', 1], ['Karten- und Navigations-App', 1], ['Mitglieder-Portal', 1], +
-    ['Bezahlvorgang (Checkout)', 1], ['Onboarding-Strecke', 1], ['Chat-Oberfläche', 1], +
-    ['Dating-App', 2], ['Tauschbörse', 2], ['Bewertungsportal', 2], ['Podcast-App', 2], +
-    ['Rezept-Plattform', 2], ['Smart-Home-Steuerung', 2], ['Crowdfunding-Seite', 2], ['Kiosk-Terminal', 2], +
-    ['Social Network für genau zwei Personen', 3], ['Kontrollzentrum mit zu vielen Knöpfen', 3], +
-    ['Beschwerde-Hotline als App', 3], ['Wahl-O-Mat für Alltagsentscheidungen', 3], ['Notfall-Knopf-Portal', 3] +
-  ]}, +
-  { name: 'Kundin', label: 'KUNDIN', icon: 'kundin', items: [ +
-    ['Studentin im Prüfungsstress', 1], ['Landwirtin mit schlechtem Netz', 1], ['Busfahrerin im Schichtdienst', 1], +
-    ['Hebamme auf Nachtschicht', 1], ['Hochzeitsplanerin mit 14 Terminen', 1], ['Bibliothekarin, die alles sortiert', 1], +
-    ['Ärztin mit 3 Minuten Zeit', 1], ['Pilotin und alleinerziehend', 1], +
-    ['Gärtnerin mit 300 Zimmerpflanzen', 2], ['Marathonläuferin ohne Orientierungssinn', 2], +
-    ['Kapitänin eines Fischkutters ohne Empfang', 2], ['Zirkusdirektorin mit Lampenfieber', 2], +
-    ['Schornsteinfegerin mit Höhenangst', 2], ['Influencerin, die privat offline lebt', 2], +
-    ['Detektivin, die ständig etwas verlegt', 2], ['Köchin, die nie Rezepte liest', 2], +
-    ['Zeitreisende aus dem Jahr 1850', 3], ['Drachenbesitzerin mit Kleinunternehmen', 3], ['Hexe im Homeoffice', 3], +
-    ['Außerirdische Austauschstudentin', 3], ['Piratenkapitänin im Ruhestand', 3], ['Fee mit Burnout', 3], +
-    ['Waschbär-Anwältin', 3], ['Königin eines sehr kleinen Landes', 3], ['Yeti-Vorsitzende des Bergvereins', 3], +
-    ['Roboter-Oma mit Sprachfehler', 3], ['Einhorn-Züchterin mit Pollenallergie', 3] +
-  ]}, +
-  { name: 'Thema', label: 'THEMA', icon: 'thema', items: [ +
-    ['Nachbarschaftshilfe', 1], ['Zimmerpflanzen-Pflege', 1], ['Mittagspause', 1], ['Geburtstagsüberraschung', 1], +
-    ['Mülltrennung', 1], ['Mitfahrgelegenheiten', 1], ['Kaffee', 1], ['Urlaubsplanung', 1], ['Wocheneinkauf', 1], ['Umzug', 1], +
-    ['Socken, die einzeln verschwinden', 2], ['Yoga für Katzen', 2], ['Kompost mit Charakter', 2], +
-    ['Gerüchteküche im Büro', 2], ['Wolkenbeobachtung', 2], ['Schneckenrennen', 2], ['Lieblingsausreden', 2], +
-    ['Hochzeit mit Hindernissen', 2], ['Mittagsschlaf-Optimierung', 2], ['Verleih von Regenschirmen', 2], +
-    ['Weltherrschaft für Anfänger', 3], ['Schlafwandel-Navigation', 3], ['Zeitreise-Tickets', 3], +
-    ['Rückruf-Aktion für Glück', 3], ['Einhorn-Verleih', 3], ['Tauschbörse für Geheimnisse', 3], +
-    ['Die Kunst des Zuspätkommens', 3], ['Keks-Notfalldienst', 3], ['Wetterbeeinflussung im Kleingarten', 3], +
-    ['Verwaltung von Superkräften', 3], ['Geisterhaus-Vermietung', 3], ['Streit mit dem Toaster', 3] +
-  ]}, +
-  { name: 'Gerät', label: 'GERÄT / FORMAT', icon: 'geraet', items: [ +
-    ['Smartphone, Hochformat (390 × 844)', 1, 390, 844], ['Tablet, Hochformat (768 × 1024)', 1, 768, 1024], +
-    ['Desktop (1440 × 900)', 1, 1440, 900], +
-    ['Smartphone, Querformat (844 × 390)', 2, 844, 390], ['Smartwatch (ca. 200 × 240)', 2, 200, 240], +
-    ['Kiosk-Display, Querformat (1920 × 1080)', 2, 1920, 1080], ['Tablet, Querformat (1024 × 768)', 2, 1024, 768], +
-    ['Sehr kleines Display (ca. 120 × 120)', 3, 120, 120], ['Riesen-Bildschirm (3840 × 1080)', 3, 3840, 1080] +
-  ]}, +
-  { name: 'Hauptaktion', label: 'HAUPTAKTION (ORANGE!)', icon: 'aktion', items: [ +
-    ['Einen Termin buchen', 1], ['Etwas kaufen', 1], ['Sich anmelden', 1], ['Etwas bewerten', 1], ['Etwas teilen', 1], +
-    ['Eine Nachricht senden', 1], ['Hilfe rufen', 1], ['Etwas zurückgeben', 1], ['Jemanden finden', 1], ['Etwas melden', 1], +
-    ['Eine Ausrede abschicken', 2], ['Eine Überraschung planen', 2], ['Etwas verleihen', 2], +
-    ['Einen Streit schlichten', 2], ['Eine Wette abschließen', 2], +
-    ['Den Alarm auslösen', 3], ['Wolken bestellen', 3], ['Eine Verschwörung melden', 3], +
-    ['Das Universum neu starten', 3], ['Einen Drachen adoptieren', 3] +
-  ]}, +
-  { name: 'Twist', label: 'TWIST', icon: 'twist', items: [ +
-    ['Nur 3 Elemente pro Screen', 1], ['Alles auf einem Screen, ohne Scrollen', 1], ['Ohne Text, nur Symbole', 1], +
-    ['Die Nutzerin hat nur eine Hand frei', 1], ['Schlechter Empfang: Offline-Zustand mitdenken', 1], +
-    ['Besonders große Touch-Ziele', 1], ['Hoher Kontrast und große Schrift (barrierearm)', 1], +
-    ['Erster Besuch, keine Anleitung', 1], ['Fehlerfall mitzeichnen: Was geht schief?', 1], +
-    ['Die Nutzerin ist wütend', 2], ['In 30 Sekunden bedienbar', 2], ['Navigation nur am unteren Rand', 2], +
-    ['Alle Inhalte in einer einzigen Liste', 2], ['Zwei wichtige Aktionen, aber nur eine darf orange sein', 2], +
-    ['Bedienung nur mit dem Ellbogen', 3], ['Das Display ist nass vom Regen', 3], ['Es gibt nur einen einzigen Button', 3], +
-    ['Die Nutzerin liest nur jedes zweite Wort', 3], ['Bedienung im Dunkeln', 3] +
-  ]} +
-];+
  
-/* ---------- Zustand ---------- */ +  /* ---------- Inhalte: [Text, Chaos-Stufe 1–3, (Breite, Höhe nur bei Gerät)] ---------- */ 
-const S = { sel: [0,0,0,0,0,0], locked: [false,false,false,false,false,false], until: [0,0,0,0,0,0], level: 3, n: 6 }; +  const POOLS = [ 
-let hoverIdx = -1, hoverLock = false, host;+    { name: 'Produkt', label: 'PRODUKT', icon: 'produkt', items: [ 
 +      ['Mobile App', 1], ['Website mit Landingpage', 1], ['Onlineshop', 1], ['Buchungs-Plattform', 1], 
 +      ['Dashboard', 1], ['Lern-Plattform', 1], ['Karten- und Navigations-App', 1], ['Mitglieder-Portal', 1], 
 +      ['Bezahlvorgang (Checkout)', 1], ['Onboarding-Strecke', 1], ['Chat-Oberfläche', 1], 
 +      ['Dating-App', 2], ['Tauschbörse', 2], ['Bewertungsportal', 2], ['Podcast-App', 2], 
 +      ['Rezept-Plattform', 2], ['Smart-Home-Steuerung', 2], ['Crowdfunding-Seite', 2], ['Kiosk-Terminal', 2], 
 +      ['Social Network für genau zwei Personen', 3], ['Kontrollzentrum mit zu vielen Knöpfen', 3], 
 +      ['Beschwerde-Hotline als App', 3], ['Wahl-O-Mat für Alltagsentscheidungen', 3], ['Notfall-Knopf-Portal', 3] 
 +    ]}, 
 +    { name: 'Kundin', label: 'KUNDIN', icon: 'kundin', items: [ 
 +      ['Studentin im Prüfungsstress', 1], ['Landwirtin mit schlechtem Netz', 1], ['Busfahrerin im Schichtdienst', 1], 
 +      ['Hebamme auf Nachtschicht', 1], ['Hochzeitsplanerin mit 14 Terminen', 1], ['Bibliothekarin, die alles sortiert', 1], 
 +      ['Ärztin mit 3 Minuten Zeit', 1], ['Pilotin und alleinerziehend', 1], 
 +      ['Gärtnerin mit 300 Zimmerpflanzen', 2], ['Marathonläuferin ohne Orientierungssinn', 2], 
 +      ['Kapitänin eines Fischkutters ohne Empfang', 2], ['Zirkusdirektorin mit Lampenfieber', 2], 
 +      ['Schornsteinfegerin mit Höhenangst', 2], ['Influencerin, die privat offline lebt', 2], 
 +      ['Detektivin, die ständig etwas verlegt', 2], ['Köchin, die nie Rezepte liest', 2], 
 +      ['Zeitreisende aus dem Jahr 1850', 3], ['Drachenbesitzerin mit Kleinunternehmen', 3], ['Hexe im Homeoffice', 3], 
 +      ['Außerirdische Austauschstudentin', 3], ['Piratenkapitänin im Ruhestand', 3], ['Fee mit Burnout', 3], 
 +      ['Waschbär-Anwältin', 3], ['Königin eines sehr kleinen Landes', 3], ['Yeti-Vorsitzende des Bergvereins', 3], 
 +      ['Roboter-Oma mit Sprachfehler', 3], ['Einhorn-Züchterin mit Pollenallergie', 3] 
 +    ]}, 
 +    { name: 'Thema', label: 'THEMA', icon: 'thema', items: [ 
 +      ['Nachbarschaftshilfe', 1], ['Zimmerpflanzen-Pflege', 1], ['Mittagspause', 1], ['Geburtstagsüberraschung', 1], 
 +      ['Mülltrennung', 1], ['Mitfahrgelegenheiten', 1], ['Kaffee', 1], ['Urlaubsplanung', 1], ['Wocheneinkauf', 1], ['Umzug', 1], 
 +      ['Socken, die einzeln verschwinden', 2], ['Yoga für Katzen', 2], ['Kompost mit Charakter', 2], 
 +      ['Gerüchteküche im Büro', 2], ['Wolkenbeobachtung', 2], ['Schneckenrennen', 2], ['Lieblingsausreden', 2], 
 +      ['Hochzeit mit Hindernissen', 2], ['Mittagsschlaf-Optimierung', 2], ['Verleih von Regenschirmen', 2], 
 +      ['Weltherrschaft für Anfänger', 3], ['Schlafwandel-Navigation', 3], ['Zeitreise-Tickets', 3], 
 +      ['Rückruf-Aktion für Glück', 3], ['Einhorn-Verleih', 3], ['Tauschbörse für Geheimnisse', 3], 
 +      ['Die Kunst des Zuspätkommens', 3], ['Keks-Notfalldienst', 3], ['Wetterbeeinflussung im Kleingarten', 3], 
 +      ['Verwaltung von Superkräften', 3], ['Geisterhaus-Vermietung', 3], ['Streit mit dem Toaster', 3] 
 +    ]}, 
 +    { name: 'Gerät', label: 'GERÄT / FORMAT', icon: 'geraet', items: [ 
 +      ['Smartphone, Hochformat (390 × 844)', 1, 390, 844], ['Tablet, Hochformat (768 × 1024)', 1, 768, 1024], 
 +      ['Desktop (1440 × 900)', 1, 1440, 900], 
 +      ['Smartphone, Querformat (844 × 390)', 2, 844, 390], ['Smartwatch (ca. 200 × 240)', 2, 200, 240], 
 +      ['Kiosk-Display, Querformat (1920 × 1080)', 2, 1920, 1080], ['Tablet, Querformat (1024 × 768)', 2, 1024, 768], 
 +      ['Sehr kleines Display (ca. 120 × 120)', 3, 120, 120], ['Riesen-Bildschirm (3840 × 1080)', 3, 3840, 1080] 
 +    ]}, 
 +    { name: 'Hauptaktion', label: 'HAUPTAKTION (ORANGE!)', icon: 'aktion', items: [ 
 +      ['Einen Termin buchen', 1], ['Etwas kaufen', 1], ['Sich anmelden', 1], ['Etwas bewerten', 1], ['Etwas teilen', 1], 
 +      ['Eine Nachricht senden', 1], ['Hilfe rufen', 1], ['Etwas zurückgeben', 1], ['Jemanden finden', 1], ['Etwas melden', 1], 
 +      ['Eine Ausrede abschicken', 2], ['Eine Überraschung planen', 2], ['Etwas verleihen', 2], 
 +      ['Einen Streit schlichten', 2], ['Eine Wette abschließen', 2], 
 +      ['Den Alarm auslösen', 3], ['Wolken bestellen', 3], ['Eine Verschwörung melden', 3], 
 +      ['Das Universum neu starten', 3], ['Einen Drachen adoptieren', 3] 
 +    ]}, 
 +    { name: 'Twist', label: 'TWIST', icon: 'twist', items: [ 
 +      ['Nur 3 Elemente pro Screen', 1], ['Alles auf einem Screen, ohne Scrollen', 1], ['Ohne Text, nur Symbole', 1], 
 +      ['Die Nutzerin hat nur eine Hand frei', 1], ['Schlechter Empfang: Offline-Zustand mitdenken', 1], 
 +      ['Besonders große Touch-Ziele', 1], ['Hoher Kontrast und große Schrift (barrierearm)', 1], 
 +      ['Erster Besuch, keine Anleitung', 1], ['Fehlerfall mitzeichnen: Was geht schief?', 1], 
 +      ['Die Nutzerin ist wütend', 2], ['In 30 Sekunden bedienbar', 2], ['Navigation nur am unteren Rand', 2], 
 +      ['Alle Inhalte in einer einzigen Liste', 2], ['Zwei wichtige Aktionen, aber nur eine darf orange sein', 2], 
 +      ['Bedienung nur mit dem Ellbogen', 3], ['Das Display ist nass vom Regen', 3], ['Es gibt nur einen einzigen Button', 3], 
 +      ['Die Nutzerin liest nur jedes zweite Wort', 3], ['Bedienung im Dunkeln', 3] 
 +    ]} 
 +  ];
  
-/* ---------- Würfeln ---------- */ +  /* ---------- Zustand ---------- */ 
-function pickIndex(pi) { +  const S = { sel: [0, 0, 0, 0, 0, 0], locked: [false, false, false, false, false, false], until: [0, 0, 0, 0, 0, 0], level: 3, n: 6 }; 
-  const items = POOLS[pi].items; +  let hoverIdx = -1, hoverLock = false, sk = null;
-  let cands = []; +
-  items.forEach((it, ix) => { if (it[1] <= S.level) cands.push([ix, it[1] === S.level ? 3 : 1]); }); +
-  if (cands.length > 1) cands = cands.filter(c => c[0] !== S.sel[pi]); +
-  const total = cands.reduce((a, c) => a + c[1], 0); +
-  let r = Math.random() * total; +
-  for (const c of cands) { r -= c[1]; if (r <= 0) return c[0]; } +
-  return cands[0][0]; +
-} +
-function rollCard(i, order) { +
-  if (S.locked[i]) return; +
-  S.sel[i] = pickIndex(i); +
-  S.until[i] = millis() + 450 + (order || 0) * 120; +
-  loop(); +
-} +
-function rollAll() { for (let i = 0; i < S.n; i++) rollCard(i, i); updateBrief(); }+
  
-/* ---------- Layout ---------- */ +  /* ---------- Würfeln ---------- */ 
-function layout() { +  function pickIndex(pi) { 
-  const w = width, cols = w >= 720 ? 3 : (w >= 460 ? 2 : 1), gap = 14; +    const items = POOLS[pi].items; 
-  const cw = (w - gap * (cols - 1)) / cols, ch = cols === 3 ? 172 : (cols === 2 ? 164 : 144); +    let cands = []; 
-  const rects = [], rows = Math.ceil(S.n / cols); +    items.forEach(function (it, ix) { if (it[1] <= S.level) cands.push([ix, it[1] === S.level ? 3 : 1]); }); 
-  for (let i = 0; i < S.n; i++) { +    if (cands.length > 1) cands = cands.filter(function (c) { return c[0] !== S.sel[pi]; }); 
-    rects.push({ x: (i % cols) * (cw + gap), y: Math.floor(i / cols) * (ch + gap), w: cw, h: ch });+    const total = cands.reduce(function (a, c) { return a + c[1]; }, 0); 
 +    let r = Math.random() * total; 
 +    for (const c of cands) { r -= c[1]; if (r <= 0) return c[0]; } 
 +    return cands[0][0];
   }   }
-  return { rects, H: rows * ch + (rows - 1) * gap }; +  function rollCard(i, order) { 
-} +    if (S.locked[i]) return; 
-function applyLayout() { +    S.sel[i] = pickIndex(i); 
-  const w = Math.max(280, host.clientWidth); +    S.until[i] = sk.millis() + 450 + (order || 0) * 120; 
-  resizeCanvas(w, 10, true); +    sk.loop(); 
-  resizeCanvas(w, layout().H); +  } 
-}+  function rollAll() { for (let i = 0; i < S.n; i++) rollCard(i, i); updateBrief(); }
  
-/* ---------- Zeichnen ---------- */ +  /* ---------- Briefing ---------- */ 
-function wrapLines(str, w) { +  function briefText() { 
-  const words = str.split(' '), lines = []; let cur = ''; +    const out = []; 
-  for (const wd of words) { +    for (let i = 0; i < S.n; i++) out.push(POOLS[i].name + ': ' + POOLS[i].items[S.sel[i]][0]); 
-    const t = cur ? cur + ' ' + wd : wd; +    return out.join('\n');
-    if (!cur || textWidth(t) <= w) cur = t; else { lines.push(cur); cur = wd; }+
   }   }
-  if (cur) lines.push(cur); +  function updateBrief() { w('brief').textContent = briefText(); } 
-  return lines; + 
-} +  /* ---------- p5-Sketch (Instanzmodus, kein globaler Namensraum) ---------- */ 
-function fit(str, w, h) { +  const sketch = function (p) { 
-  textStyle(BOLD); +    sk = p; 
-  for (let s = 28; s >= 14; s--) { +    let cnv; 
-    textSize(s); + 
-    const ls = wrapLines(str, w); +    function layout() { 
-    if (ls.length * s * 1.2 <= h && ls.every(l => textWidth(l) <= w)) return { s, ls }; +      const W = p.width, cols = W >= 720 ? 3 : (W >= 460 ? 2 : 1), gap = 14; 
-  } +      const cw = (W - gap * (cols - 1)) / cols, ch = cols === 3 ? 172 : (cols === 2 ? 164 : 144); 
-  textSize(14); +      const rects = [], rows = Math.ceil(S.n / cols); 
-  return { s: 14, ls: wrapLines(str, w) }; +      for (let i = 0; i < S.n; i++) rects.push({ x: (i % cols) * (cw + gap), y: Math.floor(i / cols) * (ch + gap), w: cw, h: ch }); 
-} +      return { rects: rects, H: rows * ch + (rows - 1) * gap }; 
-function drawIcon(kind, x, y, item) { +    } 
-  noFill(); stroke(K); strokeWeight(2 / 0.75); // wirkt nach scale(0.75) wie 2 px, wie die Kästchen +    function applyLayout() { 
-  if (kind === 'produkt') { +      const W = Math.max(280, host.clientWidth); 
-    rect(x, y + 4, 44, 34, 4); line(x, y + 13, x + 44, y + 13); +      p.resizeCanvas(W, 10, true); 
-    noStroke(); fill(O); circle(x + 7, y + 8.5, 4); +      p.resizeCanvas(W, layout().H); 
-  } else if (kind === 'kundin') { +    } 
-    circle(x + 22, y + 13, 16); arc(x + 22, y + 44, 34, 30, PI, TWO_PI); +    function wrapLines(str, maxW) { 
-  } else if (kind === 'thema') { +      const words = str.split(' '), lines = []; let cur = ''; 
-    rect(x, y + 6, 44, 32, 3); line(x, y + 6, x + 44, y + 38); line(x + 44, y + 6, x, y + 38); +      for (const wd of words) { 
-  } else if (kind === 'geraet') { +        const t = cur ? cur + ' ' + wd : wd; 
-    const sc = Math.min(40 / item[2], 40 / item[3]), w = item[2] * sc, h = item[3] * sc; +        if (!cur || p.textWidth(t) <= maxW) cur = t; else { lines.push(cur); cur = wd; } 
-    rect(x + 22 - w / 2, y + 22 - h / 2, w, h, 3); +      } 
-  } else if (kind === 'aktion') { +      if (cur) lines.push(cur); 
-    noStroke(); fill(O); rect(x, y + 10, 44, 24, 7); +      return lines; 
-  } else if (kind === 'twist') { +    } 
-    noStroke(); fill(B); push(); translate(x + 22, y + 22); rotate(PI / 4); rect(-13, -13, 26, 26, 3); pop(); +    function fit(str, maxW, maxH) { 
-  } +      p.textStyle(p.BOLD); 
-} +      for (let s = 28; s >= 14; s--) { 
-function drawLock(x, y, locked) { +        p.textSize(s); 
-  const c = locked ? B : G3; +        const ls = wrapLines(str, maxW); 
-  stroke(c); strokeWeight(2); noFill(); +        if (ls.length * s * 1.2 <= maxH && ls.every(function (l) { return p.textWidth(l) <= maxW; })) return { s: s, ls: ls }; 
-  arc(x + 9, y + (locked ? 8 : 4), 12, 14, PI, TWO_PI); +      } 
-  fill(locked ? B : 255); rect(x, y + 8, 18, 13, 3); +      p.textSize(14); 
-} +      return { s: 14, ls: wrapLines(str, maxW) }; 
-function drawCard(i, r, item, rolling) { +    } 
-  const p = POOLS[i]; +    function drawIcon(kind, x, y, item) { 
-  // 2 px Kontur, 14 px Radius, 1 px nach innen versetzt: exakt wie das Kästchen „Dein Briefing“ +      p.noFill(); p.stroke(K); p.strokeWeight(2 / 0.75); // wirkt nach scale(0.75) wie 2 px, wie die Kästchen 
-  stroke(hoverIdx === i ? O : K); strokeWeight(2); fill(255); rect(r.x + 1, r.y + 1, r.w - 2, r.h - 2, 14); +      if (kind === 'produkt') { 
-  noStroke(); fill(G3); textStyle(BOLD); textSize(13); textAlign(LEFT, TOP); +        p.rect(x, y + 4, 44, 34, 4); p.line(x, y + 13, x + 44, y + 13); 
-  text(p.label, r.x + 20, r.y + 16); +        p.noStroke(); p.fill(O); p.circle(x + 7, y + 8.5, 4); 
-  push(); translate(r.x + r.w - 20 - 33, r.y + 14); scale(0.75); drawIcon(p.icon, 0, 0, item); pop(); +      } else if (kind === 'kundin') { 
-  const zone = { x: r.x + 20, y: r.y + 52, w: r.w - 40, h: r.h - 52 - 40 }; +        p.circle(x + 22, y + 13, 16); p.arc(x + 22, y + 44, 34, 30, p.PI, p.TWO_PI); 
-  const f = fit(item[0], zone.w, zone.h); +      } else if (kind === 'thema') { 
-  textSize(f.s); textStyle(BOLD); noStroke(); fill(rolling ? G2 : K); textAlign(LEFT, TOP); +        p.rect(x, y + 6, 44, 32, 3); p.line(x, y + 6, x + 44, y + 38); p.line(x + 44, y + 6, x, y + 38); 
-  f.ls.forEach((l, k) => text(l, zone.x, zone.y + k * f.s * 1.2)); +      } else if (kind === 'geraet') { 
-  // unten: neu würfeln + Schloss +        const sc = Math.min(40 / item[2], 40 / item[3]), dw = item[2] * sc, dh = item[3] * sc; 
-  noFill(); stroke(G3); strokeWeight(2); arc(r.x + 26, r.y + r.h - 26, 11, 11, 0.5, TWO_PI - 0.6); +        p.rect(x + 22 - dw / 2, y + 22 - dh / 2, dw, dh, 3); 
-  noStroke(); fill(G3); textSize(12); textStyle(NORMAL); textAlign(LEFT, CENTER); +      } else if (kind === 'aktion') { 
-  text('neu würfeln', r.x + 40, r.y + r.h - 26); +        p.noStroke(); p.fill(O); p.rect(x, y + 10, 44, 24, 7); 
-  drawLock(r.x + r.w - 20 - 18, r.y + r.h - 16 - 21, S.locked[i]); +      } else if (kind === 'twist') { 
-} +        p.noStroke(); p.fill(B); p.push(); p.translate(x + 22, y + 22); p.rotate(p.PI / 4); p.rect(-13, -13, 26, 26, 3); p.pop(); 
-function setup() { +      } 
-  host = document.getElementById('stage'); +    } 
-  const c = createCanvas(Math.max(280, host.clientWidth), 10); +    function drawLock(x, y, locked) { 
-  c.parent(host); textFont('Arial'); frameRate(30); +      p.stroke(locked ? B : G3); p.strokeWeight(2); p.noFill(); 
-  const el = c.elt; +      p.arc(x + 9, y + (locked ? 8 : 4), 12, 14, p.PI, p.TWO_PI); 
-  el.addEventListener('click', e => { +      p.fill(locked ? B : 255); p.rect(x, y + 8, 18, 13, 3); 
-    const h = hit(e.offsetX, e.offsetY); if (h.i < 0) return; +    } 
-    if (h.lock) { S.locked[h.i] = !S.locked[h.i]; redraw(); } else { rollCard(h.i, 0); updateBrief(); } +    function drawCard(i, r, item, rolling) { 
-  }); +      const pool = POOLS[i]; 
-  el.addEventListener('mousemove', e => { +      // 2 px Kontur, 14 px Radius, 1 px nach innen versetzt: wie das Kaestchen Dein Briefing 
-    const h = hit(e.offsetX, e.offsetY); +      p.stroke(hoverIdx === i ? O : K); p.strokeWeight(2); p.fill(255); p.rect(r.x + 1, r.y + 1, r.w - 2, r.h - 2, 14); 
-    if (h.i !== hoverIdx || h.lock !== hoverLock) { hoverIdx = h.i; hoverLock = h.lock; el.style.cursor = h.i >= 0 ? 'pointer' : 'default'; redraw(); } +      p.noStroke(); p.fill(G3); p.textStyle(p.BOLD); p.textSize(13); p.textAlign(p.LEFT, p.TOP); 
-  }); +      p.text(pool.label, r.x + 20, r.y + 16); 
-  el.addEventListener('mouseleave', () => { hoverIdx = -1; hoverLock = false; redraw(); }); +      p.push(); p.translate(r.x + r.w - 20 - 33, r.y + 14); p.scale(0.75); drawIcon(pool.icon, 0, 0, item); p.pop(); 
-  window.addEventListener('resize', () => { applyLayout(); }); +      const zone = { x: r.x + 20, y: r.y + 52, w: r.w - 40, h: r.h - 52 - 40 }; 
-  initFromHash(); +      const f = fit(item[0], zone.w, zone.h); 
-} +      p.textSize(f.s); p.textStyle(p.BOLD); p.noStroke(); p.fill(rolling ? G2 : K); p.textAlign(p.LEFT, p.TOP); 
-function draw() { +      f.ls.forEach(function (l, k) { p.text(l, zone.x, zone.y + k * f.s * 1.2); }); 
-  background(255); +      p.noFill(); p.stroke(G3); p.strokeWeight(2); p.arc(r.x + 26, r.y + r.h - 26, 11, 11, 0.5, p.TWO_PI - 0.6); 
-  const L = layout(), now = millis(); let busy = false; +      p.noStroke(); p.fill(G3); p.textSize(12); p.textStyle(p.NORMAL); p.textAlign(p.LEFT, p.CENTER); 
-  for (let i = 0; i < S.n; i++) { +      p.text('neu würfeln', r.x + 40, r.y + r.h - 26); 
-    const rolling = now < S.until[i]; if (rolling) busy = true; +      drawLock(r.x + r.w - 20 - 18, r.y + r.h - 16 - 21, S.locked[i]); 
-    const items = POOLS[i].items; +    } 
-    const idx = rolling ? Math.floor(Math.abs(Math.sin(Math.floor(now / 70) * 12.9898 + i * 78.233)) * 10000) % items.length : S.sel[i]; +    function hit(mx, my) { 
-    drawCard(i, L.rects[i], items[idx], rolling); +      const L = layout(); 
-  } +      for (let i = 0; i < S.n; i++) { 
-  if (!busy) noLoop(); +        const r = L.rects[i]; 
-} +        if (mx >= r.x && mx <= r.x + r.w && my >= r.y && my <= r.y + r.h) return { i: i, lock: mx > r.x + r.w - 56 && my > r.y + r.h - 50 }; 
-function hit(mx, my) { +      } 
-  const L = layout(); +      return { i: -1, lock: false };
-  for (let i = 0; i < S.n; i++) { +
-    const r = L.rects[i]; +
-    if (mx >= r.x && mx <= r.x + r.w && my >= r.y && my <= r.y + r.h) { +
-      return { i, lock: mx > r.x + r.w - 56 && my > r.y + r.h - 50 };+
     }     }
-  } 
-  return { i: -1, lock: false }; 
-} 
  
-/* ---------- Briefing, Teilen ---------- */ +    p.setup = function () { 
-function briefText() { +      cnv = p.createCanvas(Math.max(280, host.clientWidth), 10); 
-  const out = []; +      p.textFont('Arial'); p.frameRate(30); 
-  for (let i = 0; i < S.n; i++) out.push(POOLS[i].name + ': ' + POOLS[i].items[S.sel[i]][0]); +      const el = cnv.elt; 
-  return out.join('\n'); +      el.addEventListener('click', function (e) { 
-} +        const h = hit(e.offsetX, e.offsetY); if (h.i < 0) return; 
-function updateBrief() { document.getElementById('brief').textContent = briefText(); } +        if (h.lock) { S.locked[h.i] = !S.locked[h.i]; p.redraw(); } else { rollCard(h.i, 0); updateBrief(); } 
-function initFromHash() { +      }); 
-  let ok = false; +      el.addEventListener('mousemove', function (e) { 
-  try { +        const h = hit(e.offsetX, e.offsetY); 
-    const m = location.hash.replace('#', '').split('.'); +        if (h.i !== hoverIdx || h.lock !== hoverLock) { hoverIdx = h.i; hoverLock = h.lock; el.style.cursor = h.i >= 0 ? 'pointer' : 'default'; p.redraw(); } 
-    if (m.length === 3) { +      }); 
-      const n = parseInt(m[0], 10), l = parseInt(m[1], 10), sel = m[2].split('-').map(x => parseInt(x, 10)); +      el.addEventListener('mouseleave', function () { hoverIdx = -1; hoverLock = false; p.redraw(); }); 
-      if ((n === 3 || n === 6) && l >= 1 && l <= 3 && sel.length === 6 && +      window.addEventListener('resize', applyLayout); 
-          sel.every((v, i) => Number.isInteger(v) && v >= 0 && v < POOLS[i].items.length)) { +      syncSeg(); applyLayout(); rollAll(); 
-        S.n = n; S.level = l; S.sel = sel; ok = true;+    }; 
 +    p.draw = function () { 
 +      p.background(255); 
 +      const L = layout(), now = p.millis(); let busy = false; 
 +      for (let i = 0; i < S.n; i++) { 
 +        const rolling = now < S.until[i]; if (rolling) busy = true; 
 +        const items = POOLS[i].items; 
 +        const idx = rolling ? Math.floor(Math.abs(Math.sin(Math.floor(now / 70) * 12.9898 + i * 78.233)) * 10000) % items.length : S.sel[i]; 
 +        drawCard(i, L.rects[i], items[idx], rolling);
       }       }
 +      if (!busy) p.noLoop();
 +    };
 +    sk.applyLayout = applyLayout;
 +  };
 +
 +  /* ---------- Bedienelemente ---------- */
 +  function syncSeg() {
 +    qa('[data-n]').forEach(function (b) { b.setAttribute('aria-pressed', String(+b.dataset.n === S.n)); });
 +    qa('[data-l]').forEach(function (b) { b.setAttribute('aria-pressed', String(+b.dataset.l === S.level)); });
 +  }
 +  w('rollAll').addEventListener('click', rollAll);
 +  qa('[data-n]').forEach(function (b) { b.addEventListener('click', function () {
 +    S.n = +b.dataset.n; syncSeg(); sk.applyLayout(); updateBrief();
 +  }); });
 +  qa('[data-l]').forEach(function (b) { b.addEventListener('click', function () {
 +    S.level = +b.dataset.l; syncSeg(); rollAll();
 +  }); });
 +
 +  async function copyText(text) {
 +    try { await navigator.clipboard.writeText(text); }
 +    catch (e) {
 +      const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select();
 +      try { document.execCommand('copy'); } catch (_) {}
 +      ta.remove();
     }     }
-  } catch (e) {} +  } 
-  syncSeg(); applyLayout(); +  w('copy').addEventListener('click', async function () { 
-  if (ok) { updateBrief(); } else { rollAll(); } +    await copyText(briefText()); 
-}+    const a = w('icCopy'), b = w('icDone'); 
 +    a.style.display = 'none'; b.style.display = 'block'; 
 +    setTimeout(function () { a.style.display = 'block'; b.style.display = 'none'; }, 1400); 
 +  });
  
-/* ---------- Bedienelemente ---------- */ +  /* ---------- Timer ---------- */ 
-function syncSeg() { +  let tTotal = 600, tLeft = 600, tEnd = 0, tTimer = null; 
-  document.querySelectorAll('[data-n]').forEach(b => b.setAttribute('aria-pressed', String(+b.dataset.n === S.n))); +  const timeEl = w('time'), startBtn = w('tStart'); 
-  document.querySelectorAll('[data-l]').forEach(b => b.setAttribute('aria-pressed', String(+b.dataset.l === S.level))); +  function fmt(s) { return String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0'); } 
-} +  function showTime() { timeEl.textContent = tLeft > 0 ? fmt(tLeft) : 'Zeit!'; timeEl.classList.toggle('done', tLeft <= 0); } 
-document.getElementById('rollAll').addEventListener('click', rollAll); +  function tick() { 
-document.querySelectorAll('[data-n]').forEach(b => b.addEventListener('click', () => { +    tLeft = Math.max(0, Math.ceil((tEnd - Date.now()) / 1000)); showTime(); 
-  S.n = +b.dataset.n; syncSeg(); applyLayout(); updateBrief(); +    if (tLeft <= 0) { clearInterval(tTimer); tTimer = null; startBtn.textContent = 'Start'; } 
-})); +  } 
-document.querySelectorAll('[data-l]').forEach(b => b.addEventListener('click', () => { +  startBtn.addEventListener('click', function () { 
-  S.level = +b.dataset.l; syncSeg(); rollAll(); +    if (tTimer) { clearInterval(tTimer); tTimer = null; startBtn.textContent = 'Weiter'; return; } 
-})); +    if (tLeft <= 0) tLeft = tTotal; 
-async function copyText(text) { +    tEnd = Date.now() + tLeft * 1000; tTimer = setInterval(tick, 250); startBtn.textContent = 'Pause'; tick(); 
-  try { await navigator.clipboard.writeText(text); } +  }); 
-  catch (e) { +  function resetTimer() { 
-    const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); +    if (tTimer) { clearInterval(tTimer); tTimer = null; } 
-    try { document.execCommand('copy'); } catch (_) {} +    tLeft = tTotal; startBtn.textContent = 'Start'; showTime();
-    ta.remove();+
   }   }
-} +  w('tReset').addEventListener('click', resetTimer); 
-document.getElementById('copyTxt').addEventListener('click', async () => { +  qa('[data-min]').forEach(function (b) { b.addEventListener('click', function () { 
-  await copyText(briefText()); +    tTotal = +b.dataset.min * 60; 
-  const a = document.getElementById('icCopy'), b = document.getElementById('icDone'); +    qa('[data-min]').forEach(function (x) { x.setAttribute('aria-pressed', String(x === b)); }); 
-  a.style.display = 'none'; b.style.display = 'block'; +    resetTimer(); 
-  setTimeout(() => { a.style.display = 'block'; b.style.display = 'none'; }, 1400); +  }); });
-});+
  
-/* ---------- Timer ---------- */ +  new p5(sketch, host); 
-let tTotal = 600, tLeft = 600, tEnd = 0, tTimer = null; +})();
-const timeEl = document.getElementById('time'), startBtn = document.getElementById('tStart'); +
-function fmt(s) { return String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0'); } +
-function showTime() { +
-  timeEl.textContent = tLeft > 0 ? fmt(tLeft) : 'Zeit!'; +
-  timeEl.classList.toggle('done', tLeft <= 0); +
-} +
-function tick() { +
-  tLeft = Math.max(0, Math.ceil((tEnd - Date.now()) / 1000)); showTime(); +
-  if (tLeft <= 0) { clearInterval(tTimer); tTimer = null; startBtn.textContent = 'Start'; } +
-} +
-startBtn.addEventListener('click', () => { +
-  if (tTimer) { clearInterval(tTimer); tTimer = null; startBtn.textContent = 'Weiter'; return; } +
-  if (tLeft <= 0) tLeft = tTotal; +
-  tEnd = Date.now() + tLeft * 1000; tTimer = setInterval(tick, 250); startBtn.textContent = 'Pause'; tick(); +
-}); +
-function resetTimer() { +
-  if (tTimer) { clearInterval(tTimer); tTimer = null; } +
-  tLeft = tTotal; startBtn.textContent = 'Start'; showTime(); +
-} +
-document.getElementById('tReset').addEventListener('click', resetTimer); +
-document.querySelectorAll('[data-min]').forEach(b => b.addEventListener('click', () => { +
-  tTotal = +b.dataset.min * 60; +
-  document.querySelectorAll('[data-min]').forEach(x => x.setAttribute('aria-pressed', String(x === b))); +
-  resetTimer(); +
-}));+
 </script> </script>
-</body> +</HTML>
-</html>+
/var/www/vhosts/ct-lab.info/wiki.ct-lab.info/data/pages/extras/codikon/p5js/wireframe-ideen-generator.txt · Last modified: by Leonie Winkelmann