CoolFace
Apppublic

HirModel/human-translation-accessibility-stack

sourceHugging Facecc-by-nc-4.0updated 5mo agoView on Hugging Face
0likes
threshold_for_nonspeakers.html857 linesDownload Raw Back to root
1<!DOCTYPE html>2<html lang="en">3<head>4  <meta charset="UTF-8">5  <meta name="viewport" content="width=device-width, initial-scale=1.0">6  <title>Threshold ยท Communication Bridge for Nonspeakers</title>7  <style>8    :root {9      --bg: #0b0c0d;10      --panel: #151312;11      --panel2: #1f1b18;12      --ink: #f3ede2;13      --muted: #cbbda9;14      --soft: #9e8f7a;15      --copper: #c77748;16      --terracotta: #b75d3a;17      --gold: #d7a85b;18      --green: #73956f;19      --line: rgba(215,168,91,.36);20      --danger: #d26a5c;21      --safe: #7dbb8e;22      --shadow: rgba(0,0,0,.45);23    }24    25    * { box-sizing: border-box; margin: 0; padding: 0; }26    27    body {28      margin: 0;29      background: var(--bg);30      color: var(--ink);31      font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;32      line-height: 1.6;33      touch-action: manipulation;34      user-select: none;35      -webkit-user-select: none;36      overflow-x: hidden;37    }38    39    .container {40      max-width: 100%;41      padding: 0;42    }43    44    /* HEADER */45    .header {46      background: linear-gradient(180deg, var(--panel) 0%, var(--bg) 100%);47      border-bottom: 2px solid var(--copper);48      padding: 20px;49      text-align: center;50    }51    52    .header h1 {53      font-size: clamp(24px, 5vw, 36px);54      color: var(--gold);55      margin-bottom: 8px;56      font-weight: 600;57    }58    59    .header p {60      font-size: clamp(12px, 3vw, 14px);61      color: var(--muted);62      max-width: 600px;63      margin: 0 auto;64    }65    66    /* MODE SELECTOR */67    .mode-select {68      display: flex;69      gap: 10px;70      padding: 15px;71      background: var(--panel);72      border-bottom: 1px solid var(--line);73      justify-content: center;74      flex-wrap: wrap;75    }76    77    .mode-btn {78      background: rgba(215,168,91,.12);79      border: 2px solid rgba(215,168,91,.3);80      color: var(--ink);81      padding: 12px 20px;82      border-radius: 12px;83      font-size: 15px;84      font-weight: 600;85      cursor: pointer;86      transition: all 0.2s;87      min-width: 140px;88    }89    90    .mode-btn:hover {91      background: rgba(215,168,91,.2);92      border-color: var(--gold);93      transform: translateY(-2px);94    }95    96    .mode-btn.active {97      background: var(--copper);98      border-color: var(--copper);99      color: var(--bg);100    }101    102    /* MAIN INTERFACE */103    .main-interface {104      min-height: calc(100vh - 180px);105      padding: 20px;106    }107    108    .interface-panel {109      display: none;110    }111    112    .interface-panel.active {113      display: block;114    }115    116    /* FEELING GRID */117    .feeling-grid {118      display: grid;119      grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));120      gap: 15px;121      margin-top: 20px;122    }123    124    .feeling-card {125      background: linear-gradient(145deg, rgba(255,255,255,.04), rgba(255,255,255,.01));126      border: 2px solid var(--line);127      border-radius: 16px;128      padding: 30px 20px;129      text-align: center;130      cursor: pointer;131      transition: all 0.3s;132      position: relative;133      overflow: hidden;134    }135    136    .feeling-card::before {137      content: '';138      position: absolute;139      top: 0;140      left: 0;141      right: 0;142      bottom: 0;143      background: radial-gradient(circle at center, var(--card-color), transparent 70%);144      opacity: 0;145      transition: opacity 0.3s;146    }147    148    .feeling-card:hover::before,149    .feeling-card.selected::before {150      opacity: 0.15;151    }152    153    .feeling-card:hover {154      transform: translateY(-4px);155      border-color: var(--card-color);156      box-shadow: 0 8px 20px rgba(0,0,0,.3);157    }158    159    .feeling-card.selected {160      border-color: var(--card-color);161      border-width: 3px;162      background: linear-gradient(145deg, rgba(255,255,255,.08), rgba(255,255,255,.02));163    }164    165    .feeling-icon {166      font-size: 48px;167      margin-bottom: 10px;168      position: relative;169      z-index: 1;170    }171    172    .feeling-label {173      font-size: 16px;174      font-weight: 600;175      color: var(--ink);176      position: relative;177      z-index: 1;178    }179    180    .feeling-sublabel {181      font-size: 12px;182      color: var(--muted);183      margin-top: 5px;184      position: relative;185      z-index: 1;186    }187    188    /* INTENSITY SLIDER */189    .intensity-section {190      margin-top: 30px;191      padding: 25px;192      background: var(--panel);193      border-radius: 16px;194      border: 1px solid var(--line);195    }196    197    .intensity-label {198      font-size: 18px;199      color: var(--gold);200      margin-bottom: 15px;201      text-align: center;202      font-weight: 600;203    }204    205    .slider-container {206      position: relative;207      padding: 20px 0;208    }209    210    .intensity-slider {211      width: 100%;212      height: 60px;213      -webkit-appearance: none;214      appearance: none;215      background: linear-gradient(90deg, 216        rgba(115,149,111,.3) 0%, 217        rgba(215,168,91,.3) 50%, 218        rgba(210,106,92,.3) 100%);219      border-radius: 30px;220      outline: none;221      cursor: pointer;222    }223    224    .intensity-slider::-webkit-slider-thumb {225      -webkit-appearance: none;226      appearance: none;227      width: 70px;228      height: 70px;229      background: radial-gradient(circle, var(--gold), var(--copper));230      border: 4px solid var(--ink);231      border-radius: 50%;232      cursor: grab;233      box-shadow: 0 4px 12px rgba(0,0,0,.4);234    }235    236    .intensity-slider::-webkit-slider-thumb:active {237      cursor: grabbing;238      transform: scale(1.1);239    }240    241    .intensity-value {242      text-align: center;243      font-size: 24px;244      color: var(--ink);245      margin-top: 15px;246      font-weight: 600;247    }248    249    /* BODY MAP */250    .body-map {251      display: grid;252      grid-template-columns: repeat(3, 1fr);253      gap: 10px;254      max-width: 500px;255      margin: 20px auto;256    }257    258    .body-zone {259      background: var(--panel);260      border: 2px solid var(--line);261      padding: 20px 10px;262      text-align: center;263      border-radius: 12px;264      cursor: pointer;265      transition: all 0.2s;266      font-size: 14px;267      font-weight: 600;268    }269    270    .body-zone:hover {271      background: rgba(215,168,91,.15);272      border-color: var(--gold);273      transform: scale(1.05);274    }275    276    .body-zone.active {277      background: var(--copper);278      border-color: var(--copper);279      color: var(--bg);280    }281    282    /* TEMPERATURE SELECTOR */283    .temp-selector {284      display: grid;285      grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));286      gap: 15px;287      margin-top: 20px;288    }289    290    .temp-card {291      background: var(--panel);292      border: 2px solid var(--line);293      padding: 25px 15px;294      border-radius: 12px;295      text-align: center;296      cursor: pointer;297      transition: all 0.2s;298    }299    300    .temp-card:hover {301      transform: translateY(-3px);302    }303    304    .temp-card.active {305      border-color: var(--card-color);306      background: linear-gradient(145deg, 307        rgba(var(--card-color-rgb), 0.15), 308        rgba(var(--card-color-rgb), 0.05));309    }310    311    .temp-icon {312      font-size: 40px;313      margin-bottom: 8px;314    }315    316    /* OUTPUT PANEL */317    .output-panel {318      position: fixed;319      bottom: 0;320      left: 0;321      right: 0;322      background: linear-gradient(180deg, transparent, var(--panel) 20%, var(--panel));323      border-top: 2px solid var(--copper);324      padding: 20px;325      transform: translateY(100%);326      transition: transform 0.3s;327      max-height: 40vh;328      overflow-y: auto;329    }330    331    .output-panel.visible {332      transform: translateY(0);333    }334    335    .output-text {336      font-size: 18px;337      color: var(--ink);338      background: var(--bg);339      padding: 20px;340      border-radius: 12px;341      border: 1px solid var(--line);342      margin-bottom: 15px;343      line-height: 1.6;344    }345    346    .output-controls {347      display: flex;348      gap: 10px;349      flex-wrap: wrap;350      justify-content: center;351    }352    353    .output-btn {354      background: var(--copper);355      border: none;356      color: var(--ink);357      padding: 12px 24px;358      border-radius: 8px;359      font-size: 16px;360      font-weight: 600;361      cursor: pointer;362      transition: all 0.2s;363    }364    365    .output-btn:hover {366      background: var(--terracotta);367      transform: translateY(-2px);368    }369    370    .output-btn.secondary {371      background: rgba(215,168,91,.2);372      border: 1px solid var(--gold);373    }374    375    .provenance {376      font-size: 11px;377      color: var(--soft);378      margin-top: 10px;379      padding: 10px;380      background: rgba(0,0,0,.2);381      border-radius: 6px;382      border-left: 3px solid var(--gold);383    }384    385    /* SECTION HEADERS */386    .section-header {387      font-size: 20px;388      color: var(--gold);389      margin: 30px 0 15px;390      padding-bottom: 10px;391      border-bottom: 1px solid var(--line);392      font-weight: 600;393    }394    395    /* URGENCY INDICATOR */396    .urgency-indicator {397      display: flex;398      gap: 10px;399      justify-content: center;400      margin: 20px 0;401    }402    403    .urgency-btn {404      flex: 1;405      max-width: 200px;406      padding: 20px;407      background: var(--panel);408      border: 2px solid var(--line);409      border-radius: 12px;410      cursor: pointer;411      transition: all 0.2s;412      text-align: center;413    }414    415    .urgency-btn:hover {416      transform: scale(1.05);417    }418    419    .urgency-btn.active {420      border-width: 3px;421    }422    423    .urgency-btn.low.active {424      border-color: var(--green);425      background: rgba(115,149,111,.15);426    }427    428    .urgency-btn.medium.active {429      border-color: var(--gold);430      background: rgba(215,168,91,.15);431    }432    433    .urgency-btn.high.active {434      border-color: var(--danger);435      background: rgba(210,106,92,.15);436    }437    438    .urgency-icon {439      font-size: 32px;440      margin-bottom: 8px;441    }442  </style>443</head>444<body>445  <div class="container">446    <div class="header">447      <h1>Threshold</h1>448      <p>Communication bridge for nonspeakers ยท Internal โ†’ External ยท HIR-bounded translation</p>449    </div>450    451    <div class="mode-select">452      <button class="mode-btn active" data-mode="feeling">How I Feel</button>453      <button class="mode-btn" data-mode="body">Where I Feel It</button>454      <button class="mode-btn" data-mode="need">What I Need</button>455      <button class="mode-btn" data-mode="custom">My Words</button>456    </div>457    458    <div class="main-interface">459      <!-- FEELING MODE -->460      <div class="interface-panel active" id="feeling-panel">461        <div class="section-header">Choose what matches</div>462        <div class="feeling-grid">463          <div class="feeling-card" data-feeling="safe" style="--card-color: #7dbb8e">464            <div class="feeling-icon">๐ŸŒฟ</div>465            <div class="feeling-label">Safe</div>466            <div class="feeling-sublabel">settled, calm</div>467          </div>468          <div class="feeling-card" data-feeling="urgent" style="--card-color: #d26a5c">469            <div class="feeling-icon">โšก</div>470            <div class="feeling-label">Urgent</div>471            <div class="feeling-sublabel">needs now</div>472          </div>473          <div class="feeling-card" data-feeling="pressure" style="--card-color: #c77748">474            <div class="feeling-icon">๐Ÿ”ฅ</div>475            <div class="feeling-label">Pressure</div>476            <div class="feeling-sublabel">building, tight</div>477          </div>478          <div class="feeling-card" data-feeling="tired" style="--card-color: #9e8f7a">479            <div class="feeling-icon">๐ŸŒ™</div>480            <div class="feeling-label">Tired</div>481            <div class="feeling-sublabel">heavy, need rest</div>482          </div>483          <div class="feeling-card" data-feeling="excited" style="--card-color: #d7a85b">484            <div class="feeling-icon">โœจ</div>485            <div class="feeling-label">Excited</div>486            <div class="feeling-sublabel">energy, want</div>487          </div>488          <div class="feeling-card" data-feeling="uncomfortable" style="--card-color: #b75d3a">489            <div class="feeling-icon">โš ๏ธ</div>490            <div class="feeling-label">Uncomfortable</div>491            <div class="feeling-sublabel">something wrong</div>492          </div>493          <div class="feeling-card" data-feeling="confused" style="--card-color: #6b7a88">494            <div class="feeling-icon">โ“</div>495            <div class="feeling-label">Confused</div>496            <div class="feeling-sublabel">unclear, lost</div>497          </div>498          <div class="feeling-card" data-feeling="happy" style="--card-color: #73956f">499            <div class="feeling-icon">๐Ÿ˜Š</div>500            <div class="feeling-label">Happy</div>501            <div class="feeling-sublabel">good, light</div>502          </div>503        </div>504        505        <div class="intensity-section">506          <div class="intensity-label">How much?</div>507          <div class="slider-container">508            <input type="range" min="1" max="10" value="5" class="intensity-slider" id="feeling-intensity">509            <div class="intensity-value"><span id="intensity-display">5</span> / 10</div>510          </div>511        </div>512        513        <div class="section-header">How urgent?</div>514        <div class="urgency-indicator">515          <div class="urgency-btn low" data-urgency="low">516            <div class="urgency-icon">๐Ÿ•</div>517            <div>Can wait</div>518          </div>519          <div class="urgency-btn medium" data-urgency="medium">520            <div class="urgency-icon">โฐ</div>521            <div>Soon</div>522          </div>523          <div class="urgency-btn high" data-urgency="high">524            <div class="urgency-icon">๐Ÿšจ</div>525            <div>Right now</div>526          </div>527        </div>528      </div>529      530      <!-- BODY MODE -->531      <div class="interface-panel" id="body-panel">532        <div class="section-header">Where do you feel it?</div>533        <div class="body-map">534          <div class="body-zone" data-zone="head">Head</div>535          <div class="body-zone" data-zone="neck">Neck</div>536          <div class="body-zone" data-zone="shoulders">Shoulders</div>537          <div class="body-zone" data-zone="chest">Chest</div>538          <div class="body-zone" data-zone="stomach">Stomach</div>539          <div class="body-zone" data-zone="back">Back</div>540          <div class="body-zone" data-zone="arms">Arms</div>541          <div class="body-zone" data-zone="hands">Hands</div>542          <div class="body-zone" data-zone="legs">Legs</div>543        </div>544        545        <div class="section-header">What does it feel like?</div>546        <div class="temp-selector">547          <div class="temp-card" data-temp="hot" style="--card-color: #d26a5c; --card-color-rgb: 210,106,92">548            <div class="temp-icon">๐Ÿ”ฅ</div>549            <div>Hot</div>550          </div>551          <div class="temp-card" data-temp="cold" style="--card-color: #7dbb8e; --card-color-rgb: 125,187,142">552            <div class="temp-icon">โ„๏ธ</div>553            <div>Cold</div>554          </div>555          <div class="temp-card" data-temp="tight" style="--card-color: #c77748; --card-color-rgb: 199,119,72">556            <div class="temp-icon">๐Ÿ”’</div>557            <div>Tight</div>558          </div>559          <div class="temp-card" data-temp="heavy" style="--card-color: #9e8f7a; --card-color-rgb: 158,143,122">560            <div class="temp-icon">โฌ‡๏ธ</div>561            <div>Heavy</div>562          </div>563          <div class="temp-card" data-temp="sharp" style="--card-color: #b75d3a; --card-color-rgb: 183,93,58">564            <div class="temp-icon">๐Ÿ“</div>565            <div>Sharp</div>566          </div>567          <div class="temp-card" data-temp="numb" style="--card-color: #6b7a88; --card-color-rgb: 107,122,136">568            <div class="temp-icon">โž–</div>569            <div>Numb</div>570          </div>571        </div>572        573        <div class="intensity-section">574          <div class="intensity-label">How strong?</div>575          <div class="slider-container">576            <input type="range" min="1" max="10" value="5" class="intensity-slider" id="body-intensity">577            <div class="intensity-value"><span id="body-intensity-display">5</span> / 10</div>578          </div>579        </div>580      </div>581      582      <!-- NEED MODE -->583      <div class="interface-panel" id="need-panel">584        <div class="section-header">What would help?</div>585        <div class="feeling-grid">586          <div class="feeling-card" data-need="quiet" style="--card-color: #73956f">587            <div class="feeling-icon">๐Ÿ”‡</div>588            <div class="feeling-label">Quiet</div>589            <div class="feeling-sublabel">less noise</div>590          </div>591          <div class="feeling-card" data-need="space" style="--card-color: #7dbb8e">592            <div class="feeling-icon">๐ŸŒŒ</div>593            <div class="feeling-label">Space</div>594            <div class="feeling-sublabel">room to be</div>595          </div>596          <div class="feeling-card" data-need="water" style="--card-color: #6b7a88">597            <div class="feeling-icon">๐Ÿ’ง</div>598            <div class="feeling-label">Water</div>599            <div class="feeling-sublabel">thirsty</div>600          </div>601          <div class="feeling-card" data-need="food" style="--card-color: #d7a85b">602            <div class="feeling-icon">๐ŸŽ</div>603            <div class="feeling-label">Food</div>604            <div class="feeling-sublabel">hungry</div>605          </div>606          <div class="feeling-card" data-need="rest" style="--card-color: #9e8f7a">607            <div class="feeling-icon">๐Ÿ›Œ</div>608            <div class="feeling-label">Rest</div>609            <div class="feeling-sublabel">need to lie down</div>610          </div>611          <div class="feeling-card" data-need="bathroom" style="--card-color: #c77748">612            <div class="feeling-icon">๐Ÿšป</div>613            <div class="feeling-label">Bathroom</div>614            <div class="feeling-sublabel">need to go</div>615          </div>616          <div class="feeling-card" data-need="help" style="--card-color: #d26a5c">617            <div class="feeling-icon">๐Ÿ†˜</div>618            <div class="feeling-label">Help</div>619            <div class="feeling-sublabel">something's wrong</div>620          </div>621          <div class="feeling-card" data-need="connection" style="--card-color: #b75d3a">622            <div class="feeling-icon">๐Ÿค</div>623            <div class="feeling-label">Connection</div>624            <div class="feeling-sublabel">want presence</div>625          </div>626        </div>627      </div>628      629      <!-- CUSTOM MODE -->630      <div class="interface-panel" id="custom-panel">631        <div class="section-header">Build your own message</div>632        <div style="padding: 20px;">633          <p style="color: var(--muted); margin-bottom: 20px; text-align: center;">634            This mode lets you create personal meanings that the system will remember for you.635            Tap any feeling/body/need card from other modes, then add your own words here.636          </p>637          <div style="background: var(--panel); padding: 20px; border-radius: 12px; border: 1px solid var(--line);">638            <div style="color: var(--gold); margin-bottom: 10px; font-weight: 600;">My message:</div>639            <textarea id="custom-message" 640              style="width: 100%; min-height: 120px; background: var(--bg); color: var(--ink); border: 1px solid var(--line); border-radius: 8px; padding: 15px; font-size: 16px; font-family: inherit; resize: vertical;"641              placeholder="Type or dictate what you want to say..."></textarea>642          </div>643        </div>644      </div>645    </div>646    647    <!-- OUTPUT PANEL -->648    <div class="output-panel" id="output">649      <div class="output-text" id="output-text"></div>650      <div class="provenance" id="provenance"></div>651      <div class="output-controls">652        <button class="output-btn" onclick="speakMessage()">๐Ÿ”Š Speak</button>653        <button class="output-btn" onclick="copyMessage()">๐Ÿ“‹ Copy</button>654        <button class="output-btn secondary" onclick="saveMapping()">๐Ÿ’พ Remember This</button>655        <button class="output-btn secondary" onclick="closeOutput()">โœ• Close</button>656      </div>657    </div>658  </div>659  660  <script>661    // STATE662    const state = {663      mode: 'feeling',664      feeling: null,665      feelingIntensity: 5,666      urgency: null,667      bodyZone: null,668      bodyQuality: null,669      bodyIntensity: 5,670      need: null,671      customMessage: '',672      timestamp: null,673      mappings: JSON.parse(localStorage.getItem('threshold_mappings') || '{}')674    };675    676    // MODE SWITCHING677    document.querySelectorAll('.mode-btn').forEach(btn => {678      btn.addEventListener('click', () => {679        const mode = btn.dataset.mode;680        document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));681        btn.classList.add('active');682        document.querySelectorAll('.interface-panel').forEach(p => p.classList.remove('active'));683        document.getElementById(mode + '-panel').classList.add('active');684        state.mode = mode;685      });686    });687    688    // FEELING SELECTION689    document.querySelectorAll('.feeling-card').forEach(card => {690      card.addEventListener('click', () => {691        const panel = card.closest('.interface-panel');692        panel.querySelectorAll('.feeling-card').forEach(c => c.classList.remove('selected'));693        card.classList.add('selected');694        695        if (state.mode === 'feeling') {696          state.feeling = card.dataset.feeling;697        } else if (state.mode === 'need') {698          state.need = card.dataset.need;699        }700        701        generateOutput();702      });703    });704    705    // INTENSITY SLIDERS706    document.getElementById('feeling-intensity').addEventListener('input', (e) => {707      state.feelingIntensity = parseInt(e.target.value);708      document.getElementById('intensity-display').textContent = state.feelingIntensity;709      if (state.feeling) generateOutput();710    });711    712    document.getElementById('body-intensity').addEventListener('input', (e) => {713      state.bodyIntensity = parseInt(e.target.value);714      document.getElementById('body-intensity-display').textContent = state.bodyIntensity;715      if (state.bodyZone) generateOutput();716    });717    718    // URGENCY719    document.querySelectorAll('.urgency-btn').forEach(btn => {720      btn.addEventListener('click', () => {721        document.querySelectorAll('.urgency-btn').forEach(b => b.classList.remove('active'));722        btn.classList.add('active');723        state.urgency = btn.dataset.urgency;724        if (state.feeling) generateOutput();725      });726    });727    728    // BODY MAP729    document.querySelectorAll('.body-zone').forEach(zone => {730      zone.addEventListener('click', () => {731        document.querySelectorAll('.body-zone').forEach(z => z.classList.remove('active'));732        zone.classList.add('active');733        state.bodyZone = zone.dataset.zone;734        generateOutput();735      });736    });737    738    // TEMP/QUALITY739    document.querySelectorAll('.temp-card').forEach(card => {740      card.addEventListener('click', () => {741        document.querySelectorAll('.temp-card').forEach(c => c.classList.remove('active'));742        card.classList.add('active');743        state.bodyQuality = card.dataset.temp;744        if (state.bodyZone) generateOutput();745      });746    });747    748    // CUSTOM MESSAGE749    document.getElementById('custom-message').addEventListener('input', (e) => {750      state.customMessage = e.target.value;751      if (state.customMessage.trim()) generateOutput();752    });753    754    // GENERATE OUTPUT755    function generateOutput() {756      state.timestamp = new Date();757      let message = '';758      let provenance = '';759      760      if (state.mode === 'feeling' && state.feeling) {761        const intensityWord = state.feelingIntensity <= 3 ? 'a little' :762                             state.feelingIntensity <= 6 ? 'moderately' :763                             state.feelingIntensity <= 8 ? 'very' : 'extremely';764        const urgencyPhrase = state.urgency === 'low' ? 'when you can' :765                             state.urgency === 'medium' ? 'soon' :766                             state.urgency === 'high' ? 'right now' : '';767        768        message = `I feel ${intensityWord} ${state.feeling}`;769        if (urgencyPhrase) message += ` and I need attention ${urgencyPhrase}`;770        message += '.';771        772        provenance = `Mode: How I Feel | Feeling: ${state.feeling} | Intensity: ${state.feelingIntensity}/10`;773        if (state.urgency) provenance += ` | Urgency: ${state.urgency}`;774      }775      else if (state.mode === 'body' && state.bodyZone) {776        const qualityPhrase = state.bodyQuality ? ` and it feels ${state.bodyQuality}` : '';777        const intensityWord = state.bodyIntensity <= 3 ? 'mild' :778                             state.bodyIntensity <= 6 ? 'noticeable' :779                             state.bodyIntensity <= 8 ? 'strong' : 'intense';780        781        message = `I feel something in my ${state.bodyZone}${qualityPhrase}. The sensation is ${intensityWord}.`;782        783        provenance = `Mode: Where I Feel It | Zone: ${state.bodyZone}`;784        if (state.bodyQuality) provenance += ` | Quality: ${state.bodyQuality}`;785        provenance += ` | Intensity: ${state.bodyIntensity}/10`;786      }787      else if (state.mode === 'need' && state.need) {788        const needMessages = {789          quiet: 'I need it to be quieter.',790          space: 'I need some space.',791          water: 'I need water.',792          food: 'I need food.',793          rest: 'I need to rest.',794          bathroom: 'I need to use the bathroom.',795          help: 'I need help. Something is wrong.',796          connection: 'I need someone with me.'797        };798        799        message = needMessages[state.need] || `I need ${state.need}.`;800        provenance = `Mode: What I Need | Need: ${state.need}`;801      }802      else if (state.mode === 'custom' && state.customMessage.trim()) {803        message = state.customMessage.trim();804        provenance = `Mode: My Words | Custom message entered by user`;805      }806      807      if (message) {808        document.getElementById('output-text').textContent = message;809        document.getElementById('provenance').innerHTML = 810          `<strong>Translation details:</strong> ${provenance}<br>` +811          `<strong>Timestamp:</strong> ${state.timestamp.toLocaleString()}<br>` +812          `<strong>Source:</strong> User-defined meaning, not imposed interpretation<br>` +813          `<strong>Certainty:</strong> Direct user signal, high fidelity`;814        document.getElementById('output').classList.add('visible');815      }816    }817    818    // OUTPUT CONTROLS819    function speakMessage() {820      const text = document.getElementById('output-text').textContent;821      if ('speechSynthesis' in window) {822        const utterance = new SpeechSynthesisUtterance(text);823        utterance.rate = 0.9;824        utterance.pitch = 1;825        speechSynthesis.speak(utterance);826      } else {827        alert('Speech synthesis not supported in this browser');828      }829    }830    831    function copyMessage() {832      const text = document.getElementById('output-text').textContent;833      navigator.clipboard.writeText(text).then(() => {834        alert('Message copied to clipboard');835      });836    }837    838    function saveMapping() {839      const key = `${state.mode}_${state.feeling || state.need || state.bodyZone}_${state.timestamp.getTime()}`;840      const mapping = {841        mode: state.mode,842        message: document.getElementById('output-text').textContent,843        state: {...state},844        timestamp: state.timestamp.toISOString()845      };846      847      state.mappings[key] = mapping;848      localStorage.setItem('threshold_mappings', JSON.stringify(state.mappings));849      alert('This translation has been saved to your personal vocabulary');850    }851    852    function closeOutput() {853      document.getElementById('output').classList.remove('visible');854    }855  </script>856</body>857</html>